Commit bf32ae50 by hschlueter Committed by Ian Baker

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
parent a93e8cc6
...@@ -27,7 +27,6 @@ import androidx.annotation.Nullable; ...@@ -27,7 +27,6 @@ import androidx.annotation.Nullable;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.decoder.DecoderInputBuffer; import com.google.android.exoplayer2.decoder.DecoderInputBuffer;
import com.google.android.exoplayer2.mediacodec.MediaCodecAdapter;
import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.MimeTypes;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
...@@ -35,11 +34,11 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf; ...@@ -35,11 +34,11 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; 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} * <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 * This is done by simplifying the calls needed to queue and dequeue buffers, removing the need to
* dequeue buffers, removing the need to track buffer indices and codec events. * track buffer indices and codec events.
*/ */
public final class Codec { public final class Codec {
...@@ -64,11 +63,12 @@ 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 * @param format The {@link Format} (of the input data) used to determine the underlying {@link
* MediaCodec} and its configuration values. * 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. * @return A configured and started decoder wrapper.
* @throws TransformationException If the underlying codec cannot be created. * @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. */ /** A factory for {@link Codec encoder} instances. */
...@@ -106,7 +106,8 @@ public final class Codec { ...@@ -106,7 +106,8 @@ public final class Codec {
private static final int MEDIA_CODEC_PCM_ENCODING = C.ENCODING_PCM_16BIT; private static final int MEDIA_CODEC_PCM_ENCODING = C.ENCODING_PCM_16BIT;
private final BufferInfo outputBufferInfo; private final BufferInfo outputBufferInfo;
private final MediaCodecAdapter mediaCodecAdapter; private final MediaCodec mediaCodec;
@Nullable private final Surface inputSurface;
private @MonotonicNonNull Format outputFormat; private @MonotonicNonNull Format outputFormat;
@Nullable private ByteBuffer outputBuffer; @Nullable private ByteBuffer outputBuffer;
...@@ -116,9 +117,10 @@ public final class Codec { ...@@ -116,9 +117,10 @@ public final class Codec {
private boolean inputStreamEnded; private boolean inputStreamEnded;
private boolean outputStreamEnded; private boolean outputStreamEnded;
/** Creates a {@code Codec} from a configured and started {@link MediaCodecAdapter}. */ /** Creates a {@code Codec} from a configured and started {@link MediaCodec}. */
public Codec(MediaCodecAdapter mediaCodecAdapter) { public Codec(MediaCodec mediaCodec, @Nullable Surface inputSurface) {
this.mediaCodecAdapter = mediaCodecAdapter; this.mediaCodec = mediaCodec;
this.inputSurface = inputSurface;
outputBufferInfo = new BufferInfo(); outputBufferInfo = new BufferInfo();
inputBufferIndex = C.INDEX_UNSET; inputBufferIndex = C.INDEX_UNSET;
outputBufferIndex = C.INDEX_UNSET; outputBufferIndex = C.INDEX_UNSET;
...@@ -127,7 +129,7 @@ public final class Codec { ...@@ -127,7 +129,7 @@ public final class Codec {
/** Returns the input {@link Surface}, or null if the input is not a surface. */ /** Returns the input {@link Surface}, or null if the input is not a surface. */
@Nullable @Nullable
public Surface getInputSurface() { public Surface getInputSurface() {
return mediaCodecAdapter.getInputSurface(); return inputSurface;
} }
/** /**
...@@ -142,11 +144,11 @@ public final class Codec { ...@@ -142,11 +144,11 @@ public final class Codec {
return false; return false;
} }
if (inputBufferIndex < 0) { if (inputBufferIndex < 0) {
inputBufferIndex = mediaCodecAdapter.dequeueInputBufferIndex(); inputBufferIndex = mediaCodec.dequeueInputBuffer(/* timeoutUs= */ 0);
if (inputBufferIndex < 0) { if (inputBufferIndex < 0) {
return false; return false;
} }
inputBuffer.data = mediaCodecAdapter.getInputBuffer(inputBufferIndex); inputBuffer.data = mediaCodec.getInputBuffer(inputBufferIndex);
inputBuffer.clear(); inputBuffer.clear();
} }
checkNotNull(inputBuffer.data); checkNotNull(inputBuffer.data);
...@@ -172,13 +174,13 @@ public final class Codec { ...@@ -172,13 +174,13 @@ public final class Codec {
inputStreamEnded = true; inputStreamEnded = true;
flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM; 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; inputBufferIndex = C.INDEX_UNSET;
inputBuffer.data = null; inputBuffer.data = null;
} }
public void signalEndOfInputStream() { public void signalEndOfInputStream() {
mediaCodecAdapter.signalEndOfInputStream(); mediaCodec.signalEndOfInputStream();
} }
/** Returns the current output format, if available. */ /** Returns the current output format, if available. */
...@@ -222,7 +224,7 @@ public final class Codec { ...@@ -222,7 +224,7 @@ public final class Codec {
*/ */
public void releaseOutputBuffer(boolean render) { public void releaseOutputBuffer(boolean render) {
outputBuffer = null; outputBuffer = null;
mediaCodecAdapter.releaseOutputBuffer(outputBufferIndex, render); mediaCodec.releaseOutputBuffer(outputBufferIndex, render);
outputBufferIndex = C.INDEX_UNSET; outputBufferIndex = C.INDEX_UNSET;
} }
...@@ -234,7 +236,10 @@ public final class Codec { ...@@ -234,7 +236,10 @@ public final class Codec {
/** Releases the underlying codec. */ /** Releases the underlying codec. */
public void release() { public void release() {
outputBuffer = null; outputBuffer = null;
mediaCodecAdapter.release(); if (inputSurface != null) {
inputSurface.release();
}
mediaCodec.release();
} }
/** /**
...@@ -247,7 +252,7 @@ public final class Codec { ...@@ -247,7 +252,7 @@ public final class Codec {
return false; return false;
} }
outputBuffer = checkNotNull(mediaCodecAdapter.getOutputBuffer(outputBufferIndex)); outputBuffer = checkNotNull(mediaCodec.getOutputBuffer(outputBufferIndex));
outputBuffer.position(outputBufferInfo.offset); outputBuffer.position(outputBufferInfo.offset);
outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size); outputBuffer.limit(outputBufferInfo.offset + outputBufferInfo.size);
return true; return true;
...@@ -265,10 +270,10 @@ public final class Codec { ...@@ -265,10 +270,10 @@ public final class Codec {
return false; return false;
} }
outputBufferIndex = mediaCodecAdapter.dequeueOutputBufferIndex(outputBufferInfo); outputBufferIndex = mediaCodec.dequeueOutputBuffer(outputBufferInfo, /* timeoutUs= */ 0);
if (outputBufferIndex < 0) { if (outputBufferIndex < 0) {
if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
outputFormat = getFormat(mediaCodecAdapter.getOutputFormat()); outputFormat = getFormat(mediaCodec.getOutputFormat());
} }
return false; return false;
} }
......
...@@ -25,29 +25,17 @@ import android.media.MediaCodec; ...@@ -25,29 +25,17 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaFormat; import android.media.MediaFormat;
import android.view.Surface; import android.view.Surface;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.Format; 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.MediaFormatUtil;
import com.google.android.exoplayer2.util.TraceUtil;
import java.io.IOException; import java.io.IOException;
import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** A default {@link Codec.DecoderFactory} and {@link Codec.EncoderFactory}. */ /** A default {@link Codec.DecoderFactory} and {@link Codec.EncoderFactory}. */
/* package */ final class DefaultCodecFactory /* package */ final class DefaultCodecFactory
implements Codec.DecoderFactory, Codec.EncoderFactory { 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 @Override
public Codec createForAudioDecoding(Format format) throws TransformationException { public Codec createForAudioDecoding(Format format) throws TransformationException {
MediaFormat mediaFormat = MediaFormat mediaFormat =
...@@ -57,22 +45,17 @@ import java.io.IOException; ...@@ -57,22 +45,17 @@ import java.io.IOException;
mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize); mediaFormat, MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData); MediaFormatUtil.setCsdBuffers(mediaFormat, format.initializationData);
MediaCodecAdapter adapter; return createCodec(
try { format,
adapter = mediaFormat,
new MediaCodecFactory() /* isVideo= */ false,
.createAdapter( /* isDecoder= */ true,
MediaCodecAdapter.Configuration.createForAudioDecoding( /* outputSurface= */ null);
PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format, /* crypto= */ null));
} catch (Exception e) {
throw createTransformationException(e, format, /* isVideo= */ false, /* isDecoder= */ true);
}
return new Codec(adapter);
} }
@Override @Override
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
public Codec createForVideoDecoding(Format format, Surface surface) public Codec createForVideoDecoding(Format format, Surface outputSurface)
throws TransformationException { throws TransformationException {
MediaFormat mediaFormat = MediaFormat mediaFormat =
MediaFormat.createVideoFormat( MediaFormat.createVideoFormat(
...@@ -87,21 +70,8 @@ import java.io.IOException; ...@@ -87,21 +70,8 @@ import java.io.IOException;
mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0); mediaFormat.setInteger(MediaFormat.KEY_ALLOW_FRAME_DROP, 0);
} }
MediaCodecAdapter adapter; return createCodec(
try { format, mediaFormat, /* isVideo= */ true, /* isDecoder= */ true, outputSurface);
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);
} }
@Override @Override
...@@ -111,17 +81,12 @@ import java.io.IOException; ...@@ -111,17 +81,12 @@ import java.io.IOException;
checkNotNull(format.sampleMimeType), format.sampleRate, format.channelCount); checkNotNull(format.sampleMimeType), format.sampleRate, format.channelCount);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
MediaCodecAdapter adapter; return createCodec(
try { format,
adapter = mediaFormat,
new MediaCodecFactory() /* isVideo= */ false,
.createAdapter( /* isDecoder= */ false,
MediaCodecAdapter.Configuration.createForAudioEncoding( /* outputSurface= */ null);
PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format));
} catch (Exception e) {
throw createTransformationException(e, format, /* isVideo= */ false, /* isDecoder= */ false);
}
return new Codec(adapter);
} }
@Override @Override
...@@ -141,36 +106,70 @@ import java.io.IOException; ...@@ -141,36 +106,70 @@ import java.io.IOException;
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 413_000); mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 413_000);
MediaCodecAdapter adapter; return createCodec(
format,
mediaFormat,
/* isVideo= */ true,
/* isDecoder= */ false,
/* outputSurface= */ null);
}
@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 { try {
adapter = mediaCodec =
new MediaCodecFactory() isDecoder
.createAdapter( ? MediaCodec.createDecoderByType(format.sampleMimeType)
MediaCodecAdapter.Configuration.createForVideoEncoding( : MediaCodec.createEncoderByType(format.sampleMimeType);
PLACEHOLDER_MEDIA_CODEC_INFO, mediaFormat, format)); configureCodec(mediaCodec, mediaFormat, isDecoder, outputSurface);
if (isVideo && !isDecoder) {
inputSurface = mediaCodec.createInputSurface();
}
startCodec(mediaCodec);
} catch (Exception e) { } catch (Exception e) {
throw createTransformationException(e, format, /* isVideo= */ true, /* isDecoder= */ false); if (inputSurface != null) {
inputSurface.release();
}
if (mediaCodec != null) {
mediaCodec.release();
}
throw createTransformationException(e, format, isVideo, isDecoder);
} }
return new Codec(adapter); return new Codec(mediaCodec, inputSurface);
} }
private static final class MediaCodecFactory extends SynchronousMediaCodecAdapter.Factory { private static void configureCodec(
@Override MediaCodec codec,
protected MediaCodec createCodec(MediaCodecAdapter.Configuration configuration) MediaFormat mediaFormat,
throws IOException { boolean isDecoder,
String sampleMimeType = @Nullable Surface outputSurface) {
checkNotNull(configuration.mediaFormat.getString(MediaFormat.KEY_MIME)); TraceUtil.beginSection("configureCodec");
boolean isDecoder = (configuration.flags & MediaCodec.CONFIGURE_FLAG_ENCODE) == 0; codec.configure(
return isDecoder mediaFormat,
? MediaCodec.createDecoderByType(checkNotNull(sampleMimeType)) outputSurface,
: MediaCodec.createEncoderByType(checkNotNull(sampleMimeType)); /* 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( private static TransformationException createTransformationException(
Exception cause, Format format, boolean isVideo, boolean isDecoder) { Exception cause, Format format, boolean isVideo, boolean isDecoder) {
String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder"); String componentName = (isVideo ? "Video" : "Audio") + (isDecoder ? "Decoder" : "Encoder");
if (cause instanceof IOException) { if (cause instanceof IOException || cause instanceof MediaCodec.CodecException) {
return TransformationException.createForCodec( return TransformationException.createForCodec(
cause, cause,
componentName, componentName,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment