Bug 1349883 - part 3: resolve decode promise according to buffer status. r=jya draft
authorJohn Lin <jolin@mozilla.com>
Fri, 07 Apr 2017 17:07:02 +0800
changeset 561002 33b6a056129b07af874a91e069aba1c5ba982572
parent 561001 fc855d595bbf1845c3e12d9caa0de282b97ab471
child 561003 33c2a23d642d4a66dbf650794fe045777499c3bd
push id53589
push userbmo:jolin@mozilla.com
push dateWed, 12 Apr 2017 03:42:17 +0000
reviewersjya
bugs1349883
milestone55.0a1
Bug 1349883 - part 3: resolve decode promise according to buffer status. r=jya MozReview-Commit-ID: JwOOi56t30Y
dom/media/platforms/android/JavaCallbacksSupport.h
dom/media/platforms/android/RemoteDataDecoder.cpp
dom/media/platforms/android/RemoteDataDecoder.h
media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.cpp
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/CodecProxy.java
widget/android/GeneratedJNINatives.h
widget/android/GeneratedJNIWrappers.cpp
widget/android/GeneratedJNIWrappers.h
--- a/dom/media/platforms/android/JavaCallbacksSupport.h
+++ b/dom/media/platforms/android/JavaCallbacksSupport.h
@@ -18,22 +18,22 @@ public:
   typedef java::CodecProxy::NativeCallbacks::Natives<JavaCallbacksSupport> Base;
   using Base::AttachNative;
   using Base::DisposeNative;
 
   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(java::Sample::Param aSample) = 0;
 
   void OnOutput(jni::Object::Param aSample)
   {
     if (!mCanceled) {
--- a/dom/media/platforms/android/RemoteDataDecoder.cpp
+++ b/dom/media/platforms/android/RemoteDataDecoder.cpp
@@ -90,19 +90,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();
@@ -138,18 +138,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
@@ -292,19 +291,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));
@@ -342,17 +341,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
@@ -432,25 +431,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__);
   });
 }
 
@@ -538,21 +539,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/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.cpp
+++ b/media/webrtc/signaling/src/media-conduit/WebrtcMediaCodecVP8VideoCodec.cpp
@@ -71,17 +71,17 @@ public:
         if(mEncodedImage._buffer) {
             delete [] mEncodedImage._buffer;
         }
         mEncodedImage._buffer = newBuffer;
         mEncodedImage._size = minimumSize;
     }
   }
 
-  void HandleInputExhausted() override
+  void HandleInput(jlong aTimestamp, bool aProcessed) override
   {
     CSFLogDebug(logTag,  "%s %p", __FUNCTION__, this);
   }
 
   void HandleOutputFormatChanged(MediaFormat::Param aFormat) override
   {
     CSFLogDebug(logTag,  "%s %p", __FUNCTION__, this);
   }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/CodecProxy.java
@@ -30,25 +30,25 @@ public final class CodecProxy {
     private boolean mIsEncoder;
     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 void disposeNative() {
             throw new UnsupportedOperationException();
         }
@@ -60,24 +60,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/GeneratedJNINatives.h
+++ b/widget/android/GeneratedJNINatives.h
@@ -449,19 +449,19 @@ public:
 
 template<class Impl>
 const JNINativeMethod CodecProxy::NativeCallbacks::Natives<Impl>::methods[] = {
 
     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/GeneratedJNIWrappers.cpp
+++ b/widget/android/GeneratedJNIWrappers.cpp
@@ -1820,18 +1820,18 @@ constexpr char CodecProxy::NativeCallbac
 auto CodecProxy::NativeCallbacks::DisposeNative() const -> void
 {
     return mozilla::jni::Method<DisposeNative_t>::Call(NativeCallbacks::mCtx, nullptr);
 }
 
 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/GeneratedJNIWrappers.h
+++ b/widget/android/GeneratedJNIWrappers.h
@@ -5182,24 +5182,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";
-        static constexpr char signature[] =
-                "()V";
+        typedef mozilla::jni::Args<
+                int64_t,
+                bool> Args;
+        static constexpr char name[] = "onInputStatus";
+        static constexpr char signature[] =
+                "(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;
     };