Bug 1370805: P2. Let current operation completes before continuing. r?jwwang draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 07 Jun 2017 15:10:04 +0200
changeset 590901 17862b4659ddc3d4f719323ae962e5391cafd64f
parent 590361 913f441a66637d5ddfbdc100f36751350174d261
child 590902 2953be3b92d698326a8c50a63a48220dc8f73bc8
push id62873
push userbmo:jyavenard@mozilla.com
push dateThu, 08 Jun 2017 09:12:40 +0000
reviewersjwwang
bugs1370805
milestone55.0a1
Bug 1370805: P2. Let current operation completes before continuing. r?jwwang This is a simpler approach required as both InitPromise and FlushPromise are exclusives. It's in practice simpler too. MozReview-Commit-ID: ItaAhC0Bk8T
dom/media/platforms/wrappers/H264Converter.cpp
dom/media/platforms/wrappers/H264Converter.h
--- a/dom/media/platforms/wrappers/H264Converter.cpp
+++ b/dom/media/platforms/wrappers/H264Converter.cpp
@@ -55,16 +55,18 @@ H264Converter::Init()
   // We haven't been able to initialize a decoder due to a missing SPS/PPS.
   return MediaDataDecoder::InitPromise::CreateAndResolve(
            TrackType::kVideoTrack, __func__);
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 H264Converter::Decode(MediaRawData* aSample)
 {
+  MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Flush operatin didn't complete");
+
   MOZ_RELEASE_ASSERT(!mDecodePromiseRequest.Exists()
                      && !mInitPromiseRequest.Exists(),
                      "Can't request a new decode until previous one completed");
 
   if (!mp4_demuxer::AnnexB::ConvertSampleToAVCC(aSample)) {
     // We need AVCC content to be able to later parse the SPS.
     // This is a no-op if the data is already AVCC.
     return DecodePromise::CreateAndReject(
@@ -127,20 +129,47 @@ H264Converter::Decode(MediaRawData* aSam
 
   return mDecoder->Decode(aSample);
 }
 
 RefPtr<MediaDataDecoder::FlushPromise>
 H264Converter::Flush()
 {
   mDecodePromiseRequest.DisconnectIfExists();
-  mFlushRequest.DisconnectIfExists();
-  mShutdownRequest.DisconnectIfExists();
   mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mNeedKeyframe = true;
+
+  MOZ_RELEASE_ASSERT(mFlushPromise.IsEmpty(), "Previous flush didn't complete");
+
+  /*
+      When we detect a change of content in the H264 stream, we first flush the
+    current decoder (1), shut it down (2) create a new decoder and initialize
+    it (3).
+    It is possible possible for H264Converter::Flush to be called during any of
+    those time.
+    If during (1):
+      - mFlushRequest will not be empty.
+      - The old decoder can still be used, with the current extradata as stored
+        in mCurrentConfig.mExtraData.
+
+    If during (2):
+      - mShutdownRequest won't be empty.
+      - mDecoder is empty.
+      - The old decoder is no longer referenced by the H264Converter.
+
+    If during (3):
+      - mInitPromiseRequest won't be empty.
+      - mDecoder is set but not usable yet.
+  */
+
+  if (mFlushRequest.Exists() || mShutdownRequest.Exists() ||
+      mInitPromiseRequest.Exists()) {
+    // We let the current decoder complete and will resume after.
+    return mFlushPromise.Ensure(__func__);
+  }
   if (mDecoder) {
     return mDecoder->Flush();
   }
   return FlushPromise::CreateAndResolve(true, __func__);
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 H264Converter::Drain()
@@ -154,17 +183,20 @@ H264Converter::Drain()
 
 RefPtr<ShutdownPromise>
 H264Converter::Shutdown()
 {
   mInitPromiseRequest.DisconnectIfExists();
   mDecodePromiseRequest.DisconnectIfExists();
   mFlushRequest.DisconnectIfExists();
   mShutdownRequest.DisconnectIfExists();
+  mFlushPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mPendingSample = nullptr;
+  mNeedAVCC.reset();
+
   if (mShutdownPromise) {
     // We have a shutdown in progress, return that promise instead as we can't
     // shutdown a decoder twice.
     return mShutdownPromise.forget();
   }
   if (mDecoder) {
     RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
     return decoder->Shutdown();
@@ -273,23 +305,36 @@ H264Converter::OnDecoderInitDone(const T
 {
   mInitPromiseRequest.Complete();
   RefPtr<MediaRawData> sample = mPendingSample.forget();
 
   mNeedAVCC =
     Some(mDecoder->NeedsConversion() == ConversionRequired::kNeedAVCC);
   mCanRecycleDecoder = Some(CanRecycleDecoder());
 
+  if (!mFlushPromise.IsEmpty()) {
+    // A Flush is pending, abort the current operation.
+    mFlushPromise.Resolve(true, __func__);
+    return;
+  }
+
   DecodeFirstSample(sample);
 }
 
 void
 H264Converter::OnDecoderInitFailed(const MediaResult& aError)
 {
   mInitPromiseRequest.Complete();
+
+  if (!mFlushPromise.IsEmpty()) {
+    // A Flush is pending, abort the current operation.
+    mFlushPromise.Reject(aError, __func__);
+    return;
+  }
+
   mDecodePromise.Reject(
     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                 RESULT_DETAIL("Unable to initialize H264 decoder")),
     __func__);
 }
 
 bool
 H264Converter::CanRecycleDecoder() const
@@ -376,39 +421,57 @@ H264Converter::CheckForSPSChange(MediaRa
   // The SPS has changed, signal to flush the current decoder and create a
   // new one.
   RefPtr<H264Converter> self = this;
   mDecoder->Flush()
     ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
            __func__,
            [self, sample, this]() {
              mFlushRequest.Complete();
+
+             if (!mFlushPromise.IsEmpty()) {
+               // A Flush is pending, abort the current operation.
+               mFlushPromise.Resolve(true, __func__);
+               return;
+             }
+
              mShutdownPromise = Shutdown();
              mShutdownPromise
                ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
                       __func__,
                       [self, sample, this]() {
                         mShutdownRequest.Complete();
                         mShutdownPromise = nullptr;
-                        mNeedAVCC.reset();
+
+                        if (!mFlushPromise.IsEmpty()) {
+                          // A Flush is pending, abort the current operation.
+                          mFlushPromise.Resolve(true, __func__);
+                          return;
+                        }
+
                         nsresult rv = CreateDecoderAndInit(sample);
                         if (rv == NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER) {
                           // All good so far, will continue later.
                           return;
                         }
                         MOZ_ASSERT(NS_FAILED(rv));
                         mDecodePromise.Reject(rv, __func__);
                         return;
                       },
                       [] { MOZ_CRASH("Can't reach here'"); })
                ->Track(mShutdownRequest);
            },
            [self, this](const MediaResult& aError) {
              mFlushRequest.Complete();
-             mDecodePromise.Reject(aError, __func__);
+               if (!mFlushPromise.IsEmpty()) {
+                 // A Flush is pending, abort the current operation.
+                 mFlushPromise.Reject(aError, __func__);
+                 return;
+               }
+               mDecodePromise.Reject(aError, __func__);
            })
     ->Track(mFlushRequest);
   return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
 }
 
 void
 H264Converter::UpdateConfigFromExtraData(MediaByteBuffer* aExtraData)
 {
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -89,16 +89,17 @@ private:
   RefPtr<MediaRawData> mPendingSample;
   RefPtr<MediaDataDecoder> mDecoder;
   MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
   MozPromiseRequestHolder<DecodePromise> mDecodePromiseRequest;
   MozPromiseHolder<DecodePromise> mDecodePromise;
   MozPromiseRequestHolder<FlushPromise> mFlushRequest;
   MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
   RefPtr<ShutdownPromise> mShutdownPromise;
+  MozPromiseHolder<FlushPromise> mFlushPromise;
 
   RefPtr<GMPCrashHelper> mGMPCrashHelper;
   Maybe<bool> mNeedAVCC;
   nsresult mLastError;
   bool mNeedKeyframe = true;
   const TrackInfo::TrackType mType;
   MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
   const CreateDecoderParams::OptionSet mDecoderOptions;