Extract CodecInfo from CodecImpl and expose default CodecInfo configs

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

This change abstracts the codec information to a separate `CodecInfo` class. This allows having codecs with the same information configured with different configurations (passthrough/frame-dropping, decoder/encoder). It also does the following:
* Replaces the map of default codecs with public static final CodecInfo constants for each codec.
* Modifies the ShadowMediaCodecConfig constructor to accept a set of CodecImpls instead of mime types.
* Updates factory methods to reflect the constructor change.
* Updates addSupportedMimeTypes to addDecoder, taking a CodecInfo instead of a mimeType.

This is a non-functional change.

PiperOrigin-RevId: 739857883
This commit is contained in:
shahddaghash 2025-03-24 02:15:23 -07:00 committed by Copybara-Service
parent a472300c7a
commit d5dcbf4a12
2 changed files with 207 additions and 179 deletions

View File

@ -19,6 +19,8 @@ import static android.media.AudioFormat.CHANNEL_OUT_5POINT1;
import static android.media.AudioFormat.CHANNEL_OUT_STEREO;
import static android.media.AudioFormat.ENCODING_AC3;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AAC;
import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.CODEC_INFO_AC3;
import static androidx.media3.test.utils.robolectric.TestPlayerRunHelper.advance;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
@ -124,7 +126,7 @@ public class AudioCapabilitiesEndToEndTest {
@Config(minSdk = 29)
public void playAc3WithDirectPlayback_directPlaybackNotSupportMidPlayback_recoversToAac()
throws Exception {
shadowMediaCodecConfig.addSupportedMimeTypes(MimeTypes.AUDIO_AAC);
shadowMediaCodecConfig.addDecoders(CODEC_INFO_AAC);
setupDefaultPcmSupport();
addDirectPlaybackSupportForAC3();
setUiModeToTv();
@ -189,7 +191,7 @@ public class AudioCapabilitiesEndToEndTest {
public void
playAc3WithDirectPlayback_directPlaybackNotSupportMidPlaybackButDeviceHasAc3Codec_recoversToAc3()
throws Throwable {
shadowMediaCodecConfig.addSupportedMimeTypes(MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AC3);
shadowMediaCodecConfig.addDecoders(CODEC_INFO_AAC, CODEC_INFO_AC3);
setupDefaultPcmSupport();
addDirectPlaybackSupportForAC3();
setUiModeToTv();
@ -249,7 +251,7 @@ public class AudioCapabilitiesEndToEndTest {
final AtomicBoolean directPlaybackSupportAddedReference = new AtomicBoolean();
setupDefaultPcmSupport();
shadowMediaCodecConfig.addSupportedMimeTypes(MimeTypes.AUDIO_AAC);
shadowMediaCodecConfig.addDecoders(CODEC_INFO_AAC);
setUiModeToTv();
RenderersFactory renderersFactory =
createRenderersFactory(
@ -323,7 +325,7 @@ public class AudioCapabilitiesEndToEndTest {
final AtomicBoolean directPlaybackSupportAddedReference = new AtomicBoolean();
setupDefaultPcmSupport();
shadowMediaCodecConfig.addSupportedMimeTypes(MimeTypes.AUDIO_AAC, MimeTypes.AUDIO_AC3);
shadowMediaCodecConfig.addDecoders(CODEC_INFO_AAC, CODEC_INFO_AC3);
setUiModeToTv();
RenderersFactory renderersFactory =
createRenderersFactory(
@ -396,7 +398,7 @@ public class AudioCapabilitiesEndToEndTest {
throws Throwable {
final AtomicBoolean directPlaybackSupportAddedReference = new AtomicBoolean();
shadowMediaCodecConfig.addSupportedMimeTypes(MimeTypes.AUDIO_AAC);
shadowMediaCodecConfig.addDecoders(CODEC_INFO_AAC);
setupDefaultPcmSupport();
setUiModeToTv();
RenderersFactory renderersFactory =

View File

@ -15,9 +15,6 @@
*/
package androidx.media3.test.utils.robolectric;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel;
import android.media.MediaCodecInfo;
@ -27,40 +24,191 @@ import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.mediacodec.MediaCodecUtil;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Ints;
import java.nio.ByteBuffer;
import java.util.HashSet;
import java.util.Set;
import org.junit.rules.ExternalResource;
import org.robolectric.shadows.MediaCodecInfoBuilder;
import org.robolectric.shadows.ShadowMediaCodec;
import org.robolectric.shadows.ShadowMediaCodecList;
/**
* A JUnit @Rule to configure Roboelectric's {@link ShadowMediaCodec}.
* A JUnit @Rule to configure Robolectric's {@link ShadowMediaCodec}.
*
* <p>Registers a {@link org.robolectric.shadows.ShadowMediaCodec.CodecConfig} for each audio/video
* MIME type known by ExoPlayer.
*/
@UnstableApi
public final class ShadowMediaCodecConfig extends ExternalResource {
private static final ImmutableMap<String, CodecImpl> ALL_SUPPORTED_CODECS =
createAllSupportedCodecs();
public static ShadowMediaCodecConfig forAllSupportedMimeTypes() {
return new ShadowMediaCodecConfig(ALL_SUPPORTED_CODECS.keySet());
/** Class that holds information about a {@link CodecImpl} configuration. */
public static final class CodecInfo {
public final String codecName;
public final String mimeType;
public final ImmutableList<CodecProfileLevel> profileLevels;
public final ImmutableList<Integer> colorFormats;
/**
* Creates an instance.
*
* <p>This method is equivalent to {@code CodecInfo(codecName, mimeType, ImmutableList.of(),
* ImmutableList.of()}.
*
* @param codecName The name of the codec.
* @param mimeType The MIME type of the codec.
*/
public CodecInfo(String codecName, String mimeType) {
this(
codecName,
mimeType,
/* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of());
}
/**
* 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.
*/
public CodecInfo(
String codecName,
String mimeType,
ImmutableList<CodecProfileLevel> profileLevels,
ImmutableList<Integer> colorFormats) {
this.codecName = codecName;
this.mimeType = mimeType;
this.profileLevels = profileLevels;
this.colorFormats = colorFormats;
}
}
public static final CodecInfo CODEC_INFO_AVC =
new CodecInfo(
/* codecName= */ "exotest.video.avc",
MimeTypes.VIDEO_H264,
/* profileLevels= */ ImmutableList.of(
createCodecProfileLevel(
CodecProfileLevel.AVCProfileHigh, CodecProfileLevel.AVCLevel62)),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
public static final CodecInfo CODEC_INFO_HEVC =
new CodecInfo(
/* codecName= */ "exotest.video.hevc",
MimeTypes.VIDEO_H265,
/* profileLevels= */ ImmutableList.of(
createCodecProfileLevel(
CodecProfileLevel.HEVCProfileMain, CodecProfileLevel.HEVCMainTierLevel61)),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
public static final CodecInfo CODEC_INFO_MPEG2 =
new CodecInfo(
/* codecName= */ "exotest.video.mpeg2",
MimeTypes.VIDEO_MPEG2,
/* profileLevels= */ ImmutableList.of(
createCodecProfileLevel(
CodecProfileLevel.MPEG2ProfileMain, CodecProfileLevel.MPEG2LevelML)),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
public static final CodecInfo CODEC_INFO_VP9 =
new CodecInfo(
/* codecName= */ "exotest.video.vp9",
MimeTypes.VIDEO_VP9,
/* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
public static final CodecInfo CODEC_INFO_AV1 =
new CodecInfo(
/* codecName= */ "exotest.video.av1",
MimeTypes.VIDEO_AV1,
/* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible));
public static final CodecInfo CODEC_INFO_AAC =
new CodecInfo(/* codecName= */ "exotest.audio.aac", MimeTypes.AUDIO_AAC);
public static final CodecInfo CODEC_INFO_AC3 =
new CodecInfo(/* codecName= */ "exotest.audio.ac3", MimeTypes.AUDIO_AC3);
public static final CodecInfo CODEC_INFO_AC4 =
new CodecInfo(/* codecName= */ "exotest.audio.ac4", MimeTypes.AUDIO_AC4);
public static final CodecInfo CODEC_INFO_E_AC3 =
new CodecInfo(/* codecName= */ "exotest.audio.eac3", MimeTypes.AUDIO_E_AC3);
public static final CodecInfo CODEC_INFO_E_AC3_JOC =
new CodecInfo(/* codecName= */ "exotest.audio.eac3joc", MimeTypes.AUDIO_E_AC3_JOC);
public static final CodecInfo CODEC_INFO_FLAC =
new CodecInfo(/* codecName= */ "exotest.audio.flac", MimeTypes.AUDIO_FLAC);
public static final CodecInfo CODEC_INFO_MPEG =
new CodecInfo(/* codecName= */ "exotest.audio.mpeg", MimeTypes.AUDIO_MPEG);
public static final CodecInfo CODEC_INFO_MPEG_L2 =
new CodecInfo(/* codecName= */ "exotest.audio.mpegl2", MimeTypes.AUDIO_MPEG_L2);
public static final CodecInfo CODEC_INFO_OPUS =
new CodecInfo(/* codecName= */ "exotest.audio.opus", MimeTypes.AUDIO_OPUS);
public static final CodecInfo CODEC_INFO_VORBIS =
new CodecInfo(/* codecName= */ "exotest.audio.vorbis", MimeTypes.AUDIO_VORBIS);
// In ExoPlayer, raw audio should use a bypass mode and never need this codec. However, to easily
// assert failures of the bypass mode we want to detect when the raw audio is decoded by this
public static final CodecInfo CODEC_INFO_RAW =
new CodecInfo(/* codecName= */ "exotest.audio.raw", MimeTypes.AUDIO_RAW);
private static final ImmutableSet<CodecInfo> ALL_SUPPORTED_CODECS =
ImmutableSet.of(
CODEC_INFO_AVC,
CODEC_INFO_HEVC,
CODEC_INFO_MPEG2,
CODEC_INFO_VP9,
CODEC_INFO_AV1,
CODEC_INFO_AAC,
CODEC_INFO_AC3,
CODEC_INFO_AC4,
CODEC_INFO_E_AC3,
CODEC_INFO_E_AC3_JOC,
CODEC_INFO_FLAC,
CODEC_INFO_MPEG,
CODEC_INFO_MPEG_L2,
CODEC_INFO_OPUS,
CODEC_INFO_VORBIS,
CODEC_INFO_RAW);
/**
* @deprecated Use {@link ShadowMediaCodecConfig#withAllDefaultSupportedCodecs()} instead.
*/
// TODO(b/399861060): Remove in Media3 1.8.
@Deprecated
public static ShadowMediaCodecConfig forAllSupportedMimeTypes() {
return withAllDefaultSupportedCodecs();
}
/**
* Returns a {@link ShadowMediaCodecConfig} instance populated with a default list of supported
* decoders using a default codec configuration.
*
* <p>The default codec configuration drops all samples on audio decoders and works as passthrough
* on video decoders.
*/
public static ShadowMediaCodecConfig withAllDefaultSupportedCodecs() {
return new ShadowMediaCodecConfig(
createDecoders(ALL_SUPPORTED_CODECS.asList(), /* forcePassthrough= */ false));
}
/**
* @deprecated Use {@link ShadowMediaCodecConfig#withNoDefaultSupportedCodecs()} instead.
*/
// TODO(b/399861060): Remove in Media3 1.8.
@Deprecated
public static ShadowMediaCodecConfig withNoDefaultSupportedMimeTypes() {
return withNoDefaultSupportedCodecs();
}
/** Returns a {@link ShadowMediaCodecConfig} instance populated with no shadow codecs. */
public static ShadowMediaCodecConfig withNoDefaultSupportedCodecs() {
return new ShadowMediaCodecConfig(ImmutableSet.of());
}
private final Set<String> supportedMimeTypes;
private final ImmutableSet<CodecImpl> defaultCodecs;
private ShadowMediaCodecConfig(Set<String> mimeTypes) {
supportedMimeTypes = new HashSet<>(mimeTypes);
private ShadowMediaCodecConfig(ImmutableSet<CodecImpl> defaultCodecs) {
this.defaultCodecs = defaultCodecs;
}
/**
@ -106,140 +254,44 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
}
}
public void addSupportedMimeTypes(String... mimeTypes) {
for (String mimeType : mimeTypes) {
checkState(!supportedMimeTypes.contains(mimeType), "MIME type already added: " + mimeType);
checkArgument(
ALL_SUPPORTED_CODECS.containsKey(mimeType), "MIME type not supported: " + mimeType);
/**
* Configures and publishes {@linkplain ShadowMediaCodec shadow decoders} based on {@code
* decoders}.
*
* <p>This method configures frame-dropping decoders.
*/
public void addDecoders(CodecInfo... decoders) {
for (CodecInfo decoderInfo : decoders) {
CodecImpl decoder = CodecImpl.createFrameDroppingDecoder(decoderInfo);
decoder.configure();
}
ImmutableSet<String> addedMimeTypes = ImmutableSet.copyOf(mimeTypes);
supportedMimeTypes.addAll(addedMimeTypes);
configureCodecs(addedMimeTypes);
}
@Override
protected void before() throws Throwable {
configureCodecs(supportedMimeTypes);
for (CodecImpl codec : this.defaultCodecs) {
codec.configure();
}
}
@Override
protected void after() {
supportedMimeTypes.clear();
MediaCodecUtil.clearDecoderInfoCache();
ShadowMediaCodecList.reset();
ShadowMediaCodec.clearCodecs();
}
private void configureCodecs(Set<String> mimeTypes) {
for (String mimeType : mimeTypes) {
checkStateNotNull(ALL_SUPPORTED_CODECS.get(mimeType)).configure();
private static ImmutableSet<CodecImpl> createDecoders(
ImmutableList<CodecInfo> decoderInfos, boolean forcePassthrough) {
ImmutableSet.Builder<CodecImpl> builder = new ImmutableSet.Builder<>();
for (CodecInfo info : decoderInfos) {
if (!forcePassthrough && MimeTypes.isAudio(info.mimeType)) {
builder.add(CodecImpl.createFrameDroppingDecoder(info));
} else {
builder.add(CodecImpl.createPassthroughDecoder(info));
}
}
}
private static ImmutableMap<String, CodecImpl> createAllSupportedCodecs() {
ImmutableMap.Builder<String, CodecImpl> codecs = new ImmutableMap.Builder<>();
// Video codecs
codecs.put(
MimeTypes.VIDEO_H264,
new CodecImpl(
/* codecName= */ "exotest.video.avc",
/* mimeType= */ MimeTypes.VIDEO_H264,
/* profileLevels= */ ImmutableList.of(
createCodecProfileLevel(
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
MediaCodecInfo.CodecProfileLevel.AVCLevel62)),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put(
MimeTypes.VIDEO_H265,
new CodecImpl(
/* codecName= */ "exotest.video.hevc",
/* mimeType= */ MimeTypes.VIDEO_H265,
/* profileLevels= */ ImmutableList.of(
createCodecProfileLevel(
CodecProfileLevel.HEVCProfileMain, CodecProfileLevel.HEVCMainTierLevel61)),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put(
MimeTypes.VIDEO_MPEG2,
new CodecImpl(
/* codecName= */ "exotest.video.mpeg2",
/* mimeType= */ MimeTypes.VIDEO_MPEG2,
/* profileLevels= */ ImmutableList.of(
createCodecProfileLevel(
MediaCodecInfo.CodecProfileLevel.MPEG2ProfileMain,
MediaCodecInfo.CodecProfileLevel.MPEG2LevelML)),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put(
MimeTypes.VIDEO_VP9,
new CodecImpl(
/* codecName= */ "exotest.video.vp9",
/* mimeType= */ MimeTypes.VIDEO_VP9,
/* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
codecs.put(
MimeTypes.VIDEO_AV1,
new CodecImpl(
/* codecName= */ "exotest.video.av1",
/* mimeType= */ MimeTypes.VIDEO_AV1,
/* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of(
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible),
/* isPassthrough= */ true,
/* isEncoder= */ false));
// Frame-dropping audio decoders.
codecs.put(
MimeTypes.AUDIO_AAC,
new CodecImpl(/* codecName= */ "exotest.audio.aac", /* mimeType= */ MimeTypes.AUDIO_AAC));
codecs.put(
MimeTypes.AUDIO_AC3,
new CodecImpl(/* codecName= */ "exotest.audio.ac3", /* mimeType= */ MimeTypes.AUDIO_AC3));
codecs.put(
MimeTypes.AUDIO_AC4,
new CodecImpl(/* codecName= */ "exotest.audio.ac4", /* mimeType= */ MimeTypes.AUDIO_AC4));
codecs.put(
MimeTypes.AUDIO_E_AC3,
new CodecImpl(
/* codecName= */ "exotest.audio.eac3", /* mimeType= */ MimeTypes.AUDIO_E_AC3));
codecs.put(
MimeTypes.AUDIO_E_AC3_JOC,
new CodecImpl(
/* codecName= */ "exotest.audio.eac3joc", /* mimeType= */ MimeTypes.AUDIO_E_AC3_JOC));
codecs.put(
MimeTypes.AUDIO_FLAC,
new CodecImpl(/* codecName= */ "exotest.audio.flac", /* mimeType= */ MimeTypes.AUDIO_FLAC));
codecs.put(
MimeTypes.AUDIO_MPEG,
new CodecImpl(/* codecName= */ "exotest.audio.mpeg", /* mimeType= */ MimeTypes.AUDIO_MPEG));
codecs.put(
MimeTypes.AUDIO_MPEG_L2,
new CodecImpl(
/* codecName= */ "exotest.audio.mpegl2", /* mimeType= */ MimeTypes.AUDIO_MPEG_L2));
codecs.put(
MimeTypes.AUDIO_OPUS,
new CodecImpl(/* codecName= */ "exotest.audio.opus", /* mimeType= */ MimeTypes.AUDIO_OPUS));
codecs.put(
MimeTypes.AUDIO_VORBIS,
new CodecImpl(
/* codecName= */ "exotest.audio.vorbis", /* mimeType= */ MimeTypes.AUDIO_VORBIS));
// Raw audio should use a bypass mode and never need this codec. However, to easily assert
// failures of the bypass mode we want to detect when the raw audio is decoded by this
codecs.put(
MimeTypes.AUDIO_RAW,
new CodecImpl(/* codecName= */ "exotest.audio.raw", /* mimeType= */ MimeTypes.AUDIO_RAW));
return codecs.buildOrThrow();
return builder.build();
}
/**
@ -248,55 +300,29 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
*/
private static final class CodecImpl implements ShadowMediaCodec.CodecConfig.Codec {
private final String codecName;
private final String mimeType;
private final ImmutableList<MediaCodecInfo.CodecProfileLevel> profileLevels;
private final ImmutableList<Integer> colorFormats;
private final CodecInfo codecInfo;
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) {
this(
codecName,
mimeType,
/* profileLevels= */ ImmutableList.of(),
/* colorFormats= */ ImmutableList.of(),
/* isPassthrough= */ false,
/* isEncoder= */ false);
public static CodecImpl createFrameDroppingDecoder(CodecInfo codecInfo) {
return new CodecImpl(codecInfo, /* isPassthrough= */ false, /* isEncoder= */ false);
}
public static CodecImpl createPassthroughDecoder(CodecInfo codecInfo) {
return new CodecImpl(codecInfo, /* isPassthrough= */ true, /* 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 codecInfo The {@link CodecInfo} that holds the codec information.
* @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(
String codecName,
String mimeType,
ImmutableList<CodecProfileLevel> profileLevels,
ImmutableList<Integer> colorFormats,
boolean isPassthrough,
boolean isEncoder) {
this.codecName = codecName;
this.mimeType = mimeType;
this.profileLevels = profileLevels;
this.colorFormats = colorFormats;
private CodecImpl(CodecInfo codecInfo, boolean isPassthrough, boolean isEncoder) {
this.codecInfo = codecInfo;
this.isPassthrough = isPassthrough;
this.isEncoder = isEncoder;
}
@ -304,13 +330,13 @@ public final class ShadowMediaCodecConfig extends ExternalResource {
public void configure() {
// TODO: Update ShadowMediaCodec to consider the MediaFormat.KEY_MAX_INPUT_SIZE value passed
// to configure() so we don't have to specify large buffers here.
int bufferSize = mimeType.equals(MimeTypes.VIDEO_H265) ? 250_000 : 150_000;
int bufferSize = codecInfo.mimeType.equals(MimeTypes.VIDEO_H265) ? 250_000 : 150_000;
configureShadowMediaCodec(
codecName,
mimeType,
codecInfo.codecName,
codecInfo.mimeType,
isEncoder,
profileLevels,
colorFormats,
codecInfo.profileLevels,
codecInfo.colorFormats,
new ShadowMediaCodec.CodecConfig(
/* inputBufferSize= */ bufferSize,
/* outputBufferSize= */ bufferSize,