Skip to content

Commit

Permalink
fix: improve serviceLocation for content steering (#177)
Browse files Browse the repository at this point in the history
  • Loading branch information
wseymour15 authored Aug 30, 2023
1 parent 5493dda commit fee1870
Show file tree
Hide file tree
Showing 19 changed files with 944 additions and 278 deletions.
16 changes: 15 additions & 1 deletion src/inheritAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
});
}));
};
Expand Down
104 changes: 68 additions & 36 deletions src/toM3u8.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -98,7 +115,7 @@ export const formatAudioPlaylist = ({
uri: '',
endList: attributes.type === 'static',
timeline: attributes.periodStart,
resolvedUri: '',
resolvedUri: attributes.baseUrl || '',
targetDuration: attributes.duration,
discontinuitySequence,
discontinuityStarts,
Expand All @@ -111,6 +128,10 @@ export const formatAudioPlaylist = ({
playlist.contentProtection = attributes.contentProtection;
}

if (attributes.serviceLocation) {
playlist.attributes.serviceLocation = attributes.serviceLocation;
}

if (sidx) {
playlist.sidx = sidx;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -152,7 +174,7 @@ export const formatVttPlaylist = ({
if (attributes.codecs) {
m3u8Attributes.CODECS = attributes.codecs;
}
return {
const vttPlaylist = {
attributes: m3u8Attributes,
uri: '',
endList: attributes.type === 'static',
Expand All @@ -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) => {
Expand Down Expand Up @@ -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,
Expand All @@ -304,6 +332,10 @@ export const formatVideoPlaylist = ({
playlist.contentProtection = attributes.contentProtection;
}

if (attributes.serviceLocation) {
playlist.attributes.serviceLocation = attributes.serviceLocation;
}

if (sidx) {
playlist.sidx = sidx;
}
Expand Down
13 changes: 8 additions & 5 deletions test/inheritAttributes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -927,10 +927,11 @@ QUnit.test('end to end - content steering - resolvable base URLs', function(asse
id="test"
width="720">
</Representation>
<BaseURL>/video</BaseURL>
</AdaptationSet>
<AdaptationSet mimeType="text/vtt" lang="en">
<Representation bandwidth="256" id="en">
<BaseURL>/video</BaseURL>
<BaseURL>/vtt</BaseURL>
</Representation>
</AdaptationSet>
</Period>
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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'
},
Expand All @@ -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'
},
Expand Down
2 changes: 1 addition & 1 deletion test/manifests/608-captions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }],
Expand Down
2 changes: 1 addition & 1 deletion test/manifests/708-captions.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 }],
Expand Down
4 changes: 2 additions & 2 deletions test/manifests/audio-only.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion test/manifests/location.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion test/manifests/locations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 6 additions & 6 deletions test/manifests/maat_vtt_segmentTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down
2 changes: 1 addition & 1 deletion test/manifests/multiperiod-segment-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const parsedManifest = {
timeline: 6
}],
targetDuration: 3,
resolvedUri: '',
resolvedUri: 'https://www.example.com/base',
segments: [
{
duration: 3,
Expand Down
4 changes: 2 additions & 2 deletions test/manifests/multiperiod-segment-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
resolvedUri: '',
resolvedUri: 'https://www.example.com/base',
targetDuration: 5,
segments: [
{
Expand Down Expand Up @@ -145,7 +145,7 @@ export const parsedManifest = {
uri: '',
endList: true,
timeline: 0,
resolvedUri: '',
resolvedUri: 'https://www.example.com/base',
targetDuration: 5,
segments: [
{
Expand Down
Loading

0 comments on commit fee1870

Please sign in to comment.