summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java')
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java191
1 files changed, 191 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
new file mode 100644
index 000000000..3025c14d0
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/media/CodecProxy.java
@@ -0,0 +1,191 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.media;
+
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodec.CryptoInfo;
+import android.media.MediaFormat;
+import android.os.DeadObjectException;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.Surface;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.mozglue.JNIObject;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+// Proxy class of ICodec binder.
+public final class CodecProxy {
+ private static final String LOGTAG = "GeckoRemoteCodecProxy";
+ private static final boolean DEBUG = false;
+
+ private ICodec mRemote;
+ private FormatParam mFormat;
+ private Surface mOutputSurface;
+ private CallbacksForwarder mCallbacks;
+
+ public interface Callbacks {
+ void onInputExhausted();
+ 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 onOutputFormatChanged(MediaFormat format);
+ public native void onOutput(Sample output);
+ public native void onError(boolean fatal);
+
+ @Override // JNIObject
+ protected native void disposeNative();
+ }
+
+ private class CallbacksForwarder extends ICodecCallbacks.Stub {
+ private final Callbacks mCallbacks;
+
+ CallbacksForwarder(Callbacks callbacks) {
+ mCallbacks = callbacks;
+ }
+
+ @Override
+ public void onInputExhausted() throws RemoteException {
+ mCallbacks.onInputExhausted();
+ }
+
+ @Override
+ public void onOutputFormatChanged(FormatParam format) throws RemoteException {
+ mCallbacks.onOutputFormatChanged(format.asFormat());
+ }
+
+ @Override
+ public void onOutput(Sample sample) throws RemoteException {
+ mCallbacks.onOutput(sample);
+ mRemote.releaseOutput(sample);
+ sample.dispose();
+ }
+
+ @Override
+ public void onError(boolean fatal) throws RemoteException {
+ reportError(fatal);
+ }
+
+ public void reportError(boolean fatal) {
+ mCallbacks.onError(fatal);
+ }
+ }
+
+ @WrapForJNI
+ public static CodecProxy create(MediaFormat format, Surface surface, Callbacks callbacks) {
+ return RemoteManager.getInstance().createCodec(format, surface, callbacks);
+ }
+
+ public static CodecProxy createCodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
+ return new CodecProxy(format, surface, callbacks);
+ }
+
+ private CodecProxy(MediaFormat format, Surface surface, Callbacks callbacks) {
+ mFormat = new FormatParam(format);
+ mOutputSurface = surface;
+ mCallbacks = new CallbacksForwarder(callbacks);
+ }
+
+ boolean init(ICodec remote) {
+ try {
+ remote.setCallbacks(mCallbacks);
+ remote.configure(mFormat, mOutputSurface, 0);
+ remote.start();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ mRemote = remote;
+ return true;
+ }
+
+ boolean deinit() {
+ try {
+ mRemote.stop();
+ mRemote.release();
+ mRemote = null;
+ return true;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ @WrapForJNI
+ public synchronized boolean input(ByteBuffer bytes, BufferInfo info, CryptoInfo cryptoInfo) {
+ if (mRemote == null) {
+ Log.e(LOGTAG, "cannot send input to an ended codec");
+ return false;
+ }
+
+ try {
+ Sample sample = (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) ?
+ Sample.EOS : mRemote.dequeueInput(info.size).set(bytes, info, cryptoInfo);
+ mRemote.queueInput(sample);
+ sample.dispose();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } catch (DeadObjectException e) {
+ return false;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ Log.e(LOGTAG, "fail to input sample: size=" + info.size +
+ ", pts=" + info.presentationTimeUs +
+ ", flags=" + Integer.toHexString(info.flags));
+ return false;
+ }
+ return true;
+ }
+
+ @WrapForJNI
+ public synchronized boolean flush() {
+ if (mRemote == null) {
+ Log.e(LOGTAG, "cannot flush an ended codec");
+ return false;
+ }
+ try {
+ if (DEBUG) Log.d(LOGTAG, "flush " + this);
+ mRemote.flush();
+ } catch (DeadObjectException e) {
+ return false;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ @WrapForJNI
+ public synchronized boolean release() {
+ if (mRemote == null) {
+ Log.w(LOGTAG, "codec already ended");
+ return true;
+ }
+ if (DEBUG) Log.d(LOGTAG, "release " + this);
+ try {
+ RemoteManager.getInstance().releaseCodec(this);
+ } catch (DeadObjectException e) {
+ return false;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return true;
+ }
+
+ public synchronized void reportError(boolean fatal) {
+ mCallbacks.reportError(fatal);
+ }
+}