Add support for explicit passthrough and encoder shadow codec configs

This is a step towards unifying ShadowMediaCodecConfig structure to accomodate both ExoPlayer and Transcoding codecs configuration.

ExoPlayer's `CodecConfig.Codec` implementation, due to b/174737370, discards all audio samples. This issue is only applicable for ExoPlayer tests and doesn't impact transcoding tests. To accomodate for having transcoding codecs, 2 fields were added to CodecImpl: `isPassThrough` that specifies whether we want to drop frames/samples or not, and `isEncoder` that specifies whether we are configuring a decoder or an encoder.

This is a non-functional refactor.

PiperOrigin-RevId: 739112659
This commit is contained in:
shahddaghash 2025-03-21 02:59:16 -07:00 committed by Copybara-Service
parent 3f5019b908
commit 3be1d5646f

View File

@ -23,7 +23,6 @@ import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecPro
import android.media.MediaCodecInfo; import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaFormat; import android.media.MediaFormat;
import androidx.media3.common.C;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil; import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
@ -150,7 +149,9 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
MediaCodecInfo.CodecProfileLevel.AVCLevel62)), MediaCodecInfo.CodecProfileLevel.AVCLevel62)),
/* colorFormats= */ ImmutableList.of( /* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible))); MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put( codecs.put(
MimeTypes.VIDEO_H265, MimeTypes.VIDEO_H265,
new CodecImpl( new CodecImpl(
@ -160,7 +161,9 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
createCodecProfileLevel( createCodecProfileLevel(
CodecProfileLevel.HEVCProfileMain, CodecProfileLevel.HEVCMainTierLevel61)), CodecProfileLevel.HEVCProfileMain, CodecProfileLevel.HEVCMainTierLevel61)),
/* colorFormats= */ ImmutableList.of( /* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible))); MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put( codecs.put(
MimeTypes.VIDEO_MPEG2, MimeTypes.VIDEO_MPEG2,
new CodecImpl( new CodecImpl(
@ -171,7 +174,9 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
MediaCodecInfo.CodecProfileLevel.MPEG2ProfileMain, MediaCodecInfo.CodecProfileLevel.MPEG2ProfileMain,
MediaCodecInfo.CodecProfileLevel.MPEG2LevelML)), MediaCodecInfo.CodecProfileLevel.MPEG2LevelML)),
/* colorFormats= */ ImmutableList.of( /* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible))); MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put( codecs.put(
MimeTypes.VIDEO_VP9, MimeTypes.VIDEO_VP9,
new CodecImpl( new CodecImpl(
@ -179,7 +184,9 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
/* mimeType= */ MimeTypes.VIDEO_VP9, /* mimeType= */ MimeTypes.VIDEO_VP9,
/* profileLevels= */ ImmutableList.of(), /* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of( /* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible))); MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put( codecs.put(
MimeTypes.VIDEO_AV1, MimeTypes.VIDEO_AV1,
new CodecImpl( new CodecImpl(
@ -187,9 +194,11 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
/* mimeType= */ MimeTypes.VIDEO_AV1, /* mimeType= */ MimeTypes.VIDEO_AV1,
/* profileLevels= */ ImmutableList.of(), /* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of( /* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible))); MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
// Audio codecs // Frame-dropping audio decoders.
codecs.put( codecs.put(
MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AAC,
new CodecImpl(/* codecName= */ "exotest.audio.aac", /* mimeType= */ MimeTypes.AUDIO_AAC)); new CodecImpl(/* codecName= */ "exotest.audio.aac", /* mimeType= */ MimeTypes.AUDIO_AAC));
@ -234,10 +243,8 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
} }
/** /**
* A {@link ShadowMediaCodec.CodecConfig.Codec} that passes data through without modifying it. * A {@link ShadowMediaCodec.CodecConfig.Codec} that provides pass-through or frame dropping
* * encoders and decoders.
* <p>Note: This currently drops all audio data - removing this restriction is tracked in
* [internal b/174737370].
*/ */
private static final class CodecImpl implements ShadowMediaCodec.CodecConfig.Codec { private static final class CodecImpl implements ShadowMediaCodec.CodecConfig.Codec {
@ -245,26 +252,53 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
private final String mimeType; private final String mimeType;
private final ImmutableList<MediaCodecInfo.CodecProfileLevel> profileLevels; private final ImmutableList<MediaCodecInfo.CodecProfileLevel> profileLevels;
private final ImmutableList<Integer> colorFormats; private final ImmutableList<Integer> colorFormats;
private final @C.TrackType int trackType; private final boolean isPassthrough;
private final boolean isEncoder;
/**
* Creates a frame dropping decoder with the specified {@code codecName} and {@code mimeType}.
*
* <p>This method is equivalent to {@code new CodecImpl(codecName, mimeType, ImmutableList.of(),
* ImmutableList.of(), false, false)}.
*
* @param codecName The name of the codec.
* @param mimeType The MIME type of the codec.
*/
public CodecImpl(String codecName, String mimeType) { public CodecImpl(String codecName, String mimeType) {
this( this(
codecName, codecName,
mimeType, mimeType,
/* profileLevels= */ ImmutableList.of(), /* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of()); /* colorFormats= */ ImmutableList.of(),
/* isPassthrough= */ false,
/* isEncoder= */ false);
} }
/**
* Creates an instance.
*
* @param codecName The name of the codec.
* @param mimeType The MIME type of the codec.
* @param profileLevels A list of profiles and levels supported by the codec.
* @param colorFormats A list of color formats supported by the codec.
* @param isPassthrough If {@code true}, the codec acts as a pass-through codec, directly
* copying input data to the output. If {@code false}, the codec drops frames.
* @param isEncoder If {@code true}, the codec is an encoder. If {@code false}, the codec is a
* decoder.
*/
public CodecImpl( public CodecImpl(
String codecName, String codecName,
String mimeType, String mimeType,
ImmutableList<CodecProfileLevel> profileLevels, ImmutableList<CodecProfileLevel> profileLevels,
ImmutableList<Integer> colorFormats) { ImmutableList<Integer> colorFormats,
boolean isPassthrough,
boolean isEncoder) {
this.codecName = codecName; this.codecName = codecName;
this.mimeType = mimeType; this.mimeType = mimeType;
this.profileLevels = profileLevels; this.profileLevels = profileLevels;
this.colorFormats = colorFormats; this.colorFormats = colorFormats;
trackType = MimeTypes.getTrackType(mimeType); this.isPassthrough = isPassthrough;
this.isEncoder = isEncoder;
} }
public void configure() { public void configure() {
@ -274,7 +308,7 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
configureShadowMediaCodec( configureShadowMediaCodec(
codecName, codecName,
mimeType, mimeType,
/* isEncoder= */ false, isEncoder,
profileLevels, profileLevels,
colorFormats, colorFormats,
new ShadowMediaCodec.CodecConfig( new ShadowMediaCodec.CodecConfig(
@ -285,12 +319,12 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
@Override @Override
public void process(ByteBuffer in, ByteBuffer out) { public void process(ByteBuffer in, ByteBuffer out) {
byte[] bytes = new byte[in.remaining()];
in.get(bytes);
// TODO(internal b/174737370): Output audio bytes as well. // TODO(internal b/174737370): Output audio bytes as well.
if (trackType != C.TRACK_TYPE_AUDIO) { if (isPassthrough) {
out.put(bytes); out.put(in);
} else {
byte[] bytes = new byte[in.remaining()];
in.get(bytes);
} }
} }
} }