Add more MediaCodec methods to MediaCodecAdapter

Add more MediaCodec methods to MediaCodedAdapter so that renderers
interact with the MediaCodec through the MediaCodecAdapter.

PiperOrigin-RevId: 341023452
This commit is contained in:
christosts 2020-11-06 12:02:01 +00:00 committed by Andrew Lewis
parent ae4cf9f1da
commit 92ec1ab628
5 changed files with 158 additions and 21 deletions

View file

@ -19,6 +19,8 @@ package com.google.android.exoplayer2.mediacodec;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.view.Surface;
import androidx.annotation.IntDef;
@ -26,6 +28,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Renderer.VideoScalingMode;
import com.google.android.exoplayer2.decoder.CryptoInfo;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@ -108,6 +111,16 @@ import java.nio.ByteBuffer;
bufferEnqueuer.queueSecureInputBuffer(index, offset, info, presentationTimeUs, flags);
}
@Override
public void releaseOutputBuffer(int index, boolean render) {
codec.releaseOutputBuffer(index, render);
}
@Override
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
codec.releaseOutputBuffer(index, renderTimeStampNs);
}
@Override
public int dequeueInputBufferIndex() {
return asynchronousMediaCodecCallback.dequeueInputBufferIndex();
@ -148,14 +161,14 @@ import java.nio.ByteBuffer;
}
@Override
public void shutdown() {
if (state == STATE_STARTED) {
bufferEnqueuer.shutdown();
}
if (state == STATE_CONFIGURED || state == STATE_STARTED) {
public void release() {
if (state == STATE_STARTED) {
bufferEnqueuer.shutdown();
}
if (state == STATE_CONFIGURED || state == STATE_STARTED) {
asynchronousMediaCodecCallback.shutdown();
}
state = STATE_SHUT_DOWN;
}
state = STATE_SHUT_DOWN;
}
@Override
@ -163,6 +176,30 @@ import java.nio.ByteBuffer;
return codec;
}
@Override
public void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler) {
codec.setOnFrameRenderedListener(
(codec, presentationTimeUs, nanoTime) ->
listener.onFrameRendered(
AsynchronousMediaCodecAdapter.this, presentationTimeUs, nanoTime),
handler);
}
@Override
public void setOutputSurface(Surface surface) {
codec.setOutputSurface(surface);
}
@Override
public void setParameters(Bundle params) {
codec.setParameters(params);
}
@Override
public void setVideoScalingMode(@VideoScalingMode int scalingMode) {
codec.setVideoScalingMode(scalingMode);
}
@VisibleForTesting
/* package */ void onError(MediaCodec.CodecException error) {
asynchronousMediaCodecCallback.onError(codec, error);

View file

@ -19,8 +19,12 @@ package com.google.android.exoplayer2.mediacodec;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Handler;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.Renderer.VideoScalingMode;
import com.google.android.exoplayer2.decoder.CryptoInfo;
import java.nio.ByteBuffer;
@ -32,6 +36,15 @@ import java.nio.ByteBuffer;
*/
public interface MediaCodecAdapter {
/**
* Listener to be called when an output frame has rendered on the output surface.
*
* @see MediaCodec.OnFrameRenderedListener
*/
interface OnFrameRenderedListener {
void onFrameRendered(MediaCodecAdapter codec, long presentationTimeUs, long nanoTime);
}
/**
* Configures this adapter and the underlying {@link MediaCodec}. Needs to be called before {@link
* #start()}.
@ -118,18 +131,64 @@ public interface MediaCodecAdapter {
void queueSecureInputBuffer(
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
/** Flushes both the adapter and the underlying {@link MediaCodec}. */
void flush();
/**
* Returns the buffer to the {@link MediaCodec}. If the {@link MediaCodec} was configured with an
* output surface, setting {@code render} to {@code true} will first send the buffer to the output
* surface. The surface will release the buffer back to the codec once it is no longer
* used/displayed.
*
* @see MediaCodec#releaseOutputBuffer(int, boolean)
*/
void releaseOutputBuffer(int index, boolean render);
/**
* Shuts down the adapter.
* Updates the output buffer's surface timestamp and sends it to the {@link MediaCodec} to render
* it on the output surface. If the {@link MediaCodec} is not configured with an output surface,
* this call will simply return the buffer to the {@link MediaCodec}.
*
* <p>This method does not stop or release the underlying {@link MediaCodec}. It should be called
* before stopping or releasing the {@link MediaCodec} to avoid the possibility of the adapter
* interacting with a stopped or released {@link MediaCodec}.
* @see MediaCodec#releaseOutputBuffer(int, long)
*/
void shutdown();
@RequiresApi(21)
void releaseOutputBuffer(int index, long renderTimeStampNs);
/** Flushes the adapter and the underlying {@link MediaCodec}. */
void flush();
/** Releases the adapter and the underlying {@link MediaCodec}. */
void release();
/** Returns the {@link MediaCodec} instance of this adapter. */
MediaCodec getCodec();
/**
* Registers a callback to be invoked when an output frame is rendered on the output surface.
*
* @see MediaCodec#setOnFrameRenderedListener
*/
@RequiresApi(23)
void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler);
/**
* Dynamically sets the output surface of a {@link MediaCodec}.
*
* @see MediaCodec#setOutputSurface(Surface)
*/
@RequiresApi(23)
void setOutputSurface(Surface surface);
/**
* Communicate additional parameter changes to the {@link MediaCodec} instance.
*
* @see MediaCodec#setParameters(Bundle)
*/
@RequiresApi(19)
void setParameters(Bundle params);
/**
* Specifies the scaling mode to use, if a surface has been specified in a previous call to {@link
* #configure}.
*
* @see MediaCodec#setVideoScalingMode(int)
*/
void setVideoScalingMode(@VideoScalingMode int scalingMode);
}

