Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #2657 from aballier/aacoutput

Fix AAC/AC3 recoding output over SPDIF / HDMI with ffmpeg-1.1 and later (eg, every platform that uses internal ffmpeg)
  • Loading branch information...
commit b73bd86de8661772091f1406ac0722aaf8427737 2 parents 976f09f + 72f2836
@Voyager1 Voyager1 authored
View
12 lib/DllAvCodec.h
@@ -87,6 +87,7 @@ class DllAvCodecInterface
virtual int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt)=0;
virtual int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt)=0;
virtual int avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size, const short *samples)=0;
+ virtual int avcodec_encode_audio2(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr)=0;
virtual int avpicture_get_size(PixelFormat pix_fmt, int width, int height)=0;
virtual AVCodecContext *avcodec_alloc_context3(AVCodec *codec)=0;
virtual void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)=0;
@@ -111,6 +112,8 @@ class DllAvCodecInterface
virtual AVCodec *av_codec_next(AVCodec *c)=0;
virtual int av_dup_packet(AVPacket *pkt)=0;
virtual void av_init_packet(AVPacket *pkt)=0;
+ virtual int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, enum AVSampleFormat sample_fmt, const uint8_t *buf, int buf_size, int align) = 0;
+ virtual void avcodec_free_frame(AVFrame **frame)=0;
};
#if (defined USE_EXTERNAL_FFMPEG) || (defined TARGET_DARWIN)
@@ -148,6 +151,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
virtual int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt) { return ::avcodec_decode_audio4(avctx, frame, got_frame_ptr, avpkt); }
virtual int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt) { return ::avcodec_decode_subtitle2(avctx, sub, got_sub_ptr, avpkt); }
virtual int avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size, const short *samples) { return ::avcodec_encode_audio(avctx, buf, buf_size, samples); }
+ virtual int avcodec_encode_audio2(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr) { return ::avcodec_encode_audio2(avctx, avpkt, frame, got_packet_ptr); }
virtual int avpicture_get_size(PixelFormat pix_fmt, int width, int height) { return ::avpicture_get_size(pix_fmt, width, height); }
virtual AVCodecContext *avcodec_alloc_context3(AVCodec *codec) { return ::avcodec_alloc_context3(codec); }
virtual void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode) { ::avcodec_string(buf, buf_size, enc, encode); }
@@ -179,6 +183,8 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
virtual int av_dup_packet(AVPacket *pkt) { return ::av_dup_packet(pkt); }
virtual void av_init_packet(AVPacket *pkt) { return ::av_init_packet(pkt); }
+ virtual int avcodec_fill_audio_frame(AVFrame *frame, int nb_channels, enum AVSampleFormat sample_fmt, const uint8_t *buf, int buf_size, int align) { return ::avcodec_fill_audio_frame(frame, nb_channels, sample_fmt, buf, buf_size, align); }
+ virtual void avcodec_free_frame(AVFrame **frame) { return ::avcodec_free_frame(frame); };
// DLL faking.
virtual bool ResolveExports() { return true; }
@@ -200,6 +206,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_audio4, AVCodecContext*, AVFrame*, int*, AVPacket*)
DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_subtitle2, AVCodecContext*, AVSubtitle*, int*, AVPacket*)
DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_encode_audio, AVCodecContext*, uint8_t*, int, const short*)
+ DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_encode_audio2, AVCodecContext*, AVPacket*, const AVFrame*, int*)
DEFINE_FUNC_ALIGNED1(AVCodecContext*, __cdecl, avcodec_alloc_context3, AVCodec *)
DEFINE_FUNC_ALIGNED1(AVCodecParserContext*, __cdecl, av_parser_init, int)
DEFINE_FUNC_ALIGNED9(int, __cdecl, av_parser_parse2, AVCodecParserContext*,AVCodecContext*, uint8_t**, int*, const uint8_t*, int, int64_t, int64_t, int64_t)
@@ -227,6 +234,8 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
DEFINE_METHOD2(int, avcodec_default_get_buffer, (AVCodecContext *p1, AVFrame *p2))
DEFINE_METHOD2(void, avcodec_default_release_buffer, (AVCodecContext *p1, AVFrame *p2))
DEFINE_METHOD2(enum PixelFormat, avcodec_default_get_format, (struct AVCodecContext *p1, const enum PixelFormat *p2))
+ DEFINE_METHOD6(int, avcodec_fill_audio_frame, (AVFrame* p1, int p2, enum AVSampleFormat p3, const uint8_t* p4, int p5, int p6))
+ DEFINE_METHOD1(void, avcodec_free_frame, (AVFrame **p1))
DEFINE_METHOD1(AVCodec*, av_codec_next, (AVCodec *p1))
BEGIN_METHOD_RESOLVE()
@@ -242,6 +251,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
RESOLVE_METHOD(avcodec_decode_audio4)
RESOLVE_METHOD(avcodec_decode_subtitle2)
RESOLVE_METHOD(avcodec_encode_audio)
+ RESOLVE_METHOD(avcodec_encode_audio2)
RESOLVE_METHOD(avpicture_get_size)
RESOLVE_METHOD(avcodec_alloc_context3)
RESOLVE_METHOD(avcodec_string)
@@ -261,6 +271,8 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
RESOLVE_METHOD(av_codec_next)
RESOLVE_METHOD(av_dup_packet)
RESOLVE_METHOD(av_init_packet)
+ RESOLVE_METHOD(avcodec_fill_audio_frame)
+ RESOLVE_METHOD(avcodec_free_frame)
END_METHOD_RESOLVE()
/* dependencies of libavcodec */
View
109 xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
@@ -31,11 +31,15 @@
CAEEncoderFFmpeg::CAEEncoderFFmpeg():
m_BitRate (0 ),
m_CodecCtx (NULL ),
+ m_SwrCtx (NULL ),
m_BufferSize (0 ),
m_OutputSize (0 ),
m_OutputRatio (0.0 ),
m_SampleRateMul (0.0 ),
- m_NeededFrames (0 )
+ m_NeededFrames (0 ),
+ m_NeedConversion(false),
+ m_ResampBuffer (NULL ),
+ m_ResampBufferSize(0 )
{
}
@@ -43,6 +47,9 @@ CAEEncoderFFmpeg::~CAEEncoderFFmpeg()
{
Reset();
m_dllAvUtil.av_freep(&m_CodecCtx);
+ m_dllAvUtil.av_freep(&m_ResampBuffer);
+ if (m_SwrCtx)
+ m_dllSwResample.swr_free(&m_SwrCtx);
}
bool CAEEncoderFFmpeg::IsCompatible(AEAudioFormat format)
@@ -95,7 +102,7 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
{
Reset();
- if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load())
+ if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllSwResample.Load())
return false;
m_dllAvCodec.avcodec_register_all();
@@ -143,6 +150,7 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
bool hasS32 = false;
bool hasS16 = false;
bool hasU8 = false;
+ bool hasUnknownFormat = false;
for(int i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i)
{
@@ -153,9 +161,8 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
case AV_SAMPLE_FMT_S32: hasS32 = true; break;
case AV_SAMPLE_FMT_S16: hasS16 = true; break;
case AV_SAMPLE_FMT_U8 : hasU8 = true; break;
-
- default:
- return false;
+ case AV_SAMPLE_FMT_NONE: return false;
+ default: hasUnknownFormat = true; break;
}
}
@@ -184,6 +191,13 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
format.m_dataFormat = AE_FMT_U8;
}
+ else if (hasUnknownFormat)
+ {
+ m_CodecCtx->sample_fmt = codec->sample_fmts[0];
+ format.m_dataFormat = AE_FMT_FLOAT;
+ m_NeedConversion = true;
+ CLog::Log(LOGNOTICE, "CAEEncoderFFmpeg::Initialize - Unknown audio format, it will be resampled.");
+ }
else
{
CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Initialize - Unable to find a suitable data format for the codec (%s)", m_CodecName.c_str());
@@ -212,6 +226,18 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
m_OutputRatio = (double)m_NeededFrames / m_OutputSize;
m_SampleRateMul = 1.0 / (double)m_CodecCtx->sample_rate;
+ if (m_NeedConversion)
+ {
+ m_SwrCtx = m_dllSwResample.swr_alloc_set_opts(NULL,
+ m_CodecCtx->channel_layout, m_CodecCtx->sample_fmt, m_CodecCtx->sample_rate,
+ m_CodecCtx->channel_layout, AV_SAMPLE_FMT_FLT, m_CodecCtx->sample_rate,
+ 0, NULL);
+ if (!m_SwrCtx || m_dllSwResample.swr_init(m_SwrCtx) < 0)
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Initialize - Failed to initialise resampler.");
+ return false;
+ }
+ }
CLog::Log(LOGNOTICE, "CAEEncoderFFmpeg::Initialize - %s encoder ready", m_CodecName.c_str());
return true;
}
@@ -238,20 +264,89 @@ unsigned int CAEEncoderFFmpeg::GetFrames()
int CAEEncoderFFmpeg::Encode(float *data, unsigned int frames)
{
+ int got_output;
+ AVFrame *frame;
+ const uint8_t *input = (const uint8_t*) data;
+
if (!m_CodecCtx || frames < m_NeededFrames)
return 0;
+ /* size of the buffer sent to the encoder: either from the input data or after
+ * conversion, in all cases it is in the m_CodecCtx->sample_fmt format */
+ int buf_size = m_dllAvUtil.av_samples_get_buffer_size(NULL, m_CodecCtx->channels, frames, m_CodecCtx->sample_fmt, 0);
+ assert(buf_size>0);
+
+ /* allocate the input frame
+ * sadly, we have to alloc/dealloc it everytime since we have no guarantee the
+ * data argument will be constant over iterated calls and the frame needs to
+ * setup pointers inside data */
+ frame = m_dllAvCodec.avcodec_alloc_frame();
+ if (!frame)
+ return 0;
+
+ frame->nb_samples = m_CodecCtx->frame_size;
+ frame->format = m_CodecCtx->sample_fmt;
+ frame->channel_layout = m_CodecCtx->channel_layout;
+
+ if (m_NeedConversion)
+ {
+ if (!m_ResampBuffer || buf_size > m_ResampBufferSize)
+ {
+ m_ResampBuffer = (uint8_t*)m_dllAvUtil.av_realloc(m_ResampBuffer, buf_size);
+ if (!m_ResampBuffer)
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Failed to allocate %i bytes buffer for resampling", buf_size);
+ m_dllAvCodec.avcodec_free_frame(&frame);
+ return 0;
+ }
+ m_ResampBufferSize = buf_size;
+ }
+
+ m_dllAvCodec.avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt, m_ResampBuffer, buf_size, 0);
+
+ /* important note: the '&input' here works because we convert from a packed
+ * format (ie, interleaved). If it were to be used to convert from planar
+ * formats (ie, non-interleaved, which is not currently supported by AE),
+ * we would need to adapt it or it would segfault. */
+ if (m_dllSwResample.swr_convert(m_SwrCtx, frame->extended_data, frames, &input, frames) < 0)
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Resampling failed");
+ m_dllAvCodec.avcodec_free_frame(&frame);
+ return 0;
+ }
+ }
+ else
+ m_dllAvCodec.avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt,
+ input, buf_size, 0);
+
+ /* initialize the output packet */
+ m_dllAvCodec.av_init_packet(&m_Pkt);
+ m_Pkt.size = sizeof(m_Buffer) - IEC61937_DATA_OFFSET;
+ m_Pkt.data = m_Buffer + IEC61937_DATA_OFFSET;
+
/* encode it */
- int size = m_dllAvCodec.avcodec_encode_audio(m_CodecCtx, m_Buffer + IEC61937_DATA_OFFSET, FF_MIN_BUFFER_SIZE, (short*)data);
+ int ret = m_dllAvCodec.avcodec_encode_audio2(m_CodecCtx, &m_Pkt, frame, &got_output);
+
+ /* free temporary data */
+ m_dllAvCodec.avcodec_free_frame(&frame);
+
+ if (ret < 0 || !got_output)
+ {
+ CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Encoding failed");
+ return 0;
+ }
/* pack it into an IEC958 frame */
- m_BufferSize = m_PackFunc(NULL, size, m_Buffer);
+ m_BufferSize = m_PackFunc(NULL, m_Pkt.size, m_Buffer);
if (m_BufferSize != m_OutputSize)
{
m_OutputSize = m_BufferSize;
m_OutputRatio = (double)m_NeededFrames / m_OutputSize;
}
+ /* free the packet */
+ m_dllAvCodec.av_free_packet(&m_Pkt);
+
/* return the number of frames used */
return m_NeededFrames;
}
View
8 xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h
@@ -27,6 +27,7 @@
#undef restrict
#include "DllAvCodec.h"
#include "DllAvFormat.h"
+#include "DllSwResample.h"
class CAEEncoderFFmpeg: public IAEEncoder
{
@@ -49,6 +50,7 @@ class CAEEncoderFFmpeg: public IAEEncoder
DllAvCodec m_dllAvCodec;
DllAvFormat m_dllAvFormat;
DllAvUtil m_dllAvUtil;
+ DllSwResample m_dllSwResample;
std::string m_CodecName;
CodecID m_CodecID;
@@ -57,7 +59,9 @@ class CAEEncoderFFmpeg: public IAEEncoder
AEAudioFormat m_CurrentFormat;
AVCodecContext *m_CodecCtx;
+ SwrContext *m_SwrCtx;
CAEChannelInfo m_Layout;
+ AVPacket m_Pkt;
uint8_t m_Buffer[IEC61937_DATA_OFFSET + FF_MIN_BUFFER_SIZE];
int m_BufferSize;
int m_OutputSize;
@@ -67,5 +71,9 @@ class CAEEncoderFFmpeg: public IAEEncoder
unsigned int m_NeededFrames;
unsigned int BuildChannelLayout(const int64_t ffmap, CAEChannelInfo& layout);
+
+ bool m_NeedConversion;
+ uint8_t *m_ResampBuffer;
+ int m_ResampBufferSize;
};
Please sign in to comment.
Something went wrong with that request. Please try again.