Bug 1349883 - Part 3: Resolve decode promise according to buffer status. r=jya, a=gchang
authorJohn Lin <jolin@mozilla.com>
Thu, 13 Apr 2017 18:48:00 +0800
changeset 393533 327f2348eb373976c561b418b50ffa0b136d7a71
parent 393532 e0352fcf12c17aee1f3e5ccd5c363b517bbdc0a3
child 393534 0e6f81e3f63e6ffd7bf03067975ce58755b55244
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjya, gchang
bugs1349883
milestone54.0a2
Bug 1349883 - Part 3: Resolve decode promise according to buffer status. r=jya, a=gchang MozReview-Commit-ID: GwCPHXW0fqK
dom/media/platforms/android/RemoteDataDecoder.cpp
dom/media/platforms/android/RemoteDataDecoder.h
mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
widget/android/fennec/FennecJNINatives.h
widget/android/fennec/FennecJNIWrappers.cpp
widget/android/fennec/FennecJNIWrappers.h
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -39,22 +39,22 @@ class JavaCallbacksSupport
 public:
   typedef CodecProxy::NativeCallbacks::Natives<JavaCallbacksSupport> Base;
   using Base::AttachNative;
 
   JavaCallbacksSupport() : mCanceled(false) { }
 
   virtual ~JavaCallbacksSupport() { }
 
-  virtual void HandleInputExhausted() = 0;
+  virtual void HandleInput(int64_t aTimestamp, bool aProcessed) = 0;
 
-  void OnInputExhausted()
+  void OnInputStatus(jlong aTimestamp, bool aProcessed)
   {
     if (!mCanceled) {
-      HandleInputExhausted();
+      HandleInput(aTimestamp, aProcessed);
     }
   }
 
   virtual void HandleOutput(Sample::Param aSample) = 0;
 
   void OnOutput(jni::Object::Param aSample)
   {
     if (!mCanceled) {
@@ -154,19 +154,19 @@ public:
     gfx::IntSize mDisplaySize;
   };
 
   class CallbacksSupport final : public JavaCallbacksSupport
   {
   public:
     CallbacksSupport(RemoteVideoDecoder* aDecoder) : mDecoder(aDecoder) { }
 
-    void HandleInputExhausted() override
+    void HandleInput(int64_t aTimestamp, bool aProcessed) override
     {
-      mDecoder->ReturnDecodedData();
+      mDecoder->UpdateInputStatus(aTimestamp, aProcessed);
     }
 
     void HandleOutput(Sample::Param aSample) override
     {
       UniquePtr<VideoData::Listener> releaseSample(
         new RenderOrReleaseOutput(mDecoder->mJavaDecoder, aSample));
 
       BufferInfo::LocalRef info = aSample->Info();
@@ -202,18 +202,17 @@ public:
           gl::OriginPos::BottomLeft);
 
         RefPtr<VideoData> v = VideoData::CreateFromImage(
           inputInfo.mDisplaySize, offset, presentationTimeUs, inputInfo.mDurationUs,
           img, !!(flags & MediaCodec::BUFFER_FLAG_SYNC_FRAME),
           presentationTimeUs);
 
         v->SetListener(Move(releaseSample));
-
-        mDecoder->Output(v);
+        mDecoder->UpdateOutputStatus(v);
       }
 
       if (isEOS) {
         mDecoder->DrainComplete();
       }
     }
 
     void HandleError(const MediaResult& aError) override
@@ -355,19 +354,19 @@ public:
   }
 
 private:
   class CallbacksSupport final : public JavaCallbacksSupport
   {
   public:
     CallbacksSupport(RemoteAudioDecoder* aDecoder) : mDecoder(aDecoder) { }
 
-    void HandleInputExhausted() override
+    void HandleInput(int64_t aTimestamp, bool aProcessed) override
     {
-      mDecoder->ReturnDecodedData();
+      mDecoder->UpdateInputStatus(aTimestamp, aProcessed);
     }
 
     void HandleOutput(Sample::Param aSample) override
     {
       BufferInfo::LocalRef info = aSample->Info();
 
       int32_t flags;
       bool ok = NS_SUCCEEDED(info->Flags(&flags));
@@ -405,17 +404,17 @@ private:
           jni::ByteBuffer::New(audio.get(), size);
         aSample->WriteToByteBuffer(dest);
 
         RefPtr<AudioData> data = new AudioData(
           0, presentationTimeUs,
           FramesToUsecs(numFrames, mOutputSampleRate).value(), numFrames,
           Move(audio), mOutputChannels, mOutputSampleRate);
 
-        mDecoder->Output(data);
+        mDecoder->UpdateOutputStatus(data);
       }
 
       if ((flags & MediaCodec::BUFFER_FLAG_END_OF_STREAM) != 0) {
         mDecoder->DrainComplete();
       }
     }
 
     void HandleOutputFormatChanged(MediaFormat::Param aFormat) override
