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:
claincly 2022-02-07 21:57:34 +00:00 committed by Ian Baker
parent fab5dfa156
commit 31aa9d458d
2 changed files with 39 additions and 28 deletions

View File

@ -18,6 +18,7 @@ package androidx.media3.transformer;
import static androidx.media3.common.util.Assertions.checkArgument; import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull; 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.Assertions.checkStateNotNull;
import static androidx.media3.common.util.Util.SDK_INT; import static androidx.media3.common.util.Util.SDK_INT;
import static androidx.media3.transformer.CodecFactoryUtil.createCodec; import static androidx.media3.transformer.CodecFactoryUtil.createCodec;
@ -151,30 +152,44 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
// Applying suggested profile/level settings from // Applying suggested profile/level settings from
// https://developer.android.com/guide/topics/media/sharing-video#b-frames_and_encoding_profiles // https://developer.android.com/guide/topics/media/sharing-video#b-frames_and_encoding_profiles
if (Util.SDK_INT >= 29) { if (Util.SDK_INT >= 29) {
if (EncoderUtil.isProfileLevelSupported( int supportedEncodingLevel =
encoderInfo, EncoderUtil.findHighestSupportedEncodingLevel(
mimeType, encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, if (supportedEncodingLevel != EncoderUtil.LEVEL_UNSET) {
EncoderUtil.LEVEL_UNSET)) {
// Use the highest supported profile and use B-frames. // Use the highest supported profile and use B-frames.
mediaFormat.setInteger( mediaFormat.setInteger(
MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel);
mediaFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 1); mediaFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, 1);
} }
} else if (Util.SDK_INT >= 26) { } else if (Util.SDK_INT >= 26) {
if (EncoderUtil.isProfileLevelSupported( int supportedEncodingLevel =
encoderInfo, EncoderUtil.findHighestSupportedEncodingLevel(
mimeType, encoderInfo, mimeType, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh);
MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, if (supportedEncodingLevel != EncoderUtil.LEVEL_UNSET) {
EncoderUtil.LEVEL_UNSET)) { // Use the highest-supported profile, but disable the generation of B-frames using
// Use the highest-supported profile, but disable the generation of B-frames. This // MediaFormat.KEY_LATENCY. This accommodates some limitations in the MediaMuxer in these
// accommodates some limitations in the MediaMuxer in these system versions. // system versions.
mediaFormat.setInteger( mediaFormat.setInteger(
MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileHigh); 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); 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 { } 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.setInteger(
MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline);
} }
@ -270,11 +285,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
@Nullable String codecs = null; @Nullable String codecs = null;
if (profileLevel != null if (profileLevel != null
&& requestedFormat.sampleMimeType.equals(mimeType) && requestedFormat.sampleMimeType.equals(mimeType)
&& EncoderUtil.isProfileLevelSupported( && profileLevel.second
pickedEncoder, <= EncoderUtil.findHighestSupportedEncodingLevel(
mimeType, pickedEncoder, mimeType, /* profile= */ profileLevel.first)) {
/* profile= */ profileLevel.first,
/* level= */ profileLevel.second)) {
codecs = requestedFormat.codecs; codecs = requestedFormat.codecs;
} }

View File

@ -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 encoderInfo The {@link MediaCodecInfo encoderInfo}.
* @param mimeType The {@link MimeTypes MIME type}. * @param mimeType The {@link MimeTypes MIME type}.
* @param profile The encoding profile. * @param profile The encoding profile.
* @param level The encoding level, specify {@link #LEVEL_UNSET} if checking whether the encoder * @return The highest supported encoding level, as documented in {@link
* supports a specific profile. * MediaCodecInfo.CodecProfileLevel}, or {@link #LEVEL_UNSET} if the profile is not supported.
* @return Whether the profile and level (if set) is supported by the encoder.
*/ */
public static boolean isProfileLevelSupported( public static int findHighestSupportedEncodingLevel(
MediaCodecInfo encoderInfo, String mimeType, int profile, int level) { MediaCodecInfo encoderInfo, String mimeType, int profile) {
// TODO(b/214964116): Merge into MediaCodecUtil. // TODO(b/214964116): Merge into MediaCodecUtil.
MediaCodecInfo.CodecProfileLevel[] profileLevels = MediaCodecInfo.CodecProfileLevel[] profileLevels =
encoderInfo.getCapabilitiesForType(mimeType).profileLevels; encoderInfo.getCapabilitiesForType(mimeType).profileLevels;
for (MediaCodecInfo.CodecProfileLevel profileLevel : profileLevels) { for (MediaCodecInfo.CodecProfileLevel profileLevel : profileLevels) {
if (profileLevel.profile == profile if (profileLevel.profile == profile) {
&& (level == LEVEL_UNSET || profileLevel.level == level)) { return profileLevel.level;
return true;
} }
} }
return false; return LEVEL_UNSET;
} }
/** /**