From f5276323c6973ffa6031d9c5b9f15cb05f46ef29 Mon Sep 17 00:00:00 2001 From: ibaker Date: Tue, 22 Nov 2022 13:09:17 +0000 Subject: [PATCH] Add `DefaultExtractorsFactory.setTsSubtitleFormats` ExoPlayer is unable to detect the presence of subtitle tracks in some MPEG-TS files that don't fully declare them. It's possible for a developer to provide the list instead, but doing so is quite awkward without this helper method. This is consistent for how `DefaultExtractorsFactory` allows other aspects of the delegate `Extractor` implementations to be customised. * Issue: google/ExoPlayer#10175 * Issue: google/ExoPlayer#10505 #minor-release PiperOrigin-RevId: 490214619 (cherry picked from commit 4853444f0dc90163c257d0e20962604510557df4) --- docs/troubleshooting.md | 29 ++++++++++++++++++- .../extractor/DefaultExtractorsFactory.java | 25 +++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index f93fcd020c..5ced4e270d 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -11,6 +11,7 @@ redirect_from: * [Why is seeking inaccurate in some MP3 files?][] * [Why is seeking in my video slow?][] * [Why do some MPEG-TS files fail to play?][] +* [Why are subtitles not found in some MPEG-TS files?][] * [Why do some MP4/FMP4 files play incorrectly?][] * [Why do some streams fail with HTTP response code 301 or 302?][] * [Why do some streams fail with UnrecognizedInputFormatException?][] @@ -20,7 +21,7 @@ redirect_from: * [How can I query whether the stream being played is a live stream?][] * [How do I keep audio playing when my app is backgrounded?][] * [Why does ExoPlayer support my content but the Cast extension doesn't?][] -* [Why does content fail to play, but no error is surfaced?] +* [Why does content fail to play, but no error is surfaced?][] * [How can I get a decoding extension to load and be used for playback?][] * [Can I play YouTube videos directly with ExoPlayer?][] * [Video playback is stuttering][] @@ -145,6 +146,31 @@ computationally expensive relative to AUD based frame boundary detection. Use of `FLAG_ALLOW_NON_IDR_KEYFRAMES` may result in temporary visual corruption at the start of playback and immediately after seeks when playing some MPEG-TS files. +#### Why are subtitles not found in some MPEG-TS files? #### + +Some MPEG-TS files include CEA-608 tracks but don't declare them in the +container metadata, so ExoPlayer is unable to detect them. You can manually +specify that the subtitle track(s) exist by providing a list of expected +subtitle formats to the `DefaultExtractorsFactory`, including the accessibility +channels that can be used to identify them in the MPEG-TS stream: + +```java +DefaultExtractorsFactory extractorsFactory = + new DefaultExtractorsFactory() + .setTsSubtitleFormats( + ImmutableList.of( + new Format.Builder() + .setSampleMimeType(MimeTypes.APPLICATION_CEA608) + .setAccessibilityChannel(accessibilityChannel) + // Set other subtitle format info, e.g. language. + .build())); +Player player = + new ExoPlayer.Builder( + context, + new DefaultMediaSourceFactory(context, extractorsFactory)) + .build(); +``` + #### Why do some MP4/FMP4 files play incorrectly? #### Some MP4/FMP4 files contain edit lists that rewrite the media timeline by @@ -333,6 +359,7 @@ particularly when playing DRM protected or high frame rate content, you can try [Why is seeking inaccurate in some MP3 files?]: #why-is-seeking-inaccurate-in-some-mp3-files [Why is seeking in my video slow?]: #why-is-seeking-in-my-video-slow [Why do some MPEG-TS files fail to play?]: #why-do-some-mpeg-ts-files-fail-to-play +[Why are subtitles not found in some MPEG-TS files?]: #why-are-subtitles-not-found-in-some-mpeg-ts-files [Why do some MP4/FMP4 files play incorrectly?]: #why-do-some-mp4fmp4-files-play-incorrectly [Why do some streams fail with HTTP response code 301 or 302?]: #why-do-some-streams-fail-with-http-response-code-301-or-302 [Why do some streams fail with UnrecognizedInputFormatException?]: #why-do-some-streams-fail-with-unrecognizedinputformatexception diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java index 7d15ad1388..66bab7db62 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java @@ -21,6 +21,7 @@ import static com.google.android.exoplayer2.util.FileTypes.inferFileTypeFromUri; import android.net.Uri; import androidx.annotation.GuardedBy; import androidx.annotation.Nullable; +import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.extractor.amr.AmrExtractor; @@ -43,6 +44,7 @@ import com.google.android.exoplayer2.extractor.ts.TsPayloadReader; import com.google.android.exoplayer2.extractor.wav.WavExtractor; import com.google.android.exoplayer2.util.FileTypes; import com.google.android.exoplayer2.util.TimestampAdjuster; +import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -126,11 +128,13 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { private @Mp3Extractor.Flags int mp3Flags; private @TsExtractor.Mode int tsMode; private @DefaultTsPayloadReaderFactory.Flags int tsFlags; + private ImmutableList tsSubtitleFormats; private int tsTimestampSearchBytes; public DefaultExtractorsFactory() { tsMode = TsExtractor.MODE_SINGLE_PMT; tsTimestampSearchBytes = TsExtractor.DEFAULT_TIMESTAMP_SEARCH_BYTES; + tsSubtitleFormats = ImmutableList.of(); } /** @@ -301,6 +305,20 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { return this; } + /** + * Sets a list of subtitle formats to pass to the {@link DefaultTsPayloadReaderFactory} used by + * {@link TsExtractor} instances created by the factory. + * + * @see DefaultTsPayloadReaderFactory#DefaultTsPayloadReaderFactory(int, List) + * @param subtitleFormats The subtitle formats. + * @return The factory, for convenience. + */ + @CanIgnoreReturnValue + public synchronized DefaultExtractorsFactory setTsSubtitleFormats(List subtitleFormats) { + tsSubtitleFormats = ImmutableList.copyOf(subtitleFormats); + return this; + } + /** * Sets the number of bytes searched to find a timestamp for {@link TsExtractor} instances created * by the factory. @@ -414,7 +432,12 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory { extractors.add(new PsExtractor()); break; case FileTypes.TS: - extractors.add(new TsExtractor(tsMode, tsFlags, tsTimestampSearchBytes)); + extractors.add( + new TsExtractor( + tsMode, + new TimestampAdjuster(0), + new DefaultTsPayloadReaderFactory(tsFlags, tsSubtitleFormats), + tsTimestampSearchBytes)); break; case FileTypes.WAV: extractors.add(new WavExtractor());