Skip to content
This repository
Browse code

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 authored May 06, 2013
12  lib/DllAvCodec.h
@@ -87,6 +87,7 @@ class DllAvCodecInterface
87 87
   virtual int avcodec_decode_audio4(AVCodecContext *avctx, AVFrame *frame, int *got_frame_ptr, AVPacket *avpkt)=0;
88 88
   virtual int avcodec_decode_subtitle2(AVCodecContext *avctx, AVSubtitle *sub, int *got_sub_ptr, AVPacket *avpkt)=0;
89 89
   virtual int avcodec_encode_audio(AVCodecContext *avctx, uint8_t *buf, int buf_size, const short *samples)=0;
  90
+  virtual int avcodec_encode_audio2(AVCodecContext *avctx, AVPacket *avpkt, const AVFrame *frame, int *got_packet_ptr)=0;
90 91
   virtual int avpicture_get_size(PixelFormat pix_fmt, int width, int height)=0;
91 92
   virtual AVCodecContext *avcodec_alloc_context3(AVCodec *codec)=0;
92 93
   virtual void avcodec_string(char *buf, int buf_size, AVCodecContext *enc, int encode)=0;
@@ -111,6 +112,8 @@ class DllAvCodecInterface
111 112
   virtual AVCodec *av_codec_next(AVCodec *c)=0;
112 113
   virtual int av_dup_packet(AVPacket *pkt)=0;
113 114
   virtual void av_init_packet(AVPacket *pkt)=0;
  115
+  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;
  116
+  virtual void avcodec_free_frame(AVFrame **frame)=0;
114 117
 };
115 118
 
116 119
 #if (defined USE_EXTERNAL_FFMPEG) || (defined TARGET_DARWIN)
@@ -148,6 +151,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
148 151
   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); }
149 152
   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); }
150 153
   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); }
  154
+  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); }
151 155
   virtual int avpicture_get_size(PixelFormat pix_fmt, int width, int height) { return ::avpicture_get_size(pix_fmt, width, height); }
152 156
   virtual AVCodecContext *avcodec_alloc_context3(AVCodec *codec) { return ::avcodec_alloc_context3(codec); }
153 157
   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
179 183
 
180 184
   virtual int av_dup_packet(AVPacket *pkt) { return ::av_dup_packet(pkt); }
181 185
   virtual void av_init_packet(AVPacket *pkt) { return ::av_init_packet(pkt); }
  186
+  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); }
  187
+  virtual void avcodec_free_frame(AVFrame **frame) { return ::avcodec_free_frame(frame); };
182 188
 
183 189
   // DLL faking.
184 190
   virtual bool ResolveExports() { return true; }
@@ -200,6 +206,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
200 206
   DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_audio4, AVCodecContext*, AVFrame*, int*, AVPacket*)
201 207
   DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_decode_subtitle2, AVCodecContext*, AVSubtitle*, int*, AVPacket*)
202 208
   DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_encode_audio, AVCodecContext*, uint8_t*, int, const short*)
  209
+  DEFINE_FUNC_ALIGNED4(int, __cdecl, avcodec_encode_audio2, AVCodecContext*, AVPacket*, const AVFrame*, int*)
203 210
   DEFINE_FUNC_ALIGNED1(AVCodecContext*, __cdecl, avcodec_alloc_context3, AVCodec *)
204 211
   DEFINE_FUNC_ALIGNED1(AVCodecParserContext*, __cdecl, av_parser_init, int)
205 212
   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
227 234
   DEFINE_METHOD2(int, avcodec_default_get_buffer, (AVCodecContext *p1, AVFrame *p2))
228 235
   DEFINE_METHOD2(void, avcodec_default_release_buffer, (AVCodecContext *p1, AVFrame *p2))
229 236
   DEFINE_METHOD2(enum PixelFormat, avcodec_default_get_format, (struct AVCodecContext *p1, const enum PixelFormat *p2))
  237
+  DEFINE_METHOD6(int, avcodec_fill_audio_frame, (AVFrame* p1, int p2, enum AVSampleFormat p3, const uint8_t* p4, int p5, int p6))
  238
+  DEFINE_METHOD1(void, avcodec_free_frame, (AVFrame **p1))
230 239
 
