Default to parse subtitles while extracting, instead of while rendering
To override this change, and go back to parsing during rendering, apps must make two method calls: 1. `MediaSource.Factory.experimentalParseSubtitlesDuringExtraction(false)` 2. `TextRenderer.experimentalSetLegacyDecodingEnabled(true)` PiperOrigin-RevId: 634262798
This commit is contained in:
parent
5c6f48ecaf
commit
0352db9a37
@ -67,6 +67,21 @@
|
|||||||
* Text:
|
* Text:
|
||||||
* Fix issue where subtitles starting before a seek position are skipped.
|
* Fix issue where subtitles starting before a seek position are skipped.
|
||||||
This issue was only introduced in Media3 1.4.0-alpha01.
|
This issue was only introduced in Media3 1.4.0-alpha01.
|
||||||
|
* Change default subtitle parsing behavior so it happens during extraction
|
||||||
|
instead of during rendering (see
|
||||||
|
[ExoPlayer's architecture diagram](https://developer.android.com/media/media3/exoplayer/glossary#exoplayer)
|
||||||
|
for the difference between extraction and rendering).
|
||||||
|
* This change can be overridden by calling **both**
|
||||||
|
`MediaSource.Factory.experimentalParseSubtitlesDuringExtraction(false)`
|
||||||
|
and `TextRenderer.experimentalSetLegacyDecodingEnabled(true)`. See
|
||||||
|
the
|
||||||
|
[docs on customization](https://developer.android.com/media/media3/exoplayer/customization)
|
||||||
|
for how to plumb these components into an `ExoPlayer` instance.
|
||||||
|
These methods (and all support for legacy subtitle decoding) will be
|
||||||
|
removed in a future release.
|
||||||
|
* Apps with custom `SubtitleDecoder` implementations need to update
|
||||||
|
them to implement `SubtitleParser` instead (and
|
||||||
|
`SubtitleParser.Factory` instead of `SubtitleDecoderFactory`).
|
||||||
* Metadata:
|
* Metadata:
|
||||||
* Fix mapping of MP4 to ID3 sort tags. Previously the 'album sort'
|
* Fix mapping of MP4 to ID3 sort tags. Previously the 'album sort'
|
||||||
(`soal`), 'artist sort' (`soar`) and 'album artist sort' (`soaa`) MP4
|
(`soal`), 'artist sort' (`soar`) and 'album artist sort' (`soaa`) MP4
|
||||||
|
@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Looper;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MediaItem.SubtitleConfiguration;
|
import androidx.media3.common.MediaItem.SubtitleConfiguration;
|
||||||
@ -31,6 +32,8 @@ import androidx.media3.common.text.CueGroup;
|
|||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
import androidx.media3.exoplayer.source.ClippingMediaSource;
|
import androidx.media3.exoplayer.source.ClippingMediaSource;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
|
import androidx.media3.exoplayer.text.TextOutput;
|
||||||
|
import androidx.media3.exoplayer.text.TextRenderer;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -86,14 +89,8 @@ public final class ClippedPlaybackTest {
|
|||||||
getInstrumentation()
|
getInstrumentation()
|
||||||
.runOnMainSync(
|
.runOnMainSync(
|
||||||
() -> {
|
() -> {
|
||||||
Context context = getInstrumentation().getContext();
|
|
||||||
player.set(
|
player.set(
|
||||||
new ExoPlayer.Builder(context)
|
buildPlayer(getInstrumentation().getContext(), parseSubtitlesDuringExtraction));
|
||||||
.setMediaSourceFactory(
|
|
||||||
new DefaultMediaSourceFactory(context)
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(
|
|
||||||
parseSubtitlesDuringExtraction))
|
|
||||||
.build());
|
|
||||||
player.get().addListener(textCapturer);
|
player.get().addListener(textCapturer);
|
||||||
player.get().setMediaItem(mediaItem);
|
player.get().setMediaItem(mediaItem);
|
||||||
player.get().prepare();
|
player.get().prepare();
|
||||||
@ -138,14 +135,8 @@ public final class ClippedPlaybackTest {
|
|||||||
getInstrumentation()
|
getInstrumentation()
|
||||||
.runOnMainSync(
|
.runOnMainSync(
|
||||||
() -> {
|
() -> {
|
||||||
Context context = getInstrumentation().getContext();
|
|
||||||
player.set(
|
player.set(
|
||||||
new ExoPlayer.Builder(context)
|
buildPlayer(getInstrumentation().getContext(), parseSubtitlesDuringExtraction));
|
||||||
.setMediaSourceFactory(
|
|
||||||
new DefaultMediaSourceFactory(context)
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(
|
|
||||||
parseSubtitlesDuringExtraction))
|
|
||||||
.build());
|
|
||||||
player.get().addListener(textCapturer);
|
player.get().addListener(textCapturer);
|
||||||
player.get().setMediaItems(mediaItems);
|
player.get().setMediaItems(mediaItems);
|
||||||
player.get().prepare();
|
player.get().prepare();
|
||||||
@ -163,6 +154,33 @@ public final class ClippedPlaybackTest {
|
|||||||
.isEqualTo("This is the first subtitle.");
|
.isEqualTo("This is the first subtitle.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using deprecated TextRenderer.experimentalSetLegacyDecodingEnabled() and
|
||||||
|
// MediaSource.Factory.experimentalParseSubtitlesDuringExtraction() methods to ensure legacy
|
||||||
|
// subtitle handling keeps working.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
private static ExoPlayer buildPlayer(Context context, boolean parseSubtitlesDuringExtraction) {
|
||||||
|
return new ExoPlayer.Builder(context)
|
||||||
|
.setRenderersFactory(
|
||||||
|
new DefaultRenderersFactory(context) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void buildTextRenderers(
|
||||||
|
Context context,
|
||||||
|
TextOutput output,
|
||||||
|
Looper outputLooper,
|
||||||
|
@ExtensionRendererMode int extensionRendererMode,
|
||||||
|
ArrayList<Renderer> out) {
|
||||||
|
super.buildTextRenderers(context, output, outputLooper, extensionRendererMode, out);
|
||||||
|
((TextRenderer) Iterables.getLast(out))
|
||||||
|
.experimentalSetLegacyDecodingEnabled(!parseSubtitlesDuringExtraction);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setMediaSourceFactory(
|
||||||
|
new DefaultMediaSourceFactory(context)
|
||||||
|
.experimentalParseSubtitlesDuringExtraction(parseSubtitlesDuringExtraction))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private static void playWhenLoadingIsDone(Player player) {
|
private static void playWhenLoadingIsDone(Player player) {
|
||||||
AtomicBoolean loadingStarted = new AtomicBoolean(false);
|
AtomicBoolean loadingStarted = new AtomicBoolean(false);
|
||||||
player.addListener(
|
player.addListener(
|
||||||
|
@ -187,10 +187,12 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
liveMaxOffsetMs = C.TIME_UNSET;
|
liveMaxOffsetMs = C.TIME_UNSET;
|
||||||
liveMinSpeed = C.RATE_UNSET;
|
liveMinSpeed = C.RATE_UNSET;
|
||||||
liveMaxSpeed = C.RATE_UNSET;
|
liveMaxSpeed = C.RATE_UNSET;
|
||||||
|
parseSubtitlesDuringExtraction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public DefaultMediaSourceFactory experimentalParseSubtitlesDuringExtraction(
|
public DefaultMediaSourceFactory experimentalParseSubtitlesDuringExtraction(
|
||||||
boolean parseSubtitlesDuringExtraction) {
|
boolean parseSubtitlesDuringExtraction) {
|
||||||
@ -620,6 +622,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
this.subtitleParserFactory = subtitleParserFactory;
|
this.subtitleParserFactory = subtitleParserFactory;
|
||||||
mediaSourceFactorySuppliers = new HashMap<>();
|
mediaSourceFactorySuppliers = new HashMap<>();
|
||||||
mediaSourceFactories = new HashMap<>();
|
mediaSourceFactories = new HashMap<>();
|
||||||
|
parseSubtitlesDuringExtraction = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public @C.ContentType int[] getSupportedTypes() {
|
public @C.ContentType int[] getSupportedTypes() {
|
||||||
|
@ -102,24 +102,25 @@ public interface MediaSource {
|
|||||||
/**
|
/**
|
||||||
* Sets whether subtitles should be parsed as part of extraction (before being added to the
|
* Sets whether subtitles should be parsed as part of extraction (before being added to the
|
||||||
* sample queue) or as part of rendering (when being taken from the sample queue). Defaults to
|
* sample queue) or as part of rendering (when being taken from the sample queue). Defaults to
|
||||||
* {@code false} (i.e. subtitles will be parsed as part of rendering).
|
* {@code true} (i.e. subtitles will be parsed during extraction).
|
||||||
*
|
*
|
||||||
* <p>This method is experimental and will be renamed or removed in a future release.
|
* <p>This method is experimental and will be renamed or removed in a future release.
|
||||||
*
|
*
|
||||||
|
* @deprecated This method (and all support for 'legacy' subtitle decoding during rendering)
|
||||||
|
* will be removed in a future release.
|
||||||
* @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or
|
* @param parseSubtitlesDuringExtraction Whether to parse subtitles during extraction or
|
||||||
* rendering.
|
* rendering.
|
||||||
* @return This factory, for convenience.
|
* @return This factory, for convenience.
|
||||||
*/
|
*/
|
||||||
// TODO: b/289916598 - Flip the default of this to true.
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
@Deprecated
|
||||||
default Factory experimentalParseSubtitlesDuringExtraction(
|
default Factory experimentalParseSubtitlesDuringExtraction(
|
||||||
boolean parseSubtitlesDuringExtraction) {
|
boolean parseSubtitlesDuringExtraction) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link SubtitleParser.Factory} to be used for parsing subtitles during extraction if
|
* Sets the {@link SubtitleParser.Factory} to be used for parsing subtitles during extraction.
|
||||||
* {@link #experimentalParseSubtitlesDuringExtraction} is enabled.
|
|
||||||
*
|
*
|
||||||
* @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during
|
* @param subtitleParserFactory The {@link SubtitleParser.Factory} for parsing subtitles during
|
||||||
* extraction.
|
* extraction.
|
||||||
|
@ -161,7 +161,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
finalStreamEndPositionUs = C.TIME_UNSET;
|
finalStreamEndPositionUs = C.TIME_UNSET;
|
||||||
outputStreamOffsetUs = C.TIME_UNSET;
|
outputStreamOffsetUs = C.TIME_UNSET;
|
||||||
lastRendererPositionUs = C.TIME_UNSET;
|
lastRendererPositionUs = C.TIME_UNSET;
|
||||||
legacyDecodingEnabled = true;
|
legacyDecodingEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -277,11 +277,15 @@ public final class TextRenderer extends BaseRenderer implements Callback {
|
|||||||
* MimeTypes#APPLICATION_MEDIA3_CUES} (which have been parsed from their original format during
|
* MimeTypes#APPLICATION_MEDIA3_CUES} (which have been parsed from their original format during
|
||||||
* extraction), and will throw an exception if passed data of a different type.
|
* extraction), and will throw an exception if passed data of a different type.
|
||||||
*
|
*
|
||||||
* <p>This is enabled by default.
|
* <p>This is disabled by default.
|
||||||
*
|
*
|
||||||
* <p>This method is experimental. It may change behavior, be renamed, or removed in a future
|
* <p>This method is experimental. It may change behavior, be renamed, or removed in a future
|
||||||
* release.
|
* release.
|
||||||
|
*
|
||||||
|
* @deprecated This method (and all support for 'legacy' subtitle decoding during rendering) will
|
||||||
|
* be removed in a future release.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void experimentalSetLegacyDecodingEnabled(boolean legacyDecodingEnabled) {
|
public void experimentalSetLegacyDecodingEnabled(boolean legacyDecodingEnabled) {
|
||||||
this.legacyDecodingEnabled = legacyDecodingEnabled;
|
this.legacyDecodingEnabled = legacyDecodingEnabled;
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ import androidx.media3.common.MediaItem;
|
|||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.extractor.DefaultExtractorsFactory;
|
|
||||||
import androidx.media3.test.utils.CapturingRenderersFactory;
|
import androidx.media3.test.utils.CapturingRenderersFactory;
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.FakeClock;
|
import androidx.media3.test.utils.FakeClock;
|
||||||
@ -66,11 +65,8 @@ public final class MkvPlaybackTest {
|
|||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
CapturingRenderersFactory capturingRenderersFactory =
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
// TODO: b/289916598 - Remove this when transcoding is the default.
|
|
||||||
DefaultExtractorsFactory extractorsFactory =
|
|
||||||
new DefaultExtractorsFactory().setTextTrackTranscodingEnabled(true);
|
|
||||||
DefaultMediaSourceFactory mediaSourceFactory =
|
DefaultMediaSourceFactory mediaSourceFactory =
|
||||||
new DefaultMediaSourceFactory(applicationContext, extractorsFactory);
|
new DefaultMediaSourceFactory(applicationContext);
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory, mediaSourceFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory, mediaSourceFactory)
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
|
@ -28,8 +28,6 @@ import androidx.media3.common.MimeTypes;
|
|||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
|
||||||
import androidx.media3.test.utils.CapturingRenderersFactory;
|
import androidx.media3.test.utils.CapturingRenderersFactory;
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
import androidx.media3.test.utils.FakeClock;
|
import androidx.media3.test.utils.FakeClock;
|
||||||
@ -107,13 +105,9 @@ public final class PlaylistPlaybackTest {
|
|||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
CapturingRenderersFactory capturingRenderersFactory =
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
MediaSource.Factory mediaSourceFactory =
|
|
||||||
new DefaultMediaSourceFactory(applicationContext)
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true);
|
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.setMediaSourceFactory(mediaSourceFactory)
|
|
||||||
.build();
|
.build();
|
||||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||||
player.setVideoSurface(surface);
|
player.setVideoSurface(surface);
|
||||||
|
@ -22,7 +22,6 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.SurfaceTexture;
|
import android.graphics.SurfaceTexture;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Looper;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
@ -35,11 +34,9 @@ import androidx.media3.exoplayer.DefaultLoadControl;
|
|||||||
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.Renderer;
|
|
||||||
import androidx.media3.exoplayer.RenderersFactory;
|
import androidx.media3.exoplayer.RenderersFactory;
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.text.TextOutput;
|
|
||||||
import androidx.media3.exoplayer.text.TextRenderer;
|
import androidx.media3.exoplayer.text.TextRenderer;
|
||||||
import androidx.media3.test.utils.CapturingRenderersFactory;
|
import androidx.media3.test.utils.CapturingRenderersFactory;
|
||||||
import androidx.media3.test.utils.DumpFileAsserts;
|
import androidx.media3.test.utils.DumpFileAsserts;
|
||||||
@ -50,8 +47,6 @@ import androidx.media3.test.utils.robolectric.ShadowMediaCodecConfig;
|
|||||||
import androidx.media3.test.utils.robolectric.TestPlayerRunHelper;
|
import androidx.media3.test.utils.robolectric.TestPlayerRunHelper;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@ -78,13 +73,9 @@ public class WebvttPlaybackTest {
|
|||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
CapturingRenderersFactory capturingRenderersFactory =
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
MediaSource.Factory mediaSourceFactory =
|
|
||||||
new DefaultMediaSourceFactory(applicationContext)
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true);
|
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.setMediaSourceFactory(mediaSourceFactory)
|
|
||||||
.build();
|
.build();
|
||||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||||
player.setVideoSurface(surface);
|
player.setVideoSurface(surface);
|
||||||
@ -120,13 +111,9 @@ public class WebvttPlaybackTest {
|
|||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
CapturingRenderersFactory capturingRenderersFactory =
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
MediaSource.Factory mediaSourceFactory =
|
|
||||||
new DefaultMediaSourceFactory(applicationContext)
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true);
|
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.setMediaSourceFactory(mediaSourceFactory)
|
|
||||||
.setLoadControl(
|
.setLoadControl(
|
||||||
new DefaultLoadControl.Builder()
|
new DefaultLoadControl.Builder()
|
||||||
.setBackBuffer(
|
.setBackBuffer(
|
||||||
@ -167,11 +154,21 @@ public class WebvttPlaybackTest {
|
|||||||
applicationContext, playbackOutput, "playbackdumps/webvtt/" + inputFile + ".seek.dump");
|
applicationContext, playbackOutput, "playbackdumps/webvtt/" + inputFile + ".seek.dump");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using deprecated TextRenderer.experimentalSetLegacyDecodingEnabled() and
|
||||||
|
// MediaSource.Factory.experimentalParseSubtitlesDuringExtraction() methods to ensure legacy
|
||||||
|
// subtitle handling keeps working.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void test_legacyParseInRenderer() throws Exception {
|
public void test_legacyParseInRenderer() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
CapturingRenderersFactory capturingRenderersFactory =
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext)
|
||||||
|
.setTextRendererFactory(
|
||||||
|
(textOutput, outputLooper) -> {
|
||||||
|
TextRenderer renderer = new TextRenderer(textOutput, outputLooper);
|
||||||
|
renderer.experimentalSetLegacyDecodingEnabled(true);
|
||||||
|
return renderer;
|
||||||
|
});
|
||||||
MediaSource.Factory mediaSourceFactory =
|
MediaSource.Factory mediaSourceFactory =
|
||||||
new DefaultMediaSourceFactory(applicationContext)
|
new DefaultMediaSourceFactory(applicationContext)
|
||||||
.experimentalParseSubtitlesDuringExtraction(false);
|
.experimentalParseSubtitlesDuringExtraction(false);
|
||||||
@ -215,11 +212,21 @@ public class WebvttPlaybackTest {
|
|||||||
applicationContext, playbackOutput, "playbackdumps/webvtt/" + inputFile + ".dump");
|
applicationContext, playbackOutput, "playbackdumps/webvtt/" + inputFile + ".dump");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using deprecated TextRenderer.experimentalSetLegacyDecodingEnabled() and
|
||||||
|
// MediaSource.Factory.experimentalParseSubtitlesDuringExtraction() methods to ensure legacy
|
||||||
|
// subtitle handling keeps working.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void test_legacyParseInRendererWithSeek() throws Exception {
|
public void test_legacyParseInRendererWithSeek() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
CapturingRenderersFactory capturingRenderersFactory =
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext)
|
||||||
|
.setTextRendererFactory(
|
||||||
|
(textOutput, outputLooper) -> {
|
||||||
|
TextRenderer renderer = new TextRenderer(textOutput, outputLooper);
|
||||||
|
renderer.experimentalSetLegacyDecodingEnabled(true);
|
||||||
|
return renderer;
|
||||||
|
});
|
||||||
MediaSource.Factory mediaSourceFactory =
|
MediaSource.Factory mediaSourceFactory =
|
||||||
new DefaultMediaSourceFactory(applicationContext)
|
new DefaultMediaSourceFactory(applicationContext)
|
||||||
.experimentalParseSubtitlesDuringExtraction(false);
|
.experimentalParseSubtitlesDuringExtraction(false);
|
||||||
@ -278,22 +285,12 @@ public class WebvttPlaybackTest {
|
|||||||
applicationContext, playbackOutput, "playbackdumps/webvtt/" + inputFile + ".seek.dump");
|
applicationContext, playbackOutput, "playbackdumps/webvtt/" + inputFile + ".seek.dump");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deliberately configuring legacy subtitle handling to check unconfigured TextRenderer fails.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void textRendererDoesntSupportLegacyDecoding_playbackFails() throws Exception {
|
public void textRenderer_doesntSupportLegacyDecodingByDefault_playbackFails() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
RenderersFactory renderersFactory =
|
RenderersFactory renderersFactory = new DefaultRenderersFactory(applicationContext);
|
||||||
new DefaultRenderersFactory(applicationContext) {
|
|
||||||
@Override
|
|
||||||
protected void buildTextRenderers(
|
|
||||||
Context context,
|
|
||||||
TextOutput output,
|
|
||||||
Looper outputLooper,
|
|
||||||
@ExtensionRendererMode int extensionRendererMode,
|
|
||||||
ArrayList<Renderer> out) {
|
|
||||||
super.buildTextRenderers(context, output, outputLooper, extensionRendererMode, out);
|
|
||||||
((TextRenderer) Iterables.getLast(out)).experimentalSetLegacyDecodingEnabled(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
MediaSource.Factory mediaSourceFactory =
|
MediaSource.Factory mediaSourceFactory =
|
||||||
new DefaultMediaSourceFactory(applicationContext)
|
new DefaultMediaSourceFactory(applicationContext)
|
||||||
.experimentalParseSubtitlesDuringExtraction(false);
|
.experimentalParseSubtitlesDuringExtraction(false);
|
||||||
|
@ -163,6 +163,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||||||
fallbackTargetLiveOffsetMs = DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS;
|
fallbackTargetLiveOffsetMs = DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS;
|
||||||
minLiveStartPositionUs = MIN_LIVE_DEFAULT_START_POSITION_US;
|
minLiveStartPositionUs = MIN_LIVE_DEFAULT_START_POSITION_US;
|
||||||
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
|
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
|
||||||
|
experimentalParseSubtitlesDuringExtraction(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
@ -205,6 +206,7 @@ public final class DashMediaSource extends BaseMediaSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
public Factory experimentalParseSubtitlesDuringExtraction(
|
public Factory experimentalParseSubtitlesDuringExtraction(
|
||||||
boolean parseSubtitlesDuringExtraction) {
|
boolean parseSubtitlesDuringExtraction) {
|
||||||
|
@ -58,9 +58,6 @@ public final class DashPlaybackTest {
|
|||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.setMediaSourceFactory(
|
|
||||||
new DashMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true))
|
|
||||||
.build();
|
.build();
|
||||||
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
||||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||||
@ -87,9 +84,6 @@ public final class DashPlaybackTest {
|
|||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.setMediaSourceFactory(
|
|
||||||
new DashMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true))
|
|
||||||
.build();
|
.build();
|
||||||
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
||||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||||
@ -116,9 +110,6 @@ public final class DashPlaybackTest {
|
|||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setMediaSourceFactory(
|
|
||||||
new DashMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true))
|
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.build();
|
.build();
|
||||||
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
||||||
@ -145,9 +136,6 @@ public final class DashPlaybackTest {
|
|||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setMediaSourceFactory(
|
|
||||||
new DashMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true))
|
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.build();
|
.build();
|
||||||
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
||||||
@ -171,6 +159,9 @@ public final class DashPlaybackTest {
|
|||||||
* This test and {@link #cea608_parseDuringExtraction()} use the same output dump file, to
|
* This test and {@link #cea608_parseDuringExtraction()} use the same output dump file, to
|
||||||
* demonstrate the flag has no effect on the resulting subtitles.
|
* demonstrate the flag has no effect on the resulting subtitles.
|
||||||
*/
|
*/
|
||||||
|
// Using deprecated MediaSource.Factory.experimentalParseSubtitlesDuringExtraction() method to
|
||||||
|
// ensure legacy subtitle handling keeps working.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void cea608_parseDuringRendering() throws Exception {
|
public void cea608_parseDuringRendering() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
@ -204,6 +195,9 @@ public final class DashPlaybackTest {
|
|||||||
* This test and {@link #cea608_parseDuringRendering()} use the same output dump file, to
|
* This test and {@link #cea608_parseDuringRendering()} use the same output dump file, to
|
||||||
* demonstrate the flag has no effect on the resulting subtitles.
|
* demonstrate the flag has no effect on the resulting subtitles.
|
||||||
*/
|
*/
|
||||||
|
// Explicitly enable parsing during extraction (even though a) it's the default and b) currently
|
||||||
|
// all CEA-608 parsing happens during rendering) to make this test clearer & more future-proof.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void cea608_parseDuringExtraction() throws Exception {
|
public void cea608_parseDuringExtraction() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
|
@ -169,6 +169,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
metadataType = METADATA_TYPE_ID3;
|
metadataType = METADATA_TYPE_ID3;
|
||||||
elapsedRealTimeOffsetMs = C.TIME_UNSET;
|
elapsedRealTimeOffsetMs = C.TIME_UNSET;
|
||||||
allowChunklessPreparation = true;
|
allowChunklessPreparation = true;
|
||||||
|
experimentalParseSubtitlesDuringExtraction(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -206,6 +207,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
public Factory experimentalParseSubtitlesDuringExtraction(
|
public Factory experimentalParseSubtitlesDuringExtraction(
|
||||||
boolean parseSubtitlesDuringExtraction) {
|
boolean parseSubtitlesDuringExtraction) {
|
||||||
|
@ -53,9 +53,6 @@ public final class HlsPlaybackTest {
|
|||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setMediaSourceFactory(
|
|
||||||
new HlsMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true))
|
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.build();
|
.build();
|
||||||
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
||||||
@ -79,9 +76,6 @@ public final class HlsPlaybackTest {
|
|||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setMediaSourceFactory(
|
|
||||||
new HlsMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true))
|
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.build();
|
.build();
|
||||||
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
player.setVideoSurface(new Surface(new SurfaceTexture(/* texName= */ 1)));
|
||||||
@ -102,6 +96,9 @@ public final class HlsPlaybackTest {
|
|||||||
* This test and {@link #cea608_parseDuringExtraction()} use the same output dump file, to
|
* This test and {@link #cea608_parseDuringExtraction()} use the same output dump file, to
|
||||||
* demonstrate the flag has no effect on the resulting subtitles.
|
* demonstrate the flag has no effect on the resulting subtitles.
|
||||||
*/
|
*/
|
||||||
|
// Using deprecated MediaSource.Factory.experimentalParseSubtitlesDuringExtraction() method to
|
||||||
|
// ensure legacy subtitle handling keeps working.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void cea608_parseDuringRendering() throws Exception {
|
public void cea608_parseDuringRendering() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
@ -131,6 +128,9 @@ public final class HlsPlaybackTest {
|
|||||||
* This test and {@link #cea608_parseDuringRendering()} use the same output dump file, to
|
* This test and {@link #cea608_parseDuringRendering()} use the same output dump file, to
|
||||||
* demonstrate the flag has no effect on the resulting subtitles.
|
* demonstrate the flag has no effect on the resulting subtitles.
|
||||||
*/
|
*/
|
||||||
|
// Explicitly enable parsing during extraction (even though a) it's the default and b) currently
|
||||||
|
// all CEA-608 parsing happens during rendering) to make this test clearer & more future-proof.
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@Test
|
@Test
|
||||||
public void cea608_parseDuringExtraction() throws Exception {
|
public void cea608_parseDuringExtraction() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
@ -164,9 +164,6 @@ public final class HlsPlaybackTest {
|
|||||||
new CapturingRenderersFactory(applicationContext);
|
new CapturingRenderersFactory(applicationContext);
|
||||||
ExoPlayer player =
|
ExoPlayer player =
|
||||||
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
new ExoPlayer.Builder(applicationContext, capturingRenderersFactory)
|
||||||
.setMediaSourceFactory(
|
|
||||||
new HlsMediaSource.Factory(new DefaultDataSource.Factory(applicationContext))
|
|
||||||
.experimentalParseSubtitlesDuringExtraction(true))
|
|
||||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
.setLoadControl(
|
.setLoadControl(
|
||||||
new DefaultLoadControl.Builder()
|
new DefaultLoadControl.Builder()
|
||||||
|
@ -139,6 +139,7 @@ public final class SsMediaSource extends BaseMediaSource
|
|||||||
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
|
loadErrorHandlingPolicy = new DefaultLoadErrorHandlingPolicy();
|
||||||
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
|
livePresentationDelayMs = DEFAULT_LIVE_PRESENTATION_DELAY_MS;
|
||||||
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
|
compositeSequenceableLoaderFactory = new DefaultCompositeSequenceableLoaderFactory();
|
||||||
|
experimentalParseSubtitlesDuringExtraction(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
@ -161,6 +162,7 @@ public final class SsMediaSource extends BaseMediaSource
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
public Factory experimentalParseSubtitlesDuringExtraction(
|
public Factory experimentalParseSubtitlesDuringExtraction(
|
||||||
boolean parseSubtitlesDuringExtraction) {
|
boolean parseSubtitlesDuringExtraction) {
|
||||||
|
@ -103,6 +103,7 @@ public class SsMediaSourceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("deprecation") // Testing deprecated method
|
||||||
public void
|
public void
|
||||||
setExperimentalParseSubtitlesDuringExtraction_withNonDefaultChunkSourceFactory_setSucceeds() {
|
setExperimentalParseSubtitlesDuringExtraction_withNonDefaultChunkSourceFactory_setSucceeds() {
|
||||||
SsMediaSource.Factory ssMediaSourceFactory =
|
SsMediaSource.Factory ssMediaSourceFactory =
|
||||||
@ -113,6 +114,7 @@ public class SsMediaSourceTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@SuppressWarnings("deprecation") // Testing deprecated method
|
||||||
public void
|
public void
|
||||||
setExperimentalParseSubtitlesDuringExtraction_withDefaultChunkSourceFactory_setSucceeds() {
|
setExperimentalParseSubtitlesDuringExtraction_withDefaultChunkSourceFactory_setSucceeds() {
|
||||||
SsMediaSource.Factory ssMediaSourceFactory =
|
SsMediaSource.Factory ssMediaSourceFactory =
|
||||||
|
@ -161,6 +161,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
|||||||
tsMode = TsExtractor.MODE_SINGLE_PMT;
|
tsMode = TsExtractor.MODE_SINGLE_PMT;
|
||||||
tsTimestampSearchBytes = TsExtractor.DEFAULT_TIMESTAMP_SEARCH_BYTES;
|
tsTimestampSearchBytes = TsExtractor.DEFAULT_TIMESTAMP_SEARCH_BYTES;
|
||||||
subtitleParserFactory = new DefaultSubtitleParserFactory();
|
subtitleParserFactory = new DefaultSubtitleParserFactory();
|
||||||
|
textTrackTranscodingEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -361,7 +362,8 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated Use {@link #experimentalSetTextTrackTranscodingEnabled(boolean)} instead.
|
* @deprecated This method (and all support for 'legacy' subtitle decoding during rendering) will
|
||||||
|
* be removed in a future release.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
@ -370,6 +372,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
|
|||||||
return experimentalSetTextTrackTranscodingEnabled(textTrackTranscodingEnabled);
|
return experimentalSetTextTrackTranscodingEnabled(textTrackTranscodingEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
@Override
|
@Override
|
||||||
public synchronized DefaultExtractorsFactory experimentalSetTextTrackTranscodingEnabled(
|
public synchronized DefaultExtractorsFactory experimentalSetTextTrackTranscodingEnabled(
|
||||||
boolean textTrackTranscodingEnabled) {
|
boolean textTrackTranscodingEnabled) {
|
||||||
|
@ -37,15 +37,17 @@ public interface ExtractorsFactory {
|
|||||||
* Enables transcoding of text track samples to {@link MimeTypes#APPLICATION_MEDIA3_CUES} before
|
* Enables transcoding of text track samples to {@link MimeTypes#APPLICATION_MEDIA3_CUES} before
|
||||||
* the data is emitted to {@link TrackOutput}.
|
* the data is emitted to {@link TrackOutput}.
|
||||||
*
|
*
|
||||||
* <p>Transcoding is disabled by default.
|
* <p>Transcoding is enabled by default.
|
||||||
*
|
*
|
||||||
* <p>This method is experimental and will be renamed or removed in a future release.
|
* <p>This method is experimental and will be renamed or removed in a future release.
|
||||||
*
|
*
|
||||||
* @param textTrackTranscodingEnabled Whether to enable transcoding.
|
* @param textTrackTranscodingEnabled Whether to enable transcoding.
|
||||||
* @return The factory, for convenience.
|
* @return The factory, for convenience.
|
||||||
|
* @deprecated This method (and all support for 'legacy' subtitle decoding during rendering) will
|
||||||
|
* be removed in a future release.
|
||||||
*/
|
*/
|
||||||
// TODO: b/289916598 - Flip this to default to enabled and deprecate it.
|
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
|
@Deprecated
|
||||||
default ExtractorsFactory experimentalSetTextTrackTranscodingEnabled(
|
default ExtractorsFactory experimentalSetTextTrackTranscodingEnabled(
|
||||||
boolean textTrackTranscodingEnabled) {
|
boolean textTrackTranscodingEnabled) {
|
||||||
return this;
|
return this;
|
||||||
|
@ -137,11 +137,23 @@ public final class DefaultExtractorsFactoryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void subtitleTranscoding_notEnabledByDefault() {
|
public void subtitleTranscoding_enabledByDefault() {
|
||||||
DefaultExtractorsFactory defaultExtractorsFactory = new DefaultExtractorsFactory();
|
DefaultExtractorsFactory defaultExtractorsFactory = new DefaultExtractorsFactory();
|
||||||
|
|
||||||
Extractor[] extractors = defaultExtractorsFactory.createExtractors();
|
Extractor[] extractors = defaultExtractorsFactory.createExtractors();
|
||||||
|
|
||||||
|
assertThat(stream(extractors).map(Object::getClass))
|
||||||
|
.contains(SubtitleTranscodingExtractor.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // Testing legacy subtitle handling
|
||||||
|
@Test
|
||||||
|
public void subtitleTranscoding_noExtractorWrappingIfDisabled() {
|
||||||
|
DefaultExtractorsFactory defaultExtractorsFactory =
|
||||||
|
new DefaultExtractorsFactory().setTextTrackTranscodingEnabled(false);
|
||||||
|
|
||||||
|
Extractor[] extractors = defaultExtractorsFactory.createExtractors();
|
||||||
|
|
||||||
assertThat(stream(extractors).map(Object::getClass))
|
assertThat(stream(extractors).map(Object::getClass))
|
||||||
.doesNotContain(SubtitleTranscodingExtractor.class);
|
.doesNotContain(SubtitleTranscodingExtractor.class);
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,13 @@ import android.media.MediaCodec;
|
|||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.decoder.CryptoInfo;
|
import androidx.media3.decoder.CryptoInfo;
|
||||||
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
import androidx.media3.exoplayer.DefaultRenderersFactory;
|
||||||
@ -50,6 +52,7 @@ import androidx.media3.exoplayer.video.VideoRendererEventListener;
|
|||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSortedMap;
|
import com.google.common.collect.ImmutableSortedMap;
|
||||||
|
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -72,7 +75,9 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
private final CapturingMediaCodecAdapter.Factory mediaCodecAdapterFactory;
|
private final CapturingMediaCodecAdapter.Factory mediaCodecAdapterFactory;
|
||||||
private final CapturingAudioSink audioSink;
|
private final CapturingAudioSink audioSink;
|
||||||
private final CapturingImageOutput imageOutput;
|
private final CapturingImageOutput imageOutput;
|
||||||
|
|
||||||
private ImageDecoder.Factory imageDecoderFactory;
|
private ImageDecoder.Factory imageDecoderFactory;
|
||||||
|
private TextRendererFactory textRendererFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
@ -85,6 +90,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
this.audioSink = new CapturingAudioSink(new DefaultAudioSink.Builder(context).build());
|
this.audioSink = new CapturingAudioSink(new DefaultAudioSink.Builder(context).build());
|
||||||
this.imageOutput = new CapturingImageOutput();
|
this.imageOutput = new CapturingImageOutput();
|
||||||
this.imageDecoderFactory = ImageDecoder.Factory.DEFAULT;
|
this.imageDecoderFactory = ImageDecoder.Factory.DEFAULT;
|
||||||
|
this.textRendererFactory = TextRenderer::new;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,6 +105,18 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the factory for {@link Renderer} instances that handle {@link C#TRACK_TYPE_TEXT} tracks.
|
||||||
|
*
|
||||||
|
* @param textRendererFactory The {@link TextRendererFactory}.
|
||||||
|
* @return This factory, for convenience.
|
||||||
|
*/
|
||||||
|
@CanIgnoreReturnValue
|
||||||
|
public CapturingRenderersFactory setTextRendererFactory(TextRendererFactory textRendererFactory) {
|
||||||
|
this.textRendererFactory = textRendererFactory;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Renderer[] createRenderers(
|
public Renderer[] createRenderers(
|
||||||
Handler eventHandler,
|
Handler eventHandler,
|
||||||
@ -146,7 +164,7 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
eventHandler,
|
eventHandler,
|
||||||
audioRendererEventListener,
|
audioRendererEventListener,
|
||||||
audioSink));
|
audioSink));
|
||||||
renderers.add(new TextRenderer(textRendererOutput, eventHandler.getLooper()));
|
renderers.add(textRendererFactory.create(textRendererOutput, eventHandler.getLooper()));
|
||||||
renderers.add(new MetadataRenderer(metadataRendererOutput, eventHandler.getLooper()));
|
renderers.add(new MetadataRenderer(metadataRendererOutput, eventHandler.getLooper()));
|
||||||
renderers.add(new ImageRenderer(imageDecoderFactory, imageOutput));
|
renderers.add(new ImageRenderer(imageDecoderFactory, imageOutput));
|
||||||
|
|
||||||
@ -160,6 +178,18 @@ public class CapturingRenderersFactory implements RenderersFactory, Dumper.Dumpa
|
|||||||
imageOutput.dump(dumper);
|
imageOutput.dump(dumper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A factory for {@link Renderer} instances that handle {@link C#TRACK_TYPE_TEXT} tracks. */
|
||||||
|
public interface TextRendererFactory {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Renderer} instance for a {@link C#TRACK_TYPE_TEXT} track.
|
||||||
|
*
|
||||||
|
* @param textOutput A {@link TextOutput} to handle the parsed subtitles.
|
||||||
|
* @param outputLooper The looper used to invoke {@code textOutput}.
|
||||||
|
*/
|
||||||
|
Renderer create(TextOutput textOutput, Looper outputLooper);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link MediaCodecAdapter} that captures interactions and exposes them for test assertions via
|
* A {@link MediaCodecAdapter} that captures interactions and exposes them for test assertions via
|
||||||
* {@link Dumper.Dumpable}.
|
* {@link Dumper.Dumpable}.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user