commit a20df35f4aa93c6d111a9482927db93ecb642b84
parent d3db46b73621b7fe59ae086f4855efc7363144fe
Author: Ryan VanderMeulen <rvandermeulen@mozilla.com>
Date: Thu, 30 Oct 2025 11:50:57 +0000
Bug 1991789 - Remove JellyBeanAsyncCodec. r=geckoview-reviewers,media-playback-reviewers,alwu,ohall
Differential Revision: https://phabricator.services.mozilla.com/D270356
Diffstat:
5 files changed, 8 insertions(+), 509 deletions(-)
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/AsyncCodec.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/AsyncCodec.java
@@ -10,11 +10,15 @@ import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Handler;
import android.view.Surface;
+import java.io.IOException;
import java.nio.ByteBuffer;
-// A wrapper interface that mimics the new {@link android.media.MediaCodec}
-// asynchronous mode API in Lollipop.
+// A wrapper interface that mimics the new {@link android.media.MediaCodec} asynchronous mode API.
public interface AsyncCodec {
+ static AsyncCodec create(final String name) throws IOException {
+ return new LollipopAsyncCodec(name);
+ }
+
interface Callbacks {
void onInputBufferAvailable(AsyncCodec codec, int index);
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/AsyncCodecFactory.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/AsyncCodecFactory.java
@@ -1,19 +0,0 @@
-/* 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.os.Build;
-import java.io.IOException;
-
-public final class AsyncCodecFactory {
- public static AsyncCodec create(final String name) throws IOException {
- // A bug that getInputBuffer() could fail after flush() then start() wasn't fixed until MR1.
- // See:
- // https://android.googlesource.com/platform/frameworks/av/+/d9e0603a1be07dbb347c55050c7d4629ea7492e8
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1
- ? new LollipopAsyncCodec(name)
- : new JellyBeanAsyncCodec(name);
- }
-}
diff --git 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
@@ -518,7 +518,7 @@ import org.mozilla.gecko.gfx.GeckoSurface;
final int flags,
final String drmStubId) {
try {
- final AsyncCodec codec = AsyncCodecFactory.create(name);
+ final AsyncCodec codec = AsyncCodec.create(name);
codec.setCallbacks(new Callbacks(), null);
final MediaCrypto crypto = RemoteMediaDrmBridgeStub.getMediaCrypto(drmStubId);
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/JellyBeanAsyncCodec.java
@@ -1,481 +0,0 @@
-/* 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.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaCrypto;
-import android.media.MediaFormat;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.view.Surface;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
-
-// Implement async API using MediaCodec sync mode (API v16).
-// This class uses internal worker thread/handler (mBufferPoller) to poll
-// input and output buffer and notifies the client through callbacks.
-final class JellyBeanAsyncCodec implements AsyncCodec {
- private static final String LOGTAG = "GeckoAsyncCodecAPIv16";
- private static final boolean DEBUG = false;
-
- private static final int ERROR_CODEC = -10000;
-
- private abstract class CancelableHandler extends Handler {
- private static final int MSG_CANCELLATION = 0x434E434C; // 'CNCL'
-
- protected CancelableHandler(final Looper looper) {
- super(looper);
- }
-
- protected void cancel() {
- removeCallbacksAndMessages(null);
- sendEmptyMessage(MSG_CANCELLATION);
- // Wait until handleMessageLocked() is done.
- synchronized (this) {
- }
- }
-
- protected boolean isCanceled() {
- return hasMessages(MSG_CANCELLATION);
- }
-
- // Subclass should implement this and return true if it handles msg.
- // Warning: Never, ever call super.handleMessage() in this method!
- protected abstract boolean handleMessageLocked(Message msg);
-
- public final void handleMessage(final Message msg) {
- // Block cancel() during handleMessageLocked().
- synchronized (this) {
- 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);
- }
- break;
- default:
- super.handleMessage(msg);
- break;
- }
- }
- }
-
- // A handler to invoke AsyncCodec.Callbacks methods.
- private final class CallbackSender extends CancelableHandler {
- 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 Callbacks mCallbacks;
-
- private CallbackSender(final Looper looper, final Callbacks callbacks) {
- super(looper);
- mCallbacks = callbacks;
- }
-
- public void notifyInputBuffer(final int index) {
- if (isCanceled()) {
- return;
- }
-
- final Message msg = obtainMessage(MSG_INPUT_BUFFER_AVAILABLE);
- msg.arg1 = index;
- processMessage(msg);
- }
-
- private void processMessage(final Message msg) {
- if (Looper.myLooper() == getLooper()) {
- handleMessage(msg);
- } else {
- sendMessage(msg);
- }
- }
-
- public void notifyOutputBuffer(final int index, final MediaCodec.BufferInfo info) {
- if (isCanceled()) {
- return;
- }
-
- final Message msg = obtainMessage(MSG_OUTPUT_BUFFER_AVAILABLE, info);
- msg.arg1 = index;
- processMessage(msg);
- }
-
- public void notifyOutputFormat(final MediaFormat format) {
- if (isCanceled()) {
- return;
- }
- processMessage(obtainMessage(MSG_OUTPUT_FORMAT_CHANGE, format));
- }
-
- public void notifyError(final int result) {
- Log.e(LOGTAG, "codec error:" + result);
- processMessage(obtainMessage(MSG_ERROR, result, 0));
- }
-
- protected boolean handleMessageLocked(final Message msg) {
- switch (msg.what) {
- case MSG_INPUT_BUFFER_AVAILABLE: // arg1: buffer index.
- mCallbacks.onInputBufferAvailable(JellyBeanAsyncCodec.this, msg.arg1);
- break;
- case MSG_OUTPUT_BUFFER_AVAILABLE: // arg1: buffer index, obj: info.
- mCallbacks.onOutputBufferAvailable(
- JellyBeanAsyncCodec.this, msg.arg1, (MediaCodec.BufferInfo) msg.obj);
- break;
- case MSG_OUTPUT_FORMAT_CHANGE: // obj: output format.
- mCallbacks.onOutputFormatChanged(JellyBeanAsyncCodec.this, (MediaFormat) msg.obj);
- break;
- case MSG_ERROR: // arg1: error code.
- mCallbacks.onError(JellyBeanAsyncCodec.this, msg.arg1);
- break;
- default:
- return false;
- }
-
- return true;
- }
- }
-
- // Handler to poll input and output buffers using dequeue(Input|Output)Buffer(),
- // with 10ms time-out. Once triggered and successfully gets a buffer, it
- // will schedule next polling until EOS or failure. To prevent it from
- // automatically polling more buffer, use cancel() it inherits from
- // CancelableHandler.
- private final class BufferPoller extends CancelableHandler {
- private static final int MSG_POLL_INPUT_BUFFERS = 1;
- private static final int MSG_POLL_OUTPUT_BUFFERS = 2;
-
- private static final long DEQUEUE_TIMEOUT_US = 10000;
-
- public BufferPoller(final Looper looper) {
- super(looper);
- }
-
- private void schedulePollingIfNotCanceled(final int what) {
- if (isCanceled()) {
- return;
- }
-
- schedulePolling(what);
- }
-
- private void schedulePolling(final int what) {
- if (needsBuffer(what)) {
- sendEmptyMessage(what);
- }
- }
-
- private boolean needsBuffer(final int what) {
- if (mOutputEnded && (what == MSG_POLL_OUTPUT_BUFFERS)) {
- return false;
- }
-
- return !(mInputEnded && (what == MSG_POLL_INPUT_BUFFERS));
- }
-
- protected boolean handleMessageLocked(final Message msg) {
- try {
- switch (msg.what) {
- case MSG_POLL_INPUT_BUFFERS:
- pollInputBuffer();
- break;
- case MSG_POLL_OUTPUT_BUFFERS:
- pollOutputBuffer();
- break;
- default:
- return false;
- }
- } catch (final IllegalStateException e) {
- e.printStackTrace();
- mCallbackSender.notifyError(ERROR_CODEC);
- }
-
- return true;
- }
-
- private void pollInputBuffer() {
- final int result = mCodec.dequeueInputBuffer(DEQUEUE_TIMEOUT_US);
- if (result >= 0) {
- mCallbackSender.notifyInputBuffer(result);
- } else if (result == MediaCodec.INFO_TRY_AGAIN_LATER) {
- mBufferPoller.schedulePollingIfNotCanceled(BufferPoller.MSG_POLL_INPUT_BUFFERS);
- } else {
- mCallbackSender.notifyError(result);
- }
- }
-
- private void pollOutputBuffer() {
- boolean dequeueMoreBuffer = true;
- final MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
- final int result = mCodec.dequeueOutputBuffer(info, DEQUEUE_TIMEOUT_US);
- if (result >= 0) {
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mOutputEnded = true;
- }
- mCallbackSender.notifyOutputBuffer(result, info);
- } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
- mOutputBuffers = mCodec.getOutputBuffers();
- } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- mOutputBuffers = mCodec.getOutputBuffers();
- mCallbackSender.notifyOutputFormat(mCodec.getOutputFormat());
- } else if (result == MediaCodec.INFO_TRY_AGAIN_LATER) {
- // When input ended, keep polling remaining output buffer until EOS.
- dequeueMoreBuffer = mInputEnded;
- } else {
- mCallbackSender.notifyError(result);
- dequeueMoreBuffer = false;
- }
-
- if (dequeueMoreBuffer) {
- schedulePollingIfNotCanceled(MSG_POLL_OUTPUT_BUFFERS);
- }
- }
- }
-
- private MediaCodec mCodec;
- private ByteBuffer[] mInputBuffers;
- private ByteBuffer[] mOutputBuffers;
- private AsyncCodec.Callbacks mCallbacks;
- private CallbackSender mCallbackSender;
-
- private BufferPoller mBufferPoller;
- private volatile boolean mInputEnded;
- private volatile boolean mOutputEnded;
-
- // Must be called on a thread with looper.
- /* package */ JellyBeanAsyncCodec(final String name) throws IOException {
- mCodec = MediaCodec.createByCodecName(name);
- initBufferPoller(name + " buffer poller");
- }
-
- private void initBufferPoller(final String name) {
- if (mBufferPoller != null) {
- Log.e(LOGTAG, "poller already initialized");
- return;
- }
- final 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());
- }
- }
-
- @Override
- public void setCallbacks(final AsyncCodec.Callbacks callbacks, final Handler handler) {
- if (callbacks == null) {
- return;
- }
-
- 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 poller thread.
- looper = mBufferPoller.getLooper();
- }
- mCallbackSender = new CallbackSender(looper, callbacks);
- if (DEBUG) {
- Log.d(LOGTAG, "setCallbacks(): sender=" + mCallbackSender);
- }
- }
-
- @Override
- public void configure(
- final MediaFormat format, final Surface surface, final MediaCrypto crypto, final int flags) {
- assertCallbacks();
-
- 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;
- }
- }
-
- private void assertCallbacks() {
- if (mCallbackSender == null) {
- throw new IllegalStateException(LOGTAG + ": callback must be supplied with setCallbacks().");
- }
- }
-
- @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);
- }
- }
-
- @Override
- public final void setBitrate(final int bps) {
- final Bundle params = new Bundle();
- params.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bps);
- mCodec.setParameters(params);
- }
-
- @Override
- public final void queueInputBuffer(
- final int index,
- final int offset,
- final int size,
- final long presentationTimeUs,
- final int flags) {
- assertCallbacks();
-
- mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
-
- 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);
- }
-
- try {
- mCodec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
- } catch (final IllegalStateException e) {
- e.printStackTrace();
- mCallbackSender.notifyError(ERROR_CODEC);
- return;
- }
-
- mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
- mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
- }
-
- @Override
- public final void queueSecureInputBuffer(
- final int index,
- final int offset,
- final MediaCodec.CryptoInfo cryptoInfo,
- final long presentationTimeUs,
- final int flags) {
- assertCallbacks();
-
- mInputEnded = (flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
-
- try {
- mCodec.queueSecureInputBuffer(index, offset, cryptoInfo, presentationTimeUs, flags);
- } catch (final IllegalStateException e) {
- e.printStackTrace();
- mCallbackSender.notifyError(ERROR_CODEC);
- return;
- }
-
- mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_INPUT_BUFFERS);
- mBufferPoller.schedulePolling(BufferPoller.MSG_POLL_OUTPUT_BUFFERS);
- }
-
- @Override
- public final void releaseOutputBuffer(final int index, final boolean render) {
- assertCallbacks();
-
- mCodec.releaseOutputBuffer(index, render);
- }
-
- @Override
- public final ByteBuffer getInputBuffer(final int index) {
- assertCallbacks();
-
- return mInputBuffers[index];
- }
-
- @Override
- public final ByteBuffer getOutputBuffer(final int index) {
- assertCallbacks();
-
- return mOutputBuffers[index];
- }
-
- @Override
- public MediaFormat getInputFormat() {
- return null;
- }
-
- @Override
- public void flush() {
- assertCallbacks();
-
- mInputEnded = false;
- mOutputEnded = false;
- cancelPendingTasks();
- mCodec.flush();
- }
-
- private void cancelPendingTasks() {
- mBufferPoller.cancel();
- mCallbackSender.cancel();
- }
-
- @Override
- public void stop() {
- assertCallbacks();
-
- cancelPendingTasks();
- mCodec.stop();
- }
-
- @Override
- public void release() {
- assertCallbacks();
-
- cancelPendingTasks();
- mCallbackSender = null;
- mCodec.release();
- stopBufferPoller();
- }
-
- private void stopBufferPoller() {
- if (mBufferPoller == null) {
- Log.e(LOGTAG, "no initialized poller.");
- return;
- }
-
- mBufferPoller.getLooper().quit();
- mBufferPoller = null;
-
- if (DEBUG) {
- Log.d(LOGTAG, "stop poller " + this);
- }
- }
-}
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
@@ -9,7 +9,6 @@ import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Binder;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -86,11 +85,7 @@ import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
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()));
- }
+ notify(obtainMessage(MSG_ERROR, e.getErrorCode()));
}
}