Support setting H.264 level
Try to follow recommendations in https://developer.android.com/media/optimize/sharing more closely For maximum compatibility the H.264 level should be less than or equal to 4.1 PiperOrigin-RevId: 658755139
This commit is contained in:
parent
b09cea9e3a
commit
a79b80fcee
@ -15,7 +15,12 @@
|
||||
*/
|
||||
package androidx.media3.transformer.mh;
|
||||
|
||||
import static android.media.MediaCodecInfo.CodecProfileLevel.AVCLevel41;
|
||||
import static android.media.MediaCodecInfo.CodecProfileLevel.AVCProfileHigh;
|
||||
import static androidx.media3.common.util.Assertions.checkState;
|
||||
import static androidx.media3.common.util.MediaFormatUtil.createFormatFromMediaFormat;
|
||||
import static androidx.media3.common.util.Util.SDK_INT;
|
||||
import static androidx.media3.exoplayer.mediacodec.MediaCodecUtil.getCodecProfileAndLevel;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.FORCE_TRANSCODE_VIDEO_EFFECTS;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_4K60_PORTRAIT;
|
||||
@ -24,6 +29,7 @@ import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_BT2020_SDR;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_SEF;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_SEF_H265;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_WITH_INCREASING_TIMESTAMPS;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.MP4_TRIM_OPTIMIZATION_PIXEL;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.assumeFormatsSupported;
|
||||
import static androidx.media3.transformer.AndroidTestUtil.recordTestSkipped;
|
||||
@ -34,7 +40,9 @@ import static org.junit.Assume.assumeFalse;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.MediaFormat;
|
||||
import android.net.Uri;
|
||||
import android.util.Pair;
|
||||
import androidx.media3.common.Effect;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.MediaItem;
|
||||
@ -42,6 +50,7 @@ import androidx.media3.common.MimeTypes;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.effect.Presentation;
|
||||
import androidx.media3.effect.ScaleAndRotateTransformation;
|
||||
import androidx.media3.exoplayer.MediaExtractorCompat;
|
||||
import androidx.media3.extractor.mp4.Mp4Extractor;
|
||||
import androidx.media3.extractor.text.DefaultSubtitleParserFactory;
|
||||
import androidx.media3.test.utils.FakeExtractorOutput;
|
||||
@ -438,4 +447,43 @@ public class ExportTest {
|
||||
int inputVideoLevel = 41;
|
||||
assertThat((int) sps[spsLevelIndex]).isAtLeast(inputVideoLevel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void export_setEncodingProfileLevel_changesProfileAndLevel() throws Exception {
|
||||
assumeTrue(
|
||||
"Android encoding guidelines recommend H.264 baseline profile prior to API 25",
|
||||
Util.SDK_INT >= 25);
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
Transformer transformer =
|
||||
new Transformer.Builder(context)
|
||||
.setEncoderFactory(
|
||||
new ForceEncodeEncoderFactory(
|
||||
new DefaultEncoderFactory.Builder(context)
|
||||
.setRequestedVideoEncoderSettings(
|
||||
new VideoEncoderSettings.Builder()
|
||||
.setEncodingProfileLevel(AVCProfileHigh, AVCLevel41)
|
||||
.build())
|
||||
.build()))
|
||||
.build();
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder()
|
||||
.setUri(MP4_ASSET_WITH_INCREASING_TIMESTAMPS_320W_240H_15S.uri)
|
||||
.build();
|
||||
EditedMediaItem editedMediaItem =
|
||||
new EditedMediaItem.Builder(mediaItem).setRemoveAudio(true).build();
|
||||
|
||||
ExportTestResult result =
|
||||
new TransformerAndroidTestRunner.Builder(context, transformer)
|
||||
.build()
|
||||
.run(testId, editedMediaItem);
|
||||
|
||||
MediaExtractorCompat mediaExtractor = new MediaExtractorCompat(context);
|
||||
mediaExtractor.setDataSource(Uri.parse(result.filePath), 0);
|
||||
checkState(mediaExtractor.getTrackCount() == 1);
|
||||
MediaFormat mediaFormat = mediaExtractor.getTrackFormat(0);
|
||||
Format format = createFormatFromMediaFormat(mediaFormat);
|
||||
Pair<Integer, Integer> profileAndLevel = getCodecProfileAndLevel(format);
|
||||
assertThat(profileAndLevel.first).isAtMost(AVCProfileHigh);
|
||||
assertThat(profileAndLevel.second).isAtMost(AVCLevel41);
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +308,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
|
||||
if (supportedVideoEncoderSettings.profile != VideoEncoderSettings.NO_VALUE
|
||||
&& supportedVideoEncoderSettings.level != VideoEncoderSettings.NO_VALUE
|
||||
&& Util.SDK_INT >= 23) {
|
||||
&& Util.SDK_INT >= 24) {
|
||||
// For API levels below 24, setting profile and level can lead to failures in MediaCodec
|
||||
// configuration. The encoder selects the profile/level when we don't set them.
|
||||
// Set profile and level at the same time to maximize compatibility, or the encoder will pick
|
||||
// the values.
|
||||
mediaFormat.setInteger(MediaFormat.KEY_PROFILE, supportedVideoEncoderSettings.profile);
|
||||
@ -586,9 +588,11 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* Applying suggested profile/level settings from
|
||||
* Applying suggested profile settings from
|
||||
* https://developer.android.com/media/optimize/sharing#b-frames_and_encoding_profiles
|
||||
*
|
||||
* <p>Sets H.264 level only if it wasn't set previously.
|
||||
*
|
||||
* <p>The adjustment is applied in-place to {@code mediaFormat}.
|
||||
*/
|
||||
private static void adjustMediaFormatForH264EncoderSettings(
|
||||
@ -614,7 +618,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
// Use the highest supported profile. Don't configure B-frames, because it doesn't work on
|
||||
// some devices.
|
||||
mediaFormat.setInteger(MediaFormat.KEY_PROFILE, expectedEncodingProfile);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel);
|
||||
if (!mediaFormat.containsKey(MediaFormat.KEY_LEVEL)) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel);
|
||||
}
|
||||
}
|
||||
} else if (Util.SDK_INT >= 26 && !deviceNeedsNoH264HighProfileWorkaround()) {
|
||||
int expectedEncodingProfile = MediaCodecInfo.CodecProfileLevel.AVCProfileHigh;
|
||||
@ -626,7 +632,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
// MediaFormat.KEY_LATENCY. This accommodates some limitations in the MediaMuxer in these
|
||||
// system versions.
|
||||
mediaFormat.setInteger(MediaFormat.KEY_PROFILE, expectedEncodingProfile);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel);
|
||||
if (!mediaFormat.containsKey(MediaFormat.KEY_LEVEL)) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedEncodingLevel);
|
||||
}
|
||||
// TODO(b/210593256): Set KEY_LATENCY to 2 to enable B-frame production after in-app muxing
|
||||
// is the default and it supports B-frames.
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LATENCY, 1);
|
||||
@ -640,7 +648,9 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
|
||||
// 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, expectedEncodingProfile);
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedLevel);
|
||||
if (!mediaFormat.containsKey(MediaFormat.KEY_LEVEL)) {
|
||||
mediaFormat.setInteger(MediaFormat.KEY_LEVEL, supportedLevel);
|
||||
}
|
||||
}
|
||||
// For API levels below 24, setting profile and level can lead to failures in MediaCodec
|
||||
// configuration. The encoder selects the profile/level when we don't set them.
|
||||
|
@ -137,8 +137,8 @@ public final class VideoEncoderSettings {
|
||||
* <p>The value must be one of the values defined in {@link MediaCodecInfo.CodecProfileLevel},
|
||||
* or {@link #NO_VALUE}.
|
||||
*
|
||||
* <p>Profile and level settings will be ignored when using {@link DefaultEncoderFactory} and
|
||||
* encoding to H264.
|
||||
* <p>Profile settings will be ignored when using {@link DefaultEncoderFactory} and encoding to
|
||||
* H264.
|
||||
*
|
||||
* @param encodingProfile The {@link VideoEncoderSettings#profile}.
|
||||
* @param encodingLevel The {@link VideoEncoderSettings#level}.
|
||||
|
Loading…
x
Reference in New Issue
Block a user