Bug 1324530 - part 1: add method to AsyncCodec for resuming receiving input buffers after flush(). r=esawin draft
authorJohn Lin <jolin@mozilla.com>
Wed, 28 Dec 2016 15:41:55 +0800
changeset 456913 249dbf6a03eee2f0646590cf8d164841cd882577
parent 456716 a14094edbad78fc1d16e8d4c57902537cf286fd1
child 456914 f7bcddf4d7b6177900be4f013849e84d396544c1
push id40642
push userbmo:jolin@mozilla.com
push dateFri, 06 Jan 2017 14:13:15 +0000
reviewersesawin
bugs1324530
milestone53.0a1
Bug 1324530 - part 1: add method to AsyncCodec for resuming receiving input buffers after flush(). r=esawin 2 birds with 1 stone: - according to MediaCodec doc, when running in async mode start() must be called after flush() to 'resume receiving input buffers'. - in JellyBeanAsyncCodec's case, explicit resuming polling input buffers helps Codec decide which buffers are invalid by flush(). MozReview-Commit-ID: Ee5QbppgsVf
mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
mobile/android/base/java/org/mozilla/gecko/media/Codec.java
mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
--- a/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/AsyncCodec.java
@@ -24,15 +24,17 @@ public interface AsyncCodec {
     }
 
     public abstract void setCallbacks(Callbacks callbacks, Handler handler);
     public abstract void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags);
     public abstract boolean isAdaptivePlaybackSupported(String mimeType);
     public abstract void start();
     public abstract void stop();
     public abstract void flush();
+    // Must be called after flush().
+    public abstract void resumeReceivingInputs();
     public abstract void release();
     public abstract ByteBuffer getInputBuffer(int index);
     public abstract ByteBuffer getOutputBuffer(int index);
     public abstract void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags);
     public abstract void queueSecureInputBuffer(int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
     public abstract void releaseOutputBuffer(int index, boolean render);
 }
--- a/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/Codec.java
@@ -193,17 +193,17 @@ import java.util.concurrent.ConcurrentLi
             boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
             if (DEBUG && eos) {
                 Log.d(LOGTAG, "output EOS");
             }
        }
 
         private void outputDummy(MediaCodec.BufferInfo info) {
             try {
-                if (DEBUG) Log.d(LOGTAG, "return dummy sample");
+                if (DEBUG) { Log.d(LOGTAG, "return dummy sample"); }
                 mCallbacks.onOutput(Sample.create(null, info, null));
             } catch (RemoteException e) {
                 // Dead recipient.
                 e.printStackTrace();
             }
         }
 
         private synchronized void onRelease(Sample sample, boolean render) {
@@ -272,21 +272,21 @@ import java.util.concurrent.ConcurrentLi
                                           int flags,
                                           String drmStubId) throws RemoteException {
         if (mCallbacks == null) {
             Log.e(LOGTAG, "FAIL: callbacks must be set before calling configure()");
             return false;
         }
 
         if (mCodec != null) {
-            if (DEBUG) Log.d(LOGTAG, "release existing codec: " + mCodec);
+            if (DEBUG) { Log.d(LOGTAG, "release existing codec: " + mCodec); }
             releaseCodec();
         }
 
-        if (DEBUG) Log.d(LOGTAG, "configure " + this);
+        if (DEBUG) { Log.d(LOGTAG, "configure " + this); }
 
         MediaFormat fmt = format.asFormat();
         String codecName = getDecoderForFormat(fmt);
         if (codecName == null) {
             Log.e(LOGTAG, "FAIL: cannot find codec");
             return false;
         }
 
@@ -301,32 +301,32 @@ import java.util.concurrent.ConcurrentLi
 
             codec.setCallbacks(new Callbacks(), null);
 
             // Video decoder should config with adaptive playback capability.
             if (surface != null) {
                 mIsAdaptivePlaybackSupported = codec.isAdaptivePlaybackSupported(
                                                    fmt.getString(MediaFormat.KEY_MIME));
                 if (mIsAdaptivePlaybackSupported) {
-                    if (DEBUG) Log.d(LOGTAG, "codec supports adaptive playback  = " + mIsAdaptivePlaybackSupported);
+                    if (DEBUG) { Log.d(LOGTAG, "codec supports adaptive playback  = " + mIsAdaptivePlaybackSupported); }
                     // TODO: may need to find a way to not use hard code to decide the max w/h.
                     fmt.setInteger(MediaFormat.KEY_MAX_WIDTH, 1920);
                     fmt.setInteger(MediaFormat.KEY_MAX_HEIGHT, 1080);
                 }
             }
 
             codec.configure(fmt, surface, crypto, flags);
             mCodec = codec;
             mInputProcessor = new InputProcessor();
             mOutputProcessor = new OutputProcessor();
             mSamplePool = new SamplePool(codecName);
-            if (DEBUG) Log.d(LOGTAG, codec.toString() + " created");
+            if (DEBUG) { Log.d(LOGTAG, codec.toString() + " created"); }
             return true;
         } catch (Exception e) {
-            if (DEBUG) Log.d(LOGTAG, "FAIL: cannot create codec -- " + codecName);
+            if (DEBUG) { Log.d(LOGTAG, "FAIL: cannot create codec -- " + codecName); }
             e.printStackTrace();
             return false;
         }
     }
 
     @Override
     public synchronized boolean isAdaptivePlaybackSupported() {
         return mIsAdaptivePlaybackSupported;
@@ -364,17 +364,17 @@ import java.util.concurrent.ConcurrentLi
         return null;
         // TODO: API 21+ is simpler.
         //static MediaCodecList sCodecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
         //return sCodecList.findDecoderForFormat(format);
     }
 
     @Override
     public synchronized void start() throws RemoteException {
-        if (DEBUG) Log.d(LOGTAG, "start " + this);
+        if (DEBUG) { Log.d(LOGTAG, "start " + this); }
         mFlushing = false;
         try {
             mCodec.start();
         } catch (Exception e) {
             reportError(Error.FATAL, e);
         }
     }
 
