Bug 1336431: P2. Asynchronously flush and shutdown decoder when SPS changes. r?cpearce draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 13 Feb 2017 00:04:56 +0100
changeset 483573 e774042b557fc9ca4ecb4bcbfd414cd48e07d019
parent 483572 8d7195f41336e5e8ee7bc1de1aa10771652d27eb
child 483574 e4743fa9af00b8073e348c0f7e004401cc213baf
push id45346
push userbmo:jyavenard@mozilla.com
push dateTue, 14 Feb 2017 15:11:15 +0000
reviewerscpearce
bugs1336431, 1319987
milestone54.0a1
Bug 1336431: P2. Asynchronously flush and shutdown decoder when SPS changes. r?cpearce Those steps were missed in bug 1319987. This is an area of the code rarely used under normal circumstances, asadaptive playback is only used with MSE. MozReview-Commit-ID: HZn3UxDy2GX
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
@@ -130,19 +130,27 @@ H264Converter::Drain()
     return mDecoder->Drain();
   }
   return DecodePromise::CreateAndResolve(DecodedData(), __func__);
 }
 
 RefPtr<ShutdownPromise>
 H264Converter::Shutdown()
 {
+  mInitPromiseRequest.DisconnectIfExists();
+  mDecodePromiseRequest.DisconnectIfExists();
+  mFlushRequest.DisconnectIfExists();
+  mShutdownRequest.DisconnectIfExists();
+  mPendingSample = nullptr;
+  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) {
-    mInitPromiseRequest.DisconnectIfExists();
-    mDecodePromiseRequest.DisconnectIfExists();
     RefPtr<MediaDataDecoder> decoder = mDecoder.forget();
     return decoder->Shutdown();
   }
   return ShutdownPromise::CreateAndResolve(true, __func__);
 }
 
 bool
 H264Converter::IsHardwareAccelerated(nsACString& aFailureReason) const
@@ -238,46 +246,46 @@ H264Converter::CreateDecoderAndInit(Medi
 }
 
 void
 H264Converter::OnDecoderInitDone(const TrackType aTrackType)
 {
   mInitPromiseRequest.Complete();
   RefPtr<MediaRawData> sample = mPendingSample.forget();
   if (mNeedKeyframe && !sample->mKeyframe) {
-    mDecodePromise.ResolveIfExists(DecodedData(), __func__);
+    mDecodePromise.Resolve(DecodedData(), __func__);
   }
   mNeedKeyframe = false;
   if (!mNeedAVCC
       && !mp4_demuxer::AnnexB::ConvertSampleToAnnexB(sample, mNeedKeyframe)) {
-    mDecodePromise.RejectIfExists(
+    mDecodePromise.Reject(
       MediaResult(NS_ERROR_OUT_OF_MEMORY,
                   RESULT_DETAIL("ConvertSampleToAnnexB")),
       __func__);
     return;
   }
   RefPtr<H264Converter> self = this;
   mDecoder->Decode(sample)
     ->Then(AbstractThread::GetCurrent()->AsTaskQueue(), __func__,
            [self, this](const MediaDataDecoder::DecodedData& aResults) {
              mDecodePromiseRequest.Complete();
-             mDecodePromise.ResolveIfExists(aResults, __func__);
+             mDecodePromise.Resolve(aResults, __func__);
            },
            [self, this](const MediaResult& aError) {
              mDecodePromiseRequest.Complete();
-             mDecodePromise.RejectIfExists(aError, __func__);
+             mDecodePromise.Reject(aError, __func__);
            })
     ->Track(mDecodePromiseRequest);
 }
 
 void
 H264Converter::OnDecoderInitFailed(const MediaResult& aError)
 {
   mInitPromiseRequest.Complete();
-  mDecodePromise.RejectIfExists(
+  mDecodePromise.Reject(
     MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                 RESULT_DETAIL("Unable to initialize H264 decoder")),
     __func__);
 }
 
 nsresult
 H264Converter::CheckForSPSChange(MediaRawData* aSample)
 {
@@ -293,19 +301,49 @@ H264Converter::CheckForSPSChange(MediaRa
       && mDecoder->SupportDecoderRecycling()) {
     // Do not recreate the decoder, reuse it.
     UpdateConfigFromExtraData(extra_data);
     mNeedKeyframe = true;
     return NS_OK;
   }
   // The SPS has changed, signal to flush the current decoder and create a
   // new one.
-  mDecoder->Flush();
-  Shutdown();
-  return CreateDecoderAndInit(aSample);
+  mPendingSample = aSample;
+  RefPtr<H264Converter> self = this;
+  mDecoder->Flush()
+    ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
+           __func__,
+           [self, this]() {
+             mFlushRequest.Complete();
+             mShutdownPromise = Shutdown();
+             mShutdownPromise
+               ->Then(AbstractThread::GetCurrent()->AsTaskQueue(),
+                      __func__,
+                      [self, this]() {
+                        mShutdownRequest.Complete();
+                        mShutdownPromise = nullptr;
+                        RefPtr<MediaRawData> sample = mPendingSample.forget();
+                        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__);
+           })
+    ->Track(mFlushRequest);
+  return NS_ERROR_DOM_MEDIA_INITIALIZING_DECODER;
 }
 
 void
 H264Converter::UpdateConfigFromExtraData(MediaByteBuffer* aExtraData)
 {
   mp4_demuxer::SPSData spsdata;
   if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtraData, spsdata)
       && spsdata.pic_width > 0
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -67,16 +67,20 @@ private:
   RefPtr<layers::KnowsCompositor> mKnowsCompositor;
   RefPtr<layers::ImageContainer> mImageContainer;
   const RefPtr<TaskQueue> mTaskQueue;
   RefPtr<MediaRawData> mPendingSample;
   RefPtr<MediaDataDecoder> mDecoder;
   MozPromiseRequestHolder<InitPromise> mInitPromiseRequest;
   MozPromiseRequestHolder<DecodePromise> mDecodePromiseRequest;
   MozPromiseHolder<DecodePromise> mDecodePromise;
+  MozPromiseRequestHolder<FlushPromise> mFlushRequest;
+  MozPromiseRequestHolder<ShutdownPromise> mShutdownRequest;
+  RefPtr<ShutdownPromise> mShutdownPromise;
+
   RefPtr<GMPCrashHelper> mGMPCrashHelper;
   bool mNeedAVCC;
   nsresult mLastError;
   bool mNeedKeyframe = true;
   const TrackInfo::TrackType mType;
   MediaEventProducer<TrackInfo::TrackType>* const mOnWaitingForKeyEvent;
 };