Bug 1349883 - part 2: reveal more input buffer status to callbacks. r=esawin,jya draft
authorJohn Lin <jolin@mozilla.com>
Thu, 06 Apr 2017 16:37:08 +0800
changeset 561001 fc855d595bbf1845c3e12d9caa0de282b97ab471
parent 561000 f9bcc709ac59a8ade9d49da2fdf49daaef4fb14e
child 561002 33b6a056129b07af874a91e069aba1c5ba982572
push id53589
push userbmo:jolin@mozilla.com
push dateWed, 12 Apr 2017 03:42:17 +0000
reviewersesawin, jya
bugs1349883
milestone55.0a1
Bug 1349883 - part 2: reveal more input buffer status to callbacks. r=esawin,jya Promise based MediaDataDecoder expects one response per request, but ICodecCallbacks was not designed that way. onInputExhausted() is called only when there are none or just a few input buffers waiting to be queued, and onOutput() is called as soon as output buffers are available. It means these 2 kinds of events are usually interleaved and hard to align with pending promises. Reporting each input buffer status makes it easier for RemoteDataDecoder to resolve promise properly. MozReview-Commit-ID: K09txmHTtmX
mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/media/ICodecCallbacks.aidl
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Codec.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/CodecProxy.java
--- a/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/media/ICodecCallbacks.aidl
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/media/ICodecCallbacks.aidl
@@ -4,13 +4,14 @@
 
 package org.mozilla.gecko.media;
 
 // Non-default types used in interface.
 import org.mozilla.gecko.media.FormatParam;
 import org.mozilla.gecko.media.Sample;
 
 interface ICodecCallbacks {
-    oneway void onInputExhausted();
+    oneway void onInputQueued(long timestamp);
+    oneway void onInputPending(long timestamp);
     oneway void onOutputFormatChanged(in FormatParam format);
     oneway void onOutput(in Sample sample);
     oneway void onError(boolean fatal);
 }
\ No newline at end of file
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Codec.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/Codec.java
@@ -45,22 +45,30 @@ import java.util.concurrent.ConcurrentLi
         }
 
         @Override
         public void onOutputFormatChanged(AsyncCodec codec, MediaFormat format) {
             mOutputProcessor.onFormatChanged(format);
         }
     }
 
+    private static final class Input {
+        public final Sample sample;
+        public boolean reported;
+
+        public Input(final Sample sample) {
+            this.sample = sample;
+        }
+    }
+
     private final class InputProcessor {
-        private static final int FEW_PENDING_INPUTS = 2;
         private boolean mHasInputCapacitySet;
         private Queue<Integer> mAvailableInputBuffers = new LinkedList<>();
         private Queue<Sample> mDequeuedSamples = new LinkedList<>();
-        private Queue<Sample> mInputSamples = new LinkedList<>();
+        private Queue<Input> mInputSamples = new LinkedList<>();
         private boolean mStopped;
 
         private synchronized Sample onAllocate(int size) {
             Sample sample = mSamplePool.obtainInput(size);
             mDequeuedSamples.add(sample);
             return sample;
         }
 
@@ -81,17 +89,17 @@ import java.util.concurrent.ConcurrentLi
             dequeued.info = sample.info;
             dequeued.cryptoInfo = sample.cryptoInfo;
             queueSample(dequeued);
 
             sample.dispose();
         }
 
         private void queueSample(Sample sample) {
-            if (!mInputSamples.offer(sample)) {
+            if (!mInputSamples.offer(new Input(sample))) {
                 reportError(Error.FATAL, new Exception("FAIL: input sample queue is full"));
                 return;
             }
 
             try {
                 feedSampleToBuffer();
             } catch (Exception e) {
                 reportError(Error.FATAL, e);
@@ -118,17 +126,17 @@ import java.util.concurrent.ConcurrentLi
             }
 
         }
 
         private void feedSampleToBuffer() {
             while (!mAvailableInputBuffers.isEmpty() && !mInputSamples.isEmpty()) {
                 int index = mAvailableInputBuffers.poll();
                 int len = 0;
-                Sample sample = mInputSamples.poll();
+                final Sample sample = mInputSamples.poll().sample;
                 long pts = sample.info.presentationTimeUs;
                 int flags = sample.info.flags;
                 MediaCodec.CryptoInfo cryptoInfo = sample.cryptoInfo;
                 if (!sample.isEOS() && sample.buffer != null) {
                     len = sample.info.size;
                     ByteBuffer buf = mCodec.getInputBuffer(index);
                     try {
                         sample.writeToByteBuffer(buf);
@@ -139,32 +147,42 @@ import java.util.concurrent.ConcurrentLi
                     mSamplePool.recycleInput(sample);
                 }
 
                 if (cryptoInfo != null && len > 0) {
                     mCodec.queueSecureInputBuffer(index, 0, cryptoInfo, pts, flags);
                 } else {
                     mCodec.queueInputBuffer(index, 0, len, pts, flags);
                 }
-            }
-            // To avoid input queue flood, request more input samples only when
-            // there are just a few waiting to be processed.
-            if (mDequeuedSamples.size() + mInputSamples.size() <= FEW_PENDING_INPUTS) {
                 try {
-                    mCallbacks.onInputExhausted();
+                    mCallbacks.onInputQueued(pts);
                 } catch (RemoteException e) {
                     e.printStackTrace();
                 }
             }
+            reportPendingInputs();
+        }
+
+        private void reportPendingInputs() {
+            try {
+                for (Input i : mInputSamples) {
+                    if (!i.reported) {
+                        i.reported = true;
+                        mCallbacks.onInputPending(i.sample.info.presentationTimeUs);
+                    }
+                }
+            } catch (RemoteException e) {
+                e.printStackTrace();
+            }
         }
 
         private synchronized void reset() {
-            for (Sample s : mInputSamples) {
-                if (!s.isEOS()) {
-                    mSamplePool.recycleInput(s);
+            for (Input i : mInputSamples) {
+                if (!i.sample.isEOS()) {
+                    mSamplePool.recycleInput(i.sample);
                 }
             }
             mInputSamples.clear();
 
             for (Sample s : mDequeuedSamples) {
                 mSamplePool.recycleInput(s);
             }
             mDequeuedSamples.clear();
--- 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
@@ -58,17 +58,24 @@ public final class CodecProxy {
         private final Callbacks mCallbacks;
         private boolean mEndOfInput;
 
         CallbacksForwarder(Callbacks callbacks) {
             mCallbacks = callbacks;
         }
 
         @Override
-        public void onInputExhausted() throws RemoteException {
+        public synchronized void onInputQueued(long timestamp) throws RemoteException {
+            if (!mEndOfInput) {
+                mCallbacks.onInputExhausted();
+            }
+        }
+
+        @Override
+        public synchronized void onInputPending(long timestamp) throws RemoteException {
             if (!mEndOfInput) {
                 mCallbacks.onInputExhausted();
             }
         }
 
         @Override
         public void onOutputFormatChanged(FormatParam format) throws RemoteException {
             mCallbacks.onOutputFormatChanged(format.asFormat());