Bug 1237210: [ffmpeg] P1. Have specialized AVCodecContext initialization. r=gerald
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 06 Jan 2016 21:05:59 +1100
changeset 278816 e079e281ae095936a3d5468862b728ff7609d328
parent 278815 d540e9c1a4fd83a80dcac542fcd30f14506398d1
child 278817 84db9e4cf21291b85f40963867467d9650e72453
push id29860
push usercbook@mozilla.com
push dateThu, 07 Jan 2016 10:51:20 +0000
treeherdermozilla-central@e0bcd16e1d4b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgerald
bugs1237210
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1237210: [ffmpeg] P1. Have specialized AVCodecContext initialization. r=gerald A common initialization procedure is too restrictive, and we end up setting up audio configuration for video decoding and vice-versa
dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -30,16 +30,32 @@ RefPtr<MediaDataDecoder::InitPromise>
 FFmpegAudioDecoder<LIBAV_VER>::Init()
 {
   nsresult rv = InitDecoder();
 
   return rv == NS_OK ? InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__)
                      : InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
 }
 
+void
+FFmpegAudioDecoder<LIBAV_VER>::InitCodecContext()
+{
+  MOZ_ASSERT(mCodecContext);
+  // We do not want to set this value to 0 as FFmpeg by default will
+  // use the number of cores, which with our mozlibavutil get_cpu_count
+  // isn't implemented.
+  mCodecContext->thread_count = 1;
+  // FFmpeg takes this as a suggestion for what format to use for audio samples.
+  uint32_t major, minor;
+  FFmpegRuntimeLinker::GetVersion(major, minor);
+  // LibAV 0.8 produces rubbish float interleaved samples, request 16 bits audio.
+  mCodecContext->request_sample_fmt =
+    (major == 53) ? AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
+}
+
 static UniquePtr<AudioDataValue[]>
 CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
 {
   MOZ_ASSERT(aNumChannels <= MAX_CHANNELS);
 
   auto audio = MakeUnique<AudioDataValue[]>(aNumChannels * aNumAFrames);
 
   if (aFrame->format == AV_SAMPLE_FMT_FLT) {
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
@@ -23,16 +23,17 @@ public:
   FFmpegAudioDecoder(FlushableTaskQueue* aTaskQueue,
                      MediaDataDecoderCallback* aCallback,
                      const AudioInfo& aConfig);
   virtual ~FFmpegAudioDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
+  void InitCodecContext() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodePacket(MediaRawData* aSample);
 };
 
 } // namespace mozilla
 
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
@@ -27,49 +27,23 @@ FFmpegDataDecoder<LIBAV_VER>::FFmpegData
   : mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
   , mCodecContext(nullptr)
   , mFrame(NULL)
   , mExtraData(nullptr)
   , mCodecID(aCodecID)
   , mMonitor("FFMpegaDataDecoder")
   , mIsFlushing(false)
