Bug 1199193: Ensure DrainComplete() is called once all decoded frames have been output. r=rillian a=ritu
authorJean-Yves Avenard <jyavenard@mozilla.com>
Fri, 28 Aug 2015 09:59:45 +1000
changeset 289095 dfa2ebb06230e8b50434a7f37b8fb7d0f404555b
parent 289094 0f4b50dab0f7a9117e961a9d1f76e7695ddebec0
child 289096 93a21f5df6d540031b892414ccc8fe4735468c58
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrillian, ritu
bugs1199193
milestone42.0a2
Bug 1199193: Ensure DrainComplete() is called once all decoded frames have been output. r=rillian a=ritu
dom/media/platforms/apple/AppleVDADecoder.cpp
dom/media/platforms/apple/AppleVDADecoder.h
dom/media/platforms/apple/AppleVTDecoder.cpp
--- a/dom/media/platforms/apple/AppleVDADecoder.cpp
+++ b/dom/media/platforms/apple/AppleVDADecoder.cpp
@@ -260,58 +260,53 @@ PlatformCallback(void* decompressionOutp
 
   AppleVDADecoder::AppleFrameRef frameRef(
       media::TimeUnit::FromMicroseconds(dts),
       media::TimeUnit::FromMicroseconds(pts),
       media::TimeUnit::FromMicroseconds(duration),
       byte_offset,
       is_sync_point == 1);
 
-  // Forward the data back to an object method which can access
-  // the correct reader's callback.
-  nsCOMPtr<nsIRunnable> task =
-    NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVDADecoder::AppleFrameRef>(
-      decoder, &AppleVDADecoder::OutputFrame, image, frameRef);
-  decoder->DispatchOutputTask(task.forget());
+  decoder->OutputFrame(image, frameRef);
 }
 
 AppleVDADecoder::AppleFrameRef*
 AppleVDADecoder::CreateAppleFrameRef(const MediaRawData* aSample)
 {
   MOZ_ASSERT(aSample);
   return new AppleFrameRef(*aSample);
 }
 
 void
 AppleVDADecoder::DrainReorderedFrames()
 {
+  MonitorAutoLock mon(mMonitor);
   while (!mReorderQueue.IsEmpty()) {
     mCallback->Output(mReorderQueue.Pop());
   }
   mQueuedSamples = 0;
 }
 
 void
 AppleVDADecoder::ClearReorderedFrames()
 {
+  MonitorAutoLock mon(mMonitor);
   while (!mReorderQueue.IsEmpty()) {
     mReorderQueue.Pop();
   }
   mQueuedSamples = 0;
 }
 
 // Copy and return a decoded frame.
 nsresult
