diff --git a/externals/tXml.js b/externals/tXml.js new file mode 100644 index 0000000000..f61563de2f --- /dev/null +++ b/externals/tXml.js @@ -0,0 +1,540 @@ +// ==ClosureCompiler== +// @output_file_name default.js +// @compilation_level SIMPLE_OPTIMIZATIONS +// ==/ClosureCompiler== +// module.exports = { +// parse: parse, +// simplify: simplify, +// simplifyLostLess: simplifyLostLess, +// filter: filter, +// stringify: stringify, +// toContentString: toContentString, +// getElementById: getElementById, +// getElementsByClassName: getElementsByClassName, +// transformStream: transformStream, +// }; + +/** + * @author: Tobias Nickel + * @created: 06.04.2015 + * I needed a small xmlparser chat can be used in a worker. + */ + +/** + * @typedef tNode + * @property {string} tagName + * @property {object} attributes + * @property {(tNode|string)[]} children + **/ + +/** + * @typedef TParseOptions + * @property {number} [pos] + * @property {string[]} [noChildNodes] + * @property {boolean} [setPos] + * @property {boolean} [keepComments] + * @property {boolean} [keepWhitespace] + * @property {boolean} [simplify] + * @property {(a: tNode, b: tNode) => boolean} [filter] + */ + +/** + * parseXML / html into a DOM Object. with no validation and some failur tolerance + * @param {string} S your XML to parse + * @param {TParseOptions} [options] all other options: + * @return {(tNode | string)[]} + */ + export function parse(S, options) { + "txml"; + options = options || {}; + + var pos = options.pos || 0; + var keepComments = !!options.keepComments; + var keepWhitespace = !!options.keepWhitespace + // dash.js - BEGIN + // Attributes matchers to post-process attributes (for ex to transform from xs:duration format to number of seconds) + var attrMatchers = options.attrMatchers || []; + // List od node names that must be stored as array within their parent + var nodesAsArray = options.nodesAsArray || []; + // dash.js - END + + var openBracket = "<"; + var openBracketCC = "<".charCodeAt(0); + var closeBracket = ">"; + var closeBracketCC = ">".charCodeAt(0); + var minusCC = "-".charCodeAt(0); + var slashCC = "/".charCodeAt(0); + var exclamationCC = '!'.charCodeAt(0); + var singleQuoteCC = "'".charCodeAt(0); + var doubleQuoteCC = '"'.charCodeAt(0); + var openCornerBracketCC = '['.charCodeAt(0); + var closeCornerBracketCC = ']'.charCodeAt(0); + + + /** + * parsing a list of entries + */ + function parseChildren(tagName, parent) { + var children = []; + while (S[pos]) { + if (S.charCodeAt(pos) == openBracketCC) { + if (S.charCodeAt(pos + 1) === slashCC) { + var closeStart = pos + 2; + pos = S.indexOf(closeBracket, pos); + + var closeTag = S.substring(closeStart, pos) + if (closeTag.indexOf(tagName) == -1) { + var parsedText = S.substring(0, pos).split('\n'); + throw new Error( + 'Unexpected close tag\nLine: ' + (parsedText.length - 1) + + '\nColumn: ' + (parsedText[parsedText.length - 1].length + 1) + + '\nChar: ' + S[pos] + ); + } + + if (pos + 1) pos += 1 + + return children; + } else if (S.charCodeAt(pos + 1) === exclamationCC) { + if (S.charCodeAt(pos + 2) == minusCC) { + //comment support + const startCommentPos = pos; + while (pos !== -1 && !(S.charCodeAt(pos) === closeBracketCC && S.charCodeAt(pos - 1) == minusCC && S.charCodeAt(pos - 2) == minusCC && pos != -1)) { + pos = S.indexOf(closeBracket, pos + 1); + } + if (pos === -1) { + pos = S.length + } + if (keepComments) { + children.push(S.substring(startCommentPos, pos + 1)); + } + } else if ( + S.charCodeAt(pos + 2) === openCornerBracketCC && + S.charCodeAt(pos + 8) === openCornerBracketCC && + S.substr(pos + 3, 5).toLowerCase() === 'cdata' + ) { + // cdata + var cdataEndIndex = S.indexOf(']]>', pos); + if (cdataEndIndex == -1) { + children.push(S.substr(pos + 9)); + pos = S.length; + } else { + children.push(S.substring(pos + 9, cdataEndIndex)); + pos = cdataEndIndex + 3; + } + continue; + } else { + // doctypesupport + const startDoctype = pos + 1; + pos += 2; + var encapsuled = false; + while ((S.charCodeAt(pos) !== closeBracketCC || encapsuled === true) && S[pos]) { + if (S.charCodeAt(pos) === openCornerBracketCC) { + encapsuled = true; + } else if (encapsuled === true && S.charCodeAt(pos) === closeCornerBracketCC) { + encapsuled = false; + } + pos++; + } + children.push(S.substring(startDoctype, pos)); + } + pos++; + continue; + } + var node = parseNode(); + children.push(node); + + if (node.tagName[0] === '?') { + children.push(...node.children); + node.children = []; + } + + // dash.js - BEGIN + // Transform/process on the fly child nodes to add them to their parent as an array or an object + if (parent) { + let tagName = node.tagName; + delete node.tagName; + if (nodesAsArray.indexOf(tagName) !== -1) { + if (!parent[tagName]) { + parent[tagName] = []; + } + parent[tagName].push(node); + } else { + parent[tagName] = node; + } + } + // dash.js - END + } else { + var text = parseText(); + if (!keepWhitespace) { + text = text.trim(); + } + // dash.js - BEGIN + // Transform/process on the fly text values to add them to their parent as its "_text" property + if (parent) { + parent.__text = text; + } else { + children.push(text); + } + // dash.js - END + pos++; + } + } + return children; + } + + // dash.js - BEGIN + // Add function processAttr() used to process node attributes on the fly when parsing nodes (see parseNode())) + function processAttr(tagName, attrName, value) { + // Specific use case for SegmentTimeline tag + if (tagName === 'S') { + return parseInt(value); + } + + let attrValue = value; + attrMatchers.forEach(matcher => { + if (matcher.test(tagName, attrName, value)) { + attrValue = matcher.converter(value); + } + }); + return attrValue; + } + // dash.js - END + + /** + * returns the text outside of texts until the first '<' + */ + function parseText() { + var start = pos; + pos = S.indexOf(openBracket, pos) - 1; + if (pos === -2) + pos = S.length; + return S.slice(start, pos + 1); + } + /** + * returns text until the first nonAlphabetic letter + */ + var nameSpacer = '\r\n\t>/= '; + + function parseName() { + var start = pos; + while (nameSpacer.indexOf(S[pos]) === -1 && S[pos]) { + pos++; + } + return S.slice(start, pos); + } + /** + * is parsing a node, including tagName, Attributes and its children, + * to parse children it uses the parseChildren again, that makes the parsing recursive + */ + var NoChildNodes = options.noChildNodes || ['img', 'br', 'input', 'meta', 'link', 'hr']; + + function parseNode() { + pos++; + const tagName = parseName(); + // dash.js - BEGIN + // Set attributes as node properties which names are the attributes names + // For child nodes, see parseChildren() function where children are added as object properties + // const attributes = {}; + let children = []; + let node = { + tagName: tagName + }; + + // Support tag namespace + let p = node.tagName.indexOf(':'); + if (p !== -1) { + node.__prefix = node.tagName.substr(0, p); + node.tagName = node.tagName.substr(p + 1); + } + // dash.js - END + + // parsing attributes + while (S.charCodeAt(pos) !== closeBracketCC && S[pos]) { + var c = S.charCodeAt(pos); + if ((c > 64 && c < 91) || (c > 96 && c < 123)) { + //if('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.indexOf(S[pos])!==-1 ){ + var name = parseName(); + // search beginning of the string + var code = S.charCodeAt(pos); + while (code && code !== singleQuoteCC && code !== doubleQuoteCC && !((code > 64 && code < 91) || (code > 96 && code < 123)) && code !== closeBracketCC) { + pos++; + code = S.charCodeAt(pos); + } + if (code === singleQuoteCC || code === doubleQuoteCC) { + var value = parseString(); + if (pos === -1) { + return node; + } + } else { + value = null; + pos--; + } + // dash.js - BEGIN + // Process attributes and add them as node properties which names are the attributes names + value = processAttr(node.tagName, name, value); + node[name] = value; + // dash.js - END + } + pos++; + } + // optional parsing of children + if (S.charCodeAt(pos - 1) !== slashCC) { + if (tagName == "script") { + var start = pos + 1; + pos = S.indexOf('', pos); + children = [S.slice(start, pos)]; + pos += 9; + } else if (tagName == "style") { + var start = pos + 1; + pos = S.indexOf('', pos); + children = [S.slice(start, pos)]; + pos += 8; + } else if (NoChildNodes.indexOf(tagName) === -1) { + pos++; + // dash.js - BEGIN + // Add parent to parseChildren() + children = parseChildren(tagName, node); + // dash.js - END + } else { + pos++ + } + } else { + pos++; + } + // dash.js - BEGIN + return node; + // dash.js - END + } + + /** + * is parsing a string, that starts with a char and with the same usually ' or " + */ + + function parseString() { + var startChar = S[pos]; + var startpos = pos + 1; + pos = S.indexOf(startChar, startpos) + return S.slice(startpos, pos); + } + + /** + * + */ + function findElements() { + var r = new RegExp('\\s' + options.attrName + '\\s*=[\'"]' + options.attrValue + '[\'"]').exec(S) + if (r) { + return r.index; + } else { + return -1; + } + } + + var out = null; + if (options.attrValue !== undefined) { + options.attrName = options.attrName || 'id'; + var out = []; + + while ((pos = findElements()) !== -1) { + pos = S.lastIndexOf('<', pos); + if (pos !== -1) { + out.push(parseNode()); + } + S = S.substr(pos); + pos = 0; + } + } else if (options.parseNode) { + out = parseNode() + } else { + out = parseChildren(''); + } + + if (options.filter) { + out = filter(out, options.filter); + } + + if (options.simplify) { + return simplify(Array.isArray(out) ? out : [out]); + } + + if (options.setPos) { + out.pos = pos; + } + + return out; +} + +/** + * transform the DomObject to an object that is like the object of PHP`s simple_xmp_load_*() methods. + * this format helps you to write that is more likely to keep your program working, even if there a small changes in the XML schema. + * be aware, that it is not possible to reproduce the original xml from a simplified version, because the order of elements is not saved. + * therefore your program will be more flexible and easier to read. + * + * @param {tNode[]} children the childrenList + */ +export function simplify(children) { + var out = {}; + if (!children.length) { + return ''; + } + + if (children.length === 1 && typeof children[0] == 'string') { + return children[0]; + } + // map each object + children.forEach(function(child) { + if (typeof child !== 'object') { + return; + } + if (!out[child.tagName]) + out[child.tagName] = []; + var kids = simplify(child.children); + out[child.tagName].push(kids); + if (Object.keys(child.attributes).length && typeof kids !== 'string') { + kids._attributes = child.attributes; + } + }); + + for (var i in out) { + if (out[i].length == 1) { + out[i] = out[i][0]; + } + } + + return out; +}; + + +/** + * similar to simplify, but lost less + * + * @param {tNode[]} children the childrenList + */ +export function simplifyLostLess(children, parentAttributes = {}) { + var out = {}; + if (!children.length) { + return out; + } + + if (children.length === 1 && typeof children[0] == 'string') { + return Object.keys(parentAttributes).length ? { + _attributes: parentAttributes, + value: children[0] + } : children[0]; + } + // map each object + children.forEach(function(child) { + if (typeof child !== 'object') { + return; + } + if (!out[child.tagName]) + out[child.tagName] = []; + var kids = simplifyLostLess(child.children || [], child.attributes); + out[child.tagName].push(kids); + if (Object.keys(child.attributes).length) { + kids._attributes = child.attributes; + } + }); + + return out; +}; + +/** + * behaves the same way as Array.filter, if the filter method return true, the element is in the resultList + * @params children{Array} the children of a node + * @param f{function} the filter method + */ +export function filter(children, f, dept = 0, path = '') { + var out = []; + children.forEach(function(child, i) { + if (typeof(child) === 'object' && f(child, i, dept, path)) out.push(child); + if (child.children) { + var kids = filter(child.children, f, dept + 1, (path ? path + '.' : '') + i + '.' + child.tagName); + out = out.concat(kids); + } + }); + return out; +}; + +/** + * stringify a previously parsed string object. + * this is useful, + * 1. to remove whitespace + * 2. to recreate xml data, with some changed data. + * @param {tNode} O the object to Stringify + */ +export function stringify(O) { + var out = ''; + + function writeChildren(O) { + if (O) { + for (var i = 0; i < O.length; i++) { + if (typeof O[i] == 'string') { + out += O[i].trim(); + } else { + writeNode(O[i]); + } + } + } + } + + function writeNode(N) { + out += "<" + N.tagName; + for (var i in N.attributes) { + if (N.attributes[i] === null) { + out += ' ' + i; + } else if (N.attributes[i].indexOf('"') === -1) { + out += ' ' + i + '="' + N.attributes[i].trim() + '"'; + } else { + out += ' ' + i + "='" + N.attributes[i].trim() + "'"; + } + } + if (N.tagName[0] === '?') { + out += '?>'; + return; + } + out += '>'; + writeChildren(N.children); + out += ''; + } + writeChildren(O); + + return out; +}; + + +/** + * use this method to read the text content, of some node. + * It is great if you have mixed content like: + * this text has some big text and a link + * @return {string} + */ +export function toContentString(tDom) { + if (Array.isArray(tDom)) { + var out = ''; + tDom.forEach(function(e) { + out += ' ' + toContentString(e); + out = out.trim(); + }); + return out; + } else if (typeof tDom === 'object') { + return toContentString(tDom.children) + } else { + return ' ' + tDom; + } +}; + +export function getElementById(S, id, simplified) { + var out = parse(S, { + attrValue: id + }); + return simplified ? tXml.simplify(out) : out[0]; +}; + +export function getElementsByClassName(S, classname, simplified) { + const out = parse(S, { + attrName: 'class', + attrValue: '[a-zA-Z0-9- ]*' + classname + '[a-zA-Z0-9- ]*' + }); + return simplified ? tXml.simplify(out) : out; +}; diff --git a/package-lock.json b/package-lock.json index bf4952a478..053ec26fb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "dashjs", - "version": "4.5.0", + "version": "4.5.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4409,7 +4409,7 @@ }, "babel": { "version": "5.8.38", - "resolved": "https://registry.npmjs.org/babel/-/babel-5.8.38.tgz", + "resolved": "http://registry.npmjs.org/babel/-/babel-5.8.38.tgz", "integrity": "sha1-37CHwiiUkXxXb7Z86c8yjUWGKfs=", "dev": true, "requires": { @@ -4510,13 +4510,13 @@ "dependencies": { "bluebird": { "version": "2.11.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", + "resolved": "http://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", "integrity": "sha1-U0uQM8AiyVecVro7Plpcqvu2UOE=", "dev": true }, "lodash": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true } @@ -4926,7 +4926,7 @@ "dependencies": { "lodash": { "version": "3.10.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "resolved": "http://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true } @@ -5493,6 +5493,30 @@ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "dev": true }, + "bcp-47": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/bcp-47/-/bcp-47-1.0.8.tgz", + "integrity": "sha512-Y9y1QNBBtYtv7hcmoX0tR+tUNSFZGZ6OL6vKPObq8BbOhkCoyayF6ogfLTgAli/KuAEbsYHYUNq2AQuY6IuLag==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "bcp-47-match": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bcp-47-match/-/bcp-47-match-1.0.3.tgz", + "integrity": "sha512-LggQ4YTdjWQSKELZF5JwchnBa1u0pIQSZf5lSdOHEdbVP55h0qICA/FUp3+W99q0xqxYa1ZQizTUH87gecII5w==" + }, + "bcp-47-normalize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/bcp-47-normalize/-/bcp-47-normalize-1.1.1.tgz", + "integrity": "sha512-jWZ1Jdu3cs0EZdfCkS0UE9Gg01PtxnChjEBySeB+Zo6nkqtFfnvtoQQgP1qU1Oo4qgJgxhTI6Sf9y/pZIhPs0A==", + "requires": { + "bcp-47": "^1.0.0", + "bcp-47-match": "^1.0.0" + } + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -9857,6 +9881,20 @@ "kind-of": "^3.0.2" } }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, "is-binary-path": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", @@ -9899,6 +9937,11 @@ "kind-of": "^3.0.2" } }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, "is-descriptor": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", diff --git a/samples/advanced/load_with_manifest.html b/samples/advanced/load_with_manifest.html index 1c092eb7be..ec85f79543 100644 --- a/samples/advanced/load_with_manifest.html +++ b/samples/advanced/load_with_manifest.html @@ -23,87 +23,39 @@ var video, player; var parsedManifest = { - "BaseURL_asArray": [ + "BaseURL": [ "https://dash.akamaized.net/dash264/TestCases/1a/sony/" ], - "Period_asArray": [ + "Period": [ { - "AdaptationSet_asArray": [ + "AdaptationSet": [ { - "AudioChannelConfiguration_asArray": [ + "AudioChannelConfiguration": [ { - "__children": [], "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", "value": "2" } ], - "Role_asArray": [ + "Role": [ { - "__children": [], "schemeIdUri": "urn:mpeg:dash:role:2011", "value": "main" } ], - "Representation_asArray": [ + "Representation": [ { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodaudio_Track5.m4a" ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], "bandwidth": 64000, "id": "2_1", "audioSamplingRate": 48000, "mimeType": "audio/mp4", "codecs": "mp4a.40.5", "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - ], - "__children": [ - { - "AudioChannelConfiguration": { - "__children": [], "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", "value": "2" } - }, - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } } ], "audioSamplingRate": 48000, @@ -118,28 +70,20 @@ }, { "Role": { - "__children": [], "schemeIdUri": "urn:mpeg:dash:role:2011", "value": "main" }, - "Role_asArray": [ + "Role": [ { - "__children": [], "schemeIdUri": "urn:mpeg:dash:role:2011", "value": "main" } ], "Representation": [ { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track2.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], "bandwidth": 1005568, "height": 480, "id": "1_1", @@ -152,15 +96,9 @@ "maximumSAPPeriod": 5 }, { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track1.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], "bandwidth": 1609728, "height": 480, "id": "1_2", @@ -173,15 +111,9 @@ "maximumSAPPeriod": 5 }, { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track3.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], "bandwidth": 704512, "height": 480, "id": "1_3", @@ -194,15 +126,9 @@ "maximumSAPPeriod": 5 }, { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track4.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], "bandwidth": 452608, "height": 480, "id": "1_4", @@ -215,17 +141,11 @@ "maximumSAPPeriod": 5 } ], - "Representation_asArray": [ + "Representation": [ { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track2.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], "bandwidth": 1005568, "height": 480, "id": "1_1", @@ -238,15 +158,9 @@ "maximumSAPPeriod": 5 }, { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track1.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], "bandwidth": 1609728, "height": 480, "id": "1_2", @@ -259,15 +173,9 @@ "maximumSAPPeriod": 5 }, { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track3.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], "bandwidth": 704512, "height": 480, "id": "1_3", @@ -280,15 +188,9 @@ "maximumSAPPeriod": 5 }, { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ + "BaseURL": [ "DASH_vodvideo_Track4.m4v" ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], "bandwidth": 452608, "height": 480, "id": "1_4", @@ -301,107 +203,6 @@ "maximumSAPPeriod": 5 } ], - "__children": [ - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - } - ], "codecs": "avc1.4D401E", "contentType": "video", "frameRate": "24000/1001", @@ -421,1615 +222,10 @@ "subsegmentStartsWithSAP": 1 } ], - "__children": [ - { - "AdaptationSet": { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - }, - "AudioChannelConfiguration_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - ], - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation_asArray": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - ], - "__children": [ - { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - }, - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - } - ], - "audioSamplingRate": 48000, - "codecs": "mp4a.40.5", - "contentType": "audio", - "group": 2, - "id": 2, - "lang": "en", - "mimeType": "audio/mp4", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - } - }, - { - "AdaptationSet": { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation_asArray": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - ], - "__children": [ - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - } - ], - "codecs": "avc1.4D401E", - "contentType": "video", - "frameRate": "24000/1001", - "group": 1, - "id": 1, - "maxBandwidth": 1609728, - "maxHeight": 480, - "maxWidth": 854, - "maximumSAPPeriod": 5, - "mimeType": "video/mp4", - "minBandwidth": 452608, - "minHeight": 480, - "minWidth": 854, - "par": "16:9", - "sar": "1:1", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - } - } - ], "duration": 597, "id": "P1" } ], - "__children": [ - { - "Period": { - "AdaptationSet": [ - { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - }, - "AudioChannelConfiguration_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - ], - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - }, - "Representation_asArray": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - ], - "__children": [ - { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - }, - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - } - ], - "audioSamplingRate": 48000, - "codecs": "mp4a.40.5", - "contentType": "audio", - "group": 2, - "id": 2, - "lang": "en", - "mimeType": "audio/mp4", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - }, - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - ], - "Representation_asArray": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - ], - "__children": [ - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - } - ], - "codecs": "avc1.4D401E", - "contentType": "video", - "frameRate": "24000/1001", - "group": 1, - "id": 1, - "maxBandwidth": 1609728, - "maxHeight": 480, - "maxWidth": 854, - "maximumSAPPeriod": 5, - "mimeType": "video/mp4", - "minBandwidth": 452608, - "minHeight": 480, - "minWidth": 854, - "par": "16:9", - "sar": "1:1", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - } - ], - "AdaptationSet_asArray": [ - { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - }, - "AudioChannelConfiguration_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - ], - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - }, - "Representation_asArray": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - ], - "__children": [ - { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - }, - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - } - ], - "audioSamplingRate": 48000, - "codecs": "mp4a.40.5", - "contentType": "audio", - "group": 2, - "id": 2, - "lang": "en", - "mimeType": "audio/mp4", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - }, - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - ], - "Representation_asArray": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - ], - "__children": [ - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - } - ], - "codecs": "avc1.4D401E", - "contentType": "video", - "frameRate": "24000/1001", - "group": 1, - "id": 1, - "maxBandwidth": 1609728, - "maxHeight": 480, - "maxWidth": 854, - "maximumSAPPeriod": 5, - "mimeType": "video/mp4", - "minBandwidth": 452608, - "minHeight": 480, - "minWidth": 854, - "par": "16:9", - "sar": "1:1", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - } - ], - "__children": [ - { - "AdaptationSet": { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - }, - "AudioChannelConfiguration_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - ], - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - }, - "Representation_asArray": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - ], - "__children": [ - { - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - }, - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodaudio_Track5.m4a", - "BaseURL_asArray": [ - "DASH_vodaudio_Track5.m4a" - ], - "__children": [ - { - "BaseURL": "DASH_vodaudio_Track5.m4a" - } - ], - "bandwidth": 64000, - "id": "2_1", - "audioSamplingRate": 48000, - "mimeType": "audio/mp4", - "codecs": "mp4a.40.5", - "AudioChannelConfiguration": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:23003:3:audio_channel_configuration:2011", - "value": "2" - } - } - } - ], - "audioSamplingRate": 48000, - "codecs": "mp4a.40.5", - "contentType": "audio", - "group": 2, - "id": 2, - "lang": "en", - "mimeType": "audio/mp4", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - } - }, - { - "AdaptationSet": { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - }, - "Role_asArray": [ - { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - ], - "Representation": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - ], - "Representation_asArray": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - }, - { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - ], - "__children": [ - { - "Role": { - "__children": [], - "schemeIdUri": "urn:mpeg:dash:role:2011", - "value": "main" - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track2.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track2.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track2.m4v" - } - ], - "bandwidth": 1005568, - "height": 480, - "id": "1_1", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track1.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track1.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track1.m4v" - } - ], - "bandwidth": 1609728, - "height": 480, - "id": "1_2", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track3.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track3.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track3.m4v" - } - ], - "bandwidth": 704512, - "height": 480, - "id": "1_3", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - }, - { - "Representation": { - "BaseURL": "DASH_vodvideo_Track4.m4v", - "BaseURL_asArray": [ - "DASH_vodvideo_Track4.m4v" - ], - "__children": [ - { - "BaseURL": "DASH_vodvideo_Track4.m4v" - } - ], - "bandwidth": 452608, - "height": 480, - "id": "1_4", - "mediaStreamStructureId": "1", - "width": 854, - "sar": "1:1", - "frameRate": "24000/1001", - "mimeType": "video/mp4", - "codecs": "avc1.4D401E", - "maximumSAPPeriod": 5 - } - } - ], - "codecs": "avc1.4D401E", - "contentType": "video", - "frameRate": "24000/1001", - "group": 1, - "id": 1, - "maxBandwidth": 1609728, - "maxHeight": 480, - "maxWidth": 854, - "maximumSAPPeriod": 5, - "mimeType": "video/mp4", - "minBandwidth": 452608, - "minHeight": 480, - "minWidth": 854, - "par": "16:9", - "sar": "1:1", - "subsegmentAlignment": "true", - "subsegmentStartsWithSAP": 1 - } - } - ], - "duration": 597, - "id": "P1" - } - } - ], "xmlns": "urn:mpeg:dash:schema:mpd:2011", "xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "maxSubsegmentDuration": 5, diff --git a/samples/advanced/monitoring.html b/samples/advanced/monitoring.html index 6c0c6d201d..569dca91ff 100644 --- a/samples/advanced/monitoring.html +++ b/samples/advanced/monitoring.html @@ -49,7 +49,7 @@ var bufferLevel = dashMetrics.getCurrentBufferLevel('video', true); var bitrate = repSwitch ? Math.round(dashAdapter.getBandwidthForRepresentation(repSwitch.to, periodIdx) / 1000) : NaN; var adaptation = dashAdapter.getAdaptationForType(periodIdx, 'video', streamInfo); - var currentRep = adaptation.Representation_asArray.find(function (rep) { + var currentRep = adaptation.Representation.find(function (rep) { return rep.id === repSwitch.to }) var frameRate = currentRep.frameRate; diff --git a/samples/captioning/FakeHTMLMediaElement.js b/samples/captioning/FakeHTMLMediaElement.js new file mode 100644 index 0000000000..6a9c100a32 --- /dev/null +++ b/samples/captioning/FakeHTMLMediaElement.js @@ -0,0 +1,267 @@ + + +class InternalEventTarget { + + constructor() { + this.listeners = {}; + } + + addEventListener(type, callback) { + if (!(type in this.listeners)) { + this.listeners[type] = []; + } + this.listeners[type].push(callback); + } + + removeEventListener(type, callback) { + if (!(type in this.listeners)) { + return; + } + var stack = this.listeners[type]; + for (var i = 0, l = stack.length; i < l; i++) { + if (stack[i] === callback) { + stack.splice(i, 1); + return; + } + } + } + + dispatchEvent(event) { + if (!(event.type in this.listeners)) { + return true; + } + var stack = this.listeners[event.type].slice(); + for (var i = 0, l = stack.length; i < l; i++) { + stack[i].call(this, event); + } + return !event.defaultPrevented; + } +} + +class FakeTextTrack extends InternalEventTarget { + + constructor(kind, label, lang, isTTML, isEmbedded) { + super(); + this.kind_ = kind; + this.label_ = label; + this.lang_ = lang; + this.isTTML_ = isTTML; + this.isEmbedded_ = isEmbedded; + this.cues_ = []; + this.activeCues_ = []; + this.currentVideoTime_ = -1; + this.mode_ = 'disabled'; + this.log('New text track', label, lang); + } + + log(...params) { + let message = '[FakeTextTrack][' + this.lang_ + '] '; + Array.apply(null, params).forEach(function (item) { + message += item + ' '; + }); + console.log(message); + } + + get kind() { return this.kind_; } + get label() { return this.label_; } + get lang() { return this.lang_; } + get language() { return this.lang_; } + get isTTML() { return this.isTTML_; } + get isEmbedded() { return this.isEmbedded_; } + get mode() { return this.mode_; } + get active() { return this.mode_ === 'showing'; } + + set isTTML(value) { this.isTTML_ = value; } + set isEmbedded(value) { this.isEmbedded_ = value; } + + set mode(mode) { + this.log('mode =', mode); + this.mode_ = mode; + } + + get activeCues() { return this.activeCues_; } + get cues() { return this.cues_; } + + addCue(cue) { + this.log('add cue', cue, cue.startTime); + if (this.cues_.indexOf(cue) === -1) { + this.cues_.push(cue); + } + } + + removeCue(cue) { + this.log('remove cue', cue); + let cueIndex = this.cues_.indexOf(cue); + if (cueIndex > -1) { + this.cues_.splice(cueIndex, 1); + } + } + + clearCues() { + this.cues_ = []; + this.log('cue changed', this.cues_); + } + + notifyTimeChange(newTime) { + + if (!this.active) { return; } + + this.log('timechanged', newTime); + + // debounce time updates + var deltaVTime = newTime - this.currentVideoTime_; + if (deltaVTime < 0.01 && deltaVTime > -0.01) { + return; + } + this.currentVideoTime_ = newTime; + + const previousActivesCues = this.activeCues_.slice(); + this.activeCues_ = this.cues_.filter(cue => this.isCueActive(cue, this.currentVideoTime_)); + + // For each old cue, exit cue + previousActivesCues.forEach(cue => { + if (this.activeCues_.indexOf(cue) === -1 && cue.onexit) { + this.log('exit cue:', cue); + cue.onexit(); + } + }); + + // For each new cue, enter cue + this.activeCues_.forEach(cue => { + if (previousActivesCues.indexOf(cue) === -1 && cue.onenter) { + this.log('enter cue:', cue); + cue.onenter(); + } + }); + } + + isCueActive(cue, time) { + // this.log('isCueActive cue:', cue); + return time >= cue.startTime && time <= cue.endTime; + } +} + +class FakeMediaSource extends InternalEventTarget { + + constructor () { + super(); + this._readyState = ''; + } + + get readyState() { return this._readyState; } + get sourceBuffers() { return []; } + + open() { + this._readyState = 'open'; + this.dispatchEvent(new Event('sourceopen')); + } + + endOfStream() {} +} + + +class FakeHTMLMediaElement extends InternalEventTarget { + + constructor (parentNode) { + super(); + this._parentNode = parentNode; + this.init(); + } + + init () { + if (this._textTracks) { + this._textTracks.forEach(track => track.clearCues()); + } + this._textTracks = []; + this._paused = true; + this._readyState = 0; + this._duration = 0; + this._currentTime = 0; + this._playbackRate = 0; + this._ended = false; + this._clientWidth = 500; + this._clientHeight = 300; + this._videoWidth = 500; + this._videoHeight = 300; + this._preload = ''; + } + + // HTMLMediaElement API + + get nodeName() { return 'FAKE'; } + get parentNode() { return this._parentNode; } + get classList() { return []; } + + get readyState() { return this._readyState; } + get paused() { return this._paused; } + get duration() { return this._duration; } + get currentTime() { return this._currentTime; } + get playbackRate() { return this._playbackRate; } + get ended() { return this._ended; } + get textTracks() { return this._textTracks; } + get clientWidth() { return this._parentNode.getBoundingClientRect().width; } + get clientHeight() { return this._parentNode.getBoundingClientRect().height; } + get videoWidth() { return this._parentNode.getBoundingClientRect().width;; } + get videoHeight() { return this._parentNode.getBoundingClientRect().height; } + + set readyState(value) { + if (this._readyState === value) return; + this._readyState = value; + } + + set duration(value) { + if (this._duration === value) return; + this._duration = value; + this.dispatchEvent(new Event('durationchange')); + } + + set playbackRate(value) { + if (this._playbackRate === value) return; + this._playbackRate = value; + this.dispatchEvent(new Event('ratechange')); + } + + set currentTime(value) { + this.updateCurrentTime(value, false); + this.dispatchEvent(new Event('seeking')); + this.dispatchEvent(new Event('seeked')); + } + + pause() { + if (this.paused) return; + this._paused = true; + this.dispatchEvent(new Event('pause')); + } + + play() { + if (!this.paused) return; + this._paused = false; + this.dispatchEvent(new Event('playing')); + } + + addTextTrack(kind, label, lang) { + let track = new FakeTextTrack(kind, label, lang); + this._textTracks.push(track); + return track; + } + + load() {} + removeAttribute() {} + appendChild() {} + removeChild() {} + + getBoundingClientRect() { + return this._parentNode.getBoundingClientRect(); + } + + // Specific methods + + updateCurrentTime(value, fireTimeupdate = true) { + if (this._currentTime === value) return; + this._currentTime = value; + if (fireTimeupdate) { + this.dispatchEvent(new Event('timeupdate')); + } + this._textTracks.forEach(track => track.notifyTimeChange(this._currentTime)); + } +} diff --git a/samples/captioning/text-track-only.html b/samples/captioning/text-track-only.html new file mode 100644 index 0000000000..6b33f274f3 --- /dev/null +++ b/samples/captioning/text-track-only.html @@ -0,0 +1,143 @@ + + + + + Text track streaming + + + + + + + + + + + + + + +
+
+
+ +
+
+
+
+