View file

@ -719,7 +719,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
protected void releaseCodec() {
try {
if (codecAdapter != null) {
codecAdapter.shutdown();
codecAdapter.release();
}
if (codec != null) {
decoderCounters.decoderReleaseCount++;
@ -1071,7 +1071,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecInitializedTimestamp = SystemClock.elapsedRealtime();
} catch (Exception e) {
if (codecAdapter != null) {
codecAdapter.shutdown();
codecAdapter.release();
}
if (codec != null) {
codec.release();

View file

@ -21,8 +21,12 @@ import static com.google.android.exoplayer2.util.Util.castNonNull;
import android.media.MediaCodec;
import android.media.MediaCrypto;
import android.media.MediaFormat;
import android.os.Bundle;
import android.os.Handler;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.google.android.exoplayer2.Renderer.VideoScalingMode;
import com.google.android.exoplayer2.decoder.CryptoInfo;
import com.google.android.exoplayer2.util.Util;
import java.nio.ByteBuffer;
@ -114,13 +118,24 @@ import java.nio.ByteBuffer;
index, offset, info.getFrameworkCryptoInfo(), presentationTimeUs, flags);
}
@Override
public void releaseOutputBuffer(int index, boolean render) {
codec.releaseOutputBuffer(index, render);
}
@Override
@RequiresApi(21)
public void releaseOutputBuffer(int index, long renderTimeStampNs) {
codec.releaseOutputBuffer(index, renderTimeStampNs);
}
@Override
public void flush() {
codec.flush();
}
@Override
public void shutdown() {
public void release() {
inputByteBuffers = null;
outputByteBuffers = null;
}
@ -129,4 +144,31 @@ import java.nio.ByteBuffer;
public MediaCodec getCodec() {
return codec;
}
@Override
@RequiresApi(23)
public void setOnFrameRenderedListener(OnFrameRenderedListener listener, Handler handler) {
codec.setOnFrameRenderedListener(
(codec, presentationTimeUs, nanoTime) ->
listener.onFrameRendered(
SynchronousMediaCodecAdapter.this, presentationTimeUs, nanoTime),
handler);
}
@Override
@RequiresApi(23)
public void setOutputSurface(Surface surface) {
codec.setOutputSurface(surface);
}
@Override
@RequiresApi(19)
public void setParameters(Bundle params) {
codec.setParameters(params);
}
@Override
public void setVideoScalingMode(@VideoScalingMode int scalingMode) {
codec.setVideoScalingMode(scalingMode);
}
}

View file

@ -52,8 +52,7 @@ public class AsynchronousMediaCodecAdapterTest {
@After
public void tearDown() {
adapter.shutdown();
codec.release();
adapter.release();
}
@Test
@ -106,7 +105,7 @@ public class AsynchronousMediaCodecAdapterTest {
// non-empty adapter.
shadowOf(callbackThread.getLooper()).idle();
adapter.shutdown();
adapter.release();
assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
}
@ -183,7 +182,7 @@ public class AsynchronousMediaCodecAdapterTest {
adapter.queueInputBuffer(index, 0, 0, 0, 0);
// Progress the looper so that the ShadowMediaCodec processes the input buffer.
shadowLooper.idle();
adapter.shutdown();
adapter.release();
assertThat(adapter.dequeueOutputBufferIndex(bufferInfo))
.isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);