Bug 1349883 - Part 2: Reveal more input buffer status to callbacks. r=esawin, r=jya, a=gchang
authorJohn Lin <jolin@mozilla.com>
Thu, 13 Apr 2017 17:32:14 +0800
changeset 395867 e0352fcf12c17aee1f3e5ccd5c363b517bbdc0a3
parent 395866 fc16131f93233db6253a19ed010d9c5ab6a31303
child 395868 327f2348eb373976c561b418b50ffa0b136d7a71
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersesawin, jya, gchang
bugs1349883
milestone54.0a2
Bug 1349883 - Part 2: Reveal more input buffer status to callbacks. r=esawin, r=jya, a=gchang 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: 3FoPzXruRnh
mobile/android/base/aidl/org/mozilla/gecko/media/ICodecCallbacks.aidl
mobile/android/base/java/org/mozilla/gecko/media/Codec.java
mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
--- a/mobile/android/base/aidl/org/mozilla/gecko/media/ICodecCallbacks.aidl
+++ b/mobile/android/base/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/base/java/org/mozilla/gecko/media/Codec.java
+++ b/mobile/android/base/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/base/java/org/mozilla/gecko/media/CodecProxy.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -55,17 +55,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());