@@ -386,38 +386,39 @@ import java.util.concurrent.ConcurrentLi
             mCallbacks.onError(error == Error.FATAL);
         } catch (RemoteException re) {
             re.printStackTrace();
         }
     }
 
     @Override
     public synchronized void stop() throws RemoteException {
-        if (DEBUG) Log.d(LOGTAG, "stop " + this);
+        if (DEBUG) { Log.d(LOGTAG, "stop " + this); }
         try {
             mCodec.stop();
         } catch (Exception e) {
             reportError(Error.FATAL, e);
         }
     }
 
     @Override
     public synchronized void flush() throws RemoteException {
         mFlushing = true;
-        if (DEBUG) Log.d(LOGTAG, "flush " + this);
+        if (DEBUG) { Log.d(LOGTAG, "flush " + this); }
         mInputProcessor.reset();
         mOutputProcessor.reset();
         try {
             mCodec.flush();
         } catch (Exception e) {
             reportError(Error.FATAL, e);
         }
 
         mFlushing = false;
-        if (DEBUG) Log.d(LOGTAG, "flushed " + this);
+        if (DEBUG) { Log.d(LOGTAG, "flushed " + this); }
+        mCodec.resumeReceivingInputs();
     }
 
     @Override
     public synchronized Sample dequeueInput(int size) {
         return mInputProcessor.onAllocate(size);
     }
 
     @Override
