From 8cecf6d1893d9e2c2bb362c4441a44511130dc86 Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Thu, 14 Jul 2022 10:51:33 +0200 Subject: [PATCH 1/4] Add support for specifying the start time of the stream as part of the initialize or the attachSource call --- index.d.ts | 4 +- samples/advanced/load-with-starttime.html | 85 ++++++++++++++ samples/samples.json | 11 ++ src/streaming/MediaPlayer.js | 26 +++-- src/streaming/controllers/StreamController.js | 104 +++++++++++------- 5 files changed, 181 insertions(+), 49 deletions(-) create mode 100644 samples/advanced/load-with-starttime.html diff --git a/index.d.ts b/index.d.ts index 891cb8de79..92cb1b668f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -363,7 +363,7 @@ declare namespace dashjs { export type TrackSelectionFunction = (tracks: MediaInfo[]) => MediaInfo[]; export interface MediaPlayerClass { - initialize(view?: HTMLElement, source?: string, autoPlay?: boolean): void; + initialize(view?: HTMLElement, source?: string, autoPlay?: boolean, startTime?: number): void; on(type: AstInFutureEvent['type'], listener: (e: AstInFutureEvent) => void, scope?: object): void; @@ -450,7 +450,7 @@ declare namespace dashjs { attachView(element: HTMLElement): void; - attachSource(urlOrManifest: string | object): void; + attachSource(urlOrManifest: string | object, startTime?: number): void; isReady(): boolean; diff --git a/samples/advanced/load-with-starttime.html b/samples/advanced/load-with-starttime.html new file mode 100644 index 0000000000..5912c5a68a --- /dev/null +++ b/samples/advanced/load-with-starttime.html @@ -0,0 +1,85 @@ + + + + + Manual load with start time + + + + + + + + + + + + + +
+
+
+ +
+
+
+
+

Manual load with start time

+

A sample showing how to initialize playback at a specific start time. For VoD content the start + time is relative to the start time of the first period. For live content the start time is + relative to + MPD@availabilityStartTime.

+

In this example playback starts 20 seconds from the first period. +

+
+
+
+ +
+
+
+
+
+
+
+
+ © DASH-IF +
+
+
+ + + + + + diff --git a/samples/samples.json b/samples/samples.json index 8a88c60764..eaf982d647 100644 --- a/samples/samples.json +++ b/samples/samples.json @@ -670,6 +670,17 @@ "Video", "Audio" ] + }, + { + "title": "Manual load with start time", + "description": "A sample showing how to initialize playback at a specific start time. For VoD content the start time is relative to the start time of the first period. For live content the start time is relative to MPD@availabilityStartTime.", + "href": "advanced/load-with-starttime.html", + "image": "lib/img/bbb-4.jpg", + "labels": [ + "VoD", + "Video", + "Audio" + ] } ] }, diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index 9907fb8677..dd496f884a 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -252,14 +252,15 @@ function MediaPlayer() { * * @param {HTML5MediaElement=} view - Optional arg to set the video element. {@link module:MediaPlayer#attachView attachView()} * @param {string=} source - Optional arg to set the media source. {@link module:MediaPlayer#attachSource attachSource()} - * @param {boolean=} AutoPlay - Optional arg to set auto play. {@link module:MediaPlayer#setAutoPlay setAutoPlay()} + * @param {boolean=} autoPlay - Optional arg to set auto play. {@link module:MediaPlayer#setAutoPlay setAutoPlay()} + * @param {number|NaN} startTime - Optional arg to set start time relative to the start time of the first period for VoD content and to the AST for live content * @see {@link module:MediaPlayer#attachView attachView()} * @see {@link module:MediaPlayer#attachSource attachSource()} * @see {@link module:MediaPlayer#setAutoPlay setAutoPlay()} * @memberof module:MediaPlayer * @instance */ - function initialize(view, source, AutoPlay) { + function initialize(view, source, autoPlay, startTime = NaN) { if (!capabilities) { capabilities = Capabilities(context).getInstance(); capabilities.setConfig({ @@ -381,7 +382,7 @@ function MediaPlayer() { }); restoreDefaultUTCTimingSources(); - setAutoPlay(AutoPlay !== undefined ? AutoPlay : true); + setAutoPlay(autoPlay !== undefined ? autoPlay : true); // Detect and initialize offline module to support offline contents playback _detectOffline(); @@ -392,7 +393,7 @@ function MediaPlayer() { } if (source) { - attachSource(source); + attachSource(source, startTime); } logger.info('[dash.js ' + getVersion() + '] ' + 'MediaPlayer has been initialized'); @@ -1790,14 +1791,14 @@ function MediaPlayer() { * * @param {string|Object} urlOrManifest - A URL to a valid MPD manifest file, or a * parsed manifest object. - * + * @param {number|NaN} startTime - Start time relative to the start time of the first period for VoD content and to the AST for live content * * @throws {@link module:MediaPlayer~MEDIA_PLAYER_NOT_INITIALIZED_ERROR MEDIA_PLAYER_NOT_INITIALIZED_ERROR} if called before initialize function * * @memberof module:MediaPlayer * @instance */ - function attachSource(urlOrManifest) { + function attachSource(urlOrManifest, startTime = NaN) { if (!mediaPlayerInitialized) { throw MEDIA_PLAYER_NOT_INITIALIZED_ERROR; } @@ -1813,7 +1814,7 @@ function MediaPlayer() { } if (isReady()) { - _initializePlayback(); + _initializePlayback(startTime); } } @@ -2262,7 +2263,12 @@ function MediaPlayer() { return utcValue; } - function _initializePlayback() { + /** + * + * @param @param {number|NaN} startTime - Start time relative to the start time of the first period for VoD content and to the AST for live content + * @private + */ + function _initializePlayback(startTime = NaN) { if (offlineController) { offlineController.resetRecords(); @@ -2274,9 +2280,9 @@ function MediaPlayer() { _createPlaybackControllers(); if (typeof source === 'string') { - streamController.load(source); + streamController.load(source, startTime); } else { - streamController.loadWithManifest(source); + streamController.loadWithManifest(source, startTime); } } diff --git a/src/streaming/controllers/StreamController.js b/src/streaming/controllers/StreamController.js index c10a8ea8a3..8d2ed483f2 100644 --- a/src/streaming/controllers/StreamController.js +++ b/src/streaming/controllers/StreamController.js @@ -103,6 +103,7 @@ function StreamController() { settings, firstLicenseIsFetched, waitForPlaybackStartTimeout, + providedStartTime, errorInformation; function setup() { @@ -210,6 +211,42 @@ function StreamController() { eventBus.off(Events.SETTING_UPDATED_LIVE_DELAY_FRAGMENT_COUNT, _onLiveDelaySettingUpdated, instance); } + function _checkConfig() { + if (!manifestLoader || !manifestLoader.hasOwnProperty('load') || !timelineConverter || !timelineConverter.hasOwnProperty('initialize') || + !timelineConverter.hasOwnProperty('reset') || !timelineConverter.hasOwnProperty('getClientTimeOffset') || !manifestModel || !errHandler || + !dashMetrics || !playbackController) { + throw new Error(Constants.MISSING_CONFIG_ERROR); + } + } + + function _checkInitialize() { + if (!manifestUpdater || !manifestUpdater.hasOwnProperty('setManifest')) { + throw new Error('initialize function has to be called previously'); + } + } + + /** + * Start the streaming session by loading the target manifest + * @param {string} url + * @param {number} startTime + */ + function load(url, startTime = NaN) { + _checkConfig(); + providedStartTime = startTime; + manifestLoader.load(url); + } + + /** + * Start the streaming session by using the provided manifest object + * @param {object} manifest + * @param {number} startTime + */ + function loadWithManifest(manifest, startTime = NaN) { + _checkInitialize(); + providedStartTime = startTime; + manifestUpdater.setManifest(manifest); + } + /** * When the UTC snychronization is completed we can compose the streams * @private @@ -349,7 +386,8 @@ function StreamController() { // Apply Service description parameters. if (settings.get().streaming.applyProducerReferenceTime) { serviceDescriptionController.calculateProducerReferenceTimeOffsets(streamsInfo); - }; + } + ; const manifestInfo = streamsInfo[0].manifestInfo; if (settings.get().streaming.applyServiceDescription) { @@ -1030,6 +1068,7 @@ function StreamController() { */ function _getInitialStartTime() { // Seek new stream in priority order: + // - at start time provided via the application // - at start time provided in URI parameters // - at stream/period start time (for static streams) or live start time (for dynamic streams) let startTime; @@ -1042,25 +1081,39 @@ function StreamController() { // If start time in URI, take min value between live edge time and time from URI (capped by DVR window range) const dvrWindow = dvrInfo ? dvrInfo.range : null; if (dvrWindow) { - // #t shall be relative to period start - const startTimeFromUri = _getStartTimeFromUriParameters(true); - if (!isNaN(startTimeFromUri)) { - logger.info('Start time from URI parameters: ' + startTimeFromUri); - // If calcFromSegmentTimeline is enabled we saw problems caused by the MSE.seekableRange when starting at dvrWindow.start. Apply a small offset to avoid this problem. - const offset = settings.get().streaming.timeShiftBuffer.calcFromSegmentTimeline ? 0.1 : 0; - startTime = Math.max(Math.min(startTime, startTimeFromUri), dvrWindow.start + offset); + // If start time was provided by the application as part of the call to initialize() or attachSource() use this value + if (!isNaN(providedStartTime)) { + logger.info(`Start time provided by the app: ${providedStartTime}`); + startTime = Math.min(startTime, providedStartTime); + } else { + // #t shall be relative to period start + const startTimeFromUri = _getStartTimeFromUriParameters(true); + if (!isNaN(startTimeFromUri)) { + logger.info(`Start time from URI parameters: ${startTimeFromUri}`); + startTime = Math.min(startTime, startTimeFromUri); + } } + // If calcFromSegmentTimeline is enabled we saw problems caused by the MSE.seekableRange when starting at dvrWindow.start. Apply a small offset to avoid this problem. + const offset = settings.get().streaming.timeShiftBuffer.calcFromSegmentTimeline ? 0.1 : 0; + startTime = Math.max(startTime, dvrWindow.start + offset); } } else { // For static stream, start by default at period start const streams = getStreams(); const streamInfo = streams[0].getStreamInfo(); startTime = streamInfo.start; - // If start time in URI, take max value between period start and time from URI (if in period range) - const startTimeFromUri = _getStartTimeFromUriParameters(false); - if (!isNaN(startTimeFromUri)) { - logger.info('Start time from URI parameters: ' + startTimeFromUri); - startTime = Math.max(startTime, startTimeFromUri); + + // If start time was provided by the application as part of the call to initialize() or attachSource() use this value + if (!isNaN(providedStartTime)) { + logger.info(`Start time provided by the app: ${providedStartTime}`); + startTime = Math.max(startTime, streamInfo.start + providedStartTime); + } else { + // If start time in URI, take max value between period start and time from URI (if in period range) + const startTimeFromUri = _getStartTimeFromUriParameters(false); + if (!isNaN(startTimeFromUri)) { + logger.info(`Start time from URI parameters: ${startTimeFromUri}`); + startTime = Math.max(startTime, startTimeFromUri); + } } } @@ -1351,30 +1404,6 @@ function StreamController() { return null; } - function _checkConfig() { - if (!manifestLoader || !manifestLoader.hasOwnProperty('load') || !timelineConverter || !timelineConverter.hasOwnProperty('initialize') || - !timelineConverter.hasOwnProperty('reset') || !timelineConverter.hasOwnProperty('getClientTimeOffset') || !manifestModel || !errHandler || - !dashMetrics || !playbackController) { - throw new Error(Constants.MISSING_CONFIG_ERROR); - } - } - - function _checkInitialize() { - if (!manifestUpdater || !manifestUpdater.hasOwnProperty('setManifest')) { - throw new Error('initialize function has to be called previously'); - } - } - - function load(url) { - _checkConfig(); - manifestLoader.load(url); - } - - function loadWithManifest(manifest) { - _checkInitialize(); - manifestUpdater.setManifest(manifest); - } - function _onManifestValidityChanged(e) { if (!isNaN(e.newDuration)) { _setMediaDuration(e.newDuration); @@ -1458,6 +1487,7 @@ function StreamController() { function resetInitialSettings() { streams = []; + providedStartTime = NaN; protectionController = null; isStreamSwitchingInProgress = false; activeStream = null; From 4f6aa3b5a0f42737ef0fe71cd378ca32486031dd Mon Sep 17 00:00:00 2001 From: dsilhavy Date: Fri, 15 Jul 2022 11:21:44 +0200 Subject: [PATCH 2/4] Support for posix notation in attachSource and initialize call to define start time --- index.d.ts | 4 +- samples/advanced/load-with-starttime.html | 30 +++-- samples/samples.json | 2 +- src/streaming/MediaPlayer.js | 12 +- src/streaming/controllers/StreamController.js | 124 ++++++++---------- test/unit/helpers/ObjectsHelper.js | 21 +-- .../streaming.controllers.StreamController.js | 14 ++ 7 files changed, 111 insertions(+), 96 deletions(-) diff --git a/index.d.ts b/index.d.ts index 92cb1b668f..d986239ee5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -363,7 +363,7 @@ declare namespace dashjs { export type TrackSelectionFunction = (tracks: MediaInfo[]) => MediaInfo[]; export interface MediaPlayerClass { - initialize(view?: HTMLElement, source?: string, autoPlay?: boolean, startTime?: number): void; + initialize(view?: HTMLElement, source?: string, autoPlay?: boolean, startTime?: number | string): void; on(type: AstInFutureEvent['type'], listener: (e: AstInFutureEvent) => void, scope?: object): void; @@ -450,7 +450,7 @@ declare namespace dashjs { attachView(element: HTMLElement): void; - attachSource(urlOrManifest: string | object, startTime?: number): void; + attachSource(urlOrManifest: string | object, startTime?: number | string): void; isReady(): boolean; diff --git a/samples/advanced/load-with-starttime.html b/samples/advanced/load-with-starttime.html index 5912c5a68a..0d9caa8936 100644 --- a/samples/advanced/load-with-starttime.html +++ b/samples/advanced/load-with-starttime.html @@ -21,7 +21,7 @@ function init() { var video, player, - url = "https://dash.akamaized.net/akamai/bbb_30fps/bbb_30fps.mpd"; + url = "https://livesim.dashif.org/livesim/testpic_2s/Manifest.mpd"; player = dashjs.MediaPlayer().create(); video = document.querySelector("video"); @@ -32,9 +32,10 @@ logLevel: 4 } }); + const starttime = new Date().getTime() / 1000 - 60; player.attachView(video); /* tell the player which videoElement it should use */ - player.attachSource(url, 20); /* provide the manifest source */ - + player.attachSource(url, `posix:${starttime}`); /* start from UTC time */ + /* player.attachSource(url, starttime); start relative to AST */ } @@ -51,11 +52,24 @@

Manual load with start time

-

A sample showing how to initialize playback at a specific start time. For VoD content the start - time is relative to the start time of the first period. For live content the start time is - relative to - MPD@availabilityStartTime.

-

In this example playback starts 20 seconds from the first period. +

A sample showing how to initialize playback at a specific start time. +

    +
  • For VoD content the start time is relative to the start time of the first period.
  • +
  • For live content +
      +
    • If the parameter starts from prefix + posix: it signifies the absolute time range defined in seconds of Coordinated + Universal Time + (ITU-R TF.460-6). This is the number of seconds since 01-01-1970 00:00:00 UTC. + Fractions of + seconds may be optionally specified down to the millisecond level. +
    • +
    • If no posix prefix is used the starttime is relative to MPD@availabilityStartTime
    • +
    +
  • +
+

+

In this example playback starts 60 seconds from the current wall clock time.

diff --git a/samples/samples.json b/samples/samples.json index eaf982d647..252090df98 100644 --- a/samples/samples.json +++ b/samples/samples.json @@ -673,7 +673,7 @@ }, { "title": "Manual load with start time", - "description": "A sample showing how to initialize playback at a specific start time. For VoD content the start time is relative to the start time of the first period. For live content the start time is relative to MPD@availabilityStartTime.", + "description": "A sample showing how to initialize playback at a specific start time.", "href": "advanced/load-with-starttime.html", "image": "lib/img/bbb-4.jpg", "labels": [ diff --git a/src/streaming/MediaPlayer.js b/src/streaming/MediaPlayer.js index dd496f884a..89b68c4cfb 100644 --- a/src/streaming/MediaPlayer.js +++ b/src/streaming/MediaPlayer.js @@ -253,8 +253,10 @@ function MediaPlayer() { * @param {HTML5MediaElement=} view - Optional arg to set the video element. {@link module:MediaPlayer#attachView attachView()} * @param {string=} source - Optional arg to set the media source. {@link module:MediaPlayer#attachSource attachSource()} * @param {boolean=} autoPlay - Optional arg to set auto play. {@link module:MediaPlayer#setAutoPlay setAutoPlay()} - * @param {number|NaN} startTime - Optional arg to set start time relative to the start time of the first period for VoD content and to the AST for live content - * @see {@link module:MediaPlayer#attachView attachView()} + * @param {number|string} startTime - For VoD content the start time is relative to the start time of the first period. + * For live content + * If the parameter starts from prefix posix: it signifies the absolute time range defined in seconds of Coordinated Universal Time (ITU-R TF.460-6). This is the number of seconds since 01-01-1970 00:00:00 UTC. Fractions of seconds may be optionally specified down to the millisecond level. + * If no posix prefix is used the starttime is relative to MPD@availabilityStartTime * @see {@link module:MediaPlayer#attachSource attachSource()} * @see {@link module:MediaPlayer#setAutoPlay setAutoPlay()} * @memberof module:MediaPlayer @@ -1791,7 +1793,10 @@ function MediaPlayer() { * * @param {string|Object} urlOrManifest - A URL to a valid MPD manifest file, or a * parsed manifest object. - * @param {number|NaN} startTime - Start time relative to the start time of the first period for VoD content and to the AST for live content + * @param {number|string} startTime - For VoD content the start time is relative to the start time of the first period. + * For live content + * If the parameter starts from prefix posix: it signifies the absolute time range defined in seconds of Coordinated Universal Time (ITU-R TF.460-6). This is the number of seconds since 01-01-1970 00:00:00 UTC. Fractions of seconds may be optionally specified down to the millisecond level. + * If no posix prefix is used the starttime is relative to MPD@availabilityStartTime * * @throws {@link module:MediaPlayer~MEDIA_PLAYER_NOT_INITIALIZED_ERROR MEDIA_PLAYER_NOT_INITIALIZED_ERROR} if called before initialize function * @@ -2265,7 +2270,6 @@ function MediaPlayer() { /** * - * @param @param {number|NaN} startTime - Start time relative to the start time of the first period for VoD content and to the AST for live content * @private */ function _initializePlayback(startTime = NaN) { diff --git a/src/streaming/controllers/StreamController.js b/src/streaming/controllers/StreamController.js index 8d2ed483f2..28e8f8fde7 100644 --- a/src/streaming/controllers/StreamController.js +++ b/src/streaming/controllers/StreamController.js @@ -36,8 +36,7 @@ import EventBus from '../../core/EventBus'; import Events from '../../core/events/Events'; import FactoryMaker from '../../core/FactoryMaker'; import { - PlayList, - PlayListTrace + PlayList, PlayListTrace } from '../vo/metrics/PlayList'; import Debug from '../../core/Debug'; import InitCache from '../utils/InitCache'; @@ -58,53 +57,14 @@ function StreamController() { const context = this.context; const eventBus = EventBus(context).getInstance(); - let instance, - logger, - capabilities, - capabilitiesFilter, - manifestUpdater, - manifestLoader, - manifestModel, - adapter, - dashMetrics, - mediaSourceController, - timeSyncController, - baseURLController, - segmentBaseController, - uriFragmentModel, - abrController, - mediaController, - eventController, - initCache, - urlUtils, - errHandler, - timelineConverter, - streams, - activeStream, - protectionController, - textController, - protectionData, - autoPlay, - isStreamSwitchingInProgress, - hasMediaError, - hasInitialisationError, - mediaSource, - videoModel, - playbackController, - serviceDescriptionController, - mediaPlayerModel, - customParametersModel, - isPaused, - initialPlayback, - playbackEndedTimerInterval, - bufferSinks, - preloadingStreams, - supportsChangeType, - settings, - firstLicenseIsFetched, - waitForPlaybackStartTimeout, - providedStartTime, - errorInformation; + let instance, logger, capabilities, capabilitiesFilter, manifestUpdater, manifestLoader, manifestModel, adapter, + dashMetrics, mediaSourceController, timeSyncController, baseURLController, segmentBaseController, + uriFragmentModel, abrController, mediaController, eventController, initCache, urlUtils, errHandler, + timelineConverter, streams, activeStream, protectionController, textController, protectionData, autoPlay, + isStreamSwitchingInProgress, hasMediaError, hasInitialisationError, mediaSource, videoModel, playbackController, + serviceDescriptionController, mediaPlayerModel, customParametersModel, isPaused, initialPlayback, + playbackEndedTimerInterval, bufferSinks, preloadingStreams, supportsChangeType, settings, firstLicenseIsFetched, + waitForPlaybackStartTimeout, providedStartTime, errorInformation; function setup() { logger = Debug(context).getInstance().getLogger(instance); @@ -135,18 +95,13 @@ function StreamController() { eventController = EventController(context).getInstance(); eventController.setConfig({ - manifestUpdater: manifestUpdater, - playbackController: playbackController, - settings + manifestUpdater: manifestUpdater, playbackController: playbackController, settings }); eventController.start(); timeSyncController.setConfig({ - dashMetrics, - baseURLController, - errHandler, - settings + dashMetrics, baseURLController, errHandler, settings }); timeSyncController.initialize(); @@ -212,9 +167,7 @@ function StreamController() { } function _checkConfig() { - if (!manifestLoader || !manifestLoader.hasOwnProperty('load') || !timelineConverter || !timelineConverter.hasOwnProperty('initialize') || - !timelineConverter.hasOwnProperty('reset') || !timelineConverter.hasOwnProperty('getClientTimeOffset') || !manifestModel || !errHandler || - !dashMetrics || !playbackController) { + if (!manifestLoader || !manifestLoader.hasOwnProperty('load') || !timelineConverter || !timelineConverter.hasOwnProperty('initialize') || !timelineConverter.hasOwnProperty('reset') || !timelineConverter.hasOwnProperty('getClientTimeOffset') || !manifestModel || !errHandler || !dashMetrics || !playbackController) { throw new Error(Constants.MISSING_CONFIG_ERROR); } } @@ -1072,7 +1025,8 @@ function StreamController() { // - at start time provided in URI parameters // - at stream/period start time (for static streams) or live start time (for dynamic streams) let startTime; - if (adapter.getIsDynamic()) { + const isDynamic = adapter.getIsDynamic(); + if (isDynamic) { // For dynamic stream, start by default at (live edge - live delay) const dvrInfo = dashMetrics.getCurrentDVRInfo(); const liveEdge = dvrInfo && dvrInfo.range ? dvrInfo.range.end : 0; @@ -1082,14 +1036,19 @@ function StreamController() { const dvrWindow = dvrInfo ? dvrInfo.range : null; if (dvrWindow) { // If start time was provided by the application as part of the call to initialize() or attachSource() use this value - if (!isNaN(providedStartTime)) { + if (!isNaN(providedStartTime) || providedStartTime.toString().indexOf('posix:') !== -1) { logger.info(`Start time provided by the app: ${providedStartTime}`); - startTime = Math.min(startTime, providedStartTime); + const providedStartTimeAsPresentationTime = _getStartTimeFromProvidedData(true, providedStartTime) + if (!isNaN(providedStartTimeAsPresentationTime)) { + // Do not move closer to the live edge as defined by live delay + startTime = Math.min(startTime, providedStartTimeAsPresentationTime); + } } else { // #t shall be relative to period start const startTimeFromUri = _getStartTimeFromUriParameters(true); if (!isNaN(startTimeFromUri)) { logger.info(`Start time from URI parameters: ${startTimeFromUri}`); + // Do not move closer to the live edge as defined by live delay startTime = Math.min(startTime, startTimeFromUri); } } @@ -1106,12 +1065,17 @@ function StreamController() { // If start time was provided by the application as part of the call to initialize() or attachSource() use this value if (!isNaN(providedStartTime)) { logger.info(`Start time provided by the app: ${providedStartTime}`); - startTime = Math.max(startTime, streamInfo.start + providedStartTime); + const providedStartTimeAsPresentationTime = _getStartTimeFromProvidedData(false, providedStartTime) + if (!isNaN(providedStartTimeAsPresentationTime)) { + // Do not play earlier than the start of the first period + startTime = Math.max(startTime, providedStartTimeAsPresentationTime); + } } else { // If start time in URI, take max value between period start and time from URI (if in period range) const startTimeFromUri = _getStartTimeFromUriParameters(false); if (!isNaN(startTimeFromUri)) { logger.info(`Start time from URI parameters: ${startTimeFromUri}`); + // Do not play earlier than the start of the first period startTime = Math.max(startTime, startTimeFromUri); } } @@ -1132,14 +1096,34 @@ function StreamController() { return NaN; } const refStream = getStreams()[0]; - const refStreamStartTime = refStream.getStreamInfo().start; + const referenceTime = refStream.getStreamInfo().start; + fragData.t = fragData.t.split(',')[0]; + + return _getStartTimeFromString(isDynamic, fragData.t, referenceTime); + } + + function _getStartTimeFromProvidedData(isDynamic, providedStartTime) { + let referenceTime = 0; + + if (!isDynamic) { + const refStream = getStreams()[0]; + referenceTime = refStream.getStreamInfo().start; + } + + return _getStartTimeFromString(isDynamic, providedStartTime, referenceTime); + } + + + function _getStartTimeFromString(isDynamic, targetValue, referenceTime) { // Consider only start time of MediaRange // TODO: consider end time of MediaRange to stop playback at provided end time - fragData.t = fragData.t.split(',')[0]; // "t=