diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Codec.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Codec.java index 7322708008..b95bc862a2 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Codec.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Codec.java @@ -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}. * - *

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. + *

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; } diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultCodecFactory.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultCodecFactory.java index 22603002c4..80c43bd818 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultCodecFactory.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultCodecFactory.java @@ -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,