Skip to content

Commit be6202f

Browse files
authored
Subtitle extraction from streaming media manifests #247
Authored by fstirlitz Modified from: ytdl-org/youtube-dl#6144 Closes: #73 Fixes: ytdl-org/youtube-dl#6106 ytdl-org/youtube-dl#14977 ytdl-org/youtube-dl#21438 ytdl-org/youtube-dl#23609 ytdl-org/youtube-dl#28132 Might also fix (untested): ytdl-org/youtube-dl#15424 ytdl-org/youtube-dl#18267 ytdl-org/youtube-dl#23899 ytdl-org/youtube-dl#24375 ytdl-org/youtube-dl#24595 ytdl-org/youtube-dl#27899 Related: ytdl-org/youtube-dl#22379 ytdl-org/youtube-dl#24517 ytdl-org/youtube-dl#24886 ytdl-org/youtube-dl#27215 Notes: * The functions `extractor.common._extract_..._formats` are still kept for compatibility * Only some extractors have currently been moved to using `_extract_..._formats_and_subtitles` * Direct subtitle manifests (without a master) are not supported and are wrongly identified as containing video formats * AES support is untested * The fragmented TTML subtitles extracted from DASH/ISM are valid, but are unsupported by `ffmpeg` and most video players * Their XML fragments can be dumped using `ffmpeg -i in.mp4 -f data -map 0 -c copy out.ttml`. Once the unnecessary headers are stripped out of this, it becomes a valid self-contained ttml file * The ttml subs downloaded from DASH manifests can also be directly opened with <https://github.com/SubtitleEdit> * Fragmented WebVTT files extracted from DASH/ISM are also unsupported by most tools * Unlike the ttml files, the XML fragments of these cannot be dumped using `ffmpeg` * The webtt subs extracted from DASH can be parsed by <https://github.com/gpac/gpac> * But validity of the those extracted from ISM are untested
2 parents db9a564 + e8f834c commit be6202f

25 files changed

Lines changed: 2731 additions & 268 deletions

‎test/test_InfoExtractor.py‎

Lines changed: 489 additions & 8 deletions
Large diffs are not rendered by default.

‎test/testdata/ism/sintel.Manifest‎

