mirror of
https://github.com/samsonjs/media.git
synced 2026-04-08 11:45:51 +00:00
Split method findEncoderWithClosestFormatSupport.
Add checking for bitrate mode settings. Add logging to the encoder filtering. PiperOrigin-RevId: 435662418
This commit is contained in:
parent
db0093f4c8
commit
12543a9682
2 changed files with 93 additions and 30 deletions
|
|
@ -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.
|
||||
*
|
||||
* <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
|
||||
* 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.
|
||||
*
|
||||
* <p>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.
|
||||
* <p>{@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.
|
||||
*
|
||||
* <p>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<MediaCodecInfo> 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<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 {
|
||||
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<MediaCodecInfo> filterEncoders(
|
||||
List<MediaCodecInfo> encoders, EncoderFallbackCost cost) {
|
||||
List<MediaCodecInfo> encoders, EncoderFallbackCost cost, String filterName) {
|
||||
List<MediaCodecInfo> 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<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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<String> allowedMimeTypes) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue