Remove SynchronousMediaCodecBufferEnqueuer

Remove the SynchronousMediaCodecBufferEnqueuer interface
since we only keep the AsynchronousMediaCodecBufferEnqueuer
implementation.

PiperOrigin-RevId: 333701115
This commit is contained in:
christosts 2020-09-25 11:18:09 +01:00 committed by kim-vde
parent 7dfdde9246
commit 0066586499
7 changed files with 87 additions and 194 deletions

View File

@ -69,59 +69,33 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private long pendingFlushCount;
private @State int state;
private final MediaCodecInputBufferEnqueuer bufferEnqueuer;
private final AsynchronousMediaCodecBufferEnqueuer bufferEnqueuer;
@GuardedBy("lock")
@Nullable
private IllegalStateException internalException;
/**
* Creates an instance that wraps the specified {@link MediaCodec}. Instances created with this
* constructor will queue input buffers to the {@link MediaCodec} synchronously.
* 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.
*/
/* package */ AsynchronousMediaCodecAdapter(MediaCodec codec, int trackType) {
this(
codec,
/* enableAsynchronousQueueing= */ false,
trackType,
new HandlerThread(createThreadLabel(trackType)));
}
/**
* Creates an instance that wraps the specified {@link MediaCodec}.
*
* @param codec The {@link MediaCodec} to wrap.
* @param enableAsynchronousQueueing Whether input buffers will be queued asynchronously.
* @param trackType One of {@link C#TRACK_TYPE_AUDIO} or {@link C#TRACK_TYPE_VIDEO}. Used for
* labelling the internal thread accordingly.
*/
/* package */ AsynchronousMediaCodecAdapter(
MediaCodec codec, boolean enableAsynchronousQueueing, int trackType) {
this(
codec,
enableAsynchronousQueueing,
trackType,
new HandlerThread(createThreadLabel(trackType)));
this(codec, trackType, new HandlerThread(createThreadLabel(trackType)));
}
@VisibleForTesting
/* package */ AsynchronousMediaCodecAdapter(
MediaCodec codec,
boolean enableAsynchronousQueueing,
int trackType,
HandlerThread handlerThread) {
this.lock = new Object();
this.mediaCodecAsyncCallback = new MediaCodecAsyncCallback();
this.codec = codec;
this.handlerThread = handlerThread;
this.bufferEnqueuer =
enableAsynchronousQueueing
? new AsynchronousMediaCodecBufferEnqueuer(codec, trackType)
: new SynchronousMediaCodecBufferEnqueuer(this.codec);
this.bufferEnqueuer = new AsynchronousMediaCodecBufferEnqueuer(codec, trackType);
this.state = STATE_CREATED;
}

View File