Text tracks streaming Demo

+

This example shows how to stream and display text tracks without audio/video streaming (no video element)

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+ © DASH-IF +
+
+
+ + + + + + diff --git a/src/dash/DashAdapter.js b/src/dash/DashAdapter.js index ca4ecee69b..8c921a62ab 100644 --- a/src/dash/DashAdapter.js +++ b/src/dash/DashAdapter.js @@ -97,7 +97,7 @@ function DashAdapter() { function convertRepresentationToRepresentationInfo(voRepresentation) { if (voRepresentation) { let representationInfo = new RepresentationInfo(); - const realAdaptation = voRepresentation.adaptation.period.mpd.manifest.Period_asArray[voRepresentation.adaptation.period.index].AdaptationSet_asArray[voRepresentation.adaptation.index]; + const realAdaptation = voRepresentation.adaptation.period.mpd.manifest.Period[voRepresentation.adaptation.period.index].AdaptationSet[voRepresentation.adaptation.index]; const realRepresentation = dashManifestModel.getRepresentationFor(voRepresentation.index, realAdaptation); representationInfo.id = voRepresentation.id; @@ -914,7 +914,7 @@ function DashAdapter() { } // determine the relative insert position prior to possible removal - let relativePosition = (target[name + '_asArray'] || []).indexOf(leaf); + let relativePosition = (target[name] || []).indexOf(leaf); let insertBefore = (operation.position === 'prepend' || operation.position === 'before'); // perform removal operation first, we have already capture the appropriate relative position @@ -922,20 +922,17 @@ function DashAdapter() { // note that we ignore the 'ws' attribute of patch operations as it does not effect parsed mpd operations // purge the directly named entity - delete target[name]; - - // if we did have a positional reference we need to purge from array set and restore X2JS proper semantics - if (relativePosition != -1) { - let targetArray = target[name + '_asArray']; + if (typeof target[name] === Object) { + delete target[name]; + } else if (relativePosition != -1) { + // if we did have a positional reference we need to purge from array set and restore X2JS proper semantics + let targetArray = target[name]; targetArray.splice(relativePosition, 1); - if (targetArray.length > 1) { + if (targetArray.length > 0) { target[name] = targetArray; - } else if (targetArray.length == 1) { - // xml parsing semantics, singular asArray must be non-array in the unsuffixed key - target[name] = targetArray[0]; } else { // all nodes of this type deleted, remove entry - delete target[name + '_asArray']; + delete target[name]; } } } @@ -949,7 +946,7 @@ function DashAdapter() { Object.keys(operation.value).forEach((insert) => { let insertNodes = operation.value[insert]; - let updatedNodes = target[insert + '_asArray'] || []; + let updatedNodes = target[insert] || []; if (updatedNodes.length === 0 && target[insert]) { updatedNodes.push(target[insert]); } @@ -975,8 +972,7 @@ function DashAdapter() { } // now we properly reset the element keys on the target to match parsing semantics - target[insert + '_asArray'] = updatedNodes; - target[insert] = updatedNodes.length == 1 ? updatedNodes[0] : updatedNodes; + target[insert] = updatedNodes; }); } }); @@ -1020,7 +1016,7 @@ function DashAdapter() { } let mediaInfo = new MediaInfo(); - const realAdaptation = adaptation.period.mpd.manifest.Period_asArray[adaptation.period.index].AdaptationSet_asArray[adaptation.index]; + const realAdaptation = adaptation.period.mpd.manifest.Period[adaptation.period.index].AdaptationSet[adaptation.index]; let viewpoint; mediaInfo.id = adaptation.id; @@ -1050,8 +1046,8 @@ function DashAdapter() { return audioChannelConfiguration.value; }); - if (mediaInfo.audioChannelConfiguration.length === 0 && Array.isArray(realAdaptation.Representation_asArray) && realAdaptation.Representation_asArray.length > 0) { - mediaInfo.audioChannelConfiguration = dashManifestModel.getAudioChannelConfigurationForRepresentation(realAdaptation.Representation_asArray[0]).map(function (audioChannelConfiguration) { + if (mediaInfo.audioChannelConfiguration.length === 0 && realAdaptation.Representation && realAdaptation.Representation.length > 0) { + mediaInfo.audioChannelConfiguration = dashManifestModel.getAudioChannelConfigurationForRepresentation(realAdaptation.Representation[0]).map(function (audioChannelConfiguration) { return audioChannelConfiguration.value; }); } @@ -1108,7 +1104,7 @@ function DashAdapter() { streamInfo.start = period.start; streamInfo.duration = period.duration; streamInfo.manifestInfo = convertMpdToManifestInfo(period.mpd); - streamInfo.isLast = period.mpd.manifest.Period_asArray.length === 1 || Math.abs((streamInfo.start + streamInfo.duration) - streamInfo.manifestInfo.duration) < THRESHOLD; + streamInfo.isLast = period.mpd.manifest.Period.length === 1 || Math.abs((streamInfo.start + streamInfo.duration) - streamInfo.manifestInfo.duration) < THRESHOLD; return streamInfo; } @@ -1136,7 +1132,7 @@ function DashAdapter() { } function getPeriod(periodIdx) { - return voPeriods.length > 0 ? voPeriods[0].mpd.manifest.Period_asArray[periodIdx] : null; + return voPeriods.length > 0 ? voPeriods[0].mpd.manifest.Period[periodIdx] : null; } function findRepresentationIndex(period, representationId) { @@ -1154,10 +1150,10 @@ function DashAdapter() { representationArrayIndex; if (period) { - adaptationSetArray = period.AdaptationSet_asArray; + adaptationSetArray = period.AdaptationSet; for (adaptationSetArrayIndex = 0; adaptationSetArrayIndex < adaptationSetArray.length; adaptationSetArrayIndex = adaptationSetArrayIndex + 1) { adaptationSet = adaptationSetArray[adaptationSetArrayIndex]; - representationArray = adaptationSet.Representation_asArray; + representationArray = adaptationSet.Representation; for (representationArrayIndex = 0; representationArrayIndex < representationArray.length; representationArrayIndex = representationArrayIndex + 1) { representation = representationArray[representationArrayIndex]; if (representationId === representation.id) { @@ -1182,10 +1178,10 @@ function DashAdapter() { if (!period || !bufferType) return -1; - adaptationSetArray = period.AdaptationSet_asArray; + adaptationSetArray = period.AdaptationSet; for (adaptationSetArrayIndex = 0; adaptationSetArrayIndex < adaptationSetArray.length; adaptationSetArrayIndex = adaptationSetArrayIndex + 1) { adaptationSet = adaptationSetArray[adaptationSetArrayIndex]; - representationArray = adaptationSet.Representation_asArray; + representationArray = adaptationSet.Representation; if (dashManifestModel.getIsTypeOf(adaptationSet, bufferType)) { return representationArray.length; } diff --git a/src/dash/DashHandler.js b/src/dash/DashHandler.js index 0ea504f67a..e941841ac4 100644 --- a/src/dash/DashHandler.js +++ b/src/dash/DashHandler.js @@ -154,7 +154,7 @@ function DashHandler(config) { const request = new FragmentRequest(); const representation = segment.representation; - const bandwidth = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].bandwidth; + const bandwidth = representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index].AdaptationSet[representation.adaptation.index].Representation[representation.index].bandwidth; let url = segment.media; url = replaceTokenForTemplate(url, 'Number', segment.replacementNumber); diff --git a/src/dash/constants/DashConstants.js b/src/dash/constants/DashConstants.js index 06473db468..49fa6a2ec1 100644 --- a/src/dash/constants/DashConstants.js +++ b/src/dash/constants/DashConstants.js @@ -43,6 +43,7 @@ class DashConstants { this.SEGMENT_LIST = 'SegmentList'; this.SEGMENT_URL = 'SegmentURL'; this.SEGMENT_TIMELINE = 'SegmentTimeline'; + this.S = 'S'; this.SEGMENT_PROFILES = 'segmentProfiles'; this.ADAPTATION_SET = 'AdaptationSet'; this.REPRESENTATION = 'Representation'; @@ -54,8 +55,10 @@ class DashConstants { this.PERIOD = 'Period'; this.ASSET_IDENTIFIER = 'AssetIdentifier'; this.EVENT_STREAM = 'EventStream'; + this.EVENT = 'Event'; this.ID = 'id'; this.PROFILES = 'profiles'; + this.LOCATION = 'Location'; this.SERVICE_LOCATION = 'serviceLocation'; this.RANGE = 'range'; this.INDEX = 'index'; @@ -88,6 +91,7 @@ class DashConstants { this.AUDIO_CHANNEL_CONFIGURATION = 'AudioChannelConfiguration'; this.CONTENT_PROTECTION = 'ContentProtection'; this.ESSENTIAL_PROPERTY = 'EssentialProperty'; + this.LABEL = 'Label'; this.SUPPLEMENTAL_PROPERTY = 'SupplementalProperty'; this.INBAND_EVENT_STREAM = 'InbandEventStream'; this.PRODUCER_REFERENCE_TIME = 'ProducerReferenceTime'; @@ -98,12 +102,8 @@ class DashConstants { this.SUBSET = 'Subset'; this.LANG = 'lang'; this.VIEWPOINT = 'Viewpoint'; - this.ROLE_ASARRAY = 'Role_asArray'; - this.REPRESENTATION_ASARRAY = 'Representation_asArray'; - this.PRODUCERREFERENCETIME_ASARRAY = 'ProducerReferenceTime_asArray'; - this.ACCESSIBILITY_ASARRAY = 'Accessibility_asArray'; - this.AUDIOCHANNELCONFIGURATION_ASARRAY = 'AudioChannelConfiguration_asArray'; - this.CONTENTPROTECTION_ASARRAY = 'ContentProtection_asArray'; + this.ROLE = 'Role'; + this.ACCESSIBILITY = 'Accessibility'; this.MAIN = 'main'; this.DYNAMIC = 'dynamic'; this.STATIC = 'static'; @@ -140,8 +140,12 @@ class DashConstants { this.PUBLISH_TIME = 'publishTime'; this.ORIGINAL_PUBLISH_TIME = 'originalPublishTime'; this.ORIGINAL_MPD_ID = 'mpdId'; + this.REPLACE = 'replace'; + this.ADD = 'add'; + this.REMOVE = 'remove'; this.WALL_CLOCK_TIME = 'wallClockTime'; this.PRESENTATION_TIME = 'presentationTime'; + this.UTC_TIMING = 'UTCTiming'; this.LABEL = 'Label'; this.GROUP_LABEL = 'GroupLabel'; this.CONTENT_STEERING = 'ContentSteering'; diff --git a/src/dash/models/DashManifestModel.js b/src/dash/models/DashManifestModel.js index a5603c6d2a..ade375d52c 100644 --- a/src/dash/models/DashManifestModel.js +++ b/src/dash/models/DashManifestModel.js @@ -77,18 +77,18 @@ function DashManifestModel() { } // Check for thumbnail images - if (adaptation.Representation_asArray && adaptation.Representation_asArray.length) { - const essentialProperties = getEssentialPropertiesForRepresentation(adaptation.Representation_asArray[0]); + if (adaptation.Representation && adaptation.Representation.length) { + const essentialProperties = getEssentialPropertiesForRepresentation(adaptation.Representation[0]); if (essentialProperties && essentialProperties.length > 0 && THUMBNAILS_SCHEME_ID_URIS.indexOf(essentialProperties[0].schemeIdUri) >= 0) { return (type === Constants.IMAGE); } } // Check ContentComponent.contentType - if (adaptation.ContentComponent_asArray && adaptation.ContentComponent_asArray.length > 0) { - if (adaptation.ContentComponent_asArray.length > 1) { + if (adaptation.ContentComponent && adaptation.ContentComponent.length > 0) { + if (adaptation.ContentComponent.length > 1) { return (type === Constants.MUXED); - } else if (adaptation.ContentComponent_asArray[0].contentType === type) { + } else if (adaptation.ContentComponent[0].contentType === type) { return true; } } @@ -96,8 +96,8 @@ function DashManifestModel() { const mimeTypeRegEx = (type === Constants.TEXT) ? new RegExp('(ttml|vtt|wvtt|stpp)') : new RegExp(type); // Check codecs - if (adaptation.Representation_asArray && adaptation.Representation_asArray.length) { - const codecs = adaptation.Representation_asArray[0].codecs; + if (adaptation.Representation && adaptation.Representation.length) { + const codecs = adaptation.Representation[0].codecs; if (mimeTypeRegEx.test(codecs)) { return true; } @@ -109,10 +109,10 @@ function DashManifestModel() { } // Check Representation's mimeType - if (adaptation.Representation_asArray) { + if (adaptation.Representation) { let representation; - for (let i = 0; i < adaptation.Representation_asArray.length; i++) { - representation = adaptation.Representation_asArray[i]; + for (let i = 0; i < adaptation.Representation.length; i++) { + representation = adaptation.Representation[i]; if (representation.hasOwnProperty(DashConstants.MIME_TYPE)) { return mimeTypeRegEx.test(representation.mimeType); } @@ -132,8 +132,8 @@ function DashManifestModel() { adaptation.hasOwnProperty(DashConstants.SEGMENT_BASE)) { return true; } - if (adaptation.Representation_asArray && adaptation.Representation_asArray.length > 0) { - const representation = adaptation.Representation_asArray[0]; + if (adaptation.Representation && adaptation.Representation.length > 0) { + const representation = adaptation.Representation[0]; if (representation.hasOwnProperty(DashConstants.SEGMENT_TEMPLATE) || representation.hasOwnProperty(DashConstants.SEGMENT_TIMELINE) || representation.hasOwnProperty(DashConstants.SEGMENT_LIST) || @@ -165,14 +165,14 @@ function DashManifestModel() { } function getProducerReferenceTimesForAdaptation(adaptation) { - const prtArray = adaptation && adaptation.hasOwnProperty(DashConstants.PRODUCERREFERENCETIME_ASARRAY) ? adaptation[DashConstants.PRODUCERREFERENCETIME_ASARRAY] : []; + const prtArray = adaptation && adaptation.hasOwnProperty(DashConstants.PRODUCER_REFERENCE_TIME) ? adaptation[DashConstants.PRODUCER_REFERENCE_TIME] : []; // ProducerReferenceTime elements can also be contained in Representations - const representationsArray = adaptation && adaptation.hasOwnProperty(DashConstants.REPRESENTATION_ASARRAY) ? adaptation[DashConstants.REPRESENTATION_ASARRAY] : []; + const representationsArray = adaptation && adaptation.hasOwnProperty(DashConstants.REPRESENTATION) ? adaptation[DashConstants.REPRESENTATION] : []; representationsArray.forEach((rep) => { - if (rep.hasOwnProperty(DashConstants.PRODUCERREFERENCETIME_ASARRAY)) { - prtArray.push(...rep[DashConstants.PRODUCERREFERENCETIME_ASARRAY]); + if (rep.hasOwnProperty(DashConstants.PRODUCER_REFERENCE_TIME)) { + prtArray.push(...rep[DashConstants.PRODUCER_REFERENCE_TIME]); } }); @@ -226,19 +226,19 @@ function DashManifestModel() { } function getRolesForAdaptation(adaptation) { - return adaptation && adaptation.hasOwnProperty(DashConstants.ROLE_ASARRAY) ? adaptation.Role_asArray : []; + return adaptation && adaptation.hasOwnProperty(DashConstants.ROLE) ? adaptation.Role : []; } function getAccessibilityForAdaptation(adaptation) { - return adaptation && adaptation.hasOwnProperty(DashConstants.ACCESSIBILITY_ASARRAY) ? adaptation.Accessibility_asArray : []; + return adaptation && adaptation.hasOwnProperty(DashConstants.ACCESSIBILITY) ? adaptation.Accessibility : []; } function getAudioChannelConfigurationForAdaptation(adaptation) { - return adaptation && adaptation.hasOwnProperty(DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY) ? adaptation.AudioChannelConfiguration_asArray : []; + return adaptation && adaptation.hasOwnProperty(DashConstants.AUDIO_CHANNEL_CONFIGURATION) ? adaptation.AudioChannelConfiguration : []; } function getAudioChannelConfigurationForRepresentation(representation) { - return representation && representation.hasOwnProperty(DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY) ? representation.AudioChannelConfiguration_asArray : []; + return representation && representation.hasOwnProperty(DashConstants.AUDIO_CHANNEL_CONFIGURATION) ? representation.AudioChannelConfiguration : []; } function getRepresentationSortFunction() { @@ -246,19 +246,19 @@ function DashManifestModel() { } function processAdaptation(realAdaptation) { - if (realAdaptation && Array.isArray(realAdaptation.Representation_asArray)) { - realAdaptation.Representation_asArray.sort(getRepresentationSortFunction()); + if (realAdaptation && realAdaptation.Representation) { + realAdaptation.Representation.sort(getRepresentationSortFunction()); } return realAdaptation; } function getRealAdaptations(manifest, periodIndex) { - return manifest && manifest.Period_asArray && isInteger(periodIndex) ? manifest.Period_asArray[periodIndex] ? manifest.Period_asArray[periodIndex].AdaptationSet_asArray : [] : []; + return manifest && manifest.Period && isInteger(periodIndex) ? manifest.Period[periodIndex] ? manifest.Period[periodIndex].AdaptationSet : [] : []; } function getRealPeriods(manifest) { - return manifest && manifest.Period_asArray ? manifest.Period_asArray : []; + return manifest && manifest.Period ? manifest.Period : []; } function getRealPeriodForIndex(index, manifest) { @@ -328,9 +328,9 @@ function DashManifestModel() { function getCodec(adaptation, representationId, addResolutionInfo) { let codec = null; - if (adaptation && adaptation.Representation_asArray && adaptation.Representation_asArray.length > 0) { - const representation = isInteger(representationId) && representationId >= 0 && representationId < adaptation.Representation_asArray.length ? - adaptation.Representation_asArray[representationId] : adaptation.Representation_asArray[0]; + if (adaptation && adaptation.Representation && adaptation.Representation.length > 0) { + const representation = isInteger(representationId) && representationId >= 0 && representationId < adaptation.Representation.length ? + adaptation.Representation[representationId] : adaptation.Representation[0]; if (representation) { codec = representation.mimeType + ';codecs="' + representation.codecs + '"'; if (addResolutionInfo && representation.width !== undefined) { @@ -348,7 +348,7 @@ function DashManifestModel() { } function getMimeType(adaptation) { - return adaptation && adaptation.Representation_asArray && adaptation.Representation_asArray.length > 0 ? adaptation.Representation_asArray[0].mimeType : null; + return adaptation && adaptation.Representation && adaptation.Representation.length > 0 ? adaptation.Representation[0].mimeType : null; } function getKID(adaptation) { @@ -359,16 +359,16 @@ function DashManifestModel() { } function getLabelsForAdaptation(adaptation) { - if (!adaptation || !Array.isArray(adaptation.Label_asArray)) { + if (!adaptation || !adaptation.Label) { return []; } const labelArray = []; - for (let i = 0; i < adaptation.Label_asArray.length; i++) { + for (let i = 0; i < adaptation.Label.length; i++) { labelArray.push({ - lang: adaptation.Label_asArray[i].lang, - text: adaptation.Label_asArray[i].__text || adaptation.Label_asArray[i] + lang: adaptation.Label[i].lang, + text: adaptation.Label[i].__text || adaptation.Label[i] }); } @@ -376,10 +376,10 @@ function DashManifestModel() { } function getContentProtectionData(adaptation) { - if (!adaptation || !adaptation.hasOwnProperty(DashConstants.CONTENTPROTECTION_ASARRAY) || adaptation.ContentProtection_asArray.length === 0) { + if (!adaptation || !adaptation.hasOwnProperty(DashConstants.CONTENT_PROTECTION) || adaptation.ContentProtection.length === 0) { return null; } - return adaptation.ContentProtection_asArray; + return adaptation.ContentProtection; } function getIsDynamic(manifest) { @@ -436,12 +436,12 @@ function DashManifestModel() { } function getRepresentationCount(adaptation) { - return adaptation && Array.isArray(adaptation.Representation_asArray) ? adaptation.Representation_asArray.length : 0; + return adaptation && adaptation.Representation ? adaptation.Representation.length : 0; } function getBitrateListForAdaptation(realAdaptation) { const processedRealAdaptation = processAdaptation(realAdaptation); - const realRepresentations = processedRealAdaptation && Array.isArray(processedRealAdaptation.Representation_asArray) ? processedRealAdaptation.Representation_asArray : []; + const realRepresentations = processedRealAdaptation && processedRealAdaptation.Representation ? processedRealAdaptation.Representation : []; return realRepresentations.map((realRepresentation) => { return { @@ -465,9 +465,9 @@ function DashManifestModel() { } function getEssentialPropertiesForRepresentation(realRepresentation) { - if (!realRepresentation || !realRepresentation.EssentialProperty_asArray || !realRepresentation.EssentialProperty_asArray.length) return null; + if (!realRepresentation || !realRepresentation.EssentialProperty || !realRepresentation.EssentialProperty.length) return null; - return realRepresentation.EssentialProperty_asArray.map((prop) => { + return realRepresentation.EssentialProperty.map((prop) => { return { schemeIdUri: prop.schemeIdUri, value: prop.value @@ -476,15 +476,15 @@ function DashManifestModel() { } function getRepresentationFor(index, adaptation) { - return adaptation && adaptation.Representation_asArray && adaptation.Representation_asArray.length > 0 && - isInteger(index) ? adaptation.Representation_asArray[index] : null; + return adaptation && adaptation.Representation && adaptation.Representation.length > 0 && + isInteger(index) ? adaptation.Representation[index] : null; } function getRealAdaptationFor(voAdaptation) { if (voAdaptation && voAdaptation.period && isInteger(voAdaptation.period.index)) { - const periodArray = voAdaptation.period.mpd.manifest.Period_asArray[voAdaptation.period.index]; - if (periodArray && periodArray.AdaptationSet_asArray && isInteger(voAdaptation.index)) { - return processAdaptation(periodArray.AdaptationSet_asArray[voAdaptation.index]); + const periodArray = voAdaptation.period.mpd.manifest.Period[voAdaptation.period.index]; + if (periodArray && periodArray.AdaptationSet && isInteger(voAdaptation.index)) { + return processAdaptation(periodArray.AdaptationSet[voAdaptation.index]); } } } @@ -495,7 +495,7 @@ function DashManifestModel() { let segmentInfo, baseUrl; - if (processedRealAdaptation && processedRealAdaptation.Representation_asArray) { + if (processedRealAdaptation && processedRealAdaptation.Representation) { // TODO: TO BE REMOVED. We should get just the baseUrl elements that affects to the representations // that we are processing. Making it works properly will require much further changes and given // parsing base Urls parameters is needed for our ultra low latency examples, we will @@ -506,8 +506,8 @@ function DashManifestModel() { baseUrl = baseUrls[0]; } } - for (let i = 0, len = processedRealAdaptation.Representation_asArray.length; i < len; ++i) { - const realRepresentation = processedRealAdaptation.Representation_asArray[i]; + for (let i = 0, len = processedRealAdaptation.Representation.length; i < len; ++i) { + const realRepresentation = processedRealAdaptation.Representation[i]; const voRepresentation = new Representation(); voRepresentation.index = i; voRepresentation.adaptation = voAdaptation; @@ -640,11 +640,11 @@ function DashManifestModel() { } function calcSegmentDuration(segmentTimeline) { - if (!segmentTimeline || !segmentTimeline.S_asArray) { + if (!segmentTimeline || !segmentTimeline.S) { return NaN; } - let s0 = segmentTimeline.S_asArray[0]; - let s1 = segmentTimeline.S_asArray[1]; + let s0 = segmentTimeline.S[0]; + let s1 = segmentTimeline.S[1]; return s0.hasOwnProperty('d') ? s0.d : (s1.t - s0.t); } @@ -656,15 +656,15 @@ function DashManifestModel() { } function getAdaptationsForPeriod(voPeriod) { - const realPeriod = voPeriod && isInteger(voPeriod.index) ? voPeriod.mpd.manifest.Period_asArray[voPeriod.index] : null; + const realPeriod = voPeriod && isInteger(voPeriod.index) ? voPeriod.mpd.manifest.Period[voPeriod.index] : null; const voAdaptations = []; let voAdaptationSet, realAdaptationSet, i; - if (realPeriod && realPeriod.AdaptationSet_asArray) { - for (i = 0; i < realPeriod.AdaptationSet_asArray.length; i++) { - realAdaptationSet = realPeriod.AdaptationSet_asArray[i]; + if (realPeriod && realPeriod.AdaptationSet) { + for (i = 0; i < realPeriod.AdaptationSet.length; i++) { + realAdaptationSet = realPeriod.AdaptationSet[i]; voAdaptationSet = new AdaptationSet(); if (realAdaptationSet.hasOwnProperty(DashConstants.ID)) { voAdaptationSet.id = realAdaptationSet.id; @@ -702,8 +702,8 @@ function DashManifestModel() { let len, i; - for (i = 0, len = mpd && mpd.manifest && mpd.manifest.Period_asArray ? mpd.manifest.Period_asArray.length : 0; i < len; i++) { - realPeriod = mpd.manifest.Period_asArray[i]; + for (i = 0, len = mpd && mpd.manifest && mpd.manifest.Period ? mpd.manifest.Period.length : 0; i < len; i++) { + realPeriod = mpd.manifest.Period[i]; // If the attribute @start is present in the Period, then the // Period is a regular Period and the PeriodStart is equal @@ -863,8 +863,8 @@ function DashManifestModel() { function getEventsForPeriod(period) { const manifest = period && period.mpd && period.mpd.manifest ? period.mpd.manifest : null; - const periodArray = manifest ? manifest.Period_asArray : null; - const eventStreams = periodArray && period && isInteger(period.index) ? periodArray[period.index].EventStream_asArray : null; + const periodArray = manifest ? manifest.Period : null; + const eventStreams = periodArray && period && isInteger(period.index) ? periodArray[period.index].EventStream : null; const events = []; let i, j; @@ -889,8 +889,8 @@ function DashManifestModel() { if (eventStreams[i].hasOwnProperty(DashConstants.PRESENTATION_TIME_OFFSET)) { eventStream.presentationTimeOffset = eventStreams[i][DashConstants.PRESENTATION_TIME_OFFSET]; } - for (j = 0; eventStreams[i].Event_asArray && j < eventStreams[i].Event_asArray.length; j++) { - const currentMpdEvent = eventStreams[i].Event_asArray[j]; + for (j = 0; eventStreams[i].Event && j < eventStreams[i].Event.length; j++) { + const currentMpdEvent = eventStreams[i].Event[j]; const event = new Event(); event.presentationTime = 0; event.eventStream = eventStream; @@ -966,12 +966,12 @@ function DashManifestModel() { periodArray, adaptationArray; - if (manifest && manifest.Period_asArray && adaptation && adaptation.period && isInteger(adaptation.period.index)) { - periodArray = manifest.Period_asArray[adaptation.period.index]; - if (periodArray && periodArray.AdaptationSet_asArray && isInteger(adaptation.index)) { - adaptationArray = periodArray.AdaptationSet_asArray[adaptation.index]; + if (manifest && manifest.Period && adaptation && adaptation.period && isInteger(adaptation.period.index)) { + periodArray = manifest.Period[adaptation.period.index]; + if (periodArray && periodArray.AdaptationSet && isInteger(adaptation.index)) { + adaptationArray = periodArray.AdaptationSet[adaptation.index]; if (adaptationArray) { - inbandStreams = adaptationArray.InbandEventStream_asArray; + inbandStreams = adaptationArray.InbandEventStream; } } } @@ -985,14 +985,14 @@ function DashManifestModel() { adaptationArray, representationArray; - if (manifest && manifest.Period_asArray && representation && representation.adaptation && representation.adaptation.period && isInteger(representation.adaptation.period.index)) { - periodArray = manifest.Period_asArray[representation.adaptation.period.index]; - if (periodArray && periodArray.AdaptationSet_asArray && isInteger(representation.adaptation.index)) { - adaptationArray = periodArray.AdaptationSet_asArray[representation.adaptation.index]; - if (adaptationArray && adaptationArray.Representation_asArray && isInteger(representation.index)) { - representationArray = adaptationArray.Representation_asArray[representation.index]; + if (manifest && manifest.Period && representation && representation.adaptation && representation.adaptation.period && isInteger(representation.adaptation.period.index)) { + periodArray = manifest.Period[representation.adaptation.period.index]; + if (periodArray && periodArray.AdaptationSet && isInteger(representation.adaptation.index)) { + adaptationArray = periodArray.AdaptationSet[representation.adaptation.index]; + if (adaptationArray && adaptationArray.Representation && isInteger(representation.index)) { + representationArray = adaptationArray.Representation[representation.index]; if (representationArray) { - inbandStreams = representationArray.InbandEventStream_asArray; + inbandStreams = representationArray.InbandEventStream; } } } @@ -1004,7 +1004,7 @@ function DashManifestModel() { function getUTCTimingSources(manifest) { const isDynamic = getIsDynamic(manifest); const hasAST = manifest ? manifest.hasOwnProperty(DashConstants.AVAILABILITY_START_TIME) : false; - const utcTimingsArray = manifest ? manifest.UTCTiming_asArray : null; + const utcTimingsArray = manifest ? manifest.UTCTiming : null; const utcTimingEntries = []; // do not bother synchronizing the clock unless MPD is live, @@ -1049,9 +1049,9 @@ function DashManifestModel() { function getBaseURLsFromElement(node) { const baseUrls = []; - // if node.BaseURL_asArray and node.baseUri are undefined entries + // if node.BaseURL and node.baseUri are undefined entries // will be [undefined] which entries.some will just skip - const entries = node.BaseURL_asArray || [node.baseUri]; + const entries = node.BaseURL || [node.baseUri]; let earlyReturn = false; entries.some(entry => { @@ -1146,9 +1146,7 @@ function DashManifestModel() { if (manifest && manifest.hasOwnProperty(Constants.LOCATION)) { // for now, do not support multiple Locations - // just set Location to the first Location. - manifest.Location = manifest.Location_asArray[0]; - - return manifest.Location; + return manifest.Location[0]; } // may well be undefined @@ -1158,9 +1156,7 @@ function DashManifestModel() { function getPatchLocation(manifest) { if (manifest && manifest.hasOwnProperty(DashConstants.PATCH_LOCATION)) { // only include support for single patch location currently - manifest.PatchLocation = manifest.PatchLocation_asArray[0]; - - return manifest.PatchLocation; + return manifest.PatchLocation[0]; } // no patch location provided @@ -1178,7 +1174,7 @@ function DashManifestModel() { function getServiceDescriptions(manifest) { const serviceDescriptions = []; if (manifest && manifest.hasOwnProperty(DashConstants.SERVICE_DESCRIPTION)) { - for (const sd of manifest.ServiceDescription_asArray) { + for (const sd of manifest.ServiceDescription) { // Convert each of the properties defined in let id = null, schemeIdUri = null, @@ -1243,7 +1239,7 @@ function DashManifestModel() { const supplementalProperties = {}; if (adaptation && adaptation.hasOwnProperty(DashConstants.SUPPLEMENTAL_PROPERTY)) { - for (const sp of adaptation.SupplementalProperty_asArray) { + for (const sp of adaptation.SupplementalProperty) { if (sp.hasOwnProperty(Constants.SCHEME_ID_URI) && sp.hasOwnProperty(DashConstants.VALUE)) { supplementalProperties[sp[Constants.SCHEME_ID_URI]] = sp[DashConstants.VALUE]; } diff --git a/src/dash/parser/DashParser.js b/src/dash/parser/DashParser.js index cc417c7b14..89597a120f 100644 --- a/src/dash/parser/DashParser.js +++ b/src/dash/parser/DashParser.js @@ -29,15 +29,44 @@ * POSSIBILITY OF SUCH DAMAGE. */ import FactoryMaker from '../../core/FactoryMaker'; +import DashConstants from '../constants/DashConstants'; import ObjectIron from './objectiron'; -import X2JS from '../../../externals/xml2json'; -import StringMatcher from './matchers/StringMatcher'; import DurationMatcher from './matchers/DurationMatcher'; import DateTimeMatcher from './matchers/DateTimeMatcher'; import NumericMatcher from './matchers/NumericMatcher'; import LangMatcher from './matchers/LangMatcher'; import RepresentationBaseValuesMap from './maps/RepresentationBaseValuesMap'; import SegmentValuesMap from './maps/SegmentValuesMap'; +import * as tXml from '../../../externals/tXml'; + +// List of node that shall be represented as arrays +const arrayNodes = [ + DashConstants.PERIOD, + DashConstants.BASE_URL, + DashConstants.ADAPTATION_SET, + DashConstants.REPRESENTATION, + DashConstants.CONTENT_PROTECTION, + DashConstants.ROLE, + DashConstants.ACCESSIBILITY, + DashConstants.AUDIO_CHANNEL_CONFIGURATION, + DashConstants.CONTENT_COMPONENT, + DashConstants.ESSENTIAL_PROPERTY, + DashConstants.LABEL, + DashConstants.S, + DashConstants.SEGMENT_URL, + DashConstants.EVENT, + DashConstants.EVENT_STREAM, + DashConstants.LOCATION, + DashConstants.SERVICE_DESCRIPTION, + DashConstants.SUPPLEMENTAL_PROPERTY, + DashConstants.METRICS, + DashConstants.REPORTING, + DashConstants.PATCH_LOCATION, + DashConstants.REPLACE, + DashConstants.ADD, + DashConstants.REMOVE, + DashConstants.UTC_TIMING +]; function DashParser(config) { @@ -48,7 +77,6 @@ function DashParser(config) { let instance, logger, matchers, - converter, objectIron; function setup() { @@ -57,31 +85,15 @@ function DashParser(config) { new DurationMatcher(), new DateTimeMatcher(), new NumericMatcher(), - new LangMatcher(), - new StringMatcher() // last in list to take precedence over NumericMatcher + new LangMatcher() ]; - converter = new X2JS({ - escapeMode: false, - attributePrefix: '', - arrayAccessForm: 'property', - emptyNodeForm: 'object', - stripWhitespaces: false, - enableToStringFunc: true, - ignoreRoot: false, - matchers: matchers - }); - objectIron = ObjectIron(context).create({ adaptationset: new RepresentationBaseValuesMap(), period: new SegmentValuesMap() }); } - function getMatchers() { - return matchers; - } - function getIron() { return objectIron; } @@ -90,23 +102,21 @@ function DashParser(config) { let manifest; const startTime = window.performance.now(); - manifest = converter.xml_str2json(data); + manifest = parseXml(data); if (!manifest) { - throw new Error('parsing the manifest failed'); + throw new Error('failed to parse the manifest'); } - const jsonTime = window.performance.now(); - // handle full MPD and Patch ironing separately if (manifest.Patch) { manifest = manifest.Patch; // drop root reference // apply iron to patch operations individually - if (manifest.add_asArray) { - manifest.add_asArray.forEach((operand) => objectIron.run(operand)); + if (manifest.add) { + manifest.add.forEach((operand) => objectIron.run(operand)); } - if (manifest.replace_asArray) { - manifest.replace_asArray.forEach((operand) => objectIron.run(operand)); + if (manifest.replace) { + manifest.replace.forEach((operand) => objectIron.run(operand)); } // note that we don't need to iron remove as they contain no children } else { @@ -114,18 +124,48 @@ function DashParser(config) { objectIron.run(manifest); } - const ironedTime = window.performance.now(); - logger.info('Parsing complete: ( xml2json: ' + (jsonTime - startTime).toPrecision(3) + 'ms, objectiron: ' + (ironedTime - jsonTime).toPrecision(3) + 'ms, total: ' + ((ironedTime - startTime) / 1000).toPrecision(3) + 's)'); + const parsedTime = window.performance.now(); + logger.info('Parsing complete: ' + (parsedTime - startTime).toPrecision(3) + 'ms'); manifest.protocol = 'DASH'; return manifest; } + + function parseXml(data) { + try { + let root = tXml.parse(data, { + parseNode: true, + attrMatchers: matchers, + nodesAsArray: arrayNodes + }); + let ret = {}; + // If root element is xml node, then get first child node as root + if (root.tagName.toLowerCase().indexOf('xml') !== -1) { + for (let key in root) { + if (Array.isArray(root[key])) { + ret[key] = root[key][0]; + break; + } else if (typeof root[key] === 'object') { + ret[key] = root[key]; + break; + } + } + } else { + ret[root.tagName] = root; + delete root.tagName; + } + return ret; + } catch (e) { + return null; + } + } + instance = { - parse: parse, - getMatchers: getMatchers, - getIron: getIron + getIron: getIron, + parseXml: parseXml, + parse: parse }; setup(); diff --git a/src/dash/parser/matchers/DateTimeMatcher.js b/src/dash/parser/matchers/DateTimeMatcher.js index c62609ae9f..8f446bbdad 100644 --- a/src/dash/parser/matchers/DateTimeMatcher.js +++ b/src/dash/parser/matchers/DateTimeMatcher.js @@ -42,7 +42,7 @@ const datetimeRegex = /^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2})(? class DateTimeMatcher extends BaseMatcher { constructor() { super( - attr => datetimeRegex.test(attr.value), + (tagName, attrName, value) => datetimeRegex.test(value), str => { const match = datetimeRegex.exec(str); let utcDate; diff --git a/src/dash/parser/matchers/DurationMatcher.js b/src/dash/parser/matchers/DurationMatcher.js index 08baf1dbd4..1c48b0840e 100644 --- a/src/dash/parser/matchers/DurationMatcher.js +++ b/src/dash/parser/matchers/DurationMatcher.js @@ -46,7 +46,7 @@ const SECONDS_IN_MIN = 60; class DurationMatcher extends BaseMatcher { constructor() { super( - attr => { + (tagName, attrName, value) => { const attributeList = [ DashConstants.MIN_BUFFER_TIME, DashConstants.MEDIA_PRESENTATION_DURATION, DashConstants.MINIMUM_UPDATE_PERIOD, DashConstants.TIMESHIFT_BUFFER_DEPTH, DashConstants.MAX_SEGMENT_DURATION, @@ -56,8 +56,8 @@ class DurationMatcher extends BaseMatcher { const len = attributeList.length; for (let i = 0; i < len; i++) { - if (attr.nodeName === attributeList[i]) { - return durationRegex.test(attr.value); + if (attrName === attributeList[i]) { + return durationRegex.test(value); } } diff --git a/src/dash/parser/matchers/LangMatcher.js b/src/dash/parser/matchers/LangMatcher.js index 64d40162ac..93a83f605a 100644 --- a/src/dash/parser/matchers/LangMatcher.js +++ b/src/dash/parser/matchers/LangMatcher.js @@ -38,7 +38,7 @@ import bcp47Normalize from 'bcp-47-normalize'; class LangMatcher extends BaseMatcher { constructor() { super( - (attr, nodeName) => { + (tagName, attr/*, value*/) => { const stringAttrsInElements = { [DashConstants.ADAPTATION_SET]: [ DashConstants.LANG ], [DashConstants.REPRESENTATION]: [ DashConstants.LANG ], @@ -47,10 +47,10 @@ class LangMatcher extends BaseMatcher { [DashConstants.GROUP_LABEL]: [ DashConstants.LANG ] // still missing from 23009-1: Preselection@lang, ProgramInformation@lang }; - if (stringAttrsInElements.hasOwnProperty(nodeName)) { - let attrNames = stringAttrsInElements[nodeName]; + if (stringAttrsInElements.hasOwnProperty(tagName)) { + let attrNames = stringAttrsInElements[tagName]; if (attrNames !== undefined) { - return attrNames.indexOf(attr.name) >= 0; + return attrNames.indexOf(attr) >= 0; } else { return false; } diff --git a/src/dash/parser/matchers/NumericMatcher.js b/src/dash/parser/matchers/NumericMatcher.js index 670c56472e..168f4050f7 100644 --- a/src/dash/parser/matchers/NumericMatcher.js +++ b/src/dash/parser/matchers/NumericMatcher.js @@ -32,13 +32,16 @@ * @classdesc Matches and converts xs:numeric to float */ import BaseMatcher from './BaseMatcher'; +import DashConstants from '../../constants/DashConstants'; const numericRegex = /^[-+]?[0-9]+[.]?[0-9]*([eE][-+]?[0-9]+)?$/; +const StringAttributeList = [ DashConstants.ID ] + class NumericMatcher extends BaseMatcher { constructor() { super( - attr => numericRegex.test(attr.value), + (tagName, attrName, value) => numericRegex.test(value) && StringAttributeList.indexOf(attrName) === -1, str => parseFloat(str) ); } diff --git a/src/dash/parser/matchers/StringMatcher.js b/src/dash/parser/matchers/StringMatcher.js deleted file mode 100644 index a1e203cfe1..0000000000 --- a/src/dash/parser/matchers/StringMatcher.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * The copyright in this software is being made available under the BSD License, - * included below. This software may be subject to other third party and contributor - * rights, including patent rights, and no such rights are granted under this license. - * - * Copyright (c) 2013, Dash Industry Forum. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation and/or - * other materials provided with the distribution. - * * Neither the name of Dash Industry Forum nor the names of its - * contributors may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -/** - * @classdesc Matches and converts xs:string to string, but only for specific attributes on specific nodes - */ -import BaseMatcher from './BaseMatcher'; -import DashConstants from '../../constants/DashConstants'; - -class StringMatcher extends BaseMatcher { - constructor() { - super( - (attr, nodeName) => { - const stringAttrsInElements = { - [DashConstants.MPD]: [ DashConstants.ID, DashConstants.PROFILES ], - [DashConstants.PERIOD]: [ DashConstants.ID ], - [DashConstants.BASE_URL]: [ DashConstants.SERVICE_LOCATION, DashConstants.BYTE_RANGE ], - [DashConstants.SEGMENT_BASE]: [ DashConstants.INDEX_RANGE ], - [DashConstants.INITIALIZATION]: [ DashConstants.RANGE ], - [DashConstants.REPRESENTATION_INDEX]: [ DashConstants.RANGE ], - [DashConstants.SEGMENT_LIST]: [ DashConstants.INDEX_RANGE ], - [DashConstants.BITSTREAM_SWITCHING]: [ DashConstants.RANGE ], - [DashConstants.SEGMENT_URL]: [ DashConstants.MEDIA_RANGE, DashConstants.INDEX_RANGE ], - [DashConstants.SEGMENT_TEMPLATE]: [ DashConstants.INDEX_RANGE, DashConstants.MEDIA, DashConstants.INDEX, DashConstants.INITIALIZATION_MINUS, DashConstants.BITSTREAM_SWITCHING_MINUS ], - [DashConstants.ASSET_IDENTIFIER]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.EVENT_STREAM]: [ DashConstants.VALUE ], - [DashConstants.ADAPTATION_SET]: [ DashConstants.PROFILES, DashConstants.MIME_TYPE, DashConstants.SEGMENT_PROFILES, DashConstants.CODECS, DashConstants.CONTENT_TYPE ], - [DashConstants.FRAME_PACKING]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.AUDIO_CHANNEL_CONFIGURATION]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.CONTENT_PROTECTION]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.ESSENTIAL_PROPERTY]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.SUPPLEMENTAL_PROPERTY]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.INBAND_EVENT_STREAM]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.ACCESSIBILITY]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.ROLE]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.RATING]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.VIEWPOINT]: [ DashConstants.VALUE, DashConstants.ID ], - [DashConstants.CONTENT_COMPONENT]: [ DashConstants.CONTENT_TYPE ], - [DashConstants.REPRESENTATION]: [ DashConstants.ID, DashConstants.DEPENDENCY_ID, DashConstants.MEDIA_STREAM_STRUCTURE_ID ], - [DashConstants.SUBSET]: [ DashConstants.ID ], - [DashConstants.METRICS]: [ DashConstants.METRICS_MINUS ], - [DashConstants.REPORTING]: [ DashConstants.VALUE, DashConstants.ID ] - }; - if (stringAttrsInElements.hasOwnProperty(nodeName)) { - let attrNames = stringAttrsInElements[nodeName]; - if (attrNames !== undefined) { - return attrNames.indexOf(attr.name) >= 0; - } else { - return false; - } - } - return false; - }, - str => String(str) - ); - } -} - -export default StringMatcher; diff --git a/src/dash/parser/objectiron.js b/src/dash/parser/objectiron.js index 62403e1893..075033fdf5 100644 --- a/src/dash/parser/objectiron.js +++ b/src/dash/parser/objectiron.js @@ -72,7 +72,7 @@ function ObjectIron(mappers) { for (let i = 0, len = item.children.length; i < len; ++i) { const childItem = item.children[i]; - const array = node[childItem.name + '_asArray']; + const array = node[childItem.name]; if (array) { for (let v = 0, len2 = array.length; v < len2; ++v) { const childNode = array[v]; @@ -89,15 +89,15 @@ function ObjectIron(mappers) { return source; } - if (source.Period_asArray && 'period' in mappers) { + if (source.Period && 'period' in mappers) { const periodMapper = mappers.period; - const periods = source.Period_asArray; + const periods = source.Period; for (let i = 0, len = periods.length; i < len; ++i) { const period = periods[i]; mapItem(periodMapper, period); if ('adaptationset' in mappers) { - const adaptationSets = period.AdaptationSet_asArray; + const adaptationSets = period.AdaptationSet; if (adaptationSets) { const adaptationSetMapper = mappers.adaptationset; for (let i = 0, len = adaptationSets.length; i < len; ++i) { diff --git a/src/dash/utils/ListSegmentsGetter.js b/src/dash/utils/ListSegmentsGetter.js index 973c047eeb..e5b88ee5ca 100644 --- a/src/dash/utils/ListSegmentsGetter.js +++ b/src/dash/utils/ListSegmentsGetter.js @@ -54,11 +54,11 @@ function ListSegmentsGetter(config, isDynamic) { return mediaFinishedInformation; } - const list = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentList; + const list = representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index].AdaptationSet[representation.adaptation.index].Representation[representation.index].SegmentList; const startNumber = representation && !isNaN(representation.startNumber) ? representation.startNumber : 1; const offset = Math.max(startNumber - 1, 0); - mediaFinishedInformation.numberOfSegments = offset + list.SegmentURL_asArray.length; + mediaFinishedInformation.numberOfSegments = offset + list.SegmentURL.length; return mediaFinishedInformation } @@ -70,8 +70,8 @@ function ListSegmentsGetter(config, isDynamic) { return null; } - const list = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentList; - const len = list.SegmentURL_asArray.length; + const list = representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index].AdaptationSet[representation.adaptation.index].Representation[representation.index].SegmentList; + const len = list.SegmentURL.length; const startNumber = representation && !isNaN(representation.startNumber) ? representation.startNumber : 1; const offsetToSubtract = Math.max(startNumber - 1, 0); @@ -80,7 +80,7 @@ function ListSegmentsGetter(config, isDynamic) { let segment = null; if (relativeIndex < len) { - const s = list.SegmentURL_asArray[relativeIndex]; + const s = list.SegmentURL[relativeIndex]; segment = getIndexBasedSegment(timelineConverter, isDynamic, representation, index); if (segment) { diff --git a/src/dash/utils/TemplateSegmentsGetter.js b/src/dash/utils/TemplateSegmentsGetter.js index 9167e1635c..a759bb3568 100644 --- a/src/dash/utils/TemplateSegmentsGetter.js +++ b/src/dash/utils/TemplateSegmentsGetter.js @@ -69,7 +69,8 @@ function TemplateSegmentsGetter(config, isDynamic) { return null; } - const template = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentTemplate; + const template = representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index]. + AdaptationSet[representation.adaptation.index].Representation[representation.index].SegmentTemplate; // This is the index without @startNumber index = Math.max(index, 0); diff --git a/src/dash/utils/TimelineConverter.js b/src/dash/utils/TimelineConverter.js index 83fe18587f..a523939b5f 100644 --- a/src/dash/utils/TimelineConverter.js +++ b/src/dash/utils/TimelineConverter.js @@ -311,11 +311,11 @@ function TimelineConverter() { } function _calcRangeForTimeline(voRepresentation) { - const adaptation = voRepresentation.adaptation.period.mpd.manifest.Period_asArray[voRepresentation.adaptation.period.index].AdaptationSet_asArray[voRepresentation.adaptation.index]; + const adaptation = voRepresentation.adaptation.period.mpd.manifest.Period[voRepresentation.adaptation.period.index].AdaptationSet[voRepresentation.adaptation.index]; const representation = dashManifestModel.getRepresentationFor(voRepresentation.index, adaptation); const timeline = representation.SegmentTemplate.SegmentTimeline; const timescale = representation.SegmentTemplate.timescale; - const segments = timeline.S_asArray; + const segments = timeline.S; const range = { start: 0, end: 0 }; let d = 0; let segment, diff --git a/src/dash/utils/TimelineSegmentsGetter.js b/src/dash/utils/TimelineSegmentsGetter.js index 3f4c15aca7..394ec47438 100644 --- a/src/dash/utils/TimelineSegmentsGetter.js +++ b/src/dash/utils/TimelineSegmentsGetter.js @@ -53,8 +53,8 @@ function TimelineSegmentsGetter(config, isDynamic) { return 0; } - const base = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentTemplate || - representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentList; + const base = representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index].AdaptationSet[representation.adaptation.index].Representation[representation.index].SegmentTemplate || + representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index].AdaptationSet[representation.adaptation.index].Representation[representation.index].SegmentList; const timeline = base.SegmentTimeline; let time = 0; @@ -70,7 +70,7 @@ function TimelineSegmentsGetter(config, isDynamic) { fTimescale; fTimescale = representation.timescale; - fragments = timeline.S_asArray; + fragments = timeline.S; len = fragments.length; @@ -107,10 +107,12 @@ function TimelineSegmentsGetter(config, isDynamic) { } function iterateSegments(representation, iterFunc) { - const base = representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentTemplate || - representation.adaptation.period.mpd.manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index].Representation_asArray[representation.index].SegmentList; + const base = representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index]. + AdaptationSet[representation.adaptation.index].Representation[representation.index].SegmentTemplate || + representation.adaptation.period.mpd.manifest.Period[representation.adaptation.period.index]. + AdaptationSet[representation.adaptation.index].Representation[representation.index].SegmentList; const timeline = base.SegmentTimeline; - const list = base.SegmentURL_asArray; + const list = base.SegmentURL; let time = 0; let relativeIdx = -1; @@ -124,7 +126,7 @@ function TimelineSegmentsGetter(config, isDynamic) { fTimescale; fTimescale = representation.timescale; - fragments = timeline.S_asArray; + fragments = timeline.S; let breakIterator = false; diff --git a/src/dash/vo/SimpleXPath.js b/src/dash/vo/SimpleXPath.js index 4c9dd77544..5b67d0e8df 100644 --- a/src/dash/vo/SimpleXPath.js +++ b/src/dash/vo/SimpleXPath.js @@ -106,7 +106,7 @@ class SimpleXPath { // stop one early if this is the last element and an attribute if (level !== this.path.length - 1 || !name.startsWith('@')) { - let children = parent[name + '_asArray'] || []; + let children = parent[name] || []; if (children.length === 0 && parent[name]) { children.push(parent[name]); } @@ -117,8 +117,8 @@ class SimpleXPath { let attr = component.attribute; leaf = children.filter((elm) => elm[attr.name] == attr.value)[0] || null; } else { - // default case, select first - leaf = children[0] || null; + // default case, select element itself or first element if as array + leaf = Array.isArray(children) ? children[0] : children; } } diff --git a/src/mss/MssFragmentInfoController.js b/src/mss/MssFragmentInfoController.js index c3ddf19b7e..aa0803de22 100644 --- a/src/mss/MssFragmentInfoController.js +++ b/src/mss/MssFragmentInfoController.js @@ -96,8 +96,8 @@ function MssFragmentInfoController(config) { // Get last segment from SegmentTimeline const representation = getCurrentRepresentation(); const manifest = representation.adaptation.period.mpd.manifest; - const adaptation = manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index]; - const segments = adaptation.SegmentTemplate.SegmentTimeline.S_asArray; + const adaptation = manifest.Period[representation.adaptation.period.index].AdaptationSet[representation.adaptation.index]; + const segments = adaptation.SegmentTemplate.SegmentTimeline.S; const segment = segments[segments.length - 1]; // logger.debug('Last fragment time: ' + (segment.t / adaptation.SegmentTemplate.timescale)); diff --git a/src/mss/MssFragmentMoofProcessor.js b/src/mss/MssFragmentMoofProcessor.js index 61dfd20942..eadd6e5209 100644 --- a/src/mss/MssFragmentMoofProcessor.js +++ b/src/mss/MssFragmentMoofProcessor.js @@ -61,7 +61,7 @@ function MssFragmentMoofProcessor(config) { const representation = representationController.getCurrentRepresentation(); const manifest = representation.adaptation.period.mpd.manifest; - const adaptation = manifest.Period_asArray[representation.adaptation.period.index].AdaptationSet_asArray[representation.adaptation.index]; + const adaptation = manifest.Period[representation.adaptation.period.index].AdaptationSet[representation.adaptation.index]; const timescale = adaptation.SegmentTemplate.timescale; type = streamProcessor.getType(); diff --git a/src/mss/MssFragmentMoovProcessor.js b/src/mss/MssFragmentMoovProcessor.js index 429c5341a2..a4d2787fb6 100644 --- a/src/mss/MssFragmentMoovProcessor.js +++ b/src/mss/MssFragmentMoovProcessor.js @@ -632,9 +632,9 @@ function MssFragmentMoovProcessor(config) { period = adaptationSet.period; trackId = adaptationSet.index + 1; - contentProtection = period.mpd.manifest.Period_asArray[period.index].AdaptationSet_asArray[adaptationSet.index].ContentProtection; + contentProtection = period.mpd.manifest.Period[period.index].AdaptationSet[adaptationSet.index].ContentProtection; - timescale = period.mpd.manifest.Period_asArray[period.index].AdaptationSet_asArray[adaptationSet.index].SegmentTemplate.timescale; + timescale = period.mpd.manifest.Period[period.index].AdaptationSet[adaptationSet.index].SegmentTemplate.timescale; isoFile = ISOBoxer.createFile(); createFtypBox(isoFile); diff --git a/src/mss/parser/MssParser.js b/src/mss/parser/MssParser.js index c6f34e5c8d..81e8413f0a 100644 --- a/src/mss/parser/MssParser.js +++ b/src/mss/parser/MssParser.js @@ -98,19 +98,15 @@ function MssParser(config) { adaptation; // For each StreamIndex node, create an AdaptationSet element - period.AdaptationSet_asArray = []; + period.AdaptationSet = []; streams = smoothStreamingMedia.getElementsByTagName('StreamIndex'); for (let i = 0; i < streams.length; i++) { adaptation = mapAdaptationSet(streams[i], timescale); if (adaptation !== null) { - period.AdaptationSet_asArray.push(adaptation); + period.AdaptationSet.push(adaptation); } } - if (period.AdaptationSet_asArray.length > 0) { - period.AdaptationSet = (period.AdaptationSet_asArray.length > 1) ? period.AdaptationSet_asArray : period.AdaptationSet_asArray[0]; - } - return period; } @@ -139,20 +135,16 @@ function MssParser(config) { // Map text tracks subTypes to MPEG-DASH AdaptationSet role and accessibility (see ETSI TS 103 285 v1.1.1, section 7.1.2) if (adaptationSet.subType) { if (ROLE[adaptationSet.subType]) { - let role = { + adaptationSet.Role = [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: ROLE[adaptationSet.subType] - }; - adaptationSet.Role = role; - adaptationSet.Role_asArray = [role]; + }]; } if (ACCESSIBILITY[adaptationSet.subType]) { - let accessibility = { + adaptationSet.Accessibility = [{ schemeIdUri: 'urn:tva:metadata:cs:AudioPurposeCS:2007', value: ACCESSIBILITY[adaptationSet.subType] - }; - adaptationSet.Accessibility = accessibility; - adaptationSet.Accessibility_asArray = [accessibility]; + }]; } } @@ -185,8 +177,7 @@ function MssParser(config) { return null; } - adaptationSet.Representation = (representations.length > 1) ? representations : representations[0]; - adaptationSet.Representation_asArray = representations; + adaptationSet.Representation = representations; // Set SegmentTemplate adaptationSet.SegmentTemplate = segmentTemplate; @@ -434,7 +425,6 @@ function MssParser(config) { } segmentTimeline.S = segments; - segmentTimeline.S_asArray = segments; segmentTimeline.duration = duration / timescale; return segmentTimeline; @@ -536,8 +526,7 @@ function MssParser(config) { return { schemeIdUri: 'urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95', value: 'com.microsoft.playready', - pro: pro, - pro_asArray: pro + pro: pro }; } @@ -650,11 +639,10 @@ function MssParser(config) { } // Map period node to manifest root node - manifest.Period = mapPeriod(smoothStreamingMedia, manifest.timescale); - manifest.Period_asArray = [manifest.Period]; + period = mapPeriod(smoothStreamingMedia, manifest.timescale); + manifest.Period = [period]; // Initialize period start time - period = manifest.Period; period.start = 0; // Uncomment to test live to static manifests @@ -686,22 +674,21 @@ function MssParser(config) { contentProtections.push(contentProtection); manifest.ContentProtection = contentProtections; - manifest.ContentProtection_asArray = contentProtections; } - adaptations = period.AdaptationSet_asArray; + adaptations = period.AdaptationSet; for (i = 0; i < adaptations.length; i += 1) { adaptations[i].SegmentTemplate.initialization = '$Bandwidth$'; // Propagate content protection information into each adaptation if (manifest.ContentProtection !== undefined) { adaptations[i].ContentProtection = manifest.ContentProtection; - adaptations[i].ContentProtection_asArray = manifest.ContentProtection_asArray; + adaptations[i].ContentProtection = manifest.ContentProtection; } if (adaptations[i].contentType === 'video') { // Get video segment duration - segmentDuration = adaptations[i].SegmentTemplate.SegmentTimeline.S_asArray[0].d / adaptations[i].SegmentTemplate.timescale; + segmentDuration = adaptations[i].SegmentTemplate.SegmentTimeline.S[0].d / adaptations[i].SegmentTemplate.timescale; // Set minBufferTime to one segment duration manifest.minBufferTime = segmentDuration; @@ -770,7 +757,6 @@ function MssParser(config) { // Delete Content Protection under root manifest node delete manifest.ContentProtection; - delete manifest.ContentProtection_asArray; // In case of VOD streams, check if start time is greater than 0 // Then determine timestamp offset according to higher audio/video start time @@ -784,7 +770,7 @@ function MssParser(config) { } else { for (i = 0; i < adaptations.length; i++) { if (adaptations[i].contentType === constants.AUDIO || adaptations[i].contentType === constants.VIDEO) { - segments = adaptations[i].SegmentTemplate.SegmentTimeline.S_asArray; + segments = adaptations[i].SegmentTemplate.SegmentTimeline.S; startTime = segments[0].t; if (timestampOffset === undefined) { timestampOffset = startTime; @@ -800,7 +786,7 @@ function MssParser(config) { // Patch segment templates timestamps and determine period start time (since audio/video should not be aligned to 0) manifest.timestampOffset = timestampOffset; for (i = 0; i < adaptations.length; i++) { - segments = adaptations[i].SegmentTemplate.SegmentTimeline.S_asArray; + segments = adaptations[i].SegmentTemplate.SegmentTimeline.S; for (j = 0; j < segments.length; j++) { if (!segments[j].tManifest) { segments[j].tManifest = segments[j].t.toString(); @@ -839,10 +825,6 @@ function MssParser(config) { return xmlDoc; } - function getMatchers() { - return null; - } - function getIron() { return null; } @@ -881,7 +863,6 @@ function MssParser(config) { instance = { parse: internalParse, - getMatchers: getMatchers, getIron: getIron, reset: reset }; diff --git a/src/offline/OfflineDownload.js b/src/offline/OfflineDownload.js index 99defbb441..74e6f90bfe 100644 --- a/src/offline/OfflineDownload.js +++ b/src/offline/OfflineDownload.js @@ -411,7 +411,7 @@ function OfflineDownload(config) { return; } - if (_manifest.Period_asArray.length > 1) { + if (_manifest.Period.length > 1) { _status = OfflineConstants.OFFLINE_STATUS_ERROR; errHandler.error({ code: OfflineErrors.OFFLINE_ERROR, diff --git a/src/streaming/ManifestLoader.js b/src/streaming/ManifestLoader.js index d6a07b97e1..de5b113eef 100644 --- a/src/streaming/ManifestLoader.js +++ b/src/streaming/ManifestLoader.js @@ -164,9 +164,8 @@ function ManifestLoader(config) { return; } - // init xlinkcontroller with matchers and iron object from created parser - xlinkController.setMatchers(parser.getMatchers()); - xlinkController.setIron(parser.getIron()); + // init xlinkcontroller with created parser + xlinkController.setParser(parser); try { manifest = parser.parse(data); @@ -192,7 +191,7 @@ function ManifestLoader(config) { // In the following, we only use the first Location entry even if many are available // Compare with ManifestUpdater/DashManifestModel if (manifest.hasOwnProperty(Constants.LOCATION)) { - baseUri = urlUtils.parseBaseUrl(manifest.Location_asArray[0]); + baseUri = urlUtils.parseBaseUrl(manifest.Location[0]); logger.debug('BaseURI set by Location to: ' + baseUri); } @@ -202,8 +201,8 @@ function ManifestLoader(config) { if (settings && settings.get().streaming.enableManifestDurationMismatchFix && manifest.mediaPresentationDuration && - manifest.Period_asArray.length > 1) { - const sumPeriodDurations = manifest.Period_asArray.reduce((totalDuration, period) => totalDuration + period.duration, 0); + manifest.Period.length > 1) { + const sumPeriodDurations = manifest.Period.reduce((totalDuration, period) => totalDuration + period.duration, 0); if (!isNaN(sumPeriodDurations) && manifest.mediaPresentationDuration > sumPeriodDurations) { logger.warn('Media presentation duration greater than duration of all periods. Setting duration to total period duration'); manifest.mediaPresentationDuration = sumPeriodDurations; diff --git a/src/streaming/Stream.js b/src/streaming/Stream.js index 5e6d5425ca..4b101805c6 100644 --- a/src/streaming/Stream.js +++ b/src/streaming/Stream.js @@ -956,11 +956,11 @@ function Stream(config) { } const sameMimeType = newAdaptation && currentAdaptation && newAdaptation.mimeType === currentAdaptation.mimeType; - const oldCodecs = currentAdaptation.Representation_asArray.map((representation) => { + const oldCodecs = currentAdaptation.Representation.map((representation) => { return representation.codecs; }); - const newCodecs = newAdaptation.Representation_asArray.map((representation) => { + const newCodecs = newAdaptation.Representation.map((representation) => { return representation.codecs; }); diff --git a/src/streaming/controllers/AbrController.js b/src/streaming/controllers/AbrController.js index 3bf7ff9009..6bf9bd5212 100644 --- a/src/streaming/controllers/AbrController.js +++ b/src/streaming/controllers/AbrController.js @@ -496,7 +496,7 @@ function AbrController() { setElementSize(); } const streamInfo = streamProcessorDict[streamId][type].getStreamInfo(); - const representation = adapter.getAdaptationForType(streamInfo.index, type, streamInfo).Representation_asArray; + const representation = adapter.getAdaptationForType(streamInfo.index, type, streamInfo).Representation; let newIdx = idx; if (elementWidth > 0 && elementHeight > 0) { @@ -557,7 +557,7 @@ function AbrController() { if (configBitrate === -1) { if (configRatio > -1) { const streamInfo = streamProcessorDict[streamId][type].getStreamInfo(); - const representation = adapter.getAdaptationForType(streamInfo.index, type, streamInfo).Representation_asArray; + const representation = adapter.getAdaptationForType(streamInfo.index, type, streamInfo).Representation; if (Array.isArray(representation)) { const repIdx = Math.max(Math.round(representation.length * configRatio) - 1, 0); configBitrate = representation[repIdx].bandwidth / 1000; diff --git a/src/streaming/controllers/XlinkController.js b/src/streaming/controllers/XlinkController.js index 79dddad40e..af0b8ce10f 100644 --- a/src/streaming/controllers/XlinkController.js +++ b/src/streaming/controllers/XlinkController.js @@ -32,7 +32,6 @@ import XlinkLoader from '../XlinkLoader'; import EventBus from '../../core/EventBus'; import Events from '../../core/events/Events'; import FactoryMaker from '../../core/FactoryMaker'; -import X2JS from '../../../externals/xml2json'; import URLUtils from '../utils/URLUtils'; import DashConstants from '../../dash/constants/DashConstants'; @@ -48,10 +47,8 @@ function XlinkController(config) { const urlUtils = URLUtils(context).getInstance(); let instance, - matchers, - iron, + parser, manifest, - converter, xlinkLoader; function setup() { @@ -66,15 +63,9 @@ function XlinkController(config) { }); } - function setMatchers(value) { + function setParser(value) { if (value) { - matchers = value; - } - } - - function setIron(value) { - if (value) { - iron = value; + parser = value; } } @@ -85,21 +76,10 @@ function XlinkController(config) { function resolveManifestOnLoad(mpd) { let elements; // First resolve all periods, so unnecessary requests inside onLoad Periods with Default content are avoided - converter = new X2JS({ - escapeMode: false, - attributePrefix: '', - arrayAccessForm: 'property', - emptyNodeForm: 'object', - stripWhitespaces: false, - enableToStringFunc: false, - ignoreRoot: true, - matchers: matchers - }); - manifest = mpd; - if (manifest.Period_asArray) { - elements = getElementsToResolve(manifest.Period_asArray, manifest, DashConstants.PERIOD, RESOLVE_TYPE_ONLOAD); + if (manifest.Period) { + elements = getElementsToResolve(manifest.Period, manifest, DashConstants.PERIOD, RESOLVE_TYPE_ONLOAD); resolve(elements, DashConstants.PERIOD, RESOLVE_TYPE_ONLOAD); } else { eventBus.trigger(Events.XLINK_READY, {manifest: manifest}); @@ -156,7 +136,7 @@ function XlinkController(config) { index = element.resolvedContent.indexOf('?>') + 2; //find the closing position of the xml declaration, if it exists. } mergedContent = element.resolvedContent.substr(0,index) + openingTag + element.resolvedContent.substr(index) + closingTag; - element.resolvedContent = converter.xml_str2json(mergedContent); + element.resolvedContent = parser.parseXml(mergedContent).response; } if (isResolvingFinished(resolveObject)) { onXlinkAllElementsLoaded(resolveObject); @@ -177,13 +157,13 @@ function XlinkController(config) { switch (resolveObject.type) { // Start resolving the other elements. We can do Adaptation Set and EventStream in parallel case DashConstants.PERIOD: - for (i = 0; i < manifest[DashConstants.PERIOD + '_asArray'].length; i++) { - obj = manifest[DashConstants.PERIOD + '_asArray'][i]; - if (obj.hasOwnProperty(DashConstants.ADAPTATION_SET + '_asArray')) { - elements = elements.concat(getElementsToResolve(obj[DashConstants.ADAPTATION_SET + '_asArray'], obj, DashConstants.ADAPTATION_SET, RESOLVE_TYPE_ONLOAD)); + for (i = 0; i < manifest[DashConstants.PERIOD].length; i++) { + obj = manifest[DashConstants.PERIOD][i]; + if (obj.hasOwnProperty(DashConstants.ADAPTATION_SET)) { + elements = elements.concat(getElementsToResolve(obj[DashConstants.ADAPTATION_SET], obj, DashConstants.ADAPTATION_SET, RESOLVE_TYPE_ONLOAD)); } - if (obj.hasOwnProperty(DashConstants.EVENT_STREAM + '_asArray')) { - elements = elements.concat(getElementsToResolve(obj[DashConstants.EVENT_STREAM + '_asArray'], obj, DashConstants.EVENT_STREAM, RESOLVE_TYPE_ONLOAD)); + if (obj.hasOwnProperty(DashConstants.EVENT_STREAM)) { + elements = elements.concat(getElementsToResolve(obj[DashConstants.EVENT_STREAM], obj, DashConstants.EVENT_STREAM, RESOLVE_TYPE_ONLOAD)); } } resolve(elements, DashConstants.ADAPTATION_SET, RESOLVE_TYPE_ONLOAD); @@ -231,7 +211,7 @@ function XlinkController(config) { // Start merging back from the end because of index shifting. Note that the elements with the same parent have to be ordered by index ascending for (i = resolveObject.elements.length - 1; i >= 0; i --) { element = resolveObject.elements[i]; - type = element.type + '_asArray'; + type = element.type; // Element couldn't be resolved or is TODO Inappropriate target: Remove all Xlink attributes if (!element.resolvedContent || isInappropriateTarget()) { @@ -255,7 +235,7 @@ function XlinkController(config) { resolvedElements = []; } if (resolveObject.elements.length > 0) { - iron.run(manifest); + parser.getIron().run(manifest); } } @@ -292,8 +272,7 @@ function XlinkController(config) { instance = { resolveManifestOnLoad: resolveManifestOnLoad, - setMatchers: setMatchers, - setIron: setIron, + setParser: setParser, reset: reset }; diff --git a/src/streaming/metrics/utils/ManifestParsing.js b/src/streaming/metrics/utils/ManifestParsing.js index 6f507be873..84037106ba 100644 --- a/src/streaming/metrics/utils/ManifestParsing.js +++ b/src/streaming/metrics/utils/ManifestParsing.js @@ -45,8 +45,8 @@ function ManifestParsing (config) { function getMetrics(manifest) { let metrics = []; - if (manifest && manifest.Metrics_asArray) { - manifest.Metrics_asArray.forEach(metric => { + if (manifest && manifest.Metrics) { + manifest.Metrics.forEach(metric => { var metricEntry = new Metrics(); var isDynamic = adapter.getIsDynamic(manifest); @@ -56,8 +56,8 @@ function ManifestParsing (config) { return; } - if (metric.Range_asArray) { - metric.Range_asArray.forEach(range => { + if (metric.Range) { + metric.Range.forEach(range => { var rangeEntry = new Range(); rangeEntry.starttime = @@ -77,8 +77,8 @@ function ManifestParsing (config) { }); } - if (metric.Reporting_asArray) { - metric.Reporting_asArray.forEach(reporting => { + if (metric.Reporting) { + metric.Reporting.forEach(reporting => { var reportingEntry = new Reporting(); if (reporting.hasOwnProperty(constants.SCHEME_ID_URI)) { @@ -122,4 +122,4 @@ function ManifestParsing (config) { } ManifestParsing.__dashjs_factory_name = 'ManifestParsing'; -export default dashjs.FactoryMaker.getSingletonFactory(ManifestParsing); /* jshint ignore:line */ \ No newline at end of file +export default dashjs.FactoryMaker.getSingletonFactory(ManifestParsing); /* jshint ignore:line */ diff --git a/src/streaming/models/BaseURLTreeModel.js b/src/streaming/models/BaseURLTreeModel.js index 0ede7b9716..e789a08841 100644 --- a/src/streaming/models/BaseURLTreeModel.js +++ b/src/streaming/models/BaseURLTreeModel.js @@ -90,16 +90,16 @@ function BaseURLTreeModel() { root.data.selectedIdx = DEFAULT_INDEX; } - if (manifest && manifest.Period_asArray) { - manifest.Period_asArray.forEach((p, pi) => { + if (manifest && manifest.Period) { + manifest.Period.forEach((p, pi) => { updateChildData(root.children, pi, p); - if (p.AdaptationSet_asArray) { - p.AdaptationSet_asArray.forEach((a, ai) => { + if (p.AdaptationSet) { + p.AdaptationSet.forEach((a, ai) => { updateChildData(root.children[pi].children, ai, a); - if (a.Representation_asArray) { - a.Representation_asArray.sort( + if (a.Representation) { + a.Representation.sort( adapter.getRepresentationSortFunction() ).forEach((r, ri) => { updateChildData( diff --git a/src/streaming/utils/CapabilitiesFilter.js b/src/streaming/utils/CapabilitiesFilter.js index 973119c74f..cfa819c04a 100644 --- a/src/streaming/utils/CapabilitiesFilter.js +++ b/src/streaming/utils/CapabilitiesFilter.js @@ -65,14 +65,13 @@ function CapabilitiesFilter() { }); } - function _filterUnsupportedCodecs(type, manifest) { - if (!manifest || !manifest.Period_asArray || manifest.Period_asArray.length === 0) { + if (!manifest || !manifest.Period || manifest.Period.length === 0) { return Promise.resolve(); } const promises = []; - manifest.Period_asArray.forEach((period) => { + manifest.Period.forEach((period) => { promises.push(_filterUnsupportedAdaptationSetsOfPeriod(period, type)); }); @@ -82,13 +81,13 @@ function CapabilitiesFilter() { function _filterUnsupportedAdaptationSetsOfPeriod(period, type) { return new Promise((resolve) => { - if (!period || !period.AdaptationSet_asArray || period.AdaptationSet_asArray.length === 0) { + if (!period || !period.AdaptationSet || period.AdaptationSet.length === 0) { resolve(); return; } const promises = []; - period.AdaptationSet_asArray.forEach((as) => { + period.AdaptationSet.forEach((as) => { if (adapter.getIsTypeOf(as, type)) { promises.push(_filterUnsupportedRepresentationsOfAdaptation(as, type)); } @@ -96,9 +95,8 @@ function CapabilitiesFilter() { Promise.all(promises) .then(() => { - period.AdaptationSet_asArray = period.AdaptationSet_asArray.filter((as) => { - const supported = as.Representation_asArray && as.Representation_asArray.length > 0; - + period.AdaptationSet = period.AdaptationSet.filter((as) => { + const supported = as.Representation && as.Representation.length > 0; if (!supported) { eventBus.trigger(Events.ADAPTATION_SET_REMOVED_NO_CAPABILITIES, { adaptationSet: as @@ -108,7 +106,6 @@ function CapabilitiesFilter() { return supported; }); - resolve(); }) .catch(() => { @@ -121,7 +118,7 @@ function CapabilitiesFilter() { function _filterUnsupportedRepresentationsOfAdaptation(as, type) { return new Promise((resolve) => { - if (!as.Representation_asArray || as.Representation_asArray.length === 0) { + if (!as.Representation || as.Representation.length === 0) { resolve(); return; } @@ -129,7 +126,7 @@ function CapabilitiesFilter() { const promises = []; const configurations = []; - as.Representation_asArray.forEach((rep, i) => { + as.Representation.forEach((rep, i) => { const codec = adapter.getCodec(as, i, false); const config = _createConfiguration(type, rep, codec); @@ -139,7 +136,7 @@ function CapabilitiesFilter() { Promise.all(promises) .then((supported) => { - as.Representation_asArray = as.Representation_asArray.filter((_, i) => { + as.Representation = as.Representation.filter((_, i) => { if (!supported[i]) { logger.debug(`[Stream] Codec ${configurations[i].codec} not supported `); } @@ -193,18 +190,18 @@ function CapabilitiesFilter() { function _filterUnsupportedEssentialProperties(manifest) { - if (!manifest || !manifest.Period_asArray || manifest.Period_asArray.length === 0) { + if (!manifest || !manifest.Period || manifest.Period.length === 0) { return; } - manifest.Period_asArray.forEach((period) => { - period.AdaptationSet_asArray = period.AdaptationSet_asArray.filter((as) => { + manifest.Period.forEach((period) => { + period.AdaptationSet = period.AdaptationSet.filter((as) => { - if (!as.Representation_asArray || as.Representation_asArray.length === 0) { + if (!as.Representation || as.Representation.length === 0) { return true; } - as.Representation_asArray = as.Representation_asArray.filter((rep) => { + as.Representation = as.Representation.filter((rep) => { const essentialProperties = adapter.getEssentialPropertiesForRepresentation(rep); if (essentialProperties && essentialProperties.length > 0) { @@ -221,7 +218,7 @@ function CapabilitiesFilter() { return true; }); - return as.Representation_asArray && as.Representation_asArray.length > 0; + return as.Representation && as.Representation.length > 0; }); }); @@ -229,22 +226,22 @@ function CapabilitiesFilter() { function _applyCustomFilters(manifest) { const customCapabilitiesFilters = customParametersModel.getCustomCapabilitiesFilters(); - if (!customCapabilitiesFilters || customCapabilitiesFilters.length === 0 || !manifest || !manifest.Period_asArray || manifest.Period_asArray.length === 0) { + if (!customCapabilitiesFilters || customCapabilitiesFilters.length === 0 || !manifest || !manifest.Period || manifest.Period.length === 0) { return; } - manifest.Period_asArray.forEach((period) => { - period.AdaptationSet_asArray = period.AdaptationSet_asArray.filter((as) => { + manifest.Period.forEach((period) => { + period.AdaptationSet = period.AdaptationSet.filter((as) => { - if (!as.Representation_asArray || as.Representation_asArray.length === 0) { + if (!as.Representation || as.Representation.length === 0) { return true; } - as.Representation_asArray = as.Representation_asArray.filter((representation) => { + as.Representation = as.Representation.filter((representation) => { return !customCapabilitiesFilters.some(customFilter => !customFilter(representation)); }); - return as.Representation_asArray && as.Representation_asArray.length > 0; + return as.Representation && as.Representation.length > 0; }); }); } diff --git a/test/unit/dash.DashAdapter.js b/test/unit/dash.DashAdapter.js index c451be8479..2d45aaffb4 100644 --- a/test/unit/dash.DashAdapter.js +++ b/test/unit/dash.DashAdapter.js @@ -17,49 +17,54 @@ const errorHandlerMock = new ErrorHandlerMock(); const manifest_with_audio = { loadedTime: new Date(), mediaPresentationDuration: 10, - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ id: undefined, mimeType: Constants.AUDIO, lang: 'eng', - Role_asArray: [{ value: 'main' }] - }, { id: undefined, mimeType: Constants.AUDIO, lang: 'deu', Role_asArray: [{ value: 'main' }] }] + Role: [{ value: 'main' }] + }, { + id: undefined, + mimeType: Constants.AUDIO, + lang: 'deu', + Role: [{ value: 'main' }] + }] }] }; const manifest_with_video_with_embedded_subtitles = { loadedTime: new Date(), mediaPresentationDuration: 10, - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ id: 0, mimeType: Constants.VIDEO, - Accessibility: { schemeIdUri: 'urn:scte:dash:cc:cea-608:2015', value: 'CC1=eng;CC3=swe' }, - Accessibility_asArray: [{ schemeIdUri: 'urn:scte:dash:cc:cea-608:2015', value: 'CC1=eng;CC3=swe' }] - }, { id: 1, mimeType: Constants.VIDEO }] + Accessibility: [{ schemeIdUri: 'urn:scte:dash:cc:cea-608:2015', value: 'CC1=eng;CC3=swe' }] + }, { + id: 1, + mimeType: Constants.VIDEO + }] }] }; const manifest_with_ll_service_description = { loadedTime: new Date(), mediaPresentationDuration: 10, - ServiceDescription: {}, - ServiceDescription_asArray: [{ + ServiceDescription: [{ Scope: { schemeIdUri: 'urn:dvb:dash:lowlatency:scope:2019' }, Latency: { target: 3000, max: 5000, min: 2000, referenceId: 7 }, PlaybackRate: { max: 1.5, min: 0.5 } }], - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ id: 0, mimeType: Constants.VIDEO, - SupplementalProperty: {}, - SupplementalProperty_asArray: [{ schemeIdUri: 'urn:dvb:dash:lowlatency:critical:2019', value: 'true' }] + SupplementalProperty: [{ schemeIdUri: 'urn:dvb:dash:lowlatency:critical:2019', value: 'true' }] }] }] }; const manifest_without_supplemental_properties = { loadedTime: new Date(), mediaPresentationDuration: 10, - Period_asArray: [{ AdaptationSet_asArray: [{ id: 0, mimeType: Constants.VIDEO }] }] + Period: [{ AdaptationSet: [{ id: 0, mimeType: Constants.VIDEO }] }] }; @@ -322,8 +327,8 @@ describe('DashAdapter', function () { const manifest_with_video = { loadedTime: new Date(), mediaPresentationDuration: 10, - Period_asArray: [{ - AdaptationSet_asArray: [{ id: 0, mimeType: Constants.VIDEO }, { + Period: [{ + AdaptationSet: [{ id: 0, mimeType: Constants.VIDEO }, { id: 1, mimeType: Constants.VIDEO }] @@ -524,8 +529,7 @@ describe('DashAdapter', function () { publishTime.setMinutes(publishTime.getMinutes() - 1); const manifest = { [DashConstants.PUBLISH_TIME]: (publishTime.toISOString()), - PatchLocation: patchLocationElementTTL, - PatchLocation_asArray: [patchLocationElementTTL] + PatchLocation: [patchLocationElementTTL] }; let patchLocation = dashAdapter.getPatchLocation(manifest); @@ -538,8 +542,7 @@ describe('DashAdapter', function () { publishTime.setMinutes(publishTime.getMinutes() - 10); const manifest = { [DashConstants.PUBLISH_TIME]: (publishTime.toISOString()), - PatchLocation: patchLocationElementTTL, - PatchLocation_asArray: [patchLocationElementTTL] + PatchLocation: [patchLocationElementTTL] }; let patchLocation = dashAdapter.getPatchLocation(manifest); @@ -552,8 +555,7 @@ describe('DashAdapter', function () { publishTime.setMinutes(publishTime.getMinutes() - 120); const manifest = { [DashConstants.PUBLISH_TIME]: (publishTime.toISOString()), - PatchLocation: patchLocationElementEvergreen, - PatchLocation_asArray: [patchLocationElementEvergreen] + PatchLocation: [patchLocationElementEvergreen] }; let patchLocation = dashAdapter.getPatchLocation(manifest); @@ -571,8 +573,7 @@ describe('DashAdapter', function () { it('should not provide patch location if present in manifest without publish time', function () { const manifest = { - PatchLocation: patchLocationElementTTL, - PatchLocation_asArray: [patchLocationElementTTL] + PatchLocation: [patchLocationElementTTL] }; let patchLocation = dashAdapter.getPatchLocation(manifest); @@ -798,16 +799,14 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); - expect(manifest.Period).to.equal(addedPeriod); - expect(manifest.Period_asArray).to.deep.equal([addedPeriod]); + expect(manifest.Period).to.deep.equal([addedPeriod]); }); it('applies add operation to structure with single sibling', function () { let originalPeriod = {id: 'foo'}; let addedPeriod = {id: 'bar'}; - // special case x2js object which omits the _asArray variant let manifest = { - Period: originalPeriod + Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', @@ -820,15 +819,13 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest.Period).to.deep.equal([originalPeriod, addedPeriod]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('applies add implicit append operation with siblings', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}]; let addedPeriod = {id: 'baz'}; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', @@ -841,15 +838,13 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest.Period).to.deep.equal([originalPeriods[0], originalPeriods[1], addedPeriod]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('applies add prepend operation with siblings', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}]; let addedPeriod = {id: 'baz'}; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', @@ -863,15 +858,13 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest.Period).to.deep.equal([addedPeriod, originalPeriods[0], originalPeriods[1]]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('applies add before operation with siblings', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}, {id: 'baz'}]; let addedPeriod = {id: 'qux'}; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', @@ -885,15 +878,13 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest.Period).to.deep.equal([originalPeriods[0], addedPeriod, originalPeriods[1], originalPeriods[2]]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('applies add after operation with siblings', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}, {id: 'baz'}]; let addedPeriod = {id: 'qux'}; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', @@ -907,14 +898,12 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest.Period).to.deep.equal([originalPeriods[0], originalPeriods[1], addedPeriod, originalPeriods[2]]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('applies add attribute operation', function () { let originalPeriod = {}; let manifest = { - Period: originalPeriod, - Period_asArray: [originalPeriod] + Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', @@ -931,8 +920,7 @@ describe('DashAdapter', function () { it('applies add attribute operation on existing attribute, should act as replace', function () { let originalPeriod = {id: 'foo'}; let manifest = { - Period: originalPeriod, - Period_asArray: [originalPeriod] + Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', @@ -950,8 +938,7 @@ describe('DashAdapter', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}, {id: 'baz'}]; let replacementPeriod = {id: 'qux'}; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'replace', @@ -961,18 +948,17 @@ describe('DashAdapter', function () { }] }]); + console.log('applies replace operation with siblings'); dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest.Period).to.deep.equal([originalPeriods[0], replacementPeriod, originalPeriods[2]]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('applies replace operation without siblings', function () { let originalPeriod = {id: 'foo'}; let replacementPeriod = {id: 'bar'}; let manifest = { - Period: originalPeriod, - Period_asArray: [originalPeriod] + Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'replace', @@ -984,15 +970,13 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); - expect(manifest.Period).to.deep.equal(replacementPeriod); - expect(manifest.Period_asArray).to.deep.equal([replacementPeriod]); + expect(manifest.Period).to.deep.equal([replacementPeriod]); }); it('applies replace operation to attribute', function () { let originalPeriod = {id: 'foo'}; let manifest = { - Period: originalPeriod, - Period_asArray: [originalPeriod] + Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'replace', @@ -1008,8 +992,7 @@ describe('DashAdapter', function () { it('applies remove operation leaving multiple siblings', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}, {id: 'baz'}]; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'remove', @@ -1019,14 +1002,12 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest.Period).to.deep.equal([originalPeriods[0], originalPeriods[2]]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('applies remove operation leaving one sibling', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}]; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'remove', @@ -1035,15 +1016,13 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); - expect(manifest.Period).to.equal(originalPeriods[0]); - expect(manifest.Period_asArray).to.deep.equal([originalPeriods[0]]); + expect(manifest.Period).to.deep.equal([originalPeriods[0]]); }); it('applies remove operation leaving no siblings', function () { let originalPeriod = {id: 'foo'}; let manifest = { - Period: originalPeriod, - Period_asArray: [originalPeriod] + Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'remove', @@ -1053,14 +1032,12 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(manifest).to.not.have.property('Period'); - expect(manifest).to.not.have.property('Period_asArray'); }); it('applies remove attribute operation', function () { let originalPeriod = {id: 'foo', start: 'bar'}; let manifest = { - Period: originalPeriod, - Period_asArray: [originalPeriod] + Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'remove', @@ -1070,16 +1047,14 @@ describe('DashAdapter', function () { dashAdapter.applyPatchToManifest(manifest, patch); expect(originalPeriod).to.not.have.property('start'); - expect(manifest.Period).to.deep.equal(originalPeriod); - expect(manifest.Period_asArray).to.deep.equal([originalPeriod]); + expect(manifest.Period).to.deep.equal([originalPeriod]); }); it('applies multiple operations respecting order', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}]; let newPeriod = {id: 'baz'}; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [ { @@ -1115,14 +1090,12 @@ describe('DashAdapter', function () { // check insertion and ordering based on application expect(manifest.Period).to.deep.equal([newPeriod, originalPeriods[1]]); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); it('invalid operations are ignored', function () { let originalPeriods = [{id: 'foo'}, {id: 'bar'}]; let manifest = { - Period: originalPeriods.slice(), - Period_asArray: originalPeriods.slice() + Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [ { @@ -1152,7 +1125,6 @@ describe('DashAdapter', function () { // check ordering proper expect(manifest.Period).to.deep.equal(originalPeriods); - expect(manifest.Period).to.deep.equal(manifest.Period_asArray); }); }); }); diff --git a/test/unit/dash.DashHandler.js b/test/unit/dash.DashHandler.js index 1e5a812e4e..b72da34a47 100644 --- a/test/unit/dash.DashHandler.js +++ b/test/unit/dash.DashHandler.js @@ -125,11 +125,11 @@ describe('DashHandler', function () { period: { mpd: { manifest: { - Period_asArray: [ + Period: [ { - AdaptationSet_asArray: [ + AdaptationSet: [ { - Representation_asArray: [ + Representation: [ { bandwidth: 3000 } @@ -379,11 +379,11 @@ describe('DashHandler', function () { period: { mpd: { manifest: { - Period_asArray: [ + Period: [ { - AdaptationSet_asArray: [ + AdaptationSet: [ { - Representation_asArray: [ + Representation: [ { bandwidth: 3000 } diff --git a/test/unit/dash.DashParser.js b/test/unit/dash.DashParser.js index 5ec75fd6e2..d9d5ffbef5 100644 --- a/test/unit/dash.DashParser.js +++ b/test/unit/dash.DashParser.js @@ -34,12 +34,12 @@ describe('DashParser', function () { }); it('should throw an error when parse is called without data and config object has been set properly', function () { - expect(dashParser.parse.bind('')).to.be.throw('parsing the manifest failed'); + expect(dashParser.parse.bind('')).to.be.throw('failed to parse the manifest'); }); it('should throw an error when parse is called with invalid data', function () { let manifest = fs.readFileSync(__dirname + '/data/dash/manifest_error.xml', 'utf8'); - expect(dashParser.parse.bind(manifest)).to.be.throw('parsing the manifest failed'); + expect(dashParser.parse.bind(manifest)).to.be.throw('failed to parse the manifest'); }); it('should return an Object when parse is called with correct data', function () { diff --git a/test/unit/dash.constants.DashConstants.js b/test/unit/dash.constants.DashConstants.js index 0c6be60bbf..513da2b7ed 100644 --- a/test/unit/dash.constants.DashConstants.js +++ b/test/unit/dash.constants.DashConstants.js @@ -66,12 +66,9 @@ describe('DashConstants', function () { expect(DashConstants.SUBSET).to.equal('Subset'); expect(DashConstants.LANG).to.equal('lang'); expect(DashConstants.VIEWPOINT).to.equal('Viewpoint'); - expect(DashConstants.ROLE_ASARRAY).to.equal('Role_asArray'); - expect(DashConstants.REPRESENTATION_ASARRAY).to.equal('Representation_asArray'); - expect(DashConstants.PRODUCERREFERENCETIME_ASARRAY).to.equal('ProducerReferenceTime_asArray'); - expect(DashConstants.ACCESSIBILITY_ASARRAY).to.equal('Accessibility_asArray'); - expect(DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY).to.equal('AudioChannelConfiguration_asArray'); - expect(DashConstants.CONTENTPROTECTION_ASARRAY).to.equal('ContentProtection_asArray'); + expect(DashConstants.ROLE).to.equal('Role'); + expect(DashConstants.ACCESSIBILITY).to.equal('Accessibility'); + expect(DashConstants.CONTENT_PROTECTION).to.equal('ContentProtection'); expect(DashConstants.MAIN).to.equal('main'); expect(DashConstants.DYNAMIC).to.equal('dynamic'); expect(DashConstants.MEDIA_PRESENTATION_DURATION).to.equal('mediaPresentationDuration'); diff --git a/test/unit/dash.controllers.RepresentationController.js b/test/unit/dash.controllers.RepresentationController.js index 0347698db1..49914d93f0 100644 --- a/test/unit/dash.controllers.RepresentationController.js +++ b/test/unit/dash.controllers.RepresentationController.js @@ -31,7 +31,7 @@ describe('RepresentationController', function () { const specHelper = new SpecHelper(); const mpdHelper = new MpdHelper(); const mpd = mpdHelper.getMpd('static'); - const data = mpd.Period_asArray[0].AdaptationSet_asArray[0]; + const data = mpd.Period[0].AdaptationSet[0]; const voRepresentations = []; voRepresentations.push(voHelper.getDummyRepresentation(testType, 0), voHelper.getDummyRepresentation(testType, 1), voHelper.getDummyRepresentation(testType, 2)); const streamProcessor = objectsHelper.getDummyStreamProcessor(testType); diff --git a/test/unit/dash.models.DashManifestModel.js b/test/unit/dash.models.DashManifestModel.js index 31654efc65..787fb821d3 100644 --- a/test/unit/dash.models.DashManifestModel.js +++ b/test/unit/dash.models.DashManifestModel.js @@ -29,14 +29,6 @@ const mpd_sample = { 'id': '153202' } ], - 'Period_asArray': [ - { - 'id': '153199' - }, - { - 'id': '153202' - } - ], 'type': 'static' }, 'maxSegmentDuration': 4.5, @@ -146,28 +138,28 @@ describe('DashManifestModel', function () { }); it('should return null when getAdaptationForId is called and id and periodIndex are undefined', () => { - const manifest = { Period_asArray: [] }; + const manifest = { Period: [] }; const adaptation = dashManifestModel.getAdaptationForId(undefined, manifest, undefined); expect(adaptation).to.be.null; // jshint ignore:line }); it('should return null when getAdaptationForId is called and id is undefined', () => { - const manifest = { Period_asArray: [] }; + const manifest = { Period: [] }; const adaptation = dashManifestModel.getAdaptationForId(undefined, manifest, 2); expect(adaptation).to.be.null; // jshint ignore:line }); it('should return null when getAdaptationForId is called and id is undefined and periodIndex = 0', () => { - const manifest = { Period_asArray: [{ AdaptationSet_asArray: [{ id: 0 }] }] }; + const manifest = { Period: [{ AdaptationSet: [{ id: 0 }] }] }; const adaptation = dashManifestModel.getAdaptationForId(undefined, manifest, 0); expect(adaptation).to.be.null; // jshint ignore:line }); it('should return valid value when getAdaptationForId is called and id is 0 and periodIndex = 0', () => { - const manifest = { Period_asArray: [{ AdaptationSet_asArray: [{ id: 0 }] }] }; + const manifest = { Period: [{ AdaptationSet: [{ id: 0 }] }] }; const adaptation = dashManifestModel.getAdaptationForId(0, manifest, 0); expect(adaptation.id).to.equal(0); // jshint ignore:line @@ -180,28 +172,28 @@ describe('DashManifestModel', function () { }); it('should return null when getAdaptationForIndex is called and id and periodIndex are undefined', () => { - const manifest = { Period_asArray: [] }; + const manifest = { Period: [] }; const adaptation = dashManifestModel.getAdaptationForIndex(undefined, manifest, undefined); expect(adaptation).to.be.null; // jshint ignore:line }); it('should return null when getAdaptationForIndex is called and id is undefined', () => { - const manifest = { Period_asArray: [] }; + const manifest = { Period: [] }; const adaptation = dashManifestModel.getAdaptationForIndex(undefined, manifest, 2); expect(adaptation).to.be.null; // jshint ignore:line }); it('should return null when getAdaptationForIndex is called and id is undefined and periodIndex = 0', () => { - const manifest = { Period_asArray: [{ AdaptationSet_asArray: [{ id: 0 }] }] }; + const manifest = { Period: [{ AdaptationSet: [{ id: 0 }] }] }; const adaptation = dashManifestModel.getAdaptationForIndex(undefined, manifest, 0); expect(adaptation).to.be.null; // jshint ignore:line }); it('should return valid value when getAdaptationForIndex is called and id is 0 and periodIndex = 0', () => { - const manifest = { Period_asArray: [{ AdaptationSet_asArray: [{ id: 0 }] }] }; + const manifest = { Period: [{ AdaptationSet: [{ id: 0 }] }] }; const adaptation = dashManifestModel.getAdaptationForIndex(0, manifest, 0); expect(adaptation.id).to.equal(0); // jshint ignore:line @@ -214,7 +206,7 @@ describe('DashManifestModel', function () { }); it('should return -1 when getIndexForAdaptation is called and manifest and periodIndex are undefined', () => { - const manifest = { Period_asArray: [] }; + const manifest = { Period: [] }; var adaptation = mpdHelper.composeAdaptation('video'); const index = dashManifestModel.getIndexForAdaptation(adaptation, manifest, undefined); @@ -236,7 +228,7 @@ describe('DashManifestModel', function () { }); it('should return an empty array when getAdaptationsForType is called and periodIndex and type are undefined', () => { - const manifest = { Period_asArray: [] }; + const manifest = { Period: [] }; const adaptationsArray = dashManifestModel.getAdaptationsForType(manifest, undefined, undefined); expect(adaptationsArray).to.be.instanceOf(Array); // jshint ignore:line @@ -244,7 +236,7 @@ describe('DashManifestModel', function () { }); it('should return an empty array when getAdaptationsForType is called and type is undefined', () => { - const manifest = { Period_asArray: [{ AdaptationSet_asArray: [{ id: 0 }] }] }; + const manifest = { Period: [{ AdaptationSet: [{ id: 0 }] }] }; expect(dashManifestModel.getAdaptationsForType.bind(dashManifestModel, manifest, 0, undefined)).to.throw('type is not defined'); }); @@ -255,27 +247,27 @@ describe('DashManifestModel', function () { expect(codec).to.be.null; // jshint ignore:line }); - it('should return null when getCodec is called and adaptation.Representation_asArray is undefined', () => { + it('should return null when getCodec is called and adaptation.Representation is undefined', () => { const codec = dashManifestModel.getCodec({}); expect(codec).to.be.null; // jshint ignore:line }); - it('should return null when getCodec is called and adaptation.Representation_asArray.length is -1', () => { - const codec = dashManifestModel.getCodec({ Representation_asArray: { length: -1 } }); + it('should return null when getCodec is called and adaptation.Representation.length is -1', () => { + const codec = dashManifestModel.getCodec({ Representation: { length: -1 } }); expect(codec).to.be.null; // jshint ignore:line }); it('should return null when getCodec is called and representationId is not an integer', () => { - const codec = dashManifestModel.getCodec({ Representation_asArray: { length: 1 } }, true); + const codec = dashManifestModel.getCodec({ Representation: { length: 1 } }, true); expect(codec).to.be.null; // jshint ignore:line }); it('should return correct codec when getCodec is called and representationId is an integer and addResolutionInfo is true', () => { const codec = dashManifestModel.getCodec({ - Representation_asArray: [{ + Representation: [{ mimeType: 'video/mp4', codecs: 'avc1.4D400D', width: 1080, @@ -288,7 +280,7 @@ describe('DashManifestModel', function () { it('should return correct codec when getCodec is called and representationId is an integer and addResolutionInfo is false', () => { const codec = dashManifestModel.getCodec({ - Representation_asArray: [{ + Representation: [{ mimeType: 'video/mp4', codecs: 'avc1.4D400D', width: 1080, @@ -301,7 +293,7 @@ describe('DashManifestModel', function () { it('should return correct codec without a correct mime type profile when getCodec is called and representationId is an integer and addResolutionInfo is false', () => { const codec = dashManifestModel.getCodec({ - Representation_asArray: [{ + Representation: [{ mimeType: 'video/mp4 profiles="cmfc,cfhd"', codecs: 'avc1.4D400D', width: 1080, @@ -314,7 +306,7 @@ describe('DashManifestModel', function () { it('should return correct codec without an invalid mime type profile when getCodec is called and representationId is an integer and addResolutionInfo is false', () => { const codec = dashManifestModel.getCodec({ - Representation_asArray: [{ + Representation: [{ mimeType: 'video/mp4 profiles="cmfc,cf', codecs: 'avc1.4D400D', width: 1080, @@ -331,14 +323,14 @@ describe('DashManifestModel', function () { expect(mimeType).to.be.null; // jshint ignore:line }); - it('should return null when getMimeType is called and adaptation.Representation_asArray is undefined', () => { + it('should return null when getMimeType is called and adaptation.Representation is undefined', () => { const mimeType = dashManifestModel.getMimeType({}); expect(mimeType).to.be.null; // jshint ignore:line }); - it('should return null when getMimeType is called and adaptation.Representation_asArray.length is -1', () => { - const mimeType = dashManifestModel.getMimeType({ Representation_asArray: { length: -1 } }); + it('should return null when getMimeType is called and adaptation.Representation.length is -1', () => { + const mimeType = dashManifestModel.getMimeType({ Representation: { length: -1 } }); expect(mimeType).to.be.null; // jshint ignore:line }); @@ -370,14 +362,14 @@ describe('DashManifestModel', function () { }); it('should return empty array when getLabelsForAdaptation is called and adaptation is not well defined', () => { - const labels = dashManifestModel.getLabelsForAdaptation({ Label_asArray: true }); + const labels = dashManifestModel.getLabelsForAdaptation({ Label: true }); expect(labels).to.be.instanceOf(Array); // jshint ignore:line expect(labels).to.be.empty; // jshint ignore:line }); - it('should return empty array when getLabelsForAdaptation is called and adaptation is well defined with an empty Label_asArray', () => { - const labels = dashManifestModel.getLabelsForAdaptation({ Label_asArray: [] }); + it('should return empty array when getLabelsForAdaptation is called and adaptation is well defined with an empty Label', () => { + const labels = dashManifestModel.getLabelsForAdaptation({ Label: [] }); expect(labels).to.be.instanceOf(Array); // jshint ignore:line expect(labels).to.be.empty; // jshint ignore:line @@ -385,7 +377,7 @@ describe('DashManifestModel', function () { it('should return correct array when getLabelsForAdaptation is called and adaptation is well defined', () => { const labels = dashManifestModel.getLabelsForAdaptation({ - Label_asArray: [{ + Label: [{ lang: 'fre', __text: 'french' }, { lang: 'eng', __text: 'english' }] @@ -402,8 +394,8 @@ describe('DashManifestModel', function () { expect(contentProtection).to.be.null; // jshint ignore:line }); - it('should return null when getContentProtectionData is called and adaptation is defined, but ContentProtection_asArray is an empty array', () => { - const adaptation = { ContentProtection_asArray: [] }; + it('should return null when getContentProtectionData is called and adaptation is defined, but ContentProtection is an empty array', () => { + const adaptation = { ContentProtection: [] }; const contentProtection = dashManifestModel.getContentProtectionData(adaptation); expect(contentProtection).to.be.null; // jshint ignore:line @@ -459,7 +451,7 @@ describe('DashManifestModel', function () { }); it('should not return empty array when getBitrateListForAdaptation is called and adaptation is defined', () => { - const realAdaptation = { Representation_asArray: [{}] }; + const realAdaptation = { Representation: [{}] }; const bitrateList = dashManifestModel.getBitrateListForAdaptation(realAdaptation); @@ -473,7 +465,7 @@ describe('DashManifestModel', function () { expect(representation).to.be.null; // jshint ignore:line }); - it('should return null when getRepresentationFor is called and index and andadaptation.Representation_asArray are undefined', () => { + it('should return null when getRepresentationFor is called and index and andadaptation.Representation are undefined', () => { const adaptation = {}; const representation = dashManifestModel.getRepresentationFor(undefined, adaptation); @@ -507,7 +499,7 @@ describe('DashManifestModel', function () { }); it('should return valid location when getLocation is called and manifest is a valid object', () => { - const location = dashManifestModel.getLocation({ Location: '', Location_asArray: ['location_1'] }); + const location = dashManifestModel.getLocation({ Location: '', Location: ['location_1'] }); expect(location).to.be.equal('location_1'); // jshint ignore:line }); @@ -531,7 +523,7 @@ describe('DashManifestModel', function () { }; const manifest = { [DashConstants.PATCH_LOCATION]: patchLocation, - PatchLocation_asArray: [patchLocation] + PatchLocation: [patchLocation] }; const location = dashManifestModel.getPatchLocation(manifest); @@ -565,16 +557,6 @@ describe('DashManifestModel', function () { AdaptationSet: [{ Representation: [{ InbandEventStream: [] }] }] } ], - Period_asArray: [ - { - 'id': '153199', - AdaptationSet_asArray: [{ Representation_asArray: [{ InbandEventStream_asArray: [] }] }] - }, - { - 'id': '153202', - AdaptationSet_asArray: [{ Representation_asArray: [{ InbandEventStream_asArray: [] }] }] - } - ], 'type': 'static' }; const representation = { adaptation: { index: 0, period: { index: 0 } }, index: 0 }; @@ -635,14 +617,6 @@ describe('DashManifestModel', function () { 'id': '153202' } ], - 'Period_asArray': [ - { - 'id': '153199' - }, - { - 'id': '153202' - } - ], 'type': 'static', 'mediaPresentationDuration': 300.0 }, @@ -674,19 +648,6 @@ describe('DashManifestModel', function () { 'id': '153201' } ], - 'Period_asArray': [ - { - 'id': '153199', - 'duration': 100 - }, - { - 'id': '153200', - 'duration': 50 - }, - { - 'id': '153201' - } - ], 'type': 'static', 'mediaPresentationDuration': 320.0 }, @@ -724,19 +685,6 @@ describe('DashManifestModel', function () { 'start': 120 } ], - 'Period_asArray': [ - { - 'id': '153199' - }, - { - 'id': '153200', - 'start': 80 - }, - { - 'id': '153201', - 'start': 120 - } - ], 'type': 'static', 'mediaPresentationDuration': 300.0 }, @@ -787,12 +735,12 @@ describe('DashManifestModel', function () { index: 0, mpd: { manifest: { - Period_asArray: [{ - AdaptationSet_asArray: [{ - Representation_asArray: [{ + Period: [{ + AdaptationSet: [{ + Representation: [{ SegmentTemplate: { SegmentTimeline: { - S_asArray: [{ + S: [{ d: 2, r: 2 }] @@ -942,7 +890,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs with BaseURL[0] serviceLocation set to URL when no serviceLocation was specified', () => { const node = { - BaseURL_asArray: [{ + BaseURL: [{ __text: TEST_URL }] }; @@ -958,7 +906,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs with length 1 when multiple relative BaseUrls were specified', () => { const node = { - BaseURL_asArray: [ + BaseURL: [ { __text: RELATIVE_TEST_URL + '0' }, @@ -978,7 +926,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs when multiple BaseUrls were specified', () => { const node = { - BaseURL_asArray: [ + BaseURL: [ { __text: TEST_URL + '0' }, @@ -1000,7 +948,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs with BaseURL[0] serviceLocation set when serviceLocation was specified', () => { const node = { - BaseURL_asArray: [{ + BaseURL: [{ __text: TEST_URL, serviceLocation: SERVICE_LOCATION }] @@ -1017,7 +965,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs with BaseURL[0] having correct defaults for DVB extensions when not specified', () => { const node = { - BaseURL_asArray: [{ + BaseURL: [{ __text: TEST_URL }] }; @@ -1034,7 +982,7 @@ describe('DashManifestModel', function () { const TEST_PRIORITY = 3; const TEST_WEIGHT = 2; const node = { - BaseURL_asArray: [{ + BaseURL: [{ __text: TEST_URL, 'dvb:priority': TEST_PRIORITY, 'dvb:weight': TEST_WEIGHT @@ -1052,7 +1000,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs with BaseURL[0] resolved to the document base uri when the base uri is specified and the input url is relative', () => { const node = { baseUri: TEST_URL, - BaseURL_asArray: [{ + BaseURL: [{ __text: RELATIVE_TEST_URL }] }; @@ -1067,7 +1015,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs with BaseURL[0] resolved to the document base uri when the base uri is the mpd and the input url is relative', () => { const node = { baseUri: TEST_URL + 'example.mpd', - BaseURL_asArray: [{ + BaseURL: [{ __text: RELATIVE_TEST_URL }] }; @@ -1082,7 +1030,7 @@ describe('DashManifestModel', function () { it('returns an Array of BaseURLs with BaseURL[0] ignoring the document base uri when the base uri is specified and the input url is absolute', () => { const node = { baseUri: TEST_URL, - BaseURL_asArray: [{ + BaseURL: [{ __text: TEST_URL }] }; @@ -1119,7 +1067,7 @@ describe('DashManifestModel', function () { it('returns an empty Array where a single ProducerReferenceTime element on a node has missing mandatory attributes', () => { const node = { - [DashConstants.PRODUCERREFERENCETIME_ASARRAY]: [ + [DashConstants.PRODUCER_REFERENCE_TIME]: [ { [DashConstants.ID]: 4, [DashConstants.WALL_CLOCK_TIME]: '1970-01-01T00:00:00Z' @@ -1136,7 +1084,7 @@ describe('DashManifestModel', function () { it('returns an Array of ProducerReferenceTime elements with mandatory attributes', () => { const node = { - [DashConstants.PRODUCERREFERENCETIME_ASARRAY]: [ + [DashConstants.PRODUCER_REFERENCE_TIME]: [ { [DashConstants.ID]: 4, [DashConstants.WALL_CLOCK_TIME]: '1970-01-01T00:00:04Z', @@ -1165,7 +1113,7 @@ describe('DashManifestModel', function () { it('returns ProducerReferenceTimes with correct default attribute values', () => { const node = { - [DashConstants.PRODUCERREFERENCETIME_ASARRAY]: [ + [DashConstants.PRODUCER_REFERENCE_TIME]: [ { [DashConstants.ID]: 4, [DashConstants.WALL_CLOCK_TIME]: '1970-01-01T00:00:04Z', @@ -1182,9 +1130,9 @@ describe('DashManifestModel', function () { it('returns ProducerReferenceTimes within representations', () => { const node = { - [DashConstants.REPRESENTATION_ASARRAY]: [ + [DashConstants.REPRESENTATION]: [ { - [DashConstants.PRODUCERREFERENCETIME_ASARRAY]: [ + [DashConstants.PRODUCER_REFERENCE_TIME]: [ { [DashConstants.ID]: 1, [DashConstants.WALL_CLOCK_TIME]: '1970-01-01T00:00:01Z', @@ -1193,7 +1141,7 @@ describe('DashManifestModel', function () { ] }, { - [DashConstants.PRODUCERREFERENCETIME_ASARRAY]: [ + [DashConstants.PRODUCER_REFERENCE_TIME]: [ { [DashConstants.ID]: 2, [DashConstants.WALL_CLOCK_TIME]: '1970-01-01T00:00:02Z', @@ -1219,16 +1167,16 @@ describe('DashManifestModel', function () { it('returns ProducerReferenceTimes at both AdaptationSet and Representation level', () => { const node = { - [DashConstants.PRODUCERREFERENCETIME_ASARRAY]: [ + [DashConstants.PRODUCER_REFERENCE_TIME]: [ { [DashConstants.ID]: 1, [DashConstants.WALL_CLOCK_TIME]: '1970-01-01T00:00:01Z', [DashConstants.PRESENTATION_TIME]: 1 } ], - [DashConstants.REPRESENTATION_ASARRAY]: [ + [DashConstants.REPRESENTATION]: [ { - [DashConstants.PRODUCERREFERENCETIME_ASARRAY]: [ + [DashConstants.PRODUCER_REFERENCE_TIME]: [ { [DashConstants.ID]: 2, [DashConstants.WALL_CLOCK_TIME]: '1970-01-01T00:00:02Z', diff --git a/test/unit/dash.utils.ListSegmentsGetter.js b/test/unit/dash.utils.ListSegmentsGetter.js index 66ce6d5c39..5f2e567a38 100644 --- a/test/unit/dash.utils.ListSegmentsGetter.js +++ b/test/unit/dash.utils.ListSegmentsGetter.js @@ -9,11 +9,6 @@ const segmentsList = { 'Initialization': { 'sourceURL': 'init.m4s' }, - 'Initialization_asArray': [ - { - 'sourceURL': 'init.m4s' - } - ], 'SegmentURL': [ { 'media': 'media1.m4s' @@ -31,23 +26,6 @@ const segmentsList = { 'media': 'media5.m4s' } ], - 'SegmentURL_asArray': [ - { - 'media': 'media1.m4s' - }, - { - 'media': 'media2.m4s' - }, - { - 'media': 'media3.m4s' - }, - { - 'media': 'media4.m4s' - }, - { - 'media': 'media5.m4s' - } - ], 'presentationTimeOffset': 0, 'timescale': 1, 'duration': 10, @@ -58,7 +36,7 @@ function createRepresentationMock() { const voHelper = new VoHelper(); const representation = voHelper.getDummyRepresentation(Constants.VIDEO); representation.SegmentList = segmentsList; - representation.adaptation.period.mpd.manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray[0] = representation; + representation.adaptation.period.mpd.manifest.Period[0].AdaptationSet[0].Representation[0] = representation; representation.segmentDuration = 10; return representation; diff --git a/test/unit/dash.utils.TimelineSegmentsGetter.js b/test/unit/dash.utils.TimelineSegmentsGetter.js index 1b1afc8ad1..04dfd60276 100644 --- a/test/unit/dash.utils.TimelineSegmentsGetter.js +++ b/test/unit/dash.utils.TimelineSegmentsGetter.js @@ -20,15 +20,8 @@ const segmentTemplate = { 'timescale': 90000, 'initialization': 'test-$RepresentationID$.dash', 'SegmentTimeline': { - 'S': segments, - 'S_asArray': segments + 'S': segments }, - 'SegmentTimeline_asArray': [ - { - 'S': segments, - 'S_asArray': segments - } - ], 'media': 'test-$RepresentationID$-$Time$.dash' }; @@ -37,7 +30,7 @@ function createRepresentationMock() { const representation = voHelper.getDummyRepresentation(Constants.VIDEO); representation.timescale = 90000; representation.SegmentTemplate = segmentTemplate; - representation.adaptation.period.mpd.manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray[0] = representation; + representation.adaptation.period.mpd.manifest.Period[0].AdaptationSet[0].Representation[0] = representation; representation.adaptation.period.mpd.maxSegmentDuration = 5; representation.adaptation.period.duration = 101.1; representation.presentationTimeOffset = 0; diff --git a/test/unit/dash.vo.SimpleXPath.js b/test/unit/dash.vo.SimpleXPath.js index b6a2c12bc6..c8838a6ac7 100644 --- a/test/unit/dash.vo.SimpleXPath.js +++ b/test/unit/dash.vo.SimpleXPath.js @@ -106,70 +106,70 @@ describe('SimpleXPath', function () { let xpath = new SimpleXPath('/MPD/Period[1]/BaseURL'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('BaseURL'); - expect(result.leaf).to.equal(mpd.Period.BaseURL); + expect(result.leaf).to.equal(mpd.Period[0].BaseURL[0]); }); it('should find node when using implicit position search with one child', function () { let xpath = new SimpleXPath('/MPD/Period/BaseURL'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('BaseURL'); - expect(result.leaf).to.equal(mpd.Period.BaseURL); + expect(result.leaf).to.equal(mpd.Period[0].BaseURL[0]); }); it('should find node when using position search with multiple children', function () { let xpath = new SimpleXPath('/MPD/Period/AdaptationSet[2]/SegmentTemplate'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('SegmentTemplate'); - expect(result.leaf).to.equal(mpd.Period.AdaptationSet[1].SegmentTemplate); + expect(result.leaf).to.equal(mpd.Period[0].AdaptationSet[1].SegmentTemplate); }); it('should find node when ending with positional search of one child', function () { let xpath = new SimpleXPath('/MPD/Period[1]'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('Period'); - expect(result.leaf).to.equal(mpd.Period); + expect(result.leaf).to.equal(mpd.Period[0]); }); it('should find node when ending with positional search of multiple children', function () { let xpath = new SimpleXPath('/MPD/Period/AdaptationSet[2]'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('AdaptationSet'); - expect(result.leaf).to.equal(mpd.Period.AdaptationSet[1]); + expect(result.leaf).to.equal(mpd.Period[0].AdaptationSet[1]); }); it('should find node when attribute search used for one child', function () { let xpath = new SimpleXPath('/MPD/Period[@id="foo"]/BaseURL'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('BaseURL'); - expect(result.leaf).to.equal(mpd.Period.BaseURL); + expect(result.leaf).to.equal(mpd.Period[0].BaseURL[0]); }); it('should find node when attribute search used for multiple children', function () { let xpath = new SimpleXPath('/MPD/Period/AdaptationSet[@id=20]/SegmentTemplate'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('SegmentTemplate'); - expect(result.leaf).to.equal(mpd.Period.AdaptationSet[1].SegmentTemplate); + expect(result.leaf).to.equal(mpd.Period[0].AdaptationSet[1].SegmentTemplate); }); it('should find node when path ends in attributes search with one child', function () { let xpath = new SimpleXPath('/MPD/Period[@id="foo"]'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('Period'); - expect(result.leaf).to.equal(mpd.Period); + expect(result.leaf).to.equal(mpd.Period[0]); }); it('should find node when path ends in attributes search with multiple children', function () { let xpath = new SimpleXPath('/MPD/Period/AdaptationSet[@id=20]'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('AdaptationSet'); - expect(result.leaf).to.equal(mpd.Period.AdaptationSet[1]); + expect(result.leaf).to.equal(mpd.Period[0].AdaptationSet[1]); }); it('should find node when path targets attribute', function () { let xpath = new SimpleXPath('/MPD/BaseURL/@serviceLocation'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('serviceLocation'); - expect(result.leaf).to.equal(mpd.BaseURL); + expect(result.leaf).to.equal(mpd.BaseURL[0]); }); it('should fail to find positional search that does not exist', function () { @@ -200,7 +200,7 @@ describe('SimpleXPath', function () { let xpath = new SimpleXPath('/MPD/Period[@id="foo"]/AdaptationSet[@id="20"]/SegmentTemplate/SegmentTimeline'); let result = xpath.getMpdTarget(mpd); expect(result.name).to.equal('SegmentTimeline'); - expect(result.leaf).to.equal(mpd.Period.AdaptationSet[1].SegmentTemplate.SegmentTimeline); + expect(result.leaf).to.equal(mpd.Period[0].AdaptationSet[1].SegmentTemplate.SegmentTimeline); }); }); }); diff --git a/test/unit/data/dash/manifest_error.xml b/test/unit/data/dash/manifest_error.xml index 94820e04bc..0cd79bb7d9 100644 --- a/test/unit/data/dash/manifest_error.xml +++ b/test/unit/data/dash/manifest_error.xml @@ -1 +1 @@ - { return { S: element @@ -26,7 +25,7 @@ function staticSegmentTimeline() { function staticSegmentTemplate() { let timeline = staticSegmentTimeline(); return { - SegmentTimeline: timeline, // purposely omit the _asArray to ensure single node case captured + SegmentTimeline: timeline, __children: [{ SegmentTimeline: timeline }] @@ -36,7 +35,7 @@ function staticSegmentTemplate() { function staticAdaptationSet(id) { let template = staticSegmentTemplate(); return { - SegmentTemplate: template, // purposely omit the _asArray to ensure single node case captured + SegmentTemplate: template, __children: [{ SegmentTemplate: template }], @@ -63,10 +62,8 @@ function staticPeriod(id) { let baseUrl = staticBaseUrl(`period-${id}/`); let adaptationSets = [staticAdaptationSet(10), staticAdaptationSet(20)]; return { - BaseURL: baseUrl, - BaseURL_asArray: [baseUrl], + BaseURL: [baseUrl], AdaptationSet: adaptationSets, - AdaptationSet_asArray: adaptationSets.slice(), __children: [ { BaseURL: baseUrl }, { AdaptationSet: adaptationSets[0] }, @@ -116,12 +113,9 @@ class PatchHelper { let utcTiming = 'timetime'; let period = staticPeriod('foo'); return { - UTCTiming: utcTiming, - UTCTiming_asArray: [utcTiming], - BaseURL: baseUrl, - BaseURL_asArray: [baseUrl], - Period: period, - Period_asArray: [period], + UTCTiming: [utcTiming], + BaseURL: [baseUrl], + Period: [period], __children: [ { UTCTiming: utcTiming }, { BaseURL: baseUrl }, diff --git a/test/unit/mocks/AdapterMock.js b/test/unit/mocks/AdapterMock.js index 3e9b2a43ff..db8ca1405e 100644 --- a/test/unit/mocks/AdapterMock.js +++ b/test/unit/mocks/AdapterMock.js @@ -79,23 +79,6 @@ function AdapterMock() { { width: 900 } - ], - Representation_asArray: [ - { - width: 500 - }, - { - width: 750 - }, - { - width: 900 - }, - { - width: 900 - }, - { - width: 900 - } ] }; }; @@ -164,8 +147,8 @@ function AdapterMock() { this.getCodec = function (adaptation, representationId, addResolutionInfo) { let codec = null; - if (adaptation && adaptation.Representation_asArray && adaptation.Representation_asArray.length > 0) { - const representation = adaptation.Representation_asArray[representationId]; + if (adaptation && adaptation.Representation && adaptation.Representation.length > 0) { + const representation = adaptation.Representation[representationId]; if (representation) { codec = representation.mimeType + ';codecs="' + representation.codecs + '"'; if (addResolutionInfo && representation.width !== undefined && representation.height !== undefined) { @@ -183,9 +166,9 @@ function AdapterMock() { }; this.getEssentialPropertiesForRepresentation = function (realRepresentation) { - if (!realRepresentation || !realRepresentation.EssentialProperty_asArray || !realRepresentation.EssentialProperty_asArray.length) return null; + if (!realRepresentation || !realRepresentation.EssentialProperty || !realRepresentation.EssentialProperty.length) return null; - return realRepresentation.EssentialProperty_asArray.map((prop) => { + return realRepresentation.EssentialProperty.map((prop) => { return { schemeIdUri: prop.schemeIdUri, value: prop.value diff --git a/test/unit/mocks/DashManifestModelMock.js b/test/unit/mocks/DashManifestModelMock.js index 4ae43c5fdb..e54b09e0d2 100644 --- a/test/unit/mocks/DashManifestModelMock.js +++ b/test/unit/mocks/DashManifestModelMock.js @@ -10,7 +10,7 @@ function DashManifestModelMock () { if (type === 'video') { adaptationsArray = [{ id: 0, mimeType: 'video' }, { id: 1, mimeType: 'video' }]; } else { - adaptationsArray = [{ id: undefined, mimeType: 'audio', lang: 'eng', Role_asArray: [{ value: 'main' }] }, { id: undefined, mimeType: 'audio', lang: 'deu', Role_asArray: [{ value: 'main' }]}]; + adaptationsArray = [{ id: undefined, mimeType: 'audio', lang: 'eng', Role: [{ value: 'main' }] }, { id: undefined, mimeType: 'audio', lang: 'deu', Role: [{ value: 'main' }]}]; } return adaptationsArray; @@ -51,7 +51,7 @@ function DashManifestModelMock () { this.getRegularPeriods = function (mpd) { const voPeriods = []; - if (mpd && mpd.manifest && mpd.manifest.Period_asArray) { + if (mpd && mpd.manifest && mpd.manifest.Period) { voPeriods.push({mpd: mpd}); } @@ -62,7 +62,7 @@ function DashManifestModelMock () { let voAdaptations = []; if (voPeriod) { - voAdaptations.push(voPeriod.mpd.manifest.Period_asArray[0]); + voAdaptations.push(voPeriod.mpd.manifest.Period[0]); } return voAdaptations; diff --git a/test/unit/mocks/RepresentationControllerMock.js b/test/unit/mocks/RepresentationControllerMock.js index 386754cbdf..4b4bd5ff7c 100644 --- a/test/unit/mocks/RepresentationControllerMock.js +++ b/test/unit/mocks/RepresentationControllerMock.js @@ -1,6 +1,6 @@ function RepresentationControllerMock () { this.getCurrentRepresentation = function () { - return {adaptation: {period: {mpd: {manifest: {type: 'dynamic', Period_asArray: [{AdaptationSet_asArray: [{SegmentTemplate: {timescale: 10000000, SegmentTimeline: {S: [{tManifest: 0}]}}}]}]}}, index: 0}, index: 0}}; + return {adaptation: {period: {mpd: {manifest: {type: 'dynamic', Period: [{AdaptationSet: [{SegmentTemplate: {timescale: 10000000, SegmentTimeline: {S: [{tManifest: 0}]}}}]}]}}, index: 0}, index: 0}}; }; this.getRepresentationForQuality = function () {}; @@ -8,4 +8,4 @@ function RepresentationControllerMock () { this.updateRepresentation = function () {}; } -export default RepresentationControllerMock; \ No newline at end of file +export default RepresentationControllerMock; diff --git a/test/unit/mss.MssFragmentProcessor.js b/test/unit/mss.MssFragmentProcessor.js index 26421259c0..344f6ddd5a 100644 --- a/test/unit/mss.MssFragmentProcessor.js +++ b/test/unit/mss.MssFragmentProcessor.js @@ -88,9 +88,11 @@ describe('MssFragmentProcessor', function () { width: NaN, adaptation: { period: { - mpd: { manifest: { Period_asArray: [{ AdaptationSet_asArray: [{ SegmentTemplate: { timescale: 0 } }] }] } }, + mpd: { manifest: { Period: [{ AdaptationSet: [{ SegmentTemplate: { timescale: 0 } }] }] } }, index: 0 - }, index: 0, type: 'audio' + }, + index: 0, + type: 'audio' } }; expect(mssFragmentProcessor.generateMoov.bind(mssFragmentProcessor, rep)).to.throw({ @@ -119,7 +121,7 @@ describe('MssFragmentProcessor', function () { width: NaN, adaptation: { period: { - mpd: { manifest: { Period_asArray: [{ AdaptationSet_asArray: [{ SegmentTemplate: { timescale: 0 } }] }] } }, + mpd: { manifest: { Period: [{ AdaptationSet: [{ SegmentTemplate: { timescale: 0 } }] }] } }, index: 0 }, index: 0, type: 'audio' } @@ -150,7 +152,7 @@ describe('MssFragmentProcessor', function () { width: NaN, adaptation: { period: { - mpd: { manifest: { Period_asArray: [{ AdaptationSet_asArray: [{ SegmentTemplate: { timescale: 0 } }] }] } }, + mpd: { manifest: { Period_: [{ AdaptationSet_: [{ SegmentTemplate: { timescale: 0 } }] }] } }, index: 0 }, index: 0, type: 'video' } @@ -181,7 +183,7 @@ describe('MssFragmentProcessor', function () { width: NaN, adaptation: { period: { - mpd: { manifest: { Period_asArray: [{ AdaptationSet_asArray: [{ SegmentTemplate: { timescale: 0 } }] }] } }, + mpd: { manifest: { Period: [{ AdaptationSet: [{ SegmentTemplate: { timescale: 0 } }] }] } }, index: 0 }, index: 0, type: 'video' } diff --git a/test/unit/mss.parser.MssParser.js b/test/unit/mss.parser.MssParser.js index ee86ef533b..9590d9d66a 100644 --- a/test/unit/mss.parser.MssParser.js +++ b/test/unit/mss.parser.MssParser.js @@ -48,17 +48,17 @@ describe('MssParser', function () { let manifest = mssParser.parse(xml); expect(manifest).to.exist; // jshint ignore:line expect(manifest.protocol).to.equal('MSS'); - expect(manifest.Period.AdaptationSet_asArray).to.be.an.instanceof(Array); + expect(manifest.Period[0].AdaptationSet).to.be.an.instanceof(Array); let adaptation; - for (let i = 0; i < manifest.Period.AdaptationSet_asArray.length; i++) { - adaptation = manifest.Period.AdaptationSet_asArray[i]; + for (let i = 0; i < manifest.Period[0].AdaptationSet.length; i++) { + adaptation = manifest.Period[0].AdaptationSet[i]; expect(adaptation.id).to.exist; // jshint ignore:line expect(adaptation.id).not.to.be.empty; // jshint ignore:line - expect(adaptation.Representation_asArray).to.exist; // jshint ignore:line + expect(adaptation.Representation).to.exist; // jshint ignore:line - for (let j = 0; j < adaptation.Representation_asArray.length; j++) { - let representation = adaptation.Representation_asArray[j]; + for (let j = 0; j < adaptation.Representation.length; j++) { + let representation = adaptation.Representation[j]; // representation.id should be "type_index", because there is no name in StreamIndex node expect(representation.id).to.exist; // jshint ignore:line @@ -70,7 +70,7 @@ describe('MssParser', function () { it('should skip video adaptations if fourCC attribute is not found', function () { let xml = fs.readFileSync(__dirname + '/data/mss/manifestFourCCError.xml', 'utf8'); let manifest = mssParser.parse(xml); - let adaptations = manifest.Period.AdaptationSet_asArray; + let adaptations = manifest.Period[0].AdaptationSet; expect(manifest).to.exist; // jshint ignore:line expect(manifest.protocol).to.equal('MSS'); expect(adaptations).to.be.an.instanceof(Array); @@ -87,15 +87,17 @@ describe('MssParser', function () { let manifest = mssParser.parse(xml); expect(manifest).to.exist; // jshint ignore:line expect(manifest.protocol).to.equal('MSS'); - expect(manifest.Period.AdaptationSet_asArray).to.be.an.instanceof(Array); + expect(manifest.Period[0].AdaptationSet).to.be.an.instanceof(Array); let adaptation; - for (let i = 0; i < manifest.Period.AdaptationSet_asArray.length; i++) { - adaptation = manifest.Period.AdaptationSet_asArray[i]; + for (let i = 0; i < manifest.Period[0].AdaptationSet.length; i++) { + adaptation = manifest.Period[0].AdaptationSet[i]; if (adaptation.subType === 'CAPT') { expect(adaptation.Role).to.exist; // jshint ignore:line - expect(adaptation.Role.schemeIdUri).to.equal('urn:mpeg:dash:role:2011'); - expect(adaptation.Role.value).to.equal('main'); + expect(adaptation.Role).to.be.an.instanceof(Array); + expect(adaptation.Role).to.have.lengthOf(1); + expect(adaptation.Role[0].schemeIdUri).to.equal('urn:mpeg:dash:role:2011'); + expect(adaptation.Role[0].value).to.equal('main'); } } }); diff --git a/test/unit/streaming.thumbnail.ThumbnailController.js b/test/unit/streaming.thumbnail.ThumbnailController.js index 7813c68d00..93c94cd228 100644 --- a/test/unit/streaming.thumbnail.ThumbnailController.js +++ b/test/unit/streaming.thumbnail.ThumbnailController.js @@ -21,9 +21,9 @@ const sampleAdaptation = { index: 0, mpd: { manifest: { - Period_asArray: [{ - AdaptationSet_asArray: [{ - Representation_asArray: [{ + Period: [{ + AdaptationSet: [{ + Representation: [{ SegmentTemplate: {} }] }] diff --git a/test/unit/streaming.utils.CapabilitiesFilter.js b/test/unit/streaming.utils.CapabilitiesFilter.js index b83f3efc48..a9edf85053 100644 --- a/test/unit/streaming.utils.CapabilitiesFilter.js +++ b/test/unit/streaming.utils.CapabilitiesFilter.js @@ -43,10 +43,10 @@ describe('CapabilitiesFilter', function () { it('should not filter AdaptationSets and Representations', function (done) { const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'audio/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', @@ -64,8 +64,8 @@ describe('CapabilitiesFilter', function () { capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(1); - expect(manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray).to.have.lengthOf(2); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(2); done(); }) .catch((e) => { @@ -76,10 +76,10 @@ describe('CapabilitiesFilter', function () { it('should filter AdaptationSets', function (done) { const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'audio/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', @@ -100,9 +100,10 @@ describe('CapabilitiesFilter', function () { return false; } }); + capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(0); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(0); done(); }) .catch((e) => { @@ -112,10 +113,10 @@ describe('CapabilitiesFilter', function () { it('should filter Representations', function (done) { const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'audio/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'audio/mp4', codecs: 'mp4a.40.1', @@ -139,8 +140,8 @@ describe('CapabilitiesFilter', function () { capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(1); - expect(manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(1); done(); }) .catch((e) => { @@ -158,15 +159,15 @@ describe('CapabilitiesFilter', function () { it('should not filter AdaptationSets and Representations if filterUnsupportedEssentialProperties is disabled', function (done) { settings.update({ streaming: { capabilities: {filterUnsupportedEssentialProperties: false }} }); const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'audio/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', audioSamplingRate: '48000', - EssentialProperty_asArray: [{ + EssentialProperty: [{ schemeIdUri: 'http://dashif.org/thumbnail_tile', value: 'somevalue' }] @@ -175,7 +176,7 @@ describe('CapabilitiesFilter', function () { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', audioSamplingRate: '48000', - EssentialProperty_asArray: [{ + EssentialProperty: [{ schemeIdUri: 'http://dashif.org/thumbnail_tile', value: 'somevalue' }] @@ -193,8 +194,8 @@ describe('CapabilitiesFilter', function () { capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(1); - expect(manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray).to.have.lengthOf(2); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(2); done(); }) .catch((e) => { @@ -205,15 +206,15 @@ describe('CapabilitiesFilter', function () { it('should not filter AdaptationSets and Representations if EssentialProperties value is supported', function (done) { const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'audio/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', audioSamplingRate: '48000', - EssentialProperty_asArray: [{ + EssentialProperty: [{ schemeIdUri: 'http://dashif.org/thumbnail_tile', value: 'somevalue' }] @@ -222,7 +223,7 @@ describe('CapabilitiesFilter', function () { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', audioSamplingRate: '48000', - EssentialProperty_asArray: [{ + EssentialProperty: [{ schemeIdUri: 'http://dashif.org/thumbnail_tile', value: 'somevalue' }] @@ -239,8 +240,8 @@ describe('CapabilitiesFilter', function () { capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(1); - expect(manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray).to.have.lengthOf(2); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(2); done(); }) .catch((e) => { @@ -251,15 +252,15 @@ describe('CapabilitiesFilter', function () { it('should filter AdaptationSets if EssentialProperty value is not supported', function (done) { const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'audio/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', audioSamplingRate: '48000', - EssentialProperty_asArray: [{ + EssentialProperty: [{ schemeIdUri: 'http://dashif.org/thumbnail_tile', value: 'somevalue' }] @@ -268,7 +269,7 @@ describe('CapabilitiesFilter', function () { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', audioSamplingRate: '48000', - EssentialProperty_asArray: [{ + EssentialProperty: [{ schemeIdUri: 'http://dashif.org/thumbnail_tile', value: 'somevalue' }] @@ -285,7 +286,7 @@ describe('CapabilitiesFilter', function () { }); capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(0); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(0); done(); }) .catch((e) => { @@ -297,15 +298,15 @@ describe('CapabilitiesFilter', function () { it('should filter a single Representation if EssentialProperty value is not supported', function (done) { const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'audio/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'audio/mp4', codecs: 'mp4a.40.2', audioSamplingRate: '48000', - EssentialProperty_asArray: [{ + EssentialProperty: [{ schemeIdUri: 'http://dashif.org/thumbnail_tile', value: 'somevalue' }] @@ -327,8 +328,8 @@ describe('CapabilitiesFilter', function () { }); capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(1); - expect(manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(1); done(); }) .catch((e) => { @@ -341,10 +342,10 @@ describe('CapabilitiesFilter', function () { it('should use provided custom filters', function (done) { const manifest = { - Period_asArray: [{ - AdaptationSet_asArray: [{ + Period: [{ + AdaptationSet: [{ mimeType: 'video/mp4', - Representation_asArray: [ + Representation: [ { mimeType: 'video/mp4', height: 1080 @@ -368,8 +369,8 @@ describe('CapabilitiesFilter', function () { capabilitiesFilter.filterUnsupportedFeatures(manifest) .then(() => { - expect(manifest.Period_asArray[0].AdaptationSet_asArray).to.have.lengthOf(1); - expect(manifest.Period_asArray[0].AdaptationSet_asArray[0].Representation_asArray).to.have.lengthOf(2); + expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1); + expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(2); done(); }) .catch((e) => {