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 480643 4ba2b28336dd1644c873d6d6a14bfc486fcde0da
parent 480640 a655fc536caedd4815f58531d47b0d152e2b1b44
child 545021 303325ad4881be83ad4621bb8142e653c61a5fed
push id44615
push userbmo:jyavenard@mozilla.com
push dateWed, 08 Feb 2017 19:22:44 +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
@@ -140,17 +140,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
     {
       Maybe<int64_t> durationUs = mDecoder->mInputDurations.Get();
       if (!durationUs) {
         return;
       }
@@ -313,17 +313,18 @@ private:
     }
 
   private:
     Mutex mMutex; // To protect mValues.
     std::deque<int64_t> mValues;
   };
 
   layers::ImageContainer* mImageContainer;
-  const VideoInfo mConfig;
+  // This must be a reference until bug 1336431 is resolved.
+  const VideoInfo& mConfig;
   RefPtr<AndroidSurfaceTexture> mSurfaceTexture;
   DurationQueue mInputDurations;
   bool mIsCodecSupportAdaptivePlayback = false;
 };
 
 class RemoteAudioDecoder : public RemoteDataDecoder
 {
 public:
@@ -338,17 +339,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.
@@ -371,17 +372,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));
@@ -506,37 +507,52 @@ RemoteDataDecoder::RemoteDataDecoder(Med
 {
 }
 
 RefPtr<MediaDataDecoder::FlushPromise>
 RemoteDataDecoder::Flush()
 {
   RefPtr<RemoteDataDecoder> self = this;
   return InvokeAsync(mTaskQueue, __func__, [self, this]() {
+    mDrained = true;
+    mDraining = false;
+    mDecodedData.Clear();
     mDecodePromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     mDrainPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
     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 (mDrained) {
+      // There's no operation to perform other than returning any already
+      // decoded data.
+      ReturnDecodedData();
+      return p;
+    }
+
+    if (mDraining) {
+      // 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__);
     }
     bufferInfo->Set(0, 0, -1, MediaCodec::BUFFER_FLAG_END_OF_STREAM);
-
-    RefPtr<DecodePromise> p = mDrainPromise.Ensure(__func__);
     mJavaDecoder->Input(nullptr, bufferInfo, nullptr);
+    mDraining = true;
     return p;
   });
 }
 
 RefPtr<ShutdownPromise>
 RemoteDataDecoder::Shutdown()
 {
   LOG("");
@@ -581,16 +597,17 @@ RemoteDataDecoder::Decode(MediaRawData* 
     if (NS_FAILED(rv)) {
       return DecodePromise::CreateAndReject(
         MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
     }
     bufferInfo->Set(0, sample->Size(), sample->mTime, 0);
 
     RefPtr<DecodePromise> p = mDecodePromise.Ensure(__func__);
     mJavaDecoder->Input(bytes, bufferInfo, GetCryptoInfoFromSample(sample));
+    mDrained = false;
     return p;
   });
 }
 
 void
 RemoteDataDecoder::Output(MediaData* aSample)
 {
   if (!mTaskQueue->IsCurrentThreadIn()) {
@@ -598,48 +615,56 @@ 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();
+  mDraining = false;
+  mDrained = true;
+  ReturnDecodedData();
 }
 
 void
 RemoteDataDecoder::Error(const MediaResult& aError)
 {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     mTaskQueue->Dispatch(
       NewRunnableMethod<MediaResult>(this, &RemoteDataDecoder::Error, aError));
--- 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;
@@ -63,16 +63,20 @@ protected:
 
   java::CodecProxy::GlobalRef mJavaDecoder;
   java::CodecProxy::NativeCallbacks::GlobalRef mJavaCallbacks;
   nsString mDrmStubId;
 
   RefPtr<TaskQueue> mTaskQueue;
   // Only ever accessed on mTaskqueue.
   bool mShutdown = false;
+  // Indicates if the decoder has already been drained.
+  bool mDrained = true;
+  // Indicates if there's a draining operation pending.
+  bool mDraining = false;
   MozPromiseHolder<DecodePromise> mDecodePromise;
   MozPromiseHolder<DecodePromise> mDrainPromise;
   DecodedData mDecodedData;
 };
 
 } // namespace mozilla
 
 #endif