From a6e387e36e4a1cb24faf7ced79fc8cd3e4170947 Mon Sep 17 00:00:00 2001 From: Tatiana Perera Date: Wed, 29 Jan 2025 16:30:44 -0300 Subject: [PATCH 01/14] Update the manifest when the InbandEventStream value attribute is three --- src/streaming/ManifestUpdater.js | 20 ++++- src/streaming/controllers/EventController.js | 92 +++++++++++++++----- 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/src/streaming/ManifestUpdater.js b/src/streaming/ManifestUpdater.js index 3d2edf5f97..11820e6e98 100644 --- a/src/streaming/ManifestUpdater.js +++ b/src/streaming/ManifestUpdater.js @@ -131,6 +131,10 @@ function ManifestUpdater() { } } + function patchManifest(manifestPatch) { + logger.debug(`Not supported yet ${manifestPatch}`); + } + function startManifestRefreshTimer(delay) { stopManifestRefreshTimer(); @@ -187,6 +191,14 @@ function ManifestUpdater() { manifestLoader.load(url, serviceLocation, queryParams); } + function updateManifest (manifest) { + // TODO: Parse manifest if needed + const event = { + manifest + } + onManifestLoaded(event) + } + function _getAvailableMpdLocations(manifest) { const manifestLocations = adapter.getLocation(manifest); const synthesizedElements = contentSteeringController.getSynthesizedLocationElements(manifestLocations); @@ -304,12 +316,14 @@ function ManifestUpdater() { } instance = { + getIsUpdating, initialize, + patchManifest, + setConfig, setManifest, refreshManifest, - getIsUpdating, - setConfig, - reset + reset, + updateManifest, }; setup(); diff --git a/src/streaming/controllers/EventController.js b/src/streaming/controllers/EventController.js index 837bcabf2e..f98e4dd640 100644 --- a/src/streaming/controllers/EventController.js +++ b/src/streaming/controllers/EventController.js @@ -37,8 +37,10 @@ import XHRLoader from '../net/XHRLoader.js'; function EventController() { - const MPD_RELOAD_SCHEME = 'urn:mpeg:dash:event:2012'; + const MPD_VALIDITY_EXPIRATION_SCHEME = 'urn:mpeg:dash:event:2012'; const MPD_RELOAD_VALUE = 1; + const MPD_PATCH_VALUE = 2; + const MPD_UPDATE_VALUE = 3; const MPD_CALLBACK_SCHEME = 'urn:mpeg:dash:event:callback:2015'; const MPD_CALLBACK_VALUE = 1; @@ -262,7 +264,7 @@ function EventController() { let result = _addOrUpdateEvent(event, inbandEvents[periodId], false); if (result === EVENT_HANDLED_STATES.ADDED) { - if (event.eventStream.schemeIdUri === MPD_RELOAD_SCHEME) { + if (event.eventStream.schemeIdUri === MPD_VALIDITY_EXPIRATION_SCHEME) { _handleManifestReloadEvent(event); } logger.debug(`Added inband event with id ${event.id} from period ${periodId}`); @@ -321,30 +323,49 @@ function EventController() { } /** - * Triggers an MPD reload + * Refreshes, patches, or updates the manifest based on the InbandEventStream value attribute. + * @param {object} event + * @private + */ + function _handleManifestExiration(event, currentVideoTime) { + switch (event.eventStream.value) { + case MPD_RELOAD_VALUE: + logger.debug(`Starting manifest refresh event ${event.id} at ${currentVideoTime}`); + _refreshManifest(); + break; + case MPD_PATCH_VALUE: + logger.debug(`Starting manifest patch event ${event.id} at ${currentVideoTime}`); + _patchManifest(event.messageData); + break; + case MPD_UPDATE_VALUE: + logger.debug(`Starting manifest patch update ${event.id} at ${currentVideoTime}`); + _updateManifest(event.messageData); + break; + } + } + /** + * Triggers an MPD reload. * @param {object} event * @private */ function _handleManifestReloadEvent(event) { try { - if (event.eventStream.value == MPD_RELOAD_VALUE) { - const validUntil = event.calculatedPresentationTime; - let newDuration; - if (event.calculatedPresentationTime == 0xFFFFFFFF) {//0xFF... means remaining duration unknown - newDuration = NaN; - } else { - newDuration = event.calculatedPresentationTime + event.duration; - } - //logger.info('Manifest validity changed: Valid until: ' + validUntil + '; remaining duration: ' + newDuration); - eventBus.trigger(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, { - id: event.id, - validUntil: validUntil, - newDuration: newDuration, - newManifestValidAfter: NaN //event.message_data - this is an arraybuffer with a timestring in it, but not used yet - }, { - mode: MediaPlayerEvents.EVENT_MODE_ON_START - }); + const validUntil = event.calculatedPresentationTime; + let newDuration; + if (event.calculatedPresentationTime == 0xFFFFFFFF) {//0xFF... means remaining duration unknown + newDuration = NaN; + } else { + newDuration = event.calculatedPresentationTime + event.duration; } + logger.info('Manifest validity changed: Valid until: ' + validUntil + '; remaining duration: ' + newDuration); + eventBus.trigger(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, { + id: event.id, + validUntil: validUntil, + newDuration: newDuration, + newManifestValidAfter: NaN //event.message_data - this is an arraybuffer with a timestring in it, but not used yet + }, { + mode: MediaPlayerEvents.EVENT_MODE_ON_START + }); } catch (e) { logger.error(e); } @@ -479,11 +500,10 @@ function EventController() { } if (!event.triggeredStartEvent) { - if (event.eventStream.schemeIdUri === MPD_RELOAD_SCHEME && event.eventStream.value == MPD_RELOAD_VALUE) { + if (event.eventStream.schemeIdUri === MPD_VALIDITY_EXPIRATION_SCHEME) { //If both are set to zero, it indicates the media is over at this point. Don't reload the manifest. if (event.duration !== 0 || event.presentationTimeDelta !== 0) { - logger.debug(`Starting manifest refresh event ${eventId} at ${currentVideoTime}`); - _refreshManifest(); + _handleManifestExiration(event, currentVideoTime) } } else if (event.eventStream.schemeIdUri === MPD_CALLBACK_SCHEME && event.eventStream.value == MPD_CALLBACK_VALUE) { logger.debug(`Starting callback event ${eventId} at ${currentVideoTime}`); @@ -537,6 +557,32 @@ function EventController() { } } + /** + * Patch the manifest + * @private + */ + function _patchManifest(manifestPatch) { + try { + checkConfig(); + manifestUpdater.patchManifest(manifestPatch); + } catch (e) { + logger.error(e); + } + } + + /** + * Update the manifest + * @private + */ + function _updateManifest(manifest) { + try { + checkConfig(); + manifestUpdater.updateManifest(manifest); + } catch (e) { + logger.error(e); + } + } + /** * Send a callback request * @param {String} url From b336cd4815ad3e873d3512eba51e2bbb39700eb9 Mon Sep 17 00:00:00 2001 From: Tatiana Perera Date: Thu, 30 Jan 2025 13:31:42 -0300 Subject: [PATCH 02/14] CR suggestions --- src/streaming/ManifestUpdater.js | 14 -------------- src/streaming/controllers/EventController.js | 20 ++++++++++++++------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/streaming/ManifestUpdater.js b/src/streaming/ManifestUpdater.js index 11820e6e98..144e816423 100644 --- a/src/streaming/ManifestUpdater.js +++ b/src/streaming/ManifestUpdater.js @@ -131,10 +131,6 @@ function ManifestUpdater() { } } - function patchManifest(manifestPatch) { - logger.debug(`Not supported yet ${manifestPatch}`); - } - function startManifestRefreshTimer(delay) { stopManifestRefreshTimer(); @@ -191,14 +187,6 @@ function ManifestUpdater() { manifestLoader.load(url, serviceLocation, queryParams); } - function updateManifest (manifest) { - // TODO: Parse manifest if needed - const event = { - manifest - } - onManifestLoaded(event) - } - function _getAvailableMpdLocations(manifest) { const manifestLocations = adapter.getLocation(manifest); const synthesizedElements = contentSteeringController.getSynthesizedLocationElements(manifestLocations); @@ -318,12 +306,10 @@ function ManifestUpdater() { instance = { getIsUpdating, initialize, - patchManifest, setConfig, setManifest, refreshManifest, reset, - updateManifest, }; setup(); diff --git a/src/streaming/controllers/EventController.js b/src/streaming/controllers/EventController.js index f98e4dd640..973a7bab39 100644 --- a/src/streaming/controllers/EventController.js +++ b/src/streaming/controllers/EventController.js @@ -31,6 +31,7 @@ import FactoryMaker from '../../core/FactoryMaker.js'; import Debug from '../../core/Debug.js'; +import Events from '../core/events/Events.js'; import EventBus from '../../core/EventBus.js'; import MediaPlayerEvents from '../../streaming/MediaPlayerEvents.js'; import XHRLoader from '../net/XHRLoader.js'; @@ -265,7 +266,7 @@ function EventController() { if (result === EVENT_HANDLED_STATES.ADDED) { if (event.eventStream.schemeIdUri === MPD_VALIDITY_EXPIRATION_SCHEME) { - _handleManifestReloadEvent(event); + _handleManifestReload(event); } logger.debug(`Added inband event with id ${event.id} from period ${periodId}`); _startEvent(event, MediaPlayerEvents.EVENT_MODE_ON_RECEIVE); @@ -327,7 +328,7 @@ function EventController() { * @param {object} event * @private */ - function _handleManifestExiration(event, currentVideoTime) { + function _handleManifestValidityExpirationEvent(event, currentVideoTime) { switch (event.eventStream.value) { case MPD_RELOAD_VALUE: logger.debug(`Starting manifest refresh event ${event.id} at ${currentVideoTime}`); @@ -348,7 +349,7 @@ function EventController() { * @param {object} event * @private */ - function _handleManifestReloadEvent(event) { + function _handleManifestReload(event) { try { const validUntil = event.calculatedPresentationTime; let newDuration; @@ -503,7 +504,7 @@ function EventController() { if (event.eventStream.schemeIdUri === MPD_VALIDITY_EXPIRATION_SCHEME) { //If both are set to zero, it indicates the media is over at this point. Don't reload the manifest. if (event.duration !== 0 || event.presentationTimeDelta !== 0) { - _handleManifestExiration(event, currentVideoTime) + _handleManifestValidityExpirationEvent(event, currentVideoTime) } } else if (event.eventStream.schemeIdUri === MPD_CALLBACK_SCHEME && event.eventStream.value == MPD_CALLBACK_VALUE) { logger.debug(`Starting callback event ${eventId} at ${currentVideoTime}`); @@ -564,7 +565,11 @@ function EventController() { function _patchManifest(manifestPatch) { try { checkConfig(); - manifestUpdater.patchManifest(manifestPatch); + const event = { + manifest: manifestPatch // Parse manifest before assigne + } + logger.debug(`Patch manifest not supported yet ${event}`); + // eventBus.trigger(Events.INTERNAL_MANIFEST_LOADED, { event }); } catch (e) { logger.error(e); } @@ -577,7 +582,10 @@ function EventController() { function _updateManifest(manifest) { try { checkConfig(); - manifestUpdater.updateManifest(manifest); + const event = { + manifest // Parse manifest before assigne + } + eventBus.trigger(Events.INTERNAL_MANIFEST_LOADED, { event }); } catch (e) { logger.error(e); } From 076a10957c1d34c42c83183fb9c747d1466473af Mon Sep 17 00:00:00 2001 From: Tatiana Perera Date: Wed, 12 Feb 2025 15:52:54 -0300 Subject: [PATCH 03/14] Added manifest parsing --- src/core/events/CoreEvents.js | 1 + src/dash/DashAdapter.js | 8 ++- src/streaming/ManifestLoader.js | 76 +++++++++++++++++++- src/streaming/MediaPlayer.js | 13 ++-- src/streaming/controllers/EventController.js | 66 +++++++++-------- 5 files changed, 124 insertions(+), 40 deletions(-) diff --git a/src/core/events/CoreEvents.js b/src/core/events/CoreEvents.js index 8bffb0aa56..1ec59913a9 100644 --- a/src/core/events/CoreEvents.js +++ b/src/core/events/CoreEvents.js @@ -60,6 +60,7 @@ class CoreEvents extends EventsBase { this.LOADING_DATA_PROGRESS = 'loadingDataProgress'; this.LOADING_ABANDONED = 'loadingAborted'; this.MANIFEST_UPDATED = 'manifestUpdated'; + this.MPD_EXPIRE_UPDATE = 'mpdExpireUpdate'; this.MEDIA_FRAGMENT_LOADED = 'mediaFragmentLoaded'; this.MEDIA_FRAGMENT_NEEDED = 'mediaFragmentNeeded'; this.MEDIAINFO_UPDATED = 'mediaInfoUpdated'; diff --git a/src/dash/DashAdapter.js b/src/dash/DashAdapter.js index 1e60068e76..a69a280808 100644 --- a/src/dash/DashAdapter.js +++ b/src/dash/DashAdapter.js @@ -40,6 +40,7 @@ import PatchManifestModel from './models/PatchManifestModel.js'; import Representation from './vo/Representation.js'; import {bcp47Normalize} from 'bcp-47-normalize'; import {getId3Frames} from '@svta/common-media-library/id3/getId3Frames.js'; +import {utf8ArrayToStr} from '@svta/common-media-library/utils/utf8ArrayToStr' import Constants from '../streaming/constants/Constants.js'; /** @@ -505,8 +506,11 @@ function DashAdapter() { event.calculatedPresentationTime = calculatedPresentationTime; event.messageData = messageData; event.presentationTimeDelta = presentationTimeDelta; - event.parsedMessageData = (schemeIdUri === Constants.ID3_SCHEME_ID_URI) ? getId3Frames(messageData) : null; - + if (schemeIdUri === Constants.ID3_SCHEME_ID_URI) { + event.parsedMessageData = getId3Frames(messageData); + } else { + event.parsedMessageData = (messageData instanceof Uint8Array) ? utf8ArrayToStr(messageData) : null; + } return event; } catch (e) { return null; diff --git a/src/streaming/ManifestLoader.js b/src/streaming/ManifestLoader.js index 94b69b6876..81e0c1182e 100644 --- a/src/streaming/ManifestLoader.js +++ b/src/streaming/ManifestLoader.js @@ -43,7 +43,6 @@ import FactoryMaker from '../core/FactoryMaker.js'; import DashParser from '../dash/parser/DashParser.js'; function ManifestLoader(config) { - config = config || {}; const context = this.context; const debug = config.debug; @@ -59,10 +58,12 @@ function ManifestLoader(config) { let mssHandler = config.mssHandler; let errHandler = config.errHandler; + let manifestModel = config.manifestModel; function setup() { logger = debug.getLogger(instance); eventBus.on(Events.XLINK_READY, onXlinkReady, instance); + eventBus.on(Events.MPD_EXPIRE_UPDATE, updateManifest, instance); urlLoader = URLLoader(context).create({ errHandler: config.errHandler, @@ -249,6 +250,79 @@ function ManifestLoader(config) { }); } + function updateManifest(e) { + // Manage situations in which success is called after calling reset + if (!xlinkController) { + return; + } + const strManifest = e.xmlString + let currentManifest = manifestModel.getValue(); + let manifest; + + // Create parser according to manifest type + parser = createParser(strManifest); + + if (parser === null) { + eventBus.trigger(Events.INTERNAL_MANIFEST_LOADED, { + manifest: null, + error: new DashJSError( + Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE, + ) + }); + return; + } + // init xlinkcontroller with created parser + xlinkController.setParser(parser); + + try { + manifest = parser.parse(strManifest); + } catch (e) { + eventBus.trigger(Events.INTERNAL_MANIFEST_LOADED, { + manifest: null, + error: new DashJSError( + Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE, + ) + }); + return; + } + + if (manifest) { + manifest.url = currentManifest.url; + + // URL from which the MPD was originally retrieved (MPD updates will not change this value) + if (!manifest.originalUrl) { + manifest.originalUrl = manifest.url; + } + + // If there is a mismatch between the manifest's specified duration and the total duration of all periods, + // and the specified duration is greater than the total duration of all periods, + // overwrite the manifest's duration attribute. This is a patch for if a manifest is generated incorrectly. + if (settings && + settings.get().streaming.enableManifestDurationMismatchFix && + manifest.mediaPresentationDuration && + 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; + } + } + + // manifest.baseUri = baseUri; + manifest.loadedTime = new Date(); + xlinkController.resolveManifestOnLoad(manifest); + + eventBus.trigger(Events.ORIGINAL_MANIFEST_LOADED, { originalManifest: e.xmlString }); + } else { + eventBus.trigger(Events.INTERNAL_MANIFEST_LOADED, { + manifest: null, + error: new DashJSError( + Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE, + ) + }); + } + } + function reset() { eventBus.off(Events.XLINK_READY, onXlinkReady, instance); diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index e212ce0cc6..bd9dac699a 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -2490,12 +2490,13 @@ function MediaPlayer() { function _createManifestLoader() { return ManifestLoader(context).create({ - debug: debug, - errHandler: errHandler, - dashMetrics: dashMetrics, - mediaPlayerModel: mediaPlayerModel, - mssHandler: mssHandler, - settings: settings + debug, + errHandler, + dashMetrics, + mediaPlayerModel, + mssHandler, + manifestModel, + settings }); } diff --git a/src/streaming/controllers/EventController.js b/src/streaming/controllers/EventController.js index 973a7bab39..31be0f6d95 100644 --- a/src/streaming/controllers/EventController.js +++ b/src/streaming/controllers/EventController.js @@ -31,7 +31,7 @@ import FactoryMaker from '../../core/FactoryMaker.js'; import Debug from '../../core/Debug.js'; -import Events from '../core/events/Events.js'; +import Events from '../../core/events/Events.js'; import EventBus from '../../core/EventBus.js'; import MediaPlayerEvents from '../../streaming/MediaPlayerEvents.js'; import XHRLoader from '../net/XHRLoader.js'; @@ -42,6 +42,8 @@ function EventController() { const MPD_RELOAD_VALUE = 1; const MPD_PATCH_VALUE = 2; const MPD_UPDATE_VALUE = 3; + const MPD_VALIDITY_EXPIRATION_VALUES = [MPD_RELOAD_VALUE, MPD_PATCH_VALUE, MPD_UPDATE_VALUE]; + const MPD_CALLBACK_SCHEME = 'urn:mpeg:dash:event:callback:2015'; const MPD_CALLBACK_VALUE = 1; @@ -329,18 +331,18 @@ function EventController() { * @private */ function _handleManifestValidityExpirationEvent(event, currentVideoTime) { - switch (event.eventStream.value) { + switch (+event.eventStream.value) { case MPD_RELOAD_VALUE: logger.debug(`Starting manifest refresh event ${event.id} at ${currentVideoTime}`); _refreshManifest(); break; case MPD_PATCH_VALUE: logger.debug(`Starting manifest patch event ${event.id} at ${currentVideoTime}`); - _patchManifest(event.messageData); + _patchManifest(event.parsedMessageData); break; case MPD_UPDATE_VALUE: - logger.debug(`Starting manifest patch update ${event.id} at ${currentVideoTime}`); - _updateManifest(event.messageData); + logger.debug(`Starting manifest update ${event.id} at ${currentVideoTime}`); + _updateManifest(event.parsedMessageData); break; } } @@ -351,22 +353,24 @@ function EventController() { */ function _handleManifestReload(event) { try { - const validUntil = event.calculatedPresentationTime; - let newDuration; - if (event.calculatedPresentationTime == 0xFFFFFFFF) {//0xFF... means remaining duration unknown - newDuration = NaN; - } else { - newDuration = event.calculatedPresentationTime + event.duration; + if (MPD_VALIDITY_EXPIRATION_VALUES.includes(event.eventStream.value)) { + const validUntil = event.calculatedPresentationTime; + let newDuration; + if (validUntil == 0xFFFFFFFF) {//0xFF... means remaining duration unknown + newDuration = NaN; + } else { + newDuration = validUntil + event.duration; + } + logger.info('Manifest validity changed: Valid until: ' + validUntil + '; remaining duration: ' + newDuration); + eventBus.trigger(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, { + id: event.id, + validUntil: validUntil, + newDuration: newDuration, + newManifestValidAfter: NaN //event.message_data - this is an arraybuffer with a timestring in it, but not used yet + }, { + mode: MediaPlayerEvents.EVENT_MODE_ON_START + }); } - logger.info('Manifest validity changed: Valid until: ' + validUntil + '; remaining duration: ' + newDuration); - eventBus.trigger(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, { - id: event.id, - validUntil: validUntil, - newDuration: newDuration, - newManifestValidAfter: NaN //event.message_data - this is an arraybuffer with a timestring in it, but not used yet - }, { - mode: MediaPlayerEvents.EVENT_MODE_ON_START - }); } catch (e) { logger.error(e); } @@ -562,14 +566,13 @@ function EventController() { * Patch the manifest * @private */ - function _patchManifest(manifestPatch) { + function _patchManifest(parsedMessageData) { try { checkConfig(); - const event = { - manifest: manifestPatch // Parse manifest before assigne - } - logger.debug(`Patch manifest not supported yet ${event}`); - // eventBus.trigger(Events.INTERNAL_MANIFEST_LOADED, { event }); + const match = parsedMessageData.match(/^([\d-T:Z]+)(<.*)/); + const publishTime = match[1]; + const patchXMLString = match[2] + logger.info(`Patch manifest not supported yet: Publish time: ${publishTime}, MPD patch: ${patchXMLString}..`); } catch (e) { logger.error(e); } @@ -579,13 +582,14 @@ function EventController() { * Update the manifest * @private */ - function _updateManifest(manifest) { + function _updateManifest(parsedMessageData) { try { checkConfig(); - const event = { - manifest // Parse manifest before assigne - } - eventBus.trigger(Events.INTERNAL_MANIFEST_LOADED, { event }); + const match = parsedMessageData.match(/^([\d-T:Z]+)(<.*)/); + const publishTime = match[1]; + const xmlString = match[2] + logger.info(`Updating current manifest. Publish time: ${publishTime}, MPD: ${xmlString}.`); + eventBus.trigger(Events.MPD_EXPIRE_UPDATE, { xmlString }); } catch (e) { logger.error(e); } From 3b6e2a4c1fecb4fa01b092c15299c79ce1f15620 Mon Sep 17 00:00:00 2001 From: Sebastian Piquerez Date: Thu, 20 Feb 2025 16:09:55 -0300 Subject: [PATCH 04/14] Add minimumUpdatePeriod 0 logic with validity expiration inband event --- src/streaming/ManifestUpdater.js | 14 +++++- src/streaming/controllers/EventController.js | 53 +++++++++++++------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/src/streaming/ManifestUpdater.js b/src/streaming/ManifestUpdater.js index 144e816423..80b539de78 100644 --- a/src/streaming/ManifestUpdater.js +++ b/src/streaming/ManifestUpdater.js @@ -58,7 +58,8 @@ function ManifestUpdater() { adapter, errHandler, contentSteeringController, - settings; + settings, + refreshDisabled; function setup() { logger = Debug(context).getInstance().getLogger(instance); @@ -100,6 +101,7 @@ function ManifestUpdater() { eventBus.on(MediaPlayerEvents.PLAYBACK_STARTED, onPlaybackStarted, this); eventBus.on(MediaPlayerEvents.PLAYBACK_PAUSED, onPlaybackPaused, this); eventBus.on(Events.INTERNAL_MANIFEST_LOADED, onManifestLoaded, this); + eventBus.on(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, onManifestValidityChanged, this) } function setManifest(manifest) { @@ -142,7 +144,7 @@ function ManifestUpdater() { delay = refreshDelay * 1000; } - if (!isNaN(delay)) { + if (!isNaN(delay) && !refreshDisabled) { logger.debug('Refresh manifest in ' + delay + ' milliseconds.'); refreshTimer = setTimeout(onRefreshTimer, delay); } @@ -281,6 +283,14 @@ function ManifestUpdater() { } } + function onManifestValidityChanged(e) { + const { minimumUpdatePeriod } = manifestModel.getValue(); + if (minimumUpdatePeriod === 0 && e.inbandEvent) { + stopManifestRefreshTimer(); + refreshDisabled = true; + } + } + function onPlaybackStarted(/*e*/) { isPaused = false; startManifestRefreshTimer(); diff --git a/src/streaming/controllers/EventController.js b/src/streaming/controllers/EventController.js index 31be0f6d95..52b08fbbcd 100644 --- a/src/streaming/controllers/EventController.js +++ b/src/streaming/controllers/EventController.js @@ -331,7 +331,7 @@ function EventController() { * @private */ function _handleManifestValidityExpirationEvent(event, currentVideoTime) { - switch (+event.eventStream.value) { + switch (parseInt(event.eventStream.value)) { case MPD_RELOAD_VALUE: logger.debug(`Starting manifest refresh event ${event.id} at ${currentVideoTime}`); _refreshManifest(); @@ -353,21 +353,31 @@ function EventController() { */ function _handleManifestReload(event) { try { - if (MPD_VALIDITY_EXPIRATION_VALUES.includes(event.eventStream.value)) { - const validUntil = event.calculatedPresentationTime; - let newDuration; - if (validUntil == 0xFFFFFFFF) {//0xFF... means remaining duration unknown - newDuration = NaN; - } else { - newDuration = validUntil + event.duration; - } - logger.info('Manifest validity changed: Valid until: ' + validUntil + '; remaining duration: ' + newDuration); - eventBus.trigger(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, { + const eventValue = parseInt(event.eventStream.value); + if (MPD_VALIDITY_EXPIRATION_VALUES.includes(eventValue)) { + let validityData = { id: event.id, - validUntil: validUntil, - newDuration: newDuration, - newManifestValidAfter: NaN //event.message_data - this is an arraybuffer with a timestring in it, but not used yet - }, { + newManifestValidAfter: NaN, //event.message_data - this is an arraybuffer with a timestring in it, but not used yet + inbandEvent: true, + } + + if (eventValue === MPD_RELOAD_VALUE) { + const validUntil = event.calculatedPresentationTime; + let newDuration; + if (validUntil == 0xFFFFFFFF) {//0xFF... means remaining duration unknown + newDuration = NaN; + } else { + newDuration = validUntil + event.duration; + } + validityData = { + ...validityData, + validUntil: validUntil, + newDuration: newDuration, + } + logger.info('Manifest validity changed: Valid until: ' + validUntil + '; remaining duration: ' + newDuration); + } + + eventBus.trigger(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, validityData, { mode: MediaPlayerEvents.EVENT_MODE_ON_START }); } @@ -585,9 +595,16 @@ function EventController() { function _updateManifest(parsedMessageData) { try { checkConfig(); - const match = parsedMessageData.match(/^([\d-T:Z]+)(<.*)/); - const publishTime = match[1]; - const xmlString = match[2] + const regex = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)(<\?xml[\s\S]*<\/MPD>)/; + const match = parsedMessageData.match(regex); + let publishTime + let xmlString + if (match) { + publishTime = match[1]; + xmlString = match[2]; + } else { + throw new Error('No MPD found in the message data.') + } logger.info(`Updating current manifest. Publish time: ${publishTime}, MPD: ${xmlString}.`); eventBus.trigger(Events.MPD_EXPIRE_UPDATE, { xmlString }); } catch (e) { From 02c288ffc099c8d37c562216412f5c62e9cecc27 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Mon, 24 Mar 2025 14:56:24 -0300 Subject: [PATCH 05/14] add event controller unit test for validity expiration value 3 --- .../streaming.controllers.EventController.js | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/test/unit/test/streaming/streaming.controllers.EventController.js b/test/unit/test/streaming/streaming.controllers.EventController.js index 1d94d27306..70e6967c5a 100644 --- a/test/unit/test/streaming/streaming.controllers.EventController.js +++ b/test/unit/test/streaming/streaming.controllers.EventController.js @@ -1,11 +1,13 @@ import EventController from '../../../../src/streaming/controllers/EventController.js'; import EventBus from '../../../../src/core/EventBus.js'; +import Events from '../../../../src/core/events/Events.js'; import MediaPlayerEvents from '../../../../src/streaming/MediaPlayerEvents.js'; import PlaybackControllerMock from '../../mocks/PlaybackControllerMock.js'; import ManifestUpdaterMock from '../../mocks/ManifestUpdaterMock.js'; import Settings from '../../../../src/core/Settings.js'; import {expect} from 'chai'; +import sinon from 'sinon'; const context = {}; const eventBus = EventBus(context).getInstance(); @@ -575,5 +577,60 @@ describe('EventController', function () { eventBus.off(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, manifestValidityExpiredHandler, this); }); + + it('should fire MPD_EXPIRE_UPDATE events for value 3', async () => { + let newManifestExpiredEventStub = {...manifestExpiredEventStub}; + newManifestExpiredEventStub.eventStream.value = '3'; + newManifestExpiredEventStub.duration = 1; + newManifestExpiredEventStub.calculatedPresentationTime = 0; + newManifestExpiredEventStub.parsedMessageData = `2024-03-24T15:30:45Z + + `; + + const spy = sinon.spy(eventBus, 'trigger'); + const mpdExpireUpdatePromise = new Promise((resolve) => { + eventBus.on(Events.MPD_EXPIRE_UPDATE, () => { + resolve(); + }); + }); + + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Timeout: MPD_EXPIRE_UPDATE event was not triggered')), 4000) + ); + + eventController.addInbandEvents([newManifestExpiredEventStub], 'periodId'); + + await Promise.race([mpdExpireUpdatePromise, timeoutPromise]); + sinon.assert.calledWith(spy, Events.MPD_EXPIRE_UPDATE, sinon.match.any); + sinon.restore() + }); + + it('should fire MANIFEST_VALIDITY_CHANGED events for value 3', async () => { + let newManifestExpiredEventStub = {...manifestExpiredEventStub}; + newManifestExpiredEventStub.eventStream.value = '3'; + newManifestExpiredEventStub.duration = 1; + newManifestExpiredEventStub.calculatedPresentationTime = 0; + newManifestExpiredEventStub.parsedMessageData = `2024-03-24T15:30:45Z + + `; + + const spy = sinon.spy(eventBus, 'trigger'); + const manifestValidityChanged = new Promise((resolve) => { + eventBus.on(MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, () => { + resolve(); + }); + }); + + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Timeout: MANIFEST_VALIDITY_CHANGED event was not triggered')), 4000) + ); + + eventController.addInbandEvents([newManifestExpiredEventStub], 'periodId'); + + await Promise.race([manifestValidityChanged, timeoutPromise]); + sinon.assert.calledWith(spy, MediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, sinon.match.any); + sinon.restore() + }); + }); }); From 3130c6f0f9a70172bc493f99dcb84c21dc43cdea Mon Sep 17 00:00:00 2001 From: Tatiana Perera Date: Tue, 25 Mar 2025 12:23:34 -0300 Subject: [PATCH 06/14] Moved local constants into Constants.js file --- src/streaming/constants/Constants.js | 6 +++++ src/streaming/controllers/EventController.js | 24 ++++++++++---------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index a95bc2d6c0..ebf14c3984 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -334,6 +334,12 @@ export default { ID3_SCHEME_ID_URI: 'https://aomedia.org/emsg/ID3', COMMON_ACCESS_TOKEN_HEADER: 'common-access-token', DASH_ROLE_SCHEME_ID : 'urn:mpeg:dash:role:2011', + MPD_VALIDITY_EXPIRATION: { + SCHEME: 'urn:mpeg:dash:event:2012', + RELOAD_VALUE: 1, + PATCH_VALUE: 2, + UPDATE_VALUE: 3, + }, CODEC_FAMILIES: { MP3: 'mp3', AAC: 'aac', diff --git a/src/streaming/controllers/EventController.js b/src/streaming/controllers/EventController.js index 52b08fbbcd..9db6851b14 100644 --- a/src/streaming/controllers/EventController.js +++ b/src/streaming/controllers/EventController.js @@ -35,15 +35,15 @@ import Events from '../../core/events/Events.js'; import EventBus from '../../core/EventBus.js'; import MediaPlayerEvents from '../../streaming/MediaPlayerEvents.js'; import XHRLoader from '../net/XHRLoader.js'; +import Constants from '../constants/Constants.js'; function EventController() { - const MPD_VALIDITY_EXPIRATION_SCHEME = 'urn:mpeg:dash:event:2012'; - const MPD_RELOAD_VALUE = 1; - const MPD_PATCH_VALUE = 2; - const MPD_UPDATE_VALUE = 3; - const MPD_VALIDITY_EXPIRATION_VALUES = [MPD_RELOAD_VALUE, MPD_PATCH_VALUE, MPD_UPDATE_VALUE]; - + const MPD_VALIDITY_EXPIRATION_VALUES = [ + Constants.MPD_VALIDITY_EXPIRATION.RELOAD_VALUE, + Constants.MPD_VALIDITY_EXPIRATION.PATCH_VALUE, + Constants.MPD_VALIDITY_EXPIRATION.UPDATE_VALUE + ]; const MPD_CALLBACK_SCHEME = 'urn:mpeg:dash:event:callback:2015'; const MPD_CALLBACK_VALUE = 1; @@ -267,7 +267,7 @@ function EventController() { let result = _addOrUpdateEvent(event, inbandEvents[periodId], false); if (result === EVENT_HANDLED_STATES.ADDED) { - if (event.eventStream.schemeIdUri === MPD_VALIDITY_EXPIRATION_SCHEME) { + if (event.eventStream.schemeIdUri === Constants.MPD_VALIDITY_EXPIRATION.SCHEME) { _handleManifestReload(event); } logger.debug(`Added inband event with id ${event.id} from period ${periodId}`); @@ -332,15 +332,15 @@ function EventController() { */ function _handleManifestValidityExpirationEvent(event, currentVideoTime) { switch (parseInt(event.eventStream.value)) { - case MPD_RELOAD_VALUE: + case Constants.MPD_VALIDITY_EXPIRATION.RELOAD_VALUE: logger.debug(`Starting manifest refresh event ${event.id} at ${currentVideoTime}`); _refreshManifest(); break; - case MPD_PATCH_VALUE: + case Constants.MPD_VALIDITY_EXPIRATION.PATCH_VALUE: logger.debug(`Starting manifest patch event ${event.id} at ${currentVideoTime}`); _patchManifest(event.parsedMessageData); break; - case MPD_UPDATE_VALUE: + case Constants.MPD_VALIDITY_EXPIRATION.UPDATE_VALUE: logger.debug(`Starting manifest update ${event.id} at ${currentVideoTime}`); _updateManifest(event.parsedMessageData); break; @@ -361,7 +361,7 @@ function EventController() { inbandEvent: true, } - if (eventValue === MPD_RELOAD_VALUE) { + if (eventValue === Constants.MPD_VALIDITY_EXPIRATION.RELOAD_VALUE) { const validUntil = event.calculatedPresentationTime; let newDuration; if (validUntil == 0xFFFFFFFF) {//0xFF... means remaining duration unknown @@ -515,7 +515,7 @@ function EventController() { } if (!event.triggeredStartEvent) { - if (event.eventStream.schemeIdUri === MPD_VALIDITY_EXPIRATION_SCHEME) { + if (event.eventStream.schemeIdUri === Constants.MPD_VALIDITY_EXPIRATION.SCHEME) { //If both are set to zero, it indicates the media is over at this point. Don't reload the manifest. if (event.duration !== 0 || event.presentationTimeDelta !== 0) { _handleManifestValidityExpirationEvent(event, currentVideoTime) From 1e615b873953a305938c83d3d3c13f7b67231e49 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Thu, 27 Mar 2025 16:45:37 -0300 Subject: [PATCH 07/14] sample page --- samples/mpd-event/mpd-update-event.html | 270 ++++++++++++++++++++++++ samples/samples.json | 17 ++ 2 files changed, 287 insertions(+) create mode 100644 samples/mpd-event/mpd-update-event.html diff --git a/samples/mpd-event/mpd-update-event.html b/samples/mpd-event/mpd-update-event.html new file mode 100644 index 0000000000..073a49e1f9 --- /dev/null +++ b/samples/mpd-event/mpd-update-event.html @@ -0,0 +1,270 @@ + + + + + + MPD update events + + + + + + + + +
+ +
+
+ +
+
+
+
+

MPD Update Event

+

This sample shows how DASH MPD Update Event signals a DASH client to perform a manifest update when an inband event is received with value 3.

+
+
+
+ +
+
+ Manifest URL + + +
+
+ +
+
+ +
+ +
+ +
+
Manifest will appear here...
+
+
+
+
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/samples/samples.json b/samples/samples.json index 3c8a44999b..33445da9a2 100644 --- a/samples/samples.json +++ b/samples/samples.json @@ -886,5 +886,22 @@ ] } ] + }, + { + "section": "MPD Update Event (value 3)", + "samples": [ + { + "title": "MPD Update Event (value 3) implementation", + "description": "A sample showing ... implementation", + "href": "mpd-event/mpd-update-event.html", + "image": "lib/img/bbb-1.jpg", + "labels": [ + "VoD", + "Video", + "Audio", + "Callback events" + ] + } + ] } ] From a99baa762acad3ce6e1855dcbb96560a251e5d48 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Mon, 31 Mar 2025 15:42:39 -0300 Subject: [PATCH 08/14] sample page description --- samples/samples.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/samples.json b/samples/samples.json index 33445da9a2..93ddb97ccc 100644 --- a/samples/samples.json +++ b/samples/samples.json @@ -891,8 +891,8 @@ "section": "MPD Update Event (value 3)", "samples": [ { - "title": "MPD Update Event (value 3) implementation", - "description": "A sample showing ... implementation", + "title": "MPD Update Event implementation", + "description": "A sample showing MPD Update Event (value 3) implementation", "href": "mpd-event/mpd-update-event.html", "image": "lib/img/bbb-1.jpg", "labels": [ From e66c74362e0910ea8ea4b37bfb9085cbedc83ef5 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Wed, 8 Oct 2025 15:16:44 -0300 Subject: [PATCH 09/14] manifest updater revert instance variables order --- src/streaming/ManifestUpdater.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/streaming/ManifestUpdater.js b/src/streaming/ManifestUpdater.js index 573f020503..c2cf1831dc 100644 --- a/src/streaming/ManifestUpdater.js +++ b/src/streaming/ManifestUpdater.js @@ -359,12 +359,12 @@ function ManifestUpdater() { } instance = { - getIsUpdating, initialize, + setManifest, refreshManifest, - reset, + getIsUpdating, setConfig, - setManifest + reset }; setup(); From e58aca116c74a250768a63faf462dcc6b4e72830 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Wed, 8 Oct 2025 15:18:22 -0300 Subject: [PATCH 10/14] manifest updater revert instance variables order --- src/streaming/ManifestUpdater.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/streaming/ManifestUpdater.js b/src/streaming/ManifestUpdater.js index c2cf1831dc..573f020503 100644 --- a/src/streaming/ManifestUpdater.js +++ b/src/streaming/ManifestUpdater.js @@ -359,12 +359,12 @@ function ManifestUpdater() { } instance = { + getIsUpdating, initialize, - setManifest, refreshManifest, - getIsUpdating, + reset, setConfig, - reset + setManifest }; setup(); From 2b5c0cfa9453349d50bc3feab3245d7bf95286a6 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Wed, 8 Oct 2025 15:28:42 -0300 Subject: [PATCH 11/14] fix sample page dash dist path --- samples/mpd-event/mpd-update-event.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/mpd-event/mpd-update-event.html b/samples/mpd-event/mpd-update-event.html index 073a49e1f9..87416f8f15 100644 --- a/samples/mpd-event/mpd-update-event.html +++ b/samples/mpd-event/mpd-update-event.html @@ -82,7 +82,7 @@
- +
Date: Wed, 8 Oct 2025 15:31:51 -0300 Subject: [PATCH 12/14] add valid parsedMessageData unit test --- test/unit/test/dash/dash.DashAdapter.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/unit/test/dash/dash.DashAdapter.js b/test/unit/test/dash/dash.DashAdapter.js index f400637fcf..5baf2c7360 100644 --- a/test/unit/test/dash/dash.DashAdapter.js +++ b/test/unit/test/dash/dash.DashAdapter.js @@ -356,6 +356,20 @@ describe('DashAdapter', function () { expect(event).to.be.an('object'); }); + it('should return an event with a valid parsedMessageData', function () { + const representation = { presentationTimeOffset: 0, adaptation: { period: { start: 0 } } }; + const messageData = new Uint8Array([10, 20, 30]); + const eventBox = { + scheme_id_uri: 'id', + value: 'value', + message_data: messageData, + } + + const event = dashAdapter.getEvent( eventBox, { 'id/value': {} }, 0, representation); + expect(event.parsedMessageData).to.be.a('string').and.not.empty; + expect(event.parsedMessageData).to.not.be.undefined; + }); + it('should calculate correct start time for a version 0 event without PTO', function () { const representation = { adaptation: { period: { start: 10 } } }; const eventBox = { scheme_id_uri: 'id', value: 'value', presentation_time_delta: 12, version: 0 }; From 7b64001a7a123023f6fe87b8726073e85a94d3b3 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Wed, 8 Oct 2025 15:59:34 -0300 Subject: [PATCH 13/14] manifest loader unit tests --- src/streaming/ManifestLoader.js | 4 +- .../streaming/streaming.ManifestLoader.js | 164 ++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 test/unit/test/streaming/streaming.ManifestLoader.js diff --git a/src/streaming/ManifestLoader.js b/src/streaming/ManifestLoader.js index 81e0c1182e..da8a04cea5 100644 --- a/src/streaming/ManifestLoader.js +++ b/src/streaming/ManifestLoader.js @@ -63,7 +63,7 @@ function ManifestLoader(config) { function setup() { logger = debug.getLogger(instance); eventBus.on(Events.XLINK_READY, onXlinkReady, instance); - eventBus.on(Events.MPD_EXPIRE_UPDATE, updateManifest, instance); + eventBus.on(Events.MPD_EXPIRE_UPDATE, _updateManifest, instance); urlLoader = URLLoader(context).create({ errHandler: config.errHandler, @@ -250,7 +250,7 @@ function ManifestLoader(config) { }); } - function updateManifest(e) { + function _updateManifest(e) { // Manage situations in which success is called after calling reset if (!xlinkController) { return; diff --git a/test/unit/test/streaming/streaming.ManifestLoader.js b/test/unit/test/streaming/streaming.ManifestLoader.js new file mode 100644 index 0000000000..9ad495067d --- /dev/null +++ b/test/unit/test/streaming/streaming.ManifestLoader.js @@ -0,0 +1,164 @@ +import ManifestLoader from '../../../../src/streaming/ManifestLoader.js'; +import Events from '../../../../src/core/events/Events.js'; +import EventBus from '../../../../src/core/EventBus.js'; +import Errors from '../../../../src/core/errors/Errors.js'; +import Settings from '../../../../src/core/Settings.js'; +import ErrorHandlerMock from '../../mocks/ErrorHandlerMock.js'; +import ManifestModelMock from '../../mocks/ManifestModelMock.js'; +import DashMetricsMock from '../../mocks/DashMetricsMock.js'; +import MediaPlayerModelMock from '../../mocks/MediaPlayerModelMock.js'; +import DebugMock from '../../mocks/DebugMock.js'; + +import {expect} from 'chai'; +import sinon from 'sinon'; + +describe('ManifestLoader', function () { + const context = {}; + const eventBus = EventBus(context).getInstance(); + let manifestLoader; + let errorHandlerMock; + let manifestModelMock; + let dashMetricsMock; + let mediaPlayerModelMock; + let debugMock; + let settings; + + beforeEach(function () { + errorHandlerMock = new ErrorHandlerMock(); + manifestModelMock = new ManifestModelMock(); + dashMetricsMock = new DashMetricsMock(); + mediaPlayerModelMock = new MediaPlayerModelMock(); + debugMock = new DebugMock(); + settings = Settings(context).getInstance(); + + manifestModelMock.setValue({ + url: 'http://example.com/manifest.mpd', + originalUrl: 'http://example.com/manifest.mpd' + }); + + manifestLoader = ManifestLoader(context).create({ + errHandler: errorHandlerMock, + manifestModel: manifestModelMock, + dashMetrics: dashMetricsMock, + mediaPlayerModel: mediaPlayerModelMock, + debug: debugMock, + settings: settings + }); + }); + + afterEach(function () { + manifestLoader.reset(); + eventBus.reset(); + settings.reset(); + }); + + describe('when MPD_EXPIRE_UPDATE event is triggered', function () { + + it('should trigger INTERNAL_MANIFEST_LOADED with MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE when parser.parse() throws an exception', function (done) { + const errorHandler = function (event) { + try { + expect(event.manifest).to.be.null; + expect(event.error).to.exist; + expect(event.error.code).to.equal(Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE); + + eventBus.off(Events.INTERNAL_MANIFEST_LOADED, errorHandler); + done(); + } catch (error) { + done(error); + } + }; + + eventBus.on(Events.INTERNAL_MANIFEST_LOADED, errorHandler, this); + + const malformedXml = ''; + + eventBus.trigger(Events.MPD_EXPIRE_UPDATE, { + xmlString: malformedXml + }); + }); + + it('should trigger INTERNAL_MANIFEST_LOADED with MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE when createParser returns null', function (done) { + const errorHandler = function (event) { + try { + expect(event.manifest).to.be.null; + expect(event.error).to.exist; + expect(event.error.code).to.equal(Errors.MANIFEST_LOADER_PARSING_FAILURE_ERROR_CODE); + + eventBus.off(Events.INTERNAL_MANIFEST_LOADED, errorHandler); + done(); + } catch (error) { + done(error); + } + }; + + eventBus.on(Events.INTERNAL_MANIFEST_LOADED, errorHandler, this); + + const unrecognizedFormat = 'This is not a valid manifest format'; + + eventBus.trigger(Events.MPD_EXPIRE_UPDATE, { + xmlString: unrecognizedFormat + }); + }); + + it('should not trigger INTERNAL_MANIFEST_LOADED when _updateManifest is called but xlinkController is null', function () { + // Reset manifest loader to simulate xlinkController being null + manifestLoader.reset(); + + const spy = sinon.spy(); + eventBus.on(Events.INTERNAL_MANIFEST_LOADED, spy); + + eventBus.trigger(Events.MPD_EXPIRE_UPDATE, { + xmlString: '' + }); + + // Assert: No event should be triggered + sinon.assert.notCalled(spy); + + eventBus.off(Events.INTERNAL_MANIFEST_LOADED, spy); + }); + + it('should successfully process valid DASH manifest and trigger INTERNAL_MANIFEST_LOADED without error', function (done) { + // Arrange: Set up event listener to capture successful processing + const successHandler = function (event) { + try { + expect(event.manifest).to.exist; + expect(event.error).to.be.undefined; + + eventBus.off(Events.INTERNAL_MANIFEST_LOADED, successHandler); + done(); + } catch (error) { + done(error); + } + }; + + eventBus.on(Events.INTERNAL_MANIFEST_LOADED, successHandler, this); + + // Act: Trigger MPD_EXPIRE_UPDATE with valid DASH manifest + const validDashManifest = ` + + + + + + + + + + + + + `; + + eventBus.trigger(Events.MPD_EXPIRE_UPDATE, { + xmlString: validDashManifest + }); + }); + }); +}); \ No newline at end of file From 4143d364240ca52ceef1986d864ce55a0906d201 Mon Sep 17 00:00:00 2001 From: Constanza Dibueno Date: Wed, 8 Oct 2025 16:00:33 -0300 Subject: [PATCH 14/14] remove comments --- test/unit/test/streaming/streaming.ManifestLoader.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/unit/test/streaming/streaming.ManifestLoader.js b/test/unit/test/streaming/streaming.ManifestLoader.js index 9ad495067d..4c72b3952c 100644 --- a/test/unit/test/streaming/streaming.ManifestLoader.js +++ b/test/unit/test/streaming/streaming.ManifestLoader.js @@ -118,7 +118,6 @@ describe('ManifestLoader', function () { }); it('should successfully process valid DASH manifest and trigger INTERNAL_MANIFEST_LOADED without error', function (done) { - // Arrange: Set up event listener to capture successful processing const successHandler = function (event) { try { expect(event.manifest).to.exist; @@ -133,7 +132,6 @@ describe('ManifestLoader', function () { eventBus.on(Events.INTERNAL_MANIFEST_LOADED, successHandler, this); - // Act: Trigger MPD_EXPIRE_UPDATE with valid DASH manifest const validDashManifest = `