@@ -427,16 +428,16 @@ import java.util.concurrent.ConcurrentLi
 
     @Override
     public synchronized void releaseOutput(Sample sample, boolean render) {
         mOutputProcessor.onRelease(sample, render);
     }
 
     @Override
     public synchronized void release() throws RemoteException {
-        if (DEBUG) Log.d(LOGTAG, "release " + this);
+        if (DEBUG) { Log.d(LOGTAG, "release " + this); }
         releaseCodec();
         mSamplePool.reset();
         mSamplePool = null;
         mCallbacks.asBinder().unlinkToDeath(this, 0);
         mCallbacks = null;
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
+++ b/mobile/android/base/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
@@ -56,17 +56,17 @@ final class JellyBeanAsyncCodec implemen
                 if (isCanceled() || handleMessageLocked(msg)) {
                     return;
                 }
             }
 
             switch (msg.what) {
                 case MSG_CANCELLATION:
                     // Just a marker. Nothing to do here.
-                    if (DEBUG) Log.d(LOGTAG, "handler " + this + " done cancellation, codec=" + JellyBeanAsyncCodec.this);
+                    if (DEBUG) { Log.d(LOGTAG, "handler " + this + " done cancellation, codec=" + JellyBeanAsyncCodec.this); }
                     break;
                 default:
                     super.handleMessage(msg);
                     break;
             }
         }
      }
 
@@ -268,17 +268,17 @@ final class JellyBeanAsyncCodec implemen
     private void initBufferPoller(String name) {
         if (mBufferPoller != null) {
             Log.e(LOGTAG, "poller already initialized");
             return;
         }
         HandlerThread thread = new HandlerThread(name);
         thread.start();
         mBufferPoller = new BufferPoller(thread.getLooper());
-        if (DEBUG) Log.d(LOGTAG, "start poller for codec:" + this + ", thread=" + thread.getThreadId());
+        if (DEBUG) { Log.d(LOGTAG, "start poller for codec:" + this + ", thread=" + thread.getThreadId()); }
     }
 
     @Override
     public void setCallbacks(AsyncCodec.Callbacks callbacks, Handler handler) {
         if (callbacks == null) {
             return;
         }
 
@@ -287,17 +287,17 @@ final class JellyBeanAsyncCodec implemen
             // Use this thread if no handler supplied.
             looper = Looper.myLooper();
         }
         if (looper == null) {
             // This thread has no looper. Use poller thread.
             looper = mBufferPoller.getLooper();
         }
         mCallbackSender = new CallbackSender(looper, callbacks);
-        if (DEBUG) Log.d(LOGTAG, "setCallbacks(): sender=" + mCallbackSender);
+        if (DEBUG) { Log.d(LOGTAG, "setCallbacks(): sender=" + mCallbackSender); }
     }
 
     @Override
     public void configure(MediaFormat format, Surface surface, MediaCrypto crypto, int flags) {
         assertCallbacks();
 
         mCodec.configure(format, surface, crypto, flags);
     }
@@ -316,20 +316,25 @@ final class JellyBeanAsyncCodec implemen
     @Override
     public void start() {
         assertCallbacks();
 
         mCodec.start();
         mInputEnded = false;
         mOutputEnded = false;
         mInputBuffers = mCodec.getInputBuffers();
+        resumeReceivingInputs();
+        mOutputBuffers = mCodec.getOutputBuffers();
+    }
+
+    @Override
+    public void resumeReceivingInputs() {
         for (int i = 0; i < mInputBuffers.length; i++) {
             mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
         }
-        mOutputBuffers = mCodec.getOutputBuffers();
     }
 
     @Override
     public final void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags) {
         assertCallbacks();
 
         mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
 
@@ -391,19 +396,16 @@ final class JellyBeanAsyncCodec implemen
     @Override
     public void flush() {
         assertCallbacks();
 
         mInputEnded = false;
         mOutputEnded = false;
         cancelPendingTasks();
         mCodec.flush();
-        for (int i = 0; i < mInputBuffers.length; i++) {
-            mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
-        }
     }
 
     private void cancelPendingTasks() {
         mBufferPoller.cancel();
         mCallbackSender.cancel();
     }
 
     @Override
@@ -428,11 +430,11 @@ final class JellyBeanAsyncCodec implemen
         if (mBufferPoller == null) {
             Log.e(LOGTAG, "no initialized poller.");
             return;
         }
 
         mBufferPoller.getLooper().quit();
         mBufferPoller = null;
 
-        if (DEBUG) Log.d(LOGTAG, "stop poller " + this);
+        if (DEBUG) { Log.d(LOGTAG, "stop poller " + this); }
     }
 }