-AppleVDADecoder::OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
+AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
                              AppleVDADecoder::AppleFrameRef aFrameRef)
 {
-  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
-
-  if (mIsFlushing) {
-    // We are in the process of flushing; ignore frame.
+  if (mIsShutDown || mIsFlushing) {
+    // We are in the process of flushing or shutting down; ignore frame.
     return NS_OK;
   }
 
   LOG("mp4 output frame %lld dts %lld pts %lld duration %lld us%s",
       aFrameRef.byte_offset,
       aFrameRef.decode_timestamp.ToMicroseconds(),
       aFrameRef.composition_timestamp.ToMicroseconds(),
       aFrameRef.duration.ToMicroseconds(),
@@ -422,16 +417,17 @@ AppleVDADecoder::OutputFrame(CFRefPtr<CV
   if (!data) {
     NS_ERROR("Couldn't create VideoData for frame");
     mCallback->Error();
     return NS_ERROR_FAILURE;
   }
 
   // Frames come out in DTS order but we need to output them
   // in composition order.
+  MonitorAutoLock mon(mMonitor);
   mReorderQueue.Push(data);
   while (mReorderQueue.Length() > mMaxRefFrames) {
     mCallback->Output(mReorderQueue.Pop());
   }
   LOG("%llu decoded frames queued",
       static_cast<unsigned long long>(mReorderQueue.Length()));
 
   return NS_OK;
--- a/dom/media/platforms/apple/AppleVDADecoder.h
+++ b/dom/media/platforms/apple/AppleVDADecoder.h
@@ -76,26 +76,19 @@ public:
   virtual nsresult Flush() override;
   virtual nsresult Drain() override;
   virtual nsresult Shutdown() override;
   virtual bool IsHardwareAccelerated() const override
   {
     return true;
   }
 
-  void DispatchOutputTask(already_AddRefed<nsIRunnable> aTask)
-  {
-    nsCOMPtr<nsIRunnable> task = aTask;
-    if (mIsShutDown || mIsFlushing) {
-      return;
-    }
-    mTaskQueue->Dispatch(task.forget(), AbstractThread::DontAssertDispatchSuccess);
-  }
-
-  nsresult OutputFrame(CFRefPtr<CVPixelBufferRef> aImage,
+  // Access from the taskqueue and the decoder's thread.
+  // OutputFrame is thread-safe.
+  nsresult OutputFrame(CVPixelBufferRef aImage,
                        AppleFrameRef aFrameRef);
 
 protected:
   // Flush and Drain operation, always run
   virtual void ProcessFlush();
   virtual void ProcessDrain();
   virtual void ProcessShutdown();
 
@@ -103,41 +96,43 @@ protected:
   void DrainReorderedFrames();
   void ClearReorderedFrames();
   CFDictionaryRef CreateOutputConfiguration();
 
   nsRefPtr<MediaByteBuffer> mExtraData;
   nsRefPtr<FlushableTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
   nsRefPtr<layers::ImageContainer> mImageContainer;
-  ReorderQueue mReorderQueue;
   uint32_t mPictureWidth;
   uint32_t mPictureHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
+  // Accessed on multiple threads, but only set in constructor.
   uint32_t mMaxRefFrames;
   // Increased when Input is called, and decreased when ProcessFrame runs.
   // Reaching 0 indicates that there's no pending Input.
   Atomic<uint32_t> mInputIncoming;
   Atomic<bool> mIsShutDown;
 
-  bool mUseSoftwareImages;
-  bool mIs106;
+  const bool mUseSoftwareImages;
+  const bool mIs106;
 
   // Number of times a sample was queued via Input(). Will be decreased upon
   // the decoder's callback being invoked.
   // This is used to calculate how many frames has been buffered by the decoder.
-  uint32_t mQueuedSamples;
+  Atomic<uint32_t> mQueuedSamples;
 
   // 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;
+  ReorderQueue mReorderQueue;
 
 private:
   VDADecoder mDecoder;
 
   // Method to pass a frame to VideoToolbox for decoding.
   nsresult SubmitFrame(MediaRawData* aSample);
   // Method to set up the decompression session.
   nsresult InitializeSession();
--- a/dom/media/platforms/apple/AppleVTDecoder.cpp
+++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
@@ -164,20 +164,17 @@ PlatformCallback(void* decompressionOutp
     NS_WARNING("VideoToolbox decoder returned no data");
     image = nullptr;
   } else if (flags & kVTDecodeInfo_FrameDropped) {
     NS_WARNING("  ...frame tagged as dropped...");
   } else {
     MOZ_ASSERT(CFGetTypeID(image) == CVPixelBufferGetTypeID(),
       "VideoToolbox returned an unexpected image type");
   }
-  nsCOMPtr<nsIRunnable> task =
-    NS_NewRunnableMethodWithArgs<CFRefPtr<CVPixelBufferRef>, AppleVTDecoder::AppleFrameRef>(
-      decoder, &AppleVTDecoder::OutputFrame, image, *frameRef);
-  decoder->DispatchOutputTask(task.forget());
+  decoder->OutputFrame(image, *frameRef);
 }
 
 nsresult
 AppleVTDecoder::WaitForAsynchronousFrames()
 {
   OSStatus rv = VTDecompressionSessionWaitForAsynchronousFrames(mSession);
   if (rv != noErr) {
     LOG("AppleVTDecoder: Error %d waiting for asynchronous frames", rv);