mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
Add DASH support for parsing standalone TTML files during extraction
This change applies to standalone TTML files linked directly from the manifest. As a result, we no longer have the flakiness in the DashPlaybackTest which uses sidecar-loaded (standalone file) TTML subtitles. We experimentally opt into parsing subtitles during extraction and use SubtitleExtractor in hybrid mode. PiperOrigin-RevId: 577457256
This commit is contained in:
parent
1359b0147d
commit
97efa70852
5 changed files with 19 additions and 7 deletions
|
|
@ -38,6 +38,9 @@
|
||||||
* RTMP Extension:
|
* RTMP Extension:
|
||||||
* HLS Extension:
|
* HLS Extension:
|
||||||
* DASH Extension:
|
* DASH Extension:
|
||||||
|
* Extend experimental support for parsing subtitles during extraction to
|
||||||
|
work with standalone TTML files (previously it only worked with
|
||||||
|
subtitles muxed into MP4 segments).
|
||||||
* Smooth Streaming Extension:
|
* Smooth Streaming Extension:
|
||||||
* RTSP Extension:
|
* RTSP Extension:
|
||||||
* Decoder Extensions (FFmpeg, VP9, AV1, MIDI, etc.):
|
* Decoder Extensions (FFmpeg, VP9, AV1, MIDI, etc.):
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import com.google.common.base.Ascii;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import org.checkerframework.dataflow.qual.Pure;
|
||||||
|
|
||||||
/** Defines common MIME types and helper methods. */
|
/** Defines common MIME types and helper methods. */
|
||||||
public final class MimeTypes {
|
public final class MimeTypes {
|
||||||
|
|
@ -218,6 +219,7 @@ public final class MimeTypes {
|
||||||
* "application" as their base type.
|
* "application" as their base type.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
@Pure
|
||||||
public static boolean isText(@Nullable String mimeType) {
|
public static boolean isText(@Nullable String mimeType) {
|
||||||
return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType))
|
return BASE_TYPE_TEXT.equals(getTopLevelType(mimeType))
|
||||||
|| APPLICATION_MEDIA3_CUES.equals(mimeType)
|
|| APPLICATION_MEDIA3_CUES.equals(mimeType)
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import androidx.media3.extractor.SeekMap;
|
||||||
import androidx.media3.extractor.TrackOutput;
|
import androidx.media3.extractor.TrackOutput;
|
||||||
import androidx.media3.extractor.mkv.MatroskaExtractor;
|
import androidx.media3.extractor.mkv.MatroskaExtractor;
|
||||||
import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
|
import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
|
||||||
|
import androidx.media3.extractor.text.SubtitleExtractor;
|
||||||
import androidx.media3.extractor.text.SubtitleParser;
|
import androidx.media3.extractor.text.SubtitleParser;
|
||||||
import androidx.media3.extractor.text.SubtitleTranscodingExtractor;
|
import androidx.media3.extractor.text.SubtitleTranscodingExtractor;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -86,8 +87,14 @@ public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtrac
|
||||||
@Nullable String containerMimeType = representationFormat.containerMimeType;
|
@Nullable String containerMimeType = representationFormat.containerMimeType;
|
||||||
Extractor extractor;
|
Extractor extractor;
|
||||||
if (MimeTypes.isText(containerMimeType)) {
|
if (MimeTypes.isText(containerMimeType)) {
|
||||||
// Text types do not need an extractor.
|
if (subtitleParserFactory == null) {
|
||||||
|
// Subtitles will be parsed after decoding
|
||||||
return null;
|
return null;
|
||||||
|
} else {
|
||||||
|
extractor =
|
||||||
|
new SubtitleExtractor(
|
||||||
|
subtitleParserFactory.create(representationFormat), representationFormat);
|
||||||
|
}
|
||||||
} else if (MimeTypes.isMatroska(containerMimeType)) {
|
} else if (MimeTypes.isMatroska(containerMimeType)) {
|
||||||
extractor = new MatroskaExtractor(MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES);
|
extractor = new MatroskaExtractor(MatroskaExtractor.FLAG_DISABLE_SEEK_FOR_CUES);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -103,7 +110,7 @@ public final class BundledChunkExtractor implements ExtractorOutput, ChunkExtrac
|
||||||
closedCaptionFormats,
|
closedCaptionFormats,
|
||||||
playerEmsgTrackOutput);
|
playerEmsgTrackOutput);
|
||||||
}
|
}
|
||||||
if (subtitleParserFactory != null) {
|
if (subtitleParserFactory != null && !MimeTypes.isText(containerMimeType)) {
|
||||||
extractor = new SubtitleTranscodingExtractor(extractor, subtitleParserFactory);
|
extractor = new SubtitleTranscodingExtractor(extractor, subtitleParserFactory);
|
||||||
}
|
}
|
||||||
return new BundledChunkExtractor(extractor, primaryTrackType, representationFormat);
|
return new BundledChunkExtractor(extractor, primaryTrackType, representationFormat);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,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 androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
@ -52,8 +51,6 @@ public final class DashPlaybackTest {
|
||||||
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
ShadowMediaCodecConfig.forAllSupportedMimeTypes();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore(
|
|
||||||
"Disabled until subtitles are reliably asserted in robolectric tests [internal b/174661563].")
|
|
||||||
public void ttmlStandaloneXmlFile() throws Exception {
|
public void ttmlStandaloneXmlFile() throws Exception {
|
||||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||||
CapturingRenderersFactory capturingRenderersFactory =
|
CapturingRenderersFactory capturingRenderersFactory =
|
||||||
|
|
@ -61,6 +58,9 @@ 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);
|
||||||
|
|
|
||||||
|
|
@ -131,13 +131,13 @@ public class SubtitleExtractor implements Extractor {
|
||||||
public void init(ExtractorOutput output) {
|
public void init(ExtractorOutput output) {
|
||||||
checkState(state == STATE_CREATED);
|
checkState(state == STATE_CREATED);
|
||||||
trackOutput = output.track(/* id= */ 0, C.TRACK_TYPE_TEXT);
|
trackOutput = output.track(/* id= */ 0, C.TRACK_TYPE_TEXT);
|
||||||
|
trackOutput.format(format);
|
||||||
output.endTracks();
|
output.endTracks();
|
||||||
output.seekMap(
|
output.seekMap(
|
||||||
new IndexSeekMap(
|
new IndexSeekMap(
|
||||||
/* positions= */ new long[] {0},
|
/* positions= */ new long[] {0},
|
||||||
/* timesUs= */ new long[] {0},
|
/* timesUs= */ new long[] {0},
|
||||||
/* durationUs= */ C.TIME_UNSET));
|
/* durationUs= */ C.TIME_UNSET));
|
||||||
trackOutput.format(format);
|
|
||||||
state = STATE_INITIALIZED;
|
state = STATE_INITIALIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue