Bug 1274626 part 2 - [WIP] - switch the video decoder to blank decoder dynamically; r?kaku draft
authorKaku Kuo <tkuo@mozilla.com>
Thu, 14 Jul 2016 18:45:41 +0800
changeset 387689 b3682695ab1cf5bcbb1c4f5dd6b9e6b70686fa71
parent 387688 a4b84f99c116b8d1c78b80630a7582bb9dcdba75
child 525414 da606b17650536de9689b433aa61f8281aeba0f6
push id23033
push usertkuo@mozilla.com
push dateThu, 14 Jul 2016 15:45:19 +0000
reviewerskaku
bugs1274626
milestone50.0a1
Bug 1274626 part 2 - [WIP] - switch the video decoder to blank decoder dynamically; r?kaku MozReview-Commit-ID: By8h71EsXrg
dom/media/MediaDecoderReader.h
dom/media/MediaDecoderReaderWrapper.cpp
dom/media/MediaDecoderReaderWrapper.h
dom/media/MediaDecoderStateMachine.cpp
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/platforms/PDMFactory.cpp
dom/media/platforms/PDMFactory.h
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -302,16 +302,21 @@ public:
     MOZ_ASSERT(OnTaskQueue());
     mIsSuspended = aState;
   }
 
   AbstractCanonical<bool>* CanonicalIsSuspended() {
     return &mIsSuspended;
   }
 
+  // Switch the video decoder to BlankDecoderModule. It might takes effective
+  // since a few samples later depends on how much demuxed samples are already
+  // queued in the original video decoder.
+  virtual void SetVideoBlankDecode(bool aIsBlankDecode) {}
+
 protected:
   virtual ~MediaDecoderReader();
 
   // Populates aBuffered with the time ranges which are buffered. This may only
   // be called on the decode task queue, and should only be used internally by
   // UpdateBuffered - mBuffered (or mirrors of it) should be used for everything
   // else.
   //
--- a/dom/media/MediaDecoderReaderWrapper.cpp
+++ b/dom/media/MediaDecoderReaderWrapper.cpp
@@ -405,9 +405,15 @@ MediaDecoderReaderWrapper::OnMetadataRea
         self->mReader->DispatchSetStartTime(self->StartTime().ToMicroseconds());
       },
       [] () {
         NS_WARNING("Setting start time on reader failed");
       });
   }
 }
 
+void
+MediaDecoderReaderWrapper::SetVideoBlankDecode(bool aIsBlankDecode)
+{
+  return mReader->SetVideoBlankDecode(aIsBlankDecode);
+}
+
 } // namespace mozilla
--- a/dom/media/MediaDecoderReaderWrapper.h
+++ b/dom/media/MediaDecoderReaderWrapper.h
@@ -115,16 +115,18 @@ public:
   AbstractCanonical<bool>* CanonicalIsSuspended() {
     return mReader->CanonicalIsSuspended();
   }
 
 #ifdef MOZ_EME
   void SetCDMProxy(CDMProxy* aProxy) { mReader->SetCDMProxy(aProxy); }
 #endif
 
