Fix profile-level setting.
On some old devices, the encoding level needs to be set with the encoding profile, but not on newer devices. The profile/level override is applied by following https://developer.android.com/guide/topics/media/sharing-video PiperOrigin-RevId: 427008536
This commit is contained in:
parent
fab5dfa156
commit
31aa9d458d
@ -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;
|
||||
@ -151,30 +152,44 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
// Applying suggested profile/level settings from
|
||||
// https://developer.android.com/guide/topics/media/sharing-video#b-frames_and_encoding_profiles
|
||||
if (Util.SDK_INT >= 29) {
|
||||
if (EncoderUtil.isProfileLevelSupported(
|
||||
encoderInfo,
|
||||
mimeType,
|
||||
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
|
||||
EncoderUtil.LEVEL_UNSET)) {
|
||||
int supportedEncodingLevel =
|
||||
EncoderUtil.findHighestSupportedEncodingLevel(
|
||||
encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
|
||||
if (supportedEncodingLevel != EncoderUtil.LEVEL_UNSET) {
|
||||
// Use the highest supported profile and use B-frames.
|
||||
mediaFormat.setInteger(
|
||||
MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 1);
|
||||
}
|
||||
} else if (Util.SDK_INT >= 26) {
|
||||
if (EncoderUtil.isProfileLevelSupported(
|
||||
encoderInfo,
|
||||
mimeType,
|
||||
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh,
|
||||
EncoderUtil.LEVEL_UNSET)) {
|
||||
// Use the highest-supported profile, but disable the generation of B-frames. This
|
||||
// accommodates some limitations in the MediaMuxer in these system versions.
|
||||
int supportedEncodingLevel =
|
||||
EncoderUtil.findHighestSupportedEncodingLevel(
|
||||
encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
|
||||
if (supportedEncodingLevel != EncoderUtil.LEVEL_UNSET) {
|
||||
// Use the highest-supported profile, but disable the generation of B-frames using
|
||||
// MediaFormat.KEY_LATENCY. This accommodates some limitations in the MediaMuxer in these
|
||||
// system versions.
|
||||
mediaFormat.setInteger(
|
||||
MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel);
|
||||
// TODO(b/210593256): Set KEY_LATENCY to 2 to enable B-frame production after switching to
|
||||
// in-app muxing.
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LATENCY, 1);
|
||||
}
|
||||
} else if (Util.SDK_INT >= 23) {
|
||||
int supportedLevel =
|
||||
EncoderUtil.findHighestSupportedEncodingLevel(
|
||||
encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
|
||||
checkState(supportedLevel != EncoderUtil.LEVEL_UNSET);
|
||||
// Use the baseline profile for safest results, as encoding in baseline is required per
|
||||
// https://source.android.com/compatibility/5.0/android-5.0-cdd#5_2_video_encoding
|
||||
mediaFormat.setInteger(
|
||||
MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedLevel);
|
||||
} else {
|
||||
// Use the baseline profile for safest results.
|
||||
// Use the baseline profile for safest results, as encoding in baseline is required per
|
||||
// https://source.android.com/compatibility/5.0/android-5.0-cdd#5_2_video_encoding
|
||||
mediaFormat.setInteger(
|
||||
MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
|
||||
}
|
||||
@ -270,11 +285,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
@Nullable String codecs = null;
|
||||
if (profileLevel != null
|
||||
&& requestedFormat.sampleMimeType.equals(mimeType)
|
||||
&& EncoderUtil.isProfileLevelSupported(
|
||||
pickedEncoder,
|
||||
mimeType,
|
||||
/* profile= */ profileLevel.first,
|
||||
/* level= */ profileLevel.second)) {
|
||||
&& profileLevel.second
|
||||
<= EncoderUtil.findHighestSupportedEncodingLevel(
|
||||
pickedEncoder, mimeType, /* profile= */ profileLevel.first)) {
|
||||
codecs = requestedFormat.codecs;
|
||||
}
|
||||
|
||||
|
@ -113,28 +113,26 @@ public final class EncoderUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the {@link MediaCodecInfo encoder} supports the given profile and level.
|
||||
* Finds the highest supported encoding level given a profile.
|
||||
*
|
||||
* @param encoderInfo The {@link MediaCodecInfo encoderInfo}.
|
||||
* @param mimeType The {@link MimeTypes MIME type}.
|
||||
* @param profile The encoding profile.
|
||||
* @param level The encoding level, specify {@link #LEVEL_UNSET} if checking whether the encoder
|
||||
* supports a specific profile.
|
||||
* @return Whether the profile and level (if set) is supported by the encoder.
|
||||
* @return The highest supported encoding level, as documented in {@link
|
||||
* MediaCodecInfo.CodecProfileLevel}, or {@link #LEVEL_UNSET} if the profile is not supported.
|
||||
*/
|
||||
public static boolean isProfileLevelSupported(
|
||||
MediaCodecInfo encoderInfo, String mimeType, int profile, int level) {
|
||||
public static int findHighestSupportedEncodingLevel(
|
||||
MediaCodecInfo encoderInfo, String mimeType, int profile) {
|
||||
// TODO(b/214964116): Merge into MediaCodecUtil.
|
||||
MediaCodecInfo.CodecProfileLevel[] profileLevels =
|
||||
encoderInfo.getCapabilitiesForType(mimeType).profileLevels;
|
||||
|
||||
for (MediaCodecInfo.CodecProfileLevel profileLevel : profileLevels) {
|
||||
if (profileLevel.profile == profile
|
||||
&& (level == LEVEL_UNSET || profileLevel.level == level)) {
|
||||
return true;
|
||||
if (profileLevel.profile == profile) {
|
||||
return profileLevel.level;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return LEVEL_UNSET;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user