diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java index ed5deb8761..dc347ce6aa 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java @@ -30,6 +30,7 @@ import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_ import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_WITH_RECONFIGURATION; import static androidx.media3.exoplayer.mediacodec.MediaCodecPerformancePointCoverageProvider.COVERAGE_RESULT_NO; import static androidx.media3.exoplayer.mediacodec.MediaCodecPerformancePointCoverageProvider.COVERAGE_RESULT_YES; +import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel; import android.graphics.Point; import android.media.MediaCodec; @@ -775,12 +776,8 @@ public final class MediaCodecInfo { level = CodecProfileLevel.VP9Level1; } - CodecProfileLevel profileLevel = new CodecProfileLevel(); // Since this method is for legacy devices only, assume that only profile 0 is supported. - profileLevel.profile = CodecProfileLevel.VP9Profile0; - profileLevel.level = level; - - return new CodecProfileLevel[] {profileLevel}; + return new CodecProfileLevel[] {createCodecProfileLevel(CodecProfileLevel.VP9Profile0, level)}; } /** diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java index 2381c3e98e..27656d65f9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java @@ -322,6 +322,18 @@ public final class MediaCodecUtil { return maxH264DecodableFrameSize; } + /** + * Returns a {@link CodecProfileLevel} configured with the provided {@code profile} and {@code + * level}. + */ + @UnstableApi + public static CodecProfileLevel createCodecProfileLevel(int profile, int level) { + CodecProfileLevel profileLevel = new CodecProfileLevel(); + profileLevel.profile = profile; + profileLevel.level = level; + return profileLevel; + } + /** * @deprecated Use {@link CodecSpecificDataUtil#getCodecProfileAndLevel(Format)}. */ diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java index f770e4846a..0355719983 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java @@ -16,8 +16,18 @@ package androidx.media3.exoplayer.video; import static android.media.MediaCodec.INFO_TRY_AGAIN_LATER; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel42; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileHigh; +import static android.media.MediaCodecInfo.CodecProfileLevel.DolbyVisionLevelFhd30; +import static android.media.MediaCodecInfo.CodecProfileLevel.DolbyVisionProfileDvheDtr; +import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCHighTierLevel51; +import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel41; +import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCProfileMain; +import static android.media.MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10; import static android.view.Display.DEFAULT_DISPLAY; import static androidx.media3.common.util.Util.msToUs; +import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.format; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample; @@ -139,8 +149,7 @@ public class MediaCodecVideoRendererTest { /* name= */ "h264-codec-hw", /* mimeType= */ MimeTypes.VIDEO_H264, /* codecMimeType= */ MimeTypes.VIDEO_H264, - /* capabilities= */ createCodecCapabilities( - CodecProfileLevel.AVCProfileHigh, CodecProfileLevel.AVCLevel4), + /* capabilities= */ createCodecCapabilities(AVCProfileHigh, CodecProfileLevel.AVCLevel4), /* hardwareAccelerated= */ true, /* softwareOnly= */ false, /* vendor= */ false, @@ -152,8 +161,7 @@ public class MediaCodecVideoRendererTest { /* name= */ "h264-codec-sw", /* mimeType= */ MimeTypes.VIDEO_H264, /* codecMimeType= */ MimeTypes.VIDEO_H264, - /* capabilities= */ createCodecCapabilities( - CodecProfileLevel.AVCProfileHigh, CodecProfileLevel.AVCLevel5), + /* capabilities= */ createCodecCapabilities(AVCProfileHigh, CodecProfileLevel.AVCLevel5), /* hardwareAccelerated= */ false, /* softwareOnly= */ true, /* vendor= */ false, @@ -1817,11 +1825,10 @@ public class MediaCodecVideoRendererTest { case MimeTypes.VIDEO_H264: CodecCapabilities capabilitiesH264 = new CodecCapabilities(); capabilitiesH264.profileLevels = - new CodecProfileLevel[] {new CodecProfileLevel(), new CodecProfileLevel()}; - capabilitiesH264.profileLevels[0].profile = CodecProfileLevel.AVCProfileBaseline; - capabilitiesH264.profileLevels[0].level = CodecProfileLevel.AVCLevel42; - capabilitiesH264.profileLevels[1].profile = CodecProfileLevel.AVCProfileHigh; - capabilitiesH264.profileLevels[1].level = CodecProfileLevel.AVCLevel42; + new CodecProfileLevel[] { + createCodecProfileLevel(AVCProfileBaseline, AVCLevel42), + createCodecProfileLevel(AVCProfileHigh, AVCLevel42) + }; return ImmutableList.of( MediaCodecInfo.newInstance( /* name= */ "h264-codec", @@ -1836,11 +1843,10 @@ public class MediaCodecVideoRendererTest { case MimeTypes.VIDEO_H265: CodecCapabilities capabilitiesH265 = new CodecCapabilities(); capabilitiesH265.profileLevels = - new CodecProfileLevel[] {new CodecProfileLevel(), new CodecProfileLevel()}; - capabilitiesH265.profileLevels[0].profile = CodecProfileLevel.HEVCProfileMain; - capabilitiesH265.profileLevels[0].level = CodecProfileLevel.HEVCMainTierLevel41; - capabilitiesH265.profileLevels[1].profile = CodecProfileLevel.HEVCProfileMain10; - capabilitiesH265.profileLevels[1].level = CodecProfileLevel.HEVCHighTierLevel51; + new CodecProfileLevel[] { + createCodecProfileLevel(HEVCProfileMain, HEVCMainTierLevel41), + createCodecProfileLevel(HEVCProfileMain10, HEVCHighTierLevel51) + }; return ImmutableList.of( MediaCodecInfo.newInstance( /* name= */ "h265-codec", @@ -1899,11 +1905,8 @@ public class MediaCodecVideoRendererTest { switch (mimeType) { case MimeTypes.VIDEO_DOLBY_VISION: { - CodecCapabilities capabilitiesDolby = new CodecCapabilities(); - capabilitiesDolby.profileLevels = new CodecProfileLevel[] {new CodecProfileLevel()}; - capabilitiesDolby.profileLevels[0].profile = - CodecProfileLevel.DolbyVisionProfileDvheDtr; - capabilitiesDolby.profileLevels[0].level = CodecProfileLevel.DolbyVisionLevelFhd30; + CodecCapabilities capabilitiesDolby = + createCodecCapabilities(DolbyVisionProfileDvheDtr, DolbyVisionLevelFhd30); return ImmutableList.of( MediaCodecInfo.newInstance( /* name= */ "dvhe-codec", @@ -1920,11 +1923,10 @@ public class MediaCodecVideoRendererTest { { CodecCapabilities capabilitiesH265 = new CodecCapabilities(); capabilitiesH265.profileLevels = - new CodecProfileLevel[] {new CodecProfileLevel(), new CodecProfileLevel()}; - capabilitiesH265.profileLevels[0].profile = CodecProfileLevel.HEVCProfileMain; - capabilitiesH265.profileLevels[0].level = CodecProfileLevel.HEVCMainTierLevel41; - capabilitiesH265.profileLevels[1].profile = CodecProfileLevel.HEVCProfileMain10; - capabilitiesH265.profileLevels[1].level = CodecProfileLevel.HEVCHighTierLevel51; + new CodecProfileLevel[] { + createCodecProfileLevel(HEVCProfileMain, HEVCMainTierLevel41), + createCodecProfileLevel(HEVCProfileMain10, HEVCHighTierLevel51) + }; return ImmutableList.of( MediaCodecInfo.newInstance( /* name= */ "h265-codec", @@ -2150,9 +2152,7 @@ public class MediaCodecVideoRendererTest { private static CodecCapabilities createCodecCapabilities(int profile, int level) { CodecCapabilities capabilities = new CodecCapabilities(); - capabilities.profileLevels = new CodecProfileLevel[] {new CodecProfileLevel()}; - capabilities.profileLevels[0].profile = profile; - capabilities.profileLevels[0].level = level; + capabilities.profileLevels = new CodecProfileLevel[] {createCodecProfileLevel(profile, level)}; return capabilities; } 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 3943a4af29..4280d63dab 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 @@ -18,6 +18,7 @@ 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; import android.media.MediaCodecInfo.CodecProfileLevel; @@ -145,7 +146,7 @@ public final class ShadowMediaCodecConfig extends ExternalResource { /* codecName= */ "exotest.video.avc", /* mimeType= */ MimeTypes.VIDEO_H264, /* profileLevels= */ ImmutableList.of( - createProfileLevel( + createCodecProfileLevel( MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, MediaCodecInfo.CodecProfileLevel.AVCLevel62)), /* colorFormats= */ ImmutableList.of( @@ -156,7 +157,7 @@ public final class ShadowMediaCodecConfig extends ExternalResource { /* codecName= */ "exotest.video.hevc", /* mimeType= */ MimeTypes.VIDEO_H265, /* profileLevels= */ ImmutableList.of( - createProfileLevel( + createCodecProfileLevel( CodecProfileLevel.HEVCProfileMain, CodecProfileLevel.HEVCMainTierLevel61)), /* colorFormats= */ ImmutableList.of( MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible))); @@ -166,7 +167,7 @@ public final class ShadowMediaCodecConfig extends ExternalResource { /* codecName= */ "exotest.video.mpeg2", /* mimeType= */ MimeTypes.VIDEO_MPEG2, /* profileLevels= */ ImmutableList.of( - createProfileLevel( + createCodecProfileLevel( MediaCodecInfo.CodecProfileLevel.MPEG2ProfileMain, MediaCodecInfo.CodecProfileLevel.MPEG2LevelML)), /* colorFormats= */ ImmutableList.of( @@ -232,13 +233,6 @@ public final class ShadowMediaCodecConfig extends ExternalResource { return codecs.buildOrThrow(); } - private static MediaCodecInfo.CodecProfileLevel createProfileLevel(int profile, int level) { - MediaCodecInfo.CodecProfileLevel profileLevel = new MediaCodecInfo.CodecProfileLevel(); - profileLevel.profile = profile; - profileLevel.level = level; - return profileLevel; - } - /** * A {@link ShadowMediaCodec.CodecConfig.Codec} that passes data through without modifying it. * diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/DefaultEncoderFactoryTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/DefaultEncoderFactoryTest.java index 1096e92618..3995f76334 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/DefaultEncoderFactoryTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/DefaultEncoderFactoryTest.java @@ -16,6 +16,9 @@ package androidx.media3.transformer; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel4; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileHigh; +import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel; import static androidx.media3.transformer.ExportException.ERROR_CODE_ENCODING_FORMAT_UNSUPPORTED; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.google.common.truth.Truth.assertThat; @@ -23,6 +26,7 @@ import static org.junit.Assert.assertThrows; import android.content.Context; import android.media.MediaCodecInfo; +import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaFormat; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; @@ -58,11 +62,9 @@ public class DefaultEncoderFactoryTest { private static void createShadowH264Encoder() { MediaFormat avcFormat = new MediaFormat(); avcFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC); - MediaCodecInfo.CodecProfileLevel profileLevel = new MediaCodecInfo.CodecProfileLevel(); - profileLevel.profile = MediaCodecInfo.CodecProfileLevel.AVCProfileHigh; // Using Level4 gives us 8192 16x16 blocks. If using width 1920 uses 120 blocks, 8192 / 120 = 68 // blocks will be left for encoding height 1088. - profileLevel.level = MediaCodecInfo.CodecProfileLevel.AVCLevel4; + CodecProfileLevel profileLevel = createCodecProfileLevel(AVCProfileHigh, AVCLevel4); createShadowVideoEncoder(avcFormat, profileLevel, "test.transformer.avc.encoder"); } @@ -79,16 +81,14 @@ public class DefaultEncoderFactoryTest { } private static void createShadowVideoEncoder( - MediaFormat supportedFormat, - MediaCodecInfo.CodecProfileLevel supportedProfileLevel, - String name) { + MediaFormat supportedFormat, CodecProfileLevel supportedProfileLevel, String name) { MediaCodecInfo.CodecCapabilities capabilities = MediaCodecInfoBuilder.CodecCapabilitiesBuilder.newBuilder() .setMediaFormat(supportedFormat) .setIsEncoder(true) .setColorFormats( new int[] {MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible}) - .setProfileLevels(new MediaCodecInfo.CodecProfileLevel[] {supportedProfileLevel}) + .setProfileLevels(new CodecProfileLevel[] {supportedProfileLevel}) .build(); createShadowEncoder(name, capabilities); } diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/EncoderUtilTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/EncoderUtilTest.java index 515d6d046d..957d6670bd 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/EncoderUtilTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/EncoderUtilTest.java @@ -16,10 +16,14 @@ package androidx.media3.transformer; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel4; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileHigh; import static androidx.media3.common.MimeTypes.VIDEO_H264; +import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel; import static com.google.common.truth.Truth.assertThat; import android.media.MediaCodecInfo; +import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaFormat; import android.util.Size; import androidx.annotation.Nullable; @@ -48,11 +52,9 @@ public class EncoderUtilTest { public void setUp() { MediaFormat avcFormat = new MediaFormat(); avcFormat.setString(MediaFormat.KEY_MIME, VIDEO_H264); - MediaCodecInfo.CodecProfileLevel profileLevel = new MediaCodecInfo.CodecProfileLevel(); - profileLevel.profile = MediaCodecInfo.CodecProfileLevel.AVCProfileHigh; // Using Level4 gives us 8192 16x16 blocks. If using width 1920 uses 120 blocks, 8192 / 120 = 68 // blocks will be left for encoding height 1088. - profileLevel.level = MediaCodecInfo.CodecProfileLevel.AVCLevel4; + CodecProfileLevel profileLevel = createCodecProfileLevel(AVCProfileHigh, AVCLevel4); ShadowMediaCodecList.addCodec( MediaCodecInfoBuilder.newBuilder() @@ -64,7 +66,7 @@ public class EncoderUtilTest { .setIsEncoder(true) .setColorFormats( new int[] {MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible}) - .setProfileLevels(new MediaCodecInfo.CodecProfileLevel[] {profileLevel}) + .setProfileLevels(new CodecProfileLevel[] {profileLevel}) .build()) .build()); } diff --git a/libraries/transformer/src/test/java/androidx/media3/transformer/VideoEncoderWrapperTest.java b/libraries/transformer/src/test/java/androidx/media3/transformer/VideoEncoderWrapperTest.java index a5db3ab632..309cbab0ba 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/VideoEncoderWrapperTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/VideoEncoderWrapperTest.java @@ -15,11 +15,15 @@ */ package androidx.media3.transformer; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel4; +import static android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileHigh; +import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.createCodecProfileLevel; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.media.MediaCodecInfo; +import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaFormat; import android.net.Uri; import android.os.Looper; @@ -139,19 +143,15 @@ public final class VideoEncoderWrapperTest { private static void createShadowH264Encoder() { MediaFormat avcFormat = new MediaFormat(); avcFormat.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC); - MediaCodecInfo.CodecProfileLevel profileLevel = new MediaCodecInfo.CodecProfileLevel(); - profileLevel.profile = MediaCodecInfo.CodecProfileLevel.AVCProfileHigh; // Using Level4 gives us 8192 16x16 blocks. If using width 1920 uses 120 blocks, 8192 / 120 = 68 // blocks will be left for encoding height 1088. - profileLevel.level = MediaCodecInfo.CodecProfileLevel.AVCLevel4; + CodecProfileLevel profileLevel = createCodecProfileLevel(AVCProfileHigh, AVCLevel4); createShadowVideoEncoder(avcFormat, profileLevel, "test.transformer.avc.encoder"); } private static void createShadowVideoEncoder( - MediaFormat supportedFormat, - MediaCodecInfo.CodecProfileLevel supportedProfileLevel, - String name) { + MediaFormat supportedFormat, CodecProfileLevel supportedProfileLevel, String name) { // ShadowMediaCodecList is static. The added encoders will be visible for every test. ShadowMediaCodecList.addCodec( MediaCodecInfoBuilder.newBuilder() @@ -163,8 +163,7 @@ public final class VideoEncoderWrapperTest { .setIsEncoder(true) .setColorFormats( new int[] {MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible}) - .setProfileLevels( - new MediaCodecInfo.CodecProfileLevel[] {supportedProfileLevel}) + .setProfileLevels(new CodecProfileLevel[] {supportedProfileLevel}) .build()) .build()); }