Bug 1312337. Part 1 - move creation/initialization of decoders into DecoderFactory. draft
authorJW Wang <jwwang@mozilla.com>
Wed, 26 Oct 2016 17:37:19 +0800
changeset 432040 3b32b92ab3867846a51bdfeb486e61d4e097a946
parent 431500 969c3295d3aa77931cca26eddb047d9d74bd9858
child 432041 9725e72bb2f515529ed3e09fade96a3214a7a043
push id34177
push userjwwang@mozilla.com
push dateTue, 01 Nov 2016 05:20:21 +0000
bugs1312337
milestone52.0a1
Bug 1312337. Part 1 - move creation/initialization of decoders into DecoderFactory. So m{Audio,Video}.mDecoder will never reference a half-baked decoder. MozReview-Commit-ID: FwuzaRR9ugf
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -32,16 +32,180 @@ using mozilla::layers::LayersBackend;
 static mozilla::LazyLogModule sFormatDecoderLog("MediaFormatReader");
 mozilla::LazyLogModule gMediaDemuxerLog("MediaDemuxer");
 
 #define LOG(arg, ...) MOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Debug, ("MediaFormatReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #define LOGV(arg, ...) MOZ_LOG(sFormatDecoderLog, mozilla::LogLevel::Verbose, ("MediaFormatReader(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 namespace mozilla {
 
