Skip to content

Commit 52301f5

Browse files
authored
Feature optimizations (#3399)
* WiP Improve stream switching logic. Trigger SEEKING event when switching between periods in order for the controller to adapt to the new times. That way we can reuse existing source buffers * Do not set a starttime in the playback controller. Instead the correct start time will be set when the stream was initialized * Reset streamInitialized logic in PlaybackController.js * Fix SEEK_TARGET event
1 parent 45d5e51 commit 52301f5

File tree

6 files changed

+40
-32
lines changed

6 files changed

+40
-32
lines changed

src/streaming/MediaPlayerEvents.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -344,10 +344,10 @@ class MediaPlayerEvents extends EventsBase {
344344
this.MANIFEST_VALIDITY_CHANGED = 'manifestValidityChanged';
345345

346346
/**
347-
* A gap occured in the timeline which requires a seek
348-
* @event MediaPlayerEvents#MANIFEST_VALIDITY_CHANGED
347+
* A gap occured in the timeline which requires a seek to the next period
348+
* @event MediaPlayerEvents#GAP_CAUSED_SEEK_TO_PERIOD_END
349349
*/
350-
this.GAP_CAUSED_PLAYBACK_SEEK = 'gapCausedPlaybackSeek';
350+
this.GAP_CAUSED_SEEK_TO_PERIOD_END = 'gapCausedSeekToPeriodEnd';
351351
}
352352
}
353353

src/streaming/StreamProcessor.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ function StreamProcessor(config) {
687687
}
688688

