Commit 12543a96 by claincly Committed by Ian Baker

Split method findEncoderWithClosestFormatSupport.

Add checking for bitrate mode settings.
Add logging to the encoder filtering.

PiperOrigin-RevId: 435662418
parent db0093f4
...@@ -30,6 +30,7 @@ import android.util.Size; ...@@ -30,6 +30,7 @@ import android.util.Size;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.Format; import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes; import androidx.media3.common.MimeTypes;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
...@@ -42,6 +43,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; ...@@ -42,6 +43,7 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@UnstableApi @UnstableApi
public final class DefaultEncoderFactory implements Codec.EncoderFactory { public final class DefaultEncoderFactory implements Codec.EncoderFactory {
private static final int DEFAULT_FRAME_RATE = 30; private static final int DEFAULT_FRAME_RATE = 30;
private static final String TAG = "DefaultEncoderFactory";
private final EncoderSelector videoEncoderSelector; private final EncoderSelector videoEncoderSelector;
private final VideoEncoderSettings requestedVideoEncoderSettings; private final VideoEncoderSettings requestedVideoEncoderSettings;
...@@ -63,15 +65,20 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -63,15 +65,20 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
/** /**
* Creates a new instance. * Creates a new instance.
* *
* <p>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.
*
* <p>Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding * <p>Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding
* quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link * quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link
* VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider implementing * VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider implementing
* {@link Codec.EncoderFactory} if such adjustments are unwanted. * {@link Codec.EncoderFactory} if such adjustments are unwanted.
* *
* <p>With format fallback enabled, and when the requested {@link Format} is not supported, {@code * <p>{@code requestedVideoEncoderSettings} should be handled with care because there is no
* DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link * fallback support for it. For example, using incompatible {@link VideoEncoderSettings#profile}
* Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME * and {@link VideoEncoderSettings#level} can cause codec configuration failure. Setting an
* type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level}, etc. * unsupported {@link VideoEncoderSettings#bitrateMode} may cause encoder instantiation failure.
* *
* @param videoEncoderSelector The {@link EncoderSelector}. * @param videoEncoderSelector The {@link EncoderSelector}.
* @param requestedVideoEncoderSettings The {@link VideoEncoderSettings}. * @param requestedVideoEncoderSettings The {@link VideoEncoderSettings}.
...@@ -187,7 +194,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -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.
* *
* <p>Returns the {@link MediaCodecInfo encoder} and the supported {@link Format} in a {@link * <p>Returns the {@link MediaCodecInfo encoder} and the supported {@link Format} in a {@link
* Pair}, or {@code null} if none is found. * Pair}, or {@code null} if none is found.
...@@ -215,27 +222,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -215,27 +222,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
return new VideoEncoderQueryResult( return new VideoEncoderQueryResult(
encodersForMimeType.get(0), requestedFormat, videoEncoderSettings); encodersForMimeType.get(0), requestedFormat, videoEncoderSettings);
} }
ImmutableList<MediaCodecInfo> filteredEncoders = ImmutableList<MediaCodecInfo> filteredEncoders =
filterEncoders( filterEncodersByResolution(
encodersForMimeType, encodersForMimeType, mimeType, requestedFormat.width, requestedFormat.height);
/* 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());
});
if (filteredEncoders.isEmpty()) { if (filteredEncoders.isEmpty()) {
return null; return null;
} }
// The supported resolution is the same for all remaining encoders. // The supported resolution is the same for all remaining encoders.
Size finalResolution = Size finalResolution =
checkNotNull( checkNotNull(
...@@ -247,14 +240,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -247,14 +240,13 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
? videoEncoderSettings.bitrate ? videoEncoderSettings.bitrate
: getSuggestedBitrate( : getSuggestedBitrate(
finalResolution.getWidth(), finalResolution.getHeight(), requestedFormat.frameRate); finalResolution.getWidth(), finalResolution.getHeight(), requestedFormat.frameRate);
filteredEncoders = filterEncodersByBitrate(filteredEncoders, mimeType, requestedBitrate);
if (filteredEncoders.isEmpty()) {
return null;
}
filteredEncoders = filteredEncoders =
filterEncoders( filterEncodersByBitrateMode(filteredEncoders, mimeType, videoEncoderSettings.bitrateMode);
filteredEncoders,
/* cost= */ (encoderInfo) -> {
int achievableBitrate =
EncoderUtil.getClosestSupportedBitrate(encoderInfo, mimeType, requestedBitrate);
return abs(achievableBitrate - requestedBitrate);
});
if (filteredEncoders.isEmpty()) { if (filteredEncoders.isEmpty()) {
return null; return null;
} }
...@@ -264,6 +256,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -264,6 +256,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
EncoderUtil.getClosestSupportedBitrate(pickedEncoder, mimeType, requestedBitrate); EncoderUtil.getClosestSupportedBitrate(pickedEncoder, mimeType, requestedBitrate);
VideoEncoderSettings.Builder supportedEncodingSettingBuilder = VideoEncoderSettings.Builder supportedEncodingSettingBuilder =
videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate); videoEncoderSettings.buildUpon().setBitrate(closestSupportedBitrate);
if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE if (videoEncoderSettings.profile == VideoEncoderSettings.NO_VALUE
|| videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE || videoEncoderSettings.level == VideoEncoderSettings.NO_VALUE
|| videoEncoderSettings.level || videoEncoderSettings.level
...@@ -285,6 +278,52 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -285,6 +278,52 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
pickedEncoder, supportedEncoderFormat, supportedEncodingSettingBuilder.build()); pickedEncoder, supportedEncoderFormat, supportedEncodingSettingBuilder.build());
} }
/** Returns a list of encoders that support the requested resolution most closely. */
private static ImmutableList<MediaCodecInfo> filterEncodersByResolution(
List<MediaCodecInfo> 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<MediaCodecInfo> filterEncodersByBitrate(
List<MediaCodecInfo> 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<MediaCodecInfo> filterEncodersByBitrateMode(
List<MediaCodecInfo> 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 { private static final class VideoEncoderQueryResult {
public final MediaCodecInfo encoder; public final MediaCodecInfo encoder;
public final Format supportedFormat; public final Format supportedFormat;
...@@ -376,7 +415,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -376,7 +415,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
* all encoders are {@link Integer#MAX_VALUE}. * all encoders are {@link Integer#MAX_VALUE}.
*/ */
private static ImmutableList<MediaCodecInfo> filterEncoders( private static ImmutableList<MediaCodecInfo> filterEncoders(
List<MediaCodecInfo> encoders, EncoderFallbackCost cost) { List<MediaCodecInfo> encoders, EncoderFallbackCost cost, String filterName) {
List<MediaCodecInfo> filteredEncoders = new ArrayList<>(encoders.size()); List<MediaCodecInfo> filteredEncoders = new ArrayList<>(encoders.size());
int minGap = Integer.MAX_VALUE; int minGap = Integer.MAX_VALUE;
...@@ -395,9 +434,24 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory { ...@@ -395,9 +434,24 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
filteredEncoders.add(encoderInfo); filteredEncoders.add(encoderInfo);
} }
} }
List<MediaCodecInfo> 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); return ImmutableList.copyOf(filteredEncoders);
} }
/**
* Finds a {@link MimeTypes MIME type} that is supported by the encoder and in the {@code
* allowedMimeTypes}.
*/
@Nullable @Nullable
private static String findFallbackMimeType( private static String findFallbackMimeType(
EncoderSelector encoderSelector, String requestedMimeType, List<String> allowedMimeTypes) { EncoderSelector encoderSelector, String requestedMimeType, List<String> allowedMimeTypes) {
......
...@@ -194,6 +194,15 @@ public final class EncoderUtil { ...@@ -194,6 +194,15 @@ public final class EncoderUtil {
.clamp(bitrate); .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. */ /** Checks if a {@link MediaCodecInfo codec} is hardware-accelerated. */
public static boolean isHardwareAccelerated(MediaCodecInfo encoderInfo, String mimeType) { public static boolean isHardwareAccelerated(MediaCodecInfo encoderInfo, String mimeType) {
// TODO(b/214964116): Merge into MediaCodecUtil. // TODO(b/214964116): Merge into MediaCodecUtil.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment