From 12543a968292f5477c9d705440165c96fcba0120 Mon Sep 17 00:00:00 2001 From: claincly Date: Fri, 18 Mar 2022 16:43:26 +0000 Subject: [PATCH] Split method findEncoderWithClosestFormatSupport. Add checking for bitrate mode settings. Add logging to the encoder filtering. PiperOrigin-RevId: 435662418 --- .../transformer/DefaultEncoderFactory.java | 114 +++++++++++++----- .../media3/transformer/EncoderUtil.java | 9 ++ 2 files changed, 93 insertions(+), 30 deletions(-) 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 94215a3a89..1beaeaeea1 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/DefaultEncoderFactory.java @@ -30,6 +30,7 @@ import android.util.Size; import androidx.annotation.Nullable; import androidx.media3.common.Format; import androidx.media3.common.MimeTypes; +import androidx.media3.common.util.Log; import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.Util; import com.google.common.collect.ImmutableList; @@ -42,6 +43,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; @UnstableApi public final class DefaultEncoderFactory implements Codec.EncoderFactory { private static final int DEFAULT_FRAME_RATE = 30; + private static final String TAG = "DefaultEncoderFactory"; private final EncoderSelector videoEncoderSelector; private final VideoEncoderSettings requestedVideoEncoderSettings; @@ -63,15 +65,20 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { /** * Creates a new instance. * + *

With format fallback enabled, when the requested {@link Format} is not supported, {@code + * DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link + * Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME + * type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level} etc. + * *

Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding * quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link * VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider implementing * {@link Codec.EncoderFactory} if such adjustments are unwanted. * - *

With format fallback enabled, and when the requested {@link Format} is not supported, {@code - * DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link - * Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME - * type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level}, etc. + *

{@code requestedVideoEncoderSettings} should be handled with care because there is no + * fallback support for it. For example, using incompatible {@link VideoEncoderSettings#profile} + * and {@link VideoEncoderSettings#level} can cause codec configuration failure. Setting an + * unsupported {@link VideoEncoderSettings#bitrateMode} may cause encoder instantiation failure. * * @param videoEncoderSelector The {@link EncoderSelector}. * @param requestedVideoEncoderSettings The {@link VideoEncoderSettings}. @@ -187,7 +194,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { } /** - * Finds a {@link MediaCodecInfo encoder} that supports the requested format most closely. + * Finds an {@link MediaCodecInfo encoder} that supports the requested format most closely. * *

Returns the {@link MediaCodecInfo encoder} and the supported {@link Format} in a {@link * Pair}, or {@code null} if none is found. @@ -215,27 +222,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { return new VideoEncoderQueryResult( encodersForMimeType.get(0), requestedFormat, videoEncoderSettings); } + ImmutableList filteredEncoders = - filterEncoders( - encodersForMimeType, - /* cost= */ (encoderInfo) -> { - @Nullable - Size closestSupportedResolution = - EncoderUtil.getSupportedResolution( - encoderInfo, mimeType, requestedFormat.width, requestedFormat.height); - if (closestSupportedResolution == null) { - // Drops encoder. - return Integer.MAX_VALUE; - } - return abs( - requestedFormat.width * requestedFormat.height - - closestSupportedResolution.getWidth() - * closestSupportedResolution.getHeight()); - }); + filterEncodersByResolution( + encodersForMimeType, mimeType, requestedFormat.width, requestedFormat.height); if (filteredEncoders.isEmpty()) { return null; } - // The supported resolution is the same for all remaining encoders. Size finalResolution = checkNotNull( @@ -247,14 +240,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ? videoEncoderSettings.bitrate : getSuggestedBitrate( finalResolution.getWidth(), finalResolution.getHeight(), requestedFormat.frameRate); + filteredEncoders = filterEncodersByBitrate(filteredEncoders, mimeType, requestedBitrate); + if (filteredEncoders.isEmpty()) { + return null; + } + filteredEncoders = - filterEncoders( - filteredEncoders, - /* cost= */ (encoderInfo) -> { - int achievableBitrate = - EncoderUtil.getClosestSupportedBitrate(encoderInfo, mimeType, requestedBitrate); - return abs(achievableBitrate - requestedBitrate); - }); + filterEncodersByBitrateMode(filteredEncoders, mimeType, videoEncoderSettings.bitrateMode); if (filteredEncoders.isEmpty()) { return null; } @@ -264,6 +256,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { EncoderUtil.getClosestSupportedBitrate(pickedEncoder, mimeType, requestedBitrate); VideoEncoderSettings.Builder supportedEncodingSettingBuilder = videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate); + if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE || videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE || videoEncoderSettings.level @@ -285,6 +278,52 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { pickedEncoder, supportedEncoderFormat, supportedEncodingSettingBuilder.build()); } + /** Returns a list of encoders that support the requested resolution most closely. */ + private static ImmutableList filterEncodersByResolution( + List encoders, String mimeType, int requestedWidth, int requestedHeight) { + return filterEncoders( + encoders, + /* cost= */ (encoderInfo) -> { + @Nullable + Size closestSupportedResolution = + EncoderUtil.getSupportedResolution( + encoderInfo, mimeType, requestedWidth, requestedHeight); + if (closestSupportedResolution == null) { + // Drops encoder. + return Integer.MAX_VALUE; + } + return abs( + requestedWidth * requestedHeight + - closestSupportedResolution.getWidth() * closestSupportedResolution.getHeight()); + }, + /* filterName= */ "resolution"); + } + + /** Returns a list of encoders that support the requested bitrate most closely. */ + private static ImmutableList filterEncodersByBitrate( + List encoders, String mimeType, int requestedBitrate) { + return filterEncoders( + encoders, + /* cost= */ (encoderInfo) -> { + int achievableBitrate = + EncoderUtil.getClosestSupportedBitrate(encoderInfo, mimeType, requestedBitrate); + return abs(achievableBitrate - requestedBitrate); + }, + /* filterName= */ "bitrate"); + } + + /** Returns a list of encoders that support the requested bitrate mode. */ + private static ImmutableList filterEncodersByBitrateMode( + List encoders, String mimeType, int requestedBitrateMode) { + return filterEncoders( + encoders, + /* cost= */ (encoderInfo) -> + EncoderUtil.isBitrateModeSupported(encoderInfo, mimeType, requestedBitrateMode) + ? 0 + : Integer.MAX_VALUE, // Drops encoder. + /* filterName= */ "bitrate mode"); + } + private static final class VideoEncoderQueryResult { public final MediaCodecInfo encoder; public final Format supportedFormat; @@ -376,7 +415,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { * all encoders are {@link Integer#MAX_VALUE}. */ private static ImmutableList filterEncoders( - List encoders, EncoderFallbackCost cost) { + List encoders, EncoderFallbackCost cost, String filterName) { List filteredEncoders = new ArrayList<>(encoders.size()); int minGap = Integer.MAX_VALUE; @@ -395,9 +434,24 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { filteredEncoders.add(encoderInfo); } } + + List removedEncoders = new ArrayList<>(encoders); + removedEncoders.removeAll(filteredEncoders); + StringBuilder stringBuilder = + new StringBuilder("Encoders removed for ").append(filterName).append(":\n"); + for (int i = 0; i < removedEncoders.size(); i++) { + MediaCodecInfo encoderInfo = removedEncoders.get(i); + stringBuilder.append(Util.formatInvariant(" %s\n", encoderInfo.getName())); + } + Log.d(TAG, stringBuilder.toString()); + return ImmutableList.copyOf(filteredEncoders); } + /** + * Finds a {@link MimeTypes MIME type} that is supported by the encoder and in the {@code + * allowedMimeTypes}. + */ @Nullable private static String findFallbackMimeType( EncoderSelector encoderSelector, String requestedMimeType, List allowedMimeTypes) { diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java index 1fee2a8fae..8dc0bafc36 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/EncoderUtil.java @@ -194,6 +194,15 @@ public final class EncoderUtil { .clamp(bitrate); } + /** Returns whether the bitrate mode is supported by the encoder. */ + public static boolean isBitrateModeSupported( + MediaCodecInfo encoderInfo, String mimeType, int bitrateMode) { + return encoderInfo + .getCapabilitiesForType(mimeType) + .getEncoderCapabilities() + .isBitrateModeSupported(bitrateMode); + } + /** Checks if a {@link MediaCodecInfo codec} is hardware-accelerated. */ public static boolean isHardwareAccelerated(MediaCodecInfo encoderInfo, String mimeType) { // TODO(b/214964116): Merge into MediaCodecUtil.