diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java index f6fb40610d..50cb39c5a4 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -370,18 +370,16 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { int operatingRate = supportedVideoEncoderSettings.operatingRate; int priority = supportedVideoEncoderSettings.priority; - if (Util.SDK_INT >= 23 - && operatingRate != VideoEncoderSettings.RATE_UNSET - && priority != VideoEncoderSettings.RATE_UNSET) { - // Setting operating rate and priority is supported from API 23. + // Setting operating rate and priority is supported from API 23. + if (Util.SDK_INT >= 23) { if (operatingRate == VideoEncoderSettings.NO_VALUE && priority == VideoEncoderSettings.NO_VALUE) { adjustMediaFormatForEncoderPerformanceSettings(mediaFormat); } else { - if (operatingRate != VideoEncoderSettings.NO_VALUE) { + if (operatingRate != VideoEncoderSettings.RATE_UNSET) { mediaFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, operatingRate); } - if (priority != VideoEncoderSettings.NO_VALUE) { + if (priority != VideoEncoderSettings.RATE_UNSET) { mediaFormat.setInteger(MediaFormat.KEY_PRIORITY, priority); } } @@ -651,7 +649,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { * *
The adjustment is applied in-place to {@code mediaFormat}. */ - static void adjustMediaFormatForEncoderPerformanceSettings(MediaFormat mediaFormat) { + private static void adjustMediaFormatForEncoderPerformanceSettings(MediaFormat mediaFormat) { if (Util.SDK_INT < 25) { // Not setting priority and operating rate achieves better encoding performance. return; @@ -661,11 +659,29 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { if (Util.SDK_INT == 26) { mediaFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, DEFAULT_FRAME_RATE); + } else if (deviceNeedsLowerOperatingRateAvoidingOverflowWorkaround()) { + mediaFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, 1000); } else { - mediaFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, /* value= */ 1000); + mediaFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, Integer.MAX_VALUE); } } + private static boolean deviceNeedsLowerOperatingRateAvoidingOverflowWorkaround() { + // On these chipsets, setting an operating rate close to Integer.MAX_VALUE will cause the + // encoder to throw at configuration time. Setting the operating rate to 1000 avoids being close + // to an integer overflow limit while being higher than a maximum feasible operating rate. See + // [internal b/311206113, b/317297946, b/312299527]. + return Util.SDK_INT >= 31 + && Util.SDK_INT <= 34 + && (Build.SOC_MODEL.equals("SM8550") + || Build.SOC_MODEL.equals("SM7450") + || Build.SOC_MODEL.equals("SM6450") + || Build.SOC_MODEL.equals("SC9863A") + || Build.SOC_MODEL.equals("T612") + || Build.SOC_MODEL.equals("T606") + || Build.SOC_MODEL.equals("T603")); + } + /** * Applying suggested profile settings from * https://developer.android.com/media/optimize/sharing#b-frames_and_encoding_profiles diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoEncoderSettings.java b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoEncoderSettings.java index 932f93133f..927e788a75 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/VideoEncoderSettings.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/VideoEncoderSettings.java @@ -174,8 +174,8 @@ public final class VideoEncoderSettings { * Sets encoding operating rate and priority. The default values are {@link #NO_VALUE}, which is * treated as configuring the encoder for maximum throughput. * - *
To disable the configuration for operating rate and priority, use {@link #RATE_UNSET} for - * both arguments. + *
To disable the configuration for either operating rate or priority, use {@link + * #RATE_UNSET} for that argument. * * @param operatingRate The {@link MediaFormat#KEY_OPERATING_RATE operating rate} in frames per * second. @@ -185,7 +185,6 @@ public final class VideoEncoderSettings { @CanIgnoreReturnValue @VisibleForTesting public Builder setEncoderPerformanceParameters(int operatingRate, int priority) { - checkArgument((operatingRate == RATE_UNSET) == (priority == RATE_UNSET)); this.operatingRate = operatingRate; this.priority = priority; return this; 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 100ad747df..cf92f68d50 100644 --- a/libraries/transformer/src/test/java/androidx/media3/transformer/DefaultEncoderFactoryTest.java +++ b/libraries/transformer/src/test/java/androidx/media3/transformer/DefaultEncoderFactoryTest.java @@ -234,7 +234,7 @@ public class DefaultEncoderFactoryTest { @Config(sdk = 29) @Test public void - createForVideoEncoding_withH264Encoding_configuresEncoderWithCorrectPerformanceSettings() + createForVideoEncoding_withH264EncodingOnApi31_configuresEncoderWithCorrectPerformanceSettings() throws Exception { Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30); Codec videoEncoder = @@ -248,7 +248,79 @@ public class DefaultEncoderFactoryTest { assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_PRIORITY)).isTrue(); assertThat(configurationMediaFormat.getInteger(MediaFormat.KEY_PRIORITY)).isEqualTo(1); assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_OPERATING_RATE)).isTrue(); - assertThat(configurationMediaFormat.getInteger(MediaFormat.KEY_OPERATING_RATE)).isEqualTo(1000); + assertThat(configurationMediaFormat.getInteger(MediaFormat.KEY_OPERATING_RATE)) + .isEqualTo(Integer.MAX_VALUE); + } + + @Config(sdk = 31) + @Test + public void + createForVideoEncoding_withH264EncodingOnApi29AndConservativeDefault_configuresEncoderWithCorrectPerformanceSettings() + throws Exception { + Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30); + Codec videoEncoder = + new DefaultEncoderFactory.Builder(context) + .setRequestedVideoEncoderSettings( + new VideoEncoderSettings.Builder() + .setEncoderPerformanceParameters(/* operatingRate= */ -1, /* priority= */ 1) + .build()) + .build() + .createForVideoEncoding(requestedVideoFormat); + + assertThat(videoEncoder).isInstanceOf(DefaultCodec.class); + MediaFormat configurationMediaFormat = + ((DefaultCodec) videoEncoder).getConfigurationMediaFormat(); + assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_PRIORITY)).isTrue(); + assertThat(configurationMediaFormat.getInteger(MediaFormat.KEY_PRIORITY)).isEqualTo(1); + assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_OPERATING_RATE)).isTrue(); + assertThat(configurationMediaFormat.getInteger(MediaFormat.KEY_OPERATING_RATE)).isEqualTo(-1); + } + + @Config(sdk = 31) + @Test + public void + createForVideoEncoding_withOperatingRateUnset_configuresEncoderWithCorrectPerformanceSettings() + throws Exception { + Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30); + Codec videoEncoder = + new DefaultEncoderFactory.Builder(context) + .setRequestedVideoEncoderSettings( + new VideoEncoderSettings.Builder() + .setEncoderPerformanceParameters( + /* operatingRate= */ VideoEncoderSettings.RATE_UNSET, /* priority= */ 1) + .build()) + .build() + .createForVideoEncoding(requestedVideoFormat); + + assertThat(videoEncoder).isInstanceOf(DefaultCodec.class); + MediaFormat configurationMediaFormat = + ((DefaultCodec) videoEncoder).getConfigurationMediaFormat(); + assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_PRIORITY)).isTrue(); + assertThat(configurationMediaFormat.getInteger(MediaFormat.KEY_PRIORITY)).isEqualTo(1); + assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_OPERATING_RATE)).isFalse(); + } + + @Config(sdk = 31) + @Test + public void + createForVideoEncoding_withOperatingRatePriorityUnset_configuresEncoderWithCorrectPerformanceSettings() + throws Exception { + Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30); + Codec videoEncoder = + new DefaultEncoderFactory.Builder(context) + .setRequestedVideoEncoderSettings( + new VideoEncoderSettings.Builder() + .setEncoderPerformanceParameters( + VideoEncoderSettings.RATE_UNSET, VideoEncoderSettings.RATE_UNSET) + .build()) + .build() + .createForVideoEncoding(requestedVideoFormat); + + assertThat(videoEncoder).isInstanceOf(DefaultCodec.class); + MediaFormat configurationMediaFormat = + ((DefaultCodec) videoEncoder).getConfigurationMediaFormat(); + assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_PRIORITY)).isFalse(); + assertThat(configurationMediaFormat.containsKey(MediaFormat.KEY_OPERATING_RATE)).isFalse(); } @Test