Bug 1206568: P1. Ensure FFmpeg decoder is only accessed through the decoder's task queue. r=cpearce
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 21 Sep 2015 13:54:29 +1000
changeset 263886 aa240de3760314147098b3bf9d509aac9d95f394
parent 263885 178cdfac3315dee9b3fdb257f0330c522ab31f88
child 263887 f72556c17c7c37d2a30b879fdbfa6f208137527c
push id65461
push userjyavenard@mozilla.com
push dateWed, 23 Sep 2015 03:00:50 +0000
treeherdermozilla-inbound@0f7f4567efef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1206568
milestone44.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 1206568: P1. Ensure FFmpeg decoder is only accessed through the decoder's task queue. r=cpearce Add strong assertion that it is indeed the case.
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
@@ -13,18 +13,17 @@
 #define MAX_CHANNELS 16
 
 namespace mozilla
 {
 
 FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const AudioInfo& aConfig)
-  : FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mMimeType))
-  , mCallback(aCallback)
+  : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
 {
   MOZ_COUNT_CTOR(FFmpegAudioDecoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
   mExtraData->AppendElements(*aConfig.mCodecSpecificConfig);
 }
 
 nsRefPtr<MediaDataDecoder::InitPromise>
@@ -80,16 +79,17 @@ CopyAndPackAudio(AVFrame* aFrame, uint32
   }
 
   return audio.forget();
 }
 
 void
 FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   AVPacket packet;
   av_init_packet(&packet);
 
   packet.data = const_cast<uint8_t*>(aSample->Data());
   packet.size = aSample->Size();
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
@@ -155,22 +155,22 @@ nsresult
 FFmpegAudioDecoder<LIBAV_VER>::Input(MediaRawData* aSample)
 {
   nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
     this, &FFmpegAudioDecoder::DecodePacket, nsRefPtr<MediaRawData>(aSample)));
   mTaskQueue->Dispatch(runnable.forget());
   return NS_OK;
 }
 
-nsresult
-FFmpegAudioDecoder<LIBAV_VER>::Drain()
+void
+FFmpegAudioDecoder<LIBAV_VER>::ProcessDrain()
 {
-  mTaskQueue->AwaitIdle();
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  ProcessFlush();
   mCallback->DrainComplete();
-  return Flush();
 }
 
 AVCodecID
 FFmpegAudioDecoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
   if (aMimeType.EqualsLiteral("audio/mpeg")) {
     return AV_CODEC_ID_MP3;
   }
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
@@ -22,20 +22,18 @@ class FFmpegAudioDecoder<LIBAV_VER> : pu
 public:
   FFmpegAudioDecoder(FlushableTaskQueue* aTaskQueue,
                      MediaDataDecoderCallback* aCallback,
                      const AudioInfo& aConfig);
   virtual ~FFmpegAudioDecoder();
 
   virtual nsRefPtr<InitPromise> Init() override;
   virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Drain() override;
+  virtual void ProcessDrain() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodePacket(MediaRawData* aSample);
-
-  MediaDataDecoderCallback* mCallback;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegAACDecoder_h__
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
@@ -16,22 +16,26 @@
 
 namespace mozilla
 {
 
 bool FFmpegDataDecoder<LIBAV_VER>::sFFmpegInitDone = false;
 StaticMutex FFmpegDataDecoder<LIBAV_VER>::sMonitor;
 
 FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
+                                                MediaDataDecoderCallback* aCallback,
                                                 AVCodecID aCodecID)
   : mTaskQueue(aTaskQueue)
+  , mCallback(aCallback)
   , mCodecContext(nullptr)
   , mFrame(NULL)
   , mExtraData(nullptr)
   , mCodecID(aCodecID)
+  , mMonitor("FFMpegaDataDecoder")
+  , mIsFlushing(false)
 {
   MOZ_COUNT_CTOR(FFmpegDataDecoder);
 }
 
 FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder()
 {
   MOZ_COUNT_DTOR(FFmpegDataDecoder);
 }
@@ -123,46 +127,89 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
     return NS_ERROR_FAILURE;
   }
 
   FFMPEG_LOG("FFmpeg init successful.");
   return NS_OK;
 }
 
 nsresult