@ -37,13 +37,13 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* A {@link MediaCodecInputBufferEnqueuer} that defers queueing operations on a background thread.
* Performs {@link MediaCodec} input buffer queueing on a background thread.
*
* <p>The implementation of this class assumes that its public methods will be called from the same
* thread.
*/
@RequiresApi(23)
class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnqueuer {
class AsynchronousMediaCodecBufferEnqueuer {
private static final int MSG_QUEUE_INPUT_BUFFER = 0;
private static final int MSG_QUEUE_SECURE_INPUT_BUFFER = 1;
@ -85,7 +85,11 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
needsSynchronizationWorkaround = needsSynchronizationWorkaround();
}
@Override
/**
* Starts this instance.
*
* <p>Call this method after creating an instance and before queueing input buffers.
*/
public void start() {
if (!started) {
handlerThread.start();
@ -100,7 +104,11 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
}
}
@Override
/**
* Submits an input buffer for decoding.
*
* @see android.media.MediaCodec#queueInputBuffer
*/
public void queueInputBuffer(
int index, int offset, int size, long presentationTimeUs, int flags) {
maybeThrowException();
@ -111,7 +119,15 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
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(
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags) {
maybeThrowException();
@ -123,7 +139,7 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
message.sendToTarget();
}
@Override
/** Flushes the instance. */
public void flush() {
if (started) {
try {
@ -137,7 +153,7 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
}
}
@Override
/** Shut down the instance. Make sure to call this method to release its internal resources. */
public void shutdown() {
if (started) {
flush();
@ -146,36 +162,8 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
started = false;
}
private void doHandleMessage(Message msg) {
MessageParams params = null;
switch (msg.what) {
case MSG_QUEUE_INPUT_BUFFER:
params = (MessageParams) msg.obj;
doQueueInputBuffer(
params.index, params.offset, params.size, params.presentationTimeUs, params.flags);
break;
case MSG_QUEUE_SECURE_INPUT_BUFFER:
params = (MessageParams) msg.obj;
doQueueSecureInputBuffer(
params.index,
params.offset,
params.cryptoInfo,
params.presentationTimeUs,
params.flags);
break;
case MSG_FLUSH:
conditionVariable.open();
break;
default:
setPendingRuntimeException(new IllegalStateException(String.valueOf(msg.what)));
}
if (params != null) {
recycleMessageParams(params);
}
}
private void maybeThrowException() {
RuntimeException exception = pendingRuntimeException.getAndSet(null);
@Nullable RuntimeException exception = pendingRuntimeException.getAndSet(null);
if (exception != null) {
throw exception;
}
@ -202,6 +190,34 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
pendingRuntimeException.set(exception);
}
private void doHandleMessage(Message msg) {
@Nullable MessageParams params = null;
switch (msg.what) {
case MSG_QUEUE_INPUT_BUFFER:
params = (MessageParams) msg.obj;
doQueueInputBuffer(
params.index, params.offset, params.size, params.presentationTimeUs, params.flags);
break;
case MSG_QUEUE_SECURE_INPUT_BUFFER:
params = (MessageParams) msg.obj;
doQueueSecureInputBuffer(
params.index,
params.offset,
params.cryptoInfo,
params.presentationTimeUs,
params.flags);
break;
case MSG_FLUSH:
conditionVariable.open();
break;
default:
setPendingRuntimeException(new IllegalStateException(String.valueOf(msg.what)));
}
if (params != null) {
recycleMessageParams(params);
}
}
private void doQueueInputBuffer(
int index, int offset, int size, long presentationTimeUs, int flag) {
try {
@ -226,13 +242,6 @@ class AsynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnque
}
}
@VisibleForTesting
/* package */ static int getInstancePoolSize() {
synchronized (MESSAGE_PARAMS_INSTANCE_POOL) {
return MESSAGE_PARAMS_INSTANCE_POOL.size();
}
}
private static MessageParams getMessageParams() {
synchronized (MESSAGE_PARAMS_INSTANCE_POOL) {
if (MESSAGE_PARAMS_INSTANCE_POOL.isEmpty()) {

View File

@ -1,56 +0,0 @@
/*
* Copyright (C) 2020 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 com.google.android.exoplayer2.mediacodec;
import android.media.MediaCodec;
import com.google.android.exoplayer2.decoder.CryptoInfo;
/** Abstracts operations to enqueue input buffer on a {@link android.media.MediaCodec}. */
interface MediaCodecInputBufferEnqueuer {
/**
* Starts this instance.
*
* <p>Call this method after creating an instance.
*/
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
* android.media.MediaCodec.CryptoInfo}.
*
* @see android.media.MediaCodec#queueSecureInputBuffer
*/
void queueSecureInputBuffer(
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
/** Flushes the instance. */
void flush();
/** Shut down the instance. Make sure to call this method to release its internal resources. */
void shutdown();
}

View File

@ -1056,13 +1056,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
TraceUtil.beginSection("createCodec:" + codecName);
codec = MediaCodec.createByCodecName(codecName);
if (enableAsynchronousBufferQueueing && Util.SDK_INT >= 23) {
codecAdapter =
new AsynchronousMediaCodecAdapter(
codec, /* enableAsynchronousQueueing= */ true, getTrackType());
codecAdapter = new AsynchronousMediaCodecAdapter(codec, getTrackType());
} else {
codecAdapter = new SynchronousMediaCodecAdapter(codec);
}
TraceUtil.endSection();
TraceUtil.beginSection("configureCodec");
configureCodec(codecInfo, codecAdapter, inputFormat, crypto, codecOperatingRate);

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) 2020 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 com.google.android.exoplayer2.mediacodec;
import android.media.MediaCodec;
import com.google.android.exoplayer2.decoder.CryptoInfo;
/**
* A {@link MediaCodecInputBufferEnqueuer} that forwards queueing methods directly to {@link
* MediaCodec}.
*/
class SynchronousMediaCodecBufferEnqueuer implements MediaCodecInputBufferEnqueuer {
private final MediaCodec codec;
/**
* Creates an instance that queues input buffers on the specified {@link MediaCodec}.
*
* @param codec The {@link MediaCodec} to submit input buffers to.
*/
SynchronousMediaCodecBufferEnqueuer(MediaCodec codec) {
this.codec = codec;
}
@Override
public void start() {}
@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 flush() {}
@Override
public void shutdown() {}
}

View File

@ -48,7 +48,6 @@ public class AsynchronousMediaCodecAdapterTest {
adapter =
new AsynchronousMediaCodecAdapter(
codec,
/* enableAsynchronousQueueing= */ false,
/* trackType= */ C.TRACK_TYPE_VIDEO,
handlerThread);
bufferInfo = new MediaCodec.BufferInfo();

View File

@ -19,6 +19,7 @@ package com.google.android.exoplayer2.mediacodec;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.doAnswer;
import static org.robolectric.Shadows.shadowOf;
import android.media.MediaCodec;
import android.media.MediaFormat;
@ -28,6 +29,7 @@ import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.decoder.CryptoInfo;
import com.google.android.exoplayer2.util.ConditionVariable;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.After;
import org.junit.Before;
@ -66,6 +68,33 @@ public class AsynchronousMediaCodecBufferEnqueuerTest {
assertThat(TestHandlerThread.INSTANCES_STARTED.get()).isEqualTo(0);
}
@Test
public void queueInputBuffer_queuesInputBufferOnMediaCodec() {
enqueuer.start();
int inputBufferIndex = codec.dequeueInputBuffer(0);
assertThat(inputBufferIndex).isAtLeast(0);
byte[] inputData = new byte[] {0, 1, 2, 3};
codec.getInputBuffer(inputBufferIndex).put(inputData);
enqueuer.queueInputBuffer(
inputBufferIndex,
/* offset= */ 0,
/* size= */ 4,
/* presentationTimeUs= */ 0,
/* flags= */ 0);
shadowOf(handlerThread.getLooper()).idle();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
assertThat(codec.dequeueOutputBuffer(bufferInfo, 0))
.isEqualTo(MediaCodec.INFO_OUTPUT_FORMAT_CHANGED);
assertThat(codec.dequeueOutputBuffer(bufferInfo, 0)).isEqualTo(inputBufferIndex);
ByteBuffer outputBuffer = codec.getOutputBuffer(inputBufferIndex);
assertThat(outputBuffer.limit()).isEqualTo(4);
byte[] outputData = new byte[4];
outputBuffer.get(outputData);
assertThat(outputData).isEqualTo(inputData);
}
@Test
public void queueInputBuffer_withPendingCryptoExceptionSet_throwsCryptoException() {
enqueuer.setPendingRuntimeException(
@ -111,7 +140,7 @@ public class AsynchronousMediaCodecBufferEnqueuerTest {
enqueuer.queueSecureInputBuffer(
/* index= */ 0,
/* offset= */ 0,
/* info= */ info,
info,
/* presentationTimeUs= */ 0,
/* flags= */ 0));
}