From c030e49dd6b12d9b4d55bb1d1bd6847e51578ee8 Mon Sep 17 00:00:00 2001 From: shahddaghash Date: Thu, 6 Mar 2025 07:36:49 -0800 Subject: [PATCH] Refactor ShadowMediaCodec to be used for transcoding cases This is a step towards unifying ShadowMediaCodecConfig for ExoPlayer and Transcoding tests. This change includes extracting encoder/decoder configurtion logic to a static method that can be called by `ShadowMediaCodecConfig.CodecImpl#configure()` and `TestUtil#addCodec()`. This is a non-functional refactor. PiperOrigin-RevId: 734137675 --- .../robolectric/ShadowMediaCodecConfig.java | 69 ++++++++++++++----- .../androidx/media3/transformer/TestUtil.java | 37 +++------- 2 files changed, 61 insertions(+), 45 deletions(-) diff --git a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java index a1ac4c5435..3943a4af29 100644 --- a/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java +++ b/libraries/test_utils_robolectric/src/main/java/androidx/media3/test/utils/robolectric/ShadowMediaCodecConfig.java @@ -63,6 +63,49 @@ public final class ShadowMediaCodecConfig extends ExternalResource { supportedMimeTypes = new HashSet<>(mimeTypes); } + /** + * Configures a shadow MediaCodec. + * + * @param codecName The name of the codec. + * @param mimeType The MIME type of the codec. + * @param isEncoder Whether the codec is an encoder or a decoder. + * @param profileLevels A list of profiles and levels supported by the codec. + * @param colorFormats A list of color formats supported by the codec. + * @param codecConfig The {@link ShadowMediaCodec.CodecConfig} for the codec, specifying its + * behavior. + */ + public static void configureShadowMediaCodec( + String codecName, + String mimeType, + boolean isEncoder, + ImmutableList profileLevels, + ImmutableList colorFormats, + ShadowMediaCodec.CodecConfig codecConfig) { + MediaFormat mediaFormat = new MediaFormat(); + mediaFormat.setString(MediaFormat.KEY_MIME, mimeType); + MediaCodecInfoBuilder.CodecCapabilitiesBuilder capabilities = + MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder() + .setMediaFormat(mediaFormat) + .setIsEncoder(isEncoder); + if (!profileLevels.isEmpty()) { + capabilities.setProfileLevels(profileLevels.toArray(new CodecProfileLevel[0])); + } + if (!colorFormats.isEmpty()) { + capabilities.setColorFormats(Ints.toArray(colorFormats)); + } + ShadowMediaCodecList.addCodec( + MediaCodecInfoBuilder.newBuilder() + .setName(codecName) + .setIsEncoder(isEncoder) + .setCapabilities(capabilities.build()) + .build()); + if (isEncoder) { + ShadowMediaCodec.addEncoder(codecName, codecConfig); + } else { + ShadowMediaCodec.addDecoder(codecName, codecConfig); + } + } + public void addSupportedMimeTypes(String... mimeTypes) { for (String mimeType : mimeTypes) { checkState(!supportedMimeTypes.contains(mimeType), "MIME type already added: " + mimeType); @@ -231,29 +274,19 @@ public final class ShadowMediaCodecConfig extends ExternalResource { } public void configure() { - MediaFormat mediaFormat = new MediaFormat(); - mediaFormat.setString(MediaFormat.KEY_MIME, mimeType); - MediaCodecInfoBuilder.CodecCapabilitiesBuilder capabilities = - MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder().setMediaFormat(mediaFormat); - if (!profileLevels.isEmpty()) { - capabilities.setProfileLevels( - profileLevels.toArray(new MediaCodecInfo.CodecProfileLevel[0])); - } - if (!colorFormats.isEmpty()) { - capabilities.setColorFormats(Ints.toArray(colorFormats)); - } - ShadowMediaCodecList.addCodec( - MediaCodecInfoBuilder.newBuilder() - .setName(codecName) - .setCapabilities(capabilities.build()) - .build()); // 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; - ShadowMediaCodec.addDecoder( + configureShadowMediaCodec( codecName, + mimeType, + /* isEncoder= */ false, + profileLevels, + colorFormats, new ShadowMediaCodec.CodecConfig( - /* inputBufferSize= */ bufferSize, /* outputBufferSize= */ bufferSize, this)); + /* inputBufferSize= */ bufferSize, + /* outputBufferSize= */ bufferSize, + /* codec= */ this)); } @Override diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/TestUtil.java b/libraries/transformer/src/test/java/androidx/media3/transformer/TestUtil.java index 53869e80e8..83844a1a72 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/TestUtil.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/TestUtil.java @@ -15,7 +15,8 @@ */ package androidx.media3.transformer; -import android.media.MediaFormat; +import static androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig.configureShadowMediaCodec; + import androidx.media3.common.MimeTypes; import androidx.media3.common.audio.AudioProcessor; import androidx.media3.common.audio.ChannelMixingAudioProcessor; @@ -24,10 +25,8 @@ import androidx.media3.common.audio.SonicAudioProcessor; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; -import com.google.common.primitives.Ints; import java.util.List; import java.util.StringJoiner; -import org.robolectric.shadows.MediaCodecInfoBuilder; import org.robolectric.shadows.ShadowMediaCodec; import org.robolectric.shadows.ShadowMediaCodecList; @@ -209,33 +208,17 @@ public final class TestUtil { private static void addCodec( String mimeType, ShadowMediaCodec.CodecConfig codecConfig, - List colorFormats, + ImmutableList colorFormats, boolean isDecoder) { String codecName = Util.formatInvariant( isDecoder ? "exo.%s.decoder" : "exo.%s.encoder", mimeType.replace('/', '-')); - if (isDecoder) { - ShadowMediaCodec.addDecoder(codecName, codecConfig); - } else { - ShadowMediaCodec.addEncoder(codecName, codecConfig); - } - - MediaFormat mediaFormat = new MediaFormat(); - mediaFormat.setString(MediaFormat.KEY_MIME, mimeType); - MediaCodecInfoBuilder.CodecCapabilitiesBuilder codecCapabilities = - MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder() - .setMediaFormat(mediaFormat) - .setIsEncoder(!isDecoder); - - if (!colorFormats.isEmpty()) { - codecCapabilities.setColorFormats(Ints.toArray(colorFormats)); - } - - ShadowMediaCodecList.addCodec( - MediaCodecInfoBuilder.newBuilder() - .setName(codecName) - .setIsEncoder(!isDecoder) - .setCapabilities(codecCapabilities.build()) - .build()); + configureShadowMediaCodec( + codecName, + mimeType, + !isDecoder, + /* profileLevels= */ ImmutableList.of(), + colorFormats, + codecConfig); } }