231 240
   DEFINE_METHOD1(AVCodec*, av_codec_next, (AVCodec *p1))
232 241
   BEGIN_METHOD_RESOLVE()
@@ -242,6 +251,7 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
242 251
     RESOLVE_METHOD(avcodec_decode_audio4)
243 252
     RESOLVE_METHOD(avcodec_decode_subtitle2)
244 253
     RESOLVE_METHOD(avcodec_encode_audio)
  254
+    RESOLVE_METHOD(avcodec_encode_audio2)
245 255
     RESOLVE_METHOD(avpicture_get_size)
246 256
     RESOLVE_METHOD(avcodec_alloc_context3)
247 257
     RESOLVE_METHOD(avcodec_string)
@@ -261,6 +271,8 @@ class DllAvCodec : public DllDynamic, DllAvCodecInterface
261 271
     RESOLVE_METHOD(av_codec_next)
262 272
     RESOLVE_METHOD(av_dup_packet)
263 273
     RESOLVE_METHOD(av_init_packet)
  274
+    RESOLVE_METHOD(avcodec_fill_audio_frame)
  275
+    RESOLVE_METHOD(avcodec_free_frame)
264 276
   END_METHOD_RESOLVE()
265 277
 
266 278
   /* dependencies of libavcodec */
109  xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.cpp
@@ -31,11 +31,15 @@
31 31
 CAEEncoderFFmpeg::CAEEncoderFFmpeg():
32 32
   m_BitRate       (0    ),
33 33
   m_CodecCtx      (NULL ),
  34
+  m_SwrCtx        (NULL ),
34 35
   m_BufferSize    (0    ),
35 36
   m_OutputSize    (0    ),
36 37
   m_OutputRatio   (0.0  ),
37 38
   m_SampleRateMul (0.0  ),
38  
-  m_NeededFrames  (0    )
  39
+  m_NeededFrames  (0    ),
  40
+  m_NeedConversion(false),
  41
+  m_ResampBuffer  (NULL ),
  42
+  m_ResampBufferSize(0  )
39 43
 {
40 44
 }
41 45
 
@@ -43,6 +47,9 @@ CAEEncoderFFmpeg::~CAEEncoderFFmpeg()
43 47
 {
44 48
   Reset();
45 49
   m_dllAvUtil.av_freep(&m_CodecCtx);
  50
+  m_dllAvUtil.av_freep(&m_ResampBuffer);
  51
+  if (m_SwrCtx)
  52
+    m_dllSwResample.swr_free(&m_SwrCtx);
46 53
 }
47 54
 
48 55
 bool CAEEncoderFFmpeg::IsCompatible(AEAudioFormat format)
@@ -95,7 +102,7 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
95 102
 {
96 103
   Reset();
97 104
 
98  
-  if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load())
  105
+  if (!m_dllAvUtil.Load() || !m_dllAvCodec.Load() || !m_dllSwResample.Load())
99 106
     return false;
100 107
 
101 108
   m_dllAvCodec.avcodec_register_all();
@@ -143,6 +150,7 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
143 150
     bool hasS32    = false;
144 151
     bool hasS16    = false;
145 152
     bool hasU8     = false;
  153
+    bool hasUnknownFormat = false;
146 154
 
147 155
     for(int i = 0; codec->sample_fmts[i] != AV_SAMPLE_FMT_NONE; ++i)
148 156
     {
@@ -153,9 +161,8 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
153 161
         case AV_SAMPLE_FMT_S32: hasS32    = true; break;
154 162
         case AV_SAMPLE_FMT_S16: hasS16    = true; break;
155 163
         case AV_SAMPLE_FMT_U8 : hasU8     = true; break;
156  
-
157  
-        default:
158  
-          return false;
  164
+        case AV_SAMPLE_FMT_NONE: return false;
  165
+        default: hasUnknownFormat = true; break;
159 166
       }
160 167
     }
161 168
 
@@ -184,6 +191,13 @@ bool CAEEncoderFFmpeg::Initialize(AEAudioFormat &format)
184 191
       m_CodecCtx->sample_fmt = AV_SAMPLE_FMT_U8;
185 192
       format.m_dataFormat    = AE_FMT_U8;
186 193
     }
  194
+    else if (hasUnknownFormat)
  195
