From 5394c850e43eeacfc25a61fc4b979b289156cb46 Mon Sep 17 00:00:00 2001 From: wseymour15 Date: Tue, 8 Aug 2023 16:23:18 -0500 Subject: [PATCH] additional tests and cleanup --- src/inheritAttributes.js | 13 ++- test/toM3u8.test.js | 241 ++++++++++++++++++++++++++++++++++++++- test/toPlaylists.test.js | 176 +++++++++++++++++++++++++++- 3 files changed, 422 insertions(+), 8 deletions(-) diff --git a/src/inheritAttributes.js b/src/inheritAttributes.js index de493a1..e9a7f1d 100644 --- a/src/inheritAttributes.js +++ b/src/inheritAttributes.js @@ -474,10 +474,6 @@ export const generateContentSteeringInformation = (contentSteeringNodes) => { // to `false` if it doesn't exist infoFromContentSteeringTag.queryBeforeStart = (infoFromContentSteeringTag.queryBeforeStart === 'true'); - // Get CDN info from BaseURLs that have a valid serviceLocation - // const contentSteeringBaseUrls = baseURLNodes.filter(({ attributes }) => attributes.serviceLocation); - // const cdns = contentSteeringBaseUrls.map(base => merge({ url: getContent(base) }, parseAttributes(base))); - return infoFromContentSteeringTag; }; @@ -570,8 +566,6 @@ export const inheritAttributes = (mpd, options = {}) => { const mpdAttributes = parseAttributes(mpd); const mpdBaseUrls = buildBaseUrls([ manifestUri ], findChildren(mpd, 'BaseURL')); - - // mpdBaseUrls.map((base, i) => merge({ baseUrl: periodBaseUrls[i] }, parseAttributes(base))); const contentSteeringNodes = findChildren(mpd, 'ContentSteering'); // See DASH spec section 5.3.1.2, Semantics of MPD element. Default type to 'static'. @@ -611,6 +605,13 @@ export const inheritAttributes = (mpd, options = {}) => { return { locations: mpdAttributes.locations, contentSteeringInfo: generateContentSteeringInformation(contentSteeringNodes), + // TODO: There are occurences where this `representationInfo` array contains undesired + // duplicates. This generally occurs when there are multiple BaseURL nodes that are + // direct children of the MPD node. When we attempt to resolve URLs from a combination of the + // parent BaseURL and a child BaseURL, and the value does not resolve, + // we end up returning the child BaseURL multiple times. + // We need to determine a way to remove these duplicates in a safe way. + // See: https://github.com/videojs/mpd-parser/pull/17#discussion_r162750527 representationInfo: flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls))), eventStream: flatten(periods.map(toEventStream)) }; diff --git a/test/toM3u8.test.js b/test/toM3u8.test.js index 45a6596..2440371 100644 --- a/test/toM3u8.test.js +++ b/test/toM3u8.test.js @@ -213,7 +213,246 @@ QUnit.test('playlists', function(assert) { assert.deepEqual(toM3u8({ dashPlaylists }), expected); }); -// check here maybe +QUnit.test('playlists with content steering', 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/', + 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/', + uri: '' + }, + number: 1, + presentationTime: 0, + resolvedUri: 'https://cdn1.example.com/', + timeline: 0, + uri: '' + } + ] + }, { + attributes: { + bandwidth: 5000000, + baseUrl: 'https://cdn2.example.com/', + 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/', + uri: '' + }, + number: 1, + presentationTime: 0, + resolvedUri: 'https://cdn2.example.com/', + timeline: 0, + uri: '' + } + ] + }, { + attributes: { + bandwidth: 256, + baseUrl: 'https://example.com/en.vtt', + clientOffset: 0, + id: 'en', + lang: 'en', + mimeType: 'text/vtt', + periodStart: 0, + role: {}, + sourceDuration: 0, + type: 'dyanmic' + } + }, { + attributes: { + bandwidth: 256, + baseUrl: 'https://example.com/en.vtt', + clientOffset: 0, + id: 'en', + lang: 'en', + mimeType: 'text/vtt', + periodStart: 0, + role: {}, + 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 + }, + discontinuitySequence: 0, + discontinuityStarts: [], + endList: false, + mediaSequence: 0, + resolvedUri: 'https://example.com/en.vtt', + segments: [ + { + duration: 0, + number: 0, + resolvedUri: 'https://example.com/en.vtt', + timeline: 0, + uri: 'https://example.com/en.vtt' + } + ], + targetDuration: 0, + timeline: 0, + timelineStarts: [ + { + start: 0, + timeline: 0 + }, + { + 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' + }, + discontinuitySequence: 0, + discontinuityStarts: [ + 1 + ], + endList: false, + mediaSequence: 0, + resolvedUri: '', + segments: [ + { + duration: 0, + map: { + resolvedUri: 'https://cdn1.example.com/', + uri: '' + }, + number: 0, + presentationTime: 0, + resolvedUri: 'https://cdn1.example.com/', + timeline: 0, + uri: '' + }, + { + discontinuity: true, + duration: 0, + map: { + resolvedUri: 'https://cdn2.example.com/', + uri: '' + }, + number: 1, + presentationTime: 0, + resolvedUri: 'https://cdn2.example.com/', + timeline: 0, + uri: '' + } + ], + targetDuration: 0, + timeline: 0, + timelineStarts: [ + { + start: 0, + timeline: 0 + }, + { + start: 0, + timeline: 0 + } + ], + uri: '' + } + ], + segments: [], + timelineStarts: [ + { + start: 0, + timeline: 0 + } + ], + uri: '' + }; + + assert.deepEqual(toM3u8({ dashPlaylists, contentSteering }), expected); +}); + QUnit.test('playlists with segments', function(assert) { const dashPlaylists = [{ attributes: { diff --git a/test/toPlaylists.test.js b/test/toPlaylists.test.js index 934195d..fd0528e 100644 --- a/test/toPlaylists.test.js +++ b/test/toPlaylists.test.js @@ -9,7 +9,6 @@ QUnit.test('no representations', function(assert) { assert.deepEqual(toPlaylists([]), []); }); -// test this QUnit.test('pretty simple', function(assert) { const representations = [{ attributes: { @@ -86,6 +85,181 @@ QUnit.test('segment base', function(assert) { assert.deepEqual(toPlaylists(representations), playlists); }); +QUnit.test('playlist with content steering BaseURLs', function(assert) { + const representations = [ + { + attributes: { + bandwidth: 5000000, + baseUrl: 'https://cdn1.example.com/', + 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/', + 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://example.com/en.vtt', + clientOffset: 0, + id: 'en', + lang: 'en', + mimeType: 'text/vtt', + periodStart: 0, + role: {}, + sourceDuration: 0, + type: 'dyanmic' + }, + segmentInfo: {} + }, + { + attributes: { + bandwidth: 256, + baseUrl: 'https://example.com/en.vtt', + clientOffset: 0, + id: 'en', + lang: 'en', + mimeType: 'text/vtt', + periodStart: 0, + role: {}, + sourceDuration: 0, + type: 'dyanmic' + }, + segmentInfo: {} + } + ]; + + const playlists = [{ + attributes: { + bandwidth: 5000000, + baseUrl: 'https://cdn1.example.com/', + 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/', + uri: '' + }, + number: 1, + presentationTime: 0, + resolvedUri: 'https://cdn1.example.com/', + timeline: 0, + uri: '' + } + ] + }, { + attributes: { + bandwidth: 5000000, + baseUrl: 'https://cdn2.example.com/', + 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/', + uri: '' + }, + number: 1, + presentationTime: 0, + resolvedUri: 'https://cdn2.example.com/', + timeline: 0, + uri: '' + } + ] + }, { + attributes: { + bandwidth: 256, + baseUrl: 'https://example.com/en.vtt', + clientOffset: 0, + id: 'en', + lang: 'en', + mimeType: 'text/vtt', + periodStart: 0, + role: {}, + sourceDuration: 0, + type: 'dyanmic' + } + }, { + attributes: { + bandwidth: 256, + baseUrl: 'https://example.com/en.vtt', + clientOffset: 0, + id: 'en', + lang: 'en', + mimeType: 'text/vtt', + periodStart: 0, + role: {}, + sourceDuration: 0, + type: 'dyanmic' + } + }]; + + assert.deepEqual(toPlaylists(representations), playlists); +}); + QUnit.test('segment base with sidx', function(assert) { const representations = [{ attributes: {