-FFmpegDataDecoder<LIBAV_VER>::Flush()
+FFmpegDataDecoder<LIBAV_VER>::Shutdown()
 {
-  mTaskQueue->Flush();
-  avcodec_flush_buffers(mCodecContext);
+  if (mTaskQueue) {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown);
+    mTaskQueue->Dispatch(runnable.forget());
+  } else {
+    ProcessShutdown();
+  }
   return NS_OK;
 }
 
 nsresult
-FFmpegDataDecoder<LIBAV_VER>::Shutdown()
+FFmpegDataDecoder<LIBAV_VER>::Flush()
+{
+  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+  mIsFlushing = true;
+  mTaskQueue->Flush();
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessFlush);
+  MonitorAutoLock mon(mMonitor);
+  mTaskQueue->Dispatch(runnable.forget());
+  while (mIsFlushing) {
+    mon.Wait();
+  }
+  return NS_OK;
+}
+
+nsresult
+FFmpegDataDecoder<LIBAV_VER>::Drain()
+{
+  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessDrain);
+  mTaskQueue->Dispatch(runnable.forget());
+  return NS_OK;
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::ProcessFlush()
+{
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  if (mCodecContext) {
+    avcodec_flush_buffers(mCodecContext);
+  }
+  MonitorAutoLock mon(mMonitor);
+  mIsFlushing = false;
+  mon.NotifyAll();
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown()
 {
   StaticMutexAutoLock mon(sMonitor);
 
   if (sFFmpegInitDone) {
     avcodec_close(mCodecContext);
     av_freep(&mCodecContext);
 #if LIBAVCODEC_VERSION_MAJOR >= 55
     av_frame_free(&mFrame);
 #elif LIBAVCODEC_VERSION_MAJOR == 54
     avcodec_free_frame(&mFrame);
 #else
     delete mFrame;
     mFrame = nullptr;
 #endif
   }
-  return NS_OK;
 }
 
 AVFrame*
 FFmpegDataDecoder<LIBAV_VER>::PrepareFrame()
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 #if LIBAVCODEC_VERSION_MAJOR >= 55
   if (mFrame) {
     av_frame_unref(mFrame);
   } else {
     mFrame = av_frame_alloc();
   }
 #elif LIBAVCODEC_VERSION_MAJOR == 54
   if (mFrame) {
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
@@ -18,37 +18,53 @@ template <int V>
 class FFmpegDataDecoder : public MediaDataDecoder
 {
 };
 
 template <>
 class FFmpegDataDecoder<LIBAV_VER> : public MediaDataDecoder
 {
 public:
-  FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue, AVCodecID aCodecID);
+  FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
+                    MediaDataDecoderCallback* aCallback,
+                    AVCodecID aCodecID);
   virtual ~FFmpegDataDecoder();
 
   static bool Link();
 
   virtual nsRefPtr<InitPromise> Init() override = 0;
   virtual nsresult Input(MediaRawData* aSample) override = 0;
   virtual nsresult Flush() override;
-  virtual nsresult Drain() override = 0;
+  virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
 
 protected:
+  // Flush and Drain operation, always run
+  virtual void ProcessFlush();
+  virtual void ProcessDrain() = 0;
+  virtual void ProcessShutdown();
   AVFrame*        PrepareFrame();
   nsresult        InitDecoder();
 
-  FlushableTaskQueue* mTaskQueue;
+  nsRefPtr<FlushableTaskQueue> mTaskQueue;
+  MediaDataDecoderCallback* mCallback;
+
   AVCodecContext* mCodecContext;
   AVFrame*        mFrame;
   nsRefPtr<MediaByteBuffer> mExtraData;
   AVCodecID mCodecID;
 
+  // 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;
+
 private:
   static bool sFFmpegInitDone;
   static StaticMutex sMonitor;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDataDecoder_h__
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -23,18 +23,17 @@ typedef mozilla::layers::PlanarYCbCrImag
 
 namespace mozilla
 {
 
 FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const VideoInfo& aConfig,
   ImageContainer* aImageContainer)
-  : FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mMimeType))
-  , mCallback(aCallback)
+  : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
   , mImageContainer(aImageContainer)
   , mDisplayWidth(aConfig.mDisplay.width)
   , mDisplayHeight(aConfig.mDisplay.height)
   , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegH264Decoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