+    {
  196
+      m_CodecCtx->sample_fmt = codec->sample_fmts[0];
  197
+      format.m_dataFormat    = AE_FMT_FLOAT;
  198
+      m_NeedConversion       = true;
  199
+      CLog::Log(LOGNOTICE, "CAEEncoderFFmpeg::Initialize - Unknown audio format, it will be resampled.");
  200
+    }
187 201
     else
188 202
     {
189 203
       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)
212 226
   m_OutputRatio   = (double)m_NeededFrames / m_OutputSize;
213 227
   m_SampleRateMul = 1.0 / (double)m_CodecCtx->sample_rate;
214 228
 
  229
+  if (m_NeedConversion)
  230
+  {
  231
+    m_SwrCtx = m_dllSwResample.swr_alloc_set_opts(NULL,
  232
+                      m_CodecCtx->channel_layout, m_CodecCtx->sample_fmt, m_CodecCtx->sample_rate,
  233
+                      m_CodecCtx->channel_layout, AV_SAMPLE_FMT_FLT, m_CodecCtx->sample_rate,
  234
+                      0, NULL);
  235
+    if (!m_SwrCtx || m_dllSwResample.swr_init(m_SwrCtx) < 0)
  236
+    {
  237
+      CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Initialize - Failed to initialise resampler.");
  238
+      return false;
  239
+    }
  240
+  }
215 241
   CLog::Log(LOGNOTICE, "CAEEncoderFFmpeg::Initialize - %s encoder ready", m_CodecName.c_str());
216 242
   return true;
217 243
 }
@@ -238,20 +264,89 @@ unsigned int CAEEncoderFFmpeg::GetFrames()
238 264
 
239 265
 int CAEEncoderFFmpeg::Encode(float *data, unsigned int frames)
240 266
 {
  267
+  int got_output;
  268
+  AVFrame *frame;
  269
+  const uint8_t *input = (const uint8_t*) data;
  270
+
241 271
   if (!m_CodecCtx || frames < m_NeededFrames)
242 272
     return 0;
243 273
 
  274
+  /* size of the buffer sent to the encoder: either from the input data or after
  275
+   * conversion, in all cases it is in the m_CodecCtx->sample_fmt format */
  276
+  int buf_size = m_dllAvUtil.av_samples_get_buffer_size(NULL, m_CodecCtx->channels, frames, m_CodecCtx->sample_fmt, 0);
  277
+  assert(buf_size>0);
  278
+
  279
+  /* allocate the input frame
  280
+   * sadly, we have to alloc/dealloc it everytime since we have no guarantee the
  281
+   * data argument will be constant over iterated calls and the frame needs to
  282
+   * setup pointers inside data */
  283
+  frame = m_dllAvCodec.avcodec_alloc_frame();
  284
+  if (!frame)
  285
+    return 0;
  286
+
  287
+  frame->nb_samples     = m_CodecCtx->frame_size;
  288
+  frame->format         = m_CodecCtx->sample_fmt;
  289
+  frame->channel_layout = m_CodecCtx->channel_layout;
  290
+
  291
+  if (m_NeedConversion)
  292
+  {
  293
+    if (!m_ResampBuffer || buf_size > m_ResampBufferSize)
  294
+    {
  295
+      m_ResampBuffer = (uint8_t*)m_dllAvUtil.av_realloc(m_ResampBuffer, buf_size);
  296
+      if (!m_ResampBuffer)
  297
+      {
  298
+        CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Failed to allocate %i bytes buffer for resampling", buf_size);
  299
+        m_dllAvCodec.avcodec_free_frame(&frame);
  300
+        return 0;
  301
+      }
  302
+      m_ResampBufferSize = buf_size;
  303
+    }
  304
+
  305
+    m_dllAvCodec.avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt, m_ResampBuffer, buf_size, 0);
  306
+
  307
+    /* important note: the '&input' here works because we convert from a packed
  308
+     * format (ie, interleaved). If it were to be used to convert from planar
  309
+     * formats (ie, non-interleaved, which is not currently supported by AE),
  310
+     * we would need to adapt it or it would segfault. */
  311
+    if (m_dllSwResample.swr_convert(m_SwrCtx, frame->extended_data, frames, &input, frames) < 0)
  312
+    {
  313
+      CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Resampling failed");
  314
+      m_dllAvCodec.avcodec_free_frame(&frame);
  315
+      return 0;
  316
+    }
  317
+  }
  318
