Skip to content

Commit

Permalink
DVR Window Refactoring (#3809)
Browse files Browse the repository at this point in the history
* Do not update currenttime when manifest update is in progress to avoid using a wrong DVR window

* Remove unnecessary DVR window update
  • Loading branch information
dsilhavy authored Nov 12, 2021
1 parent 2b3976b commit 1011442
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 52 deletions.
23 changes: 2 additions & 21 deletions src/streaming/StreamProcessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ function StreamProcessor(config) {
representationController,
shouldUseExplicitTimeForRequest,
qualityChangeInProgress,
manifestUpdateInProgress,
dashHandler,
segmentsController,
bufferingTime;
Expand All @@ -106,8 +105,6 @@ function StreamProcessor(config) {
eventBus.on(Events.QUOTA_EXCEEDED, _onQuotaExceeded, instance);
eventBus.on(Events.SET_FRAGMENTED_TEXT_AFTER_DISABLED, _onSetFragmentedTextAfterDisabled, instance);
eventBus.on(Events.SET_NON_FRAGMENTED_TEXT, _onSetNonFragmentedText, instance);
eventBus.on(Events.MANIFEST_UPDATED, _onManifestUpdated, instance);
eventBus.on(Events.STREAMS_COMPOSED, _onStreamsComposed, instance);
eventBus.on(Events.SOURCE_BUFFER_ERROR, _onSourceBufferError, instance);
}

Expand Down Expand Up @@ -209,7 +206,6 @@ function StreamProcessor(config) {
mediaInfo = null;
bufferingTime = 0;
shouldUseExplicitTimeForRequest = false;
manifestUpdateInProgress = false;
qualityChangeInProgress = false;
}

Expand Down Expand Up @@ -253,8 +249,6 @@ function StreamProcessor(config) {
eventBus.off(Events.SET_FRAGMENTED_TEXT_AFTER_DISABLED, _onSetFragmentedTextAfterDisabled, instance);
eventBus.off(Events.SET_NON_FRAGMENTED_TEXT, _onSetNonFragmentedText, instance);
eventBus.off(Events.QUOTA_EXCEEDED, _onQuotaExceeded, instance);
eventBus.off(Events.MANIFEST_UPDATED, _onManifestUpdated, instance);
eventBus.off(Events.STREAMS_COMPOSED, _onStreamsComposed, instance);
eventBus.off(Events.SOURCE_BUFFER_ERROR, _onSourceBufferError, instance);

resetInitialSettings();
Expand Down Expand Up @@ -364,7 +358,7 @@ function StreamProcessor(config) {
// Event propagation may have been stopped (see MssHandler)
if (!e.sender) return;

if (manifestUpdateInProgress) {
if (playbackController.getIsManifestUpdateInProgress()) {
_noValidRequest();
return;
}
Expand Down Expand Up @@ -398,7 +392,7 @@ function StreamProcessor(config) {
*/
function _onMediaFragmentNeeded(e, rescheduleIfNoRequest = true) {
// Don't schedule next fragments while updating manifest or pruning to avoid buffer inconsistencies
if (manifestUpdateInProgress || bufferController.getIsPruningInProgress()) {
if (playbackController.getIsManifestUpdateInProgress() || bufferController.getIsPruningInProgress()) {
_noValidRequest();
return;
}
Expand Down Expand Up @@ -520,19 +514,6 @@ function StreamProcessor(config) {
scheduleController.startScheduleTimer(settings.get().streaming.lowLatencyEnabled ? settings.get().streaming.scheduling.lowLatencyTimeout : settings.get().streaming.scheduling.defaultTimeout);
}

/**
* A new manifest has been loaded, updating is still in progress. Wait for the update to be finished before fetching new segments.
* Otherwise we end up in inconsistencies like wrong base urls especially if periods have been removed.
* @private
*/
function _onManifestUpdated() {
manifestUpdateInProgress = true;
}

function _onStreamsComposed() {
manifestUpdateInProgress = false;
}

function _onDataUpdateCompleted(e) {
if (!e.error) {
// Update representation if no error
Expand Down
62 changes: 45 additions & 17 deletions src/streaming/controllers/PlaybackController.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function PlaybackController() {
isLowLatencySeekingInProgress,
playbackStalled,
minPlaybackRateChange,
manifestUpdateInProgress,
settings;

function setup() {
Expand Down Expand Up @@ -102,14 +103,16 @@ function PlaybackController() {
const isSafari = /safari/.test(ua) && !/chrome/.test(ua);
minPlaybackRateChange = isSafari ? 0.25 : 0.02;

eventBus.on(Events.DATA_UPDATE_COMPLETED, _onDataUpdateCompleted, this);
eventBus.on(Events.LOADING_PROGRESS, _onFragmentLoadProgress, this);
eventBus.on(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, this);
eventBus.on(MediaPlayerEvents.PLAYBACK_PROGRESS, _onPlaybackProgression, this);
eventBus.on(MediaPlayerEvents.PLAYBACK_TIME_UPDATED, _onPlaybackProgression, this);
eventBus.on(MediaPlayerEvents.PLAYBACK_ENDED, _onPlaybackEnded, this, { priority: EventBus.EVENT_PRIORITY_HIGH });
eventBus.on(MediaPlayerEvents.STREAM_INITIALIZING, _onStreamInitializing, this);
eventBus.on(MediaPlayerEvents.REPRESENTATION_SWITCH, _onRepresentationSwitch, this);
eventBus.on(Events.DATA_UPDATE_COMPLETED, _onDataUpdateCompleted, instance);
eventBus.on(Events.LOADING_PROGRESS, _onFragmentLoadProgress, instance);
eventBus.on(Events.MANIFEST_UPDATED, _onManifestUpdated, instance);
eventBus.on(Events.STREAMS_COMPOSED, _onStreamsComposed, instance);
eventBus.on(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance);
eventBus.on(MediaPlayerEvents.PLAYBACK_PROGRESS, _onPlaybackProgression, instance);
eventBus.on(MediaPlayerEvents.PLAYBACK_TIME_UPDATED, _onPlaybackProgression, instance);
eventBus.on(MediaPlayerEvents.PLAYBACK_ENDED, _onPlaybackEnded, instance, { priority: EventBus.EVENT_PRIORITY_HIGH });
eventBus.on(MediaPlayerEvents.STREAM_INITIALIZING, _onStreamInitializing, instance);
eventBus.on(MediaPlayerEvents.REPRESENTATION_SWITCH, _onRepresentationSwitch, instance);

if (playOnceInitialized) {
playOnceInitialized = false;
Expand Down Expand Up @@ -223,6 +226,10 @@ function PlaybackController() {
return streamController;
}

function getIsManifestUpdateInProgress() {
return manifestUpdateInProgress;
}

/**
* Computes the desirable delay for the live edge to avoid a risk of getting 404 when playing at the bleeding edge
* @param {number} fragmentDuration - seconds?
Expand Down Expand Up @@ -347,16 +354,19 @@ function PlaybackController() {
playOnceInitialized = false;
liveDelay = 0;
availabilityStartTime = 0;
manifestUpdateInProgress = false;
seekTarget = NaN;
if (videoModel) {
eventBus.off(Events.DATA_UPDATE_COMPLETED, _onDataUpdateCompleted, this);
eventBus.off(Events.LOADING_PROGRESS, _onFragmentLoadProgress, this);
eventBus.off(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, this);
eventBus.off(MediaPlayerEvents.PLAYBACK_PROGRESS, _onPlaybackProgression, this);
eventBus.off(MediaPlayerEvents.PLAYBACK_TIME_UPDATED, _onPlaybackProgression, this);
eventBus.off(MediaPlayerEvents.PLAYBACK_ENDED, _onPlaybackEnded, this);
eventBus.off(MediaPlayerEvents.STREAM_INITIALIZING, _onStreamInitializing, this);
eventBus.off(MediaPlayerEvents.REPRESENTATION_SWITCH, _onRepresentationSwitch, this);
eventBus.off(Events.DATA_UPDATE_COMPLETED, _onDataUpdateCompleted, instance);
eventBus.off(Events.LOADING_PROGRESS, _onFragmentLoadProgress, instance);
eventBus.off(Events.MANIFEST_UPDATED, _onManifestUpdated, instance);
eventBus.off(Events.STREAMS_COMPOSED, _onStreamsComposed, instance);
eventBus.off(MediaPlayerEvents.BUFFER_LEVEL_STATE_CHANGED, _onBufferLevelStateChanged, instance);
eventBus.off(MediaPlayerEvents.PLAYBACK_PROGRESS, _onPlaybackProgression, instance);
eventBus.off(MediaPlayerEvents.PLAYBACK_TIME_UPDATED, _onPlaybackProgression, instance);
eventBus.off(MediaPlayerEvents.PLAYBACK_ENDED, _onPlaybackEnded, instance);
eventBus.off(MediaPlayerEvents.STREAM_INITIALIZING, _onStreamInitializing, instance);
eventBus.off(MediaPlayerEvents.REPRESENTATION_SWITCH, _onRepresentationSwitch, instance);
videoModel.setPlaybackRate(1.0, true);
stopUpdatingWallclockTime();
removeAllListeners();
Expand Down Expand Up @@ -444,7 +454,7 @@ function PlaybackController() {
* @param {object} mediaType
*/
function updateCurrentTime(mediaType = null) {
if (isPaused() || !isDynamic || videoModel.getReadyState() === 0 || isSeeking()) return;
if (isPaused() || !isDynamic || videoModel.getReadyState() === 0 || isSeeking() || manifestUpdateInProgress) return;

// Note: In some cases we filter certain media types completely (for instance due to an unsupported video codec). This happens after the first entry to the DVR metric has been added.
// Now the DVR window for the filtered media type is not updated anymore. Consequently, always use a mediaType that is available to get a valid DVR window.
Expand Down Expand Up @@ -594,6 +604,7 @@ function PlaybackController() {
// Updates playback time for paused dynamic streams
// (video element doesn't call timeupdate when the playback is paused)
if (getIsDynamic()) {
streamController.addDVRMetric();
if (isPaused()) {
_updateLivePlaybackTime();
} else {
Expand Down Expand Up @@ -942,6 +953,22 @@ function PlaybackController() {
}
}

/**
* A new manifest has been loaded, updating is still in progress.
* @private
*/
function _onManifestUpdated() {
manifestUpdateInProgress = true;
}

/**
* Manifest update was completed
* @private
*/
function _onStreamsComposed() {
manifestUpdateInProgress = false;
}


function _checkEnableLowLatency(mediaInfo) {
if (mediaInfo && mediaInfo.supplementalProperties &&
Expand Down Expand Up @@ -1000,6 +1027,7 @@ function PlaybackController() {
getBufferLevel,
getTime,
getNormalizedTime,
getIsManifestUpdateInProgress,
getPlaybackRate,
getPlayedRanges,
getEnded,
Expand Down
18 changes: 4 additions & 14 deletions src/streaming/controllers/StreamController.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ function StreamController() {
eventBus.on(Events.MANIFEST_UPDATED, _onManifestUpdated, instance);
eventBus.on(Events.STREAM_BUFFERING_COMPLETED, _onStreamBufferingCompleted, instance);
eventBus.on(Events.TIME_SYNCHRONIZATION_COMPLETED, _onTimeSyncCompleted, instance);
eventBus.on(Events.WALLCLOCK_TIME_UPDATED, _onWallclockTimeUpdated, instance);
eventBus.on(Events.CURRENT_TRACK_CHANGED, _onCurrentTrackChanged, instance);
}

Expand All @@ -202,7 +201,6 @@ function StreamController() {
eventBus.off(Events.MANIFEST_UPDATED, _onManifestUpdated, instance);
eventBus.off(Events.STREAM_BUFFERING_COMPLETED, _onStreamBufferingCompleted, instance);
eventBus.off(Events.TIME_SYNCHRONIZATION_COMPLETED, _onTimeSyncCompleted, instance);
eventBus.off(Events.WALLCLOCK_TIME_UPDATED, _onWallclockTimeUpdated, instance);
eventBus.off(Events.CURRENT_TRACK_CHANGED, _onCurrentTrackChanged, instance);
}

Expand Down Expand Up @@ -260,6 +258,7 @@ function StreamController() {
if (!activeStream) {
_initializeForFirstStream(streamsInfo);
}

eventBus.trigger(Events.STREAMS_COMPOSED);
// Additional periods might have been added after an MPD update. Check again if we can start prebuffering.
_checkIfPrebufferingCanStart();
Expand Down Expand Up @@ -324,7 +323,7 @@ function StreamController() {
function _initializeForFirstStream(streamsInfo) {

// Add the DVR window so we can calculate the right starting point
_addDVRMetric();
addDVRMetric();

// If the start is in the future we need to wait
const dvrRange = dashMetrics.getCurrentDVRInfo().range;
Expand Down Expand Up @@ -660,7 +659,7 @@ function StreamController() {
/**
* Add the DVR window to the metric list. We need the DVR window to restrict the seeking and calculate the right start time.
*/
function _addDVRMetric() {
function addDVRMetric() {
try {
const isDynamic = adapter.getIsDynamic();
const streamsInfo = adapter.getStreamsInfo();
Expand Down Expand Up @@ -724,16 +723,6 @@ function StreamController() {
stream.prepareQualityChange(e);
}

/**
* Update the DVR window when the wallclock time has updated
* @private
*/
function _onWallclockTimeUpdated() {
if (adapter.getIsDynamic()) {
_addDVRMetric();
}
}

/**
* When the playback time is updated we add the droppedFrames metric to the dash metric object
* @private
Expand Down Expand Up @@ -1508,6 +1497,7 @@ function StreamController() {
instance = {
initialize,
getActiveStreamInfo,
addDVRMetric,
hasVideoTrack,
hasAudioTrack,
getStreamById,
Expand Down

0 comments on commit 1011442

Please sign in to comment.