From 19e12b5e282e661a9a17a6bfbb87c565faf2bc6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81lvaro=20Velad=20Galv=C3=A1n?= Date: Wed, 20 Apr 2022 23:23:54 +0200 Subject: [PATCH] feat(hls): make a head request if hls subtitles have no extension (#4140) Closes https://github.com/shaka-project/shaka-player/issues/4135 Closes https://github.com/shaka-project/shaka-player/issues/1959 --- lib/hls/hls_parser.js | 10 ++++-- test/hls/hls_parser_unit.js | 63 +++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index 97934283f2..5817a1d06d 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -2252,11 +2252,10 @@ shaka.hls.HlsParser = class { if (contentType == ContentType.TEXT) { // The extension map didn't work. - if (!codecs || codecs == 'vtt' || codecs == 'wvtt') { + if (codecs == 'vtt' || codecs == 'wvtt') { // If codecs is 'vtt', it's WebVTT. - // If there was no codecs string, assume HLS text streams are WebVTT. return 'text/vtt'; - } else { + } else if (codecs && codecs !== '') { // Otherwise, assume MP4-embedded text, since text-based formats tend // not to have a codecs string at all. return 'application/mp4'; @@ -2281,6 +2280,11 @@ shaka.hls.HlsParser = class { const contentMimeType = response.headers['content-type']; if (!contentMimeType) { + if (contentType == ContentType.TEXT) { + // If there was no codecs string and no content-type, assume HLS text + // streams are WebVTT. + return 'text/vtt'; + } // If the HLS content is lacking in both MIME type metadata and // segment file extensions, we fall back to assuming it's MP4. const fallbackMimeType = map['mp4']; diff --git a/test/hls/hls_parser_unit.js b/test/hls/hls_parser_unit.js index 1bc5e82595..dad19f5188 100644 --- a/test/hls/hls_parser_unit.js +++ b/test/hls/hls_parser_unit.js @@ -1138,6 +1138,69 @@ describe('HlsParser', () => { expect(actual).toEqual(manifest); }); + it('gets mime type of SUBTITLES from header request', async () => { + const master = [ + '#EXTM3U\n', + '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",', + 'URI="audio"\n', + '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",', + 'URI="text"\n', + '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",', + 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n', + 'video\n', + ].join(''); + + const media = [ + '#EXTM3U\n', + '#EXT-X-PLAYLIST-TYPE:VOD\n', + '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n', + '#EXTINF:5,\n', + '#EXT-X-BYTERANGE:121090@616\n', + 'main.mp4', + ].join(''); + + const textMedia = [ + '#EXTM3U\n', + '#EXT-X-PLAYLIST-TYPE:VOD\n', + '#EXTINF:5,\n', + '#EXT-X-BYTERANGE:121090@616\n', + 'main.subs', + ].join(''); + + const manifest = shaka.test.ManifestGenerator.generate((manifest) => { + manifest.anyTimeline(); + manifest.addPartialVariant((variant) => { + variant.addPartialStream(ContentType.VIDEO, (stream) => { + stream.mime('video/mp4', 'avc1'); + }); + variant.addPartialStream(ContentType.AUDIO, (stream) => { + stream.mime('audio/mp4', 'mp4a'); + }); + }); + manifest.addPartialTextStream((stream) => { + stream.language = 'en'; + stream.kind = TextStreamKind.SUBTITLE; + stream.mime('application/mp4', ''); + }); + manifest.sequenceMode = true; + }); + + fakeNetEngine + .setResponseText('test:/master', master) + .setResponseText('test:/audio', media) + .setResponseText('test:/video', media) + .setResponseText('test:/text', textMedia) + .setResponseText('test:/main.subs', vttText) + .setHeaders('test:/main.subs', { + 'content-type': 'application/mp4; foo=bar', + }) + .setResponseValue('test:/init.mp4', initSegmentData) + .setResponseValue('test:/main.mp4', segmentData); + + const actual = await parser.start('test:/master', playerInterface); + expect(actual).toEqual(manifest); + }); + it('parses manifest with FORCED SUBTITLES', async () => { const master = [ '#EXTM3U\n',