+  else
  319
+    m_dllAvCodec.avcodec_fill_audio_frame(frame, m_CodecCtx->channels, m_CodecCtx->sample_fmt,
  320
+                    input, buf_size, 0);
  321
+
  322
+  /* initialize the output packet */
  323
+  m_dllAvCodec.av_init_packet(&m_Pkt);
  324
+  m_Pkt.size      = sizeof(m_Buffer) - IEC61937_DATA_OFFSET;
  325
+  m_Pkt.data      = m_Buffer + IEC61937_DATA_OFFSET;
  326
+
244 327
   /* encode it */
245  
-  int size = m_dllAvCodec.avcodec_encode_audio(m_CodecCtx, m_Buffer + IEC61937_DATA_OFFSET, FF_MIN_BUFFER_SIZE, (short*)data);
  328
+  int ret = m_dllAvCodec.avcodec_encode_audio2(m_CodecCtx, &m_Pkt, frame, &got_output);
  329
+
  330
+  /* free temporary data */
  331
+  m_dllAvCodec.avcodec_free_frame(&frame);
  332
+
  333
+  if (ret < 0 || !got_output)
  334
+  {
  335
+    CLog::Log(LOGERROR, "CAEEncoderFFmpeg::Encode - Encoding failed");
  336
+    return 0;
  337
+  }
246 338
 
247 339
   /* pack it into an IEC958 frame */
248  
-  m_BufferSize = m_PackFunc(NULL, size, m_Buffer);
  340
+  m_BufferSize = m_PackFunc(NULL, m_Pkt.size, m_Buffer);
249 341
   if (m_BufferSize != m_OutputSize)
250 342
   {
251 343
     m_OutputSize  = m_BufferSize;
252 344
     m_OutputRatio = (double)m_NeededFrames / m_OutputSize;
253 345
   }
254 346
 
  347
+  /* free the packet */
  348
+  m_dllAvCodec.av_free_packet(&m_Pkt);
  349
+
255 350
   /* return the number of frames used */
256 351
   return m_NeededFrames;
257 352
 }
8  xbmc/cores/AudioEngine/Encoders/AEEncoderFFmpeg.h
@@ -27,6 +27,7 @@
27 27
 #undef restrict
28 28
 #include "DllAvCodec.h"
29 29
 #include "DllAvFormat.h"
  30
+#include "DllSwResample.h"
30 31
 
31 32
 class CAEEncoderFFmpeg: public IAEEncoder
32 33
 {
@@ -49,6 +50,7 @@ class CAEEncoderFFmpeg: public IAEEncoder
49 50
   DllAvCodec  m_dllAvCodec;
50 51
   DllAvFormat m_dllAvFormat;
51 52
   DllAvUtil   m_dllAvUtil;
  53
+  DllSwResample m_dllSwResample;
52 54
 
53 55
   std::string                m_CodecName;
54 56
   CodecID                   m_CodecID;
@@ -57,7 +59,9 @@ class CAEEncoderFFmpeg: public IAEEncoder
57 59
 
58 60
   AEAudioFormat     m_CurrentFormat;
59 61
   AVCodecContext   *m_CodecCtx;
  62
+  SwrContext       *m_SwrCtx;
60 63
   CAEChannelInfo    m_Layout;
  64
+  AVPacket          m_Pkt;
61 65
   uint8_t           m_Buffer[IEC61937_DATA_OFFSET + FF_MIN_BUFFER_SIZE];
62 66
   int               m_BufferSize;
63 67
   int               m_OutputSize;
@@ -67,5 +71,9 @@ class CAEEncoderFFmpeg: public IAEEncoder
67 71
   unsigned int      m_NeededFrames;
68 72
 
69 73
   unsigned int BuildChannelLayout(const int64_t ffmap, CAEChannelInfo& layout);
  74
+
  75
+  bool              m_NeedConversion;
  76
+  uint8_t          *m_ResampBuffer;
  77
+  int               m_ResampBufferSize;
70 78
 };
71 79
 

0 notes on commit b73bd86

Please sign in to comment.
Something went wrong with that request. Please try again.