shaka-player
Version:
DASH/EME video player library
1,556 lines (1,371 loc) • 79.8 kB
JavaScript
/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
describe('HlsParser', function() {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
const ManifestParser = shaka.test.ManifestParser;
const TextStreamKind = shaka.util.ManifestParserUtils.TextStreamKind;
const Util = shaka.test.Util;
const originalAlwaysWarn = shaka.log.alwaysWarn;
const vttText = [
'WEBVTT\n',
'\n',
'00:03.837 --> 00:07.297\n',
'Hello, world!\n',
].join('');
/** @type {!shaka.test.FakeNetworkingEngine} */
let fakeNetEngine;
/** @type {!shaka.hls.HlsParser} */
let parser;
/** @type {shaka.extern.ManifestParser.PlayerInterface} */
let playerInterface;
/** @type {shaka.extern.ManifestConfiguration} */
let config;
/** @type {!ArrayBuffer} */
let initSegmentData;
/** @type {!ArrayBuffer} */
let segmentData;
/** @type {!ArrayBuffer} */
let selfInitializingSegmentData;
afterEach(() => {
shaka.log.alwaysWarn = originalAlwaysWarn;
});
beforeEach(function() {
// TODO: use StreamGenerator?
initSegmentData = new Uint8Array([
0x00, 0x00, 0x00, 0x30, // size (48)
0x6D, 0x6F, 0x6F, 0x76, // type (moov)
0x00, 0x00, 0x00, 0x28, // trak size (40)
0x74, 0x72, 0x61, 0x6B, // type (trak)
0x00, 0x00, 0x00, 0x20, // mdia size (32)
0x6D, 0x64, 0x69, 0x61, // type (mdia)
0x00, 0x00, 0x00, 0x18, // mdhd size (24)
0x6D, 0x64, 0x68, 0x64, // type (mdhd)
0x00, 0x00, 0x00, 0x00, // version and flags
0x00, 0x00, 0x00, 0x00, // creation time (0)
0x00, 0x00, 0x00, 0x00, // modification time (0)
0x00, 0x00, 0x03, 0xe8, // timescale (1000)
]).buffer;
segmentData = new Uint8Array([
0x00, 0x00, 0x00, 0x24, // size (36)
0x6D, 0x6F, 0x6F, 0x66, // type (moof)
0x00, 0x00, 0x00, 0x1C, // traf size (28)
0x74, 0x72, 0x61, 0x66, // type (traf)
0x00, 0x00, 0x00, 0x14, // tfdt size (20)
0x74, 0x66, 0x64, 0x74, // type (tfdt)
0x01, 0x00, 0x00, 0x00, // version and flags
0x00, 0x00, 0x00, 0x00, // baseMediaDecodeTime first 4 bytes (0)
0x00, 0x00, 0x00, 0x00, // baseMediaDecodeTime last 4 bytes (0)
]).buffer;
// segment starts at 0s.
selfInitializingSegmentData = shaka.util.Uint8ArrayUtils.concat(
new Uint8Array(initSegmentData),
new Uint8Array(segmentData)).buffer;
fakeNetEngine = new shaka.test.FakeNetworkingEngine();
config = shaka.util.PlayerConfiguration.createDefault().manifest;
playerInterface = {
filterNewPeriod: function() {},
filterAllPeriods: function() {},
networkingEngine: fakeNetEngine,
onError: fail,
onEvent: fail,
onTimelineRegionAdded: fail,
};
parser = new shaka.hls.HlsParser();
parser.configure(config);
});
/**
* @param {string} master
* @param {string} media
* @param {shaka.extern.Manifest} manifest
* @return {!Promise.<shaka.extern.Manifest>}
*/
async function testHlsParser(master, media, manifest) {
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/audio2', media)
.setResponseText('test:/video', media)
.setResponseText('test:/video2', media)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData)
.setResponseValue('test:/main.test', segmentData)
.setResponseValue('test:/selfInit.mp4', selfInitializingSegmentData);
let actual = await parser.start('test:/master', playerInterface);
expect(actual).toEqual(manifest);
return actual;
}
it('parses manifest attributes', function(done) {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'CHANNELS="2",URI="audio"\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub2",LANGUAGE="es",',
'URI="text2"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\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.vtt',
].join('');
const manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.language('en')
.bandwidth(200)
.addPartialStream(ContentType.VIDEO)
.anyInitSegment()
.presentationTimeOffset(0)
.mime('video/mp4', 'avc1')
.frameRate(60)
.size(960, 540)
.addPartialStream(ContentType.AUDIO)
.language('en')
.anyInitSegment()
.presentationTimeOffset(0)
.mime('audio/mp4', 'mp4a')
.channelsCount(2)
.addPartialStream(ContentType.TEXT)
.language('en')
.nullInitSegment()
.presentationTimeOffset(0)
.mime('text/vtt', '')
.kind(TextStreamKind.SUBTITLE)
.addPartialStream(ContentType.TEXT)
.language('es')
.nullInitSegment()
.presentationTimeOffset(0)
.mime('text/vtt', '')
.kind(TextStreamKind.SUBTITLE)
.build();
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', textMedia)
.setResponseText('test:/text2', textMedia)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
parser.start('test:/master', playerInterface)
.then(function(actual) { expect(actual).toEqual(manifest); })
.catch(fail).then(done);
});
it('ignores duplicate CODECS', async function() {
let master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1.4d001e,avc1.42000d",',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'video',
].join('');
let 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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1.4d001e')
.build();
await testHlsParser(master, media, manifest);
});
it('parses video-only variant', async function() {
let master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'video',
].join('');
let 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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.build();
await testHlsParser(master, media, manifest);
});
it('guesses video-only variant by codecs', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1"\n',
'video',
].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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.build();
await testHlsParser(master, media, manifest);
});
it('parses audio-only variant', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="mp4a"\n',
'audio',
].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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', 'mp4a')
.build();
await testHlsParser(master, media, manifest);
});
it('parses audio+video variant', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'CHANNELS="2",URI="audio"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', 'mp4a')
.build();
await testHlsParser(master, media, manifest);
});
it('parses audio+video variant with closed captions', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",CHANNELS="2",',
'URI="audio"\n',
'#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="cap1",LANGUAGE="eng",',
'INSTREAM-ID="CC1"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,CLOSED-CAPTIONS="cap1",AUDIO="aud1"\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 closedCaptions = new Map([['CC1', 'en']]);
const manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.closedCaptions(closedCaptions)
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', 'mp4a')
.build();
await testHlsParser(master, media, manifest);
});
it('parses audio+video variant with no closed captions', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",CHANNELS="2",',
'URI="audio"\n',
'#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="cap1",LANGUAGE="eng",',
'INSTREAM-ID="CC1"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,CLOSED-CAPTIONS="NONE",AUDIO="aud1"\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 manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', 'mp4a')
.build();
await testHlsParser(master, media, manifest);
});
it('handles audio tags on audio streams', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="mp4a",AUDIO="aud1"\n',
'audio\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'URI="audio"\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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', 'mp4a')
.build();
await testHlsParser(master, media, manifest);
});
it('sets seek range correctly for non-zero start', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'video',
].join('');
const media = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MEDIA-SEQUENCE:131\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.mp4',
].join('');
segmentData = new Uint8Array([
0x00, 0x00, 0x00, 0x24, // size (36)
0x6D, 0x6F, 0x6F, 0x66, // type (moof)
0x00, 0x00, 0x00, 0x1C, // traf size (28)
0x74, 0x72, 0x61, 0x66, // type (traf)
0x00, 0x00, 0x00, 0x14, // tfdt size (20)
0x74, 0x66, 0x64, 0x74, // type (tfdt)
0x01, 0x00, 0x00, 0x00, // version and flags
0x00, 0x00, 0x00, 0x00, // baseMediaDecodeTime first 4 bytes (0)
0x00, 0x0A, 0x00, 0x00, // baseMediaDecodeTime last 4 bytes (655360)
]).buffer;
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/video', media)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
let manifest = await parser.start('test:/master', playerInterface);
let presentationTimeline = manifest.presentationTimeline;
const stream = manifest.periods[0].variants[0].video;
// baseMediaDecodeTime (655360) / timescale (1000)
expect(stream.presentationTimeOffset).toBe(655.36);
const pos = stream.findSegmentPosition(0);
expect(pos).not.toBe(null);
expect(stream.getSegmentReference(pos).startTime).toBe(0);
expect(presentationTimeline.getSeekRangeStart()).toBe(0);
expect(presentationTimeline.getSeekRangeEnd()).toBe(5);
});
it('parses multiplexed variant', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'video',
].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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1,mp4a')
.build();
await testHlsParser(master, media, manifest);
});
it('parses multiplexed variant without codecs', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'video',
].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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', jasmine.any(String))
.build();
await testHlsParser(master, media, manifest);
});
it('parses audio+video variant without codecs', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
'video\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'URI="audio"\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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', jasmine.any(String))
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', jasmine.any(String))
.build();
await testHlsParser(master, media, manifest);
});
it('parses audio variant without URI', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
'video\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",NAME="audio"\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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', jasmine.any(String))
.build();
await testHlsParser(master, media, manifest);
});
it('parses video variant without URI', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="mp4a",VIDEO="vid1"\n',
'audio\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid1",NAME="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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', jasmine.any(String))
.build();
await testHlsParser(master, media, manifest);
});
it('parses multiple variants', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
'video\n',
'#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud2"\n',
'video2\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'URI="audio"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud2",LANGUAGE="fr",',
'URI="audio2"\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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.bandwidth(200)
.addPartialStream(ContentType.VIDEO)
.size(960, 540)
.addPartialStream(ContentType.AUDIO)
.language('en')
.addPartialVariant()
.bandwidth(300)
.addPartialStream(ContentType.VIDEO)
.size(960, 540)
.addPartialStream(ContentType.AUDIO)
.language('fr')
.build();
await testHlsParser(master, media, manifest);
});
it('parses multiple streams with the same group id', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
'video\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
'URI="audio"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="fr",',
'URI="audio2"\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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.language('en')
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.AUDIO)
.language('en')
.addPartialVariant()
.language('fr')
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.AUDIO)
.language('fr')
.build();
await testHlsParser(master, media, manifest);
});
it('parses characteristics from audio tags', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
'video\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
'URI="audio"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
'CHARACTERISTICS="public.accessibility.describes-video,',
'public.accessibility.describes-music-and-sound",URI="audio2"\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 manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.language('en')
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.AUDIO)
.language('en')
.addPartialVariant()
.language('en')
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.AUDIO)
.language('en')
.roles([
'public.accessibility.describes-video',
'public.accessibility.describes-music-and-sound',
])
.build();
await testHlsParser(master, media, manifest);
});
it('should call filterAllPeriods for parsing', function(done) {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'video',
].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('');
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
let filterAllPeriods = jasmine.createSpy('filterAllPeriods');
playerInterface.filterAllPeriods = Util.spyFunc(filterAllPeriods);
parser.start('test:/master', playerInterface)
.then(function(manifest) {
expect(filterAllPeriods.calls.count()).toBe(1);
}).catch(fail).then(done);
});
it('gets mime type from header request', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
'RESOLUTION=960x540,FRAME-RATE=60\n',
'video',
].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.test',
].join('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.build();
// The extra parameters should be stripped by the parser.
fakeNetEngine.setHeaders(
'test:/main.test', {
'content-type': 'video/mp4; foo=bar',
});
await testHlsParser(master, media, manifest);
});
it('parses manifest with text streams', 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-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub2",LANGUAGE="es",',
'URI="text2"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
'video\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub2"\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.vtt',
].join('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.addPartialStream(ContentType.AUDIO)
.mime('audio/mp4', 'mp4a')
.addPartialStream(ContentType.TEXT)
.language('en')
.mime('text/vtt', '')
.kind(TextStreamKind.SUBTITLE)
.addPartialStream(ContentType.TEXT)
.language('es')
.mime('text/vtt', '')
.kind(TextStreamKind.SUBTITLE)
.build();
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', textMedia)
.setResponseText('test:/text2', textMedia)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
let actual = await parser.start('test:/master', playerInterface);
expect(actual).toEqual(manifest);
});
it('parses manifest with text streams without SUBTITLES', 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-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub2",LANGUAGE="es",',
'URI="text2"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\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.vtt',
].join('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.AUDIO)
.addPartialStream(ContentType.TEXT)
.mime('text/vtt', '')
.kind(TextStreamKind.SUBTITLE)
.addPartialStream(ContentType.TEXT)
.mime('text/vtt', '')
.kind(TextStreamKind.SUBTITLE)
.build();
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', textMedia)
.setResponseText('test:/text2', textMedia)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
let actual = await parser.start('test:/master', playerInterface);
expect(actual).toEqual(manifest);
});
it('calculates duration from stream lengths', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'CHANNELS="2",URI="audio"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
'video\n',
].join('');
const video = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const audio = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const text = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.vtt',
].join('');
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', audio)
.setResponseText('test:/video', video)
.setResponseText('test:/text', text)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
let actual = await parser.start('test:/master', playerInterface);
// Duration should be the minimum of the streams, but ignore the text
// stream.
let timeline = actual.presentationTimeline;
expect(timeline.getDuration()).toBe(10);
let period = actual.periods[0];
expect(period.textStreams.length).toBe(1);
expect(period.variants.length).toBe(1);
expect(period.variants[0].audio).toBeTruthy();
expect(period.variants[0].video).toBeTruthy();
});
it('Disable audio does not create audio streams', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'CHANNELS="2",URI="audio"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
'video\n',
].join('');
const video = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const audio = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const text = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.vtt',
].join('');
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', audio)
.setResponseText('test:/video', video)
.setResponseText('test:/text', text)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
config.disableAudio = true;
parser.configure(config);
const actual = await parser.start('test:/master', playerInterface);
const variant = actual.periods[0].variants[0];
expect(variant.audio).toBe(null);
expect(variant.video).toBeTruthy();
});
it('Disable video does not create video streams', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'CHANNELS="2",URI="audio"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
'video\n',
].join('');
const video = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const audio = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const text = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.vtt',
].join('');
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', audio)
.setResponseText('test:/video', video)
.setResponseText('test:/text', text)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
config.disableVideo = true;
parser.configure(config);
const actual = await parser.start('test:/master', playerInterface);
const variant = actual.periods[0].variants[0];
expect(variant.audio).toBeTruthy();
expect(variant.video).toBe(null);
});
it('Disable text does not create text streams', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'CHANNELS="2",URI="audio"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
'video\n',
].join('');
const video = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const audio = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'main.mp4\n',
'#EXTINF:5,\n',
'main.mp4\n',
].join('');
const text = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.vtt',
].join('');
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', audio)
.setResponseText('test:/video', video)
.setResponseText('test:/text', text)
.setResponseText('test:/main.vtt', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
config.disableText = true;
parser.configure(config);
const actual = await parser.start('test:/master', playerInterface);
const stream = actual.periods[0].textStreams[0];
expect(stream).toBeUndefined();
});
it('parses manifest with MP4+TTML streams', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,stpp.ttml.im1t",',
'RESOLUTION=960x540,FRAME-RATE=60,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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.TEXT)
.language('en')
.mime('application/mp4', 'stpp.ttml.im1t')
.build();
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', media)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
let actual = await parser.start('test:/master', playerInterface);
expect(actual).toEqual(manifest);
});
it('detects VTT streams by codec', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,vtt",',
'RESOLUTION=960x540,FRAME-RATE=60,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.foo',
].join('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.TEXT)
.mime('text/vtt', 'vtt')
.build();
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', textMedia)
.setResponseText('test:/main.foo', vttText)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
let actual = await parser.start('test:/master', playerInterface);
expect(actual).toEqual(manifest);
});
it('allows init segments in text streams', function(done) {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,wvtt",',
'RESOLUTION=960x540,FRAME-RATE=60,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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(jasmine.any(Number))
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.addPartialStream(ContentType.TEXT)
.anyInitSegment()
.kind(TextStreamKind.SUBTITLE)
.build();
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', media)
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
parser.start('test:/master', playerInterface)
.then(function(actual) { expect(actual).toEqual(manifest); })
.catch(fail).then(done);
});
it('drops failed text streams when configured to', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,vtt",',
'RESOLUTION=960x540,FRAME-RATE=60,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.foo',
].join('');
/* eslint-disable indent */
const manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.build();
/* eslint-enable indent */
fakeNetEngine
.setResponseText('test:/master', master)
.setResponseText('test:/audio', media)
.setResponseText('test:/video', media)
.setResponseText('test:/text', textMedia)
.setResponseText('test:/main.foo', '')
.setResponseValue('test:/init.mp4', initSegmentData)
.setResponseValue('test:/main.mp4', segmentData);
config.hls.ignoreTextStreamFailures = true;
const actual = await parser.start('test:/master', playerInterface);
expect(actual).toEqual(manifest);
});
it('parses video described by a media tag', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,VIDEO="vid"\n',
'audio\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",URI="video"',
].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('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.mime('video/mp4', 'avc1')
.frameRate(60)
.size(960, 540)
.addPartialStream(ContentType.AUDIO)
.build();
await testHlsParser(master, media, manifest);
});
it('constructs relative URIs', function(done) {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,VIDEO="vid"\n',
'audio/audio.m3u8\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",URI="video/video.m3u8"',
].join('');
const media = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="init.mp4"\n',
'#EXTINF:5,\n',
'segment.mp4',
].join('');
fakeNetEngine
.setResponseText('test:/host/master.m3u8', master)
.setResponseText('test:/host/audio/audio.m3u8', media)
.setResponseText('test:/host/video/video.m3u8', media)
.setResponseValue('test:/host/audio/init.mp4', initSegmentData)
.setResponseValue('test:/host/audio/segment.mp4', segmentData)
.setResponseValue('test:/host/video/init.mp4', initSegmentData)
.setResponseValue('test:/host/video/segment.mp4', segmentData);
parser.start('test:/host/master.m3u8', playerInterface)
.then(function(actual) {
let video = actual.periods[0].variants[0].video;
let audio = actual.periods[0].variants[0].audio;
let videoPosition = video.findSegmentPosition(0);
let audioPosition = audio.findSegmentPosition(0);
goog.asserts.assert(videoPosition != null,
'Cannot find first video segment');
goog.asserts.assert(audioPosition != null,
'Cannot find first audio segment');
let videoReference = video.getSegmentReference(videoPosition);
let audioReference = audio.getSegmentReference(audioPosition);
expect(videoReference).not.toBe(null);
expect(audioReference).not.toBe(null);
if (videoReference) {
expect(videoReference.getUris()[0])
.toEqual('test:/host/video/segment.mp4');
}
if (audioReference) {
expect(audioReference.getUris()[0])
.toEqual('test:/host/audio/segment.mp4');
}
}).catch(fail).then(done);
});
it('allows streams with no init segment', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,VIDEO="vid"\n',
'audio\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",URI="video"',
].join('');
const media = [
'#EXTM3U\n',
'#EXT-X-TARGETDURATION:6\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'selfInit.mp4',
].join('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.addPartialStream(ContentType.VIDEO)
.nullInitSegment()
.mime('video/mp4', 'avc1')
.addPartialStream(ContentType.AUDIO)
.nullInitSegment()
.mime('audio/mp4', 'mp4a')
.build();
await testHlsParser(master, media, manifest);
});
it('drops variants encrypted with AES-128', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
'video\n',
'#EXT-X-STREAM-INF:BANDWIDTH=300,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=120,AUDIO="aud2"\n',
'video2\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'URI="audio"\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud2",LANGUAGE="fr",',
'URI="audio2"\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 mediaWithAesEncryption = [
'#EXTM3U\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-KEY:METHOD=AES-128,',
'URI="800k.key\n',
'#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
'#EXTINF:5,\n',
'#EXT-X-BYTERANGE:121090@616\n',
'main.mp4',
].join('');
let manifest = new shaka.test.ManifestGenerator()
.anyTimeline()
.addPeriod(0)
.addPartialVariant()
.bandwidth(200)
.addPartialStream(ContentType.VIDEO)
.size(960, 540)
.addPartialStream(ContentType.AUDIO)
.language('en')
.build();
fakeNetEngine
.setRe