@@ -52,30 +51,33 @@ FFmpegH264Decoder<LIBAV_VER>::Init()
   mCodecContext->release_buffer = ReleaseBufferCb;
 
   return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
 }
 
 int64_t
 FFmpegH264Decoder<LIBAV_VER>::GetPts(const AVPacket& packet)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 #if LIBAVCODEC_VERSION_MAJOR == 53
   if (mFrame->pkt_pts == 0) {
     return mFrame->pkt_dts;
   } else {
     return mFrame->pkt_pts;
   }
 #else
   return mFrame->pkt_pts;
 #endif
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
   uint8_t* inputData = const_cast<uint8_t*>(aSample->Data());
   size_t inputSize = aSample->Size();
 
   if (inputSize && (mCodecID == AV_CODEC_ID_VP8
 #if LIBAVCODEC_VERSION_MAJOR >= 55
       || mCodecID == AV_CODEC_ID_VP9
 #endif
       )) {
@@ -117,16 +119,18 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
   }
   return DoDecodeFrame(aSample, inputData, inputSize);
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
                                             uint8_t* aData, int aSize)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
   AVPacket packet;
   av_init_packet(&packet);
 
   packet.data = aData;
   packet.size = aSize;
   packet.dts = aSample->mTimecode;
   packet.pts = aSample->mTime;
   packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
@@ -203,16 +207,18 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
     return DecodeResult::DECODE_FRAME;
   }
   return DecodeResult::DECODE_NO_FRAME;
 }
 
 void
 FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
   if (DoDecodeFrame(aSample) != DecodeResult::DECODE_ERROR &&
       mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 /* static */ int
 FFmpegH264Decoder<LIBAV_VER>::AllocateBufferCb(AVCodecContext* aCodecContext,
@@ -334,42 +340,25 @@ FFmpegH264Decoder<LIBAV_VER>::Input(Medi
       this, &FFmpegH264Decoder<LIBAV_VER>::DecodeFrame,
       nsRefPtr<MediaRawData>(aSample)));
   mTaskQueue->Dispatch(runnable.forget());
 
   return NS_OK;
 }
 
 void
-FFmpegH264Decoder<LIBAV_VER>::DoDrain()
+FFmpegH264Decoder<LIBAV_VER>::ProcessDrain()
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   nsRefPtr<MediaRawData> empty(new MediaRawData());
   while (DoDecodeFrame(empty) == DecodeResult::DECODE_FRAME) {
   }
   mCallback->DrainComplete();
 }
 
-nsresult
-FFmpegH264Decoder<LIBAV_VER>::Drain()
-{
-  nsCOMPtr<nsIRunnable> runnable(
-    NS_NewRunnableMethod(this, &FFmpegH264Decoder<LIBAV_VER>::DoDrain));
-  mTaskQueue->Dispatch(runnable.forget());
-
-  return NS_OK;
-}
-
-nsresult
-FFmpegH264Decoder<LIBAV_VER>::Flush()
-{
-  nsresult rv = FFmpegDataDecoder::Flush();
-  // Even if the above fails we may as well clear our frame queue.
-  return rv;
-}
-
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
   if (mCodecParser) {
     av_parser_close(mCodecParser);
     mCodecParser = nullptr;
   }
 }
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
@@ -33,18 +33,17 @@ public:
   FFmpegH264Decoder(FlushableTaskQueue* aTaskQueue,
                     MediaDataDecoderCallback* aCallback,
                     const VideoInfo& aConfig,
                     ImageContainer* aImageContainer);
   virtual ~FFmpegH264Decoder();
 
   virtual nsRefPtr<InitPromise> Init() override;
   virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Drain() override;
-  virtual nsresult Flush() override;
+  virtual void ProcessDrain() 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();
@@ -57,17 +56,16 @@ private:
    */
   int AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext,
                                  AVFrame* aFrame);
 
   static int AllocateBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
   static void ReleaseBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
   int64_t GetPts(const AVPacket& packet);
 
-  MediaDataDecoderCallback* mCallback;
   nsRefPtr<ImageContainer> mImageContainer;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
   AVCodecParserContext* mCodecParser;
 };
 
 } // namespace mozilla