diff --git a/karma.conf.js b/karma.conf.js index 3c99eebff5..20fbd77ace 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -257,6 +257,7 @@ module.exports = (config) => { {pattern: 'test/test/assets/dash-multi-codec/*', included: false}, {pattern: 'test/test/assets/dash-multi-codec-ec3/*', included: false}, {pattern: 'test/test/assets/3675/*', included: false}, + {pattern: 'test/test/assets/7401/*', included: false}, {pattern: 'test/test/assets/6339/*', included: false}, {pattern: 'test/test/assets/dash-aes-128/*', included: false}, {pattern: 'test/test/assets/dash-clearkey/*', included: false}, diff --git a/lib/dash/dash_parser.js b/lib/dash/dash_parser.js index 74ef240013..9f2721a9ca 100644 --- a/lib/dash/dash_parser.js +++ b/lib/dash/dash_parser.js @@ -1341,11 +1341,13 @@ shaka.dash.DashParser = class { for (let i = 0; i < periodNodes.length; i++) { const elem = periodNodes[i]; const next = periodNodes[i + 1]; - const start = /** @type {number} */ ( + let start = /** @type {number} */ ( TXml.parseAttr(elem, 'start', TXml.parseDuration, prevEnd)); const periodId = elem.attributes['id']; const givenDuration = TXml.parseAttr(elem, 'duration', TXml.parseDuration); + start = (i == 0 && start == 0 && this.isTransitionFromDynamicToStatic_) ? + seekRangeStart : start; let periodDuration = null; if (next) { @@ -1355,7 +1357,7 @@ shaka.dash.DashParser = class { const nextStart = TXml.parseAttr(next, 'start', TXml.parseDuration); if (nextStart != null) { - periodDuration = nextStart - start; + periodDuration = nextStart - start + seekRangeStart; } } else if (presentationDuration != null) { // "The Period extends until the Period.start of the next Period, or diff --git a/test/player_integration.js b/test/player_integration.js index 455c0779d2..75132ba94d 100644 --- a/test/player_integration.js +++ b/test/player_integration.js @@ -102,6 +102,46 @@ describe('Player', () => { expect(seekRange.start).toBeCloseTo(start); }); }); + describe('Live to VOD', () => { + it('playback transition when current time is in the past', async () => { + const netEngine = player.getNetworkingEngine(); + const startTime = Date.now(); + netEngine.registerRequestFilter((type, request) => { + if (type != shaka.net.NetworkingEngine.RequestType.MANIFEST) { + return; + } + // Simulate a live stream by providing different manifests over time. + const time = (Date.now() - startTime) / 1000; + const manifestNumber = Math.min(5, Math.floor(0.5 + time / 2)); + request.uris = [ + '/base/test/test/assets/7401/dash_' + manifestNumber + '.mpd', + ]; + console.log('getting manifest', request.uris); + }); + player.configure('streaming.bufferBehind', 1); + player.configure('streaming.evictionGoal', 1); + // Play the stream . + await player.load('/base/test/test/assets/7401/dash_0.mpd', 1020); + await video.play(); + video.pause(); + // Wait for the stream to be over. + eventManager.listen(player, 'error', Util.spyFunc(onErrorSpy)); + /** @type {shaka.test.Waiter} */ + const waiter = new shaka.test.Waiter(eventManager) + .setPlayer(player) + .timeoutAfter(40) + .failOnTimeout(true); + // wait for Dynamic to static + await waiter.waitUntilVodTransition(video); + expect(player.isLive()).toBe(false); + // set the playback to 1020 in middle of the second period + video.currentTime = 1020; + await video.play(); + await waiter.waitForEnd(video); + // The stream should have transitioned to VOD by now. + expect(player.isLive()).toBe(false); + }); + }); describe('attach', () => { beforeEach(async () => { diff --git a/test/test/assets/7401/chunk-stream0-00001.m4s b/test/test/assets/7401/chunk-stream0-00001.m4s new file mode 100644 index 0000000000..b423109cbc Binary files /dev/null and b/test/test/assets/7401/chunk-stream0-00001.m4s differ diff --git a/test/test/assets/7401/chunk-stream0-00002.m4s b/test/test/assets/7401/chunk-stream0-00002.m4s new file mode 100644 index 0000000000..6e805b076b Binary files /dev/null and b/test/test/assets/7401/chunk-stream0-00002.m4s differ diff --git a/test/test/assets/7401/chunk-stream0-00003.m4s b/test/test/assets/7401/chunk-stream0-00003.m4s new file mode 100644 index 0000000000..9a357bb2c7 Binary files /dev/null and b/test/test/assets/7401/chunk-stream0-00003.m4s differ diff --git a/test/test/assets/7401/chunk-stream0-00004.m4s b/test/test/assets/7401/chunk-stream0-00004.m4s new file mode 100644 index 0000000000..de1e8191da Binary files /dev/null and b/test/test/assets/7401/chunk-stream0-00004.m4s differ diff --git a/test/test/assets/7401/chunk-stream0-00005.m4s b/test/test/assets/7401/chunk-stream0-00005.m4s new file mode 100644 index 0000000000..a00d931eea Binary files /dev/null and b/test/test/assets/7401/chunk-stream0-00005.m4s differ diff --git a/test/test/assets/7401/chunk-stream0-00006.m4s b/test/test/assets/7401/chunk-stream0-00006.m4s new file mode 100644 index 0000000000..dce9209c3f Binary files /dev/null and b/test/test/assets/7401/chunk-stream0-00006.m4s differ diff --git a/test/test/assets/7401/chunk-stream0-00007.m4s b/test/test/assets/7401/chunk-stream0-00007.m4s new file mode 100644 index 0000000000..0a30cb49ca Binary files /dev/null and b/test/test/assets/7401/chunk-stream0-00007.m4s differ diff --git a/test/test/assets/7401/dash_0.mpd b/test/test/assets/7401/dash_0.mpd new file mode 100644 index 0000000000..d575921096 --- /dev/null +++ b/test/test/assets/7401/dash_0.mpd @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test/assets/7401/dash_1.mpd b/test/test/assets/7401/dash_1.mpd new file mode 100644 index 0000000000..cc415e0b76 --- /dev/null +++ b/test/test/assets/7401/dash_1.mpd @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test/assets/7401/dash_2.mpd b/test/test/assets/7401/dash_2.mpd new file mode 100644 index 0000000000..e8b5252d75 --- /dev/null +++ b/test/test/assets/7401/dash_2.mpd @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/test/assets/7401/init-stream0.m4s b/test/test/assets/7401/init-stream0.m4s new file mode 100644 index 0000000000..ba6f961f21 Binary files /dev/null and b/test/test/assets/7401/init-stream0.m4s differ diff --git a/test/test/util/waiter.js b/test/test/util/waiter.js index 42f6447975..4035a9b489 100644 --- a/test/test/util/waiter.js +++ b/test/test/util/waiter.js @@ -143,6 +143,41 @@ shaka.test.Waiter = class { return this.waitUntilGeneric_(goalName, p, cleanup, mediaElement); } + /** + * Wait for the video player to fetch the static manifest. + * Promise is resolved player.isLive is false. + * + * @param {!HTMLMediaElement} mediaElement + * @return {!Promise} + */ + waitUntilVodTransition(mediaElement) { + // The name of what we're waiting for + const goalName = 'manifest to be static'; + + // The cleanup on timeout + let timer = null; + const cleanup = () => { + if (timer) { + timer.stop(); + } + }; + + // The conditions for success + const p = new Promise((resolve) => { + const check = () => { + if (!this.player_.isLive()) { + cleanup(); + resolve(); + } + }; + + timer = new shaka.util.Timer(check); + timer.tickEvery(/* seconds= */ 1); + }); + + return this.waitUntilGeneric_(goalName, p, cleanup, mediaElement); + } + /** * Wait for the video playhead to reach a certain target time. * If the playhead reaches |timeGoal| or the video ends before |timeout|