Add a Builder for DefaultEncoderFactory.

PiperOrigin-RevId: 456728032
This commit is contained in:
samrobinson 2022-06-23 11:37:19 +01:00 committed by Ian Baker
parent 74d61bbffb
commit 352967f656
10 changed files with 124 additions and 68 deletions

View File

@ -42,7 +42,6 @@ import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.util.DebugTextViewHelper;
import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.EncoderSelector;
import androidx.media3.transformer.GlEffect;
import androidx.media3.transformer.GlTextureProcessor;
import androidx.media3.transformer.ProgressHolder;
@ -260,10 +259,9 @@ public final class TransformerActivity extends AppCompatActivity {
.setRemoveAudio(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_AUDIO))
.setRemoveVideo(bundle.getBoolean(ConfigurationActivity.SHOULD_REMOVE_VIDEO))
.setEncoderFactory(
new DefaultEncoderFactory(
/* context= */ this,
EncoderSelector.DEFAULT,
/* enableFallback= */ bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK)));
new DefaultEncoderFactory.Builder(this.getApplicationContext())
.setEnableFallback(bundle.getBoolean(ConfigurationActivity.ENABLE_FALLBACK))
.build());
ImmutableList.Builder<GlEffect> effects = new ImmutableList.Builder<>();
@Nullable

View File

@ -225,7 +225,7 @@ public final class AndroidTestUtil {
/** Creates an instance that wraps {@link DefaultEncoderFactory}. */
public ForceEncodeEncoderFactory(Context context) {
encoderFactory = new DefaultEncoderFactory(context);
encoderFactory = new DefaultEncoderFactory.Builder(context).build();
}
/**

View File

@ -46,8 +46,7 @@ public class TransformerEndToEndTest {
.setTransformationRequest(
new TransformationRequest.Builder().setResolution(480).build())
.setEncoderFactory(
new DefaultEncoderFactory(
context, EncoderSelector.DEFAULT, /* enableFallback= */ false))
new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build())
.build();
// Result of the following command:
// ffprobe -count_frames -select_streams v:0 -show_entries stream=nb_read_frames sample.mp4
@ -71,8 +70,7 @@ public class TransformerEndToEndTest {
.setTransformationRequest(
new TransformationRequest.Builder().setResolution(480).build())
.setEncoderFactory(
new DefaultEncoderFactory(
context, EncoderSelector.DEFAULT, /* enableFallback= */ false))
new DefaultEncoderFactory.Builder(context).setEnableFallback(false).build())
.build();
long expectedDurationMs = 967;
@ -138,7 +136,7 @@ public class TransformerEndToEndTest {
private final Codec.EncoderFactory encoderFactory;
public VideoUnsupportedEncoderFactory(Context context) {
encoderFactory = new DefaultEncoderFactory(context);
encoderFactory = new DefaultEncoderFactory.Builder(context).build();
}
@Override

View File

@ -31,7 +31,6 @@ import androidx.media3.common.util.Util;
import androidx.media3.transformer.AndroidTestUtil;
import androidx.media3.transformer.AndroidTestUtil.ForceEncodeEncoderFactory;
import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.EncoderSelector;
import androidx.media3.transformer.TransformationRequest;
import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner;
@ -81,11 +80,10 @@ public class TransformationTest {
.setRemoveAudio(true)
.setEncoderFactory(
new ForceEncodeEncoderFactory(
/* wrappedEncoderFactory= */ new DefaultEncoderFactory(
context,
EncoderSelector.DEFAULT,
new VideoEncoderSettings.Builder().setBitrate(5_000_000).build(),
/* enableFallback= */ true)))
/* wrappedEncoderFactory= */ new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder().setBitrate(5_000_000).build())
.build()))
.build();
new TransformerAndroidTestRunner.Builder(context, transformer)
.setMaybeCalculateSsim(true)

View File

@ -24,7 +24,6 @@ import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Assertions;
import androidx.media3.transformer.AndroidTestUtil;
import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.EncoderSelector;
import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.media3.transformer.VideoEncoderSettings;
@ -117,14 +116,14 @@ public class BitrateAnalysisTest {
.setRemoveAudio(true)
.setEncoderFactory(
new AndroidTestUtil.ForceEncodeEncoderFactory(
/* wrappedEncoderFactory= */ new DefaultEncoderFactory(
context,
EncoderSelector.DEFAULT,
new VideoEncoderSettings.Builder()
.setBitrate(bitrate)
.setBitrateMode(bitrateMode)
.build(),
/* enableFallback= */ false)))
/* wrappedEncoderFactory= */ new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder()
.setBitrate(bitrate)
.setBitrateMode(bitrateMode)
.build())
.setEnableFallback(false)
.build()))
.build();
new TransformerAndroidTestRunner.Builder(context, transformer)

