diff --git a/index.js b/index.js index 43f0a54..af18f13 100644 --- a/index.js +++ b/index.js @@ -82,6 +82,7 @@ class HLSVod { this.subtitleSequencesCount = 0; this.mediaStartExcessTime = 0; this.audioCodecsMap = {}; + this.audioGenerationInfo = {} } toJSON() { @@ -247,9 +248,10 @@ class HLSVod { let addedAudioLanguage = {}; let backupSegments = {}; - const numberOfCodecs = Object.keys(this.audioCodecsMap) + const numberOfCodecs = Object.keys(this.audioCodecsMap).length let codecsMatchingInVod = 0; const firstVod = Object.keys(this.audioSegments).length === 0; + this.audioGenerationInfo.firstVod = firstVod; if (Object.keys(this.audioSegments).length === 0) { for (let i = 0; i < m3u.items.StreamItem.length; i++) { @@ -259,33 +261,36 @@ class HLSVod { } } } + let checkedCodecs = {} for (let i = 0; i < m3u.items.StreamItem.length; i++) { const streamItem = m3u.items.StreamItem[i]; - let generateSegments = true; + let generateBackupAudioSegments = true; if (streamItem.get("audio")) { let audioGroupId = streamItem.get("audio"); const audioCodecs = streamItem.get("codecs").split(",").find(c => { return c.match(/^mp4a/) || c.match(/^ac-3/) || c.match(/^ec-3/); }); + let handledLang = {}; + if (Object.keys(this.audioCodecsMap).length !== 0) { if (!this.audioCodecsMap[audioCodecs] && !firstVod) { continue; - } else if (!firstVod && this.audioCodecsMap[audioCodecs]) { + } else if (!firstVod && this.audioCodecsMap[audioCodecs] && !checkedCodecs[audioCodecs]) { codecsMatchingInVod++; + checkedCodecs[audioCodecs] = true; } } let audioGroupIdToUse = audioGroupId; if (!this.audioSegments[audioGroupId]) { audioGroupIdToUse = Object.keys(this.audioSegments)[0]; } - if (backupSegments.length === 0) { - backupSegments[audioGroupId] = {}; + if (!backupSegments[audioGroupIdToUse]) { + backupSegments[audioGroupIdToUse] = {}; } debug(`Lookup media item for '${audioGroupId}'`); - let audioGroupItems = m3u.items.MediaItem.filter((item) => { return item.get("type") === "AUDIO" && item.get("group-id") === audioGroupId; }); @@ -300,45 +305,60 @@ class HLSVod { } else { itemLang = item.get("language"); } - // Initialize lang. in new group. + + + if (firstVod) { - if (!this.audioSegments[audioGroupId][itemLang]) { - this.audioSegments[audioGroupId][itemLang] = []; - } if (!this.audioCodecsMap[audioCodecs]) { this.audioCodecsMap[audioCodecs] = {}; } - } else { - if (!this.audioSegments[audioGroupIdToUse][itemLang] && Object.keys(backupSegments[audioGroupIdToUse]).length !== 0) { - return; - } else if (Object.keys(backupSegments[audioGroupIdToUse]).length === 0) { - backupSegments[audioGroupIdToUse][itemLang] = []; - + } + + // Initialize lang. in new group. + if (!this.allowedAudioLanguages) { + if (firstVod) { + generateBackupAudioSegments = false; + if (!this.audioSegments[audioGroupId][itemLang]) { + this.audioSegments[audioGroupId][itemLang] = []; + } } else { - generateSegments = false; - addedAudioLanguage[itemLang] = true; + if (!this.audioCodecsMap[audioCodecs]) { + skipGroupId = true; + return; + } + + if (!this.audioSegments[audioGroupIdToUse][itemLang]) { + handledLang[itemLang] = true; + return; + } else if (!handledLang[itemLang]) { + generateBackupAudioSegments = false; + addedAudioLanguage[itemLang] = true; + handledLang[itemLang] = true; + } } - if (!this.audioCodecsMap[audioCodecs]) { - skipGroupId = true; - return; + if (Object.keys(this.audioSegments).length > 1) { + const groupIds = Object.keys(this.audioSegments) + for (let index = 0; index < groupIds.length; index++) { + const groupId = groupIds[index]; + if (this.audioSegments[groupId][itemLang]) { + audioGroupIdToUse = groupId; + return itemLang; + } + } } } const itemChannels = item.get("channels") ? item.get("channels") : "2"; this.audioCodecsMap[audioCodecs][itemChannels] = audioGroupId; - if (Object.keys(this.audioSegments).length > 1) { - const groupIds = Object.keys(this.audioSegments) - for (let index = 0; index < groupIds.length; index++) { - const groupId = groupIds[index]; - if (this.audioSegments[groupId][itemLang]) { - audioGroupIdToUse = groupId; - return itemLang; + if (this.allowedAudioLanguages) { + for (let i = 0; i < this.allowedAudioLanguages.length; i++) { + const lang = this.allowedAudioLanguages[i]; + if (!this.audioSegments[audioGroupIdToUse][lang.language]) { + this.audioSegments[audioGroupIdToUse][lang.language] = []; } - } - } - if (this.allowedAudioLanguages) { + } for (let index = 0; index < this.allowedAudioLanguages.length; index++) { const element = this.allowedAudioLanguages[index]; if (element.language.toLowerCase() === itemLang.toLowerCase() || element.name.toLowerCase() === itemLang.toLowerCase()) { @@ -356,6 +376,7 @@ class HLSVod { } if (audioLanguages.length < 1) { + generateBackupAudioSegments = true; const item = audioGroupItems[0]; let itemLang; if (!item.get("language")) { @@ -363,32 +384,52 @@ class HLSVod { } else { itemLang = item.get("language"); } - audioLanguages.push(itemLang) + backupSegments[audioGroupIdToUse][itemLang] = []; } + this.audioGenerationInfo.addedLanguages = addedAudioLanguage; + this.audioGenerationInfo.backupInfo = backupSegments; + // # For each lang, find the lang playlist uri and do _loadAudioManifest() on it. - for (let j = 0; j < audioLanguages.length; j++) { - let audioLang = Object.keys(backupSegments[audioGroupIdToUse])[0]; - let audioUri = audioGroupItems[0].get("uri"); - if (!audioUri) { - //# if mediaItems dont have uris - let audioVariant = m3u.items.StreamItem.find((item) => { - return !item.get("resolution") && item.get("audio") === audioGroupId; - }); - if (audioVariant) { - audioUri = audioVariant.get("uri"); + if (!generateBackupAudioSegments) { + for (let j = 0; j < audioLanguages.length; j++) { + let audioLang = audioLanguages[j]; + let audioUri = audioGroupItems[j].get("uri"); + if (!audioUri) { + //# if mediaItems dont have uris + let audioVariant = m3u.items.StreamItem.find((item) => { + return !item.get("resolution") && item.get("audio") === audioGroupId; + }); + if (audioVariant) { + audioUri = audioVariant.get("uri"); + } + } + if (audioUri) { + if (!audioGroups[audioGroupIdToUse]) { + audioGroups[audioGroupIdToUse] = {}; + } + // # Prevents 'loading' an audio track with same GroupID and LANG. + // # otherwise it just would've loaded OVER the latest occurrent of the LANG in GroupID. + if (!audioGroups[audioGroupIdToUse][audioLang]) { + audioGroups[audioGroupIdToUse][audioLang] = true; + let audioManifestUrl = urlResolve(baseUrl, audioUri); + audioManifestPromises.push(this._loadAudioManifest(audioManifestUrl, audioGroupIdToUse, audioLang, _injectAudioManifest)); + } else { + debug(`Audio manifest for language "${audioLang}" from '${audioGroupIdToUse}' in already loaded, skipping`); + } + } else { + debug(`No media item for '${audioGroupIdToUse}' in "${audioLang}" was found, skipping`); } } - if (audioUri) { - let audioManifestUrl = urlResolve(baseUrl, audioUri); - audioManifestPromises.push(this._loadAudioManifest(audioManifestUrl, audioGroupIdToUse, audioLang, _injectAudioManifest)); - } else { - debug(`No media item for '${audioGroupId}' in "${audioLang}" was found, skipping`); + } else if (generateBackupAudioSegments) { + let audioLang = Object.keys(backupSegments[audioGroupIdToUse])[0]; + if (!this.audioSegments[audioGroupIdToUse]) { + this.audioSegments[audioGroupIdToUse] = {}; } - } - if (generateSegments) { - let audioLang = []; - let audioUri = audioGroupItems[j].get("uri"); + if (!this.audioSegments[audioGroupIdToUse][audioLang]) { + this.audioSegments[audioGroupIdToUse][audioLang] = []; + } + let audioUri = audioGroupItems[0].get("uri"); if (!audioUri) { //# if mediaItems dont have uris let audioVariant = m3u.items.StreamItem.find((item) => { @@ -524,55 +565,13 @@ class HLSVod { if (numberOfCodecs > 0 && numberOfCodecs != codecsMatchingInVod) { reject(new Error("Loaded VOD is missing audio codec(s)")); } - let allAudioLanguagesCount = 0; - const groupIds = Object.keys(this.audioSegments) - for(let i = 0; i < groupIds.length;i++) { - allAudioLanguagesCount += Object.keys(this.audioSegments[groupIds[i]]).length - } - - if(addedAudioLanguage.length === 0) { - const backupGroupId = Object.keys(backupSegments)[0]; - const backupLang = Object.keys(backupSegments[backupGroupId])[0]; - for(let i = 0; i < groupIds.length;i++) { - const langs = Object.keys(this.audioSegments[groupIds[i]]) - for (let j = 0; j < langs.length; j++) { - this.audioSegments[groupIds[i]][langs[j]].concat(backupSegments[backupGroupId][backupLang]) - } - } - } - - if (addedAudioLanguage.length < allAudioLanguagesCount && addedAudioLanguage.length !== 0) { - let isAdded = []; - for(let i = 0; i < groupIds.length;i++) { - const langs = Object.keys(this.audioSegments[groupIds[i]]) - for (let j = 0; j < langs.length; j++) { - const lang = langs[j]; - if (isAdded.length > 0) { - continue; - } - if(addedAudioLanguage[lang] && isAdded.length === 0) { - isAdded.push(groupIds[i]); - isAdded.push(lang); - continue; - } - } - } - for(let i = 0; i < groupIds.length;i++) { - const langs = Object.keys(this.audioSegments[groupIds[i]]) - for (let j = 0; j < langs.length; j++) { - const lang = langs[j]; - if (!addedAudioLanguage[lang]) { - this.audioSegments[groupIds][lang] = this.audioSegments[isAdded[0]][isAdded[1]] - } - } - } - } debug("Codec to Audio Group Id mapping"); debug(this.audioCodecsMap); return Promise.all(audioManifestPromises.concat(subtitleManifestPromises)) - }).then(this._cleanupUnused.bind(this)) + }).then(this._fillMissingAudioSegments.bind(this)) + .then(this._cleanupUnused.bind(this)) .then(this._createMediaSequences.bind(this)) .then(resolve) .catch((err) => { @@ -982,6 +981,10 @@ class HLSVod { return [audioCodecs, channels]; } + getAudioCodecs() { + return Object.keys(this.audioCodecsMap); + } + getSubtitleGroups(all = false) { return Object.keys(this.subtitleSegments).filter(groupId => groupId !== this.DUMMY_DEFAULT_SUBTITLE_GROUP_ID || all); } @@ -2061,6 +2064,7 @@ class HLSVod { const previousVodSeqCount = this.previousVod.getLiveMediaSequencesCount("audio"); const audioGroups = this.previousVod.getAudioGroups(); this.audioCodecsMap = this.previousVod.audioCodecsMap; + if (audioGroups.length > 0) { for (let i = 0; i < audioGroups.length; i++) { const audioGroupId = audioGroups[i]; @@ -2149,6 +2153,68 @@ class HLSVod { } } + _fillMissingAudioSegments() { + return new Promise((resolve, reject) => { + if (Object.keys(this.audioGenerationInfo.addedLanguages).length === 0) { + const backupGroupId = Object.keys(this.audioGenerationInfo.backupInfo)[0]; + const backupLang = Object.keys(this.audioGenerationInfo.backupInfo[backupGroupId])[0]; + const groupIds = Object.keys(this.audioSegments) + for (let i = 0; i < groupIds.length; i++) { + const langs = Object.keys(this.audioSegments[groupIds[i]]) + for (let j = 0; j < langs.length; j++) { + this.audioSegments[groupIds[i]][langs[j]] = this.audioSegments[groupIds[i]][langs[j]].concat(this.audioSegments[backupGroupId][backupLang]) + } + } + delete this.audioSegments[backupGroupId][backupLang] + } + if (!this.audioGenerationInfo.firstVod || this.allowedAudioLanguages) { + let allAudioLanguagesCount = 0; + const groupIds = Object.keys(this.audioSegments) + for (let i = 0; i < groupIds.length; i++) { + allAudioLanguagesCount += Object.keys(this.audioSegments[groupIds[i]]).length + } + + if (Object.keys(this.audioGenerationInfo.addedLanguages).length < allAudioLanguagesCount && Object.keys(this.audioGenerationInfo.addedLanguages).length > 0) { + let isAdded = []; + for (let i = 0; i < groupIds.length; i++) { + const langs = Object.keys(this.audioSegments[groupIds[i]]) + for (let j = 0; j < langs.length; j++) { + const lang = langs[j]; + if (isAdded.length > 0) { + continue; + } + if (this.audioGenerationInfo.addedLanguages[lang] && isAdded.length === 0) { + isAdded.push(groupIds[i]); + isAdded.push(lang); + continue; + } + } + } + let vodIndex = 0; + for (let i = 0; i < this.audioSegments[isAdded[0]][isAdded[1]].length; i++) { + if (this.audioSegments[isAdded[0]][isAdded[1]][i].vodTransition) { + vodIndex = i; + continue; + } + } + for (let i = 0; i < groupIds.length; i++) { + const langs = Object.keys(this.audioSegments[groupIds[i]]) + for (let j = 0; j < langs.length; j++) { + const lang = langs[j]; + if (!this.audioGenerationInfo.addedLanguages[lang]) { + for (let keep = vodIndex; keep < this.audioSegments[isAdded[0]][isAdded[1]].length; keep++) { + const segment = this.audioSegments[isAdded[0]][isAdded[1]][keep]; + this.audioSegments[groupIds[i]][lang].push(segment); + } + } + } + } + } + } + resolve(); + }); + } + _cleanupUnused() { return new Promise((resolve, reject) => { // Remove all bandwidths that are remaining from previous VOD and has not been initiated @@ -2265,7 +2331,7 @@ class HLSVod { video_sequence_list = this.generateSequencesTypeAVideo(bw); if (firstAudioLanguage) { - audio_sequence_list = this.generateSequencesTypeAExtraMedia(this.audioSegments, audioGroupId, firstAudioLanguage, "audio");// segments,firstGroupId, firstLanguage, type + audio_sequence_list = this.generateSequencesTypeAExtraMedia(this.audioSegments, audioGroupId, firstAudioLanguage, "audio"); } if (this.shouldContainSubtitles) { subtitle_sequence_list = this.generateSequencesTypeAExtraMedia(this.subtitleSegments, subtitleGroupId, firstSubtitleLanguage, "subtitle"); @@ -2299,6 +2365,7 @@ class HLSVod { }); } } + // Set Sequences Counts this.videoSequencesCount = video_sequence_list.length; this.audioSequencesCount = audio_sequence_list.length; diff --git a/spec/hlsvod_audio_spec.js b/spec/hlsvod_audio_spec.js index a9f074f..0a9378b 100644 --- a/spec/hlsvod_audio_spec.js +++ b/spec/hlsvod_audio_spec.js @@ -408,26 +408,10 @@ describe("HLSVod with demuxed audio", () => { }); }); - it("load with one of the two languages specified", (done) => { + it("load with all the language specified but vod only has one", (done) => { const audioTracks = [ { language: "en", name: "English" }, - ] - hlsOpts.allowedAudioLanguages = audioTracks; - mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); - mockVod2 = new HLSVod("http://mock.com/mock2.m3u8", null, 0, 0, null, hlsOpts); - - mockVod - .load(mockMasterManifestMultipleLangs, mockMediaManifestMultipleLangs, mockAudioManifestMultipleLangs) - .then(() => { - const audioLanguages = mockVod.getAudioLangsForAudioGroup("aac"); - expect(audioLanguages).toEqual(["en"]); - done(); - }); - }); - - it("load without the language specified", (done) => { - const audioTracks = [ - { language: "es", name: "Espanol" }, + { language: "sv", name: "Svenska" }, ] hlsOpts.allowedAudioLanguages = audioTracks; mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); @@ -437,41 +421,38 @@ describe("HLSVod with demuxed audio", () => { .load(mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) .then(() => { const audioLanguages = mockVod.getAudioLangsForAudioGroup("aac"); - expect(audioLanguages).toEqual(["en"]); + expect(audioLanguages).toEqual(["en", "sv"]); + const m3u8 = mockVod.getLiveMediaAudioSequences(0, "acc", "sv", 0); + const m3u82 = mockVod.getLiveMediaAudioSequences(0, "acc", "en", 0); + const subStrings = m3u8.split("\n"); + const subStrings2 = m3u82.split("\n"); + expect(subStrings[7]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-1.ts"); + expect(subStrings2[7]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-1.ts"); done(); }); }); - it("load with the language specified", (done) => { + it("load with only the language specified", (done) => { const audioTracks = [ { language: "en", name: "English" }, ]; hlsOpts.allowedAudioLanguages = audioTracks; mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); - mockVod2 = new HLSVod("http://mock.com/mock2.m3u8", null, 0, 0, null, hlsOpts); - mockVod - .load(mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) + .load(mockMasterManifestMultipleLangs, mockMediaManifestMultipleLangs, mockAudioManifestMultipleLangs) .then(() => { - mockVod2.loadAfter(mockVod, mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) - .then(() => { - const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "acc", "en", 0); - const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "aac", "en", 1); - const subStrings = m3u8.split("\n") - const subStrings2 = m3u8_2.split("\n") - expect(subStrings[33]).toEqual("https://vod.streaming.a2d.tv/3e542405-583b-4edc-93ab-eca86427d148/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209.ism/hls/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209-textstream_swe=1000-693.webvtt"); - expect(subStrings2[32]).toEqual("#EXT-X-DISCONTINUITY") - expect(subStrings2[33]).toEqual("#EXTINF:3.000,"); - expect(subStrings2[34]).toEqual("https://d3t8zrj2x5ol3r.cloudfront.net/u/file~text_vtt~dummy.vtt/1/s/webvtt.vtt"); - done(); - }); + const m3u8 = mockVod.getLiveMediaAudioSequences(0, "acc", "en", 0); + const subStrings = m3u8.split("\n"); + expect(subStrings[8]).toEqual("https://maitv-vod.lab.eyevinn.technology/ads/apotea-15s.mp4/audio/en-00000.ts"); + const audioLanguages = mockVod.getAudioLangsForAudioGroup("aac"); + expect(audioLanguages).toEqual(["en"]); + done(); }); }); - it("load with all the language specified", (done) => { + it("load without the language specified", (done) => { const audioTracks = [ - { language: "en", name: "English" }, - { language: "sv", name: "Svenska" }, + { language: "es", name: "Espanol" }, ] hlsOpts.allowedAudioLanguages = audioTracks; mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); @@ -480,24 +461,19 @@ describe("HLSVod with demuxed audio", () => { mockVod .load(mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) .then(() => { - mockVod2.loadAfter(mockVod, mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) - .then(() => { - const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "acc", "en", 0); - const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "aac", "en", 1); - const subStrings = m3u8.split("\n") - const subStrings2 = m3u8_2.split("\n") - expect(subStrings[33]).toEqual("https://vod.streaming.a2d.tv/3e542405-583b-4edc-93ab-eca86427d148/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209.ism/hls/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209-textstream_swe=1000-693.webvtt"); - expect(subStrings2[32]).toEqual("#EXT-X-DISCONTINUITY") - expect(subStrings2[33]).toEqual("#EXTINF:3.000,"); - expect(subStrings2[34]).toEqual("https://d3t8zrj2x5ol3r.cloudfront.net/u/file~text_vtt~dummy.vtt/1/s/webvtt.vtt"); - done(); - }); + const audioLanguages = mockVod.getAudioLangsForAudioGroup("aac"); + const m3u8 = mockVod.getLiveMediaAudioSequences(0, "acc", "es", 0); + const subStrings = m3u8.split("\n") + expect(subStrings[7]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-1.ts"); + expect(audioLanguages).toEqual(["es"]); + done(); }); }); - it("load with one of the two languages specified", (done) => { + it("load vod after a vod with specified langs", (done) => { const audioTracks = [ { language: "en", name: "English" }, + { language: "sv", name: "Svenska" }, ] hlsOpts.allowedAudioLanguages = audioTracks; mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); @@ -508,51 +484,26 @@ describe("HLSVod with demuxed audio", () => { .then(() => { mockVod2.loadAfter(mockVod, mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) .then(() => { - const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "acc", "en", 0); - const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "aac", "en", 1); - const subStrings = m3u8.split("\n") - const subStrings2 = m3u8_2.split("\n") - expect(subStrings[33]).toEqual("https://vod.streaming.a2d.tv/3e542405-583b-4edc-93ab-eca86427d148/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209.ism/hls/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209-textstream_swe=1000-693.webvtt"); - expect(subStrings2[32]).toEqual("#EXT-X-DISCONTINUITY") - expect(subStrings2[33]).toEqual("#EXTINF:3.000,"); - expect(subStrings2[34]).toEqual("https://d3t8zrj2x5ol3r.cloudfront.net/u/file~text_vtt~dummy.vtt/1/s/webvtt.vtt"); - done(); - }); - }); - }); - - it("load without the language specified", (done) => { - const audioTracks = [ - { language: "es", name: "Espanol" }, - ] - hlsOpts.allowedAudioLanguages = audioTracks; - mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); - mockVod2 = new HLSVod("http://mock.com/mock2.m3u8", null, 0, 0, null, hlsOpts); - - mockVod - .load(mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) - .then(() => { - mockVod2.loadAfter(mockVod, mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) - .then(() => { - const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "acc", "en", 0); - const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "aac", "en", 1); - const subStrings = m3u8.split("\n") - const subStrings2 = m3u8_2.split("\n") - expect(subStrings[33]).toEqual("https://vod.streaming.a2d.tv/3e542405-583b-4edc-93ab-eca86427d148/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209.ism/hls/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209-textstream_swe=1000-693.webvtt"); - expect(subStrings2[32]).toEqual("#EXT-X-DISCONTINUITY") - expect(subStrings2[33]).toEqual("#EXTINF:3.000,"); - expect(subStrings2[34]).toEqual("https://d3t8zrj2x5ol3r.cloudfront.net/u/file~text_vtt~dummy.vtt/1/s/webvtt.vtt"); + const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "acc", "sv", 1); + const m3u82 = mockVod2.getLiveMediaAudioSequences(0, "acc", "en", 1); + const subStrings = m3u8.split("\n"); + const subStrings2 = m3u82.split("\n"); + expect(subStrings[19]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-19.ts"); + expect(subStrings[20]).toEqual("#EXT-X-DISCONTINUITY"); + expect(subStrings[21]).toEqual("#EXTINF:6.000,"); + expect(subStrings[22]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-1.ts"); + + expect(subStrings2[19]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-19.ts"); + expect(subStrings2[20]).toEqual("#EXT-X-DISCONTINUITY"); + expect(subStrings2[21]).toEqual("#EXTINF:6.000,"); + expect(subStrings2[22]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-1.ts"); done(); }); }); }); }); - describe("correct language functionality without allowedAudioLanguages list", () => { - - const hlsOpts = { - forcedDemuxMode: true, - } + describe("correct language functionality without allowedAudioLanguages list", () => { let mockMasterManifestOneLang; let mockMediaManifestOneLang; let mockAudioManifestOneLang; @@ -595,7 +546,7 @@ describe("HLSVod with demuxed audio", () => { if (lang) { return fs.createReadStream(`testvectors/hls_multiaudiotracks/audio-96000-${lang}.m3u8`); } else { - return fs.createReadStream(`testvectors/hls_multiaudiotracks/aac-en.m3u8`); + return fs.createReadStream(`testvectors/hls_multiaudiotracks/audio-96000-en.m3u8`); } }; @@ -623,7 +574,7 @@ describe("HLSVod with demuxed audio", () => { }; }); - it("load vod after vod with one matching languages", (done) => { + fit("load vod with multiple languages after vod with one matching languages", (done) => { mockVod = new HLSVod("http://mock.com/mock.m3u8"); mockVod2 = new HLSVod("http://mock.com/mock2.m3u8"); @@ -636,49 +587,103 @@ describe("HLSVod with demuxed audio", () => { done(); }); }); + }); + it("loading vod with one matching languages after vod with multiple languages", (done) => { + mockVod = new HLSVod("http://mock.com/mock.m3u8"); + mockVod2 = new HLSVod("http://mock.com/mock2.m3u8"); + + mockVod + .load(mockMasterManifestMultipleLangs, mockMediaManifestMultipleLangs, mockAudioManifestMultipleLangs) + .then(() => { + mockVod2.loadAfter(mockVod, mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) + .then(() => { + const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "audio-aacl-96", "pl", 1); + const subStrings2 = m3u8_2.split("\n"); + expect(subStrings2[41]).toEqual("http://mock.com/1woxvooiidb(11186147_ISMUSP)-audio=96000_pl-26.aac"); + expect(subStrings2[42]).toEqual("#EXT-X-DISCONTINUITY") + expect(subStrings2[43]).toEqual("#EXTINF:6.000,"); + expect(subStrings2[44]).toEqual("http://ys-intermediate.tv4play.se/prod/6/4/6/5/30751/u-6400-b-640x360-704-1-1.ts"); + done(); + }); + }); }); it("load vod after vod with no matching languages", (done) => { - mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); - mockVod2 = new HLSVod("http://mock.com/mock2.m3u8", null, 0, 0, null, hlsOpts); + mockVod = new HLSVod("http://mock.com/mock.m3u8"); + mockVod2 = new HLSVod("http://mock.com/mock2.m3u8"); mockVod .load(mockMasterManifestMultipleLangs, mockMediaManifestMultipleLangs, mockAudioManifestMultipleLangs) .then(() => { mockVod2.loadAfter(mockVod, mockMasterManifestMultipleLangs2, mockMediaManifestMultipleLangs2, mockAudioManifestMultipleLangs2) .then(() => { - const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "audio-aacl-96", "en", 0); const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "audio-aacl-96", "en", 1); - const subStrings = m3u8.split("\n") - const subStrings2 = m3u8_2.split("\n") - expect(subStrings[33]).toEqual("https://vod.streaming.a2d.tv/3e542405-583b-4edc-93ab-eca86427d148/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209.ism/hls/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209-textstream_swe=1000-693.webvtt"); - expect(subStrings2[32]).toEqual("#EXT-X-DISCONTINUITY") - expect(subStrings2[33]).toEqual("#EXTINF:3.000,"); - expect(subStrings2[34]).toEqual("https://d3t8zrj2x5ol3r.cloudfront.net/u/file~text_vtt~dummy.vtt/1/s/webvtt.vtt"); + const subStrings2 = m3u8_2.split("\n"); + expect(subStrings2[41]).toEqual("http://mock.com/1woxvooiidb(11186147_ISMUSP)-audio=96000_en-26.aac"); + expect(subStrings2[42]).toEqual("#EXT-X-DISCONTINUITY") + expect(subStrings2[43]).toEqual("#EXTINF:3.000,"); + expect(subStrings2[44]).toEqual("http://mock.com/1woxvooiidb(11186147_ISMUSP)-audio=96000_sv-1.aac"); done(); }); }); }); + }); + describe("correct language functionality without allowedAudioLanguages list", () => { + let mockMasterManifestOneCodec; + let mockMediaManifestOneCodec; + let mockAudioManifestOneCodec; + let mockMasterManifestMultipleCodec; + let mockMediaManifestMultipleCodec; + let mockAudioManifestMultipleCodec; + + beforeEach(() => { + mockMasterManifestOneCodec = function () { + return fs.createReadStream("testvectors/hls_reload1_audio/master.m3u8"); + }; + + mockMediaManifestOneCodec = function () { + return fs.createReadStream("testvectors/hls_reload1_audio/1.m3u8"); + }; + + mockAudioManifestOneCodec = function () { + return fs.createReadStream("testvectors/hls_reload1_audio/aac-en.m3u8"); + }; + + mockMasterManifestMultipleCodec = function () { + return fs.createReadStream("testvectors/hls_multicodec/master.m3u8"); + }; + + mockMediaManifestMultipleCodec = function () { + return fs.createReadStream(`testvectors/hls_multicodec/video.m3u8`); + }; + + mockAudioManifestMultipleCodec = function (groupId) { + if (groupId === "audio6_t0") { + return fs.createReadStream(`testvectors/hls_multicodec/audio_hd.m3u8`); + } else { + return fs.createReadStream(`testvectors/hls_multicodec/audio_stereo.m3u8`); + } + }; + }); + + it("load vod after vod with the same codecs", (done) => { - mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); - mockVod2 = new HLSVod("http://mock.com/mock2.m3u8", null, 0, 0, null, hlsOpts); + mockVod = new HLSVod("http://mock.com/mock.m3u8"); + mockVod2 = new HLSVod("http://mock.com/mock2.m3u8"); mockVod - .load(mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) + .load(mockMasterManifestOneCodec, mockMediaManifestOneCodec, mockAudioManifestOneCodec) .then(() => { - mockVod2.loadAfter(mockVod, mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) + mockVod2.loadAfter(mockVod, mockMasterManifestOneCodec, mockMediaManifestOneCodec, mockAudioManifestOneCodec) .then(() => { - const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "acc", "en", 0); - const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "aac", "en", 1); - const subStrings = m3u8.split("\n") - const subStrings2 = m3u8_2.split("\n") - expect(subStrings[33]).toEqual("https://vod.streaming.a2d.tv/3e542405-583b-4edc-93ab-eca86427d148/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209.ism/hls/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209-textstream_swe=1000-693.webvtt"); - expect(subStrings2[32]).toEqual("#EXT-X-DISCONTINUITY") - expect(subStrings2[33]).toEqual("#EXTINF:3.000,"); - expect(subStrings2[34]).toEqual("https://d3t8zrj2x5ol3r.cloudfront.net/u/file~text_vtt~dummy.vtt/1/s/webvtt.vtt"); + const audioGroupIds = mockVod.getAudioGroups(); + const audioGroupIds2 = mockVod2.getAudioGroups(); + const codecs = mockVod.getAudioCodecsAndChannelsForGroupId(audioGroupIds[0]); + const codecs2 = mockVod2.getAudioCodecsAndChannelsForGroupId(audioGroupIds2[0]); + expect(codecs).toEqual(codecs2); done(); }); }); @@ -686,25 +691,37 @@ describe("HLSVod with demuxed audio", () => { }); it("load vod after vod with the different codecs", (done) => { - mockVod = new HLSVod("http://mock.com/mock.m3u8", null, 0, 0, null, hlsOpts); - mockVod2 = new HLSVod("http://mock.com/mock2.m3u8", null, 0, 0, null, hlsOpts); + mockVod = new HLSVod("http://mock.com/mock.m3u8"); + mockVod2 = new HLSVod("http://mock.com/mock2.m3u8"); mockVod - .load(mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) + .load(mockMasterManifestMultipleCodec, mockMediaManifestMultipleCodec, mockAudioManifestMultipleCodec) .then(() => { - mockVod2.loadAfter(mockVod, mockMasterManifestOneLang, mockMediaManifestOneLang, mockAudioManifestOneLang) + const codecs2 = mockVod.getAudioCodecs(); + mockVod2.loadAfter(mockVod, mockMasterManifestOneCodec, mockMediaManifestOneCodec, mockAudioManifestOneCodec) + .catch((e) => { + expect(e.message).toEqual("Loaded VOD is missing audio codec(s)"); + done(); + }); + }); + }); + + it("load vod after vod where the second one has more codecs", (done) => { + mockVod = new HLSVod("http://mock.com/mock.m3u8"); + mockVod2 = new HLSVod("http://mock.com/mock2.m3u8"); + + mockVod + .load(mockMasterManifestOneCodec, mockMediaManifestOneCodec, mockAudioManifestOneCodec) + .then(() => { + mockVod2.loadAfter(mockVod, mockMasterManifestMultipleCodec, mockMediaManifestMultipleCodec, mockAudioManifestMultipleCodec) .then(() => { - const m3u8 = mockVod2.getLiveMediaAudioSequences(0, "acc", "en", 0); - const m3u8_2 = mockVod2.getLiveMediaAudioSequences(0, "aac", "en", 1); - const subStrings = m3u8.split("\n") - const subStrings2 = m3u8_2.split("\n") - expect(subStrings[33]).toEqual("https://vod.streaming.a2d.tv/3e542405-583b-4edc-93ab-eca86427d148/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209.ism/hls/ab92a690-62de-11ed-aa51-c96fb4f9434f_20337209-textstream_swe=1000-693.webvtt"); - expect(subStrings2[32]).toEqual("#EXT-X-DISCONTINUITY") - expect(subStrings2[33]).toEqual("#EXTINF:3.000,"); - expect(subStrings2[34]).toEqual("https://d3t8zrj2x5ol3r.cloudfront.net/u/file~text_vtt~dummy.vtt/1/s/webvtt.vtt"); + const codecs = mockVod.getAudioCodecs(); + const codecs2 = mockVod2.getAudioCodecs(); + expect(codecs.length).toEqual(codecs2.length) done(); }); }); }); + }); }); \ No newline at end of file diff --git a/testvectors/hls_multicodec/audio_hd.m3u8 b/testvectors/hls_multicodec/audio_hd.m3u8 new file mode 100644 index 0000000..45f6222 --- /dev/null +++ b/testvectors/hls_multicodec/audio_hd.m3u8 @@ -0,0 +1,139 @@ +#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-MEDIA-SEQUENCE:0 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97142@1220 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97630@98362 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97472@195992 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97184@293464 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97137@390648 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97064@487785 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97605@584849 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97085@682454 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97076@779539 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97067@876615 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97484@973682 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97228@1071166 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97219@1168394 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97212@1265613 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97401@1362825 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97102@1460226 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97280@1557328 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96816@1654608 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97860@1751424 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96937@1849284 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97193@1946221 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96895@2043414 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97648@2140309 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97157@2237957 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97261@2335114 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97069@2432375 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97419@2529444 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97202@2626863 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97187@2724065 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97250@2821252 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97319@2918502 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97069@3015821 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97155@3112890 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97219@3210045 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97491@3307264 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97144@3404755 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97151@3501899 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97059@3599050 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97464@3696109 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97250@3793573 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97228@3890823 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96944@3988051 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97524@4084995 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXTINF:5.120667, +#EXT-X-BYTERANGE:84456@4182519 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-hd--mp4a.mp4 +#EXT-X-ENDLIST \ No newline at end of file diff --git a/testvectors/hls_multicodec/audio_stereo.m3u8 b/testvectors/hls_multicodec/audio_stereo.m3u8 new file mode 100644 index 0000000..ef34daa --- /dev/null +++ b/testvectors/hls_multicodec/audio_stereo.m3u8 @@ -0,0 +1,139 @@ +#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-MEDIA-SEQUENCE:0 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97142@1220 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97630@98362 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97472@195992 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97184@293464 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97137@390648 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97064@487785 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97605@584849 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97085@682454 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97076@779539 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97067@876615 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97484@973682 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97228@1071166 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97219@1168394 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97212@1265613 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97401@1362825 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97102@1460226 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97280@1557328 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96816@1654608 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97860@1751424 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96937@1849284 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97193@1946221 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96895@2043414 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97648@2140309 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97157@2237957 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97261@2335114 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97069@2432375 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97419@2529444 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97202@2626863 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97187@2724065 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97250@2821252 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97319@2918502 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97069@3015821 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97155@3112890 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97219@3210045 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97491@3307264 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97144@3404755 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97151@3501899 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97059@3599050 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97464@3696109 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97250@3793573 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97228@3890823 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96944@3988051 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97524@4084995 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXTINF:5.120667, +#EXT-X-BYTERANGE:84456@4182519 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-stereo-mp4a.mp4 +#EXT-X-ENDLIST \ No newline at end of file diff --git a/testvectors/hls_multicodec/master.m3u8 b/testvectors/hls_multicodec/master.m3u8 new file mode 100644 index 0000000..68f6621 --- /dev/null +++ b/testvectors/hls_multicodec/master.m3u8 @@ -0,0 +1,12 @@ +#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-INDEPENDENT-SEGMENTS + + +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio1_t0",NAME="English",LANGUAGE="en",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="2",URI="audio_stereo.m3u8" +#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="audio6_t0",NAME="Swedish",LANGUAGE="sv",AUTOSELECT=YES,DEFAULT=YES,CHANNELS="16/JOC",URI="audio_hd.m3u8" + +#EXT-X-STREAM-INF:BANDWIDTH=2982354,AVERAGE-BANDWIDTH=1993175,FRAME-RATE=24.000,CODECS="hvc1.2.4.L63.90,ec-3",RESOLUTION=640x360,VIDEO-RANGE="PQ",AUDIO="audio6_t0" +video.m3u8 +#EXT-X-STREAM-INF:BANDWIDTH=2344306,AVERAGE-BANDWIDTH=1353714,FRAME-RATE=24.000,CODECS="hvc1.2.4.L63.90,mp4a.40.2",RESOLUTION=640x360,VIDEO-RANGE="PQ",AUDIO="audio1_t0" +video.m3u8 \ No newline at end of file diff --git a/testvectors/hls_multicodec/video.m3u8 b/testvectors/hls_multicodec/video.m3u8 new file mode 100644 index 0000000..a805199 --- /dev/null +++ b/testvectors/hls_multicodec/video.m3u8 @@ -0,0 +1,139 @@ +#EXTM3U +#EXT-X-VERSION:6 +#EXT-X-PLAYLIST-TYPE:VOD +#EXT-X-INDEPENDENT-SEGMENTS +#EXT-X-TARGETDURATION:6 +#EXT-X-MEDIA-SEQUENCE:0 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97142@1220 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97630@98362 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97472@195992 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97184@293464 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97137@390648 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97064@487785 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97605@584849 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97085@682454 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97076@779539 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97067@876615 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97484@973682 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97228@1071166 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97219@1168394 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97212@1265613 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97401@1362825 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97102@1460226 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97280@1557328 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96816@1654608 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97860@1751424 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96937@1849284 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97193@1946221 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96895@2043414 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97648@2140309 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97157@2237957 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97261@2335114 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97069@2432375 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97419@2529444 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97202@2626863 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97187@2724065 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97250@2821252 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97319@2918502 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97069@3015821 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97155@3112890 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97219@3210045 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97491@3307264 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97144@3404755 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97151@3501899 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97059@3599050 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97464@3696109 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97250@3793573 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:97228@3890823 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.994667, +#EXT-X-BYTERANGE:96944@3988051 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:6.016000, +#EXT-X-BYTERANGE:97524@4084995 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXTINF:5.120667, +#EXT-X-BYTERANGE:84456@4182519 +media/VIDEO_e4da5fcd-5ffc-4713-bcdd-95ea579d790b_stereo_aac-audio-en-mp4a.mp4 +#EXT-X-ENDLIST \ No newline at end of file