Commit e7f59126 by christosts Committed by Ian Baker

Add Factory to MediaCodecAdapter

In a later change, MediaCoderAdapter.Factory will be injectable to
MediaCodecRenderer.

PiperOrigin-RevId: 346525171
parent 9a00ba1d
...@@ -29,6 +29,7 @@ import androidx.annotation.RequiresApi; ...@@ -29,6 +29,7 @@ import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoInfo; import com.google.android.exoplayer2.decoder.CryptoInfo;
import com.google.common.base.Supplier;
import java.lang.annotation.Documented; import java.lang.annotation.Documented;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -42,49 +43,87 @@ import java.nio.ByteBuffer; ...@@ -42,49 +43,87 @@ import java.nio.ByteBuffer;
@RequiresApi(23) @RequiresApi(23)
/* package */ final class AsynchronousMediaCodecAdapter implements MediaCodecAdapter { /* package */ final class AsynchronousMediaCodecAdapter implements MediaCodecAdapter {
@Documented /** A factory for {@link AsynchronousMediaCodecAdapter} instances. */
@Retention(RetentionPolicy.SOURCE) public static final class Factory implements MediaCodecAdapter.Factory {
@IntDef({STATE_CREATED, STATE_CONFIGURED, STATE_STARTED, STATE_SHUT_DOWN}) private final Supplier<HandlerThread> callbackThreadSupplier;
private @interface State {} private final Supplier<HandlerThread> queueingThreadSupplier;
private final boolean forceQueueingSynchronizationWorkaround;
private static final int STATE_CREATED = 0;
private static final int STATE_CONFIGURED = 1;
private static final int STATE_STARTED = 2;
private static final int STATE_SHUT_DOWN = 3;
private final MediaCodec codec;
private final AsynchronousMediaCodecCallback asynchronousMediaCodecCallback;
private final AsynchronousMediaCodecBufferEnqueuer bufferEnqueuer;
private final boolean synchronizeCodecInteractionsWithQueueing; private final boolean synchronizeCodecInteractionsWithQueueing;
private boolean codecReleased;
@State private int state; /** Creates a factory for the specified {@code trackType}. */
public Factory(int trackType) {
this(
trackType,
/* forceQueueingSynchronizationWorkaround= */ false,
/* synchronizeCodecInteractionsWithQueueing= */ false);
}
/** /**
* Creates an instance that wraps the specified {@link MediaCodec}. * Creates an factory for {@link AsynchronousMediaCodecAdapter} instances.
* *
* @param codec The {@link MediaCodec} to wrap.
* @param trackType One of {@link C#TRACK_TYPE_AUDIO} or {@link C#TRACK_TYPE_VIDEO}. Used for * @param trackType One of {@link C#TRACK_TYPE_AUDIO} or {@link C#TRACK_TYPE_VIDEO}. Used for
* labelling the internal thread accordingly. * labelling the internal thread accordingly.
* @param forceQueueingSynchronizationWorkaround Whether the queueing synchronization workaround
* will be enabled by default or only for the predefined devices.
* @param synchronizeCodecInteractionsWithQueueing Whether the adapter should synchronize {@link * @param synchronizeCodecInteractionsWithQueueing Whether the adapter should synchronize {@link
* MediaCodec} interactions with asynchronous buffer queueing. When {@code true}, codec * MediaCodec} interactions with asynchronous buffer queueing. When {@code true}, codec
* interactions will wait until all input buffers pending queueing wil be submitted to the * interactions will wait until all input buffers pending queueing wil be submitted to the
* {@link MediaCodec}. * {@link MediaCodec}.
*/ */
/* package */ AsynchronousMediaCodecAdapter( public Factory(
MediaCodec codec,
int trackType, int trackType,
boolean forceQueueingSynchronizationWorkaround, boolean forceQueueingSynchronizationWorkaround,
boolean synchronizeCodecInteractionsWithQueueing) { boolean synchronizeCodecInteractionsWithQueueing) {
this( this(
codec, /* callbackThreadSupplier= */ () ->
new HandlerThread(createCallbackThreadLabel(trackType)), new HandlerThread(createCallbackThreadLabel(trackType)),
/* queueingThreadSupplier= */ () ->
new HandlerThread(createQueueingThreadLabel(trackType)), new HandlerThread(createQueueingThreadLabel(trackType)),
forceQueueingSynchronizationWorkaround, forceQueueingSynchronizationWorkaround,
synchronizeCodecInteractionsWithQueueing); synchronizeCodecInteractionsWithQueueing);
} }
@VisibleForTesting @VisibleForTesting
/* package */ AsynchronousMediaCodecAdapter( /* package */ Factory(
Supplier<HandlerThread> callbackThreadSupplier,
Supplier<HandlerThread> queueingThreadSupplier,
boolean forceQueueingSynchronizationWorkaround,
boolean synchronizeCodecInteractionsWithQueueing) {
this.callbackThreadSupplier = callbackThreadSupplier;
this.queueingThreadSupplier = queueingThreadSupplier;
this.forceQueueingSynchronizationWorkaround = forceQueueingSynchronizationWorkaround;
this.synchronizeCodecInteractionsWithQueueing = synchronizeCodecInteractionsWithQueueing;
}
@Override
public AsynchronousMediaCodecAdapter createAdapter(MediaCodec codec) {
return new AsynchronousMediaCodecAdapter(
codec,
callbackThreadSupplier.get(),
queueingThreadSupplier.get(),
forceQueueingSynchronizationWorkaround,
synchronizeCodecInteractionsWithQueueing);
}
}
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({STATE_CREATED, STATE_CONFIGURED, STATE_STARTED, STATE_SHUT_DOWN})
private @interface State {}
private static final int STATE_CREATED = 0;
private static final int STATE_CONFIGURED = 1;
private static final int STATE_STARTED = 2;
private static final int STATE_SHUT_DOWN = 3;
private final MediaCodec codec;
private final AsynchronousMediaCodecCallback asynchronousMediaCodecCallback;
private final AsynchronousMediaCodecBufferEnqueuer bufferEnqueuer;
private final boolean synchronizeCodecInteractionsWithQueueing;
private boolean codecReleased;
@State private int state;
private AsynchronousMediaCodecAdapter(
MediaCodec codec, MediaCodec codec,
HandlerThread callbackThread, HandlerThread callbackThread,
HandlerThread enqueueingThread, HandlerThread enqueueingThread,
......
...@@ -36,6 +36,13 @@ import java.nio.ByteBuffer; ...@@ -36,6 +36,13 @@ import java.nio.ByteBuffer;
*/ */
public interface MediaCodecAdapter { public interface MediaCodecAdapter {
/** A factory for {@link MediaCodecAdapter} instances. */
interface Factory {
/** Creates an instance wrapping the provided {@link MediaCodec} instance. */
MediaCodecAdapter createAdapter(MediaCodec codec);
}
/** /**
* Listener to be called when an output frame has rendered on the output surface. * Listener to be called when an output frame has rendered on the output surface.
* *
......
...@@ -1076,13 +1076,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { ...@@ -1076,13 +1076,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
MediaCodec codec = MediaCodec.createByCodecName(codecName); MediaCodec codec = MediaCodec.createByCodecName(codecName);
if (enableAsynchronousBufferQueueing && Util.SDK_INT >= 23) { if (enableAsynchronousBufferQueueing && Util.SDK_INT >= 23) {
codecAdapter = codecAdapter =
new AsynchronousMediaCodecAdapter( new AsynchronousMediaCodecAdapter.Factory(
codec,
getTrackType(), getTrackType(),
forceAsyncQueueingSynchronizationWorkaround, forceAsyncQueueingSynchronizationWorkaround,
enableSynchronizeCodecInteractionsWithQueueing); enableSynchronizeCodecInteractionsWithQueueing)
.createAdapter(codec);
} else { } else {
codecAdapter = new SynchronousMediaCodecAdapter(codec); codecAdapter = new SynchronousMediaCodecAdapter.Factory().createAdapter(codec);
} }
TraceUtil.endSection(); TraceUtil.endSection();
TraceUtil.beginSection("configureCodec"); TraceUtil.beginSection("configureCodec");
......
...@@ -36,11 +36,19 @@ import java.nio.ByteBuffer; ...@@ -36,11 +36,19 @@ import java.nio.ByteBuffer;
*/ */
/* package */ final class SynchronousMediaCodecAdapter implements MediaCodecAdapter { /* package */ final class SynchronousMediaCodecAdapter implements MediaCodecAdapter {
/** A factory for {@link SynchronousMediaCodecAdapter} instances. */
public static final class Factory implements MediaCodecAdapter.Factory {
@Override
public MediaCodecAdapter createAdapter(MediaCodec codec) {
return new SynchronousMediaCodecAdapter(codec);
}
}
private final MediaCodec codec; private final MediaCodec codec;
@Nullable private ByteBuffer[] inputByteBuffers; @Nullable private ByteBuffer[] inputByteBuffers;
@Nullable private ByteBuffer[] outputByteBuffers; @Nullable private ByteBuffer[] outputByteBuffers;
public SynchronousMediaCodecAdapter(MediaCodec mediaCodec) { private SynchronousMediaCodecAdapter(MediaCodec mediaCodec) {
this.codec = mediaCodec; this.codec = mediaCodec;
} }
......
...@@ -47,12 +47,12 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -47,12 +47,12 @@ public class AsynchronousMediaCodecAdapterTest {
callbackThread = new HandlerThread("TestCallbackThread"); callbackThread = new HandlerThread("TestCallbackThread");
queueingThread = new HandlerThread("TestQueueingThread"); queueingThread = new HandlerThread("TestQueueingThread");
adapter = adapter =
new AsynchronousMediaCodecAdapter( new AsynchronousMediaCodecAdapter.Factory(
codec, /* callbackThreadSupplier= */ () -> callbackThread,
callbackThread, /* queueingThreadSupplier= */ () -> queueingThread,
queueingThread,
/* forceQueueingSynchronizationWorkaround= */ false, /* forceQueueingSynchronizationWorkaround= */ false,
/* synchronizeCodecInteractionsWithQueueing= */ false); /* synchronizeCodecInteractionsWithQueueing= */ false)
.createAdapter(codec);
bufferInfo = new MediaCodec.BufferInfo(); bufferInfo = new MediaCodec.BufferInfo();
} }
...@@ -64,7 +64,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -64,7 +64,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void dequeueInputBufferIndex_withoutInputBuffer_returnsTryAgainLater() { public void dequeueInputBufferIndex_withoutInputBuffer_returnsTryAgainLater() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
// After adapter.start(), the ShadowMediaCodec offers one input buffer. We pause the looper so // After adapter.start(), the ShadowMediaCodec offers one input buffer. We pause the looper so
// that the buffer is not propagated to the adapter. // that the buffer is not propagated to the adapter.
shadowOf(callbackThread.getLooper()).pause(); shadowOf(callbackThread.getLooper()).pause();
...@@ -76,7 +76,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -76,7 +76,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void dequeueInputBufferIndex_withInputBuffer_returnsInputBuffer() { public void dequeueInputBufferIndex_withInputBuffer_returnsInputBuffer() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
adapter.start(); adapter.start();
// After start(), the ShadowMediaCodec offers input buffer 0. We advance the looper to make sure // After start(), the ShadowMediaCodec offers input buffer 0. We advance the looper to make sure
// and messages have been propagated to the adapter. // and messages have been propagated to the adapter.
...@@ -90,7 +90,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -90,7 +90,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void dequeueInputBufferIndex_withMediaCodecError_throwsException() throws Exception { public void dequeueInputBufferIndex_withMediaCodecError_throwsException() throws Exception {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
// Pause the looper so that we interact with the adapter from this thread only. // Pause the looper so that we interact with the adapter from this thread only.
shadowOf(callbackThread.getLooper()).pause(); shadowOf(callbackThread.getLooper()).pause();
adapter.start(); adapter.start();
...@@ -104,7 +104,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -104,7 +104,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() { public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
adapter.start(); adapter.start();
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we // After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
// progress the adapter's looper. We progress the looper so that we call shutdown() on a // progress the adapter's looper. We progress the looper so that we call shutdown() on a
...@@ -119,7 +119,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -119,7 +119,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void dequeueOutputBufferIndex_withoutOutputBuffer_returnsTryAgainLater() { public void dequeueOutputBufferIndex_withoutOutputBuffer_returnsTryAgainLater() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
adapter.start(); adapter.start();
// After start(), the ShadowMediaCodec offers an output format change. We progress the looper // After start(), the ShadowMediaCodec offers an output format change. We progress the looper
...@@ -136,7 +136,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -136,7 +136,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void dequeueOutputBufferIndex_withOutputBuffer_returnsOutputBuffer() { public void dequeueOutputBufferIndex_withOutputBuffer_returnsOutputBuffer() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
adapter.start(); adapter.start();
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we // After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
// progress the adapter's looper. // progress the adapter's looper.
...@@ -164,7 +164,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -164,7 +164,7 @@ public class AsynchronousMediaCodecAdapterTest {
public void dequeueOutputBufferIndex_withMediaCodecError_throwsException() throws Exception { public void dequeueOutputBufferIndex_withMediaCodecError_throwsException() throws Exception {
// Pause the looper so that we interact with the adapter from this thread only. // Pause the looper so that we interact with the adapter from this thread only.
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
shadowOf(callbackThread.getLooper()).pause(); shadowOf(callbackThread.getLooper()).pause();
adapter.start(); adapter.start();
...@@ -177,7 +177,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -177,7 +177,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() { public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
adapter.start(); adapter.start();
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we // After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
// progress the adapter's looper. // progress the adapter's looper.
...@@ -197,7 +197,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -197,7 +197,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void getOutputFormat_withoutFormatReceived_throwsException() { public void getOutputFormat_withoutFormatReceived_throwsException() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
// After start() the ShadowMediaCodec offers an output format change. Pause the looper so that // After start() the ShadowMediaCodec offers an output format change. Pause the looper so that
// the format change is not propagated to the adapter. // the format change is not propagated to the adapter.
shadowOf(callbackThread.getLooper()).pause(); shadowOf(callbackThread.getLooper()).pause();
...@@ -209,7 +209,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -209,7 +209,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void getOutputFormat_withMultipleFormats_returnsCorrectFormat() { public void getOutputFormat_withMultipleFormats_returnsCorrectFormat() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
adapter.start(); adapter.start();
// After start(), the ShadowMediaCodec offers an output format, which is available only if we // After start(), the ShadowMediaCodec offers an output format, which is available only if we
// progress the adapter's looper. // progress the adapter's looper.
...@@ -233,7 +233,7 @@ public class AsynchronousMediaCodecAdapterTest { ...@@ -233,7 +233,7 @@ public class AsynchronousMediaCodecAdapterTest {
@Test @Test
public void getOutputFormat_afterFlush_returnsPreviousFormat() { public void getOutputFormat_afterFlush_returnsPreviousFormat() {
adapter.configure( adapter.configure(
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0); createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
adapter.start(); adapter.start();
// After start(), the ShadowMediaCodec offers an output format, which is available only if we // After start(), the ShadowMediaCodec offers an output format, which is available only if we
// progress the adapter's looper. // progress the adapter's looper.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment