mirror of
https://github.com/androidx/media.git
synced 2025-05-10 00:59:51 +08:00
Use CRYPTO_ASYNC mode for video from API 34
This allows us to remove the additional thread we create for asynchronous buffer queuing and use a synchronized queueing approach again. This API looks strictly beneficial on all tested devices, but since this code path in MediaCodec has not been used widely, we leave an opt-out flag for now. PiperOrigin-RevId: 591867472
This commit is contained in:
parent
bb7aa2fb19
commit
e5aa69237e
@ -163,6 +163,20 @@ public class DefaultRenderersFactory implements RenderersFactory {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to enable {@link MediaCodec#CONFIGURE_FLAG_USE_CRYPTO_ASYNC} on API 34 and above
|
||||||
|
* when operating the codec in asynchronous mode.
|
||||||
|
*
|
||||||
|
* <p>This method is experimental. Its default value may change, or it may be renamed or removed
|
||||||
|
* in a future release.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public DefaultRenderersFactory experimentalSetMediaCodecAsyncCryptoFlagEnabled(
|
||||||
|
boolean enableAsyncCryptoFlag) {
|
||||||
|
codecAdapterFactory.experimentalSetAsyncCryptoFlagEnabled(enableAsyncCryptoFlag);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether to enable fallback to lower-priority decoders if decoder initialization fails.
|
* Sets whether to enable fallback to lower-priority decoders if decoder initialization fails.
|
||||||
* This may result in using a decoder that is less efficient or slower than the primary decoder.
|
* This may result in using a decoder that is less efficient or slower than the primary decoder.
|
||||||
|
@ -26,12 +26,16 @@ import android.os.Handler;
|
|||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import androidx.annotation.ChecksSdkIntAtLeast;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.TraceUtil;
|
import androidx.media3.common.util.TraceUtil;
|
||||||
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.decoder.CryptoInfo;
|
import androidx.media3.decoder.CryptoInfo;
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -54,6 +58,8 @@ import java.nio.ByteBuffer;
|
|||||||
private final Supplier<HandlerThread> callbackThreadSupplier;
|
private final Supplier<HandlerThread> callbackThreadSupplier;
|
||||||
private final Supplier<HandlerThread> queueingThreadSupplier;
|
private final Supplier<HandlerThread> queueingThreadSupplier;
|
||||||
|
|
||||||
|
private boolean enableSynchronousBufferQueueingWithAsyncCryptoFlag;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an factory for {@link AsynchronousMediaCodecAdapter} instances.
|
* Creates an factory for {@link AsynchronousMediaCodecAdapter} instances.
|
||||||
*
|
*
|
||||||
@ -74,6 +80,18 @@ import java.nio.ByteBuffer;
|
|||||||
Supplier<HandlerThread> queueingThreadSupplier) {
|
Supplier<HandlerThread> queueingThreadSupplier) {
|
||||||
this.callbackThreadSupplier = callbackThreadSupplier;
|
this.callbackThreadSupplier = callbackThreadSupplier;
|
||||||
this.queueingThreadSupplier = queueingThreadSupplier;
|
this.queueingThreadSupplier = queueingThreadSupplier;
|
||||||
|
enableSynchronousBufferQueueingWithAsyncCryptoFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to enable {@link MediaCodec#CONFIGURE_FLAG_USE_CRYPTO_ASYNC} on API 34 and
|
||||||
|
* above.
|
||||||
|
*
|
||||||
|
* <p>This method is experimental. Its default value may change, or it may be renamed or removed
|
||||||
|
* in a future release.
|
||||||
|
*/
|
||||||
|
public void experimentalSetAsyncCryptoFlagEnabled(boolean enableAsyncCryptoFlag) {
|
||||||
|
enableSynchronousBufferQueueingWithAsyncCryptoFlag = enableAsyncCryptoFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -85,15 +103,21 @@ import java.nio.ByteBuffer;
|
|||||||
try {
|
try {
|
||||||
TraceUtil.beginSection("createCodec:" + codecName);
|
TraceUtil.beginSection("createCodec:" + codecName);
|
||||||
codec = MediaCodec.createByCodecName(codecName);
|
codec = MediaCodec.createByCodecName(codecName);
|
||||||
|
int flags = configuration.flags;
|
||||||
|
MediaCodecBufferEnqueuer bufferEnqueuer;
|
||||||
|
if (enableSynchronousBufferQueueingWithAsyncCryptoFlag
|
||||||
|
&& useSynchronousBufferQueueingWithAsyncCryptoFlag(configuration.format)) {
|
||||||
|
bufferEnqueuer = new SynchronousMediaCodecBufferEnqueuer(codec);
|
||||||
|
flags |= MediaCodec.CONFIGURE_FLAG_USE_CRYPTO_ASYNC;
|
||||||
|
} else {
|
||||||
|
bufferEnqueuer =
|
||||||
|
new AsynchronousMediaCodecBufferEnqueuer(codec, queueingThreadSupplier.get());
|
||||||
|
}
|
||||||
codecAdapter =
|
codecAdapter =
|
||||||
new AsynchronousMediaCodecAdapter(
|
new AsynchronousMediaCodecAdapter(codec, callbackThreadSupplier.get(), bufferEnqueuer);
|
||||||
codec, callbackThreadSupplier.get(), queueingThreadSupplier.get());
|
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
codecAdapter.initialize(
|
codecAdapter.initialize(
|
||||||
configuration.mediaFormat,
|
configuration.mediaFormat, configuration.surface, configuration.crypto, flags);
|
||||||
configuration.surface,
|
|
||||||
configuration.crypto,
|
|
||||||
configuration.flags);
|
|
||||||
return codecAdapter;
|
return codecAdapter;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (codecAdapter != null) {
|
if (codecAdapter != null) {
|
||||||
@ -104,6 +128,16 @@ import java.nio.ByteBuffer;
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ChecksSdkIntAtLeast(api = 34)
|
||||||
|
private static boolean useSynchronousBufferQueueingWithAsyncCryptoFlag(Format format) {
|
||||||
|
if (Util.SDK_INT < 34) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// TODO: b/316565675 - Remove restriction to video once MediaCodec supports
|
||||||
|
// CONFIGURE_FLAG_USE_CRYPTO_ASYNC for audio too
|
||||||
|
return MimeTypes.isVideo(format.sampleMimeType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Documented
|
@Documented
|
||||||
@ -118,15 +152,15 @@ import java.nio.ByteBuffer;
|
|||||||
|
|
||||||
private final MediaCodec codec;
|
private final MediaCodec codec;
|
||||||
private final AsynchronousMediaCodecCallback asynchronousMediaCodecCallback;
|
private final AsynchronousMediaCodecCallback asynchronousMediaCodecCallback;
|
||||||
private final AsynchronousMediaCodecBufferEnqueuer bufferEnqueuer;
|
private final MediaCodecBufferEnqueuer bufferEnqueuer;
|
||||||
private boolean codecReleased;
|
private boolean codecReleased;
|
||||||
private @State int state;
|
private @State int state;
|
||||||
|
|
||||||
private AsynchronousMediaCodecAdapter(
|
private AsynchronousMediaCodecAdapter(
|
||||||
MediaCodec codec, HandlerThread callbackThread, HandlerThread enqueueingThread) {
|
MediaCodec codec, HandlerThread callbackThread, MediaCodecBufferEnqueuer bufferEnqueuer) {
|
||||||
this.codec = codec;
|
this.codec = codec;
|
||||||
this.asynchronousMediaCodecCallback = new AsynchronousMediaCodecCallback(callbackThread);
|
this.asynchronousMediaCodecCallback = new AsynchronousMediaCodecCallback(callbackThread);
|
||||||
this.bufferEnqueuer = new AsynchronousMediaCodecBufferEnqueuer(codec, enqueueingThread);
|
this.bufferEnqueuer = bufferEnqueuer;
|
||||||
this.state = STATE_CREATED;
|
this.state = STATE_CREATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package androidx.media3.exoplayer.mediacodec;
|
package androidx.media3.exoplayer.mediacodec;
|
||||||
|
|
||||||
import static androidx.annotation.VisibleForTesting.NONE;
|
import static androidx.annotation.VisibleForTesting.NONE;
|
||||||
@ -39,13 +38,11 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs {@link MediaCodec} input buffer queueing on a background thread.
|
* Performs {@link MediaCodec} input buffer queueing on a background thread. This is required on API
|
||||||
*
|
* 33 and below because queuing secure buffers blocks until decryption is complete.
|
||||||
* <p>The implementation of this class assumes that its public methods will be called from the same
|
|
||||||
* thread.
|
|
||||||
*/
|
*/
|
||||||
@RequiresApi(23)
|
@RequiresApi(23)
|
||||||
class AsynchronousMediaCodecBufferEnqueuer {
|
/* package */ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecBufferEnqueuer {
|
||||||
|
|
||||||
private static final int MSG_QUEUE_INPUT_BUFFER = 0;
|
private static final int MSG_QUEUE_INPUT_BUFFER = 0;
|
||||||
private static final int MSG_QUEUE_SECURE_INPUT_BUFFER = 1;
|
private static final int MSG_QUEUE_SECURE_INPUT_BUFFER = 1;
|
||||||
@ -83,11 +80,7 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
|||||||
pendingRuntimeException = new AtomicReference<>();
|
pendingRuntimeException = new AtomicReference<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Starts this instance.
|
|
||||||
*
|
|
||||||
* <p>Call this method after creating an instance and before queueing input buffers.
|
|
||||||
*/
|
|
||||||
public void start() {
|
public void start() {
|
||||||
if (!started) {
|
if (!started) {
|
||||||
handlerThread.start();
|
handlerThread.start();
|
||||||
@ -102,11 +95,7 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Submits an input buffer for decoding.
|
|
||||||
*
|
|
||||||
* @see android.media.MediaCodec#queueInputBuffer
|
|
||||||
*/
|
|
||||||
public void queueInputBuffer(
|
public void queueInputBuffer(
|
||||||
int index, int offset, int size, long presentationTimeUs, int flags) {
|
int index, int offset, int size, long presentationTimeUs, int flags) {
|
||||||
maybeThrowException();
|
maybeThrowException();
|
||||||
@ -116,15 +105,7 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
|||||||
message.sendToTarget();
|
message.sendToTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Submits an input buffer that potentially contains encrypted data for decoding.
|
|
||||||
*
|
|
||||||
* <p>Note: This method behaves as {@link MediaCodec#queueSecureInputBuffer} with the difference
|
|
||||||
* that {@code info} is of type {@link CryptoInfo} and not {@link
|
|
||||||
* android.media.MediaCodec.CryptoInfo}.
|
|
||||||
*
|
|
||||||
* @see android.media.MediaCodec#queueSecureInputBuffer
|
|
||||||
*/
|
|
||||||
public void queueSecureInputBuffer(
|
public void queueSecureInputBuffer(
|
||||||
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags) {
|
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags) {
|
||||||
maybeThrowException();
|
maybeThrowException();
|
||||||
@ -136,12 +117,13 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
|||||||
message.sendToTarget();
|
message.sendToTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setParameters(Bundle params) {
|
public void setParameters(Bundle params) {
|
||||||
maybeThrowException();
|
maybeThrowException();
|
||||||
castNonNull(handler).obtainMessage(MSG_SET_PARAMETERS, params).sendToTarget();
|
castNonNull(handler).obtainMessage(MSG_SET_PARAMETERS, params).sendToTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Flushes the instance. */
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
if (started) {
|
if (started) {
|
||||||
try {
|
try {
|
||||||
@ -155,7 +137,7 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Shuts down the instance. Make sure to call this method to release its internal resources. */
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (started) {
|
if (started) {
|
||||||
flush();
|
flush();
|
||||||
@ -164,12 +146,12 @@ class AsynchronousMediaCodecBufferEnqueuer {
|
|||||||
started = false;
|
started = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Blocks the current thread until all input buffers pending queueing are submitted. */
|
@Override
|
||||||
public void waitUntilQueueingComplete() throws InterruptedException {
|
public void waitUntilQueueingComplete() throws InterruptedException {
|
||||||
blockUntilHandlerThreadIsIdle();
|
blockUntilHandlerThreadIsIdle();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Throw any exception that occurred on the enqueuer's background queueing thread. */
|
@Override
|
||||||
public void maybeThrowException() {
|
public void maybeThrowException() {
|
||||||
@Nullable RuntimeException exception = pendingRuntimeException.getAndSet(null);
|
@Nullable RuntimeException exception = pendingRuntimeException.getAndSet(null);
|
||||||
if (exception != null) {
|
if (exception != null) {
|
||||||
|
@ -63,6 +63,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@Nullable
|
@Nullable
|
||||||
private MediaCodec.CodecException mediaCodecException;
|
private MediaCodec.CodecException mediaCodecException;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
@Nullable
|
||||||
|
private MediaCodec.CryptoException mediaCodecCryptoException;
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private long pendingFlushCount;
|
private long pendingFlushCount;
|
||||||
|
|
||||||
@ -228,6 +232,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCryptoError(MediaCodec codec, MediaCodec.CryptoException e) {
|
||||||
|
synchronized (lock) {
|
||||||
|
mediaCodecCryptoException = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
|
public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
@ -287,6 +298,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
private void maybeThrowException() {
|
private void maybeThrowException() {
|
||||||
maybeThrowInternalException();
|
maybeThrowInternalException();
|
||||||
maybeThrowMediaCodecException();
|
maybeThrowMediaCodecException();
|
||||||
|
maybeThrowMediaCodecCryptoException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
@ -307,6 +319,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private void maybeThrowMediaCodecCryptoException() {
|
||||||
|
if (mediaCodecCryptoException != null) {
|
||||||
|
MediaCodec.CryptoException cryptoException = mediaCodecCryptoException;
|
||||||
|
mediaCodecCryptoException = null;
|
||||||
|
throw cryptoException;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setInternalException(IllegalStateException e) {
|
private void setInternalException(IllegalStateException e) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
internalException = e;
|
internalException = e;
|
||||||
|
@ -17,6 +17,7 @@ package androidx.media3.exoplayer.mediacodec;
|
|||||||
|
|
||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
@ -54,9 +55,11 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.
|
|||||||
private static final String TAG = "DMCodecAdapterFactory";
|
private static final String TAG = "DMCodecAdapterFactory";
|
||||||
|
|
||||||
private @Mode int asynchronousMode;
|
private @Mode int asynchronousMode;
|
||||||
|
private boolean asyncCryptoFlagEnabled;
|
||||||
|
|
||||||
public DefaultMediaCodecAdapterFactory() {
|
public DefaultMediaCodecAdapterFactory() {
|
||||||
asynchronousMode = MODE_DEFAULT;
|
asynchronousMode = MODE_DEFAULT;
|
||||||
|
asyncCryptoFlagEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,6 +86,20 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to enable {@link MediaCodec#CONFIGURE_FLAG_USE_CRYPTO_ASYNC} on API 34 and above
|
||||||
|
* for {@link AsynchronousMediaCodecAdapter} instances.
|
||||||
|
*
|
||||||
|
* <p>This method is experimental. Its default value may change, or it may be renamed or removed
|
||||||
|
* in a future release.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public DefaultMediaCodecAdapterFactory experimentalSetAsyncCryptoFlagEnabled(
|
||||||
|
boolean enableAsyncCryptoFlag) {
|
||||||
|
asyncCryptoFlagEnabled = enableAsyncCryptoFlag;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configuration)
|
public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configuration)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@ -96,6 +113,7 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.
|
|||||||
+ Util.getTrackTypeString(trackType));
|
+ Util.getTrackTypeString(trackType));
|
||||||
AsynchronousMediaCodecAdapter.Factory factory =
|
AsynchronousMediaCodecAdapter.Factory factory =
|
||||||
new AsynchronousMediaCodecAdapter.Factory(trackType);
|
new AsynchronousMediaCodecAdapter.Factory(trackType);
|
||||||
|
factory.experimentalSetAsyncCryptoFlagEnabled(asyncCryptoFlagEnabled);
|
||||||
return factory.createAdapter(configuration);
|
return factory.createAdapter(configuration);
|
||||||
}
|
}
|
||||||
return new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration);
|
return new SynchronousMediaCodecAdapter.Factory().createAdapter(configuration);
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.media3.exoplayer.mediacodec;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.media3.decoder.CryptoInfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to queue buffers to a {@link MediaCodec}.
|
||||||
|
*
|
||||||
|
* <p>All methods must be called from the same thread.
|
||||||
|
*/
|
||||||
|
/* package */ interface MediaCodecBufferEnqueuer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts this instance.
|
||||||
|
*
|
||||||
|
* <p>Call this method after creating an instance and before queueing input buffers.
|
||||||
|
*/
|
||||||
|
void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits an input buffer for decoding.
|
||||||
|
*
|
||||||
|
* @see android.media.MediaCodec#queueInputBuffer
|
||||||
|
*/
|
||||||
|
void queueInputBuffer(int index, int offset, int size, long presentationTimeUs, int flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits an input buffer that potentially contains encrypted data for decoding.
|
||||||
|
*
|
||||||
|
* <p>Note: This method behaves as {@link MediaCodec#queueSecureInputBuffer} with the difference
|
||||||
|
* that {@code info} is of type {@link CryptoInfo} and not {@link MediaCodec.CryptoInfo}.
|
||||||
|
*
|
||||||
|
* @see MediaCodec#queueSecureInputBuffer
|
||||||
|
*/
|
||||||
|
void queueSecureInputBuffer(
|
||||||
|
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submits new codec parameters that should be applied from the next queued input buffer.
|
||||||
|
*
|
||||||
|
* @see MediaCodec#setParameters(Bundle)
|
||||||
|
*/
|
||||||
|
@RequiresApi(19)
|
||||||
|
void setParameters(Bundle parameters);
|
||||||
|
|
||||||
|
/** Flushes the instance. */
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
/** Shuts down the instance. Make sure to call this method to release its internal resources. */
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
/** Blocks the current thread until all input buffers pending queueing are submitted. */
|
||||||
|
void waitUntilQueueingComplete() throws InterruptedException;
|
||||||
|
|
||||||
|
/** Throw any exception that occurred during the enqueueing process. */
|
||||||
|
void maybeThrowException();
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package androidx.media3.exoplayer.mediacodec;
|
||||||
|
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.decoder.CryptoInfo;
|
||||||
|
|
||||||
|
@RequiresApi(23)
|
||||||
|
@UnstableApi
|
||||||
|
/* package */ class SynchronousMediaCodecBufferEnqueuer implements MediaCodecBufferEnqueuer {
|
||||||
|
|
||||||
|
private final MediaCodec codec;
|
||||||
|
|
||||||
|
public SynchronousMediaCodecBufferEnqueuer(MediaCodec codec) {
|
||||||
|
this.codec = codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void queueInputBuffer(
|
||||||
|
int index, int offset, int size, long presentationTimeUs, int flags) {
|
||||||
|
codec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void queueSecureInputBuffer(
|
||||||
|
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags) {
|
||||||
|
codec.queueSecureInputBuffer(
|
||||||
|
index, offset, info.getFrameworkCryptoInfo(), presentationTimeUs, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParameters(Bundle parameters) {
|
||||||
|
codec.setParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void waitUntilQueueingComplete() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void maybeThrowException() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user