View File

@ -26,7 +26,6 @@ import androidx.media3.common.MediaItem;
import androidx.media3.common.util.Util;
import androidx.media3.transformer.AndroidTestUtil;
import androidx.media3.transformer.DefaultEncoderFactory;
import androidx.media3.transformer.EncoderSelector;
import androidx.media3.transformer.Transformer;
import androidx.media3.transformer.TransformerAndroidTestRunner;
import androidx.media3.transformer.VideoEncoderSettings;
@ -127,13 +126,13 @@ public class EncoderPerformanceAnalysisTest {
.setRemoveAudio(true)
.setEncoderFactory(
new AndroidTestUtil.ForceEncodeEncoderFactory(
/* wrappedEncoderFactory= */ new DefaultEncoderFactory(
context,
EncoderSelector.DEFAULT,
new VideoEncoderSettings.Builder()
.setEncoderPerformanceParameters(operatingRate, priority)
.build(),
/* enableFallback= */ false)))
/* wrappedEncoderFactory= */ new DefaultEncoderFactory.Builder(context)
.setRequestedVideoEncoderSettings(
new VideoEncoderSettings.Builder()
.setEncoderPerformanceParameters(operatingRate, priority)
.build())
.setEnableFallback(false)
.build()))
.build();
new TransformerAndroidTestRunner.Builder(context, transformer)

View File

