Improve MIME type fallback logic.
- The MIME type should ideally default to HEVC if there is an encoder for it. - Next, check if AVC is supported. - If there is no encoder for AVC, then we should pick an encoder in the list of existing encoders instead of abandoning the transformation. PiperOrigin-RevId: 425900638
This commit is contained in:
parent
0de79209c4
commit
7e873b5121
@ -18,6 +18,7 @@ package androidx.media3.transformer;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
import static androidx.media3.common.util.Util.SDK_INT;
|
||||
import static androidx.media3.transformer.CodecFactoryUtil.createCodec;
|
||||
@ -41,8 +42,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
||||
@UnstableApi
|
||||
public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
// TODO(b/214973843): Add option to disable fallback.
|
||||
// TODO(b/210591626): Fall back adaptively to H265 if possible.
|
||||
private static final String DEFAULT_FALLBACK_MIME_TYPE = MimeTypes.VIDEO_H264;
|
||||
private static final int DEFAULT_COLOR_FORMAT =
|
||||
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface;
|
||||
private static final int DEFAULT_FRAME_RATE = 60;
|
||||
@ -147,22 +146,15 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
@Nullable
|
||||
private static Pair<MediaCodecInfo, Format> findEncoderWithClosestFormatSupport(
|
||||
Format requestedFormat, EncoderSelector encoderSelector, List<String> allowedMimeTypes) {
|
||||
String mimeType = requestedFormat.sampleMimeType;
|
||||
|
||||
// TODO(b/210591626) Improve MIME type selection.
|
||||
List<MediaCodecInfo> encodersForMimeType = encoderSelector.selectEncoderInfos(mimeType);
|
||||
if (!allowedMimeTypes.contains(mimeType) || encodersForMimeType.isEmpty()) {
|
||||
mimeType =
|
||||
allowedMimeTypes.contains(DEFAULT_FALLBACK_MIME_TYPE)
|
||||
? DEFAULT_FALLBACK_MIME_TYPE
|
||||
: allowedMimeTypes.get(0);
|
||||
encodersForMimeType = encoderSelector.selectEncoderInfos(mimeType);
|
||||
if (encodersForMimeType.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@Nullable
|
||||
String mimeType =
|
||||
findFallbackMimeType(encoderSelector, requestedFormat.sampleMimeType, allowedMimeTypes);
|
||||
if (mimeType == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String finalMimeType = mimeType;
|
||||
List<MediaCodecInfo> encodersForMimeType = encoderSelector.selectEncoderInfos(mimeType);
|
||||
checkState(!encodersForMimeType.isEmpty());
|
||||
ImmutableList<MediaCodecInfo> filteredEncoders =
|
||||
filterEncoders(
|
||||
encodersForMimeType,
|
||||
@ -170,7 +162,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
@Nullable
|
||||
Pair<Integer, Integer> closestSupportedResolution =
|
||||
EncoderUtil.getClosestSupportedResolution(
|
||||
encoderInfo, finalMimeType, requestedFormat.width, requestedFormat.height);
|
||||
encoderInfo, mimeType, requestedFormat.width, requestedFormat.height);
|
||||
if (closestSupportedResolution == null) {
|
||||
// Drops encoder.
|
||||
return Integer.MAX_VALUE;
|
||||
@ -186,10 +178,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
Pair<Integer, Integer> finalResolution =
|
||||
checkNotNull(
|
||||
EncoderUtil.getClosestSupportedResolution(
|
||||
filteredEncoders.get(0),
|
||||
finalMimeType,
|
||||
requestedFormat.width,
|
||||
requestedFormat.height));
|
||||
filteredEncoders.get(0), mimeType, requestedFormat.width, requestedFormat.height));
|
||||
|
||||
int requestedBitrate =
|
||||
requestedFormat.averageBitrate == Format.NO_VALUE
|
||||
@ -205,8 +194,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
filteredEncoders,
|
||||
/* cost= */ (encoderInfo) -> {
|
||||
int achievableBitrate =
|
||||
EncoderUtil.getClosestSupportedBitrate(
|
||||
encoderInfo, finalMimeType, requestedBitrate);
|
||||
EncoderUtil.getClosestSupportedBitrate(encoderInfo, mimeType, requestedBitrate);
|
||||
return abs(achievableBitrate - requestedBitrate);
|
||||
});
|
||||
if (filteredEncoders.isEmpty()) {
|
||||
@ -218,10 +206,10 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
Pair<Integer, Integer> profileLevel = MediaCodecUtil.getCodecProfileAndLevel(requestedFormat);
|
||||
@Nullable String codecs = null;
|
||||
if (profileLevel != null
|
||||
&& requestedFormat.sampleMimeType.equals(finalMimeType)
|
||||
&& requestedFormat.sampleMimeType.equals(mimeType)
|
||||
&& EncoderUtil.isProfileLevelSupported(
|
||||
pickedEncoder,
|
||||
finalMimeType,
|
||||
mimeType,
|
||||
/* profile= */ profileLevel.first,
|
||||
/* level= */ profileLevel.second)) {
|
||||
codecs = requestedFormat.codecs;
|
||||
@ -230,7 +218,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
Format encoderSupportedFormat =
|
||||
requestedFormat
|
||||
.buildUpon()
|
||||
.setSampleMimeType(finalMimeType)
|
||||
.setSampleMimeType(mimeType)
|
||||
.setCodecs(codecs)
|
||||
.setWidth(finalResolution.first)
|
||||
.setHeight(finalResolution.second)
|
||||
@ -239,8 +227,7 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
? requestedFormat.frameRate
|
||||
: DEFAULT_FRAME_RATE)
|
||||
.setAverageBitrate(
|
||||
EncoderUtil.getClosestSupportedBitrate(
|
||||
pickedEncoder, finalMimeType, requestedBitrate))
|
||||
EncoderUtil.getClosestSupportedBitrate(pickedEncoder, mimeType, requestedBitrate))
|
||||
.build();
|
||||
return Pair.create(pickedEncoder, encoderSupportedFormat);
|
||||
}
|
||||
@ -279,6 +266,32 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
return ImmutableList.copyOf(filteredEncoders);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String findFallbackMimeType(
|
||||
EncoderSelector encoderSelector, String requestedMimeType, List<String> allowedMimeTypes) {
|
||||
if (mimeTypeIsSupported(encoderSelector, requestedMimeType, allowedMimeTypes)) {
|
||||
return requestedMimeType;
|
||||
} else if (mimeTypeIsSupported(encoderSelector, MimeTypes.VIDEO_H265, allowedMimeTypes)) {
|
||||
return MimeTypes.VIDEO_H265;
|
||||
} else if (mimeTypeIsSupported(encoderSelector, MimeTypes.VIDEO_H264, allowedMimeTypes)) {
|
||||
return MimeTypes.VIDEO_H264;
|
||||
} else {
|
||||
for (int i = 0; i < allowedMimeTypes.size(); i++) {
|
||||
String allowedMimeType = allowedMimeTypes.get(i);
|
||||
if (mimeTypeIsSupported(encoderSelector, allowedMimeType, allowedMimeTypes)) {
|
||||
return allowedMimeType;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean mimeTypeIsSupported(
|
||||
EncoderSelector encoderSelector, String mimeType, List<String> allowedMimeTypes) {
|
||||
return !encoderSelector.selectEncoderInfos(mimeType).isEmpty()
|
||||
&& allowedMimeTypes.contains(mimeType);
|
||||
}
|
||||
|
||||
/** Computes the video bit rate using the Kush Gauge. */
|
||||
private static int getSuggestedBitrate(int width, int height, float frameRate) {
|
||||
// TODO(b/210591626) Implement bitrate estimation.
|
||||
|
Loading…
x
Reference in New Issue
Block a user