689689
function onSeekTarget(e) {
690-
if (e.mediaType !== type || e.streamId !== streamInfo.id) return;
690+
if ((e.mediaType && e.mediaType !== type) || e.streamId !== streamInfo.id) return;
691691

692692
bufferingTime = e.time;
693693
scheduleController.setSeekTarget(e.time);

src/streaming/controllers/BufferController.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ function BufferController(config) {
275275
if (e.error.code === QUOTA_EXCEEDED_ERROR_CODE || !hasEnoughSpaceToAppend()) {
276276
logger.warn('Clearing playback buffer to overcome quota exceed situation');
277277
// Notify Schedulecontroller to stop scheduling until buffer has been pruned
278-
triggerEvent(Events.QUOTA_EXCEEDED, { criticalBufferLevel: criticalBufferLevel });
278+
triggerEvent(Events.QUOTA_EXCEEDED, {criticalBufferLevel: criticalBufferLevel});
279279
clearBuffers(getClearRanges());
280280
}
281281
return;
@@ -298,7 +298,7 @@ function BufferController(config) {
298298
// (and previous buffered data removed) then seek stream to current time
299299
const currentTime = playbackController.getTime();
300300
logger.debug('AppendToBuffer seek target should be ' + currentTime);
301-
triggerEvent(Events.SEEK_TARGET, {time: currentTime});
301+
triggerEvent(Events.SEEK_TARGET, {time: currentTime, mediaType: type, streamId: streamInfo.id});
302302
}
303303

304304
if (appendedBytesInfo) {
@@ -696,7 +696,7 @@ function BufferController(config) {
696696

697697
if (e.unintended) {
698698
logger.warn('Detected unintended removal from:', e.from, 'to', e.to, 'setting index handler time to', e.from);
699-
triggerEvent(Events.SEEK_TARGET, {time: e.from});
699+
triggerEvent(Events.SEEK_TARGET, {time: e.from, mediaType: type, streamId: streamInfo.id});
700700
}
701701

702702
if (isPruningInProgress) {

src/streaming/controllers/GapController.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ function GapController() {
118118

119119
function _shouldCheckForGaps() {
120120
return settings.get().streaming.jumpGaps && streamController.getActiveStreamProcessors().length > 0 &&
121-
!playbackController.isSeeking() && !playbackController.isPaused() && !streamController.getIsStreamSwitchInProgress() &&
121+
(!playbackController.isSeeking() || streamController.hasStreamFinishedBuffering(streamController.getActiveStream())) && !playbackController.isPaused() && !streamController.getIsStreamSwitchInProgress() &&
122122
!streamController.getHasMediaOrIntialisationError();
123123
}
124124

@@ -210,14 +210,14 @@ function GapController() {
210210
// Playback has stalled before period end. We seek to the end of the period
211211
const timeToStreamEnd = playbackController.getTimeToStreamEnd();
212212
if (isNaN(seekToPosition) && playbackStalled && isFinite(timeToStreamEnd) && !isNaN(timeToStreamEnd) && ((timeToStreamEnd < smallGapLimit) || streamController.hasStreamFinishedBuffering(streamController.getActiveStream()))) {
213-
seekToPosition = parseFloat((currentTime + timeToStreamEnd).toFixed(5));
213+
seekToPosition = parseFloat(playbackController.getStreamEndTime().toFixed(5));
214214
jumpToStreamEnd = true;
215215
}
216216

217217
if (seekToPosition > 0 && lastGapJumpPosition !== seekToPosition) {
218218
if (jumpToStreamEnd) {
219219
logger.warn(`Jumping to end of stream because of gap from ${currentTime} to ${seekToPosition}. Gap duration: ${seekToPosition - currentTime}`);
220-
eventBus.trigger(Events.GAP_CAUSED_PLAYBACK_SEEK, {seekTime: seekToPosition});
220+
eventBus.trigger(Events.GAP_CAUSED_SEEK_TO_PERIOD_END, {seekTime: seekToPosition});
221221
} else {
222222
logger.warn(`Jumping gap from ${currentTime} to ${seekToPosition}. Gap duration: ${seekToPosition - currentTime}`);
223223
playbackController.seek(seekToPosition, true, true);

src/streaming/controllers/PlaybackController.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ function PlaybackController() {
7272
reset();
7373
}
7474

75-
function initialize(StreamInfo, periodSwitch, seekTime) {
76-
streamInfo = StreamInfo;
75+
function initialize(sInfo, periodSwitch, seekTime) {
76+
streamInfo = sInfo;
7777
addAllListeners();
7878
isDynamic = streamInfo.manifestInfo.isDynamic;
7979
isLowLatencySeekingInProgress = false;
@@ -401,6 +401,7 @@ function PlaybackController() {
401401
return NaN;
402402
}
403403

404+
logger.debug(`Checking DVR window for at ${currentTime} with DVR window range ${DVRWindow.start} - ${DVRWindow.end}`);
404405
if (currentTime > DVRWindow.end) {
405406
actualTime = Math.max(DVRWindow.end - liveDelay, DVRWindow.start);
406407

@@ -441,6 +442,7 @@ function PlaybackController() {
441442
const actualTime = getActualPresentationTime(currentTime);
442443
const timeChanged = (!isNaN(actualTime) && actualTime !== currentTime);
443444
if (timeChanged) {
445+
logger.debug(`UpdateCurrentTime: Seek to actual time: ${actualTime} from currentTime: ${currentTime}`);
444446
seek(actualTime);
445447
}
446448
}
@@ -804,6 +806,7 @@ function PlaybackController() {
804806
isPaused: isPaused,
805807
pause: pause,
806808
isSeeking: isSeeking,
809+
getStreamEndTime,
807810
seek: seek,
808811
reset: reset
809812
};

src/streaming/controllers/StreamController.js

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ function StreamController() {
139139
function registerEvents() {
140140
eventBus.on(Events.PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this);
141141
eventBus.on(Events.PLAYBACK_SEEKING, onPlaybackSeeking, this);
142-
eventBus.on(Events.GAP_CAUSED_PLAYBACK_SEEK, onGapCausedPlaybackSeek, this);
142+
eventBus.on(Events.GAP_CAUSED_SEEK_TO_PERIOD_END, onGapCausedPlaybackSeek, this);
143143
eventBus.on(Events.PLAYBACK_ERROR, onPlaybackError, this);
144144
eventBus.on(Events.PLAYBACK_STARTED, onPlaybackStarted, this);
145145
eventBus.on(Events.PLAYBACK_PAUSED, onPlaybackPaused, this);
@@ -154,7 +154,7 @@ function StreamController() {
154154
function unRegisterEvents() {
155155
eventBus.off(Events.PLAYBACK_TIME_UPDATED, onPlaybackTimeUpdated, this);
156156
eventBus.off(Events.PLAYBACK_SEEKING, onPlaybackSeeking, this);
157-
eventBus.off(Events.GAP_CAUSED_PLAYBACK_SEEK, onGapCausedPlaybackSeek, this);
157+
eventBus.off(Events.GAP_CAUSED_SEEK_TO_PERIOD_END, onGapCausedPlaybackSeek, this);
158158
eventBus.off(Events.PLAYBACK_ERROR, onPlaybackError, this);
159159
eventBus.off(Events.PLAYBACK_STARTED, onPlaybackStarted, this);
160160
eventBus.off(Events.PLAYBACK_PAUSED, onPlaybackPaused, this);
@@ -331,14 +331,22 @@ function StreamController() {
331331
}
332332
}
333333

334+
function canSourceBuffersBeReused(nextStream, previousStream) {
335+
try {
336+
return (previousStream.isProtectionCompatible(nextStream, previousStream) &&
337+
(supportsChangeType || previousStream.isMediaCodecCompatible(nextStream, previousStream)) && !hasCriticalTexttracks(nextStream));
338+
} catch (e) {
339+
return false;
340+
}
341+
}
342+
334343
function onStreamCanLoadNext(nextStream, previousStream = null) {
335344

336345
if (mediaSource && !nextStream.getPreloaded()) {
337346
// Seamless period switch allowed only if:
338347
// - none of the periods uses contentProtection.
339348
// - AND changeType method implemented by browser or periods use the same codec.
340-
let seamlessPeriodSwitch = previousStream.isProtectionCompatible(nextStream, previousStream) &&
341-
(supportsChangeType || previousStream.isMediaCodecCompatible(nextStream, previousStream)) && !hasCriticalTexttracks(nextStream);
349+
let seamlessPeriodSwitch = canSourceBuffersBeReused(nextStream, previousStream);
342350

343351
if (seamlessPeriodSwitch) {
344352
nextStream.setPreloadingScheduled(true);
@@ -505,7 +513,6 @@ function StreamController() {
505513
function switchStream(stream, previousStream, seekTime) {
506514

507515
if (isStreamSwitchingInProgress || !stream || (previousStream === stream && stream.isActive())) return;
508-
logger.info('Switch stream to ' + stream.getId() + ' at t=' + seekTime);
509516
isStreamSwitchingInProgress = true;
510517

511518
eventBus.trigger(Events.PERIOD_SWITCH_STARTED, {
@@ -515,23 +522,23 @@ function StreamController() {
515522

516523
let seamlessPeriodSwitch = false;
517524
if (previousStream) {
518-
seamlessPeriodSwitch = (activeStream.isProtectionCompatible(stream, previousStream) &&
519-
(supportsChangeType || activeStream.isMediaCodecCompatible(stream, previousStream)) && (isNaN(seekTime) || stream.getPreloaded()) && !hasCriticalTexttracks(stream));
525+
seamlessPeriodSwitch = canSourceBuffersBeReused(stream, previousStream);
520526
previousStream.deactivate(seamlessPeriodSwitch);
521527
}
522528

523529
// Determine seek time when switching to new period
524530
// - seek at given seek time
525-
// - or seek at period start if seamless period switching
531+
// - or seek at period start if upcoming period is not prebuffered
526532
seekTime = !isNaN(seekTime) ? seekTime : (!seamlessPeriodSwitch && previousStream ? stream.getStreamInfo().start : NaN);
533+
logger.info(`Switch to stream ${stream.getId()}. Seektime is ${seekTime}, current playback time is ${playbackController.getTime()}`);
534+
logger.info(`Seamless period switch is set to ${seamlessPeriodSwitch}`);
527535

528536
activeStream = stream;
529537
preloadingStreams = preloadingStreams.filter((s) => {
530538
return s.getId() !== activeStream.getId();
531539
});
532540
playbackController.initialize(getActiveStreamInfo(), !!previousStream, seekTime);
533541
if (videoModel.getElement()) {
534-
//TODO detect if we should close jump to activateStream.
535542
openMediaSource(seekTime, (previousStream === null), false, seamlessPeriodSwitch);
536543
} else {
537544
activateStream(seekTime, seamlessPeriodSwitch);
@@ -551,7 +558,7 @@ function StreamController() {
551558

552559
function onMediaSourceOpen() {
553560
// Manage situations in which a call to reset happens while MediaSource is being opened
554-
if (!mediaSource || mediaSource.readyState != 'open') return;
561+
if (!mediaSource || mediaSource.readyState !== 'open') return;
555562

556563
logger.debug('MediaSource is open!');
557564
window.URL.revokeObjectURL(sourceUrl);
@@ -603,21 +610,18 @@ function StreamController() {
603610
if (buffers) {
604611
const keys = Object.keys(buffers);
605612
if (keys.length > 0 && buffers[keys[0]].changeType) {
606-
logger.debug('SourceBuffer changeType method supported. Use it to switch codecs in periods transitions');
607613
supportsChangeType = true;
608614
}
609615
}
610616

611617
if (!initialPlayback) {
612618
if (!isNaN(seekTime)) {
613-
playbackController.seek(seekTime); //we only need to call seek here, bufferingTime was set from seeking event
614-
} else {
615-
// let startTime = playbackController.getStreamStartTime(true);
616-
// if (!keepBuffers) {
617-
// getActiveStreamProcessors().forEach(p => {
618-
// p.setBufferingTime(startTime);
619-
// });
620-
// }
619+
// If the streamswitch has been triggered by a seek command there is no need to seek again. Still we need to trigger the seeking event in order for the controllers to adjust the new time
620+
if (seekTime === playbackController.getTime()) {
621+
eventBus.trigger(Events.SEEK_TARGET, {time: seekTime, streamId: activeStream.getId()});
622+
} else {
623+
playbackController.seek(seekTime);
624+
}
621625
}
622626
}
623627

@@ -734,7 +738,8 @@ function StreamController() {
734738
logger.debug('Dynamic stream: Trying to find the correct starting period');
735739
initialStream = getInitialStream();
736740
}
737-
switchStream(initialStream !== null ? initialStream : streams[0], null, NaN);
741+
const startStream = initialStream !== null ? initialStream : streams[0];
742+
switchStream(startStream, null, NaN);
738743
startPlaybackEndedTimerInterval();
739744
startCheckIfPrebufferingCanStartInterval();
740745
}

0 commit comments

Comments
 (0)