summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java250
1 files changed, 250 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java
new file mode 100644
index 0000000000..8afc96109d
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java
@@ -0,0 +1,250 @@
+/* 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.annotation.TargetApi;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCrypto;
+import android.media.MediaFormat;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.Surface;
+import androidx.annotation.NonNull;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+/* package */ final class LollipopAsyncCodec implements AsyncCodec {
+ private final MediaCodec mCodec;
+
+ private class CodecCallback extends MediaCodec.Callback {
+ private final Forwarder mForwarder;
+
+ private class Forwarder extends Handler {
+ private static final int MSG_INPUT_BUFFER_AVAILABLE = 1;
+ private static final int MSG_OUTPUT_BUFFER_AVAILABLE = 2;
+ private static final int MSG_OUTPUT_FORMAT_CHANGE = 3;
+ private static final int MSG_ERROR = 4;
+
+ private final Callbacks mTarget;
+
+ private Forwarder(final Looper looper, final Callbacks target) {
+ super(looper);
+ mTarget = target;
+ }
+
+ @Override
+ public void handleMessage(final Message msg) {
+ switch (msg.what) {
+ case MSG_INPUT_BUFFER_AVAILABLE:
+ mTarget.onInputBufferAvailable(LollipopAsyncCodec.this, msg.arg1); // index
+ break;
+ case MSG_OUTPUT_BUFFER_AVAILABLE:
+ mTarget.onOutputBufferAvailable(
+ LollipopAsyncCodec.this,
+ msg.arg1, // index
+ (MediaCodec.BufferInfo) msg.obj); // buffer info
+ break;
+ case MSG_OUTPUT_FORMAT_CHANGE:
+ mTarget.onOutputFormatChanged(
+ LollipopAsyncCodec.this, (MediaFormat) msg.obj); // output format
+ break;
+ case MSG_ERROR:
+ mTarget.onError(LollipopAsyncCodec.this, msg.arg1); // error code
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ private void onInput(final int index) {
+ notify(obtainMessage(MSG_INPUT_BUFFER_AVAILABLE, index, 0));
+ }
+
+ private void notify(final Message msg) {
+ if (Looper.myLooper() == getLooper()) {
+ handleMessage(msg);
+ } else {
+ sendMessage(msg);
+ }
+ }
+
+ private void onOutput(final int index, final MediaCodec.BufferInfo info) {
+ final Message msg = obtainMessage(MSG_OUTPUT_BUFFER_AVAILABLE, index, 0, info);
+ notify(msg);
+ }
+
+ private void onOutputFormatChanged(final MediaFormat format) {
+ notify(obtainMessage(MSG_OUTPUT_FORMAT_CHANGE, format));
+ }
+
+ private void onError(final MediaCodec.CodecException e) {
+ e.printStackTrace();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ notify(obtainMessage(MSG_ERROR, e.getErrorCode()));
+ } else {
+ notify(obtainMessage(MSG_ERROR, e.getLocalizedMessage()));
+ }
+ }
+ }
+
+ private CodecCallback(final Callbacks callbacks, final Handler handler) {
+ Looper looper = (handler == null) ? null : handler.getLooper();
+ if (looper == null) {
+ // Use this thread if no handler supplied.
+ looper = Looper.myLooper();
+ }
+ if (looper == null) {
+ // This thread has no looper. Use main thread.
+ looper = Looper.getMainLooper();
+ }
+
+ mForwarder = new Forwarder(looper, callbacks);
+ }
+
+ @Override
+ public void onInputBufferAvailable(@NonNull final MediaCodec codec, final int index) {
+ mForwarder.onInput(index);
+ }
+
+ @Override
+ public void onOutputBufferAvailable(
+ @NonNull final MediaCodec codec,
+ final int index,
+ @NonNull final MediaCodec.BufferInfo info) {
+ mForwarder.onOutput(index, info);
+ }
+
+ @Override
+ public void onOutputFormatChanged(
+ @NonNull final MediaCodec codec, @NonNull final MediaFormat format) {
+ mForwarder.onOutputFormatChanged(format);
+ }
+
+ @Override
+ public void onError(
+ @NonNull final MediaCodec codec, @NonNull final MediaCodec.CodecException e) {
+ mForwarder.onError(e);
+ }
+ }
+
+ /* package */ LollipopAsyncCodec(final String name) throws IOException {
+ mCodec = MediaCodec.createByCodecName(name);
+ }
+
+ @Override
+ public void setCallbacks(final Callbacks callbacks, final Handler handler) {
+ if (callbacks == null) {
+ return;
+ }
+
+ mCodec.setCallback(new CodecCallback(callbacks, handler));
+ }
+
+ @Override
+ public void configure(
+ final MediaFormat format, final Surface surface, final MediaCrypto crypto, final int flags) {
+ mCodec.configure(format, surface, crypto, flags);
+ }
+
+ @Override
+ public boolean isAdaptivePlaybackSupported(final String mimeType) {
+ return HardwareCodecCapabilityUtils.checkSupportsAdaptivePlayback(mCodec, mimeType);
+ }
+
+ @Override
+ public boolean isTunneledPlaybackSupported(final String mimeType) {
+ try {
+ return mCodec
+ .getCodecInfo()
+ .getCapabilitiesForType(mimeType)
+ .isFeatureSupported(CodecCapabilities.FEATURE_TunneledPlayback);
+ } catch (final Exception e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void start() {
+ mCodec.start();
+ }
+
+ @Override
+ public void stop() {
+ mCodec.stop();
+ }
+
+ @Override
+ public void flush() {
+ mCodec.flush();
+ }
+
+ @Override
+ public void resumeReceivingInputs() {
+ mCodec.start();
+ }
+
+ @Override
+ public void setBitrate(final int bps) {
+ final Bundle params = new Bundle();
+ params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps);
+ mCodec.setParameters(params);
+ }
+
+ @Override
+ public void release() {
+ mCodec.release();
+ }
+
+ @Override
+ public ByteBuffer getInputBuffer(final int index) {
+ return mCodec.getInputBuffer(index);
+ }
+
+ @Override
+ public ByteBuffer getOutputBuffer(final int index) {
+ return mCodec.getOutputBuffer(index);
+ }
+
+ @Override
+ public MediaFormat getInputFormat() {
+ return mCodec.getInputFormat();
+ }
+
+ @Override
+ public void queueInputBuffer(
+ final int index,
+ final int offset,
+ final int size,
+ final long presentationTimeUs,
+ final int flags) {
+ if ((flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
+ final Bundle params = new Bundle();
+ params.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
+ mCodec.setParameters(params);
+ }
+ mCodec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
+ }
+
+ @Override
+ public void queueSecureInputBuffer(
+ final int index,
+ final int offset,
+ final MediaCodec.CryptoInfo info,
+ final long presentationTimeUs,
+ final int flags) {
+ mCodec.queueSecureInputBuffer(index, offset, info, presentationTimeUs, flags);
+ }
+
+ @Override
+ public void releaseOutputBuffer(final int index, final boolean render) {
+ mCodec.releaseOutputBuffer(index, render);
+ }
+}