From b38ba81eee7d2d1a8396b07b48bca910d47547e8 Mon Sep 17 00:00:00 2001 From: Daniel Silhavy Date: Tue, 16 Apr 2024 13:32:25 +0200 Subject: [PATCH] Fix/mpd patch (#4457) * Add required changes to enable MPD patching with new XML parser --- externals/tXml.js | 3 +- src/dash/DashAdapter.js | 4 +- src/dash/models/DashManifestModel.js | 2 +- src/dash/models/PatchManifestModel.js | 24 +++----- src/streaming/ManifestUpdater.js | 4 +- test/unit/helpers/PatchHelper.js | 31 +++++----- test/unit/test/dash/dash.DashAdapter.js | 56 +++++++------------ .../dash/dash.models.PatchManifestModel.js | 4 +- 8 files changed, 51 insertions(+), 77 deletions(-) diff --git a/externals/tXml.js b/externals/tXml.js index db6c43296a..9d75f6c7fd 100644 --- a/externals/tXml.js +++ b/externals/tXml.js @@ -150,7 +150,6 @@ export function translateEntitiesAndCharacterReferences(entitiesList, str) { } if (pos + 1) pos += 1 - return children; } else if (S.charCodeAt(pos + 1) === exclamationCC) { if (S.charCodeAt(pos + 2) == minusCC) { @@ -210,7 +209,6 @@ export function translateEntitiesAndCharacterReferences(entitiesList, str) { // 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] = []; @@ -360,6 +358,7 @@ export function translateEntitiesAndCharacterReferences(entitiesList, str) { pos++; } // dash.js - BEGIN + node.__children = children; return node; // dash.js - END } diff --git a/src/dash/DashAdapter.js b/src/dash/DashAdapter.js index c92e4717b7..9696334949 100644 --- a/src/dash/DashAdapter.js +++ b/src/dash/DashAdapter.js @@ -904,7 +904,7 @@ function DashAdapter() { // if the inserted element matches the operation target (not leaf) and there is a relative position we // want the inserted position to be set such that our insertion is relative to original position // since replace has modified the array length we reduce the insert point by 1 - position = relativePosition + (insertBefore ? 0 : 1) + (operation.action == 'replace' ? -1 : 0); + position = relativePosition + (insertBefore ? 0 : 1) + (operation.action === 'replace' ? -1 : 0); } else { // otherwise we are in an add append/prepend case or replace case that removed the target name completely position = insertBefore ? 0 : updatedNodes.length; @@ -1188,7 +1188,6 @@ function DashAdapter() { instance = { applyPatchToManifest, - getMainAdaptationForType, areMediaInfosEqual, getAllMediaInfoForType, getAvailabilityStartTime, @@ -1207,6 +1206,7 @@ function DashAdapter() { getIsTextTrack, getIsTypeOf, getLocation, + getMainAdaptationForType, getManifestUpdatePeriod, getMediaInfoForType, getMpd, diff --git a/src/dash/models/DashManifestModel.js b/src/dash/models/DashManifestModel.js index 23b01dc485..1da2cff5cb 100644 --- a/src/dash/models/DashManifestModel.js +++ b/src/dash/models/DashManifestModel.js @@ -724,7 +724,7 @@ function DashManifestModel() { voRepresentation.media = segmentInfo.media; } if (segmentInfo.hasOwnProperty(DashConstants.START_NUMBER)) { - voRepresentation.startNumber = segmentInfo.startNumber; + voRepresentation.startNumber = parseInt(segmentInfo.startNumber); } if (segmentInfo.hasOwnProperty(DashConstants.INDEX_RANGE)) { voRepresentation.indexRange = segmentInfo.indexRange; diff --git a/src/dash/models/PatchManifestModel.js b/src/dash/models/PatchManifestModel.js index 8723fb8a2a..156dd99a3d 100644 --- a/src/dash/models/PatchManifestModel.js +++ b/src/dash/models/PatchManifestModel.js @@ -66,8 +66,8 @@ function PatchManifestModel() { } // Go through the patch operations in order and parse their actions out for usage - return (patch.__children || []).map((nodeContainer) => { - let action = Object.keys(nodeContainer)[0]; + return (patch.__children || []).map((node) => { + const action = node.tagName; // we only look add add/remove/replace actions if (action !== 'add' && action !== 'remove' && action !== 'replace') { @@ -75,7 +75,6 @@ function PatchManifestModel() { return null; } - let node = nodeContainer[action]; let selector = node.sel; // add action can have special targeting via the 'type' attribute @@ -101,16 +100,11 @@ function PatchManifestModel() { value = node.__text || ''; } else if (action !== 'remove') { value = node.__children.reduce((groups, child) => { - // note that this is informed by xml2js parse structure for the __children array - // which will be something like this for each child: - // { - // "": { } - // } - let key = Object.keys(child)[0]; + let key = child.tagName; // we also ignore if (key !== '#text') { groups[key] = groups[key] || []; - groups[key].push(child[key]); + groups[key].push(child); } return groups; }, {}); @@ -127,11 +121,11 @@ function PatchManifestModel() { } instance = { - getIsPatch: getIsPatch, - getPublishTime: getPublishTime, - getOriginalPublishTime: getOriginalPublishTime, - getMpdId: getMpdId, - getPatchOperations: getPatchOperations + getIsPatch, + getMpdId, + getOriginalPublishTime, + getPatchOperations, + getPublishTime }; setup(); diff --git a/src/streaming/ManifestUpdater.js b/src/streaming/ManifestUpdater.js index 0904e1c9a0..133f828704 100644 --- a/src/streaming/ManifestUpdater.js +++ b/src/streaming/ManifestUpdater.js @@ -207,13 +207,13 @@ function ManifestUpdater() { let publishTime = adapter.getPublishTime(manifest); // apply validated patch to manifest - patchSuccessful = adapter.applyPatchToManifest(manifest, patch); + adapter.applyPatchToManifest(manifest, patch); // get the updated publish time let updatedPublishTime = adapter.getPublishTime(manifest); // ensure the patch properly updated the in-memory publish time - patchSuccessful = publishTime.getTime() != updatedPublishTime.getTime(); + patchSuccessful = publishTime.getTime() !== updatedPublishTime.getTime(); } // if the patch failed to apply, force a full manifest refresh diff --git a/test/unit/helpers/PatchHelper.js b/test/unit/helpers/PatchHelper.js index a74b440ff0..3df59f6768 100644 --- a/test/unit/helpers/PatchHelper.js +++ b/test/unit/helpers/PatchHelper.js @@ -1,7 +1,7 @@ import DashConstants from '../../../src/dash/constants/DashConstants.js'; function staticSElements() { - return [[0,10], [10,5], [15,10]].map(([t, d]) => { + return [[0, 10], [10, 5], [15, 10]].map(([t, d]) => { return { __children: [], d: d, @@ -44,7 +44,7 @@ function staticAdaptationSet(id) { } function staticBaseUrl(url, serviceLocation) { - if(!serviceLocation) { + if (!serviceLocation) { return url; } return { @@ -82,24 +82,23 @@ class PatchHelper { [DashConstants.ORIGINAL_PUBLISH_TIME]: new Date().toISOString(), // only the ordered child array is simulated __children: operations.map((operation) => { - if (operation.action == 'add') { + if (operation.action === 'add') { // add is special because it has extra possible attributes return { - add: { - sel: operation.selector, - __children: operation.children, - __text: operation.text, - pos: operation.position, - type: operation.type - } - }; + tagName: operation.action, + sel: operation.selector, + __children: operation.children, + __text: operation.text, + pos: operation.position, + type: operation.type + } + } else { return { - [operation.action]: { - sel: operation.selector, - __children: operation.children, - __text: operation.text - } + tagName: operation.action, + sel: operation.selector, + __children: operation.children, + __text: operation.text }; } }) diff --git a/test/unit/test/dash/dash.DashAdapter.js b/test/unit/test/dash/dash.DashAdapter.js index c1f2866aac..88c02ce800 100644 --- a/test/unit/test/dash/dash.DashAdapter.js +++ b/test/unit/test/dash/dash.DashAdapter.js @@ -1146,13 +1146,11 @@ describe('DashAdapter', function () { it('applies add operation to structure with no siblings', function () { let manifest = {}; - let addedPeriod = { id: 'foo' }; + let addedPeriod = { id: 'foo', tagName: 'Period' }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', selector: '/MPD', - children: [{ - Period: addedPeriod - }] + children: [addedPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1161,8 +1159,8 @@ describe('DashAdapter', function () { }); it('applies add operation to structure with single sibling', function () { - let originalPeriod = { id: 'foo' }; - let addedPeriod = { id: 'bar' }; + let originalPeriod = { id: 'foo', tagName: 'Period' }; + let addedPeriod = { id: 'bar', tagName: 'Period' }; // special case x2js object which omits the variant let manifest = { Period: [originalPeriod] @@ -1170,9 +1168,7 @@ describe('DashAdapter', function () { let patch = patchHelper.generatePatch('foobar', [{ action: 'add', selector: '/MPD', - children: [{ - Period: addedPeriod - }] + children: [addedPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1182,16 +1178,14 @@ describe('DashAdapter', function () { it('applies add implicit append operation with siblings', function () { let originalPeriods = [{ id: 'foo' }, { id: 'bar' }]; - let addedPeriod = { id: 'baz' }; + let addedPeriod = { id: 'baz', tagName: 'Period' }; let manifest = { Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'add', selector: '/MPD', - children: [{ - Period: addedPeriod - }] + children: [addedPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1201,7 +1195,7 @@ describe('DashAdapter', function () { it('applies add prepend operation with siblings', function () { let originalPeriods = [{ id: 'foo' }, { id: 'bar' }]; - let addedPeriod = { id: 'baz' }; + let addedPeriod = { id: 'baz', tagName: 'Period' }; let manifest = { Period: originalPeriods.slice() }; @@ -1209,9 +1203,7 @@ describe('DashAdapter', function () { action: 'add', selector: '/MPD', position: 'prepend', - children: [{ - Period: addedPeriod - }] + children: [addedPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1221,7 +1213,7 @@ describe('DashAdapter', function () { it('applies add before operation with siblings', function () { let originalPeriods = [{ id: 'foo' }, { id: 'bar' }, { id: 'baz' }]; - let addedPeriod = { id: 'qux' }; + let addedPeriod = { id: 'qux', tagName: 'Period' }; let manifest = { Period: originalPeriods.slice() }; @@ -1229,9 +1221,7 @@ describe('DashAdapter', function () { action: 'add', selector: '/MPD/Period[2]', position: 'before', - children: [{ - Period: addedPeriod - }] + children: [addedPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1241,7 +1231,7 @@ describe('DashAdapter', function () { it('applies add after operation with siblings', function () { let originalPeriods = [{ id: 'foo' }, { id: 'bar' }, { id: 'baz' }]; - let addedPeriod = { id: 'qux' }; + let addedPeriod = { id: 'qux', tagName: 'Period' }; let manifest = { Period: originalPeriods.slice() }; @@ -1249,9 +1239,7 @@ describe('DashAdapter', function () { action: 'add', selector: '/MPD/Period[2]', position: 'after', - children: [{ - Period: addedPeriod - }] + children: [addedPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1295,16 +1283,14 @@ describe('DashAdapter', function () { it('applies replace operation with siblings', function () { let originalPeriods = [{ id: 'foo' }, { id: 'bar' }, { id: 'baz' }]; - let replacementPeriod = { id: 'qux' }; + let replacementPeriod = { id: 'qux', tagName: 'Period' }; let manifest = { Period: originalPeriods.slice() }; let patch = patchHelper.generatePatch('foobar', [{ action: 'replace', selector: '/MPD/Period[2]', - children: [{ - Period: replacementPeriod - }] + children: [replacementPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1314,16 +1300,14 @@ describe('DashAdapter', function () { it('applies replace operation without siblings', function () { let originalPeriod = { id: 'foo' }; - let replacementPeriod = { id: 'bar' }; + let replacementPeriod = { id: 'bar', tagName: 'Period' }; let manifest = { Period: [originalPeriod] }; let patch = patchHelper.generatePatch('foobar', [{ action: 'replace', selector: '/MPD/Period[1]', - children: [{ - Period: replacementPeriod - }] + children: [replacementPeriod] }]); dashAdapter.applyPatchToManifest(manifest, patch); @@ -1410,7 +1394,7 @@ describe('DashAdapter', function () { it('applies multiple operations respecting order', function () { let originalPeriods = [{ id: 'foo' }, { id: 'bar' }]; - let newPeriod = { id: 'baz' }; + let newPeriod = { id: 'baz', tagName: 'Period' }; let manifest = { Period: originalPeriods.slice() }; @@ -1425,9 +1409,7 @@ describe('DashAdapter', function () { action: 'add', selector: '/MPD/Period[2]', position: 'before', - children: [{ - Period: newPeriod - }] + children: [newPeriod] }, { action: 'replace', diff --git a/test/unit/test/dash/dash.models.PatchManifestModel.js b/test/unit/test/dash/dash.models.PatchManifestModel.js index 431d2aaa36..1021254318 100644 --- a/test/unit/test/dash/dash.models.PatchManifestModel.js +++ b/test/unit/test/dash/dash.models.PatchManifestModel.js @@ -92,7 +92,7 @@ describe('PatchManifestModel', function () { action: 'add', selector: '/MPD/Period', position: 'after', - children: [{ 'Period': {} }] + children: [{ tagName: 'Period' }] }]); let operations = patchManifestModel.getPatchOperations(patch); expect(operations.length).to.equal(1); @@ -137,7 +137,7 @@ describe('PatchManifestModel', function () { let patch = patchHelper.generatePatch('foobar', [{ action: 'replace', selector: '/MPD/Period', - children: [{ 'Period': {} }] + children: [{ tagName: 'Period' }] }]); let operations = patchManifestModel.getPatchOperations(patch); expect(operations.length).to.equal(1);