Remove MediaCodecAdapter dependency from Transformer.

Codec and its factories can use MediaCodec directly as for API >= 21,
the SynchronousMediaCodecAdapter methods used in Codec just correspond
to a single MediaCodec call each so there is no reason to have another
wrapping layer.

PiperOrigin-RevId: 421041177
This commit is contained in:
hschlueter 2022-01-11 16:42:12 +00:00 committed by Ian Baker
parent a93e8cc620
commit bf32ae50d7
2 changed files with 100 additions and 96 deletions

View file

@ -27,7 +27,6 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.mediacodec.MediaCodecAdapter;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer;
@ -35,11 +34,11 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* A wrapper around {@link MediaCodecAdapter}.
* A wrapper around {@link MediaCodec}.
*
* <p>Provides a layer of abstraction for callers that need to interact with {@link MediaCodec}
* through {@link MediaCodecAdapter}. This is done by simplifying the calls needed to queue and
* dequeue buffers, removing the need to track buffer indices and codec events.
* <p>Provides a layer of abstraction for callers that need to interact with {@link MediaCodec}.
* This is done by simplifying the calls needed to queue and dequeue buffers, removing the need to
* track buffer indices and codec events.
*/
public final class Codec {
@ -64,11 +63,12 @@ public final class Codec {
*
* @param format The {@link Format} (of the input data) used to determine the underlying {@link
* MediaCodec} and its configuration values.
* @param surface The {@link Surface} to which the decoder output is rendered.
* @param outputSurface The {@link Surface} to which the decoder output is rendered.
* @return A configured and started decoder wrapper.
* @throws TransformationException If the underlying codec cannot be created.
*/
Codec createForVideoDecoding(Format format, Surface surface) throws TransformationException;
Codec createForVideoDecoding(Format format, Surface outputSurface)
throws TransformationException;
}
/** A factory for {@link Codec encoder} instances. */
@ -106,7 +106,8 @@ public final class Codec {
private static final int MEDIA_CODEC_PCM_ENCODING = C.ENCODING_PCM_16BIT;
private final BufferInfo outputBufferInfo;
private final MediaCodecAdapter mediaCodecAdapter;
private final MediaCodec mediaCodec;
@Nullable private final Surface inputSurface;
private @MonotonicNonNull Format outputFormat;
@Nullable private ByteBuffer outputBuffer;
@ -116,9 +117,10 @@ public final class Codec {
private boolean inputStreamEnded;
private boolean outputStreamEnded;
/** Creates a {@code Codec} from a configured and started {@link MediaCodecAdapter}. */
public Codec(MediaCodecAdapter mediaCodecAdapter) {
this.mediaCodecAdapter = mediaCodecAdapter;
/** Creates a {@code Codec} from a configured and started {@link MediaCodec}. */
public Codec(MediaCodec mediaCodec, @Nullable Surface inputSurface) {
this.mediaCodec = mediaCodec;
this.inputSurface = inputSurface;
outputBufferInfo = new BufferInfo();
inputBufferIndex = C.INDEX_UNSET;
outputBufferIndex = C.INDEX_UNSET;
@ -127,7 +129,7 @@ public final class Codec {
/** Returns the input {@link Surface}, or null if the input is not a surface. */
@Nullable
public Surface getInputSurface() {
return mediaCodecAdapter.getInputSurface();
return inputSurface;
}
/**
@ -142,11 +144,11 @@ public final class Codec {
return false;
}
if (inputBufferIndex < 0) {
inputBufferIndex = mediaCodecAdapter.dequeueInputBufferIndex();
inputBufferIndex = mediaCodec.dequeueInputBuffer(/* timeoutUs= */ 0);
if (inputBufferIndex < 0) {
return false;
}
inputBuffer.data = mediaCodecAdapter.getInputBuffer(inputBufferIndex);
inputBuffer.data = mediaCodec.getInputBuffer(inputBufferIndex);
inputBuffer.clear();
}
checkNotNull(inputBuffer.data);
@ -172,13 +174,13 @@ public final class Codec {
inputStreamEnded = true;
flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
}
mediaCodecAdapter.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags);
mediaCodec.queueInputBuffer(inputBufferIndex, offset, size, inputBuffer.timeUs, flags);
inputBufferIndex = C.INDEX_UNSET;
inputBuffer.data = null;
}
public void signalEndOfInputStream() {
mediaCodecAdapter.signalEndOfInputStream();
mediaCodec.signalEndOfInputStream();
}
/** Returns the current output format, if available. */
@ -222,7 +224,7 @@ public final class Codec {
*/
public void releaseOutputBuffer(boolean render) {
outputBuffer = null;
mediaCodecAdapter.releaseOutputBuffer(outputBufferIndex, render);
mediaCodec.releaseOutputBuffer(outputBufferIndex, render);
outputBufferIndex = C.INDEX_UNSET;
}
@ -234,7 +236,10 @@ public final class Codec {
/** Releases the underlying codec. */
public void release() {
outputBuffer = null;
mediaCodecAdapter.release();
if (inputSurface != null) {
inputSurface.release();
}
mediaCodec.release();
}
/**
@ -247,7 +252,7 @@ public final class Codec {
return false;
}
outputBuffer = checkNotNull(mediaCodecAdapter.getOutputBuffer(outputBufferIndex));
outputBuffer = checkNotNull(mediaCodec.getOutputBuffer(outputBufferIndex));
outputBuffer.position(outputBufferInfo.offset);
outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size);
return true;
@ -265,10 +270,10 @@ public final class Codec {
return false;
}
outputBufferIndex = mediaCodecAdapter.dequeueOutputBufferIndex(outputBufferInfo);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(outputBufferInfo, /* timeoutUs= */ 0);
if (outputBufferIndex < 0) {
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
outputFormat = getFormat(mediaCodecAdapter.getOutputFormat());
outputFormat = getFormat(mediaCodec.getOutputFormat());
}
return false;
}

View file

@ -25,29 +25,17 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaFormat;
import android.view.Surface;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.mediacodec.MediaCodecAdapter;
import com.google.android.exoplayer2.mediacodec.MediaCodecInfo;
import com.google.android.exoplayer2.mediacodec.SynchronousMediaCodecAdapter;
import com.google.android.exoplayer2.util.MediaFormatUtil;
import com.google.android.exoplayer2.util.TraceUtil;
import java.io.IOException;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** A default {@link Codec.DecoderFactory} and {@link Codec.EncoderFactory}. */
/* package */ final class DefaultCodecFactory
implements Codec.DecoderFactory, Codec.EncoderFactory {
private static final MediaCodecInfo PLACEHOLDER_MEDIA_CODEC_INFO =
MediaCodecInfo.newInstance(
/* name= */ "name-placeholder",
/* mimeType= */ "mime-type-placeholder",
/* codecMimeType= */ "mime-type-placeholder",
/* capabilities= */ null,
/* hardwareAccelerated= */ false,
/* softwareOnly= */ false,
/* vendor= */ false,
/* forceDisableAdaptive= */ false,
/* forceSecure= */ false);
@Override
public Codec createForAudioDecoding(Format format) throws TransformationException {
MediaFormat mediaFormat =
@ -57,22 +45,17 @@ import java.io.IOException;
mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
MediaCodecAdapter adapter;
try {
adapter =
new MediaCodecFactory()
.createAdapter(
MediaCodecAdapter.Configuration.createForAudioDecoding(
PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format, /* crypto= */ null));
} catch (Exception e) {
throw createTransformationException(e, format, /* isVideo= */ false, /* isDecoder= */ true);
}
return new Codec(adapter);
return createCodec(
format,
mediaFormat,
/* isVideo= */ false,
/* isDecoder= */ true,
/* outputSurface= */ null);
}
@Override
@SuppressLint("InlinedApi")
public Codec createForVideoDecoding(Format format, Surface surface)
public Codec createForVideoDecoding(Format format, Surface outputSurface)
throws TransformationException {
MediaFormat mediaFormat =
MediaFormat.createVideoFormat(
@ -87,21 +70,8 @@ import java.io.IOException;
mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0);
}
MediaCodecAdapter adapter;
try {
adapter =
new MediaCodecFactory()
.createAdapter(
MediaCodecAdapter.Configuration.createForVideoDecoding(
PLACEHOLDER_MEDIA_CODEC_INFO,
mediaFormat,
format,
surface,
/* crypto= */ null));
} catch (Exception e) {
throw createTransformationException(e, format, /* isVideo= */ true, /* isDecoder= */ true);
}
return new Codec(adapter);
return createCodec(
format, mediaFormat, /* isVideo= */ true, /* isDecoder= */ true, outputSurface);
}
@Override
@ -111,17 +81,12 @@ import java.io.IOException;
checkNotNull(format.sampleMimeType), format.sampleRate, format.channelCount);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
MediaCodecAdapter adapter;
try {
adapter =
new MediaCodecFactory()
.createAdapter(
MediaCodecAdapter.Configuration.createForAudioEncoding(
PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format));
} catch (Exception e) {
throw createTransformationException(e, format, /* isVideo= */ false, /* isDecoder= */ false);
}
return new Codec(adapter);
return createCodec(
format,
mediaFormat,
/* isVideo= */ false,
/* isDecoder= */ false,
/* outputSurface= */ null);
}
@Override
@ -141,36 +106,70 @@ import java.io.IOException;
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 413_000);
MediaCodecAdapter adapter;
try {
adapter =
new MediaCodecFactory()
.createAdapter(
MediaCodecAdapter.Configuration.createForVideoEncoding(
PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format));
} catch (Exception e) {
throw createTransformationException(e, format, /* isVideo= */ true, /* isDecoder= */ false);
}
return new Codec(adapter);
return createCodec(
format,
mediaFormat,
/* isVideo= */ true,
/* isDecoder= */ false,
/* outputSurface= */ null);
}
private static final class MediaCodecFactory extends SynchronousMediaCodecAdapter.Factory {
@Override
protected MediaCodec createCodec(MediaCodecAdapter.Configuration configuration)
throws IOException {
String sampleMimeType =
checkNotNull(configuration.mediaFormat.getString(MediaFormat.KEY_MIME));
boolean isDecoder = (configuration.flags & MediaCodec.CONFIGURE_FLAG_ENCODE) == 0;
return isDecoder
? MediaCodec.createDecoderByType(checkNotNull(sampleMimeType))
: MediaCodec.createEncoderByType(checkNotNull(sampleMimeType));
@RequiresNonNull("#1.sampleMimeType")
private static Codec createCodec(
Format format,
MediaFormat mediaFormat,
boolean isVideo,
boolean isDecoder,
@Nullable Surface outputSurface)
throws TransformationException {
@Nullable MediaCodec mediaCodec = null;
@Nullable Surface inputSurface = null;
try {
mediaCodec =
isDecoder
? MediaCodec.createDecoderByType(format.sampleMimeType)
: MediaCodec.createEncoderByType(format.sampleMimeType);
configureCodec(mediaCodec, mediaFormat, isDecoder, outputSurface);
if (isVideo && !isDecoder) {
inputSurface = mediaCodec.createInputSurface();
}
startCodec(mediaCodec);
} catch (Exception e) {
if (inputSurface != null) {
inputSurface.release();
}
if (mediaCodec != null) {
mediaCodec.release();
}
throw createTransformationException(e, format, isVideo, isDecoder);
}
return new Codec(mediaCodec, inputSurface);
}
private static void configureCodec(
MediaCodec codec,
MediaFormat mediaFormat,
boolean isDecoder,
@Nullable Surface outputSurface) {
TraceUtil.beginSection("configureCodec");
codec.configure(
mediaFormat,
outputSurface,
/* crypto= */ null,
isDecoder ? 0 : MediaCodec.CONFIGURE_FLAG_ENCODE);
TraceUtil.endSection();
}
private static void startCodec(MediaCodec codec) {
TraceUtil.beginSection("startCodec");
codec.start();
TraceUtil.endSection();
}
private static TransformationException createTransformationException(
Exception cause, Format format, boolean isVideo, boolean isDecoder) {
String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder");
if (cause instanceof IOException) {
if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) {
return TransformationException.createForCodec(
cause,
componentName,