-  , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegDataDecoder);
 }
 
 FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder()
 {
   MOZ_COUNT_DTOR(FFmpegDataDecoder);
-  if (mCodecParser) {
-    av_parser_close(mCodecParser);
-    mCodecParser = nullptr;
-  }
-}
-
-/**
- * FFmpeg calls back to this function with a list of pixel formats it supports.
- * We choose a pixel format that we support and return it.
- * For now, we just look for YUV420P as it is the only non-HW accelerated format
- * supported by FFmpeg's H264 decoder.
- */
-static PixelFormat
-ChoosePixelFormat(AVCodecContext* aCodecContext, const PixelFormat* aFormats)
-{
-  FFMPEG_LOG("Choosing FFmpeg pixel format for video decoding.");
-  for (; *aFormats > -1; aFormats++) {
-    if (*aFormats == PIX_FMT_YUV420P || *aFormats == PIX_FMT_YUVJ420P) {
-      FFMPEG_LOG("Requesting pixel format YUV420P.");
-      return PIX_FMT_YUV420P;
-    }
-  }
-
-  NS_WARNING("FFmpeg does not share any supported pixel formats.");
-  return PIX_FMT_NONE;
 }
 
 nsresult
 FFmpegDataDecoder<LIBAV_VER>::InitDecoder()
 {
   FFMPEG_LOG("Initialising FFmpeg decoder.");
 
   AVCodec* codec = FindAVCodec(mCodecID);
@@ -82,29 +56,17 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
 
   if (!(mCodecContext = avcodec_alloc_context3(codec))) {
     NS_WARNING("Couldn't init ffmpeg context");
     return NS_ERROR_FAILURE;
   }
 
   mCodecContext->opaque = this;
 
-  // FFmpeg takes this as a suggestion for what format to use for audio samples.
-  uint32_t major, minor;
-  FFmpegRuntimeLinker::GetVersion(major, minor);
-  // LibAV 0.8 produces rubbish float interleaved samples, request 16 bits audio.
-  mCodecContext->request_sample_fmt = major == 53 ?
-    AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
-
-  // FFmpeg will call back to this to negotiate a video pixel format.
-  mCodecContext->get_format = ChoosePixelFormat;
-
-  mCodecContext->thread_count = PR_GetNumberOfProcessors();
-  mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
-  mCodecContext->thread_safe_callbacks = false;
+  InitCodecContext();
 
   if (mExtraData) {
     mCodecContext->extradata_size = mExtraData->Length();
     // FFmpeg may use SIMD instructions to access the data which reads the
     // data in 32 bytes block. Must ensure we have enough data to read.
     mExtraData->AppendElements(FF_INPUT_BUFFER_PADDING_SIZE);
     mCodecContext->extradata = mExtraData->Elements();
   } else {
@@ -126,21 +88,16 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLT &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLTP &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16 &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16P) {
     NS_WARNING("FFmpeg audio decoder outputs unsupported audio format.");
     return NS_ERROR_FAILURE;
   }
 
-  mCodecParser = av_parser_init(mCodecID);
-  if (mCodecParser) {
-    mCodecParser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
-  }
-
   FFMPEG_LOG("FFmpeg init successful.");
   return NS_OK;
 }
 
 nsresult
 FFmpegDataDecoder<LIBAV_VER>::Shutdown()
 {
   if (mTaskQueue) {
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
@@ -38,16 +38,17 @@ public:
 
   static AVCodec* FindAVCodec(AVCodecID aCodec);
 
 protected:
   // Flush and Drain operation, always run
   virtual void ProcessFlush();
   virtual void ProcessDrain() = 0;
   virtual void ProcessShutdown();
+  virtual void InitCodecContext() {}
   AVFrame*        PrepareFrame();
   nsresult        InitDecoder();
 
   RefPtr<FlushableTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
 
   AVCodecContext* mCodecContext;
   AVFrame*        mFrame;
@@ -56,17 +57,16 @@ protected:
 
   // For wait on mIsFlushing during Shutdown() process.
   // Protects mReorderQueue.
   Monitor mMonitor;
   // Set on reader/decode thread calling Flush() to indicate that output is
   // not required and so input samples on mTaskQueue need not be processed.
   // Cleared on mTaskQueue in ProcessDrain().
   Atomic<bool> mIsFlushing;
-  AVCodecParserContext* mCodecParser;
 
 private:
   static bool sFFmpegInitDone;
   static StaticMutex sMonitor;
 };
 
 } // namespace mozilla
 
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -17,16 +17,37 @@
 #include "mozilla/PodOperations.h"
 
 typedef mozilla::layers::Image Image;
 typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
 
 namespace mozilla
 {
 
+/**
+ * FFmpeg calls back to this function with a list of pixel formats it supports.
+ * We choose a pixel format that we support and return it.
+ * For now, we just look for YUV420P as it is the only non-HW accelerated format
+ * supported by FFmpeg's H264 decoder.
+ */
+static PixelFormat
+ChoosePixelFormat(AVCodecContext* aCodecContext, const PixelFormat* aFormats)
+{
+  FFMPEG_LOG("Choosing FFmpeg pixel format for video decoding.");
+  for (; *aFormats > -1; aFormats++) {
+    if (*aFormats == PIX_FMT_YUV420P || *aFormats == PIX_FMT_YUVJ420P) {
+      FFMPEG_LOG("Requesting pixel format YUV420P.");
+      return PIX_FMT_YUV420P;
+    }
+  }
+
+  NS_WARNING("FFmpeg does not share any supported pixel formats.");
+  return PIX_FMT_NONE;
+}
+
 FFmpegH264Decoder<LIBAV_VER>::PtsCorrectionContext::PtsCorrectionContext()
   : mNumFaultyPts(0)
   , mNumFaultyDts(0)
   , mLastPts(INT64_MIN)
   , mLastDts(INT64_MIN)
 {
 }
 
@@ -64,34 +85,50 @@ FFmpegH264Decoder<LIBAV_VER>::PtsCorrect
 FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const VideoInfo& aConfig,
   ImageContainer* aImageContainer)
   : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
   , mImageContainer(aImageContainer)
   , mDisplay(aConfig.mDisplay)
   , mImage(aConfig.mImage)
