From c2891a4dde15284c1dbc3a7d48a222d96a0f436e Mon Sep 17 00:00:00 2001 From: Joel Keers Date: Tue, 20 Aug 2024 14:10:04 +0100 Subject: [PATCH] Refresh manifest on validity changed event (#341) * refresh manifest on validity changed event, fix test state leakage * update wording for test and add another * log out the clamped seek time when seeking on a growing window * use configurable-playback-ended branch of dash.js * update deps --------- Co-authored-by: Matt Stephenson --- package-lock.json | 31 ++++++++-------- package.json | 2 +- src/playbackstrategy/msestrategy.js | 15 +++++--- src/playbackstrategy/msestrategy.test.js | 45 +++++++++++++++++++++--- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/package-lock.json b/package-lock.json index e7d23f19..d8b8a2a2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "dashjs": "github:bbc/dash.js#smp-v4.7.3-2", + "dashjs": "github:bbc/dash.js#smp-v4.7.3-3", "smp-imsc": "github:bbc/imscJS#v1.0.3" }, "devDependencies": { @@ -4795,11 +4795,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -5516,8 +5516,7 @@ }, "node_modules/dashjs": { "version": "4.7.3", - "resolved": "git+ssh://git@github.com/bbc/dash.js.git#9d4535e6b374ca30474d96c0f4cddee05010a869", - "license": "BSD-3-Clause", + "resolved": "git+ssh://git@github.com/bbc/dash.js.git#20c3473aacc06165dbec4dbb75f2c585ca9fa716", "dependencies": { "bcp-47-match": "^2.0.3", "bcp-47-normalize": "^2.3.0", @@ -6580,9 +6579,9 @@ "integrity": "sha512-0k45oWBokCqh2MOexeYKpyqmGKG+8mQ2Wd8iawx+uWd/weWJQAZ6SoPybagdCI4xFisag8iAR77WPm4h3pTfxA==" }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -10189,9 +10188,9 @@ "dev": true }, "node_modules/livereload/node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" @@ -13752,9 +13751,9 @@ } }, "node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 0a4875e6..cd63ee6b 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "typescript-eslint": "^7.2.0" }, "dependencies": { - "dashjs": "github:bbc/dash.js#smp-v4.7.3-2", + "dashjs": "github:bbc/dash.js#smp-v4.7.3-3", "smp-imsc": "github:bbc/imscJS#v1.0.3" }, "repository": { diff --git a/src/playbackstrategy/msestrategy.js b/src/playbackstrategy/msestrategy.js index 7cd49d55..395e28ef 100644 --- a/src/playbackstrategy/msestrategy.js +++ b/src/playbackstrategy/msestrategy.js @@ -283,6 +283,11 @@ function MSEStrategy(mediaSources, windowType, mediaKind, playbackElement, isUHD function onManifestValidityChange(event) { DebugTool.info(`Manifest validity changed. Duration is: ${event.newDuration}`) + if (windowType === WindowTypes.GROWING) { + mediaPlayer.refreshManifest((manifest) => { + DebugTool.info(`Manifest Refreshed. Duration is: ${manifest.mediaPresentationDuration}`) + }) + } } function onStreamInitialised() { @@ -569,10 +574,12 @@ function MSEStrategy(mediaSources, windowType, mediaKind, playbackElement, isUHD if (isNaN(mediaPresentationDuration)) { mediaPlayer.seek(seekToTime) } else { - DebugTool.info("Stream ended. Clamping seek point to end of stream") - mediaPlayer.seek( - getClampedTime(seekToTime, { start: getSeekableRange().start, end: mediaPresentationDuration }) - ) + const clampedSeekTime = getClampedTime(seekToTime, { + start: getSeekableRange().start, + end: mediaPresentationDuration, + }) + DebugTool.info(`Stream ended. Clamping seek point to end of stream - seek point now: ${clampedSeekTime}`) + mediaPlayer.seek(clampedSeekTime) } }) } diff --git a/src/playbackstrategy/msestrategy.test.js b/src/playbackstrategy/msestrategy.test.js index 9097dde7..09787379 100644 --- a/src/playbackstrategy/msestrategy.test.js +++ b/src/playbackstrategy/msestrategy.test.js @@ -13,6 +13,7 @@ import PauseTriggers from "../models/pausetriggers" const mockDashInstance = { initialize: jest.fn(), retrieveManifest: jest.fn(), + refreshManifest: jest.fn(), getDebug: jest.fn(), getSource: jest.fn(), on: jest.fn(), @@ -65,7 +66,7 @@ describe("Media Source Extensions Playback Strategy", () => { let mseStrategy let eventCallbacks let dashEventCallback - const eventHandlers = {} + let eventHandlers let playbackElement let cdnArray = [] let mediaSources @@ -92,6 +93,7 @@ describe("Media Source Extensions Playback Strategy", () => { beforeEach(() => { jest.clearAllMocks() + eventHandlers = {} delete window.bigscreenPlayer mediaElement = undefined @@ -105,7 +107,8 @@ describe("Media Source Extensions Playback Strategy", () => { mockDashInstance.on.mockImplementation((eventType, handler) => { eventHandlers[eventType] = handler - dashEventCallback = (eventType, event) => eventHandlers[eventType].call(eventType, event) + dashEventCallback = (eventType, event) => + eventHandlers[eventType] ? eventHandlers[eventType].call(eventType, event) : null }) mockDashInstance.getDashMetrics.mockReturnValue({ @@ -1115,13 +1118,46 @@ describe("Media Source Extensions Playback Strategy", () => { describe("onManifestLoaded", () => { it("calls onManifestLoaded plugin with the manifest when dashjs loads it", () => { const onManifestLoadedSpy = jest.spyOn(Plugins.interface, "onManifestLoaded") - + setUpMSE(0, WindowTypes.SLIDING) + mseStrategy.load(null, 0) dashEventCallback(dashjsMediaPlayerEvents.MANIFEST_LOADED, testManifestObject) expect(onManifestLoadedSpy).toHaveBeenCalledWith(expect.any(Object)) }) }) + describe("onManifestValidityChanged", () => { + beforeEach(() => { + mockDashInstance.refreshManifest.mockReset() + }) + + it("calls refreshManifest on mediaPlayer with a growing window", () => { + setUpMSE(0, WindowTypes.GROWING) + mseStrategy.load(null, 0) + dashEventCallback(dashjsMediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, testManifestObject) + + expect(mockDashInstance.refreshManifest).toHaveBeenCalledTimes(1) + }) + + it("does not call refreshManifest on mediaPlayer with a sliding window", () => { + setUpMSE(0, WindowTypes.SLIDING) + + mseStrategy.load(null, 0) + dashEventCallback(dashjsMediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, testManifestObject) + + expect(mockDashInstance.refreshManifest).not.toHaveBeenCalled() + }) + + it("does not call refreshManifest on mediaPlayer with a static window", () => { + setUpMSE(0, WindowTypes.STATIC) + + mseStrategy.load(null, 0) + dashEventCallback(dashjsMediaPlayerEvents.MANIFEST_VALIDITY_CHANGED, testManifestObject) + + expect(mockDashInstance.refreshManifest).not.toHaveBeenCalled() + }) + }) + describe("onMetricAdded and onQualityChangeRendered", () => { const mockEvent = { mediaType: "video", @@ -1454,7 +1490,7 @@ describe("Media Source Extensions Playback Strategy", () => { describe("gap jumps", () => { it("logs a seek triggered by a gap to the debugger", () => { setUpMSE() - + mseStrategy.load(null, 0) dashEventCallback("gapCausedInternalSeek", { duration: 0.3, seekTime: 33.3 }) expect(DebugTool.gap).toHaveBeenCalledTimes(1) @@ -1463,6 +1499,7 @@ describe("Media Source Extensions Playback Strategy", () => { it("logs a seek to end triggered by a gap to the debugger", () => { setUpMSE() + mseStrategy.load(null, 0) dashEventCallback("gapCausedSeekToPeriodEnd", { duration: 0.3, seekTime: 33.3 })