[videodb] remove unused seasons table from episode_view
[xbmc.git] / xbmc / cores / AudioEngine / Encoders / AEEncoderFFmpeg.cpp
blobd129e84415595da1091cf5adbe01027b61c099ef
1 /*
2 * Copyright (C) 2010-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #define AC3_ENCODE_BITRATE 640000
10 #define DTS_ENCODE_BITRATE 1411200
12 #include "cores/AudioEngine/Encoders/AEEncoderFFmpeg.h"
14 #include "ServiceBroker.h"
15 #include "cores/AudioEngine/Utils/AEUtil.h"
16 #include "cores/FFmpeg.h"
17 #include "settings/Settings.h"
18 #include "settings/SettingsComponent.h"
19 #include "utils/log.h"
21 extern "C"
23 #include <libavutil/channel_layout.h>
26 #include <cassert>
27 #include <string.h>
29 using FFMPEG_HELP_TOOLS::FFMpegErrorToString;
30 using FFMPEG_HELP_TOOLS::FFMpegException;
32 CAEEncoderFFmpeg::CAEEncoderFFmpeg() : m_CodecCtx(NULL), m_SwrCtx(NULL)
36 CAEEncoderFFmpeg::~CAEEncoderFFmpeg()
38 Reset();
39 swr_free(&m_SwrCtx);
40 av_channel_layout_uninit(&m_CodecCtx->ch_layout);
41 avcodec_free_context(&m_CodecCtx);
44 bool CAEEncoderFFmpeg::IsCompatible(const AEAudioFormat& format)
46 if (!m_CodecCtx)
47 return false;
49 bool match = (
50 format.m_dataFormat == m_CurrentFormat.m_dataFormat &&
51 format.m_sampleRate == m_CurrentFormat.m_sampleRate
54 if (match)
56 CAEChannelInfo layout;
57 BuildChannelLayout(AV_CH_LAYOUT_5POINT1_BACK, layout); /* hard coded for AC3 & DTS currently */
58 match = (m_CurrentFormat.m_channelLayout == layout);
61 return match;
64 unsigned int CAEEncoderFFmpeg::BuildChannelLayout(const int64_t ffmap, CAEChannelInfo& layout)
66 /* build the channel layout and count the channels */
67 layout.Reset();
68 if (ffmap & AV_CH_FRONT_LEFT ) layout += AE_CH_FL ;
69 if (ffmap & AV_CH_FRONT_RIGHT ) layout += AE_CH_FR ;
70 if (ffmap & AV_CH_FRONT_CENTER ) layout += AE_CH_FC ;
71 if (ffmap & AV_CH_LOW_FREQUENCY ) layout += AE_CH_LFE ;
72 if (ffmap & AV_CH_BACK_LEFT ) layout += AE_CH_BL ;
73 if (ffmap & AV_CH_BACK_RIGHT ) layout += AE_CH_BR ;
74 if (ffmap & AV_CH_FRONT_LEFT_OF_CENTER ) layout += AE_CH_FLOC;
75 if (ffmap & AV_CH_FRONT_RIGHT_OF_CENTER) layout += AE_CH_FROC;
76 if (ffmap & AV_CH_BACK_CENTER ) layout += AE_CH_BC ;
77 if (ffmap & AV_CH_SIDE_LEFT ) layout += AE_CH_SL ;
78 if (ffmap & AV_CH_SIDE_RIGHT ) layout += AE_CH_SR ;
79 if (ffmap & AV_CH_TOP_CENTER ) layout += AE_CH_TC ;
80 if (ffmap & AV_CH_TOP_FRONT_LEFT ) layout += AE_CH_TFL ;
81 if (ffmap & AV_CH_TOP_FRONT_CENTER ) layout += AE_CH_TFC ;
82 if (ffmap & AV_CH_TOP_FRONT_RIGHT ) layout += AE_CH_TFR ;
83 if (ffmap & AV_CH_TOP_BACK_LEFT ) layout += AE_CH_TBL ;
84 if (ffmap & AV_CH_TOP_BACK_CENTER ) layout += AE_CH_TBC ;
85 if (ffmap & AV_CH_TOP_BACK_RIGHT ) layout += AE_CH_TBR ;
87 return layout.Count();
90 bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format, bool allow_planar_input)
92 Reset();
94 bool ac3 = CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_AUDIOOUTPUT_AC3PASSTHROUGH);
96 const AVCodec* codec = nullptr;
98 /* fallback to ac3 if we support it, we might not have DTS support */
99 if (ac3)
101 m_CodecName = "AC3";
102 m_CodecID = AV_CODEC_ID_AC3;
103 m_BitRate = AC3_ENCODE_BITRATE;
104 codec = avcodec_find_encoder(m_CodecID);
107 /* check we got the codec */
108 if (!codec)
109 return false;
111 m_CodecCtx = avcodec_alloc_context3(codec);
112 if (!m_CodecCtx)
113 return false;
115 m_CodecCtx->bit_rate = m_BitRate;
116 m_CodecCtx->sample_rate = format.m_sampleRate;
117 av_channel_layout_uninit(&m_CodecCtx->ch_layout);
118 av_channel_layout_from_mask(&m_CodecCtx->ch_layout, AV_CH_LAYOUT_5POINT1_BACK);
120 /* select a suitable data format */
121 if (codec->sample_fmts)
123 bool hasFloat = false;
124 bool hasDouble = false;
125 bool hasS32 = false;
126 bool hasS16 = false;
127 bool hasU8 = false;
128 bool hasFloatP = false;
129 bool hasUnknownFormat = false;
131 for(int i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i)
133 switch (codec->sample_fmts[i])
135 case AV_SAMPLE_FMT_FLT: hasFloat = true; break;
136 case AV_SAMPLE_FMT_DBL: hasDouble = true; break;
137 case AV_SAMPLE_FMT_S32: hasS32 = true; break;
138 case AV_SAMPLE_FMT_S16: hasS16 = true; break;
139 case AV_SAMPLE_FMT_U8 : hasU8 = true; break;
140 case AV_SAMPLE_FMT_FLTP:
141 if (allow_planar_input)
142 hasFloatP = true;
143 else
144 hasUnknownFormat = true;
145 break;
146 case AV_SAMPLE_FMT_NONE: return false;
147 default: hasUnknownFormat = true; break;
151 if (hasFloat)
153 m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_FLT;
154 format.m_dataFormat = AE_FMT_FLOAT;
156 else if (hasFloatP)
158 m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_FLTP;
159 format.m_dataFormat = AE_FMT_FLOATP;
161 else if (hasDouble)
163 m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_DBL;
164 format.m_dataFormat = AE_FMT_DOUBLE;
166 else if (hasS32)
168 m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S32;
169 format.m_dataFormat = AE_FMT_S32NE;
171 else if (hasS16)
173 m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;
174 format.m_dataFormat = AE_FMT_S16NE;
176 else if (hasU8)
178 m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
179 format.m_dataFormat = AE_FMT_U8;
181 else if (hasUnknownFormat)
183 m_CodecCtx->sample_fmt = codec->sample_fmts[0];
184 format.m_dataFormat = AE_FMT_FLOAT;
185 m_NeedConversion = true;
186 CLog::Log(LOGINFO,
187 "CAEEncoderFFmpeg::Initialize - Unknown audio format, it will be resampled.");
189 else
191 CLog::Log(
192 LOGERROR,
193 "CAEEncoderFFmpeg::Initialize - Unable to find a suitable data format for the codec ({})",
194 m_CodecName);
195 av_channel_layout_uninit(&m_CodecCtx->ch_layout);
196 avcodec_free_context(&m_CodecCtx);
197 return false;
201 uint64_t mask = m_CodecCtx->ch_layout.u.mask;
202 av_channel_layout_uninit(&m_CodecCtx->ch_layout);
203 av_channel_layout_from_mask(&m_CodecCtx->ch_layout, mask);
204 m_CodecCtx->ch_layout.nb_channels = BuildChannelLayout(mask, m_Layout);
206 /* open the codec */
207 if (avcodec_open2(m_CodecCtx, codec, NULL))
209 av_channel_layout_uninit(&m_CodecCtx->ch_layout);
210 avcodec_free_context(&m_CodecCtx);
211 return false;
214 format.m_frames = m_CodecCtx->frame_size;
215 int channels = m_CodecCtx->ch_layout.nb_channels;
216 format.m_frameSize = channels * (CAEUtil::DataFormatToBits(format.m_dataFormat) >> 3);
217 format.m_channelLayout = m_Layout;
219 m_CurrentFormat = format;
220 m_NeededFrames = format.m_frames;
221 m_OutputRatio = (double)m_NeededFrames / m_OutputSize;
222 m_SampleRateMul = 1.0 / (double)m_CodecCtx->sample_rate;
224 if (m_NeedConversion)
226 int ret = swr_alloc_set_opts2(&m_SwrCtx, &m_CodecCtx->ch_layout, m_CodecCtx->sample_fmt,
227 m_CodecCtx->sample_rate, &m_CodecCtx->ch_layout,
228 AV_SAMPLE_FMT_FLT, m_CodecCtx->sample_rate, 0, NULL);
229 if (ret || swr_init(m_SwrCtx) < 0)
231 CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Initialize - Failed to initialise resampler.");
232 swr_free(&m_SwrCtx);
233 av_channel_layout_uninit(&m_CodecCtx->ch_layout);
234 avcodec_free_context(&m_CodecCtx);
235 return false;
238 CLog::Log(LOGINFO, "CAEEncoderFFmpeg::Initialize - {} encoder ready", m_CodecName);
239 return true;
242 void CAEEncoderFFmpeg::Reset()
244 m_BufferSize = 0;
247 unsigned int CAEEncoderFFmpeg::GetBitRate()
249 return m_BitRate;
252 AVCodecID CAEEncoderFFmpeg::GetCodecID()
254 return m_CodecID;
257 unsigned int CAEEncoderFFmpeg::GetFrames()
259 return m_NeededFrames;
262 int CAEEncoderFFmpeg::Encode(uint8_t *in, int in_size, uint8_t *out, int out_size)
264 int size = 0;
265 int err = AVERROR_UNKNOWN;
266 AVFrame* frame = nullptr;
267 AVPacket* pkt = nullptr;
269 if (!m_CodecCtx)
270 return size;
274 /* allocate the input frame and output packet
275 * sadly, we have to alloc/dealloc it everytime since we have no guarantee the
276 * data argument will be constant over iterated calls and the frame needs to
277 * setup pointers inside data */
278 frame = av_frame_alloc();
279 pkt = av_packet_alloc();
280 if (!frame || !pkt)
281 throw FFMpegException(
282 "Failed to allocate \"AVFrame\" or \"AVPacket\" for encoding (error '{}')",
283 strerror(errno));
285 frame->nb_samples = m_CodecCtx->frame_size;
286 frame->format = m_CodecCtx->sample_fmt;
287 av_channel_layout_uninit(&frame->ch_layout);
288 av_channel_layout_copy(&frame->ch_layout, &m_CodecCtx->ch_layout);
289 int channelNum = m_CodecCtx->ch_layout.nb_channels;
291 avcodec_fill_audio_frame(frame, channelNum, m_CodecCtx->sample_fmt, in, in_size, 0);
293 /* encode it */
294 err = avcodec_send_frame(m_CodecCtx, frame);
295 if (err < 0)
296 throw FFMpegException("Error sending a frame for encoding (error '{}')",
297 FFMpegErrorToString(err));
299 err = avcodec_receive_packet(m_CodecCtx, pkt);
300 //! @TODO: This is a workaround for our current design. The caller should be made
301 // aware of the potential error values to use the ffmpeg API in a proper way, which means
302 // copying with EAGAIN and multiple packet output.
303 // For the current situation there is a relationship implicitely assumed of:
304 // 1 frame in - 1 packet out. This holds true in practice but the API does not guarantee it.
305 if (err >= 0)
307 if (pkt->size <= out_size)
309 memset(out, 0, out_size);
310 memcpy(out, pkt->data, pkt->size);
311 size = pkt->size;
313 else
315 CLog::LogF(LOGERROR, "Encoded pkt size ({}) is bigger than buffer ({})", pkt->size,
316 out_size);
318 av_packet_unref(pkt);
320 else
322 CLog::LogF(LOGERROR, "Error receiving encoded paket ({})", err);
325 catch (const FFMpegException& caught)
327 CLog::Log(LOGERROR, "CAEEncoderFFmpeg::{} - {}", __func__, caught.what());
330 av_channel_layout_uninit(&frame->ch_layout);
332 /* free temporary data */
333 av_frame_free(&frame);
335 /* free the packet */
336 av_packet_free(&pkt);
338 /* return the number of frames used */
339 return size;
342 int CAEEncoderFFmpeg::GetData(uint8_t **data)
344 int size;
345 *data = m_Buffer;
346 size = m_BufferSize;
347 m_BufferSize = 0;
348 return size;
351 double CAEEncoderFFmpeg::GetDelay(unsigned int bufferSize)
353 if (!m_CodecCtx)
354 return 0;
356 int frames = m_CodecCtx->delay;
357 if (m_BufferSize)
358 frames += m_NeededFrames;
360 return ((double)frames + ((double)bufferSize * m_OutputRatio)) * m_SampleRateMul;