@ -47,48 +47,109 @@ public final class DefaultEncoderFactory implements Codec.EncoderFactory {
private static final int DEFAULT_FRAME_RATE = 30;
private static final String TAG = "DefaultEncoderFactory";
/** A builder for {@link DefaultEncoderFactory} instances. */
public static final class Builder {
private final Context context;
@Nullable private EncoderSelector encoderSelector;
@Nullable private VideoEncoderSettings requestedVideoEncoderSettings;
private boolean enableFallback;
/** Creates a new {@link Builder}. */
public Builder(Context context) {
this.context = context;
this.enableFallback = true;
}
/**
* Sets the video {@link EncoderSelector}.
*
* <p>The default value is {@link EncoderSelector#DEFAULT}.
*/
public Builder setVideoEncoderSelector(EncoderSelector encoderSelector) {
this.encoderSelector = encoderSelector;
return this;
}
/**
* Sets the requested {@link VideoEncoderSettings}.
*
* <p>Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding
* quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link
* VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider
* implementing {@link Codec.EncoderFactory} if such adjustments are unwanted.
*
* <p>{@code requestedVideoEncoderSettings} should be handled with care because there is no
* fallback support for it. For example, using incompatible {@link VideoEncoderSettings#profile}
* and {@link VideoEncoderSettings#level} can cause codec configuration failure. Setting an
* unsupported {@link VideoEncoderSettings#bitrateMode} may cause encoder instantiation failure.
*
* <p>The default value is {@link VideoEncoderSettings#DEFAULT}.
*/
public Builder setRequestedVideoEncoderSettings(
VideoEncoderSettings requestedVideoEncoderSettings) {
this.requestedVideoEncoderSettings = requestedVideoEncoderSettings;
return this;
}
/**
* Sets whether the encoder can fallback.
*
* <p>With format fallback enabled, when the requested {@link Format} is not supported, {@code
* DefaultEncoderFactory} finds a format that is supported by the device and configures the
* {@link Codec} with it. The fallback process may change the requested {@link
* Format#sampleMimeType MIME type}, resolution, {@link Format#bitrate bitrate}, {@link
* Format#codecs profile/level} etc.
*
* <p>The default value is {@code true}.
*/
public Builder setEnableFallback(boolean enableFallback) {
this.enableFallback = enableFallback;
return this;
}
/** Creates an instance of {@link DefaultEncoderFactory}, using defaults if values are unset. */
@SuppressWarnings("deprecation")
public DefaultEncoderFactory build() {
if (encoderSelector == null) {
encoderSelector = EncoderSelector.DEFAULT;
}
if (requestedVideoEncoderSettings == null) {
requestedVideoEncoderSettings = VideoEncoderSettings.DEFAULT;
}
return new DefaultEncoderFactory(
context, encoderSelector, requestedVideoEncoderSettings, enableFallback);
}
}
private final Context context;
private final EncoderSelector videoEncoderSelector;
private final VideoEncoderSettings requestedVideoEncoderSettings;
private final boolean enableFallback;
/**
* Creates a new instance using the {@link EncoderSelector#DEFAULT default encoder selector}, a
* default {@link VideoEncoderSettings}, and with format fallback enabled.
* @deprecated Use {@link Builder} instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public DefaultEncoderFactory(Context context) {
this(context, EncoderSelector.DEFAULT, /* enableFallback= */ true);
}
/** Creates a new instance using a default {@link VideoEncoderSettings}. */
/**
* @deprecated Use {@link Builder} instead.
*/
@Deprecated
@SuppressWarnings("deprecation")
public DefaultEncoderFactory(
Context context, EncoderSelector videoEncoderSelector, boolean enableFallback) {
this(context, videoEncoderSelector, VideoEncoderSettings.DEFAULT, enableFallback);
}
/**
* Creates a new instance.
*
* <p>With format fallback enabled, when the requested {@link Format} is not supported, {@code
* DefaultEncoderFactory} finds a format that is supported by the device and configures the {@link
* Codec} with it. The fallback process may change the requested {@link Format#sampleMimeType MIME
* type}, resolution, {@link Format#bitrate bitrate}, {@link Format#codecs profile/level} etc.
*
* <p>Values in {@code requestedVideoEncoderSettings} could be adjusted to improve encoding
* quality and/or reduce failures. Specifically, {@link VideoEncoderSettings#profile} and {@link
* VideoEncoderSettings#level} are ignored for {@link MimeTypes#VIDEO_H264}. Consider implementing
* {@link Codec.EncoderFactory} if such adjustments are unwanted.
*
* <p>{@code requestedVideoEncoderSettings} should be handled with care because there is no
* fallback support for it. For example, using incompatible {@link VideoEncoderSettings#profile}
* and {@link VideoEncoderSettings#level} can cause codec configuration failure. Setting an
* unsupported {@link VideoEncoderSettings#bitrateMode} may cause encoder instantiation failure.
*
* @param context The {@link Context}.
* @param videoEncoderSelector The {@link EncoderSelector}.
* @param requestedVideoEncoderSettings The {@link VideoEncoderSettings}.
* @param enableFallback Whether to enable fallback.
* @deprecated Use {@link Builder} instead.
*/
@Deprecated
public DefaultEncoderFactory(
Context context,
EncoderSelector videoEncoderSelector,

View File

@ -122,7 +122,7 @@ public final class Transformer {
looper = Util.getCurrentOrMainLooper();
clock = Clock.DEFAULT;
listeners = new ListenerSet<>(looper, clock, (listener, flags) -> {});
encoderFactory = new DefaultEncoderFactory(this.context);
encoderFactory = new DefaultEncoderFactory.Builder(this.context).build();
decoderFactory = new DefaultDecoderFactory(this.context);
debugViewProvider = DebugViewProvider.NONE;
containerMimeType = MimeTypes.VIDEO_MP4;

View File

@ -68,7 +68,8 @@ public class DefaultEncoderFactoryTest {
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 1920, 1080, 30);
Format actualVideoFormat =
new DefaultEncoderFactory(context)
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(
requestedVideoFormat,
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
@ -86,7 +87,8 @@ public class DefaultEncoderFactoryTest {
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H265, 1920, 1080, 30);
Format actualVideoFormat =
new DefaultEncoderFactory(context)
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(
requestedVideoFormat,
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
@ -104,7 +106,8 @@ public class DefaultEncoderFactoryTest {
throws Exception {
Format requestedVideoFormat = createVideoFormat(MimeTypes.VIDEO_H264, 3840, 2160, 60);
Format actualVideoFormat =
new DefaultEncoderFactory(context)
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(
requestedVideoFormat,
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264))
@ -122,7 +125,8 @@ public class DefaultEncoderFactoryTest {
assertThrows(
TransformationException.class,
() ->
new DefaultEncoderFactory(context)
new DefaultEncoderFactory.Builder(context)
.build()
.createForVideoEncoding(
requestedVideoFormat,
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H265)));
@ -138,10 +142,9 @@ public class DefaultEncoderFactoryTest {
assertThrows(
TransformationException.class,
() ->
new DefaultEncoderFactory(
context,
/* videoEncoderSelector= */ mimeType -> ImmutableList.of(),
/* enableFallback= */ true)
new DefaultEncoderFactory.Builder(context)
.setVideoEncoderSelector(mimeType -> ImmutableList.of())
.build()
.createForVideoEncoding(
requestedVideoFormat,
/* allowedMimeTypes= */ ImmutableList.of(MimeTypes.VIDEO_H264)));

View File

@ -751,7 +751,7 @@ public final class TransformerEndToEndTest {
.setClock(clock)
.setMuxerFactory(new TestMuxerFactory())
.setEncoderFactory(
new DefaultEncoderFactory(context, EncoderSelector.DEFAULT, enableFallback));
new DefaultEncoderFactory.Builder(context).setEnableFallback(enableFallback).build());
}
private static void createEncodersAndDecoders() {