mirror of
https://github.com/samsonjs/media.git
synced 2026-03-27 09:45:47 +00:00
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:
parent
a93e8cc620
commit
bf32ae50d7
2 changed files with 100 additions and 96 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue