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
7extern "C" {
8#include "libavutil/mem.h" // for av_free
9#include "libavcodec/avcodec.h"
10}
11
12// MythTV headers
13#include "libmythbase/compat.h"
15
17#include "audiooutpututil.h"
18#include "mythaverror.h"
19
20#define LOC QString("DEnc: ")
21
23 : m_outbuf(static_cast<uint8_t*>(av_mallocz(OUTBUFSIZE)))
24{
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(reinterpret_cast<void*>(&m_outbuf));
43 m_outSize = 0;
44 }
45 if (m_inbuf)
46 {
47 av_freep(reinterpret_cast<void*>(&m_inbuf));
48 m_inSize = 0;
49 }
50 if (m_framebuf)
51 {
52 av_freep(reinterpret_cast<void*>(&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
92static constexpr const char* CODECNAME { "ac3_fixed" };
93static constexpr AVSampleFormat FFMPEG_SAMPLE_FORMAT { AV_SAMPLE_FMT_S32P };
94static constexpr AudioFormat MYTH_SAMPLE_FORMAT { FORMAT_S32 };
95#else
96static constexpr const char* CODECNAME { "ac3" };
97static 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
166int 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 m_frame = av_frame_alloc();
228 if (m_frame == nullptr)
229 {
230 m_inbuf = nullptr;
231 m_inSize = 0;
232 LOG(VB_AUDIO, LOG_ERR, LOC +
233 "AC-3 encode error, insufficient memory");
234 return m_outlen;
235 }
236 }
237 else
238 {
239 av_frame_unref(m_frame);
240 }
241 m_frame->nb_samples = m_avContext->frame_size;
242 m_frame->pts = AV_NOPTS_VALUE;
243 m_frame->format = m_avContext->sample_fmt;
244 av_channel_layout_copy(&(m_frame->ch_layout), &(m_avContext->ch_layout));
245 m_frame->sample_rate = m_avContext->sample_rate;
246
247 if (frames > 0)
248 {
249 // init AVFrame for planar data (input is interleaved)
250 for (int j = 0, jj = 0; j < channels; j++, jj += size_channel)
251 {
252 m_frame->data[j] = m_framebuf + jj;
253 }
254 }
255
256 while (i < frames)
257 {
258 AVPacket *pkt = av_packet_alloc();
259 if (pkt == nullptr)
260 {
261 LOG(VB_RECORD, LOG_ERR, "packet allocation failed");
262 return AVERROR(ENOMEM);
263 }
264 bool got_packet = false;
265
267 MYTH_SAMPLE_FORMAT, channels,
269 m_inbuf + (static_cast<ptrdiff_t>(i) * size_channel * channels),
270 size_channel * channels);
271
272 // SUGGESTION
273 // Now that avcodec_encode_audio2 is deprecated and replaced
274 // by 2 calls, this could be optimized
275 // into separate routines or separate threads.
276 int ret = avcodec_receive_packet(m_avContext, pkt);
277 if (ret == 0)
278 got_packet = true;
279 if (ret == AVERROR(EAGAIN))
280 ret = 0;
281 if (ret == 0)
282 ret = avcodec_send_frame(m_avContext, m_frame);
283 // if ret from avcodec_send_frame is AVERROR(EAGAIN) then
284 // there are 2 packets to be received while only 1 frame to be
285 // sent. The code does not cater for this. Hopefully it will not happen.
286
287 if (ret < 0)
288 {
289 std::string error;
290 LOG(VB_GENERAL, LOG_ERR, LOC +
291 QString("audio encode error: %1 (%2)")
293 .arg(got_packet));
294 av_packet_free(&pkt);
295 return ret;
296 }
297 i++;
298 if (!got_packet)
299 {
301 continue;
302 }
303 if (!m_spdifEnc)
304 {
305 m_spdifEnc = new SPDIFEncoder("spdif", AV_CODEC_ID_AC3);
306 }
307
308 m_spdifEnc->WriteFrame(pkt->data, pkt->size);
309 av_packet_unref(pkt);
310 av_packet_free(&pkt);
311
312 // Check if output buffer is big enough
313 required_len = m_outlen + m_spdifEnc->GetProcessedSize();
314 if (required_len > m_outSize)
315 {
316 required_len = ((required_len / OUTBUFSIZE) + 1) * OUTBUFSIZE;
317 LOG(VB_AUDIO, LOG_WARNING, LOC +
318 QString("low mem, reallocating out buffer from %1 to %2")
319 .arg(m_outSize) .arg(required_len));
320 auto *tmp = static_cast<uint8_t*>(realloc(m_outbuf, m_outSize, required_len));
321 if (!tmp)
322 {
323 m_outbuf = nullptr;
324 m_outSize = 0;
325 LOG(VB_AUDIO, LOG_ERR, LOC +
326 "AC-3 encode error, insufficient memory");
327 return m_outlen;
328 }
329 m_outbuf = tmp;
330 m_outSize = required_len;
331 }
332 size_t data_size = 0;
333 m_spdifEnc->GetData(m_outbuf + m_outlen, data_size);
334 m_outlen += data_size;
336 }
337
339 return m_outlen;
340}
341
342int AudioOutputDigitalEncoder::GetFrames(void *ptr, int maxlen)
343{
344 int len = std::min(maxlen, m_outlen);
345 if (len != maxlen)
346 {
347 LOG(VB_AUDIO, LOG_INFO, LOC + "GetFrames: getting less than requested");
348 }
349 memcpy(ptr, m_outbuf, len);
350 m_outlen -= len;
351 memmove(m_outbuf, m_outbuf + len, m_outlen);
352 return len;
353}
354
356{
357 m_inlen = m_outlen = 0;
358}
static constexpr AudioFormat MYTH_SAMPLE_FORMAT
static constexpr AVSampleFormat FFMPEG_SAMPLE_FORMAT
static constexpr const char * CODECNAME
static constexpr ssize_t INBUFSIZE
static constexpr ssize_t OUTBUFSIZE
@ FORMAT_S32
@ FORMAT_FLT
bool Init(AVCodecID codec_id, int bitrate, int samplerate, int channels)
int GetFrames(void *ptr, int maxlen)
int Encode(void *input, int len, AudioFormat format)
static void * realloc(void *ptr, size_t old_size, size_t new_size)
static int SampleSize(AudioFormat format)
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.
static int fromFloat(AudioFormat format, void *out, const void *in, int bytes)
Convert float samples to integers.
int GetProcessedSize()
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...
bool Succeeded() const
Definition: spdifencoder.h:24
int GetData(unsigned char *buffer, size_t &dest_size)
Retrieve encoded data and copy it in the provided buffer.
static guint32 * tmp
Definition: goom_core.cpp:26
char * av_make_error_stdstring(std::string &errbuf, int errnum)
A C++ equivalent to av_make_error_string.
Definition: mythaverror.cpp:42
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
def error(message)
Definition: smolt.py:409