diff --git a/src/inheritAttributes.js b/src/inheritAttributes.js
index 84ec1df..f6983a6 100644
--- a/src/inheritAttributes.js
+++ b/src/inheritAttributes.js
@@ -30,7 +30,21 @@ export const buildBaseUrls = (references, baseUrlElements) => {
return flatten(references.map(function(reference) {
return baseUrlElements.map(function(baseUrlElement) {
- return merge(parseAttributes(baseUrlElement), { baseUrl: resolveUrl(reference.baseUrl, getContent(baseUrlElement)) });
+ const initialBaseUrl = getContent(baseUrlElement);
+ const resolvedBaseUrl = resolveUrl(reference.baseUrl, initialBaseUrl);
+
+ const finalBaseUrl = merge(
+ parseAttributes(baseUrlElement),
+ { baseUrl: resolvedBaseUrl }
+ );
+
+ // If the URL is resolved, we want to get the serviceLocation from the reference
+ // assuming there is no serviceLocation on the initialBaseUrl
+ if (resolvedBaseUrl !== initialBaseUrl && !finalBaseUrl.serviceLocation && reference.serviceLocation) {
+ finalBaseUrl.serviceLocation = reference.serviceLocation;
+ }
+
+ return finalBaseUrl;
});
}));
};
diff --git a/src/toM3u8.js b/src/toM3u8.js
index 01ba625..287f7da 100644
--- a/src/toM3u8.js
+++ b/src/toM3u8.js
@@ -11,45 +11,62 @@ export const generateSidxKey = (sidx) => sidx &&
sidx.uri + '-' + byteRangeToString(sidx.byterange);
const mergeDiscontiguousPlaylists = playlists => {
- const mergedPlaylists = values(playlists.reduce((acc, playlist) => {
- // assuming playlist IDs are the same across periods
- // TODO: handle multiperiod where representation sets are not the same
- // across periods
- const name = playlist.attributes.id + (playlist.attributes.lang || '');
-
- if (!acc[name]) {
- // First Period
- acc[name] = playlist;
- acc[name].attributes.timelineStarts = [];
- } else {
- // Subsequent Periods
- if (playlist.segments) {
- // first segment of subsequent periods signal a discontinuity
- if (playlist.segments[0]) {
- playlist.segments[0].discontinuity = true;
+ // Break out playlists into groups based on their baseUrl
+ const playlistsByBaseUrl = playlists.reduce(function(acc, cur) {
+ if (!acc[cur.attributes.baseUrl]) {
+ acc[cur.attributes.baseUrl] = [];
+ }
+
+ acc[cur.attributes.baseUrl].push(cur);
+
+ return acc;
+ }, {});
+
+ let allPlaylists = [];
+
+ Object.values(playlistsByBaseUrl).forEach((playlistGroup) => {
+ const mergedPlaylists = values(playlistGroup.reduce((acc, playlist) => {
+ // assuming playlist IDs are the same across periods
+ // TODO: handle multiperiod where representation sets are not the same
+ // across periods
+ const name = playlist.attributes.id + (playlist.attributes.lang || '');
+
+ if (!acc[name]) {
+ // First Period
+ acc[name] = playlist;
+ acc[name].attributes.timelineStarts = [];
+ } else {
+ // Subsequent Periods
+ if (playlist.segments) {
+ // first segment of subsequent periods signal a discontinuity
+ if (playlist.segments[0]) {
+ playlist.segments[0].discontinuity = true;
+ }
+ acc[name].segments.push(...playlist.segments);
}
- acc[name].segments.push(...playlist.segments);
- }
- // bubble up contentProtection, this assumes all DRM content
- // has the same contentProtection
- if (playlist.attributes.contentProtection) {
- acc[name].attributes.contentProtection =
- playlist.attributes.contentProtection;
+ // bubble up contentProtection, this assumes all DRM content
+ // has the same contentProtection
+ if (playlist.attributes.contentProtection) {
+ acc[name].attributes.contentProtection =
+ playlist.attributes.contentProtection;
+ }
}
- }
- acc[name].attributes.timelineStarts.push({
- // Although they represent the same number, it's important to have both to make it
- // compatible with HLS potentially having a similar attribute.
- start: playlist.attributes.periodStart,
- timeline: playlist.attributes.periodStart
- });
+ acc[name].attributes.timelineStarts.push({
+ // Although they represent the same number, it's important to have both to make it
+ // compatible with HLS potentially having a similar attribute.
+ start: playlist.attributes.periodStart,
+ timeline: playlist.attributes.periodStart
+ });
- return acc;
- }, {}));
+ return acc;
+ }, {}));
- return mergedPlaylists.map(playlist => {
+ allPlaylists = allPlaylists.concat(mergedPlaylists);
+ });
+
+ return allPlaylists.map(playlist => {
playlist.discontinuityStarts =
findIndexes(playlist.segments || [], 'discontinuity');
@@ -98,7 +115,7 @@ export const formatAudioPlaylist = ({
uri: '',
endList: attributes.type === 'static',
timeline: attributes.periodStart,
- resolvedUri: '',
+ resolvedUri: attributes.baseUrl || '',
targetDuration: attributes.duration,
discontinuitySequence,
discontinuityStarts,
@@ -111,6 +128,10 @@ export const formatAudioPlaylist = ({
playlist.contentProtection = attributes.contentProtection;
}
+ if (attributes.serviceLocation) {
+ playlist.attributes.serviceLocation = attributes.serviceLocation;
+ }
+
if (sidx) {
playlist.sidx = sidx;
}
@@ -139,6 +160,7 @@ export const formatVttPlaylist = ({
duration: attributes.sourceDuration,
number: 0
}];
+
// targetDuration should be the same duration as the only segment
attributes.duration = attributes.sourceDuration;
}
@@ -152,7 +174,7 @@ export const formatVttPlaylist = ({
if (attributes.codecs) {
m3u8Attributes.CODECS = attributes.codecs;
}
- return {
+ const vttPlaylist = {
attributes: m3u8Attributes,
uri: '',
endList: attributes.type === 'static',
@@ -165,6 +187,12 @@ export const formatVttPlaylist = ({
mediaSequence,
segments
};
+
+ if (attributes.serviceLocation) {
+ vttPlaylist.attributes.serviceLocation = attributes.serviceLocation;
+ }
+
+ return vttPlaylist;
};
export const organizeAudioPlaylists = (playlists, sidxMapping = {}, isAudioOnly = false) => {
@@ -289,7 +317,7 @@ export const formatVideoPlaylist = ({
uri: '',
endList: attributes.type === 'static',
timeline: attributes.periodStart,
- resolvedUri: '',
+ resolvedUri: attributes.baseUrl || '',
targetDuration: attributes.duration,
discontinuityStarts,
timelineStarts: attributes.timelineStarts,
@@ -304,6 +332,10 @@ export const formatVideoPlaylist = ({
playlist.contentProtection = attributes.contentProtection;
}
+ if (attributes.serviceLocation) {
+ playlist.attributes.serviceLocation = attributes.serviceLocation;
+ }
+
if (sidx) {
playlist.sidx = sidx;
}
diff --git a/test/inheritAttributes.test.js b/test/inheritAttributes.test.js
index a489ef7..dae013f 100644
--- a/test/inheritAttributes.test.js
+++ b/test/inheritAttributes.test.js
@@ -927,10 +927,11 @@ QUnit.test('end to end - content steering - resolvable base URLs', function(asse
id="test"
width="720">
+ /video
- /video
+ /vtt
@@ -957,7 +958,7 @@ QUnit.test('end to end - content steering - resolvable base URLs', function(asse
attributes: {
NOW,
bandwidth: 5000000,
- baseUrl: 'https://cdn1.example.com/',
+ baseUrl: 'https://cdn1.example.com/video',
clientOffset: 0,
codecs: 'avc1.64001e',
height: 404,
@@ -980,7 +981,7 @@ QUnit.test('end to end - content steering - resolvable base URLs', function(asse
attributes: {
NOW,
bandwidth: 5000000,
- baseUrl: 'https://cdn2.example.com/',
+ baseUrl: 'https://cdn2.example.com/video',
clientOffset: 0,
codecs: 'avc1.64001e',
height: 404,
@@ -1003,13 +1004,14 @@ QUnit.test('end to end - content steering - resolvable base URLs', function(asse
attributes: {
NOW,
bandwidth: 256,
- baseUrl: 'https://cdn1.example.com/video',
+ baseUrl: 'https://cdn1.example.com/vtt',
clientOffset: 0,
id: 'en',
lang: 'en',
mimeType: 'text/vtt',
periodStart: 0,
role: {},
+ serviceLocation: 'alpha',
sourceDuration: 0,
type: 'dyanmic'
},
@@ -1019,13 +1021,14 @@ QUnit.test('end to end - content steering - resolvable base URLs', function(asse
attributes: {
NOW,
bandwidth: 256,
- baseUrl: 'https://cdn2.example.com/video',
+ baseUrl: 'https://cdn2.example.com/vtt',
clientOffset: 0,
id: 'en',
lang: 'en',
mimeType: 'text/vtt',
periodStart: 0,
role: {},
+ serviceLocation: 'beta',
sourceDuration: 0,
type: 'dyanmic'
},
diff --git a/test/manifests/608-captions.js b/test/manifests/608-captions.js
index ae12c99..cf50543 100644
--- a/test/manifests/608-captions.js
+++ b/test/manifests/608-captions.js
@@ -46,7 +46,7 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: true,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/1080p.ts',
targetDuration: 6,
mediaSequence: 0,
timelineStarts: [{ start: 0, timeline: 0 }],
diff --git a/test/manifests/708-captions.js b/test/manifests/708-captions.js
index 4b3e682..5df7a04 100644
--- a/test/manifests/708-captions.js
+++ b/test/manifests/708-captions.js
@@ -47,7 +47,7 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: true,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/1080p.ts',
targetDuration: 6,
mediaSequence: 0,
timelineStarts: [{ start: 0, timeline: 0 }],
diff --git a/test/manifests/audio-only.js b/test/manifests/audio-only.js
index be2d860..e93327e 100644
--- a/test/manifests/audio-only.js
+++ b/test/manifests/audio-only.js
@@ -27,7 +27,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'http://example.com/audio_en_2c_128k_aac.mp4',
targetDuration: 60,
segments: [],
mediaSequence: 0,
@@ -78,7 +78,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'http://example.com/audio_es_2c_128k_aac.mp4',
targetDuration: 60,
segments: [],
mediaSequence: 0,
diff --git a/test/manifests/location.js b/test/manifests/location.js
index c7fe246..6b9ae7b 100644
--- a/test/manifests/location.js
+++ b/test/manifests/location.js
@@ -32,7 +32,7 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: true,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/1080p.ts',
targetDuration: 6,
mediaSequence: 0,
discontinuitySequence: 0,
diff --git a/test/manifests/locations.js b/test/manifests/locations.js
index 6b2ac59..86911ed 100644
--- a/test/manifests/locations.js
+++ b/test/manifests/locations.js
@@ -33,7 +33,7 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: true,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/1080p.ts',
targetDuration: 6,
mediaSequence: 0,
discontinuitySequence: 0,
diff --git a/test/manifests/maat_vtt_segmentTemplate.js b/test/manifests/maat_vtt_segmentTemplate.js
index b7bbc6e..38894c9 100644
--- a/test/manifests/maat_vtt_segmentTemplate.js
+++ b/test/manifests/maat_vtt_segmentTemplate.js
@@ -25,7 +25,7 @@ export const parsedManifest = {
timelineStarts: [{ start: 0, timeline: 0 }],
discontinuitySequence: 0,
discontinuityStarts: [],
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -100,7 +100,7 @@ export const parsedManifest = {
timelineStarts: [{ start: 0, timeline: 0 }],
discontinuitySequence: 0,
discontinuityStarts: [],
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -183,7 +183,7 @@ export const parsedManifest = {
timelineStarts: [{ start: 0, timeline: 0 }],
discontinuitySequence: 0,
discontinuityStarts: [],
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -258,7 +258,7 @@ export const parsedManifest = {
timelineStarts: [{ start: 0, timeline: 0 }],
discontinuitySequence: 0,
discontinuityStarts: [],
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -421,7 +421,7 @@ export const parsedManifest = {
timelineStarts: [{ start: 0, timeline: 0 }],
discontinuitySequence: 0,
discontinuityStarts: [],
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.9185833333333333,
segments: [
{
@@ -503,7 +503,7 @@ export const parsedManifest = {
timelineStarts: [{ start: 0, timeline: 0 }],
discontinuitySequence: 0,
discontinuityStarts: [],
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.9185833333333333,
segments: [
{
diff --git a/test/manifests/multiperiod-segment-list.js b/test/manifests/multiperiod-segment-list.js
index aaf8be6..fefccca 100644
--- a/test/manifests/multiperiod-segment-list.js
+++ b/test/manifests/multiperiod-segment-list.js
@@ -36,7 +36,7 @@ export const parsedManifest = {
timeline: 6
}],
targetDuration: 3,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
segments: [
{
duration: 3,
diff --git a/test/manifests/multiperiod-segment-template.js b/test/manifests/multiperiod-segment-template.js
index 6cc21b9..139a3a7 100644
--- a/test/manifests/multiperiod-segment-template.js
+++ b/test/manifests/multiperiod-segment-template.js
@@ -30,7 +30,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 5,
segments: [
{
@@ -145,7 +145,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 5,
segments: [
{
diff --git a/test/manifests/multiperiod-startnumber-removed-periods.js b/test/manifests/multiperiod-startnumber-removed-periods.js
index e81f653..fdcbf5c 100644
--- a/test/manifests/multiperiod-startnumber-removed-periods.js
+++ b/test/manifests/multiperiod-startnumber-removed-periods.js
@@ -25,13 +25,13 @@ export const parsedManifest = {
'PROGRAM-ID': 1
},
endList: false,
- mediaSequence: 7,
+ mediaSequence: 3,
discontinuitySequence: 2,
discontinuityStarts: [0],
timelineStarts: [
{ start: 111, timeline: 111}
],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/audio/v0/',
segments: [
{
discontinuity: true,
@@ -41,7 +41,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 111,
- number: 7,
+ number: 3,
resolvedUri: 'http://example.com/audio/v0/862.m4f',
timeline: 111,
uri: '862.m4f'
@@ -53,7 +53,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 112,
- number: 8,
+ number: 4,
resolvedUri: 'http://example.com/audio/v0/863.m4f',
timeline: 111,
uri: '863.m4f'
@@ -65,7 +65,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 113,
- number: 9,
+ number: 5,
resolvedUri: 'http://example.com/audio/v0/864.m4f',
timeline: 111,
uri: '864.m4f'
@@ -107,7 +107,7 @@ export const parsedManifest = {
timelineStarts: [
{ start: 111, timeline: 111}
],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/D/',
segments: [
{
discontinuity: true,
@@ -172,7 +172,7 @@ export const parsedManifest = {
{ start: 111, timeline: 111}
],
discontinuityStarts: [0],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/E/',
segments: [
{
discontinuity: true,
@@ -231,13 +231,13 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: false,
- mediaSequence: 7,
+ mediaSequence: 3,
discontinuitySequence: 2,
timelineStarts: [
{ start: 111, timeline: 111}
],
discontinuityStarts: [0],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/F/',
segments: [
{
discontinuity: true,
@@ -247,7 +247,7 @@ export const parsedManifest = {
uri: 'F_init.mp4'
},
presentationTime: 111,
- number: 7,
+ number: 3,
resolvedUri: 'http://example.com/video/F/F862.m4f',
timeline: 111,
uri: 'F862.m4f'
@@ -259,7 +259,7 @@ export const parsedManifest = {
uri: 'F_init.mp4'
},
presentationTime: 112,
- number: 8,
+ number: 4,
resolvedUri: 'http://example.com/video/F/F863.m4f',
timeline: 111,
uri: 'F863.m4f'
@@ -271,7 +271,7 @@ export const parsedManifest = {
uri: 'F_init.mp4'
},
presentationTime: 113,
- number: 9,
+ number: 5,
resolvedUri: 'http://example.com/video/F/F864.m4f',
timeline: 111,
uri: 'F864.m4f'
@@ -302,7 +302,7 @@ export const parsedManifest = {
{ start: 111, timeline: 111}
],
discontinuityStarts: [0],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/A/',
segments: [
{
discontinuity: true,
@@ -367,7 +367,7 @@ export const parsedManifest = {
{ start: 111, timeline: 111}
],
discontinuityStarts: [0],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/B/',
segments: [
{
discontinuity: true,
@@ -426,13 +426,13 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: false,
- mediaSequence: 7,
+ mediaSequence: 3,
discontinuitySequence: 2,
timelineStarts: [
{ start: 111, timeline: 111}
],
discontinuityStarts: [0],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/C/',
segments: [
{
discontinuity: true,
@@ -442,7 +442,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 111,
- number: 7,
+ number: 3,
resolvedUri: 'http://example.com/video/C/C862.m4f',
timeline: 111,
uri: 'C862.m4f'
@@ -454,7 +454,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 112,
- number: 8,
+ number: 4,
resolvedUri: 'http://example.com/video/C/C863.m4f',
timeline: 111,
uri: 'C863.m4f'
@@ -466,7 +466,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 113,
- number: 9,
+ number: 5,
resolvedUri: 'http://example.com/video/C/C864.m4f',
timeline: 111,
uri: 'C864.m4f'
diff --git a/test/manifests/multiperiod-startnumber.js b/test/manifests/multiperiod-startnumber.js
index a7adeac..9d45358 100644
--- a/test/manifests/multiperiod-startnumber.js
+++ b/test/manifests/multiperiod-startnumber.js
@@ -27,14 +27,8 @@ export const parsedManifest = {
endList: false,
mediaSequence: 0,
discontinuitySequence: 0,
- discontinuityStarts: [3, 5, 7],
- timelineStarts: [
- { start: 100, timeline: 100},
- { start: 103, timeline: 103},
- { start: 107, timeline: 107},
- { start: 111, timeline: 111}
- ],
- resolvedUri: '',
+ discontinuityStarts: [],
+ resolvedUri: 'http://example.com/audio/1',
segments: [
{
duration: 1,
@@ -71,16 +65,36 @@ export const parsedManifest = {
resolvedUri: 'http://example.com/audio/502.m4f',
timeline: 100,
uri: '502.m4f'
- },
+ }
+ ],
+ targetDuration: 1,
+ timeline: 100,
+ timelineStarts: [
+ { start: 100, timeline: 100}
+ ],
+ uri: ''
+ },
+ {
+ attributes: {
+ 'BANDWIDTH': 128352,
+ 'CODECS': 'mp4a.40.5',
+ 'NAME': 'v0',
+ 'PROGRAM-ID': 1
+ },
+ discontinuitySequence: 1,
+ discontinuityStarts: [2, 4],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'http://example.com/audio/v0/',
+ segments: [
{
- discontinuity: true,
duration: 2,
map: {
resolvedUri: 'http://example.com/audio/v0/init.mp4',
uri: 'init.mp4'
},
presentationTime: 103,
- number: 3,
+ number: 0,
resolvedUri: 'http://example.com/audio/v0/000.m4f',
timeline: 103,
uri: '000.m4f'
@@ -92,7 +106,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 105,
- number: 4,
+ number: 1,
resolvedUri: 'http://example.com/audio/v0/001.m4f',
timeline: 103,
uri: '001.m4f'
@@ -105,7 +119,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 107,
- number: 5,
+ number: 2,
resolvedUri: 'http://example.com/audio/v0/000.m4f',
timeline: 107,
uri: '000.m4f'
@@ -117,7 +131,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 109,
- number: 6,
+ number: 3,
resolvedUri: 'http://example.com/audio/v0/001.m4f',
timeline: 107,
uri: '001.m4f'
@@ -130,7 +144,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 111,
- number: 7,
+ number: 4,
resolvedUri: 'http://example.com/audio/v0/862.m4f',
timeline: 111,
uri: '862.m4f'
@@ -142,7 +156,7 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 112,
- number: 8,
+ number: 5,
resolvedUri: 'http://example.com/audio/v0/863.m4f',
timeline: 111,
uri: '863.m4f'
@@ -154,14 +168,19 @@ export const parsedManifest = {
uri: 'init.mp4'
},
presentationTime: 113,
- number: 9,
+ number: 6,
resolvedUri: 'http://example.com/audio/v0/864.m4f',
timeline: 111,
uri: '864.m4f'
}
],
- targetDuration: 1,
- timeline: 100,
+ targetDuration: 2,
+ timeline: 103,
+ timelineStarts: [
+ { start: 103, timeline: 103},
+ { start: 107, timeline: 107},
+ { start: 111, timeline: 111}
+ ],
uri: ''
}
],
@@ -189,17 +208,11 @@ export const parsedManifest = {
},
'SUBTITLES': 'subs'
},
- endList: false,
- mediaSequence: 0,
discontinuitySequence: 0,
discontinuityStarts: [3, 5, 7],
- timelineStarts: [
- { start: 100, timeline: 100},
- { start: 103, timeline: 103},
- { start: 107, timeline: 107},
- { start: 111, timeline: 111}
- ],
- resolvedUri: '',
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'http://example.com/video/D/',
segments: [
{
duration: 1,
@@ -327,6 +340,12 @@ export const parsedManifest = {
],
targetDuration: 1,
timeline: 100,
+ timelineStarts: [
+ { start: 100, timeline: 100},
+ { start: 103, timeline: 103},
+ { start: 107, timeline: 107},
+ { start: 111, timeline: 111}
+ ],
uri: ''
},
{
@@ -343,17 +362,11 @@ export const parsedManifest = {
},
'SUBTITLES': 'subs'
},
- endList: false,
- mediaSequence: 0,
discontinuitySequence: 0,
discontinuityStarts: [3, 5, 7],
- timelineStarts: [
- { start: 100, timeline: 100},
- { start: 103, timeline: 103},
- { start: 107, timeline: 107},
- { start: 111, timeline: 111}
- ],
- resolvedUri: '',
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'http://example.com/video/E/',
segments: [
{
duration: 1,
@@ -481,6 +494,12 @@ export const parsedManifest = {
],
targetDuration: 1,
timeline: 100,
+ timelineStarts: [
+ { start: 100, timeline: 100},
+ { start: 103, timeline: 103},
+ { start: 107, timeline: 107},
+ { start: 111, timeline: 111}
+ ],
uri: ''
},
{
@@ -497,17 +516,11 @@ export const parsedManifest = {
},
'SUBTITLES': 'subs'
},
+ discontinuitySequence: 0,
+ discontinuityStarts: [],
endList: false,
mediaSequence: 0,
- discontinuitySequence: 0,
- discontinuityStarts: [3, 5, 7],
- timelineStarts: [
- { start: 100, timeline: 100},
- { start: 103, timeline: 103},
- { start: 107, timeline: 107},
- { start: 111, timeline: 111}
- ],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/E/',
segments: [
{
duration: 1,
@@ -544,97 +557,77 @@ export const parsedManifest = {
resolvedUri: 'http://example.com/video/E/F502.m4f',
timeline: 100,
uri: 'F502.m4f'
+ }
+ ],
+ targetDuration: 1,
+ timeline: 100,
+ timelineStarts: [
+ { start: 100, timeline: 100}
+ ],
+ uri: ''
+ },
+ {
+ attributes: {
+ 'AUDIO': 'audio',
+ 'BANDWIDTH': 1277155,
+ 'CODECS': 'avc1.4d001e',
+ 'FRAME-RATE': 30,
+ 'NAME': 'C',
+ 'PROGRAM-ID': 1,
+ 'RESOLUTION': {
+ height: 540,
+ width: 960
},
+ 'SUBTITLES': 'subs'
+ },
+ discontinuitySequence: 0,
+ discontinuityStarts: [],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'http://example.com/video/E/',
+ segments: [
{
- discontinuity: true,
- duration: 2,
- map: {
- resolvedUri: 'http://example.com/video/F/F_init.mp4',
- uri: 'F_init.mp4'
- },
- presentationTime: 103,
- number: 3,
- resolvedUri: 'http://example.com/video/F/F000.m4f',
- timeline: 103,
- uri: 'F000.m4f'
- },
- {
- duration: 2,
- map: {
- resolvedUri: 'http://example.com/video/F/F_init.mp4',
- uri: 'F_init.mp4'
- },
- presentationTime: 105,
- number: 4,
- resolvedUri: 'http://example.com/video/F/F001.m4f',
- timeline: 103,
- uri: 'F001.m4f'
- },
- {
- discontinuity: true,
- duration: 1,
- map: {
- resolvedUri: 'http://example.com/video/F/F_init.mp4',
- uri: 'F_init.mp4'
- },
- presentationTime: 107,
- number: 5,
- resolvedUri: 'http://example.com/video/F/F000.m4f',
- timeline: 107,
- uri: 'F000.m4f'
- },
- {
- duration: 1,
- map: {
- resolvedUri: 'http://example.com/video/F/F_init.mp4',
- uri: 'F_init.mp4'
- },
- presentationTime: 108,
- number: 6,
- resolvedUri: 'http://example.com/video/F/F001.m4f',
- timeline: 107,
- uri: 'F001.m4f'
- },
- {
- discontinuity: true,
duration: 1,
map: {
- resolvedUri: 'http://example.com/video/F/F_init.mp4',
- uri: 'F_init.mp4'
+ resolvedUri: 'http://example.com/video/E/C_init.mp4',
+ uri: 'C_init.mp4'
},
- presentationTime: 111,
- number: 7,
- resolvedUri: 'http://example.com/video/F/F862.m4f',
- timeline: 111,
- uri: 'F862.m4f'
+ presentationTime: 100,
+ number: 0,
+ resolvedUri: 'http://example.com/video/E/C500.m4f',
+ timeline: 100,
+ uri: 'C500.m4f'
},
{
duration: 1,
map: {
- resolvedUri: 'http://example.com/video/F/F_init.mp4',
- uri: 'F_init.mp4'
+ resolvedUri: 'http://example.com/video/E/C_init.mp4',
+ uri: 'C_init.mp4'
},
- presentationTime: 112,
- number: 8,
- resolvedUri: 'http://example.com/video/F/F863.m4f',
- timeline: 111,
- uri: 'F863.m4f'
+ presentationTime: 101,
+ number: 1,
+ resolvedUri: 'http://example.com/video/E/C501.m4f',
+ timeline: 100,
+ uri: 'C501.m4f'
},
{
duration: 1,
map: {
- resolvedUri: 'http://example.com/video/F/F_init.mp4',
- uri: 'F_init.mp4'
+ resolvedUri: 'http://example.com/video/E/C_init.mp4',
+ uri: 'C_init.mp4'
},
- presentationTime: 113,
- number: 9,
- resolvedUri: 'http://example.com/video/F/F864.m4f',
- timeline: 111,
- uri: 'F864.m4f'
+ presentationTime: 102,
+ number: 2,
+ resolvedUri: 'http://example.com/video/E/C502.m4f',
+ timeline: 100,
+ uri: 'C502.m4f'
}
],
targetDuration: 1,
timeline: 100,
+ timelineStarts: [
+ { start: 100, timeline: 100}
+ ],
uri: ''
},
{
@@ -651,17 +644,11 @@ export const parsedManifest = {
},
'SUBTITLES': 'subs'
},
- endList: false,
- mediaSequence: 0,
discontinuitySequence: 0,
discontinuityStarts: [3, 5, 7],
- timelineStarts: [
- { start: 100, timeline: 100},
- { start: 103, timeline: 103},
- { start: 107, timeline: 107},
- { start: 111, timeline: 111}
- ],
- resolvedUri: '',
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'http://example.com/video/A/',
segments: [
{
duration: 1,
@@ -789,6 +776,12 @@ export const parsedManifest = {
],
targetDuration: 1,
timeline: 100,
+ timelineStarts: [
+ { start: 100, timeline: 100},
+ { start: 103, timeline: 103},
+ { start: 107, timeline: 107},
+ { start: 111, timeline: 111}
+ ],
uri: ''
},
{
@@ -805,17 +798,11 @@ export const parsedManifest = {
},
'SUBTITLES': 'subs'
},
- endList: false,
- mediaSequence: 0,
discontinuitySequence: 0,
discontinuityStarts: [3, 5, 7],
- timelineStarts: [
- { start: 100, timeline: 100},
- { start: 103, timeline: 103},
- { start: 107, timeline: 107},
- { start: 111, timeline: 111}
- ],
- resolvedUri: '',
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'http://example.com/video/B/',
segments: [
{
duration: 1,
@@ -943,79 +930,158 @@ export const parsedManifest = {
],
targetDuration: 1,
timeline: 100,
+ timelineStarts: [
+ { start: 100, timeline: 100},
+ { start: 103, timeline: 103},
+ { start: 107, timeline: 107},
+ { start: 111, timeline: 111}
+ ],
uri: ''
},
{
attributes: {
'AUDIO': 'audio',
- 'BANDWIDTH': 1277155,
- 'CODECS': 'avc1.4d001e',
- 'FRAME-RATE': 30,
- 'NAME': 'C',
+ 'BANDWIDTH': 2215557,
+ 'CODECS': 'avc1.640020',
+ 'FRAME-RATE': 60,
+ 'NAME': 'F',
'PROGRAM-ID': 1,
'RESOLUTION': {
- height: 540,
- width: 960
+ height: 720,
+ width: 1280
},
'SUBTITLES': 'subs'
},
+ discontinuitySequence: 1,
+ discontinuityStarts: [2, 4],
endList: false,
mediaSequence: 0,
- discontinuitySequence: 0,
- discontinuityStarts: [3, 5, 7],
- timelineStarts: [
- { start: 100, timeline: 100},
- { start: 103, timeline: 103},
- { start: 107, timeline: 107},
- { start: 111, timeline: 111}
- ],
- resolvedUri: '',
+ resolvedUri: 'http://example.com/video/F/',
segments: [
{
- duration: 1,
+ duration: 2,
map: {
- resolvedUri: 'http://example.com/video/E/C_init.mp4',
- uri: 'C_init.mp4'
+ resolvedUri: 'http://example.com/video/F/F_init.mp4',
+ uri: 'F_init.mp4'
},
- presentationTime: 100,
+ presentationTime: 103,
number: 0,
- resolvedUri: 'http://example.com/video/E/C500.m4f',
- timeline: 100,
- uri: 'C500.m4f'
+ resolvedUri: 'http://example.com/video/F/F000.m4f',
+ timeline: 103,
+ uri: 'F000.m4f'
},
{
- duration: 1,
+ duration: 2,
map: {
- resolvedUri: 'http://example.com/video/E/C_init.mp4',
- uri: 'C_init.mp4'
+ resolvedUri: 'http://example.com/video/F/F_init.mp4',
+ uri: 'F_init.mp4'
},
- presentationTime: 101,
+ presentationTime: 105,
number: 1,
- resolvedUri: 'http://example.com/video/E/C501.m4f',
- timeline: 100,
- uri: 'C501.m4f'
+ resolvedUri: 'http://example.com/video/F/F001.m4f',
+ timeline: 103,
+ uri: 'F001.m4f'
},
{
+ discontinuity: true,
duration: 1,
map: {
- resolvedUri: 'http://example.com/video/E/C_init.mp4',
- uri: 'C_init.mp4'
+ resolvedUri: 'http://example.com/video/F/F_init.mp4',
+ uri: 'F_init.mp4'
},
- presentationTime: 102,
+ presentationTime: 107,
number: 2,
- resolvedUri: 'http://example.com/video/E/C502.m4f',
- timeline: 100,
- uri: 'C502.m4f'
+ resolvedUri: 'http://example.com/video/F/F000.m4f',
+ timeline: 107,
+ uri: 'F000.m4f'
+ },
+ {
+ duration: 1,
+ map: {
+ resolvedUri: 'http://example.com/video/F/F_init.mp4',
+ uri: 'F_init.mp4'
+ },
+ presentationTime: 108,
+ number: 3,
+ resolvedUri: 'http://example.com/video/F/F001.m4f',
+ timeline: 107,
+ uri: 'F001.m4f'
},
{
discontinuity: true,
+ duration: 1,
+ map: {
+ resolvedUri: 'http://example.com/video/F/F_init.mp4',
+ uri: 'F_init.mp4'
+ },
+ presentationTime: 111,
+ number: 4,
+ resolvedUri: 'http://example.com/video/F/F862.m4f',
+ timeline: 111,
+ uri: 'F862.m4f'
+ },
+ {
+ duration: 1,
+ map: {
+ resolvedUri: 'http://example.com/video/F/F_init.mp4',
+ uri: 'F_init.mp4'
+ },
+ presentationTime: 112,
+ number: 5,
+ resolvedUri: 'http://example.com/video/F/F863.m4f',
+ timeline: 111,
+ uri: 'F863.m4f'
+ },
+ {
+ duration: 1,
+ map: {
+ resolvedUri: 'http://example.com/video/F/F_init.mp4',
+ uri: 'F_init.mp4'
+ },
+ presentationTime: 113,
+ number: 6,
+ resolvedUri: 'http://example.com/video/F/F864.m4f',
+ timeline: 111,
+ uri: 'F864.m4f'
+ }
+ ],
+ targetDuration: 2,
+ timeline: 103,
+ timelineStarts: [
+ { start: 103, timeline: 103},
+ { start: 107, timeline: 107},
+ { start: 111, timeline: 111}
+ ],
+ uri: ''
+ },
+ {
+ attributes: {
+ 'AUDIO': 'audio',
+ 'BANDWIDTH': 1048480,
+ 'CODECS': 'avc1.4d001f',
+ 'FRAME-RATE': 30,
+ 'NAME': 'C',
+ 'PROGRAM-ID': 1,
+ 'RESOLUTION': {
+ height: 540,
+ width: 960
+ },
+ 'SUBTITLES': 'subs'
+ },
+ discontinuitySequence: 1,
+ discontinuityStarts: [2, 4],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'http://example.com/video/C/',
+ segments: [
+ {
duration: 2,
map: {
resolvedUri: 'http://example.com/video/C/C_init.mp4',
uri: 'C_init.mp4'
},
presentationTime: 103,
- number: 3,
+ number: 0,
resolvedUri: 'http://example.com/video/C/C000.m4f',
timeline: 103,
uri: 'C000.m4f'
@@ -1027,7 +1093,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 105,
- number: 4,
+ number: 1,
resolvedUri: 'http://example.com/video/C/C001.m4f',
timeline: 103,
uri: 'C001.m4f'
@@ -1040,7 +1106,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 107,
- number: 5,
+ number: 2,
resolvedUri: 'http://example.com/video/C/C000.m4f',
timeline: 107,
uri: 'C000.m4f'
@@ -1052,7 +1118,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 109,
- number: 6,
+ number: 3,
resolvedUri: 'http://example.com/video/C/C001.m4f',
timeline: 107,
uri: 'C001.m4f'
@@ -1065,7 +1131,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 111,
- number: 7,
+ number: 4,
resolvedUri: 'http://example.com/video/C/C862.m4f',
timeline: 111,
uri: 'C862.m4f'
@@ -1077,7 +1143,7 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 112,
- number: 8,
+ number: 5,
resolvedUri: 'http://example.com/video/C/C863.m4f',
timeline: 111,
uri: 'C863.m4f'
@@ -1089,14 +1155,19 @@ export const parsedManifest = {
uri: 'C_init.mp4'
},
presentationTime: 113,
- number: 9,
+ number: 6,
resolvedUri: 'http://example.com/video/C/C864.m4f',
timeline: 111,
uri: 'C864.m4f'
}
],
- targetDuration: 1,
- timeline: 100,
+ targetDuration: 2,
+ timeline: 103,
+ timelineStarts: [
+ { start: 103, timeline: 103},
+ { start: 107, timeline: 107},
+ { start: 111, timeline: 111}
+ ],
uri: ''
}
],
diff --git a/test/manifests/segmentBase.js b/test/manifests/segmentBase.js
index f45ab90..5925864 100644
--- a/test/manifests/segmentBase.js
+++ b/test/manifests/segmentBase.js
@@ -25,7 +25,7 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: true,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/1080p.ts',
targetDuration: 6,
mediaSequence: 0,
segments: [
diff --git a/test/manifests/segmentList.js b/test/manifests/segmentList.js
index 9b7ddce..3220b4a 100644
--- a/test/manifests/segmentList.js
+++ b/test/manifests/segmentList.js
@@ -27,7 +27,7 @@ export const parsedManifest = {
endList: true,
mediaSequence: 0,
targetDuration: 1,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
segments: [
{
duration: 1,
@@ -123,7 +123,7 @@ export const parsedManifest = {
'SUBTITLES': 'subs'
},
endList: true,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
mediaSequence: 0,
targetDuration: 60,
segments: [
diff --git a/test/manifests/vtt_codecs.js b/test/manifests/vtt_codecs.js
index 46dcbae..aa80010 100644
--- a/test/manifests/vtt_codecs.js
+++ b/test/manifests/vtt_codecs.js
@@ -25,7 +25,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -103,7 +103,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -189,7 +189,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -267,7 +267,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.984,
segments: [
{
@@ -440,7 +440,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.9185833333333333,
segments: [
{
@@ -525,7 +525,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 1.9185833333333333,
segments: [
{
diff --git a/test/manifests/webmsegments.js b/test/manifests/webmsegments.js
index daae010..ed376c6 100644
--- a/test/manifests/webmsegments.js
+++ b/test/manifests/webmsegments.js
@@ -22,7 +22,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 4,
segments: [
{
@@ -108,7 +108,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
- resolvedUri: '',
+ resolvedUri: 'https://www.example.com/base',
targetDuration: 4,
segments: [
{
diff --git a/test/toM3u8.test.js b/test/toM3u8.test.js
index 2440371..6d28e6b 100644
--- a/test/toM3u8.test.js
+++ b/test/toM3u8.test.js
@@ -213,6 +213,326 @@ QUnit.test('playlists', function(assert) {
assert.deepEqual(toM3u8({ dashPlaylists }), expected);
});
+QUnit.test('playlists with content steering and resolvable URLs', function(assert) {
+ const contentSteering = {
+ defaultServiceLocation: 'beta',
+ proxyServerURL: 'http://127.0.0.1:3455/steer',
+ queryBeforeStart: false,
+ serverURL: 'https://example.com/app/url'
+ };
+
+ const dashPlaylists = [
+ {
+ attributes: {
+ bandwidth: 5000000,
+ baseUrl: 'https://cdn1.example.com/video',
+ clientOffset: 0,
+ codecs: 'avc1.64001e',
+ duration: 0,
+ height: 404,
+ id: 'test',
+ mimeType: 'video/mp4',
+ periodStart: 0,
+ role: {
+ value: 'main'
+ },
+ serviceLocation: 'alpha',
+ sourceDuration: 0,
+ type: 'dyanmic',
+ width: 720
+ },
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn1.example.com/video',
+ uri: ''
+ },
+ number: 1,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn1.example.com/video',
+ timeline: 0,
+ uri: ''
+ }
+ ]
+ },
+ {
+ attributes: {
+ bandwidth: 5000000,
+ baseUrl: 'https://cdn2.example.com/video',
+ clientOffset: 0,
+ codecs: 'avc1.64001e',
+ duration: 0,
+ height: 404,
+ id: 'test',
+ mimeType: 'video/mp4',
+ periodStart: 0,
+ role: {
+ value: 'main'
+ },
+ serviceLocation: 'beta',
+ sourceDuration: 0,
+ type: 'dyanmic',
+ width: 720
+ },
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn2.example.com/video',
+ uri: ''
+ },
+ number: 1,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn2.example.com/video',
+ timeline: 0,
+ uri: ''
+ }
+ ]
+ },
+ {
+ attributes: {
+ bandwidth: 256,
+ baseUrl: 'https://cdn1.example.com/vtt',
+ clientOffset: 0,
+ duration: 0,
+ id: 'en',
+ lang: 'en',
+ mimeType: 'text/vtt',
+ periodStart: 0,
+ role: {},
+ serviceLocation: 'alpha',
+ sourceDuration: 0,
+ type: 'dyanmic'
+ },
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn1.example.com/vtt',
+ uri: ''
+ },
+ number: 1,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn1.example.com/vtt',
+ timeline: 0,
+ uri: ''
+ }
+ ]
+ },
+ {
+ attributes: {
+ bandwidth: 256,
+ baseUrl: 'https://cdn2.example.com/vtt',
+ clientOffset: 0,
+ id: 'en',
+ lang: 'en',
+ mimeType: 'text/vtt',
+ periodStart: 0,
+ role: {},
+ serviceLocation: 'beta',
+ sourceDuration: 0,
+ type: 'dyanmic'
+ }
+ }
+ ];
+
+ const expected = {
+ allowCache: true,
+ contentSteering: {
+ defaultServiceLocation: 'beta',
+ proxyServerURL: 'http://127.0.0.1:3455/steer',
+ queryBeforeStart: false,
+ serverURL: 'https://example.com/app/url'
+ },
+ discontinuityStarts: [],
+ duration: 0,
+ endList: true,
+ mediaGroups: {
+ AUDIO: {},
+ ['CLOSED-CAPTIONS']: {},
+ SUBTITLES: {
+ subs: {
+ en: {
+ autoselect: false,
+ default: false,
+ language: 'en',
+ playlists: [
+ {
+ attributes: {
+ BANDWIDTH: 256,
+ NAME: 'en',
+ ['PROGRAM-ID']: 1,
+ serviceLocation: 'alpha'
+ },
+ discontinuitySequence: 0,
+ discontinuityStarts: [],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'https://cdn1.example.com/vtt',
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn1.example.com/vtt',
+ uri: ''
+ },
+ number: 0,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn1.example.com/vtt',
+ timeline: 0,
+ uri: ''
+ }
+ ],
+ targetDuration: 0,
+ timeline: 0,
+ timelineStarts: [
+ {
+ start: 0,
+ timeline: 0
+ }
+ ],
+ uri: ''
+ },
+ {
+ attributes: {
+ BANDWIDTH: 256,
+ NAME: 'en',
+ ['PROGRAM-ID']: 1,
+ serviceLocation: 'beta'
+ },
+ discontinuitySequence: 0,
+ discontinuityStarts: [],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'https://cdn2.example.com/vtt',
+ segments: [
+ {
+ duration: 0,
+ number: 0,
+ resolvedUri: 'https://cdn2.example.com/vtt',
+ timeline: 0,
+ uri: 'https://cdn2.example.com/vtt'
+ }
+ ],
+ targetDuration: 0,
+ timeline: 0,
+ timelineStarts: [
+ {
+ start: 0,
+ timeline: 0
+ }
+ ],
+ uri: ''
+ }
+ ],
+ uri: ''
+ }
+ }
+ },
+ VIDEO: {}
+ },
+ playlists: [
+ {
+ attributes: {
+ AUDIO: 'audio',
+ BANDWIDTH: 5000000,
+ CODECS: 'avc1.64001e',
+ NAME: 'test',
+ ['PROGRAM-ID']: 1,
+ RESOLUTION: {
+ height: 404,
+ width: 720
+ },
+ SUBTITLES: 'subs',
+ serviceLocation: 'alpha'
+ },
+ discontinuitySequence: 0,
+ discontinuityStarts: [],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'https://cdn1.example.com/video',
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn1.example.com/video',
+ uri: ''
+ },
+ number: 0,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn1.example.com/video',
+ timeline: 0,
+ uri: ''
+ }
+ ],
+ targetDuration: 0,
+ timeline: 0,
+ timelineStarts: [
+ {
+ start: 0,
+ timeline: 0
+ }
+ ],
+ uri: ''
+ },
+ {
+ attributes: {
+ AUDIO: 'audio',
+ BANDWIDTH: 5000000,
+ CODECS: 'avc1.64001e',
+ NAME: 'test',
+ ['PROGRAM-ID']: 1,
+ RESOLUTION: {
+ height: 404,
+ width: 720
+ },
+ SUBTITLES: 'subs',
+ serviceLocation: 'beta'
+ },
+ discontinuitySequence: 0,
+ discontinuityStarts: [],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'https://cdn2.example.com/video',
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn2.example.com/video',
+ uri: ''
+ },
+ number: 0,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn2.example.com/video',
+ timeline: 0,
+ uri: ''
+ }
+ ],
+ targetDuration: 0,
+ timeline: 0,
+ timelineStarts: [
+ {
+ start: 0,
+ timeline: 0
+ }
+ ],
+ uri: ''
+ }
+ ],
+ segments: [],
+ timelineStarts: [
+ {
+ start: 0,
+ timeline: 0
+ }
+ ],
+ uri: ''
+ };
+
+ assert.deepEqual(toM3u8({ dashPlaylists, contentSteering }), expected);
+});
+
QUnit.test('playlists with content steering', function(assert) {
const contentSteering = {
defaultServiceLocation: 'beta',
@@ -389,15 +709,14 @@ QUnit.test('playlists with content steering', function(assert) {
height: 404,
width: 720
},
- SUBTITLES: 'subs'
+ SUBTITLES: 'subs',
+ serviceLocation: 'alpha'
},
discontinuitySequence: 0,
- discontinuityStarts: [
- 1
- ],
+ discontinuityStarts: [],
endList: false,
mediaSequence: 0,
- resolvedUri: '',
+ resolvedUri: 'https://cdn1.example.com/',
segments: [
{
duration: 0,
@@ -410,15 +729,45 @@ QUnit.test('playlists with content steering', function(assert) {
resolvedUri: 'https://cdn1.example.com/',
timeline: 0,
uri: ''
+ }
+ ],
+ targetDuration: 0,
+ timeline: 0,
+ timelineStarts: [
+ {
+ start: 0,
+ timeline: 0
+ }
+ ],
+ uri: ''
+ },
+ {
+ attributes: {
+ AUDIO: 'audio',
+ BANDWIDTH: 5000000,
+ CODECS: 'avc1.64001e',
+ NAME: 'test',
+ ['PROGRAM-ID']: 1,
+ RESOLUTION: {
+ height: 404,
+ width: 720
},
+ SUBTITLES: 'subs',
+ serviceLocation: 'beta'
+ },
+ discontinuitySequence: 0,
+ discontinuityStarts: [],
+ endList: false,
+ mediaSequence: 0,
+ resolvedUri: 'https://cdn2.example.com/',
+ segments: [
{
- discontinuity: true,
duration: 0,
map: {
resolvedUri: 'https://cdn2.example.com/',
uri: ''
},
- number: 1,
+ number: 0,
presentationTime: 0,
resolvedUri: 'https://cdn2.example.com/',
timeline: 0,
@@ -428,10 +777,6 @@ QUnit.test('playlists with content steering', function(assert) {
targetDuration: 0,
timeline: 0,
timelineStarts: [
- {
- start: 0,
- timeline: 0
- },
{
start: 0,
timeline: 0
diff --git a/test/toPlaylists.test.js b/test/toPlaylists.test.js
index fd0528e..0331c26 100644
--- a/test/toPlaylists.test.js
+++ b/test/toPlaylists.test.js
@@ -410,3 +410,204 @@ QUnit.test('presentationTime accounts for presentationTimeOffset', function(asse
assert.deepEqual(toPlaylists(representations), playlists);
});
+
+QUnit.test('playlist with content steering and resolvable BaseURLs', function(assert) {
+ const representations = [
+ {
+ attributes: {
+ bandwidth: 5000000,
+ baseUrl: 'https://cdn1.example.com/video',
+ clientOffset: 0,
+ codecs: 'avc1.64001e',
+ height: 404,
+ id: 'test',
+ mimeType: 'video/mp4',
+ periodStart: 0,
+ role: {
+ value: 'main'
+ },
+ serviceLocation: 'alpha',
+ sourceDuration: 0,
+ type: 'dyanmic',
+ width: 720
+ },
+ segmentInfo: {
+ template: {}
+ }
+ },
+ {
+ attributes: {
+ bandwidth: 5000000,
+ baseUrl: 'https://cdn2.example.com/video',
+ clientOffset: 0,
+ codecs: 'avc1.64001e',
+ height: 404,
+ id: 'test',
+ mimeType: 'video/mp4',
+ periodStart: 0,
+ role: {
+ value: 'main'
+ },
+ serviceLocation: 'beta',
+ sourceDuration: 0,
+ type: 'dyanmic',
+ width: 720
+ },
+ segmentInfo: {
+ template: {}
+ }
+ },
+ {
+ attributes: {
+ bandwidth: 256,
+ baseUrl: 'https://cdn1.example.com/vtt',
+ clientOffset: 0,
+ id: 'en',
+ lang: 'en',
+ mimeType: 'text/vtt',
+ periodStart: 0,
+ role: {},
+ serviceLocation: 'alpha',
+ sourceDuration: 0,
+ type: 'dyanmic'
+ },
+ segmentInfo: {
+ template: {}
+ }
+ },
+ {
+ attributes: {
+ bandwidth: 256,
+ baseUrl: 'https://cdn2.example.com/vtt',
+ clientOffset: 0,
+ id: 'en',
+ lang: 'en',
+ mimeType: 'text/vtt',
+ periodStart: 0,
+ role: {},
+ serviceLocation: 'beta',
+ sourceDuration: 0,
+ type: 'dyanmic'
+ },
+ segmentInfo: {}
+ }
+ ];
+
+ const playlists = [
+ {
+ attributes: {
+ bandwidth: 5000000,
+ baseUrl: 'https://cdn1.example.com/video',
+ clientOffset: 0,
+ codecs: 'avc1.64001e',
+ duration: 0,
+ height: 404,
+ id: 'test',
+ mimeType: 'video/mp4',
+ periodStart: 0,
+ role: {
+ value: 'main'
+ },
+ serviceLocation: 'alpha',
+ sourceDuration: 0,
+ type: 'dyanmic',
+ width: 720
+ },
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn1.example.com/video',
+ uri: ''
+ },
+ number: 1,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn1.example.com/video',
+ timeline: 0,
+ uri: ''
+ }
+ ]
+ },
+ {
+ attributes: {
+ bandwidth: 5000000,
+ baseUrl: 'https://cdn2.example.com/video',
+ clientOffset: 0,
+ codecs: 'avc1.64001e',
+ duration: 0,
+ height: 404,
+ id: 'test',
+ mimeType: 'video/mp4',
+ periodStart: 0,
+ role: {
+ value: 'main'
+ },
+ serviceLocation: 'beta',
+ sourceDuration: 0,
+ type: 'dyanmic',
+ width: 720
+ },
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn2.example.com/video',
+ uri: ''
+ },
+ number: 1,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn2.example.com/video',
+ timeline: 0,
+ uri: ''
+ }
+ ]
+ },
+ {
+ attributes: {
+ bandwidth: 256,
+ baseUrl: 'https://cdn1.example.com/vtt',
+ clientOffset: 0,
+ duration: 0,
+ id: 'en',
+ lang: 'en',
+ mimeType: 'text/vtt',
+ periodStart: 0,
+ role: {},
+ serviceLocation: 'alpha',
+ sourceDuration: 0,
+ type: 'dyanmic'
+ },
+ segments: [
+ {
+ duration: 0,
+ map: {
+ resolvedUri: 'https://cdn1.example.com/vtt',
+ uri: ''
+ },
+ number: 1,
+ presentationTime: 0,
+ resolvedUri: 'https://cdn1.example.com/vtt',
+ timeline: 0,
+ uri: ''
+ }
+ ]
+ },
+ {
+ attributes: {
+ bandwidth: 256,
+ baseUrl: 'https://cdn2.example.com/vtt',
+ clientOffset: 0,
+ id: 'en',
+ lang: 'en',
+ mimeType: 'text/vtt',
+ periodStart: 0,
+ role: {},
+ serviceLocation: 'beta',
+ sourceDuration: 0,
+ type: 'dyanmic'
+ }
+ }
+ ];
+
+ assert.deepEqual(toPlaylists(representations), playlists);
+});