+  , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegH264Decoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
   mExtraData->AppendElements(*aConfig.mExtraData);
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
 FFmpegH264Decoder<LIBAV_VER>::Init()
 {
   if (NS_FAILED(InitDecoder())) {
     return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
   }
 
+  return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
+}
+
+void
+FFmpegH264Decoder<LIBAV_VER>::InitCodecContext()
+{
   mCodecContext->width = mImage.width;
   mCodecContext->height = mImage.height;
 
-  return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
+  mCodecContext->thread_count = PR_GetNumberOfProcessors();
+  mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
+
+  // FFmpeg will call back to this to negotiate a video pixel format.
+  mCodecContext->get_format = ChoosePixelFormat;
+
+  mCodecParser = av_parser_init(mCodecID);
+  if (mCodecParser) {
+    mCodecParser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
+  }
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 
   uint8_t* inputData = const_cast<uint8_t*>(aSample->Data());
@@ -283,16 +320,20 @@ FFmpegH264Decoder<LIBAV_VER>::ProcessFlu
   mPtsContext.Reset();
   mDurationMap.Clear();
   FFmpegDataDecoder::ProcessFlush();
 }
 
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
+  if (mCodecParser) {
+    av_parser_close(mCodecParser);
+    mCodecParser = nullptr;
+  }
 }
 
 AVCodecID
 FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
   if (aMimeType.EqualsLiteral("video/avc") || aMimeType.EqualsLiteral("video/mp4")) {
     return AV_CODEC_ID_H264;
   }
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
@@ -37,16 +37,17 @@ public:
                     const VideoInfo& aConfig,
                     ImageContainer* aImageContainer);
   virtual ~FFmpegH264Decoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
   void ProcessFlush() override;
+  void InitCodecContext() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample, uint8_t* aData, int aSize);
   void DoDrain();
   void OutputDelayedFrames();
@@ -59,16 +60,19 @@ private:
    */
   int AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext,
                                  AVFrame* aFrame);
 
   RefPtr<ImageContainer> mImageContainer;
   nsIntSize mDisplay;
   nsIntRect mImage;
 
+  // Parser used for VP8 and VP9 decoding.
+  AVCodecParserContext* mCodecParser;
+
   class PtsCorrectionContext {
   public:
     PtsCorrectionContext();
     int64_t GuessCorrectPts(int64_t aPts, int64_t aDts);
     void Reset();
 
   private:
     int64_t mNumFaultyPts; /// Number of incorrect PTS values so far