Bug 1336358: P2. Drain Android's RemoteDataDecoder one frame at a time. r=jolin draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 07 Feb 2017 23:11:32 +0100
changeset 483579 c5a79444d9786a4c249fc5843402bb28718c1b24
parent 483578 242a34c1d6ed95dc14dcd3ca0a80fdc5ffcb6b86
child 483580 b59a491711a3db01aaee945906e91e30e45130eb
push id45346
push userbmo:jyavenard@mozilla.com
push dateTue, 14 Feb 2017 15:11:15 +0000
reviewersjolin
bugs1336358
milestone54.0a1
Bug 1336358: P2. Drain Android's RemoteDataDecoder one frame at a time. r=jolin MozReview-Commit-ID: A9R9dR71FTB
dom/media/platforms/android/RemoteDataDecoder.cpp
dom/media/platforms/android/RemoteDataDecoder.h
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -76,18 +76,19 @@ public:
   }
 
   virtual void HandleError(const MediaResult& aError) = 0;
 
   void OnError(bool aIsFatal)
   {
     if (!mCanceled) {
       HandleError(
-        aIsFatal ? MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__)
-                 : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__));
+        aIsFatal
+        ? MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, __func__)
+        : MediaResult(NS_ERROR_DOM_MEDIA_DECODE_ERR, __func__));
     }
   }
 
   void DisposeNative()
   {
     // TODO
   }
 
@@ -141,17 +142,17 @@ public:
 
   class CallbacksSupport final : public JavaCallbacksSupport
   {
   public:
     CallbacksSupport(RemoteVideoDecoder* aDecoder) : mDecoder(aDecoder) { }
 
     void HandleInputExhausted() override
     {
-      mDecoder->InputExhausted();
+      mDecoder->ReturnDecodedData();
     }
 
     void HandleOutput(Sample::Param aSample) override
     {
       UniquePtr<VideoData::Listener>
         releaseSample(new RenderOrReleaseOutput(mDecoder->mJavaDecoder, aSample));
 
       BufferInfo::LocalRef info = aSample->Info();
@@ -309,17 +310,17 @@ public:
 
     bool formatHasCSD = false;
     NS_ENSURE_SUCCESS_VOID(
       aFormat->ContainsKey(NS_LITERAL_STRING("csd-0"), &formatHasCSD));
 
     if (!formatHasCSD && aConfig.mCodecSpecificConfig->Length() >= 2) {
       jni::ByteBuffer::LocalRef buffer(env);
       buffer = jni::ByteBuffer::New(aConfig.mCodecSpecificConfig->Elements(),
-          aConfig.mCodecSpecificConfig->Length());
+                                    aConfig.mCodecSpecificConfig->Length());
       NS_ENSURE_SUCCESS_VOID(
         aFormat->SetByteBuffer(NS_LITERAL_STRING("csd-0"), buffer));
     }
   }
 
   RefPtr<InitPromise> Init() override
   {
     // Register native methods.
@@ -342,17 +343,17 @@ public:
 private:
   class CallbacksSupport final : public JavaCallbacksSupport
   {
   public:
     CallbacksSupport(RemoteAudioDecoder* aDecoder) : mDecoder(aDecoder) { }
 
     void HandleInputExhausted() override
     {
-      mDecoder->InputExhausted();
+      mDecoder->ReturnDecodedData();
     }
 
     void HandleOutput(Sample::Param aSample) override
     {
       BufferInfo::LocalRef info = aSample->Info();
 
       int32_t flags;
       bool ok = NS_SUCCEEDED(info->Flags(&flags));
@@ -477,36 +478,50 @@ RemoteDataDecoder::RemoteDataDecoder(Med
 {
 }
 
 RefPtr<MediaDataDecoder::FlushPromise>
 RemoteDataDecoder::Flush()
 {
   RefPtr<RemoteDataDecoder> self = this;
   return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    mDecodedData.Clear();
     mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
+    mDrainStatus = DrainStatus::DRAINED;
     mJavaDecoder->Flush();
     return FlushPromise::CreateAndResolve(true, __func__);
   });
 }
 
 RefPtr<MediaDataDecoder::DecodePromise>
 RemoteDataDecoder::Drain()
 {
   RefPtr<RemoteDataDecoder> self = this;
   return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
+    if (mDrainStatus == DrainStatus::DRAINED) {
+      // There's no operation to perform other than returning any already
+      // decoded data.
+      ReturnDecodedData();
+      return p;
+    }
+
+    if (mDrainStatus == DrainStatus::DRAINING) {
+      // Draining operation already pending, let it complete its course.
+      return p;
+    }
+
     BufferInfo::LocalRef bufferInfo;
     nsresult rv = BufferInfo::New(&bufferInfo);
     if (NS_FAILED(rv)) {
       return DecodePromise::CreateAndReject(NS_ERROR_OUT_OF_MEMORY, __func__);
     }
+    mDrainStatus = DrainStatus::DRAINING;
     bufferInfo->Set(0, 0, -1, MediaCodec::BUFFER_FLAG_END_OF_STREAM);
-
-    RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
     mJavaDecoder->Input(nullptr, bufferInfo, nullptr);
     return p;
   });
 }
 
 RefPtr<ShutdownPromise>
 RemoteDataDecoder::Shutdown()
 {
@@ -550,16 +565,17 @@ RemoteDataDecoder::Decode(MediaRawData* 
     BufferInfo::LocalRef bufferInfo;
     nsresult rv = BufferInfo::New(&bufferInfo);
     if (NS_FAILED(rv)) {
       return DecodePromise::CreateAndReject(
         MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
     }
     bufferInfo->Set(0, sample->Size(), sample->mTime, 0);
 
+    mDrainStatus = DrainStatus::DRAINABLE;
     RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
     mJavaDecoder->Input(bytes, bufferInfo, GetCryptoInfoFromSample(sample));
     return p;
   });
 }
 
 void
 RemoteDataDecoder::Output(MediaData* aSample)
