MythTV  master
audiooutputdigitalencoder.cpp
Go to the documentation of this file.
1 // Std C headers
2 #include <cstdio>
3 #include <cstring>
4 #include <unistd.h>
5 
6 #include "mythcorecontext.h"
7 #include "config.h"
8 
9 // libav headers
10 extern "C" {
11 #include "libavutil/mem.h" // for av_free
12 #include "libavcodec/avcodec.h"
13 }
14 
15 // MythTV headers
17 #include "audiooutpututil.h"
18 #include "compat.h"
19 #include "mythlogging.h"
20 
21 #define LOC QString("DEnc: ")
22 
24  av_context(nullptr),
25  out(nullptr), out_size(0),
26  in(nullptr), inp(nullptr), in_size(0),
27  outlen(0), inlen(0),
28  samples_per_frame(0),
29  m_spdifenc(nullptr), m_frame(nullptr)
30 {
31  out = (outbuf_t *)av_mallocz(OUTBUFSIZE);
32  if (out)
33  {
35  }
36  in = (inbuf_t *)av_mallocz(INBUFSIZE);
37  if (in)
38  {
40  }
41  inp = (inbuf_t *)av_mallocz(INBUFSIZE);
42 }
43 
45 {
46  Reset();
47  if (out)
48  {
49  av_freep(&out);
50  out_size = 0;
51  }
52  if (in)
53  {
54  av_freep(&in);
55  in_size = 0;
56  }
57  if (inp)
58  {
59  av_freep(&inp);
60  }
61 }
62 
64 {
65  if (av_context)
66  avcodec_free_context(&av_context);
67 
68  av_frame_free(&m_frame);
69 
70  delete m_spdifenc;
71  m_spdifenc = nullptr;
72 
73  clear();
74 }
75 
77  size_t old_size, size_t new_size)
78 {
79  if (!ptr)
80  return ptr;
81 
82  // av_realloc doesn't maintain 16 bytes alignment
83  void *new_ptr = av_malloc(new_size);
84  if (!new_ptr)
85  {
86  av_free(ptr);
87  return new_ptr;
88  }
89  memcpy(new_ptr, ptr, old_size);
90  av_free(ptr);
91  return new_ptr;
92 }
93 
95  AVCodecID codec_id, int bitrate, int samplerate, int channels)
96 {
97  AVCodec *codec;
98  int ret;
99 
100  LOG(VB_AUDIO, LOG_INFO, LOC +
101  QString("Init codecid=%1, br=%2, sr=%3, ch=%4")
102  .arg(ff_codec_id_string(codec_id)) .arg(bitrate)
103  .arg(samplerate) .arg(channels));
104 
105  if (!(in || inp || out))
106  {
107  LOG(VB_GENERAL, LOG_ERR, LOC + "Memory allocation failed");
108  return false;
109  }
110 
111  // Clear digital encoder from all existing content
112  Reset();
113 
114  codec = avcodec_find_encoder_by_name("ac3_fixed");
115  if (!codec)
116  {
117  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not find codec");
118  return false;
119  }
120 
121  av_context = avcodec_alloc_context3(codec);
122 
123  av_context->bit_rate = bitrate;
124  av_context->sample_rate = samplerate;
125  av_context->channels = channels;
126  av_context->channel_layout = av_get_default_channel_layout(channels);
127  av_context->sample_fmt = AV_SAMPLE_FMT_S16P;
128 
129  // open it
130  ret = avcodec_open2(av_context, codec, nullptr);
131  if (ret < 0)
132  {
133  LOG(VB_GENERAL, LOG_ERR, LOC +
134  "Could not open codec, invalid bitrate or samplerate");
135 
136  return false;
137  }
138 
139  m_spdifenc = new SPDIFEncoder("spdif", AV_CODEC_ID_AC3);
140  if (!m_spdifenc->Succeeded())
141  {
142  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not create spdif muxer");
143  return false;
144  }
145 
146  samples_per_frame = av_context->frame_size * av_context->channels;
147 
148  LOG(VB_AUDIO, LOG_INFO, QString("DigitalEncoder::Init fs=%1, spf=%2")
149  .arg(av_context->frame_size) .arg(samples_per_frame));
150 
151  return true;
152 }
153 
154 size_t AudioOutputDigitalEncoder::Encode(void *buf, int len, AudioFormat format)
155 {
156  int sampleSize = AudioOutputSettings::SampleSize(format);
157  if (sampleSize <= 0)
158  {
159  LOG(VB_AUDIO, LOG_ERR, LOC + "AC-3 encode error, sample size is zero");
160  return 0;
161  }
162 
163  // Check if there is enough space in incoming buffer
164  int required_len = inlen +
165  len * AudioOutputSettings::SampleSize(FORMAT_S16) / sampleSize;
166 
167  if (required_len > (int)in_size)
168  {
169  required_len = ((required_len / INBUFSIZE) + 1) * INBUFSIZE;
170  LOG(VB_AUDIO, LOG_INFO, LOC +
171  QString("low mem, reallocating in buffer from %1 to %2")
172  .arg(in_size) .arg(required_len));
173  inbuf_t *tmp = reinterpret_cast<inbuf_t*>
174  (realloc(in, in_size, required_len));
175  if (!tmp)
176  {
177  in = nullptr;
178  in_size = 0;
179  LOG(VB_AUDIO, LOG_ERR, LOC +
180  "AC-3 encode error, insufficient memory");
181  return outlen;
182  }
183  in = tmp;
184  in_size = required_len;
185  }
186  if (format != FORMAT_S16)
187  {
189  buf, len);
190  }
191  else
192  {
193  memcpy((char *)in + inlen, buf, len);
194  inlen += len;
195  }
196 
197  int frames = inlen / sizeof(inbuf_t) / samples_per_frame;
198  int i = 0;
199  int channels = av_context->channels;
200  int size_channel = av_context->frame_size *
202  if (!m_frame)
203  {
204  if (!(m_frame = av_frame_alloc()))
205  {
206  in = nullptr;
207  in_size = 0;
208  LOG(VB_AUDIO, LOG_ERR, LOC +
209  "AC-3 encode error, insufficient memory");
210  return outlen;
211  }
212  }
213  else
214  {
215  av_frame_unref(m_frame);
216  }
217  m_frame->nb_samples = av_context->frame_size;
218  m_frame->pts = AV_NOPTS_VALUE;
219 
220  if (frames > 0)
221  {
222  // init AVFrame for planar data (input is interleaved)
223  for (int j = 0, jj = 0; j < channels; j++, jj += av_context->frame_size)
224  {
225  m_frame->data[j] = (uint8_t*)(inp + jj);
226  }
227  }
228 
229  while (i < frames)
230  {
231  AVPacket pkt;
232  av_init_packet(&pkt);
233  pkt.data = nullptr;
234  pkt.size = 0;
235  bool got_packet = false;
236 
238  FORMAT_S16, channels,
239  (uint8_t*)inp,
240  (uint8_t*)(in + i * samples_per_frame),
241  size_channel * channels);
242 
243  // SUGGESTION
244  // Now that avcodec_encode_audio2 is deprecated and replaced
245  // by 2 calls, this could be optimized
246  // into separate routines or separate threads.
247  int ret = avcodec_receive_packet(av_context, &pkt);
248  if (ret == 0)
249  got_packet = true;
250  if (ret == AVERROR(EAGAIN))
251  ret = 0;
252  if (ret == 0)
253  ret = avcodec_send_frame(av_context, m_frame);
254  // if ret from avcodec_send_frame is AVERROR(EAGAIN) then
255  // there are 2 packets to be received while only 1 frame to be
256  // sent. The code does not cater for this. Hopefully it will not happen.
257 
258  if (ret < 0)
259  {
260  char error[AV_ERROR_MAX_STRING_SIZE];
261  LOG(VB_GENERAL, LOG_ERR, LOC +
262  QString("audio encode error: %1 (%2)")
263  .arg(av_make_error_string(error, sizeof(error), ret))
264  .arg(got_packet));
265  return ret;
266  }
267  i++;
268  if (!got_packet)
269  continue;
270 
271  if (!m_spdifenc)
272  {
273  m_spdifenc = new SPDIFEncoder("spdif", AV_CODEC_ID_AC3);
274  }
275 
276  m_spdifenc->WriteFrame(pkt.data, pkt.size);
277  av_packet_unref(&pkt);
278 
279  // Check if output buffer is big enough
280  required_len = outlen + m_spdifenc->GetProcessedSize();
281  if (required_len > (int)out_size)
282  {
283  required_len = ((required_len / OUTBUFSIZE) + 1) * OUTBUFSIZE;
284  LOG(VB_AUDIO, LOG_WARNING, LOC +
285  QString("low mem, reallocating out buffer from %1 to %2")
286  .arg(out_size) .arg(required_len));
287  outbuf_t *tmp = reinterpret_cast<outbuf_t*>
288  (realloc(out, out_size, required_len));
289  if (!tmp)
290  {
291  out = nullptr;
292  out_size = 0;
293  LOG(VB_AUDIO, LOG_ERR, LOC +
294  "AC-3 encode error, insufficient memory");
295  return outlen;
296  }
297  out = tmp;
298  out_size = required_len;
299  }
300  int data_size = 0;
301  m_spdifenc->GetData((uint8_t *)out + outlen, data_size);
302  outlen += data_size;
303  inlen -= samples_per_frame * sizeof(inbuf_t);
304  }
305 
306  memmove(in, in + i * samples_per_frame, inlen);
307  return outlen;
308 }
309 
310 size_t AudioOutputDigitalEncoder::GetFrames(void *ptr, int maxlen)
311 {
312  int len = std::min(maxlen, outlen);
313  if (len != maxlen)
314  {
315  LOG(VB_AUDIO, LOG_INFO, LOC + "GetFrames: getting less than requested");
316  }
317  memcpy(ptr, out, len);
318  outlen -= len;
319  memmove(out, (char *)out + len, outlen);
320  return len;
321 }
322 
324 {
325  inlen = outlen = 0;
326 }
void * realloc(void *ptr, size_t old_size, size_t new_size)
bool Init(AVCodecID codec_id, int bitrate, int samplerate, int channels)
void WriteFrame(unsigned char *data, int size)
Encode data through created muxer unsigned char data: pointer to data to encode int size: size of dat...
static void error(const char *str,...)
Definition: vbi.c:41
size_t GetFrames(void *ptr, int maxlen)
static guint32 * tmp
Definition: goom_core.c:35
static int fromFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert float samples to integers.
static int SampleSize(AudioFormat format)
void * av_malloc(unsigned int size)
bool Succeeded()
Definition: spdifencoder.h:24
#define INBUFSIZE
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
int GetProcessedSize()
Definition: spdifencoder.h:21
const char * frames[3]
Definition: element.c:46
int GetData(unsigned char *buffer, int &dest_size)
Retrieve encoded data and copy it in the provided buffer.
void av_free(void *ptr)
size_t Encode(void *buf, int len, AudioFormat format)
#define OUTBUFSIZE
static void DeinterleaveSamples(AudioFormat format, int channels, uint8_t *output, const uint8_t *input, int data_size)
Deinterleave input samples Deinterleave audio samples and compact them.