From dd19bc89270335ce16be4ff3b6b6d68f71815302 Mon Sep 17 00:00:00 2001 From: apodob Date: Thu, 2 Sep 2021 19:05:11 +0100 Subject: [PATCH] Integrate ExoplayerCuesDecoder and SubtitleExtractor This CL contains integration of the ExoplayerCuesDecoder and the SubtitleExtractor with the player. The SubtitleExtractor is integrated inside the DefaultMediaSourceFactory. The flag was added to the state of the DefaultMediaSourceFactory to let user decide between the ProgressiveMediaSource and the SingleSampleMediaSource as a source for subtitles. Choosing the ProgressiveMediaSource will cause data to flow through the SubtitleExtractor and eventually the ExoplayerCuesDecoder. PiperOrigin-RevId: 394500305 --- .../source/DefaultMediaSourceFactory.java | 58 +++++++++++++++++-- .../text/SubtitleDecoderFactory.java | 6 +- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java index 445f82432c..2ade14753f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java @@ -21,14 +21,18 @@ import android.content.Context; import android.util.SparseArray; import androidx.annotation.Nullable; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.drm.DrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionManagerProvider; import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory; +import com.google.android.exoplayer2.extractor.Extractor; import com.google.android.exoplayer2.extractor.ExtractorsFactory; +import com.google.android.exoplayer2.extractor.subtitle.SubtitleExtractor; import com.google.android.exoplayer2.offline.StreamKey; import com.google.android.exoplayer2.source.ads.AdsLoader; import com.google.android.exoplayer2.source.ads.AdsMediaSource; +import com.google.android.exoplayer2.text.webvtt.WebvttDecoder; import com.google.android.exoplayer2.ui.AdViewProvider; import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSpec; @@ -112,6 +116,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { private long liveMaxOffsetMs; private float liveMinSpeed; private float liveMaxSpeed; + private boolean useProgressiveMediaSourceForSubtitles; /** * Creates a new instance. @@ -166,6 +171,23 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { liveMaxSpeed = C.RATE_UNSET; } + /** + * Sets whether a {@link ProgressiveMediaSource} or {@link SingleSampleMediaSource} is constructed + * to handle {@link MediaItem.PlaybackProperties#subtitles}. Defaults to false (i.e. {@link + * SingleSampleMediaSource}. + * + *

This method is experimental, and will be renamed or removed in a future release. + * + * @param useProgressiveMediaSourceForSubtitles Indicates that {@link ProgressiveMediaSource} + * should be used for subtitles instead of {@link SingleSampleMediaSource}. + * @return This factory, for convenience. + */ + public DefaultMediaSourceFactory experimentalUseProgressiveMediaSourceForSubtitles( + boolean useProgressiveMediaSourceForSubtitles) { + this.useProgressiveMediaSourceForSubtitles = useProgressiveMediaSourceForSubtitles; + return this; + } + /** * Sets the {@link AdsLoaderProvider} that provides {@link AdsLoader} instances for media items * that have {@link MediaItem.PlaybackProperties#adsConfiguration ads configurations}. @@ -370,14 +392,38 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { if (!subtitles.isEmpty()) { MediaSource[] mediaSources = new MediaSource[subtitles.size() + 1]; mediaSources[0] = mediaSource; - SingleSampleMediaSource.Factory singleSampleSourceFactory = - new SingleSampleMediaSource.Factory(dataSourceFactory) - .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy); for (int i = 0; i < subtitles.size(); i++) { - mediaSources[i + 1] = - singleSampleSourceFactory.createMediaSource( - subtitles.get(i), /* durationUs= */ C.TIME_UNSET); + if (useProgressiveMediaSourceForSubtitles + && subtitles.get(i).mimeType.equals(MimeTypes.TEXT_VTT)) { + int index = i; + ProgressiveMediaSource.Factory progressiveMediaSourceFactory = + new ProgressiveMediaSource.Factory( + dataSourceFactory, + () -> + new Extractor[] { + new SubtitleExtractor( + new WebvttDecoder(), + new Format.Builder() + .setSampleMimeType(subtitles.get(index).mimeType) + .setLanguage(subtitles.get(index).language) + .setSelectionFlags(subtitles.get(index).selectionFlags) + .setRoleFlags(subtitles.get(index).roleFlags) + .setLabel(subtitles.get(index).label) + .build()) + }); + mediaSources[i + 1] = + progressiveMediaSourceFactory.createMediaSource( + MediaItem.fromUri(subtitles.get(i).uri.toString())); + } else { + SingleSampleMediaSource.Factory singleSampleSourceFactory = + new SingleSampleMediaSource.Factory(dataSourceFactory) + .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy); + mediaSources[i + 1] = + singleSampleSourceFactory.createMediaSource( + subtitles.get(i), /* durationUs= */ C.TIME_UNSET); + } } + mediaSource = new MergingMediaSource(mediaSources); } return maybeWrapWithAdsMediaSource(mediaItem, maybeClipMediaSource(mediaItem, mediaSource)); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java index 043c181225..06ce3079c7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java @@ -66,6 +66,7 @@ public interface SubtitleDecoderFactory { *

  • Cea708 ({@link Cea708Decoder}) *
  • DVB ({@link DvbDecoder}) *
  • PGS ({@link PgsDecoder}) + *
  • Exoplayer Cues ({@link ExoplayerCuesDecoder}) * */ SubtitleDecoderFactory DEFAULT = @@ -84,7 +85,8 @@ public interface SubtitleDecoderFactory { || MimeTypes.APPLICATION_MP4CEA608.equals(mimeType) || MimeTypes.APPLICATION_CEA708.equals(mimeType) || MimeTypes.APPLICATION_DVBSUBS.equals(mimeType) - || MimeTypes.APPLICATION_PGS.equals(mimeType); + || MimeTypes.APPLICATION_PGS.equals(mimeType) + || MimeTypes.TEXT_EXOPLAYER_CUES.equals(mimeType); } @Override @@ -116,6 +118,8 @@ public interface SubtitleDecoderFactory { return new DvbDecoder(format.initializationData); case MimeTypes.APPLICATION_PGS: return new PgsDecoder(); + case MimeTypes.TEXT_EXOPLAYER_CUES: + return new ExoplayerCuesDecoder(); default: break; }