@@ -569,60 +585,66 @@ RemoteDataDecoder::Output(MediaData* aSa
       NewRunnableMethod<MediaData*>(this, &RemoteDataDecoder::Output, aSample));
     return;
   }
   AssertOnTaskQueue();
   if (mShutdown) {
     return;
   }
   mDecodedData.AppendElement(aSample);
+  ReturnDecodedData();
 }
 
 void
-RemoteDataDecoder::InputExhausted()
+RemoteDataDecoder::ReturnDecodedData()
 {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     mTaskQueue->Dispatch(
-      NewRunnableMethod(this, &RemoteDataDecoder::InputExhausted));
+      NewRunnableMethod(this, &RemoteDataDecoder::ReturnDecodedData));
     return;
   }
   AssertOnTaskQueue();
   if (mShutdown) {
     return;
   }
-  mDecodePromise.ResolveIfExists(mDecodedData, __func__);
-  mDecodedData.Clear();
+  // We only want to clear mDecodedData when we have resolved the promises.
+  if (!mDecodePromise.IsEmpty()) {
+    mDecodePromise.Resolve(mDecodedData, __func__);
+    mDecodedData.Clear();
+  } else if (!mDrainPromise.IsEmpty()) {
+    mDrainPromise.Resolve(mDecodedData, __func__);
+    mDecodedData.Clear();
+  }
 }
 
 void
 RemoteDataDecoder::DrainComplete()
 {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     mTaskQueue->Dispatch(
       NewRunnableMethod(this, &RemoteDataDecoder::DrainComplete));
     return;
   }
   AssertOnTaskQueue();
   if (mShutdown) {
     return;
   }
-  mDrainPromise.ResolveIfExists(mDecodedData, __func__);
-  mDecodedData.Clear();
+  mDrainStatus = DrainStatus::DRAINED;
+  ReturnDecodedData();
 }
 
 void
 RemoteDataDecoder::Error(const MediaResult& aError)
 {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     mTaskQueue->Dispatch(
       NewRunnableMethod<MediaResult>(this, &RemoteDataDecoder::Error, aError));
     return;
   }
   AssertOnTaskQueue();
   if (mShutdown) {
     return;
   }
   mDecodePromise.RejectIfExists(aError, __func__);
   mDrainPromise.RejectIfExists(aError, __func__);
-  mDecodedData.Clear();
 }
 
 } // mozilla
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -43,17 +43,17 @@ protected:
   RemoteDataDecoder(MediaData::Type aType,
                     const nsACString& aMimeType,
                     java::sdk::MediaFormat::Param aFormat,
                     const nsString& aDrmStubId, TaskQueue* aTaskQueue);
 
   // Methods only called on mTaskQueue.
   RefPtr<ShutdownPromise> ProcessShutdown();
   void Output(MediaData* aSample);
-  void InputExhausted();
+  void ReturnDecodedData();
   void DrainComplete();
   void Error(const MediaResult& aError);
   void AssertOnTaskQueue()
   {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   }
 
   MediaData::Type mType;
@@ -65,14 +65,21 @@ protected:
   java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
   nsString mDrmStubId;
 
   RefPtr<TaskQueue> mTaskQueue;
   // Only ever accessed on mTaskqueue.
   bool mShutdown = false;
   MozPromiseHolder<DecodePromise> mDecodePromise;
   MozPromiseHolder<DecodePromise> mDrainPromise;
+  enum class DrainStatus
+  {
+    DRAINED,
+    DRAINABLE,
+    DRAINING,
+  };
+  DrainStatus mDrainStatus = DrainStatus::DRAINED;
   DecodedData mDecodedData;
 };
 
 } // namespace mozilla
 
 #endif