@@ -495,25 +494,27 @@ RemoteDataDecoder::RemoteDataDecoder(Med
                                      MediaFormat::Param aFormat,
                                      const nsString& aDrmStubId,
                                      TaskQueue* aTaskQueue)
   : mType(aType)
   , mMimeType(aMimeType)
   , mFormat(aFormat)
   , mDrmStubId(aDrmStubId)
   , mTaskQueue(aTaskQueue)
+  , mNumPendingInputs(0)
 {
 }
 
 RefPtr<MediaDataDecoder::FlushPromise>
 RemoteDataDecoder::Flush()
 {
   RefPtr<RemoteDataDecoder> self = this;
   return InvokeAsync(mTaskQueue, __func__, [self, this]() {
     mDecodedData.Clear();
+    mNumPendingInputs = 0;
     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__);
   });
 }
 
@@ -600,21 +601,51 @@ RemoteDataDecoder::Decode(MediaRawData* 
            ? mDecodePromise.Ensure(__func__)
            : DecodePromise::CreateAndReject(
                MediaResult(NS_ERROR_OUT_OF_MEMORY, __func__), __func__);
 
   });
 }
 
 void
-RemoteDataDecoder::Output(MediaData* aSample)
+RemoteDataDecoder::UpdateInputStatus(int64_t aTimestamp, bool aProcessed)
 {
   if (!mTaskQueue->IsCurrentThreadIn()) {
     mTaskQueue->Dispatch(
-      NewRunnableMethod<MediaData*>(this, &RemoteDataDecoder::Output, aSample));
+      NewRunnableMethod<int64_t, bool>(this,
+                                       &RemoteDataDecoder::UpdateInputStatus,
+                                       aTimestamp,
+                                       aProcessed));
+    return;
+  }
+  AssertOnTaskQueue();
+  if (mShutdown) {
+    return;
+  }
+
+  if (!aProcessed) {
+    mNumPendingInputs++;
+  } else if (mNumPendingInputs > 0) {
+    mNumPendingInputs--;
+  }
+
+  if (mNumPendingInputs == 0 || // Input has been processed, request the next one.
+      !mDecodedData.IsEmpty()) { // Previous output arrived before Decode().
+    ReturnDecodedData();
+  }
+}
+
+void
+RemoteDataDecoder::UpdateOutputStatus(MediaData* aSample)
+{
+  if (!mTaskQueue->IsCurrentThreadIn()) {
+    mTaskQueue->Dispatch(
+      NewRunnableMethod<MediaData*>(this,
+                                    &RemoteDataDecoder::UpdateOutputStatus,
+                                    aSample));
     return;
   }
   AssertOnTaskQueue();
   if (mShutdown) {
     return;
   }
   mDecodedData.AppendElement(aSample);
   ReturnDecodedData();
--- a/dom/media/platforms/android/RemoteDataDecoder.h
+++ b/dom/media/platforms/android/RemoteDataDecoder.h
@@ -40,17 +40,18 @@ protected:
   virtual ~RemoteDataDecoder() { }
   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 UpdateInputStatus(int64_t aTimestamp, bool aProcessed);
+  void UpdateOutputStatus(MediaData* aSample);
   void ReturnDecodedData();
   void DrainComplete();
   void Error(const MediaResult& aError);
   void AssertOnTaskQueue()
   {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   }
 
@@ -71,13 +72,14 @@ protected:
   enum class DrainStatus
   {
     DRAINED,
     DRAINABLE,
     DRAINING,
   };
   DrainStatus mDrainStatus = DrainStatus::DRAINED;
   DecodedData mDecodedData;
+  size_t mNumPendingInputs;
 };
 
 } // namespace mozilla
 
 #endif
