diff --git a/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java b/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java index 547ad3770a..0f30174a89 100644 --- a/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java +++ b/demos/transformer/src/main/java/com/google/android/exoplayer2/transformerdemo/TransformerActivity.java @@ -39,7 +39,6 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.transformer.DefaultEncoderFactory; -import com.google.android.exoplayer2.transformer.EncoderSelector; import com.google.android.exoplayer2.transformer.GlEffect; import com.google.android.exoplayer2.transformer.GlTextureProcessor; import com.google.android.exoplayer2.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 effects = new ImmutableList.Builder<>(); @Nullable diff --git a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java index 397b5e1f39..7f49600262 100644 --- a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java +++ b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/AndroidTestUtil.java @@ -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(); } /** diff --git a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java index 16027f63c0..25eb95e7dd 100644 --- a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java +++ b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java @@ -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 diff --git a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/TransformationTest.java b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/TransformationTest.java index afbb309ea6..dddf8d88a3 100644 --- a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/TransformationTest.java +++ b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/TransformationTest.java @@ -32,7 +32,6 @@ import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.transformer.AndroidTestUtil; import com.google.android.exoplayer2.transformer.AndroidTestUtil.ForceEncodeEncoderFactory; import com.google.android.exoplayer2.transformer.DefaultEncoderFactory; -import com.google.android.exoplayer2.transformer.EncoderSelector; import com.google.android.exoplayer2.transformer.TransformationRequest; import com.google.android.exoplayer2.transformer.Transformer; import com.google.android.exoplayer2.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) diff --git a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/BitrateAnalysisTest.java b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/BitrateAnalysisTest.java index e65d1f7886..4810bb41e6 100644 --- a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/BitrateAnalysisTest.java +++ b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/BitrateAnalysisTest.java @@ -24,7 +24,6 @@ import androidx.test.core.app.ApplicationProvider; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.transformer.AndroidTestUtil; import com.google.android.exoplayer2.transformer.DefaultEncoderFactory; -import com.google.android.exoplayer2.transformer.EncoderSelector; import com.google.android.exoplayer2.transformer.Transformer; import com.google.android.exoplayer2.transformer.TransformerAndroidTestRunner; import com.google.android.exoplayer2.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) diff --git a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/EncoderPerformanceAnalysisTest.java b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/EncoderPerformanceAnalysisTest.java index 4f90954b7b..2ea3a8274a 100644 --- a/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/EncoderPerformanceAnalysisTest.java +++ b/library/transformer/src/androidTest/java/com/google/android/exoplayer2/transformer/mh/analysis/EncoderPerformanceAnalysisTest.java @@ -26,7 +26,6 @@ import androidx.test.core.app.ApplicationProvider; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.transformer.AndroidTestUtil; import com.google.android.exoplayer2.transformer.DefaultEncoderFactory; -import com.google.android.exoplayer2.transformer.EncoderSelector; import com.google.android.exoplayer2.transformer.Transformer; import com.google.android.exoplayer2.transformer.TransformerAndroidTestRunner; import com.google.android.exoplayer2.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) diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactory.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactory.java index 309646600c..b7dca2343e 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactory.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactory.java @@ -45,48 +45,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}. + * + *

The default value is {@link EncoderSelector#DEFAULT}. + */ + public Builder setVideoEncoderSelector(EncoderSelector encoderSelector) { + this.encoderSelector = encoderSelector; + return this; + } + + /** + * Sets the requested {@link VideoEncoderSettings}. + * + *

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. + * + *

{@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. + * + *

The default value is {@link VideoEncoderSettings#DEFAULT}. + */ + public Builder setRequestedVideoEncoderSettings( + VideoEncoderSettings requestedVideoEncoderSettings) { + this.requestedVideoEncoderSettings = requestedVideoEncoderSettings; + return this; + } + + /** + * Sets whether the encoder can fallback. + * + *

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. + * + *

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. - * - *

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. - * - *

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. - * - *

{@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, diff --git a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java index 07352a3df0..9397c2a036 100644 --- a/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java +++ b/library/transformer/src/main/java/com/google/android/exoplayer2/transformer/Transformer.java @@ -120,7 +120,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; diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java index 3efc65d4ee..671a3bb501 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/DefaultEncoderFactoryTest.java @@ -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))); diff --git a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java index 05eb5eec2b..0209c9cf2c 100644 --- a/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java +++ b/library/transformer/src/test/java/com/google/android/exoplayer2/transformer/TransformerEndToEndTest.java @@ -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() {