mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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:
parent
ae4cf9f1da
commit
92ec1ab628
5 changed files with 158 additions and 21 deletions
|
|
@ -19,6 +19,8 @@ package com.google.android.exoplayer2.mediacodec;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
|
|
@ -26,6 +28,7 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
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.Renderer.VideoScalingMode;
|
||||||
import com.google.android.exoplayer2.decoder.CryptoInfo;
|
import com.google.android.exoplayer2.decoder.CryptoInfo;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
|
@ -108,6 +111,16 @@ import java.nio.ByteBuffer;
|
||||||
bufferEnqueuer.queueSecureInputBuffer(index, offset, info, presentationTimeUs, flags);
|
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
|
@Override
|
||||||
public int dequeueInputBufferIndex() {
|
public int dequeueInputBufferIndex() {
|
||||||
return asynchronousMediaCodecCallback.dequeueInputBufferIndex();
|
return asynchronousMediaCodecCallback.dequeueInputBufferIndex();
|
||||||
|
|
@ -148,14 +161,14 @@ import java.nio.ByteBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void release() {
|
||||||
if (state == STATE_STARTED) {
|
if (state == STATE_STARTED) {
|
||||||
bufferEnqueuer.shutdown();
|
bufferEnqueuer.shutdown();
|
||||||
}
|
}
|
||||||
if (state == STATE_CONFIGURED || state == STATE_STARTED) {
|
if (state == STATE_CONFIGURED || state == STATE_STARTED) {
|
||||||
asynchronousMediaCodecCallback.shutdown();
|
asynchronousMediaCodecCallback.shutdown();
|
||||||
}
|
}
|
||||||
state = STATE_SHUT_DOWN;
|
state = STATE_SHUT_DOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -163,6 +176,30 @@ import java.nio.ByteBuffer;
|
||||||
return codec;
|
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
|
@VisibleForTesting
|
||||||
/* package */ void onError(MediaCodec.CodecException error) {
|
/* package */ void onError(MediaCodec.CodecException error) {
|
||||||
asynchronousMediaCodecCallback.onError(codec, error);
|
asynchronousMediaCodecCallback.onError(codec, error);
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,12 @@ package com.google.android.exoplayer2.mediacodec;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
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.decoder.CryptoInfo;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
|
@ -32,6 +36,15 @@ import java.nio.ByteBuffer;
|
||||||
*/
|
*/
|
||||||
public interface MediaCodecAdapter {
|
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
|
* Configures this adapter and the underlying {@link MediaCodec}. Needs to be called before {@link
|
||||||
* #start()}.
|
* #start()}.
|
||||||
|
|
@ -118,18 +131,64 @@ public interface MediaCodecAdapter {
|
||||||
void queueSecureInputBuffer(
|
void queueSecureInputBuffer(
|
||||||
int index, int offset, CryptoInfo info, long presentationTimeUs, int flags);
|
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
|
* @see MediaCodec#releaseOutputBuffer(int, long)
|
||||||
* before stopping or releasing the {@link MediaCodec} to avoid the possibility of the adapter
|
|
||||||
* interacting with a stopped or released {@link MediaCodec}.
|
|
||||||
*/
|
*/
|
||||||
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. */
|
/** Returns the {@link MediaCodec} instance of this adapter. */
|
||||||
MediaCodec getCodec();
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -719,7 +719,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
protected void releaseCodec() {
|
protected void releaseCodec() {
|
||||||
try {
|
try {
|
||||||
if (codecAdapter != null) {
|
if (codecAdapter != null) {
|
||||||
codecAdapter.shutdown();
|
codecAdapter.release();
|
||||||
}
|
}
|
||||||
if (codec != null) {
|
if (codec != null) {
|
||||||
decoderCounters.decoderReleaseCount++;
|
decoderCounters.decoderReleaseCount++;
|
||||||
|
|
@ -1071,7 +1071,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
|
||||||
codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
codecInitializedTimestamp = SystemClock.elapsedRealtime();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (codecAdapter != null) {
|
if (codecAdapter != null) {
|
||||||
codecAdapter.shutdown();
|
codecAdapter.release();
|
||||||
}
|
}
|
||||||
if (codec != null) {
|
if (codec != null) {
|
||||||
codec.release();
|
codec.release();
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,12 @@ import static com.google.android.exoplayer2.util.Util.castNonNull;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
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.decoder.CryptoInfo;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
@ -114,13 +118,24 @@ import java.nio.ByteBuffer;
|
||||||
index, offset, info.getFrameworkCryptoInfo(), presentationTimeUs, flags);
|
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
|
@Override
|
||||||
public void flush() {
|
public void flush() {
|
||||||
codec.flush();
|
codec.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void release() {
|
||||||
inputByteBuffers = null;
|
inputByteBuffers = null;
|
||||||
outputByteBuffers = null;
|
outputByteBuffers = null;
|
||||||
}
|
}
|
||||||
|
|
@ -129,4 +144,31 @@ import java.nio.ByteBuffer;
|
||||||
public MediaCodec getCodec() {
|
public MediaCodec getCodec() {
|
||||||
return codec;
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
adapter.shutdown();
|
adapter.release();
|
||||||
codec.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -106,7 +105,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||||
// non-empty adapter.
|
// non-empty adapter.
|
||||||
shadowOf(callbackThread.getLooper()).idle();
|
shadowOf(callbackThread.getLooper()).idle();
|
||||||
|
|
||||||
adapter.shutdown();
|
adapter.release();
|
||||||
|
|
||||||
assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
assertThat(adapter.dequeueInputBufferIndex()).isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
||||||
}
|
}
|
||||||
|
|
@ -183,7 +182,7 @@ public class AsynchronousMediaCodecAdapterTest {
|
||||||
adapter.queueInputBuffer(index, 0, 0, 0, 0);
|
adapter.queueInputBuffer(index, 0, 0, 0, 0);
|
||||||
// Progress the looper so that the ShadowMediaCodec processes the input buffer.
|
// Progress the looper so that the ShadowMediaCodec processes the input buffer.
|
||||||
shadowLooper.idle();
|
shadowLooper.idle();
|
||||||
adapter.shutdown();
|
adapter.release();
|
||||||
|
|
||||||
assertThat(adapter.dequeueOutputBufferIndex(bufferInfo))
|
assertThat(adapter.dequeueOutputBufferIndex(bufferInfo))
|
||||||
.isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
.isEqualTo(MediaCodec.INFO_TRY_AGAIN_LATER);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue