Skip to content

Commit 0b2f694

Browse files
Merge pull request #2531 from bennettpeter:vobsub-mp4
PiperOrigin-RevId: 773649256
2 parents d00f370 + 15ddd0f commit 0b2f694

File tree

6 files changed

+706
-63
lines changed

6 files changed

+706
-63
lines changed

RELEASENOTES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
* Fix a playback stall when a subtitle segment initially fails to load and
6464
later loads successfully, followed by several empty subtitle segments
6565
([#2517](https://github.com/androidx/media/issues/2517)).
66+
* Add support for VobSub tracks in MP4 files
67+
([#2510](https://github.com/androidx/media/issues/2510)).
6668
* Metadata:
6769
* Added support for retrieving media duration and `Timeline` to
6870
`MetadataRetriever` and migrated it to an instance-based,

libraries/container/src/main/java/androidx/media3/container/Mp4Box.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,9 @@ public abstract class Mp4Box {
335335
@SuppressWarnings("ConstantCaseForConstants")
336336
public static final int TYPE_mp4v = 0x6d703476;
337337

338+
@SuppressWarnings("ConstantCaseForConstants")
339+
public static final int TYPE_mp4s = 0x6D703473;
340+
338341
@SuppressWarnings("ConstantCaseForConstants")
339342
public static final int TYPE_stts = 0x73747473;
340343

libraries/exoplayer/src/test/java/androidx/media3/exoplayer/e2etest/Mp4PlaybackTest.java

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@
1515
*/
1616
package androidx.media3.exoplayer.e2etest;
1717

18+
import static org.robolectric.annotation.GraphicsMode.Mode.NATIVE;
19+
1820
import android.content.Context;
1921
import android.graphics.SurfaceTexture;
2022
import android.view.Surface;
23+
import androidx.annotation.Nullable;
2124
import androidx.media3.common.MediaItem;
2225
import androidx.media3.common.Player;
2326
import androidx.media3.exoplayer.ExoPlayer;
@@ -35,44 +38,47 @@
3538
import org.robolectric.ParameterizedRobolectricTestRunner;
3639
import org.robolectric.ParameterizedRobolectricTestRunner.Parameter;
3740
import org.robolectric.ParameterizedRobolectricTestRunner.Parameters;
41+
import org.robolectric.annotation.GraphicsMode;
3842

3943
/** End-to-end tests using MP4 samples. */
44+
@GraphicsMode(NATIVE)
4045
@RunWith(ParameterizedRobolectricTestRunner.class)
4146
public class Mp4PlaybackTest {
4247

4348
@Parameters(name = "{0}")
44-
public static ImmutableList<String> mediaSamples() {
49+
public static ImmutableList<Sample> mediaSamples() {
4550
return ImmutableList.of(
46-
"midroll-5s.mp4",
47-
"postroll-5s.mp4",
48-
"preroll-5s.mp4",
49-
"pixel-motion-photo-2-hevc-tracks.mp4",
50-
"sample_ac3_fragmented.mp4",
51-
"sample_ac3.mp4",
52-
"sample_ac4_fragmented.mp4",
53-
"sample_ac4.mp4",
54-
"sample_android_slow_motion.mp4",
55-
"sample_eac3_fragmented.mp4",
56-
"sample_eac3.mp4",
57-
"sample_eac3joc_fragmented.mp4",
58-
"sample_eac3joc.mp4",
59-
"sample_fragmented.mp4",
60-
"sample_fragmented_seekable.mp4",
61-
"sample_fragmented_large_bitrates.mp4",
62-
"sample_fragmented_sei.mp4",
63-
"sample_mdat_too_long.mp4",
64-
"sample.mp4",
65-
"sample_with_metadata.mp4",
66-
"sample_with_numeric_genre.mp4",
67-
"sample_opus_fragmented.mp4",
68-
"sample_opus.mp4",
69-
"sample_partially_fragmented.mp4",
70-
"testvid_1022ms.mp4",
71-
"sample_edit_list.mp4",
72-
"sample_edit_list_no_sync_frame_before_edit.mp4");
51+
Sample.forFile("midroll-5s.mp4"),
52+
Sample.forFile("postroll-5s.mp4"),
53+
Sample.forFile("preroll-5s.mp4"),
54+
Sample.forFile("pixel-motion-photo-2-hevc-tracks.mp4"),
55+
Sample.forFile("sample_ac3_fragmented.mp4"),
56+
Sample.forFile("sample_ac3.mp4"),
57+
Sample.forFile("sample_ac4_fragmented.mp4"),
58+
Sample.forFile("sample_ac4.mp4"),
59+
Sample.forFile("sample_android_slow_motion.mp4"),
60+
Sample.forFile("sample_eac3_fragmented.mp4"),
61+
Sample.forFile("sample_eac3.mp4"),
62+
Sample.forFile("sample_eac3joc_fragmented.mp4"),
63+
Sample.forFile("sample_eac3joc.mp4"),
64+
Sample.forFile("sample_fragmented.mp4"),
65+
Sample.forFile("sample_fragmented_seekable.mp4"),
66+
Sample.forFile("sample_fragmented_large_bitrates.mp4"),
67+
Sample.forFile("sample_fragmented_sei.mp4"),
68+
Sample.forFile("sample_mdat_too_long.mp4"),
69+
Sample.forFile("sample.mp4"),
70+
Sample.forFile("sample_with_metadata.mp4"),
71+
Sample.forFile("sample_with_numeric_genre.mp4"),
72+
Sample.forFile("sample_opus_fragmented.mp4"),
73+
Sample.forFile("sample_opus.mp4"),
74+
Sample.forFile("sample_partially_fragmented.mp4"),
75+
Sample.withSubtitles("sample_with_vobsub.mp4", "eng"),
76+
Sample.forFile("testvid_1022ms.mp4"),
77+
Sample.forFile("sample_edit_list.mp4"),
78+
Sample.forFile("sample_edit_list_no_sync_frame_before_edit.mp4"));
7379
}
7480

75-
@Parameter public String inputFile;
81+
@Parameter public Sample sample;
7682

7783
@Rule
7884
public ShadowMediaCodecConfig mediaCodecConfig =
@@ -86,19 +92,50 @@ public void test() throws Exception {
8692
new ExoPlayer.Builder(applicationContext, renderersFactory)
8793
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
8894
.build();
95+
if (sample.subtitleLanguageToSelect != null) {
96+
player.setTrackSelectionParameters(
97+
player
98+
.getTrackSelectionParameters()
99+
.buildUpon()
100+
.setPreferredTextLanguage(sample.subtitleLanguageToSelect)
101+
.build());
102+
}
89103
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
90104
player.setVideoSurface(surface);
91105

92106
PlaybackOutput playbackOutput = PlaybackOutput.register(player, renderersFactory);
93107

94-
player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/" + inputFile));
108+
player.setMediaItem(MediaItem.fromUri("asset:///media/mp4/" + sample.filename));
95109
player.prepare();
96110
player.play();
97111
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
98112
player.release();
99113
surface.release();
100114

101115
DumpFileAsserts.assertOutput(
102-
applicationContext, playbackOutput, "playbackdumps/mp4/" + inputFile + ".dump");
116+
applicationContext, playbackOutput, "playbackdumps/mp4/" + sample.filename + ".dump");
117+
}
118+
119+
private static final class Sample {
120+
public final String filename;
121+
@Nullable public final String subtitleLanguageToSelect;
122+
123+
private Sample(String filename, @Nullable String subtitleLanguageToSelect) {
124+
this.filename = filename;
125+
this.subtitleLanguageToSelect = subtitleLanguageToSelect;
126+
}
127+
128+
public static Sample forFile(String filename) {
129+
return new Sample(filename, /* subtitleLanguageToSelect= */ null);
130+
}
131+
132+
public static Sample withSubtitles(String filename, String subtitleLanguageToSelect) {
133+
return new Sample(filename, /* enableSubtitles= */ subtitleLanguageToSelect);
134+
}
135+
136+
@Override
137+
public String toString() {
138+
return filename;
139+
}
103140
}
104141
}

0 commit comments

Comments
 (0)