Lines changed: 988 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#EXTM3U
2+
3+
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="eng",NAME="BipBop Audio 1",AUTOSELECT=YES,DEFAULT=YES
4+
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="bipbop_audio",LANGUAGE="eng",NAME="BipBop Audio 2",AUTOSELECT=NO,DEFAULT=NO,URI="alternate_audio_aac/prog_index.m3u8"
5+
6+
7+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English",DEFAULT=YES,AUTOSELECT=YES,FORCED=NO,LANGUAGE="en",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/eng/prog_index.m3u8"
8+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="English (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="en",URI="subtitles/eng_forced/prog_index.m3u8"
9+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="fr",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/fra/prog_index.m3u8"
10+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Français (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="fr",URI="subtitles/fra_forced/prog_index.m3u8"
11+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Español",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="es",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/spa/prog_index.m3u8"
12+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="Español (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="es",URI="subtitles/spa_forced/prog_index.m3u8"
13+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="日本語",DEFAULT=NO,AUTOSELECT=YES,FORCED=NO,LANGUAGE="ja",CHARACTERISTICS="public.accessibility.transcribes-spoken-dialog, public.accessibility.describes-music-and-sound",URI="subtitles/jpn/prog_index.m3u8"
14+
#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",NAME="日本語 (Forced)",DEFAULT=NO,AUTOSELECT=NO,FORCED=YES,LANGUAGE="ja",URI="subtitles/jpn_forced/prog_index.m3u8"
15+
16+
17+
#EXT-X-STREAM-INF:BANDWIDTH=263851,CODECS="mp4a.40.2, avc1.4d400d",RESOLUTION=416x234,AUDIO="bipbop_audio",SUBTITLES="subs"
18+
gear1/prog_index.m3u8
19+
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=28451,CODECS="avc1.4d400d",URI="gear1/iframe_index.m3u8"
20+
21+
#EXT-X-STREAM-INF:BANDWIDTH=577610,CODECS="mp4a.40.2, avc1.4d401e",RESOLUTION=640x360,AUDIO="bipbop_audio",SUBTITLES="subs"
22+
gear2/prog_index.m3u8
23+
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=181534,CODECS="avc1.4d401e",URI="gear2/iframe_index.m3u8"
24+
25+
#EXT-X-STREAM-INF:BANDWIDTH=915905,CODECS="mp4a.40.2, avc1.4d401f",RESOLUTION=960x540,AUDIO="bipbop_audio",SUBTITLES="subs"
26+
gear3/prog_index.m3u8
27+
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=297056,CODECS="avc1.4d401f",URI="gear3/iframe_index.m3u8"
28+
29+
#EXT-X-STREAM-INF:BANDWIDTH=1030138,CODECS="mp4a.40.2, avc1.4d401f",RESOLUTION=1280x720,AUDIO="bipbop_audio",SUBTITLES="subs"
30+
gear4/prog_index.m3u8
31+
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=339492,CODECS="avc1.4d401f",URI="gear4/iframe_index.m3u8"
32+
33+
#EXT-X-STREAM-INF:BANDWIDTH=1924009,CODECS="mp4a.40.2, avc1.4d401f",RESOLUTION=1920x1080,AUDIO="bipbop_audio",SUBTITLES="subs"
34+
gear5/prog_index.m3u8
35+
#EXT-X-I-FRAME-STREAM-INF:BANDWIDTH=669554,CODECS="avc1.4d401f",URI="gear5/iframe_index.m3u8"
36+
37+
#EXT-X-STREAM-INF:BANDWIDTH=41457,CODECS="mp4a.40.2",AUDIO="bipbop_audio",SUBTITLES="subs"
38+
gear0/prog_index.m3u8

‎test/testdata/mpd/subtitles.mpd‎

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- Created with Unified Streaming Platform (version=1.10.18-20255) -->
3+
<MPD
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns="urn:mpeg:dash:schema:mpd:2011"
6+
xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd"
7+
type="static"
8+
mediaPresentationDuration="PT14M48S"
9+
maxSegmentDuration="PT1M"
10+
minBufferTime="PT10S"
11+
profiles="urn:mpeg:dash:profile:isoff-live:2011">
12+
<Period
13+
id="1"
14+
duration="PT14M48S">
15+
<BaseURL>dash/</BaseURL>
16+
<AdaptationSet
17+
id="1"
18+
group="1"
19+
contentType="audio"
20+
segmentAlignment="true"
21+
audioSamplingRate="48000"
22+
mimeType="audio/mp4"
23+
codecs="mp4a.40.2"
24+
startWithSAP="1">
25+
<AudioChannelConfiguration
26+
schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011"
27+
value="2" />
28+
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main" />
29+
<SegmentTemplate
30+
timescale="48000"
31+
initialization="3144-kZT4LWMQw6Rh7Kpd-$RepresentationID$.dash"
32+
media="3144-kZT4LWMQw6Rh7Kpd-$RepresentationID$-$Time$.dash">
33+
<SegmentTimeline>
34+
<S t="0" d="96256" r="2" />
35+
<S d="95232" />
36+
<S d="96256" r="2" />
37+
<S d="95232" />
38+
<S d="96256" r="2" />
39+
<S d="95232" />
40+
<S d="96256" r="2" />
41+
<S d="95232" />
42+
<S d="96256" r="2" />
43+
<S d="95232" />
44+
<S d="96256" r="2" />
45+
<S d="95232" />
46+
<S d="96256" r="2" />
47+
<S d="95232" />
48+
<S d="96256" r="2" />
49+
<S d="95232" />
50+
<S d="96256" r="2" />
51+
<S d="95232" />
52+
<S d="96256" r="2" />
53+
<S d="95232" />
54+
<S d="96256" r="2" />
55+
<S d="95232" />
56+
<S d="96256" r="2" />
57+
<S d="95232" />
58+
<S d="96256" r="2" />
59+
<S d="95232" />
60+
<S d="96256" r="2" />
61+
<S d="95232" />
62+
<S d="96256" r="2" />
63+
<S d="95232" />
64+
<S d="96256" r="2" />
65+
<S d="95232" />
66+
<S d="96256" r="2" />
67+
<S d="95232" />
68+
<S d="96256" r="2" />
69+
<S d="95232" />
70+
<S d="96256" r="2" />
71+
<S d="95232" />
72+
<S d="96256" r="2" />
73+
<S d="95232" />
74+
<S d="96256" r="2" />
75+
<S d="95232" />
76+
<S d="96256" r="2" />
77+
<S d="95232" />
78+
<S d="96256" r="2" />
79+
<S d="95232" />
80+
<S d="96256" r="2" />
81+
<S d="95232" />
82+
<S d="96256" r="2" />
83+
<S d="95232" />
84+
<S d="96256" r="2" />
85+
<S d="95232" />
86+
<S d="96256" r="2" />
87+
<S d="95232" />
88+
<S d="96256" r="2" />
89+
<S d="95232" />
90+
<S d="96256" r="2" />
91+
<S d="95232" />
92+
<S d="96256" r="2" />
93+
<S d="95232" />
94+
<S d="96256" r="2" />
95+
<S d="95232" />
96+
<S d="96256" r="2" />
97+
<S d="95232" />
98+
<S d="96256" r="2" />
99+
<S d="95232" />
100+
<S d="96256" r="2" />
101+
<S d="95232" />
102+
<S d="96256" r="2" />
103+
<S d="95232" />
104+
<S d="96256" r="2" />
105+
<S d="95232" />
106+
<S d="96256" r="2" />
107+
<S d="95232" />
108+
<S d="96256" r="2" />
109+
<S d="95232" />
110+
<S d="96256" r="2" />
111+
<S d="95232" />
112+
<S d="96256" r="2" />
113+
<S d="95232" />
114+
<S d="96256" r="2" />
115+
<S d="95232" />
116+
<S d="96256" r="2" />
117+
<S d="95232" />
118+
<S d="96256" r="2" />
119+
<S d="95232" />
120+
<S d="96256" r="2" />
121+
<S d="95232" />
122+
<S d="96256" r="2" />
123+
<S d="95232" />
124+
<S d="96256" r="2" />
125+
<S d="95232" />
126+
<S d="96256" r="2" />
127+
<S d="95232" />
128+
<S d="96256" r="2" />
129+
<S d="95232" />
130+
<S d="96256" r="2" />
131+
<S d="95232" />
132+
<S d="96256" r="2" />
133+
<S d="95232" />
134+
<S d="96256" r="2" />
135+
<S d="95232" />
136+
<S d="96256" r="2" />
137+
<S d="95232" />
138+
<S d="96256" r="2" />
139+
<S d="95232" />
140+
<S d="96256" r="2" />
141+
<S d="95232" />
142+
<S d="96256" r="2" />
143+
<S d="95232" />
144+
<S d="96256" r="2" />
145+
<S d="95232" />
146+
<S d="96256" r="2" />
147+
<S d="95232" />
148+
<S d="96256" r="2" />
149+
<S d="95232" />
150+
<S d="96256" r="2" />
151+
<S d="95232" />
152+
<S d="96256" r="2" />
153+
<S d="95232" />
154+
<S d="96256" r="2" />
155+
<S d="95232" />
156+
<S d="96256" r="2" />
157+
<S d="95232" />
158+
<S d="96256" r="2" />
159+
<S d="95232" />
160+
<S d="96256" r="2" />
161+
<S d="95232" />
162+
<S d="96256" r="2" />
163+
<S d="95232" />
164+
<S d="96256" r="2" />
165+
<S d="95232" />
166+
<S d="96256" r="2" />
167+
<S d="95232" />
168+
<S d="96256" r="2" />
169+
<S d="95232" />
170+
<S d="96256" r="2" />
171+
<S d="95232" />
172+
<S d="96256" r="2" />
173+
<S d="95232" />
174+
<S d="96256" r="2" />
175+
<S d="95232" />
176+
<S d="96256" r="2" />
177+
<S d="95232" />
178+
<S d="96256" r="2" />
179+
<S d="95232" />
180+
<S d="96256" r="2" />
181+
<S d="95232" />
182+
<S d="96256" r="2" />
183+
<S d="95232" />
184+
<S d="96256" r="2" />
185+
<S d="95232" />
186+
<S d="96256" r="2" />
187+
<S d="95232" />
188+
<S d="96256" r="2" />
189+
<S d="95232" />
190+
<S d="96256" r="2" />
191+
<S d="95232" />
192+
<S d="96256" r="2" />
193+
<S d="95232" />
194+
<S d="96256" r="2" />
195+
<S d="95232" />
196+
<S d="96256" r="2" />
197+
<S d="95232" />
198+
<S d="96256" r="2" />
199+
<S d="95232" />
200+
<S d="96256" r="2" />
201+
<S d="95232" />
202+
<S d="96256" r="2" />
203+
<S d="95232" />
204+
<S d="96256" r="2" />
205+
<S d="95232" />
206+
<S d="96256" r="2" />
207+
<S d="95232" />
208+
<S d="96256" r="2" />
209+
<S d="95232" />
210+
<S d="96256" r="2" />
211+
<S d="95232" />
212+
<S d="96256" r="2" />
213+
<S d="95232" />
214+
<S d="96256" r="2" />
215+
<S d="95232" />
216+
<S d="96256" r="2" />
217+
<S d="95232" />
218+
<S d="96256" r="2" />
219+
<S d="95232" />
220+
<S d="96256" r="2" />
221+
<S d="95232" />
222+
<S d="96256" r="2" />
223+
<S d="95232" />
224+
<S d="96256" r="2" />
225+
<S d="95232" />
226+
<S d="96256" r="2" />
227+
<S d="95232" />
228+
<S d="96256" r="2" />
229+
<S d="95232" />
230+
<S d="96256" r="2" />
231+
<S d="95232" />
232+
<S d="96256" r="2" />
233+
<S d="95232" />
234+
<S d="96256" r="2" />
235+
<S d="95232" />
236+
<S d="96256" r="2" />
237+
<S d="95232" />
238+
<S d="96256" r="2" />
239+
<S d="95232" />
240+
<S d="96256" r="2" />
241+
<S d="95232" />
242+
<S d="96256" r="2" />
243+
<S d="95232" />
244+
<S d="96256" r="2" />
245+
<S d="95232" />
246+
<S d="96256" r="2" />
247+
<S d="95232" />
248+
<S d="96256" r="2" />
249+
<S d="95232" />
250+
<S d="96256" r="2" />
251+
<S d="95232" />
252+
<S d="96256" r="2" />
253+
<S d="95232" />
254+
<S d="96256" r="2" />
255+
<S d="95232" />
256+
<S d="3584" />
257+
</SegmentTimeline>
258+
</SegmentTemplate>
259+
<Representation
260+
id="audio=128001"
261+
bandwidth="128001">
262+
</Representation>
263+
</AdaptationSet>
264+
<AdaptationSet
265+
id="2"
266+
group="3"
267+
contentType="text"
268+
lang="en"
269+
mimeType="application/mp4"
270+
codecs="stpp"
271+
startWithSAP="1">
272+
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="subtitle" />
273+
<SegmentTemplate
274+
timescale="1000"
275+
initialization="3144-kZT4LWMQw6Rh7Kpd-$RepresentationID$.dash"
276+
media="3144-kZT4LWMQw6Rh7Kpd-$RepresentationID$-$Time$.dash">
277+
<SegmentTimeline>
278+
<S t="0" d="60000" r="9" />
279+
<S d="24000" />
280+
</SegmentTimeline>
281+
</SegmentTemplate>
282+
<Representation
283+
id="textstream_eng=1000"
284+
bandwidth="1000">
285+
</Representation>
286+
</AdaptationSet>
287+
<AdaptationSet
288+
id="3"
289+
group="2"
290+
contentType="video"
291+
par="960:409"
292+
minBandwidth="100000"
293+
maxBandwidth="4482000"
294+
maxWidth="1689"
295+
maxHeight="720"
296+
segmentAlignment="true"
297+
mimeType="video/mp4"
298+
codecs="avc1.4D401F"
299+
startWithSAP="1">
300+
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main" />
301+
<SegmentTemplate
302+
timescale="12288"
303+
initialization="3144-kZT4LWMQw6Rh7Kpd-$RepresentationID$.dash"
304+
media="3144-kZT4LWMQw6Rh7Kpd-$RepresentationID$-$Time$.dash">
305+
<SegmentTimeline>
306+
<S t="0" d="24576" r="443" />
307+
</SegmentTimeline>
308+
</SegmentTemplate>
309+
<Representation
310+
id="video=100000"
311+
bandwidth="100000"
312+
width="336"
313+
height="144"
314+
sar="2880:2863"
315+
scanType="progressive">
316+
</Representation>
317+
<Representation
318+
id="video=326000"
319+
bandwidth="326000"
320+
width="562"
321+
height="240"
322+
sar="115200:114929"
323+
scanType="progressive">
324+
</Representation>
325+
<Representation
326+
id="video=698000"
327+
bandwidth="698000"
328+
width="844"
329+
height="360"
330+
sar="86400:86299"
331+
scanType="progressive">
332+
</Representation>
333+
<Representation
334+
id="video=1493000"
335+
bandwidth="1493000"
336+
width="1126"
337+
height="480"
338+
sar="230400:230267"
339+
scanType="progressive">
340+
</Representation>
341+
<Representation
342+
id="video=4482000"
343+
bandwidth="4482000"
344+
width="1688"
345+
height="720"
346+
sar="86400:86299"
347+
scanType="progressive">
348+
</Representation>
349+
</AdaptationSet>
350+
</Period>
351+
</MPD>

0 commit comments

Comments
 (0)