--- a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -29,25 +29,25 @@ public final class CodecProxy {
     private ICodec mRemote;
     private FormatParam mFormat;
     private Surface mOutputSurface;
     private CallbacksForwarder mCallbacks;
     private String mRemoteDrmStubId;
     private Queue<Sample> mSurfaceOutputs = new ConcurrentLinkedQueue<>();
 
     public interface Callbacks {
-        void onInputExhausted();
+        void onInputStatus(long timestamp, boolean processed);
         void onOutputFormatChanged(MediaFormat format);
         void onOutput(Sample output);
         void onError(boolean fatal);
     }
 
     @WrapForJNI
     public static class NativeCallbacks extends JNIObject implements Callbacks {
-        public native void onInputExhausted();
+        public native void onInputStatus(long timestamp, boolean processed);
         public native void onOutputFormatChanged(MediaFormat format);
         public native void onOutput(Sample output);
         public native void onError(boolean fatal);
 
         @Override // JNIObject
         protected native void disposeNative();
     }
 
@@ -57,24 +57,24 @@ public final class CodecProxy {
 
         CallbacksForwarder(Callbacks callbacks) {
             mCallbacks = callbacks;
         }
 
         @Override
         public synchronized void onInputQueued(long timestamp) throws RemoteException {
             if (!mEndOfInput) {
-                mCallbacks.onInputExhausted();
+                mCallbacks.onInputStatus(timestamp, true /* processed */);
             }
         }
 
         @Override
         public synchronized void onInputPending(long timestamp) throws RemoteException {
             if (!mEndOfInput) {
-                mCallbacks.onInputExhausted();
+                mCallbacks.onInputStatus(timestamp, false /* processed */);
             }
         }
 
         @Override
         public void onOutputFormatChanged(FormatParam format) throws RemoteException {
             mCallbacks.onOutputFormatChanged(format.asFormat());
         }
 
--- a/widget/android/fennec/FennecJNINatives.h
+++ b/widget/android/fennec/FennecJNINatives.h
@@ -163,19 +163,19 @@ const JNINativeMethod CodecProxy::Native
     mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::DisposeNative_t>(
             mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::DisposeNative_t, Impl>
             ::template Wrap<&Impl::DisposeNative>),
 
     mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnError_t>(
             mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnError_t, Impl>
             ::template Wrap<&Impl::OnError>),
 
-    mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnInputExhausted_t>(
-            mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnInputExhausted_t, Impl>
-            ::template Wrap<&Impl::OnInputExhausted>),
+    mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnInputStatus_t>(
+            mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnInputStatus_t, Impl>
+            ::template Wrap<&Impl::OnInputStatus>),
 
     mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnOutput_t>(
             mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnOutput_t, Impl>
             ::template Wrap<&Impl::OnOutput>),
 
     mozilla::jni::MakeNativeMethod<CodecProxy::NativeCallbacks::OnOutputFormatChanged_t>(
             mozilla::jni::NativeStub<CodecProxy::NativeCallbacks::OnOutputFormatChanged_t, Impl>
             ::template Wrap<&Impl::OnOutputFormatChanged>)
--- a/widget/android/fennec/FennecJNIWrappers.cpp
+++ b/widget/android/fennec/FennecJNIWrappers.cpp
@@ -241,18 +241,18 @@ auto CodecProxy::NativeCallbacks::New() 
 }
 
 constexpr char CodecProxy::NativeCallbacks::DisposeNative_t::name[];
 constexpr char CodecProxy::NativeCallbacks::DisposeNative_t::signature[];
 
 constexpr char CodecProxy::NativeCallbacks::OnError_t::name[];
 constexpr char CodecProxy::NativeCallbacks::OnError_t::signature[];
 
-constexpr char CodecProxy::NativeCallbacks::OnInputExhausted_t::name[];
-constexpr char CodecProxy::NativeCallbacks::OnInputExhausted_t::signature[];
+constexpr char CodecProxy::NativeCallbacks::OnInputStatus_t::name[];
+constexpr char CodecProxy::NativeCallbacks::OnInputStatus_t::signature[];
 
 constexpr char CodecProxy::NativeCallbacks::OnOutput_t::name[];
 constexpr char CodecProxy::NativeCallbacks::OnOutput_t::signature[];
 
 constexpr char CodecProxy::NativeCallbacks::OnOutputFormatChanged_t::name[];
 constexpr char CodecProxy::NativeCallbacks::OnOutputFormatChanged_t::signature[];
 
 const char MediaDrmProxy::name[] =
--- a/widget/android/fennec/FennecJNIWrappers.h
+++ b/widget/android/fennec/FennecJNIWrappers.h
@@ -847,24 +847,26 @@ public:
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };
 
-    struct OnInputExhausted_t {
+    struct OnInputStatus_t {
         typedef NativeCallbacks Owner;
         typedef void ReturnType;
         typedef void SetterType;
-        typedef mozilla::jni::Args<> Args;
-        static constexpr char name[] = "onInputExhausted";
+        typedef mozilla::jni::Args<
+                int64_t,
+                bool> Args;
+        static constexpr char name[] = "onInputStatus";
         static constexpr char signature[] =
-                "()V";
+                "(JZ)V";
         static const bool isStatic = false;
         static const mozilla::jni::ExceptionMode exceptionMode =
                 mozilla::jni::ExceptionMode::ABORT;
         static const mozilla::jni::CallingThread callingThread =
                 mozilla::jni::CallingThread::ANY;
         static const mozilla::jni::DispatchTarget dispatchTarget =
                 mozilla::jni::DispatchTarget::CURRENT;
     };