+class MediaFormatReader::DecoderFactory
+{
+  using InitPromise = MediaDataDecoder::InitPromise;
+
+public:
+  explicit DecoderFactory(MediaFormatReader* aOwner) : mOwner(aOwner) {}
+  void CreateDecoder(TrackType aTrack);
+
+private:
+  enum class Stage : int8_t
+  {
+    None,
+    WaitForInit
+  };
+
+  struct Data
+  {
+    Stage mStage = Stage::None;
+    RefPtr<MediaDataDecoder> mDecoder;
+    MozPromiseRequestHolder<InitPromise> mInitPromise;
+    ~Data()
+    {
+      mInitPromise.DisconnectIfExists();
+      if (mDecoder) {
+        mDecoder->Shutdown();
+      }
+    }
+  } mAudio, mVideo;
+
+  void RunStage(TrackType aTrack);
+  MediaResult DoCreateDecoder(TrackType aTrack);
+  void DoInitDecoder(TrackType aTrack);
+
+  MediaFormatReader* const mOwner; // guaranteed to be valid by the owner.
+};
+
+void
+MediaFormatReader::DecoderFactory::CreateDecoder(TrackType aTrack)
+{
+  MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
+             aTrack == TrackInfo::kVideoTrack);
+  RunStage(aTrack);
+}
+
+void
+MediaFormatReader::DecoderFactory::RunStage(TrackType aTrack)
+{
+  auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
+
+  switch (data.mStage) {
+    case Stage::None: {
+      MOZ_ASSERT(!data.mDecoder);
+      MOZ_ASSERT(!data.mInitPromise.Exists());
+
+      MediaResult rv = DoCreateDecoder(aTrack);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Error constructing decoders");
+        data.mStage = Stage::None;
+        mOwner->NotifyError(aTrack, rv);
+        return;
+      }
+
+      DoInitDecoder(aTrack);
+      data.mStage = Stage::WaitForInit;
+      break;
+    }
+
+    case Stage::WaitForInit: {
+      MOZ_ASSERT(data.mDecoder);
+      MOZ_ASSERT(data.mInitPromise.Exists());
+      break;
+    }
+  }
+}
+
+MediaResult
+MediaFormatReader::DecoderFactory::DoCreateDecoder(TrackType aTrack)
+{
+  auto& ownerData = mOwner->GetDecoderData(aTrack);
+  auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
+
+  auto decoderCreatingError = "error creating audio decoder";
+  MediaResult result = MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, decoderCreatingError);
+
+  if (!mOwner->mPlatform) {
+    mOwner->mPlatform = new PDMFactory();
+    if (mOwner->IsEncrypted()) {
+      MOZ_ASSERT(mOwner->mCDMProxy);
+      mOwner->mPlatform->SetCDMProxy(mOwner->mCDMProxy);
+    }
+  }
+
+  switch (aTrack) {
+    case TrackInfo::kAudioTrack: {
+      data.mDecoder = mOwner->mPlatform->CreateDecoder({
+        ownerData.mInfo
+        ? *ownerData.mInfo->GetAsAudioInfo()
+        : *ownerData.mOriginalInfo->GetAsAudioInfo(),
+        ownerData.mTaskQueue,
+        ownerData.mCallback.get(),
+        mOwner->mCrashHelper,
+        ownerData.mIsBlankDecode,
+        &result
+      });
+      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.
+      data.mDecoder = mOwner->mPlatform->CreateDecoder({
+        ownerData.mInfo
+        ? *ownerData.mInfo->GetAsVideoInfo()
+        : *ownerData.mOriginalInfo->GetAsVideoInfo(),
+        ownerData.mTaskQueue,
+        ownerData.mCallback.get(),
+        mOwner->mKnowsCompositor,
+        mOwner->GetImageContainer(),
+        mOwner->mCrashHelper,
+        ownerData.mIsBlankDecode,
+        &result
+      });
+      break;
+    }
+
+    default:
+      break;
+  }
+
+  if (data.mDecoder) {
+    result = MediaResult(NS_OK);
+    return result;
+  }
+
+  ownerData.mDescription = decoderCreatingError;
+  return result;
+}
+
+void
+MediaFormatReader::DecoderFactory::DoInitDecoder(TrackType aTrack)
+{
+  auto& ownerData = mOwner->GetDecoderData(aTrack);
+  auto& data = aTrack == TrackInfo::kAudioTrack ? mAudio : mVideo;
+
+  data.mInitPromise.Begin(data.mDecoder->Init()->Then(
+    mOwner->OwnerThread(), __func__,
+    [this, &data, &ownerData] (TrackType aTrack) {
+      data.mInitPromise.Complete();
+      data.mStage = Stage::None;
+      MonitorAutoLock mon(ownerData.mMonitor);
+      ownerData.mDecoder = data.mDecoder.forget();
+      ownerData.mDescription = ownerData.mDecoder->GetDescriptionName();
+      mOwner->SetVideoDecodeThreshold();
+      mOwner->ScheduleUpdate(aTrack);
+    },
+    [this, &data, aTrack] (MediaResult aError) {
+      data.mInitPromise.Complete();
+      data.mStage = Stage::None;
+      data.mDecoder->Shutdown();
+      data.mDecoder = nullptr;
+      mOwner->NotifyError(aTrack, aError);
+    }));
+}
+
 static const char*
 TrackTypeToStr(TrackInfo::TrackType aTrack)
 {
   MOZ_ASSERT(aTrack == TrackInfo::kAudioTrack ||
              aTrack == TrackInfo::kVideoTrack ||
              aTrack == TrackInfo::kTextTrack);
   switch (aTrack) {
   case TrackInfo::kAudioTrack:
@@ -67,31 +231,33 @@ MediaFormatReader::MediaFormatReader(Abs
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
   , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
   , mInitDone(false)
   , mTrackDemuxersMayBlock(false)
   , mDemuxOnly(false)
   , mSeekScheduled(false)
   , mVideoFrameContainer(aVideoFrameContainer)
+  , mDecoderFactory(new DecoderFactory(this))
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
 }
 
 MediaFormatReader::~MediaFormatReader()
 {
   MOZ_COUNT_DTOR(MediaFormatReader);
 }
 
 RefPtr<ShutdownPromise>
 MediaFormatReader::Shutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
 
+  mDecoderFactory = nullptr;
   mDemuxerInitRequest.DisconnectIfExists();
   mMetadataPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mSeekPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mSkipRequest.DisconnectIfExists();
 
   if (mAudio.mDecoder) {
     Reset(TrackInfo::kAudioTrack);
     if (mAudio.HasPromise()) {
@@ -370,127 +536,16 @@ MediaFormatReader::IsEncrypted() const
 
 void
 MediaFormatReader::OnDemuxerInitFailed(const MediaResult& aError)
 {
   mDemuxerInitRequest.Complete();
   mMetadataPromise.Reject(aError, __func__);
 }
 
-MediaResult
-MediaFormatReader::EnsureDecoderCreated(TrackType aTrack)
-{
-  MOZ_ASSERT(OnTaskQueue());
-  MOZ_DIAGNOSTIC_ASSERT(!IsSuspended());
-
-  auto decoderCreatingError = "error creating decoder";
-  MediaResult result = MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, decoderCreatingError);
-  auto& decoder = GetDecoderData(aTrack);
-
-  if (decoder.mDecoder) {
-    result = NS_OK;
-    return result;
-  }
-
-  if (!mPlatform) {
-    mPlatform = new PDMFactory();
-    if (IsEncrypted()) {
-      MOZ_ASSERT(mCDMProxy);
-      mPlatform->SetCDMProxy(mCDMProxy);
-    }
-  }
-
-  decoder.mDecoderInitialized = false;
-
-  MonitorAutoLock mon(decoder.mMonitor);
-
-  switch (aTrack) {
-    case TrackType::kAudioTrack: {
-      decoder.mDecoder = mPlatform->CreateDecoder({
-        decoder.mInfo
-        ? *decoder.mInfo->GetAsAudioInfo()
-        : *decoder.mOriginalInfo->GetAsAudioInfo(),
-        decoder.mTaskQueue,
-        decoder.mCallback.get(),
-        mCrashHelper,
-        decoder.mIsBlankDecode,
-        &result
-      });
-      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.mInfo
-        ? *decoder.mInfo->GetAsVideoInfo()
-        : *decoder.mOriginalInfo->GetAsVideoInfo(),
-        decoder.mTaskQueue,
-        decoder.mCallback.get(),
-        mKnowsCompositor,
-        GetImageContainer(),
-        mCrashHelper,
-        decoder.mIsBlankDecode,
-        &result
-      });
-      break;
-    }
-    default:
-      break;
-  }
-  if (decoder.mDecoder) {
-    decoder.mDescription = decoder.mDecoder->GetDescriptionName();
-    result = MediaResult(NS_OK);
-    return result;
-  }
-
-  decoder.mDescription = decoderCreatingError;
-  return result;
-}
-
-bool
-MediaFormatReader::EnsureDecoderInitialized(TrackType aTrack)
-{
-  MOZ_ASSERT(OnTaskQueue());
-  MOZ_DIAGNOSTIC_ASSERT(!IsSuspended());
-
-  auto& decoder = GetDecoderData(aTrack);
-
-  if (!decoder.mDecoder || decoder.mInitPromise.Exists()) {
-    MOZ_ASSERT(decoder.mDecoder);
-    return false;
-  }
-  if (decoder.mDecoderInitialized) {
-    return true;
-  }
-
-  RefPtr<MediaFormatReader> self = this;
-  decoder.mInitPromise.Begin(decoder.mDecoder->Init()
-       ->Then(OwnerThread(), __func__,
-              [self] (TrackType aTrack) {
-                MOZ_DIAGNOSTIC_ASSERT(!self->IsSuspended());
-                auto& decoder = self->GetDecoderData(aTrack);
-                MOZ_DIAGNOSTIC_ASSERT(decoder.mDecoder);
-                decoder.mInitPromise.Complete();
-                decoder.mDecoderInitialized = true;
-                MonitorAutoLock mon(decoder.mMonitor);
-                decoder.mDescription = decoder.mDecoder->GetDescriptionName();
-                self->SetVideoDecodeThreshold();
-                self->ScheduleUpdate(aTrack);
-              },
-              [self, aTrack] (MediaResult aError) {
-                auto& decoder = self->GetDecoderData(aTrack);
-                decoder.mInitPromise.Complete();
-                decoder.ShutdownDecoder();
-                self->NotifyError(aTrack, aError);
-              }));
-  return false;
-}
-
 void
 MediaFormatReader::ReadUpdatedMetadata(MediaInfo* aInfo)
 {
   *aInfo = mInfo;
 }
 
 MediaFormatReader::DecoderData&
 MediaFormatReader::GetDecoderData(TrackType aTrack)
@@ -943,24 +998,18 @@ MediaFormatReader::HandleDemuxedSamples(
   }
 
   auto& decoder = GetDecoderData(aTrack);
 
   if (decoder.mQueuedSamples.IsEmpty()) {
     return;
   }
 
-  MediaResult rv = EnsureDecoderCreated(aTrack);
-  if (NS_FAILED(rv)) {
-    NS_WARNING("Error constructing decoders");
-    NotifyError(aTrack, rv);
-    return;
-  }
-
-  if (!EnsureDecoderInitialized(aTrack)) {
+  if (!decoder.mDecoder) {
+    mDecoderFactory->CreateDecoder(aTrack);
     return;
   }
 
   if (!ForceZeroStartTime() && decoder.mFirstDemuxedSampleTime.isNothing()) {
     decoder.mFirstDemuxedSampleTime.emplace(
       media::TimeUnit::FromMicroseconds(decoder.mQueuedSamples[0]->mTime));
   }
 
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -109,19 +109,16 @@ private:
 
   bool InitDemuxer();
   // Notify the demuxer that new data has been received.
   // The next queued task calling GetBuffered() is guaranteed to have up to date
   // buffered ranges.
   void NotifyDemuxer();
   void ReturnOutput(MediaData* aData, TrackType aTrack);
 
-  MediaResult EnsureDecoderCreated(TrackType aTrack);
-  bool EnsureDecoderInitialized(TrackType aTrack);
-
   // Enqueues a task to call Update(aTrack) on the decoder task queue.
   // Lock for corresponding track must be held.
   void ScheduleUpdate(TrackType aTrack);
   void Update(TrackType aTrack);
   // Handle actions should more data be received.
   // Returns true if no more action is required.
   bool UpdateReceivedNewData(TrackType aTrack);
   // Called when new samples need to be demuxed.
@@ -233,17 +230,16 @@ private:
       , mType(aType)
       , mMonitor("DecoderData")
       , mDescription("shutdown")
       , mUpdateScheduled(false)
       , mDemuxEOS(false)
       , mWaitingForData(false)
       , mWaitingForKey(false)
       , mReceivedNewData(false)
-      , mDecoderInitialized(false)
       , mOutputRequested(false)
       , mDecodePending(false)
       , mNeedDraining(false)
       , mDraining(false)
       , mDrainComplete(false)
       , mNumOfConsecutiveError(0)
       , mMaxConsecutiveError(aNumOfMaxError)
       , mNumSamplesInput(0)
@@ -268,17 +264,16 @@ private:
 
     // Monitor protecting mDescription and mDecoder.
     Monitor mMonitor;
     // The platform decoder.
     RefPtr<MediaDataDecoder> mDecoder;
     const char* mDescription;
     void ShutdownDecoder()
     {
-      mInitPromise.DisconnectIfExists();
       MonitorAutoLock mon(mMonitor);
       if (mDecoder) {
         mDecoder->Shutdown();
       }
       mDescription = "shutdown";
       mDecoder = nullptr;
     }
 
@@ -305,20 +300,16 @@ private:
     }
     bool IsWaiting() const
     {
       MOZ_ASSERT(mOwner->OnTaskQueue());
       return mWaitingForData || mWaitingForKey;
     }
 
     // MediaDataDecoder handler's variables.
-    // Decoder initialization promise holder.
-    MozPromiseRequestHolder<MediaDataDecoder::InitPromise> mInitPromise;
-    // False when decoder is created. True when decoder Init() promise is resolved.
-    bool mDecoderInitialized;
     bool mOutputRequested;
     // Set to true once the MediaDataDecoder has been fed a compressed sample.
     // No more samples will be passed to the decoder while true.
     // mDecodePending is reset when:
     // 1- The decoder calls InputExhausted
     // 2- The decoder is Flushed or Reset.
     bool mDecodePending;
     bool mNeedDraining;
@@ -576,13 +567,16 @@ private:
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
   layers::ImageContainer* GetImageContainer();
 
   RefPtr<CDMProxy> mCDMProxy;
 
   RefPtr<GMPCrashHelper> mCrashHelper;
 
   void SetBlankDecode(TrackType aTrack, bool aIsBlankDecode);
+
+  class DecoderFactory;
+  UniquePtr<DecoderFactory> mDecoderFactory;
 };
 
 } // namespace mozilla
 
 #endif