Update DefaultEncoderFactory to set GOP parameters

If set on the requested VideoEncoderSettings, then
these parameters are passed into the MediaFormat
used by the DefaultEncoderFactory to configure the
underlying codec.

PiperOrigin-RevId: 751059914
This commit is contained in:
Googler 2025-04-24 11:07:57 -07:00 committed by Copybara-Service
parent 910b6ab884
commit dae5ebb820
2 changed files with 186 additions and 0 deletions

View File

@ -46,6 +46,7 @@ import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.CanIgnoreReturnValue; import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale;
import org.checkerframework.checker.nullness.qual.RequiresNonNull; import org.checkerframework.checker.nullness.qual.RequiresNonNull;
/** A default implementation of {@link Codec.EncoderFactory}. */ /** A default implementation of {@link Codec.EncoderFactory}. */
@ -395,6 +396,33 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
} }
} }
int maxBFrames = supportedVideoEncoderSettings.maxBFrames;
if (SDK_INT >= 29 && maxBFrames != VideoEncoderSettings.NO_VALUE) {
mediaFormat.setInteger(MediaFormat.KEY_MAX_B_FRAMES, maxBFrames);
}
int numNonBidirectionalTemporalLayers =
supportedVideoEncoderSettings.numNonBidirectionalTemporalLayers;
int numBidirectionalTemporalLayers =
supportedVideoEncoderSettings.numBidirectionalTemporalLayers;
if (SDK_INT >= 25 && numNonBidirectionalTemporalLayers >= 0) {
String temporalSchema;
if (numNonBidirectionalTemporalLayers == 0) {
temporalSchema = "none";
} else if (numBidirectionalTemporalLayers > 0) {
temporalSchema =
String.format(
Locale.ROOT,
"android.generic.%d+%d",
numNonBidirectionalTemporalLayers,
numBidirectionalTemporalLayers);
} else {
temporalSchema =
String.format(Locale.ROOT, "android.generic.%d", numNonBidirectionalTemporalLayers);
}
mediaFormat.setString(MediaFormat.KEY_TEMPORAL_LAYERING, temporalSchema);
}
return new DefaultCodec( return new DefaultCodec(
context, context,
encoderSupportedFormat, encoderSupportedFormat,

View File

@ -337,6 +337,164 @@ public class DefaultEncoderFactoryTest {
.isFalse(); .isFalse();
} }
@Test
@Config(sdk = 29)
public void createForVideoEncoding_withMaxBFrames_configuresEncoderWithMaxBFrames()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder().setMaxBFrames(3).build())
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(videoEncoder.getConfigurationMediaFormat().getInteger(MediaFormat.KEY_MAX_B_FRAMES))
.isEqualTo(3);
}
@Test
@Config(sdk = 23)
public void createForVideoEncoding_withMaxBFramesOnApi23_doesNotConfigureMaxBFrames()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder().setMaxBFrames(3).build())
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(videoEncoder.getConfigurationMediaFormat().containsKey(MediaFormat.KEY_MAX_B_FRAMES))
.isFalse();
}
@Test
@Config(sdk = 29)
public void createForVideoEncoding_withDefaultEncoderSettings_doesNotConfigureMaxBFrames()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(videoEncoder.getConfigurationMediaFormat().containsKey(MediaFormat.KEY_MAX_B_FRAMES))
.isFalse();
}
@Config(sdk = 29)
@Test
public void
createForVideoEncoding_withTemporalLayeringSchemaWithZeroLayers_configuresEncoderWithTemporalLayeringSchema()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder()
.setTemporalLayers(
/* numNonBidirectionalLayers= */ 0, /* numBidirectionalLayers= */ 0)
.build())
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(
videoEncoder.getConfigurationMediaFormat().getString(MediaFormat.KEY_TEMPORAL_LAYERING))
.isEqualTo("none");
}
@Config(sdk = 29)
@Test
public void
createForVideoEncoding_withTemporalLayeringSchemaWithoutBidirectionalLayers_configuresEncoderWithTemporalLayeringSchema()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder()
.setTemporalLayers(
/* numNonBidirectionalLayers= */ 1, /* numBidirectionalLayers= */ 0)
.build())
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(
videoEncoder.getConfigurationMediaFormat().getString(MediaFormat.KEY_TEMPORAL_LAYERING))
.isEqualTo("android.generic.1");
}
@Config(sdk = 29)
@Test
public void
createForVideoEncoding_withTemporalLayeringSchemaWithBidirectionalLayers_configuresEncoderWithTemporalLayeringSchema()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder()
.setTemporalLayers(
/* numNonBidirectionalLayers= */ 1, /* numBidirectionalLayers= */ 2)
.build())
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(
videoEncoder.getConfigurationMediaFormat().getString(MediaFormat.KEY_TEMPORAL_LAYERING))
.isEqualTo("android.generic.1+2");
}
@Config(sdk = 23)
@Test
public void
createForVideoEncoding_withTemporalLayeringSchemaOnApi23_doesNotConfigureTemporalLayeringSchema()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder()
.setTemporalLayers(
/* numNonBidirectionalLayers= */ 1, /* numBidirectionalLayers= */ 2)
.build())
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(
videoEncoder
.getConfigurationMediaFormat()
.containsKey(MediaFormat.KEY_TEMPORAL_LAYERING))
.isFalse();
}
@Config(sdk = 29)
@Test
public void
createForVideoEncoding_withDefaultEncoderSettings_doesNotConfigureTemporalLayeringSchema()
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
DefaultCodec videoEncoder =
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(requestedVideoFormat, /* logSessionId= */ null);
assertThat(
videoEncoder
.getConfigurationMediaFormat()
.containsKey(MediaFormat.KEY_TEMPORAL_LAYERING))
.isFalse();
}
@Test @Test
public void createForVideoEncoding_withNoAvailableEncoderFromEncoderSelector_throws() { public void createForVideoEncoding_withNoAvailableEncoderFromEncoderSelector_throws() {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30); Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);