mirror of
https://github.com/androidx/media.git
synced 2025-05-03 21:57:46 +08:00
Add Factory to MediaCodecAdapter
In a later change, MediaCoderAdapter.Factory will be injectable to MediaCodecRenderer. PiperOrigin-RevId: 346525171
This commit is contained in:
parent
9a00ba1d38
commit
e7f5912677
@ -29,6 +29,7 @@ import androidx.annotation.RequiresApi;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.decoder.CryptoInfo;
|
||||
import com.google.common.base.Supplier;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
@ -42,6 +43,69 @@ import java.nio.ByteBuffer;
|
||||
@RequiresApi(23)
|
||||
/* package */ final class AsynchronousMediaCodecAdapter implements MediaCodecAdapter {
|
||||
|
||||
/** A factory for {@link AsynchronousMediaCodecAdapter} instances. */
|
||||
public static final class Factory implements MediaCodecAdapter.Factory {
|
||||
private final Supplier<HandlerThread> callbackThreadSupplier;
|
||||
private final Supplier<HandlerThread> queueingThreadSupplier;
|
||||
private final boolean forceQueueingSynchronizationWorkaround;
|
||||
private final boolean synchronizeCodecInteractionsWithQueueing;
|
||||
|
||||
/** Creates a factory for the specified {@code trackType}. */
|
||||
public Factory(int trackType) {
|
||||
this(
|
||||
trackType,
|
||||
/* forceQueueingSynchronizationWorkaround= */ false,
|
||||
/* synchronizeCodecInteractionsWithQueueing= */ false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an factory for {@link AsynchronousMediaCodecAdapter} instances.
|
||||
*
|
||||
* @param trackType One of {@link C#TRACK_TYPE_AUDIO} or {@link C#TRACK_TYPE_VIDEO}. Used for
|
||||
* 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
|
||||
* MediaCodec} interactions with asynchronous buffer queueing. When {@code true}, codec
|
||||
* interactions will wait until all input buffers pending queueing wil be submitted to the
|
||||
* {@link MediaCodec}.
|
||||
*/
|
||||
public Factory(
|
||||
int trackType,
|
||||
boolean forceQueueingSynchronizationWorkaround,
|
||||
boolean synchronizeCodecInteractionsWithQueueing) {
|
||||
this(
|
||||
/* callbackThreadSupplier= */ () ->
|
||||
new HandlerThread(createCallbackThreadLabel(trackType)),
|
||||
/* queueingThreadSupplier= */ () ->
|
||||
new HandlerThread(createQueueingThreadLabel(trackType)),
|
||||
forceQueueingSynchronizationWorkaround,
|
||||
synchronizeCodecInteractionsWithQueueing);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* 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})
|
||||
@ -59,32 +123,7 @@ import java.nio.ByteBuffer;
|
||||
private boolean codecReleased;
|
||||
@State private int state;
|
||||
|
||||
/**
|
||||
* Creates an instance that wraps the specified {@link MediaCodec}.
|
||||
*
|
||||
* @param codec The {@link MediaCodec} to wrap.
|
||||
* @param trackType One of {@link C#TRACK_TYPE_AUDIO} or {@link C#TRACK_TYPE_VIDEO}. Used for
|
||||
* labelling the internal thread accordingly.
|
||||
* @param synchronizeCodecInteractionsWithQueueing Whether the adapter should synchronize {@link
|
||||
* MediaCodec} interactions with asynchronous buffer queueing. When {@code true}, codec
|
||||
* interactions will wait until all input buffers pending queueing wil be submitted to the
|
||||
* {@link MediaCodec}.
|
||||
*/
|
||||
/* package */ AsynchronousMediaCodecAdapter(
|
||||
MediaCodec codec,
|
||||
int trackType,
|
||||
boolean forceQueueingSynchronizationWorkaround,
|
||||
boolean synchronizeCodecInteractionsWithQueueing) {
|
||||
this(
|
||||
codec,
|
||||
new HandlerThread(createCallbackThreadLabel(trackType)),
|
||||
new HandlerThread(createQueueingThreadLabel(trackType)),
|
||||
forceQueueingSynchronizationWorkaround,
|
||||
synchronizeCodecInteractionsWithQueueing);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ AsynchronousMediaCodecAdapter(
|
||||
private AsynchronousMediaCodecAdapter(
|
||||
MediaCodec codec,
|
||||
HandlerThread callbackThread,
|
||||
HandlerThread enqueueingThread,
|
||||
|
@ -36,6 +36,13 @@ import java.nio.ByteBuffer;
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -1076,13 +1076,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||
MediaCodec codec = MediaCodec.createByCodecName(codecName);
|
||||
if (enableAsynchronousBufferQueueing && Util.SDK_INT >= 23) {
|
||||
codecAdapter =
|
||||
new AsynchronousMediaCodecAdapter(
|
||||
codec,
|
||||
getTrackType(),
|
||||
forceAsyncQueueingSynchronizationWorkaround,
|
||||
enableSynchronizeCodecInteractionsWithQueueing);
|
||||
new AsynchronousMediaCodecAdapter.Factory(
|
||||
getTrackType(),
|
||||
forceAsyncQueueingSynchronizationWorkaround,
|
||||
enableSynchronizeCodecInteractionsWithQueueing)
|
||||
.createAdapter(codec);
|
||||
} else {
|
||||
codecAdapter = new SynchronousMediaCodecAdapter(codec);
|
||||
codecAdapter = new SynchronousMediaCodecAdapter.Factory().createAdapter(codec);
|
||||
}
|
||||
TraceUtil.endSection();
|
||||
TraceUtil.beginSection("configureCodec");
|
||||
|
@ -36,11 +36,19 @@ import java.nio.ByteBuffer;
|
||||
*/
|
||||
/* 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;
|
||||
@Nullable private ByteBuffer[] inputByteBuffers;
|
||||
@Nullable private ByteBuffer[] outputByteBuffers;
|
||||
|
||||
public SynchronousMediaCodecAdapter(MediaCodec mediaCodec) {
|
||||
private SynchronousMediaCodecAdapter(MediaCodec mediaCodec) {
|
||||
this.codec = mediaCodec;
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,12 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
callbackThread = new HandlerThread("TestCallbackThread");
|
||||
queueingThread = new HandlerThread("TestQueueingThread");
|
||||
adapter =
|
||||
new AsynchronousMediaCodecAdapter(
|
||||
codec,
|
||||
callbackThread,
|
||||
queueingThread,
|
||||
/* forceQueueingSynchronizationWorkaround= */ false,
|
||||
/* synchronizeCodecInteractionsWithQueueing= */ false);
|
||||
new AsynchronousMediaCodecAdapter.Factory(
|
||||
/* callbackThreadSupplier= */ () -> callbackThread,
|
||||
/* queueingThreadSupplier= */ () -> queueingThread,
|
||||
/* forceQueueingSynchronizationWorkaround= */ false,
|
||||
/* synchronizeCodecInteractionsWithQueueing= */ false)
|
||||
.createAdapter(codec);
|
||||
bufferInfo = new MediaCodec.BufferInfo();
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void dequeueInputBufferIndex_withoutInputBuffer_returnsTryAgainLater() {
|
||||
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
|
||||
// that the buffer is not propagated to the adapter.
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
@ -76,7 +76,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void dequeueInputBufferIndex_withInputBuffer_returnsInputBuffer() {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0. We advance the looper to make sure
|
||||
// and messages have been propagated to the adapter.
|
||||
@ -90,7 +90,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void dequeueInputBufferIndex_withMediaCodecError_throwsException() throws Exception {
|
||||
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.
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
adapter.start();
|
||||
@ -104,7 +104,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
adapter.start();
|
||||
// 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
|
||||
@ -119,7 +119,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void dequeueOutputBufferIndex_withoutOutputBuffer_returnsTryAgainLater() {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers an output format change. We progress the looper
|
||||
@ -136,7 +136,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void dequeueOutputBufferIndex_withOutputBuffer_returnsOutputBuffer() {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
@ -164,7 +164,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
public void dequeueOutputBufferIndex_withMediaCodecError_throwsException() throws Exception {
|
||||
// Pause the looper so that we interact with the adapter from this thread only.
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
adapter.start();
|
||||
|
||||
@ -177,7 +177,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers input buffer 0, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
@ -197,7 +197,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void getOutputFormat_withoutFormatReceived_throwsException() {
|
||||
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
|
||||
// the format change is not propagated to the adapter.
|
||||
shadowOf(callbackThread.getLooper()).pause();
|
||||
@ -209,7 +209,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void getOutputFormat_withMultipleFormats_returnsCorrectFormat() {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers an output format, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
@ -233,7 +233,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||
@Test
|
||||
public void getOutputFormat_afterFlush_returnsPreviousFormat() {
|
||||
adapter.configure(
|
||||
createMediaFormat("foo"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
createMediaFormat("format"), /* surface= */ null, /* crypto= */ null, /* flags= */ 0);
|
||||
adapter.start();
|
||||
// After start(), the ShadowMediaCodec offers an output format, which is available only if we
|
||||
// progress the adapter's looper.
|
||||
|
Loading…
x
Reference in New Issue
Block a user