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 // libav headers
7 extern "C" {
8 #include "libavutil/mem.h" // for av_free
9 #include "libavcodec/avcodec.h"
10 }
11 
12 // MythTV headers
13 #include "libmythbase/compat.h"
16 
18 #include "audiooutpututil.h"
19 
20 #define LOC QString("DEnc: ")
21 
23 {
24  m_outbuf = static_cast<uint8_t*>(av_mallocz(OUTBUFSIZE));
25  if (m_outbuf)
26  {
28  }
29  m_inbuf = static_cast<uint8_t*>(av_mallocz(INBUFSIZE));
30  if (m_inbuf)
31  {
33  }
34  m_framebuf = static_cast<uint8_t*>(av_mallocz(INBUFSIZE));
35 }
36 
38 {
39  Reset();
40  if (m_outbuf)
41  {
42  av_freep(&m_outbuf);
43  m_outSize = 0;
44  }
45  if (m_inbuf)
46  {
47  av_freep(&m_inbuf);
48  m_inSize = 0;
49  }
50  if (m_framebuf)
51  {
52  av_freep(&m_framebuf);
53  }
54 }
55 
57 {
58  if (m_avContext)
59  avcodec_free_context(&m_avContext);
60 
61  av_frame_free(&m_frame);
62 
63  delete m_spdifEnc;
64  m_spdifEnc = nullptr;
65 
66  clear();
67 }
68 
70  size_t old_size, size_t new_size)
71 {
72  if (!ptr)
73  return ptr;
74 
75  // av_realloc doesn't maintain 16 bytes alignment
76  void *new_ptr = av_malloc(new_size);
77  if (!new_ptr)
78  {
79  av_free(ptr);
80  return new_ptr;
81  }
82  memcpy(new_ptr, ptr, old_size);
83  av_free(ptr);
84  return new_ptr;
85 }
86 
87 // Encode can use either ac3 (floating point) or ac3_fixed (fixed point)
88 // To use ac3_fixed define AC3_FIXED 1
89 
90 #define AC3_FIXED 0 // NOLINT(cppcoreguidelines-macro-usage)
91 #if AC3_FIXED
92 static constexpr const char* CODECNAME { "ac3_fixed" };
93 static constexpr AVSampleFormat FFMPEG_SAMPLE_FORMAT { AV_SAMPLE_FMT_S32P };
94 static constexpr AudioFormat MYTH_SAMPLE_FORMAT { FORMAT_S32 };
95 #else
96 static constexpr const char* CODECNAME { "ac3" };
97 static constexpr AVSampleFormat FFMPEG_SAMPLE_FORMAT { AV_SAMPLE_FMT_FLTP };
99 #define MYTH_USE_FLOAT 1 // NOLINT(cppcoreguidelines-macro-usage)
100 #endif
101 
103  AVCodecID codec_id, int bitrate, int samplerate, int channels)
104 {
105  LOG(VB_AUDIO, LOG_INFO, LOC +
106  QString("Init codecid=%1, br=%2, sr=%3, ch=%4")
107  .arg(avcodec_get_name(codec_id)) .arg(bitrate)
108  .arg(samplerate) .arg(channels));
109 
110  if (!(m_inbuf || m_framebuf || m_outbuf))
111  {
112  LOG(VB_GENERAL, LOG_ERR, LOC + "Memory allocation failed");
113  return false;
114  }
115 
116  // Clear digital encoder from all existing content
117  Reset();
118 
119  LOG(VB_GENERAL, LOG_INFO, LOC + QString("Using codec %1 to encode audio").arg(CODECNAME));
120  const AVCodec *codec = avcodec_find_encoder_by_name(CODECNAME);
121  if (!codec)
122  {
123  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not find codec");
124  return false;
125  }
126 
127  m_avContext = avcodec_alloc_context3(codec);
128 
129  m_avContext->bit_rate = bitrate;
130  m_avContext->sample_rate = samplerate;
131  av_channel_layout_default(&(m_avContext->ch_layout), channels);
132  m_avContext->sample_fmt = FFMPEG_SAMPLE_FORMAT;
133 
134  // open it
135  int ret = avcodec_open2(m_avContext, codec, nullptr);
136  if (ret < 0)
137  {
138  LOG(VB_GENERAL, LOG_ERR, LOC +
139  "Could not open codec, invalid bitrate or samplerate");
140 
141  return false;
142  }
143 
144  m_spdifEnc = new SPDIFEncoder("spdif", AV_CODEC_ID_AC3);
145  if (!m_spdifEnc->Succeeded())
146  {
147  LOG(VB_GENERAL, LOG_ERR, LOC + "Could not create spdif muxer");
148  return false;
149  }
150 
151  m_samplesPerFrame = m_avContext->frame_size * m_avContext->ch_layout.nb_channels;
152 
153  LOG(VB_AUDIO, LOG_INFO, QString("DigitalEncoder::Init fs=%1, spf=%2")
154  .arg(m_avContext->frame_size) .arg(m_samplesPerFrame));
155 
156  return true;
157 }
158 
159 // input = 6 channel data from upconvert or speedup
160 // m_inbuf = 6 channel data converted to S32 or FLT samples interleaved
161 // m_framebuf = 1 frame, deinterleaved into planar format
162 // m_inlen = number of bytes available in m_inbuf
163 // format = incoming sample format, normally FORMAT_FLT
164 // upconvert and speedup both use floating point for parameter format
165 
166 int AudioOutputDigitalEncoder::Encode(void *input, int len, AudioFormat format)
167 {
168  int sampleSize = AudioOutputSettings::SampleSize(format);
169  if (sampleSize <= 0)
170  {
171  LOG(VB_AUDIO, LOG_ERR, LOC + "AC-3 encode error, sample size is zero");
172  return 0;
173  }
174 
175  // Check if there is enough space in incoming buffer
176  ssize_t required_len = m_inlen +
178 
179  if (required_len > m_inSize)
180  {
181  required_len = ((required_len / INBUFSIZE) + 1) * INBUFSIZE;
182  LOG(VB_AUDIO, LOG_INFO, LOC +
183  QString("low mem, reallocating in buffer from %1 to %2")
184  .arg(m_inSize) .arg(required_len));
185  auto *tmp = static_cast<uint8_t*> (realloc(m_inbuf, m_inSize, required_len));
186  if (!tmp)
187  {
188  m_inbuf = nullptr;
189  m_inSize = 0;
190  LOG(VB_AUDIO, LOG_ERR, LOC +
191  "AC-3 encode error, insufficient memory");
192  return m_outlen;
193  }
194  m_inbuf = tmp;
195  m_inSize = required_len;
196  }
197 
198  if (format == MYTH_SAMPLE_FORMAT)
199  {
200  // The input format is the same as the ffmpeg desired format so just copy the data
201  memcpy(m_inbuf + m_inlen, input, len);
202  m_inlen += len;
203  }
204 #if ! MYTH_USE_FLOAT
205  else if (format == FORMAT_FLT)
206  {
207  // The input format is float but ffmpeg wants something else so convert it
209  input, len);
210  }
211 #endif
212  else
213  {
214  LOG(VB_AUDIO, LOG_ERR, LOC +
215  QString("AC-3 encode error, cannot handle input format %1")
216  .arg(format));
217  return 0;
218  }
219 
221  int i = 0;
222  int channels = m_avContext->ch_layout.nb_channels;
223  int size_channel = m_avContext->frame_size *
225  if (!m_frame)
226  {
227  if (!(m_frame = av_frame_alloc()))
228  {
229  m_inbuf = nullptr;
230  m_inSize = 0;
231  LOG(VB_AUDIO, LOG_ERR, LOC +
232  "AC-3 encode error, insufficient memory");
233  return m_outlen;
234  }
235  }
236  else
237  {
238  av_frame_unref(m_frame);
239  }
240  m_frame->nb_samples = m_avContext->frame_size;
241  m_frame->pts = AV_NOPTS_VALUE;
242  m_frame->format = m_avContext->sample_fmt;
243  av_channel_layout_copy(&(m_frame->ch_layout), &(m_avContext->ch_layout));
244  m_frame->sample_rate = m_avContext->sample_rate;
245 
246  if (frames > 0)
247  {
248  // init AVFrame for planar data (input is interleaved)
249  for (int j = 0, jj = 0; j < channels; j++, jj += size_channel)
250  {
251  m_frame->data[j] = m_framebuf + jj;
252  }
253  }
254 
255  while (i < frames)
256  {
257  AVPacket *pkt = av_packet_alloc();
258  if (pkt == nullptr)
259  {
260  LOG(VB_RECORD, LOG_ERR, "packet allocation failed");
261  return AVERROR(ENOMEM);
262  }
263  bool got_packet = false;
264 
266  MYTH_SAMPLE_FORMAT, channels,
267  m_framebuf,
268  m_inbuf + static_cast<ptrdiff_t>(i) * size_channel * channels,
269  size_channel * channels);
270 
271  // SUGGESTION
272  // Now that avcodec_encode_audio2 is deprecated and replaced
273  // by 2 calls, this could be optimized
274  // into separate routines or separate threads.
275  int ret = avcodec_receive_packet(m_avContext, pkt);
276  if (ret == 0)
277  got_packet = true;
278  if (ret == AVERROR(EAGAIN))
279  ret = 0;
280  if (ret == 0)
281  ret = avcodec_send_frame(m_avContext, m_frame);
282  // if ret from avcodec_send_frame is AVERROR(EAGAIN) then
283  // there are 2 packets to be received while only 1 frame to be
284  // sent. The code does not cater for this. Hopefully it will not happen.
285 
286  if (ret < 0)
287  {
288  std::string error;
289  LOG(VB_GENERAL, LOG_ERR, LOC +
290  QString("audio encode error: %1 (%2)")
291  .arg(av_make_error_stdstring(error, ret))
292  .arg(got_packet));
293  av_packet_free(&pkt);
294  return ret;
295  }
296  i++;
297  if (!got_packet)
298  {
300  continue;
301  }
302  if (!m_spdifEnc)
303  {
304  m_spdifEnc = new SPDIFEncoder("spdif", AV_CODEC_ID_AC3);
305  }
306 
307  m_spdifEnc->WriteFrame(pkt->data, pkt->size);
308  av_packet_unref(pkt);
309  av_packet_free(&pkt);
310 
311  // Check if output buffer is big enough
312  required_len = m_outlen + m_spdifEnc->GetProcessedSize();
313  if (required_len > m_outSize)
314  {
315  required_len = ((required_len / OUTBUFSIZE) + 1) * OUTBUFSIZE;
316  LOG(VB_AUDIO, LOG_WARNING, LOC +
317  QString("low mem, reallocating out buffer from %1 to %2")
318  .arg(m_outSize) .arg(required_len));
319  auto *tmp = static_cast<uint8_t*>(realloc(m_outbuf, m_outSize, required_len));
320  if (!tmp)
321  {
322  m_outbuf = nullptr;
323  m_outSize = 0;
324  LOG(VB_AUDIO, LOG_ERR, LOC +
325  "AC-3 encode error, insufficient memory");
326  return m_outlen;
327  }
328  m_outbuf = tmp;
329  m_outSize = required_len;
330  }
331  size_t data_size = 0;
332  m_spdifEnc->GetData(m_outbuf + m_outlen, data_size);
333  m_outlen += data_size;
335  }
336 
338  return m_outlen;
339 }
340 
341 int AudioOutputDigitalEncoder::GetFrames(void *ptr, int maxlen)
342 {
343  int len = std::min(maxlen, m_outlen);
344  if (len != maxlen)
345  {
346  LOG(VB_AUDIO, LOG_INFO, LOC + "GetFrames: getting less than requested");
347  }
348  memcpy(ptr, m_outbuf, len);
349  m_outlen -= len;
350  memmove(m_outbuf, m_outbuf + len, m_outlen);
351  return len;
352 }
353 
355 {
356  m_inlen = m_outlen = 0;
357 }
SPDIFEncoder::Succeeded
bool Succeeded() const
Definition: spdifencoder.h:24
error
static void error(const char *str,...)
Definition: vbi.cpp:36
AudioOutputDigitalEncoder::~AudioOutputDigitalEncoder
~AudioOutputDigitalEncoder()
Definition: audiooutputdigitalencoder.cpp:37
AudioOutputDigitalEncoder::Init
bool Init(AVCodecID codec_id, int bitrate, int samplerate, int channels)
Definition: audiooutputdigitalencoder.cpp:102
AudioOutputDigitalEncoder::GetFrames
int GetFrames(void *ptr, int maxlen)
Definition: audiooutputdigitalencoder.cpp:341
audiooutpututil.h
AudioOutputDigitalEncoder::m_outlen
int m_outlen
Definition: audiooutputdigitalencoder.h:41
AudioOutputDigitalEncoder::realloc
static void * realloc(void *ptr, size_t old_size, size_t new_size)
Definition: audiooutputdigitalencoder.cpp:69
OUTBUFSIZE
static constexpr ssize_t OUTBUFSIZE
Definition: audiooutputdigitalencoder.h:12
AudioOutputSettings::SampleSize
static int SampleSize(AudioFormat format)
Definition: audiooutputsettings.cpp:180
AudioOutputDigitalEncoder::clear
void clear()
Definition: audiooutputdigitalencoder.cpp:354
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
AudioOutputDigitalEncoder::m_outbuf
uint8_t * m_outbuf
Definition: audiooutputdigitalencoder.h:34
AudioOutputDigitalEncoder::m_framebuf
uint8_t * m_framebuf
Definition: audiooutputdigitalencoder.h:39
AudioOutputDigitalEncoder::m_inlen
int m_inlen
Definition: audiooutputdigitalencoder.h:43
FFMPEG_SAMPLE_FORMAT
static constexpr AVSampleFormat FFMPEG_SAMPLE_FORMAT
Definition: audiooutputdigitalencoder.cpp:97
AudioOutputDigitalEncoder::m_spdifEnc
SPDIFEncoder * m_spdifEnc
Definition: audiooutputdigitalencoder.h:45
tmp
static guint32 * tmp
Definition: goom_core.cpp:26
audiooutputdigitalencoder.h
mythlogging.h
LOC
#define LOC
Definition: audiooutputdigitalencoder.cpp:20
compat.h
AudioOutputDigitalEncoder::m_inSize
ssize_t m_inSize
Definition: audiooutputdigitalencoder.h:40
FORMAT_FLT
@ FORMAT_FLT
Definition: audiooutputsettings.h:31
CODECNAME
static constexpr const char * CODECNAME
Definition: audiooutputdigitalencoder.cpp:96
AudioOutputDigitalEncoder::Encode
int Encode(void *input, int len, AudioFormat format)
Definition: audiooutputdigitalencoder.cpp:166
FORMAT_S32
@ FORMAT_S32
Definition: audiooutputsettings.h:30
INBUFSIZE
static constexpr ssize_t INBUFSIZE
Definition: audiooutputdigitalencoder.h:11
SPDIFEncoder::GetProcessedSize
int GetProcessedSize()
Definition: spdifencoder.cpp:145
AudioOutputDigitalEncoder::m_avContext
AVCodecContext * m_avContext
Definition: audiooutputdigitalencoder.h:33
SPDIFEncoder::WriteFrame
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...
Definition: spdifencoder.cpp:99
SPDIFEncoder::GetData
int GetData(unsigned char *buffer, size_t &dest_size)
Retrieve encoded data and copy it in the provided buffer.
Definition: spdifencoder.cpp:127
AudioOutputDigitalEncoder::m_samplesPerFrame
int m_samplesPerFrame
Definition: audiooutputdigitalencoder.h:44
mythcorecontext.h
AudioOutputDigitalEncoder::AudioOutputDigitalEncoder
AudioOutputDigitalEncoder(void)
Definition: audiooutputdigitalencoder.cpp:22
MYTH_SAMPLE_FORMAT
static constexpr AudioFormat MYTH_SAMPLE_FORMAT
Definition: audiooutputdigitalencoder.cpp:98
AudioOutputDigitalEncoder::m_inbuf
uint8_t * m_inbuf
Definition: audiooutputdigitalencoder.h:37
AudioOutputUtil::DeinterleaveSamples
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.
Definition: audiooutpututil.cpp:301
AudioOutputUtil::fromFloat
static int fromFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert float samples to integers.
Definition: audiooutpututil.cpp:76
SPDIFEncoder
Definition: spdifencoder.h:14
AudioFormat
AudioFormat
Definition: audiooutputsettings.h:24
AudioOutputDigitalEncoder::m_outSize
ssize_t m_outSize
Definition: audiooutputdigitalencoder.h:35
av_make_error_stdstring
char * av_make_error_stdstring(std::string &errbuf, int errnum)
Definition: mythaverror.cpp:41
AudioOutputDigitalEncoder::Reset
void Reset(void)
Definition: audiooutputdigitalencoder.cpp:56
AudioOutputDigitalEncoder::m_frame
AVFrame * m_frame
Definition: audiooutputdigitalencoder.h:46