diff --git a/package-lock.json b/package-lock.json index a11a6282..21dd4492 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,152 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0-beta.36.tgz", + "integrity": "sha512-sW77BFwJ48YvQp3Gzz5xtAUiXuYOL2aMJKDwiaY3OcvdqBFurtYfOpSa4QrNyDxmOGRFSYzUpabU2m9QrlWE7w==", + "dev": true, + "requires": { + "chalk": "2.3.1", + "esutils": "2.0.2", + "js-tokens": "3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.1" + } + }, + "chalk": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.1.tgz", + "integrity": "sha512-QUU4ofkDoMIVO7hcx1iPTISs88wsO8jA92RQIm4JAwZvFGGAV2hSAA1NX7oVj2Ej2Q6NDTcRDjPTFrMCRZoJ6g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "5.2.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "supports-color": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.2.0.tgz", + "integrity": "sha512-F39vS48la4YvTZUPVeTqsjsFNrvcMwrV3RLZINsmHo+7djCvuUzSIeXOnZ5hmjef4bajL1dNccN+tg5XAliO5Q==", + "dev": true, + "requires": { + "has-flag": "3.0.0" + } + } + } + }, + "@babel/helper-function-name": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.36.tgz", + "integrity": "sha512-/SGPOyifPf20iTrMN+WdlY2MbKa7/o4j7B/4IAsdOusASp2icT+Wcdjf4tjJHaXNX8Pe9bpgVxLNxhRvcf8E5w==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "7.0.0-beta.36", + "@babel/template": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.36.tgz", + "integrity": "sha512-vPPcx2vsSoDbcyWr9S3nd0FM3B4hEXnt0p1oKpwa08GwK0fSRxa98MyaRGf8suk8frdQlG1P3mDrz5p/Rr3pbA==", + "dev": true, + "requires": { + "@babel/types": "7.0.0-beta.36" + } + }, + "@babel/template": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.0.0-beta.36.tgz", + "integrity": "sha512-mUBi90WRyZ9iVvlWLEdeo8gn/tROyJdjKNC4W5xJTSZL+9MS89rTJSqiaJKXIkxk/YRDL/g/8snrG/O0xl33uA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36", + "babylon": "7.0.0-beta.36", + "lodash": "4.17.4" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz", + "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==", + "dev": true + } + } + }, + "@babel/traverse": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.0.0-beta.36.tgz", + "integrity": "sha512-OTUb6iSKVR/98dGThRJ1BiyfwbuX10BVnkz89IpaerjTPRhDfMBfLsqmzxz5MiywUOW4M0Clta0o7rSxkfcuzw==", + "dev": true, + "requires": { + "@babel/code-frame": "7.0.0-beta.36", + "@babel/helper-function-name": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36", + "babylon": "7.0.0-beta.36", + "debug": "3.1.0", + "globals": "11.3.0", + "invariant": "2.2.2", + "lodash": "4.17.4" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz", + "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==", + "dev": true + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "globals": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.3.0.tgz", + "integrity": "sha512-kkpcKNlmQan9Z5ZmgqKH/SMbSmjxQ7QjyNqfXVc8VJcoBV2UEg+sxQD15GQofGRh2hfpwUb70VC31DR7Rq5Hdw==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.36.tgz", + "integrity": "sha512-PyAORDO9um9tfnrddXgmWN9e6Sq9qxraQIt5ynqBOSXKA5qvK1kUr+Q3nSzKFdzorsiK+oqcUnAFvEoKxv9D+Q==", + "dev": true, + "requires": { + "esutils": "2.0.2", + "lodash": "4.17.4", + "to-fast-properties": "2.0.0" + }, + "dependencies": { + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, "JSONStream": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", @@ -293,16 +439,25 @@ } }, "babel-eslint": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-6.1.2.tgz", - "integrity": "sha1-UpNBn+NnLWZZjTJ9qWlFZ7pqXy8=", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-8.2.1.tgz", + "integrity": "sha512-RzdVOyWKQRUnLXhwLk+eKb4oyW+BykZSkpYwFhM4tnfzAG5OWfvG0w/uyzMp5XKEU0jN82+JefHr39bG2+KhRQ==", "dev": true, "requires": { - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash.assign": "4.2.0", - "lodash.pickby": "4.6.0" + "@babel/code-frame": "7.0.0-beta.36", + "@babel/traverse": "7.0.0-beta.36", + "@babel/types": "7.0.0-beta.36", + "babylon": "7.0.0-beta.36", + "eslint-scope": "3.7.1", + "eslint-visitor-keys": "1.0.0" + }, + "dependencies": { + "babylon": { + "version": "7.0.0-beta.36", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.36.tgz", + "integrity": "sha512-rw4YdadGwajAMMRl6a5swhQ0JCOOFyaYCfJ0AsmNBD8uBD/r4J8mux7wBaqavvFKqUKQYWOzA1Speams4YDzsQ==", + "dev": true + } } }, "babel-generator": { @@ -2130,6 +2285,22 @@ "unified": "6.1.6" } }, + "eslint-scope": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz", + "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=", + "dev": true, + "requires": { + "esrecurse": "4.2.0", + "estraverse": "4.2.0" + } + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", + "dev": true + }, "espree": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.2.tgz", @@ -6849,6 +7020,19 @@ "tsmlb": "1.0.0" }, "dependencies": { + "babel-eslint": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-6.1.2.tgz", + "integrity": "sha1-UpNBn+NnLWZZjTJ9qWlFZ7pqXy8=", + "dev": true, + "requires": { + "babel-traverse": "6.26.0", + "babel-types": "6.26.0", + "babylon": "6.18.0", + "lodash.assign": "4.2.0", + "lodash.pickby": "4.6.0" + } + }, "commander": { "version": "2.12.2", "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", diff --git a/package.json b/package.json index d7333240..85785845 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "src/" ], "devDependencies": { + "babel-eslint": "^8.2.1", "babel-plugin-external-helpers": "^6.22.0", "babel-plugin-transform-object-assign": "^6.8.0", "babel-preset-es2015": "^6.14.0", diff --git a/src/inheritAttributes.js b/src/inheritAttributes.js index 285427f6..0f31d791 100644 --- a/src/inheritAttributes.js +++ b/src/inheritAttributes.js @@ -1,5 +1,5 @@ import { flatten } from './utils/list'; -import { shallowMerge, getAttributes } from './utils/object'; +import { getAttributes, merge } from './utils/object'; import { parseDuration } from './utils/time'; import { findChildren, getContent } from './utils/xml'; import resolveUrl from './utils/resolveUrl'; @@ -52,7 +52,7 @@ export const getSegmentInformation = (adaptationSet) => { const segmentTemplate = findChildren(adaptationSet, 'SegmentTemplate')[0]; const segmentList = findChildren(adaptationSet, 'SegmentList')[0]; const segmentUrls = segmentList && findChildren(segmentList, 'SegmentURL') - .map(s => shallowMerge({ tag: 'SegmentURL' }, getAttributes(s))); + .map(s => merge({ tag: 'SegmentURL' }, getAttributes(s))); const segmentBase = findChildren(adaptationSet, 'SegmentBase')[0]; const segmentTimelineParentNode = segmentList || segmentTemplate; const segmentTimeline = segmentTimelineParentNode && @@ -78,21 +78,29 @@ export const getSegmentInformation = (adaptationSet) => { template.initialization = { sourceURL: template.initialization }; } - return { + const segmentInfo = { template, timeline: segmentTimeline && findChildren(segmentTimeline, 'S').map(s => getAttributes(s)), - list: segmentList && shallowMerge( + list: segmentList && merge( getAttributes(segmentList), { segmentUrls, initialization: getAttributes(segmentInitialization) }), - base: segmentBase && shallowMerge( + base: segmentBase && merge( getAttributes(segmentBase), { initialization: getAttributes(segmentInitialization) }) }; + + Object.keys(segmentInfo).forEach(key => { + if (!segmentInfo[key]) { + delete segmentInfo[key]; + } + }); + + return segmentInfo; }; /** @@ -131,15 +139,16 @@ export const getSegmentInformation = (adaptationSet) => { * Callback map function */ export const inheritBaseUrls = -(adaptationSetAttributes, adaptationSetBaseUrls, segmentInfo) => (representation) => { +(adaptationSetAttributes, adaptationSetBaseUrls, adaptationSetSegmentInfo) => (representation) => { const repBaseUrlElements = findChildren(representation, 'BaseURL'); const repBaseUrls = buildBaseUrls(adaptationSetBaseUrls, repBaseUrlElements); - const attributes = shallowMerge(adaptationSetAttributes, getAttributes(representation)); + const attributes = merge(adaptationSetAttributes, getAttributes(representation)); + const representationSegmentInfo = getSegmentInformation(representation); return repBaseUrls.map(baseUrl => { return { - segmentInfo, - attributes: shallowMerge(attributes, { baseUrl }) + segmentInfo: merge(adaptationSetSegmentInfo, representationSegmentInfo), + attributes: merge(attributes, { baseUrl }) }; }); }; @@ -167,20 +176,21 @@ export const inheritBaseUrls = * Callback map function */ export const toRepresentations = -(periodAttributes, periodBaseUrls) => (adaptationSet) => { +(periodAttributes, periodBaseUrls, periodSegmentInfo) => (adaptationSet) => { const adaptationSetAttributes = getAttributes(adaptationSet); const adaptationSetBaseUrls = buildBaseUrls(periodBaseUrls, findChildren(adaptationSet, 'BaseURL')); const role = findChildren(adaptationSet, 'Role')[0]; const roleAttributes = { role: getAttributes(role) }; - const attrs = shallowMerge(periodAttributes, + const attrs = merge(periodAttributes, adaptationSetAttributes, roleAttributes); const segmentInfo = getSegmentInformation(adaptationSet); const representations = findChildren(adaptationSet, 'Representation'); + const adaptationSetSegmentInfo = merge(periodSegmentInfo, segmentInfo); return flatten( - representations.map(inheritBaseUrls(attrs, adaptationSetBaseUrls, segmentInfo))); + representations.map(inheritBaseUrls(attrs, adaptationSetBaseUrls, adaptationSetSegmentInfo))); }; /** @@ -210,10 +220,12 @@ export const toRepresentations = */ export const toAdaptationSets = (mpdAttributes, mpdBaseUrls) => (period, periodIndex) => { const periodBaseUrls = buildBaseUrls(mpdBaseUrls, findChildren(period, 'BaseURL')); - const periodAttributes = shallowMerge({ periodIndex }, mpdAttributes); + const periodAtt = getAttributes(period); + const periodAttributes = merge(mpdAttributes, periodAtt, { periodIndex }); const adaptationSets = findChildren(period, 'AdaptationSet'); + const periodSegmentInfo = getSegmentInformation(period); - return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls))); + return flatten(adaptationSets.map(toRepresentations(periodAttributes, periodBaseUrls, periodSegmentInfo))); }; /** @@ -243,4 +255,3 @@ export const inheritAttributes = (mpd, manifestUri = '') => { return flatten(periods.map(toAdaptationSets(mpdAttributes, mpdBaseUrls))); }; - diff --git a/src/toPlaylists.js b/src/toPlaylists.js index 6d5a95d8..a8812118 100644 --- a/src/toPlaylists.js +++ b/src/toPlaylists.js @@ -1,4 +1,4 @@ -import { shallowMerge } from './utils/object'; +import { merge } from './utils/object'; import { segmentsFromTemplate } from './segment/segmentTemplate'; import { segmentsFromList } from './segment/segmentList'; import { segmentsFromBase } from './segment/segmentBase'; @@ -6,23 +6,21 @@ import { segmentsFromBase } from './segment/segmentBase'; export const generateSegments = (segmentInfo, attributes) => { if (segmentInfo.template) { return segmentsFromTemplate( - shallowMerge(segmentInfo.template, attributes), + merge(attributes, segmentInfo.template), segmentInfo.timeline ); } - if (segmentInfo.base) { - return segmentsFromBase(shallowMerge(segmentInfo.base, attributes)); + return segmentsFromBase(merge(attributes, segmentInfo.base)); } - if (segmentInfo.list) { return segmentsFromList( - shallowMerge(segmentInfo.list, attributes), segmentInfo.timeline + merge(attributes, segmentInfo.list), segmentInfo.timeline ); } }; -export const toPlaylists = representations => { +export const toPlaylists = (representations) => { return representations.map(({ attributes, segmentInfo }) => { const segments = generateSegments(segmentInfo, attributes); diff --git a/src/utils/object.js b/src/utils/object.js index e2565f4d..35308975 100644 --- a/src/utils/object.js +++ b/src/utils/object.js @@ -1,13 +1,24 @@ import { from } from './list'; -export const shallowMerge = (...objects) => { - return objects.reduce((x, y) => { - return Object.keys(y) - .reduce((o, key) => { - o[key] = y[key]; - - return o; - }, x); +const isObject = (obj) => { + return !!obj && typeof obj === 'object'; +}; + +export const merge = (...objects) => { + + return objects.reduce((result, source) => { + + Object.keys(source).forEach(key => { + + if (Array.isArray(result[key]) && Array.isArray(source[key])) { + result[key] = result[key].concat(source[key]); + } else if (isObject(result[key]) && isObject(source[key])) { + result[key] = merge(result[key], source[key]); + } else { + result[key] = source[key]; + } + }); + return result; }, {}); }; diff --git a/test/inheritAttributes.test.js b/test/inheritAttributes.test.js index aa04067f..ab725c70 100644 --- a/test/inheritAttributes.test.js +++ b/test/inheritAttributes.test.js @@ -6,6 +6,7 @@ import { import { stringToMpdXml } from '../src/stringToMpdXml'; import errors from '../src/errors'; import QUnit from 'qunit'; +import { toPlaylists } from '../src/toPlaylists'; QUnit.module('buildBaseUrls'); @@ -72,12 +73,7 @@ QUnit.module('getSegmentInformation'); QUnit.test('undefined Segment information when no Segment nodes', function(assert) { const adaptationSet = { childNodes: [] }; - const expected = { - template: void 0, - timeline: void 0, - list: void 0, - base: void 0 - }; + const expected = {}; assert.deepEqual(getSegmentInformation(adaptationSet), expected, 'undefined segment info'); @@ -92,10 +88,7 @@ QUnit.test('gets SegmentTemplate attributes', function(assert) { }] }; const expected = { - template: { media: 'video.mp4' }, - timeline: void 0, - list: void 0, - base: void 0 + template: { media: 'video.mp4' } }; assert.deepEqual(getSegmentInformation(adaptationSet), expected, @@ -111,14 +104,11 @@ QUnit.test('gets SegmentList attributes', function(assert) { }] }; const expected = { - template: void 0, - timeline: void 0, list: { duration: '10', segmentUrls: [], initialization: {} - }, - base: void 0 + } }; assert.deepEqual(getSegmentInformation(adaptationSet), expected, @@ -134,9 +124,6 @@ QUnit.test('gets SegmentBase attributes', function(assert) { }] }; const expected = { - template: void 0, - timeline: void 0, - list: void 0, base: { duration: '10', initialization: {} } }; @@ -166,9 +153,7 @@ QUnit.test('gets SegmentTemplate and SegmentTimeline attributes', function(asser }; const expected = { template: { media: 'video.mp4' }, - timeline: [{ d: '10' }, { d: '5' }, { d: '7' }], - list: void 0, - base: void 0 + timeline: [{ d: '10' }, { d: '5' }, { d: '7' }] }; assert.deepEqual(getSegmentInformation(adaptationSet), expected, @@ -226,10 +211,7 @@ QUnit.test('end to end - basic', function(assert) { width: '720' }, segmentInfo: { - base: undefined, - list: undefined, - template: {}, - timeline: undefined + template: {} } }, { attributes: { @@ -243,12 +225,7 @@ QUnit.test('end to end - basic', function(assert) { role: {}, sourceDuration: 30 }, - segmentInfo: { - base: undefined, - list: undefined, - template: undefined, - timeline: undefined - } + segmentInfo: {} }]; assert.equal(actual.length, 2); @@ -302,10 +279,7 @@ QUnit.test('end to end - inherits BaseURL from all levels', function(assert) { width: '720' }, segmentInfo: { - base: undefined, - list: undefined, - template: {}, - timeline: undefined + template: {} } }, { attributes: { @@ -319,12 +293,7 @@ QUnit.test('end to end - inherits BaseURL from all levels', function(assert) { role: {}, sourceDuration: 30 }, - segmentInfo: { - base: undefined, - list: undefined, - template: undefined, - timeline: undefined - } + segmentInfo: { } }]; assert.equal(actual.length, 2); @@ -334,25 +303,25 @@ QUnit.test('end to end - inherits BaseURL from all levels', function(assert) { QUnit.test('end to end - alternate BaseURLs', function(assert) { const actual = inheritAttributes(stringToMpdXml( ` - <MPD mediaPresentationDuration="PT30S" > + <MPD mediaPresentationDuration= "PT30S" > <BaseURL>https://www.example.com/base/</BaseURL> <BaseURL>https://www.test.com/base/</BaseURL> <Period> - <AdaptationSet mimeType="video/mp4" > + <AdaptationSet mimeType= "video/mp4" > <BaseURL>segments/</BaseURL> <BaseURL>media/</BaseURL> - <Role value="main"></Role> + <Role value= "main" ></Role> <SegmentTemplate></SegmentTemplate> <Representation - bandwidth="5000000" - codecs="avc1.64001e" - height="404" - id="test" - width="720"> + bandwidth= "5000000" + codecs= "avc1.64001e" + height= "404" + id= "test" + width= "720" > </Representation> </AdaptationSet> - <AdaptationSet mimeType="text/vtt" lang="en"> - <Representation bandwidth="256" id="en"> + <AdaptationSet mimeType= "text/vtt" lang= "en" > + <Representation bandwidth= "256" id= "en" > <BaseURL>https://example.com/en.vtt</BaseURL> </Representation> </AdaptationSet> @@ -378,10 +347,7 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) { width: '720' }, segmentInfo: { - base: undefined, - list: undefined, - template: {}, - timeline: undefined + template: {} } }, { attributes: { @@ -400,10 +366,7 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) { width: '720' }, segmentInfo: { - base: undefined, - list: undefined, - template: {}, - timeline: undefined + template: {} } }, { attributes: { @@ -422,10 +385,7 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) { width: '720' }, segmentInfo: { - base: undefined, - list: undefined, - template: {}, - timeline: undefined + template: {} } }, { attributes: { @@ -444,10 +404,122 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) { width: '720' }, segmentInfo: { - base: undefined, - list: undefined, - template: {}, - timeline: undefined + template: {} + } + }, { + attributes: { + bandwidth: '256', + baseUrl: 'https://example.com/en.vtt', + id: 'en', + lang: 'en', + mediaPresentationDuration: 'PT30S', + mimeType: 'text/vtt', + periodIndex: 0, + role: {}, + sourceDuration: 30 + }, + segmentInfo: {} + }, { + attributes: { + bandwidth: '256', + baseUrl: 'https://example.com/en.vtt', + id: 'en', + lang: 'en', + mediaPresentationDuration: 'PT30S', + mimeType: 'text/vtt', + periodIndex: 0, + role: {}, + sourceDuration: 30 + }, + segmentInfo: {} + }]; + + assert.equal(actual.length, 6); + assert.deepEqual(actual, expected); +}); + +QUnit.test(' End to End test for checking support of segments in representation', function(assert) { + const actual = inheritAttributes(stringToMpdXml( + ` + <MPD mediaPresentationDuration= "PT30S" > + <BaseURL>https://www.example.com/base/</BaseURL> + <Period> + <AdaptationSet mimeType= "video/mp4" > + <Role value= "main" ></Role> + <SegmentBase indexRangeExact= "true" indexRange= "820-2087" > + <Initialization range= "0-987" /> + </SegmentBase> + + <Representation + mimeType= "video/mp6" + bandwidth= "5000000" + codecs= "avc1.64001e" + height= "404" + id= "test" + width= "720" > + <SegmentBase> + <Initialization range= "0-567" /> + </SegmentBase> + </Representation> + <Representation + height= "545" > + </Representation> + </AdaptationSet> + <AdaptationSet mimeType= "text/vtt" lang= "en" > + <Representation bandwidth= "256" id= "en" > + <BaseURL>https://example.com/en.vtt</BaseURL> + </Representation> + </AdaptationSet> + </Period> + </MPD> + ` + )); + + const expected = [{ + attributes: { + bandwidth: '5000000', + baseUrl: 'https://www.example.com/base/', + codecs: 'avc1.64001e', + height: '404', + id: 'test', + mediaPresentationDuration: 'PT30S', + mimeType: 'video/mp6', + periodIndex: 0, + role: { + value: 'main' + }, + sourceDuration: 30, + width: '720' + }, + segmentInfo: { + base: { + indexRange: '820-2087', + indexRangeExact: 'true', + initialization: { + range: '0-567' + } + } + } + }, { + attributes: { + baseUrl: 'https://www.example.com/base/', + mediaPresentationDuration: 'PT30S', + mimeType: 'video/mp4', + periodIndex: 0, + height: '545', + role: { + value: 'main' + }, + sourceDuration: 30 + }, + segmentInfo: { + base: { + indexRange: '820-2087', + indexRangeExact: 'true', + initialization: { + range: '0-987' + } + } } }, { attributes: { @@ -461,16 +533,99 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) { role: {}, sourceDuration: 30 }, + segmentInfo: {} + }]; + + assert.equal(actual.length, 3); + assert.deepEqual(actual, expected); +}); + +QUnit.test(' End to End test for checking support of segments in period ', function(assert) { + const actual = inheritAttributes(stringToMpdXml( + ` + <MPD mediaPresentationDuration= "PT30S" > + <BaseURL>https://www.example.com/base/</BaseURL> + <Period duration= "PT0H4M40.414S" > + <SegmentBase indexRangeExact= "false" indexRange= "9999" > + <Initialization range= "0-1111" /> + </SegmentBase> + <AdaptationSet mimeType= "video/mp4" > + <Role value= "main" ></Role> + <Representation + mimeType= "video/mp6" + bandwidth= "5000000" + codecs= "avc1.64001e" + height= "404" + id= "test" + width= "720" > + </Representation> + <Representation + height= "545" > + </Representation> + </AdaptationSet> + <AdaptationSet mimeType= "text/vtt" lang= "en" > + <Representation bandwidth= "256" id= "en" > + <BaseURL>https://example.com/en.vtt</BaseURL> + </Representation> + </AdaptationSet> + </Period> + </MPD> + ` + )); + + const expected = [{ + attributes: { + bandwidth: '5000000', + baseUrl: 'https://www.example.com/base/', + duration: 'PT0H4M40.414S', + codecs: 'avc1.64001e', + height: '404', + id: 'test', + mediaPresentationDuration: 'PT30S', + mimeType: 'video/mp6', + periodIndex: 0, + role: { + value: 'main' + }, + sourceDuration: 30, + width: '720' + }, + segmentInfo: { + base: { + indexRange: '9999', + indexRangeExact: 'false', + initialization: { + range: '0-1111' + } + } + } + }, { + attributes: { + baseUrl: 'https://www.example.com/base/', + mediaPresentationDuration: 'PT30S', + duration: 'PT0H4M40.414S', + mimeType: 'video/mp4', + periodIndex: 0, + height: '545', + role: { + value: 'main' + }, + sourceDuration: 30 + }, segmentInfo: { - base: undefined, - list: undefined, - template: undefined, - timeline: undefined + base: { + indexRange: '9999', + indexRangeExact: 'false', + initialization: { + range: '0-1111' + } + } } }, { attributes: { bandwidth: '256', baseUrl: 'https://example.com/en.vtt', + duration: 'PT0H4M40.414S', id: 'en', lang: 'en', mediaPresentationDuration: 'PT30S', @@ -480,13 +635,700 @@ QUnit.test('end to end - alternate BaseURLs', function(assert) { sourceDuration: 30 }, segmentInfo: { - base: undefined, - list: undefined, - template: undefined, - timeline: undefined + base: { + indexRange: '9999', + indexRangeExact: 'false', + initialization: { + range: '0-1111' + } + } } }]; - assert.equal(actual.length, 6); + assert.equal(actual.length, 3); + assert.deepEqual(actual, expected); +}); + +QUnit.test(' End to End test for checking support of Segments in Adaptation set', function(assert) { + const actual = inheritAttributes(stringToMpdXml( + ` + <MPD mediaPresentationDuration= "PT30S" > + <BaseURL>https://www.example.com/base/</BaseURL> + <Period duration= "PT0H4M40.414S" > + <AdaptationSet mimeType= "video/mp4" > + <Role value= "main" ></Role> + <SegmentBase indexRange= "1212" indexRangeExact= "true" > + <Initialization range= "0-8888" /> + </SegmentBase> + <Representation + mimeType= "video/mp6" + bandwidth= "5000000" + codecs= "avc1.64001e" + height= "404" + id= "test" + width= "720" > + </Representation> + <Representation + height= "545" > + </Representation> + </AdaptationSet> + <AdaptationSet mimeType= "text/vtt" lang= "en" > + <Representation bandwidth= "256" id= "en" > + <BaseURL>https://example.com/en.vtt</BaseURL> + </Representation> + </AdaptationSet> + </Period> + </MPD> + ` + )); + + const expected = [{ + attributes: { + bandwidth: '5000000', + baseUrl: 'https://www.example.com/base/', + duration: 'PT0H4M40.414S', + codecs: 'avc1.64001e', + height: '404', + id: 'test', + mediaPresentationDuration: 'PT30S', + mimeType: 'video/mp6', + periodIndex: 0, + role: { + value: 'main' + }, + sourceDuration: 30, + width: '720' + }, + segmentInfo: { + base: { + indexRange: '1212', + indexRangeExact: 'true', + initialization: { + range: '0-8888' + + } + } + } + }, { + attributes: { + baseUrl: 'https://www.example.com/base/', + mediaPresentationDuration: 'PT30S', + duration: 'PT0H4M40.414S', + mimeType: 'video/mp4', + periodIndex: 0, + height: '545', + role: { + value: 'main' + }, + sourceDuration: 30 + }, + segmentInfo: { + base: { + indexRange: '1212', + indexRangeExact: 'true', + initialization: { + range: '0-8888' + } + } + } + }, { + attributes: { + bandwidth: '256', + baseUrl: 'https://example.com/en.vtt', + duration: 'PT0H4M40.414S', + id: 'en', + lang: 'en', + mediaPresentationDuration: 'PT30S', + mimeType: 'text/vtt', + periodIndex: 0, + role: {}, + sourceDuration: 30 + }, + segmentInfo: {} + }]; + + assert.equal(actual.length, 3); + assert.deepEqual(actual, expected); +}); + +// Although according to the Spec, at most one set of Segment information should be present at each level, +// This test would still handle the case and prevent errors if multiple set of segment information are present at any particular level. + +QUnit.test(' Test for checking use of only one set of Segment Information when multiple are present', function(assert) { + const actual = toPlaylists(inheritAttributes(stringToMpdXml( + ` + <MPD mediaPresentationDuration= "PT30S" > + <BaseURL>https://www.example.com/base</BaseURL> + <Period duration= "PT0H4M40.414S" > + <AdaptationSet mimeType= "video/mp4" segmentAlignment= "true" startWithSAP= "1" lang= "es" > + <Role value= "main" ></Role> + <SegmentTemplate duration= "95232" initialization= "$RepresentationID$/es/init.m4f" media= "$RepresentationID$/es/$Number$.m4f" startNumber= "0" timescale= "48000" > + </SegmentTemplate> + <SegmentList timescale= "1000" duration= "1000" > + <RepresentationIndex sourceURL= "representation-index-low" /> + <SegmentURL media= "low/segment-1.ts" /> + <SegmentURL media= "low/segment-2.ts" /> + <SegmentURL media= "low/segment-3.ts" /> + <SegmentURL media= "low/segment-4.ts" /> + <SegmentURL media= "low/segment-5.ts" /> + <SegmentURL media= "low/segment-6.ts" /> + </SegmentList> + <Representation + mimeType= "video/mp6" + bandwidth= "5000000" + codecs= "avc1.64001e" + height= "404" + id= "125000" + width= "720" > + </Representation> + <Representation + height= "545" + id="125000" > + </Representation> + </AdaptationSet> + </Period> + </MPD> + ` +))); + + const expected = [{ + attributes: { + bandwidth: '5000000', + baseUrl: 'https://www.example.com/base', + duration: 'PT0H4M40.414S', + codecs: 'avc1.64001e', + height: '404', + id: '125000', + lang: 'es', + mediaPresentationDuration: 'PT30S', + mimeType: 'video/mp6', + periodIndex: 0, + role: { + value: 'main' + }, + segmentAlignment: 'true', + sourceDuration: 30, + width: '720', + startWithSAP: '1' + }, + segments: [{ + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/0.m4f', + timeline: 0, + uri: '125000/es/0.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/1.m4f', + timeline: 0, + uri: '125000/es/1.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/2.m4f', + timeline: 0, + uri: '125000/es/2.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/3.m4f', + timeline: 0, + uri: '125000/es/3.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/4.m4f', + timeline: 0, + uri: '125000/es/4.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/5.m4f', + timeline: 0, + uri: '125000/es/5.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/6.m4f', + timeline: 0, + uri: '125000/es/6.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/7.m4f', + timeline: 0, + uri: '125000/es/7.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/8.m4f', + timeline: 0, + uri: '125000/es/8.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/9.m4f', + timeline: 0, + uri: '125000/es/9.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/10.m4f', + timeline: 0, + uri: '125000/es/10.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/11.m4f', + timeline: 0, + uri: '125000/es/11.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/12.m4f', + timeline: 0, + uri: '125000/es/12.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/13.m4f', + timeline: 0, + uri: '125000/es/13.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/14.m4f', + timeline: 0, + uri: '125000/es/14.m4f' + }, { + duration: 0.240000000000002, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/15.m4f', + timeline: 0, + uri: '125000/es/15.m4f' + }] + }, { + attributes: { + baseUrl: 'https://www.example.com/base', + duration: 'PT0H4M40.414S', + lang: 'es', + height: '545', + id: '125000', + mediaPresentationDuration: 'PT30S', + mimeType: 'video/mp4', + periodIndex: 0, + role: { + value: 'main' + }, + segmentAlignment: 'true', + sourceDuration: 30, + startWithSAP: '1' + + }, + segments: [{ + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/0.m4f', + timeline: 0, + uri: '125000/es/0.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/1.m4f', + timeline: 0, + uri: '125000/es/1.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/2.m4f', + timeline: 0, + uri: '125000/es/2.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/3.m4f', + timeline: 0, + uri: '125000/es/3.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/4.m4f', + timeline: 0, + uri: '125000/es/4.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/5.m4f', + timeline: 0, + uri: '125000/es/5.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/6.m4f', + timeline: 0, + uri: '125000/es/6.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/7.m4f', + timeline: 0, + uri: '125000/es/7.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/8.m4f', + timeline: 0, + uri: '125000/es/8.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/9.m4f', + timeline: 0, + uri: '125000/es/9.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/10.m4f', + timeline: 0, + uri: '125000/es/10.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/11.m4f', + timeline: 0, + uri: '125000/es/11.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/12.m4f', + timeline: 0, + uri: '125000/es/12.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/13.m4f', + timeline: 0, + uri: '125000/es/13.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/14.m4f', + timeline: 0, + uri: '125000/es/14.m4f' + }, { + duration: 0.240000000000002, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/15.m4f', + timeline: 0, + uri: '125000/es/15.m4f' + }] + }]; + + assert.equal(actual.length, 2); + assert.deepEqual(actual, expected); +}); + +// Although the Spec states that if SegmentTemplate or SegmentList is present on one level of the hierarchy the other one shall not be present on any lower level, +// This test would still handle the case if both are present in the hierarchy and would prevent throwing errors. + +QUnit.test('Test to check use of either Segment Template or Segment List when both are present in the hierarchy', function(assert) { + const actual = toPlaylists(inheritAttributes(stringToMpdXml( + ` + <MPD mediaPresentationDuration= "PT30S" > + <BaseURL>https://www.example.com/base</BaseURL> + <Period duration= "PT0H4M40.414S" > + <AdaptationSet mimeType= "video/mp4" segmentAlignment= "true" startWithSAP= "1" lang= "es" > + <Role value= "main" ></Role> + <SegmentTemplate duration= "95232" initialization= "$RepresentationID$/es/init.m4f" media= "$RepresentationID$/es/$Number$.m4f" startNumber= "0" timescale= "48000" > + </SegmentTemplate> + <Representation + mimeType= "video/mp6" + bandwidth= "5000000" + codecs= "avc1.64001e" + height= "404" + id= "125000" + width= "720" > + <SegmentList timescale= "1000" duration= "1000" > + <RepresentationIndex sourceURL= "representation-index-low" /> + <SegmentURL media= "low/segment-1.ts" /> + <SegmentURL media= "low/segment-2.ts" /> + <SegmentURL media= "low/segment-3.ts" /> + <SegmentURL media= "low/segment-4.ts" /> + <SegmentURL media= "low/segment-5.ts" /> + <SegmentURL media= "low/segment-6.ts" /> + </SegmentList> + </Representation> + </AdaptationSet> + </Period> + </MPD> + ` +))); + + const expected = [{ + attributes: { + bandwidth: '5000000', + baseUrl: 'https://www.example.com/base', + duration: 'PT0H4M40.414S', + codecs: 'avc1.64001e', + height: '404', + id: '125000', + lang: 'es', + mediaPresentationDuration: 'PT30S', + mimeType: 'video/mp6', + periodIndex: 0, + role: { + value: 'main' + }, + segmentAlignment: 'true', + sourceDuration: 30, + width: '720', + startWithSAP: '1' + }, + segments: [{ + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/0.m4f', + timeline: 0, + uri: '125000/es/0.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/1.m4f', + timeline: 0, + uri: '125000/es/1.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/2.m4f', + timeline: 0, + uri: '125000/es/2.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/3.m4f', + timeline: 0, + uri: '125000/es/3.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/4.m4f', + timeline: 0, + uri: '125000/es/4.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/5.m4f', + timeline: 0, + uri: '125000/es/5.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/6.m4f', + timeline: 0, + uri: '125000/es/6.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/7.m4f', + timeline: 0, + uri: '125000/es/7.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/8.m4f', + timeline: 0, + uri: '125000/es/8.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/9.m4f', + timeline: 0, + uri: '125000/es/9.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/10.m4f', + timeline: 0, + uri: '125000/es/10.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/11.m4f', + timeline: 0, + uri: '125000/es/11.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/12.m4f', + timeline: 0, + uri: '125000/es/12.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/13.m4f', + timeline: 0, + uri: '125000/es/13.m4f' + }, { + duration: 1.984, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/14.m4f', + timeline: 0, + uri: '125000/es/14.m4f' + }, { + duration: 0.240000000000002, + map: { + resolvedUri: 'https://www.example.com/125000/es/init.m4f', + uri: '125000/es/init.m4f' + }, + resolvedUri: 'https://www.example.com/125000/es/15.m4f', + timeline: 0, + uri: '125000/es/15.m4f' + }] + } +]; + + assert.equal(actual.length, 1); assert.deepEqual(actual, expected); }); diff --git a/test/manifests/maat_vtt_segmentTemplate.mpd b/test/manifests/maat_vtt_segmentTemplate.mpd index af40bbeb..bae42077 100644 --- a/test/manifests/maat_vtt_segmentTemplate.mpd +++ b/test/manifests/maat_vtt_segmentTemplate.mpd @@ -6,21 +6,33 @@ <AdaptationSet mimeType="audio/mp4" segmentAlignment="true" startWithSAP="1" lang="en"> <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role> <SegmentTemplate duration="95232" initialization="$RepresentationID$/init.m4f" media="$RepresentationID$/$Number$.m4f" startNumber="0" timescale="48000"></SegmentTemplate> - <Representation audioSamplingRate="48000" bandwidth="63000" codecs="mp4a.40.2" id="63000"></Representation> - <Representation audioSamplingRate="48000" bandwidth="125000" codecs="mp4a.40.2" id="125000"></Representation> + <Representation audioSamplingRate="48000" bandwidth="63000" codecs="mp4a.40.2" id="63000"> + <SegmentTemplate></SegmentTemplate> + </Representation> + <Representation audioSamplingRate="48000" bandwidth="125000" codecs="mp4a.40.2" id="125000"> + <SegmentTemplate></SegmentTemplate> + </Representation> </AdaptationSet> <AdaptationSet mimeType="audio/mp4" segmentAlignment="true" startWithSAP="1" lang="es"> <Role schemeIdUri="urn:mpeg:dash:role:2011"></Role> <SegmentTemplate duration="95232" initialization="$RepresentationID$/es/init.m4f" media="$RepresentationID$/es/$Number$.m4f" startNumber="0" timescale="48000"></SegmentTemplate> - <Representation audioSamplingRate="48000" bandwidth="63000" codecs="mp4a.40.2" id="63000"></Representation> - <Representation audioSamplingRate="48000" bandwidth="125000" codecs="mp4a.40.2" id="125000"></Representation> + <Representation audioSamplingRate="48000" bandwidth="63000" codecs="mp4a.40.2" id="63000"> + <SegmentTemplate></SegmentTemplate> + </Representation> + <Representation audioSamplingRate="48000" bandwidth="125000" codecs="mp4a.40.2" id="125000"> + <SegmentTemplate></SegmentTemplate> + </Representation> </AdaptationSet> <AdaptationSet mimeType="video/mp4" scanType="progressive" segmentAlignment="true" startWithSAP="1"> <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role> <SegmentTemplate duration="46046" initialization="$RepresentationID$/init.m4f" media="$RepresentationID$/$Number$.m4f" startNumber="0" timescale="24000"></SegmentTemplate> - <Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482"></Representation> - <Representation bandwidth="3971000" codecs="avc1.64001e" frameRate="2997/125" height="404" id="720" width="720"></Representation> + <Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482"> + <SegmentTemplate></SegmentTemplate> + </Representation> + <Representation bandwidth="3971000" codecs="avc1.64001e" frameRate="2997/125" height="404" id="720" width="720"> + <SegmentTemplate></SegmentTemplate> + </Representation> </AdaptationSet> <AdaptationSet mimeType="text/vtt" lang="en"> diff --git a/test/manifests/segmentBase.mpd b/test/manifests/segmentBase.mpd index 609202a5..df4b2927 100644 --- a/test/manifests/segmentBase.mpd +++ b/test/manifests/segmentBase.mpd @@ -4,7 +4,9 @@ <Period> <AdaptationSet mimeType="video/mp4" scanType="progressive" segmentAlignment="true" startWithSAP="1"> <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role> - <Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482" /> + <Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482" > + <SegmentBase></SegmentBase> + </Representation> <BaseURL>1080p.ts</BaseURL> <SegmentBase> <RepresentationIndex sourceURL="1080p.sidx"/> diff --git a/test/manifests/segmentList.mpd b/test/manifests/segmentList.mpd index 0977c5a4..453fe7d9 100644 --- a/test/manifests/segmentList.mpd +++ b/test/manifests/segmentList.mpd @@ -4,7 +4,9 @@ <Period> <AdaptationSet mimeType="video/mp4" scanType="progressive" segmentAlignment="true" startWithSAP="1"> <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role> - <Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482" /> + <Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482" > + <SegmentList></SegmentList> + </Representation> <SegmentList timescale="1000" duration="1000"> <RepresentationIndex sourceURL="representation-index-low"/> <SegmentURL media="low/segment-1.ts"/> @@ -22,7 +24,11 @@ <AdaptationSet mimeType="video/mp4" scanType="progressive" segmentAlignment="true" startWithSAP="1"> <Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role> - <Representation bandwidth="3971000" codecs="avc1.420015" frameRate="2997/125" height="404" id="720" width="720" /> + <Representation bandwidth="3971000" codecs="avc1.420015" frameRate="2997/125" height="404" id="720" width="720" > + <SegmentList> + <SegmentTimeline></SegmentTimeline> + </SegmentList> + </Representation> <SegmentList timescale="90000"> <RepresentationIndex sourceURL="representation-index-high"/> <SegmentTimeline> diff --git a/test/utils.test.js b/test/utils.test.js index b67c33cb..8f1044e3 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -1,4 +1,4 @@ -import { shallowMerge, getAttributes } from '../src/utils/object'; +import { getAttributes, merge } from '../src/utils/object'; import { parseDuration } from '../src/utils/time'; import { flatten, range, from } from '../src/utils/list'; import { findChildren, getContent } from '../src/utils/xml'; @@ -8,25 +8,74 @@ import QUnit from 'qunit'; QUnit.module('utils'); -QUnit.module('shallowMerge'); +QUnit.module('merge'); QUnit.test('empty', function(assert) { - assert.deepEqual(shallowMerge({}, { a: 1 }), { a: 1 }); - assert.deepEqual(shallowMerge({ a: 1 }, { a: 1 }), { a: 1 }); - assert.deepEqual(shallowMerge({ a: 1 }, {}), { a: 1 }); + assert.deepEqual(merge({}, { a: 1 }), { a: 1 }); + assert.deepEqual(merge({ a: 1 }, { a: 1 }), { a: 1 }); + assert.deepEqual(merge({ a: 1 }, {}), { a: 1 }); }); QUnit.test('append', function(assert) { - assert.deepEqual(shallowMerge({ a: 1 }, { b: 3 }), { a: 1, b: 3 }); + assert.deepEqual(merge({ a: 1 }, { b: 3 }), { a: 1, b: 3 }); }); QUnit.test('overwrite', function(assert) { - assert.deepEqual(shallowMerge({ a: 1 }, { a: 2 }), { a: 2 }); + assert.deepEqual(merge({ a: 1 }, { a: 2 }), { a: 2 }); }); QUnit.test('empty', function(assert) { - assert.deepEqual(shallowMerge({}, {}), {}); - assert.deepEqual(shallowMerge({}, 1), {}); - assert.deepEqual(shallowMerge(1, {}), {}); + assert.deepEqual(merge({}, {}), {}); + assert.deepEqual(merge({}, 1), {}); + assert.deepEqual(merge(1, {}), {}); +}); + +QUnit.test('Test for checking the merge when multiple segment Information are present', function(assert) { + + const adaptationSetInfo = { + + base: { duration: '10'} + }; + + const representationInfo = { + + base: { duration: '25', indexRange: '230-252'} + }; + + const expected = { + + base: { duration: '25', indexRange: '230-252'} + }; + + assert.deepEqual(merge(adaptationSetInfo, representationInfo), expected, + 'Merged SegmentBase info'); + +}); + +QUnit.test('Test for checking the merge when segment Information is present at a level and is undefined at another', function(assert) { + const periodInfo = { + base: { + initialization: { + range: '0-8888' + + } + } + }; + + const adaptationSetInfo = { + + base: { duration: '10', indexRange: '230-252'} + }; + + const representationInfo = {}; + + const expected = { + + base: { duration: '10', indexRange: '230-252', initialization: {range: '0-8888'}} + }; + + assert.deepEqual(merge(periodInfo, adaptationSetInfo, representationInfo), expected, + 'Merged SegmentBase info'); + }); QUnit.module('flatten');