+  void SetVideoBlankDecode(bool aIsBlankDecode);
+
 private:
   ~MediaDecoderReaderWrapper();
 
   void OnMetadataRead(MetadataHolder* aMetadata);
   void OnMetadataNotRead() {}
   MediaCallbackExc<WaitCallbackData>& WaitCallbackRef(MediaData::Type aType);
   MozPromiseRequestHolder<WaitForDataPromise>& WaitRequestRef(MediaData::Type aType);
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -1384,16 +1384,17 @@ void MediaDecoderStateMachine::Visibilit
 
   // Resuming from suspended decoding
 
   // If suspend timer exists, destroy it.
   mVideoDecodeSuspendTimer.Reset();
 
   if (mVideoDecodeSuspended) {
     mVideoDecodeSuspended = false;
+    mReader->SetVideoBlankDecode(false);
 
     if (mIsReaderSuspended) {
       return;
     }
 
     // If an existing seek is in flight don't bother creating a new
     // one to catch up.
     if (mSeekTask || mQueuedSeek.Exists()) {
@@ -2666,18 +2667,21 @@ bool MediaDecoderStateMachine::IsStateMa
 {
   MOZ_ASSERT(OnTaskQueue());
   return mDispatchedStateMachine || mDelayedScheduler.IsScheduled();
 }
 
 bool MediaDecoderStateMachine::IsVideoDecodeSuspended() const
 {
   MOZ_ASSERT(OnTaskQueue());
+#if 0
   return (MediaPrefs::MDSMSuspendBackgroundVideoEnabled() && mVideoDecodeSuspended) ||
          mIsReaderSuspended;
+#endif
+  return mIsReaderSuspended;
 }
 
 void
 MediaDecoderStateMachine::LogicalPlaybackRateChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (mLogicalPlaybackRate == 0) {
@@ -2959,16 +2963,17 @@ MediaDecoderStateMachine::VideoRequestSt
 }
 
 void
 MediaDecoderStateMachine::OnSuspendTimerResolved()
 {
   DECODER_LOG("OnSuspendTimerResolved");
   mVideoDecodeSuspendTimer.CompleteRequest();
   mVideoDecodeSuspended = true;
+  mReader->SetVideoBlankDecode(true);
 }
 
 void
 MediaDecoderStateMachine::OnSuspendTimerRejected()
 {
   DECODER_LOG("OnSuspendTimerRejected");
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(!mVideoDecodeSuspended);
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -401,35 +401,41 @@ MediaFormatReader::EnsureDecoderCreated(
       mPlatform->SetCDMProxy(mCDMProxy);
 #else
       // EME not supported.
       return false;
 #endif
     }
   }
 
+  RefPtr<PDMFactory> platform = mPlatform;
+  if (decoder.mIsBlankDecode) {
+    platform = new PDMFactory(true);
+  }
+  MOZ_ASSERT(platform, "No PDM is created.");
+
   decoder.mDecoderInitialized = false;
 
   MonitorAutoLock mon(decoder.mMonitor);
 
   switch (aTrack) {
     case TrackType::kAudioTrack: {
-      decoder.mDecoder = mPlatform->CreateDecoder({
+      decoder.mDecoder = platform->CreateDecoder({
         decoder.mInfo ? *decoder.mInfo->GetAsAudioInfo() : mInfo.mAudio,
         decoder.mTaskQueue,
         decoder.mCallback.get(),
         mCrashHelper
       });
       break;
     }
 
     case TrackType::kVideoTrack: {
       // Decoders use the layers backend to decide if they can use hardware decoding,
       // so specify LAYERS_NONE if we want to forcibly disable it.
-      decoder.mDecoder = mPlatform->CreateDecoder({
+      decoder.mDecoder = platform->CreateDecoder({
         mVideo.mInfo ? *mVideo.mInfo->GetAsVideoInfo() : mInfo.mVideo,
         decoder.mTaskQueue,
         decoder.mCallback.get(),
         mLayersBackendType,
         GetImageContainer(),
         mCrashHelper
       });
       break;
@@ -712,16 +718,21 @@ MediaFormatReader::NotifyDrainComplete(T
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
   LOG("%s", TrackTypeToStr(aTrack));
   if (!decoder.mOutputRequested) {
     LOG("MediaFormatReader called DrainComplete() before flushing, ignoring.");
     return;
   }
   decoder.mDrainComplete = true;
+
+  if (decoder.mIsBlankDecode) {
+    decoder.ShutdownDecoder();
+  }
+
   ScheduleUpdate(aTrack);
 }
 
 void
 MediaFormatReader::NotifyError(TrackType aTrack, MediaDataDecoderError aError)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("%s Decoding error", TrackTypeToStr(aTrack));
@@ -2020,9 +2031,43 @@ MediaFormatReader::GetMozDebugReaderData
                               mVideo.mNumSamplesInput, mVideo.mNumSamplesOutput,
                               unsigned(size_t(mVideo.mSizeOfQueue)),
                               unsigned(mVideo.mOutput.Length()),
                               mVideo.mWaitingForData, mVideo.mLastStreamSourceID);
   }
   aString += NS_ConvertUTF8toUTF16(result);
 }
 
+void
+MediaFormatReader::SetVideoBlankDecode(bool aIsBlankDecode)
+{
+  OwnerThread()->Dispatch(
+    NewRunnableMethod<TrackType, bool>(this, &MediaFormatReader::SetBlankDecode,
+                                       TrackType::kVideoTrack, aIsBlankDecode));
+
+}
+
+void
+MediaFormatReader::SetBlankDecode(TrackType aTrack, bool aIsBlankDecode)
+{
+  MOZ_ASSERT(self->OnTaskQueue());
+  auto& decoder = GetDecoderData(aTrack);
+
+  LOG("%s, decoder.mIsBlankDecode = %d => aIsBlankDecode = %d",
+      TrackTypeToStr(aTrack), decoder.mIsBlankDecode, aIsBlankDecode);
+
+  if (decoder.mIsBlankDecode == aIsBlankDecode) {
+    return;
+  }
+
+  decoder.mIsBlankDecode = aIsBlankDecode;
+
+  if (decoder.mIsBlankDecode) {
+    decoder.mNeedDraining = true;
+  } else {
+    decoder.ShutdownDecoder();
+  }
+
+  ScheduleUpdate(aTrack);
+
+}
+
 } // namespace mozilla
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -96,16 +96,18 @@ public:
 #ifdef MOZ_EME
   void SetCDMProxy(CDMProxy* aProxy) override;
 #endif
 
   // Returns a string describing the state of the decoder data.
   // Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
+  void SetVideoBlankDecode(bool aIsBlankDecode) override;
+
 private:
 
   bool HasVideo() { return mVideo.mTrackDemuxer; }
   bool HasAudio() { return mAudio.mTrackDemuxer; }
 
   bool IsWaitingOnCDMResource();
 
   bool InitDemuxer();
@@ -248,16 +250,17 @@ private:
       , mMaxConsecutiveError(aNumOfMaxError)
       , mNumSamplesInput(0)
       , mNumSamplesOutput(0)
       , mNumSamplesOutputTotal(0)
       , mNumSamplesSkippedTotal(0)
       , mSizeOfQueue(0)
       , mIsHardwareAccelerated(false)
       , mLastStreamSourceID(UINT32_MAX)
+      , mIsBlankDecode(false)
     {}
 
     MediaFormatReader* mOwner;
     // Disambiguate Audio vs Video.
     MediaData::Type mType;
     RefPtr<MediaTrackDemuxer> mTrackDemuxer;
     // TaskQueue on which decoder can choose to decode.
     // Only non-null up until the decoder is created.
@@ -422,16 +425,19 @@ private:
     Atomic<bool> mIsHardwareAccelerated;
     // Sample format monitoring.
     uint32_t mLastStreamSourceID;
     Maybe<uint32_t> mNextStreamSourceID;
     media::TimeIntervals mTimeRanges;
     Maybe<media::TimeUnit> mLastTimeRangesEnd;
     RefPtr<SharedTrackInfo> mInfo;
     Maybe<media::TimeUnit> mFirstDemuxedSampleTime;
+
+    // Use BlankDecoderModule or not.
+    bool mIsBlankDecode;
   };
 
   class DecoderDataWithPromise : public DecoderData {
   public:
     DecoderDataWithPromise(MediaFormatReader* aOwner,
                            MediaData::Type aType,
                            uint32_t aDecodeAhead,
                            uint32_t aNumOfMaxError)
@@ -567,13 +573,15 @@ private:
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
   layers::ImageContainer* GetImageContainer();
 
 #ifdef MOZ_EME
   RefPtr<CDMProxy> mCDMProxy;
 #endif
   RefPtr<GMPCrashHelper> mCrashHelper;
+
+  void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -75,16 +75,22 @@ StaticAutoPtr<PDMFactoryImpl> PDMFactory
 StaticMutex PDMFactory::sMonitor;
 
 PDMFactory::PDMFactory()
 {
   EnsureInit();
   CreatePDMs();
 }
 
+PDMFactory::PDMFactory(bool aUseBlankDecodeModule)
+{
+  EnsureInit();
+  CreatePDMs(aUseBlankDecodeModule);
+}
+
 PDMFactory::~PDMFactory()
 {
 }
 
 void
 PDMFactory::EnsureInit() const
 {
   StaticMutexAutoLock mon(sMonitor);
@@ -195,21 +201,21 @@ PDMFactory::SupportsMimeType(const nsACS
   if (mEMEPDM) {
     return mEMEPDM->SupportsMimeType(aMimeType, aDiagnostics);
   }
   RefPtr<PlatformDecoderModule> current = GetDecoder(aMimeType, aDiagnostics);
   return !!current;
 }
 
 void
-PDMFactory::CreatePDMs()
+PDMFactory::CreatePDMs(bool aUseBlankDecoderModule /* = false */)
 {
   RefPtr<PlatformDecoderModule> m;
 
-  if (MediaPrefs::PDMUseBlankDecoder()) {
+  if (MediaPrefs::PDMUseBlankDecoder() || aUseBlankDecoderModule) {
     m = CreateBlankDecoderModule();
     StartupPDM(m);
     // The Blank PDM SupportsMimeType reports true for all codecs; the creation
     // of its decoder is infallible. As such it will be used for all media, we
     // can stop creating more PDM from this point.
     return;
   }
 
--- a/dom/media/platforms/PDMFactory.h
+++ b/dom/media/platforms/PDMFactory.h
@@ -19,16 +19,18 @@ class PDMFactoryImpl;
 template<class T> class StaticAutoPtr;
 
 class PDMFactory final {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PDMFactory)
 
   PDMFactory();
 
+  explicit PDMFactory(bool aUseBlankDecodeModule);
+
   // Factory method that creates the appropriate PlatformDecoderModule for
   // the platform we're running on. Caller is responsible for deleting this
   // instance. It's expected that there will be multiple
   // PlatformDecoderModules alive at the same time.
   // This is called on the decode task queue.
   already_AddRefed<MediaDataDecoder>
   CreateDecoder(const CreateDecoderParams& aParams);
 
@@ -41,17 +43,17 @@ public:
   // does not decode, we create a PDM and use that to create MediaDataDecoders
   // that we use on on aTaskQueue to decode the decrypted stream.
   // This is called on the decode task queue.
   void SetCDMProxy(CDMProxy* aProxy);
 #endif
 
 private:
   virtual ~PDMFactory();
-  void CreatePDMs();
+  void CreatePDMs(bool aForceBlankDecoderModule = false);
   // Startup the provided PDM and add it to our list if successful.
   bool StartupPDM(PlatformDecoderModule* aPDM);
   // Returns the first PDM in our list supporting the mimetype.
   already_AddRefed<PlatformDecoderModule>
   GetDecoder(const nsACString& aMimeType,
              DecoderDoctorDiagnostics* aDiagnostics) const;
 
   already_AddRefed<MediaDataDecoder>