From 0837a993ba75e7ff535d9c2b17733ea08dea7a49 Mon Sep 17 00:00:00 2001 From: Daniel Silhavy Date: Wed, 17 Apr 2024 15:22:30 +0200 Subject: [PATCH] Feature/mpd patching test (#4462) * Add functional test for MPD patching * Add sample for MPD patching * Add new function to Typescript file and revert changes in package.json * Return right type in JSDoc * Change single teststream * Run tests on Chrome --- index.d.ts | 4 +- samples/live-streaming/mpd-patching.html | 116 ++++++++++++++++++ samples/samples.json | 11 ++ src/streaming/MediaPlayer.js | 8 ++ test/functional/adapter/DashJsAdapter.js | 25 ++++ .../config/karma.functional.conf.cjs | 2 +- .../test-configurations/streams/all.json | 22 ++++ .../test-configurations/streams/single.json | 8 +- .../test-configurations/streams/smoke.json | 13 +- test/functional/src/Constants.js | 3 +- .../cmcd.js | 2 +- .../test/feature-support/emsg-triggered.js | 1 - .../test/feature-support/mpd-patching.js | 55 +++++++++ 13 files changed, 260 insertions(+), 10 deletions(-) create mode 100644 samples/live-streaming/mpd-patching.html rename test/functional/test/{playback-advanced => feature-support}/cmcd.js (97%) create mode 100644 test/functional/test/feature-support/mpd-patching.js diff --git a/index.d.ts b/index.d.ts index 6e0b72ffb7..303ce46fda 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1571,6 +1571,8 @@ declare namespace dashjs { destroy(): void; + getManifest(): object; + } interface MediaPlayerErrors { @@ -3690,7 +3692,7 @@ declare namespace dashjs { getFonts(): FontInfo[]; getFontsForTrackId(trackId: number): FontInfo[]; - + reset(): void; } diff --git a/samples/live-streaming/mpd-patching.html b/samples/live-streaming/mpd-patching.html new file mode 100644 index 0000000000..729faaae6a --- /dev/null +++ b/samples/live-streaming/mpd-patching.html @@ -0,0 +1,116 @@ + + + + + Live stream with MPD Patching + + + + + + + + + + + + + +
+
+
+ +
+
+
+
+

Live stream with MPD Patching

+

Example showing how dash.js handles live streams with updates to the manifest file provided via + MPD patches.

+
+
+
+
+
+ +
+
+
+
+
+

Last MPD update

+
+
+
+
+
+
+
+
+
+
+
+ © DASH-IF +
+
+
+ + + + + + diff --git a/samples/samples.json b/samples/samples.json index 099ef40f80..5304b1de7e 100644 --- a/samples/samples.json +++ b/samples/samples.json @@ -165,6 +165,17 @@ "Video", "Audio" ] + }, + { + "title": "Live stream with MPD Patching", + "description": "Example showing how dash.js handles live streams with updates to the MPD provided via MPD patching", + "href": "live-streaming/mpd-patching.html", + "image": "lib/img/livesim-1.jpg", + "labels": [ + "Live", + "Video", + "Audio" + ] } ] }, diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index 1dc73b4800..91e1fe627a 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -2171,6 +2171,13 @@ function MediaPlayer() { } } + /** + * Returns the current manifest + * @returns {object} + */ + function getManifest() { + return manifestModel.getValue(); + } /** * Returns all BaseURLs that are available including synthesized elements (e.g by content steering) @@ -2642,6 +2649,7 @@ function MediaPlayer() { getPlaybackRate, getProtectionController, getCurrentRepresentationForType, + getManifest, getRepresentationsByType, getSettings, getSource, diff --git a/test/functional/adapter/DashJsAdapter.js b/test/functional/adapter/DashJsAdapter.js index 6b0b9e5c26..64b6cbac36 100644 --- a/test/functional/adapter/DashJsAdapter.js +++ b/test/functional/adapter/DashJsAdapter.js @@ -192,6 +192,10 @@ class DashJsAdapter { return this.player.getTargetLiveDelay(); } + getManifest() { + return this.player.getManifest(); + } + seek(value) { this.player.seek(value); } @@ -478,6 +482,27 @@ class DashJsAdapter { }) } + async waitForEventAndGetPayload(timeoutValue, event) { + return new Promise((resolve) => { + let timeout = null; + + const _onComplete = (res) => { + clearTimeout(timeout); + timeout = null; + this.player.off(event, _onEvent); + resolve(res); + } + const _onTimeout = () => { + _onComplete(null); + } + const _onEvent = (e) => { + _onComplete(e); + } + timeout = setTimeout(_onTimeout, timeoutValue); + this.player.on(event, _onEvent); + }) + } + async waitForMediaSegmentDownload(timeoutValue) { return new Promise((resolve) => { let timeout = null; diff --git a/test/functional/config/karma.functional.conf.cjs b/test/functional/config/karma.functional.conf.cjs index 1b690cbbac..b3c8d0edda 100644 --- a/test/functional/config/karma.functional.conf.cjs +++ b/test/functional/config/karma.functional.conf.cjs @@ -125,7 +125,7 @@ module.exports = function (config) { autoWatch: false, browserNoActivityTimeout: 180000, - browserDisconnectTimeout: 10000, + browserDisconnectTimeout: 20000, browserDisconnectTolerance: 3, // start these browsers diff --git a/test/functional/config/test-configurations/streams/all.json b/test/functional/config/test-configurations/streams/all.json index d8eff6677d..d88e4abc37 100644 --- a/test/functional/config/test-configurations/streams/all.json +++ b/test/functional/config/test-configurations/streams/all.json @@ -419,6 +419,28 @@ } ] } + }, + { + "name": "MPD Patching with $time", + "url": "https://192-46-234-23.ip.linodeusercontent.com/livesim2/segtimeline_1/patch_60/testpic_2s/Manifest.mpd", + "type": "live", + "testdata": { + "mpdPatching": true + }, + "includedTestfiles": [ + "feature-support/mpd-patching" + ] + }, + { + "name": "MPD Patching with $number", + "url": "https://192-46-234-23.ip.linodeusercontent.com/livesim2/segtimelinenr_1/patch_60/testpic_2s/Manifest.mpd", + "type": "live", + "testdata": { + "mpdPatching": true + }, + "includedTestfiles": [ + "feature-support/mpd-patching" + ] } ] } diff --git a/test/functional/config/test-configurations/streams/single.json b/test/functional/config/test-configurations/streams/single.json index 462f24d6fa..2c26ef27b1 100644 --- a/test/functional/config/test-configurations/streams/single.json +++ b/test/functional/config/test-configurations/streams/single.json @@ -9,11 +9,11 @@ }, "testvectors": [ { - "name": "AWS Single Period $number$", - "url": "https://d10gktn8v7end7.cloudfront.net/out/v1/6ee19df3afa24fe190a8ae16c2c88560/index.mpd", - "type": "live", + "name": "Segment Base", + "type": "vod", + "url": "https://dash.akamaized.net/dash264/TestCases/1a/sony/SNE_DASH_SD_CASE1A_REVISED.mpd", "includedTestfiles": [ - "playback-advanced/cmcd" + "playback/*" ] } ] diff --git a/test/functional/config/test-configurations/streams/smoke.json b/test/functional/config/test-configurations/streams/smoke.json index bbd94da482..1477d692a2 100644 --- a/test/functional/config/test-configurations/streams/smoke.json +++ b/test/functional/config/test-configurations/streams/smoke.json @@ -74,7 +74,7 @@ "playback/play", "playback/pause", "playback/seek", - "playback-advanced/cmcd", + "feature-support/cmcd", "playback-advanced/preload" ] }, @@ -152,6 +152,17 @@ "includedTestfiles": [ "playback/*" ] + }, + { + "name": "MPD Patching with $time", + "url": "https://192-46-234-23.ip.linodeusercontent.com/livesim2/segtimeline_1/patch_60/testpic_2s/Manifest.mpd", + "type": "live", + "testdata": { + "mpdPatching": true + }, + "includedTestfiles": [ + "feature-support/mpd-patching" + ] } ] } diff --git a/test/functional/src/Constants.js b/test/functional/src/Constants.js index c96b400a02..90062e0059 100644 --- a/test/functional/src/Constants.js +++ b/test/functional/src/Constants.js @@ -136,6 +136,8 @@ TESTCASES.BUFFER.INITIAL_TARGET = TESTCASES.CATEGORIES.BUFFER + 'initial-buffer- TESTCASES.BUFFER.TARGET = TESTCASES.CATEGORIES.BUFFER + 'buffer-target'; TESTCASES.FEATURE_SUPPORT.EMSG_TRIGGERED = TESTCASES.CATEGORIES.FEATURE_SUPPORT + 'emsg-triggered'; +TESTCASES.FEATURE_SUPPORT.MPD_PATCHING = TESTCASES.CATEGORIES.FEATURE_SUPPORT + 'mpd-patching'; +TESTCASES.FEATURE_SUPPORT.CMCD = TESTCASES.CATEGORIES.FEATURE_SUPPORT + 'cmcd'; TESTCASES.LIVE.CATCHUP = TESTCASES.CATEGORIES.LIVE + 'latency-catchup'; TESTCASES.LIVE.DELAY = TESTCASES.CATEGORIES.LIVE + 'live-delay'; @@ -150,7 +152,6 @@ TESTCASES.PLAYBACK_ADVANCED.ATTACH_AT_NON_ZERO = TESTCASES.CATEGORIES.PLAYBACK_A TESTCASES.PLAYBACK_ADVANCED.ATTACH_WITH_POSIX = TESTCASES.CATEGORIES.PLAYBACK_ADVANCED + 'attach-with-posix'; TESTCASES.PLAYBACK_ADVANCED.MPD_ANCHOR = TESTCASES.CATEGORIES.PLAYBACK_ADVANCED + 'mpd-anchor'; TESTCASES.PLAYBACK_ADVANCED.MULTIPERIOD_PLAYBACK = TESTCASES.CATEGORIES.PLAYBACK_ADVANCED + 'multiperiod-playback'; -TESTCASES.PLAYBACK_ADVANCED.CMCD = TESTCASES.CATEGORIES.PLAYBACK_ADVANCED + 'cmcd'; TESTCASES.PLAYBACK_ADVANCED.PRELOAD = TESTCASES.CATEGORIES.PLAYBACK_ADVANCED + 'preload'; TESTCASES.TEXT.INITIAL = TESTCASES.CATEGORIES.TEXT + 'initial-text'; diff --git a/test/functional/test/playback-advanced/cmcd.js b/test/functional/test/feature-support/cmcd.js similarity index 97% rename from test/functional/test/playback-advanced/cmcd.js rename to test/functional/test/feature-support/cmcd.js index b07cb2f16a..d9b4a250be 100644 --- a/test/functional/test/playback-advanced/cmcd.js +++ b/test/functional/test/feature-support/cmcd.js @@ -9,7 +9,7 @@ import { } from '../common/common.js'; import {expect} from 'chai'; -const TESTCASE = Constants.TESTCASES.PLAYBACK_ADVANCED.CMCD; +const TESTCASE = Constants.TESTCASES.FEATURE_SUPPORT.CMCD; Utils.getTestvectorsForTestcase(TESTCASE).forEach((item) => { const mpd = item.url; diff --git a/test/functional/test/feature-support/emsg-triggered.js b/test/functional/test/feature-support/emsg-triggered.js index 5920d30cbd..a417fb5f25 100644 --- a/test/functional/test/feature-support/emsg-triggered.js +++ b/test/functional/test/feature-support/emsg-triggered.js @@ -1,4 +1,3 @@ -import DashJsAdapter from '../../adapter/DashJsAdapter.js'; import Constants from '../../src/Constants.js'; import Utils from '../../src/Utils.js'; import {expect} from 'chai' diff --git a/test/functional/test/feature-support/mpd-patching.js b/test/functional/test/feature-support/mpd-patching.js new file mode 100644 index 0000000000..e9e0fe78b6 --- /dev/null +++ b/test/functional/test/feature-support/mpd-patching.js @@ -0,0 +1,55 @@ +import Constants from '../../src/Constants.js'; +import Utils from '../../src/Utils.js'; +import {expect} from 'chai' +import {checkIsPlaying, checkIsProgressing, checkNoCriticalErrors, initializeDashJsAdapter} from '../common/common.js'; + +const TESTCASE = Constants.TESTCASES.FEATURE_SUPPORT.MPD_PATCHING; + +Utils.getTestvectorsForTestcase(TESTCASE).forEach((item) => { + const mpd = item.url; + + describe(`${TESTCASE} - ${item.name} - ${mpd}`, function () { + + let playerAdapter + + before(function () { + if (item.type === Constants.CONTENT_TYPES.VOD || !item.testdata || !item.testdata.mpdPatching) { + this.skip(); + } + playerAdapter = initializeDashJsAdapter(item, mpd); + }) + + after(() => { + if (playerAdapter) { + playerAdapter.destroy(); + } + }) + + it(`Checking playing state`, async () => { + await checkIsPlaying(playerAdapter, true); + }) + + it(`Checking progressing state`, async () => { + await checkIsProgressing(playerAdapter); + }); + + it(`Two consecutive manifest updates shall be of type Patch`, async () => { + const manifest = playerAdapter.getManifest(); + const minimumUpdatePeriodInMs = parseInt(manifest.minimumUpdatePeriod) * 1000; + + let manifestUpdateEvent = await playerAdapter.waitForEventAndGetPayload(minimumUpdatePeriodInMs * 2, 'internalManifestLoaded') + expect(manifestUpdateEvent.manifest.tagName).to.be.equal('Patch') + manifestUpdateEvent = await playerAdapter.waitForEventAndGetPayload(minimumUpdatePeriodInMs * 2, 'internalManifestLoaded') + expect(manifestUpdateEvent.manifest.tagName).to.be.equal('Patch') + }); + + it(`Should still be progressing`, async () => { + await checkIsProgressing(playerAdapter); + }); + + it(`Expect no critical errors to be thrown`, () => { + checkNoCriticalErrors(playerAdapter); + }) + + }) +})