Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/#4212 #4544

Merged
merged 6 commits into from
Aug 12, 2024
Prev Previous commit
Next Next commit
Add bitrate selection logic based on previous Representation for case…
…s in which the upcoming periods are not preloaded
  • Loading branch information
dsilhavy committed Aug 12, 2024
commit 20d39434f569d7809c3ac7f80612706fbbfe5c13
15 changes: 10 additions & 5 deletions src/streaming/Stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,10 @@ function Stream(config) {
* Activates Stream by re-initializing some of its components
* @param {MediaSource} mediaSource
* @param {array} previousBufferSinks
* @param representationsFromPreviousPeriod
* @memberof Stream#
*/
function activate(mediaSource, previousBufferSinks) {
function activate(mediaSource, previousBufferSinks, representationsFromPreviousPeriod = []) {
return new Promise((resolve, reject) => {
if (isActive) {
resolve(previousBufferSinks);
Expand All @@ -208,7 +209,7 @@ function Stream(config) {
}


_initializeMedia(mediaSource, previousBufferSinks)
_initializeMedia(mediaSource, previousBufferSinks, representationsFromPreviousPeriod)
.then((bufferSinks) => {
isActive = true;
eventBus.trigger(Events.STREAM_ACTIVATED, {
Expand Down Expand Up @@ -252,11 +253,12 @@ function Stream(config) {
*
* @param {object} mediaSource
* @param {array} previousBufferSinks
* @param representationsFromPreviousPeriod
* @return {Promise<Array>}
* @private
*/
function _initializeMedia(mediaSource, previousBufferSinks) {
return _commonMediaInitialization(mediaSource, previousBufferSinks, []);
function _initializeMedia(mediaSource, previousBufferSinks, representationsFromPreviousPeriod = []) {
return _commonMediaInitialization(mediaSource, previousBufferSinks, representationsFromPreviousPeriod);
}

/**
Expand Down Expand Up @@ -414,7 +416,10 @@ function Stream(config) {
if (initialMediaInfo) {
// In case of mixed fragmented and embedded text tracks, check if initial selected text track is not an embedded track
const newMediaInfo = type !== Constants.TEXT || !initialMediaInfo.isEmbedded ? initialMediaInfo : allMediaForType[0];
return streamProcessor.selectMediaInfo({ newMediaInfo, previouslySelectedRepresentation: representationFromPreviousPeriod });
return streamProcessor.selectMediaInfo({
newMediaInfo,
previouslySelectedRepresentation: representationFromPreviousPeriod
});
}

return Promise.resolve();
Expand Down
62 changes: 33 additions & 29 deletions src/streaming/controllers/StreamController.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import DashJSError from '../vo/DashJSError.js';
import Errors from '../../core/errors/Errors.js';
import EventController from './EventController.js';
import ConformanceViolationConstants from '../constants/ConformanceViolationConstants.js';
import streamProcessor from '../StreamProcessor.js';

const PLAYBACK_ENDED_TIMER_INTERVAL = 200;
const DVR_WAITING_OFFSET = 2;
Expand Down Expand Up @@ -420,10 +419,12 @@ function StreamController() {
});

let keepBuffers = false;
let representationsFromPreviousPeriod = [];
activeStream = stream;

if (previousStream) {
keepBuffers = _canSourceBuffersBeReused(stream, previousStream);
representationsFromPreviousPeriod = _getRepresentationsFromPreviousPeriod(previousStream);
previousStream.deactivate(keepBuffers);
}

Expand All @@ -440,9 +441,9 @@ function StreamController() {

// If we have a video element we are not preloading into a virtual buffer
if (videoModel.getElement()) {
_openMediaSource(seekTime, keepBuffers, false);
_openMediaSource({ seekTime, keepBuffers, streamActivated: false, representationsFromPreviousPeriod });
} else {
_activateStream(seekTime, keepBuffers);
_activateStream({ seekTime, keepBuffers });
}
} catch (e) {
isStreamSwitchingInProgress = false;
Expand All @@ -451,12 +452,10 @@ function StreamController() {

/**
* Setup the Media Source. Open MSE and attach event listeners
* @param {number} seekTime
* @param {boolean} keepBuffers
* @param {boolean} streamActivated
* @private
* @param inputParameters
*/
function _openMediaSource(seekTime, keepBuffers, streamActivated = false) {
function _openMediaSource(inputParameters) {
let sourceUrl;

function _onMediaSourceOpen() {
Expand All @@ -473,9 +472,9 @@ function StreamController() {
_setMediaDuration();
const dvrInfo = dashMetrics.getCurrentDVRInfo();
mediaSourceController.setSeekable(dvrInfo.range.start, dvrInfo.range.end);
if (streamActivated) {
if (!isNaN(seekTime)) {
playbackController.seek(seekTime, true, true);
if (inputParameters.streamActivated) {
if (!isNaN(inputParameters.seekTime)) {
playbackController.seek(inputParameters.seekTime, true, true);
}
// Set the media source for all StreamProcessors
activeStream.setMediaSource(mediaSource)
Expand All @@ -484,7 +483,7 @@ function StreamController() {
activeStream.initializeForTextWithMediaSource(mediaSource);
})
} else {
_activateStream(seekTime, keepBuffers);
_activateStream(inputParameters);
}
}

Expand All @@ -499,8 +498,8 @@ function StreamController() {
mediaSource = mediaSourceController.createMediaSource();
_open();
} else {
if (keepBuffers) {
_activateStream(seekTime, keepBuffers);
if (inputParameters.keepBuffers) {
_activateStream(inputParameters);
} else {
mediaSourceController.detachMediaSource(videoModel);
_open();
Expand All @@ -513,8 +512,9 @@ function StreamController() {
* @param {number} seekTime
* @param {boolean} keepBuffers
*/
function _activateStream(seekTime, keepBuffers) {
activeStream.activate(mediaSource, keepBuffers ? bufferSinks : undefined, seekTime)
function _activateStream(inputParameters) {
const representationsFromPreviousPeriod = inputParameters.representationsFromPreviousPeriod || [];
activeStream.activate(mediaSource, inputParameters.keepBuffers ? bufferSinks : undefined, representationsFromPreviousPeriod)
.then((sinks) => {
// check if change type is supported by the browser
if (sinks) {
Expand All @@ -526,9 +526,9 @@ function StreamController() {
}

// Set the initial time for this stream in the StreamProcessor
if (!isNaN(seekTime)) {
eventBus.trigger(Events.SEEK_TARGET, { time: seekTime }, { streamId: activeStream.getId() });
playbackController.seek(seekTime, false, true);
if (!isNaN(inputParameters.seekTime)) {
eventBus.trigger(Events.SEEK_TARGET, { time: inputParameters.seekTime }, { streamId: activeStream.getId() });
playbackController.seek(inputParameters.seekTime, false, true);
activeStream.startScheduleControllers();
}

Expand All @@ -537,6 +537,13 @@ function StreamController() {
});
}

function _getRepresentationsFromPreviousPeriod(previousStream) {
const previousStreamProcessors = previousStream ? previousStream.getStreamProcessors() : [];
return previousStreamProcessors.map((streamProcessor) => {
return streamProcessor.getRepresentation();
})
}

/**
* A playback seeking event was triggered. We need to disable the preloading streams and call the respective seeking handler.
* We distinguish between inner period seeks and outer period seeks
Expand Down Expand Up @@ -635,7 +642,7 @@ function StreamController() {
* @private
*/
function _onCurrentTrackChanged(e) {
// Track was changed in non active stream. No need to do anything, this only happens when a stream starts preloading
// Track was changed in non-active stream. No need to do anything, this only happens when a stream starts preloading
if (e.newMediaInfo.streamInfo.id !== activeStream.getId()) {
return;
}
Expand All @@ -644,9 +651,9 @@ function StreamController() {
_deactivateAllPreloadingStreams();

if (settings.get().streaming.buffer.resetSourceBuffersForTrackSwitch && e.oldMediaInfo && e.oldMediaInfo.codec !== e.newMediaInfo.codec) {
const time = playbackController.getTime();
const seekTime = playbackController.getTime();
activeStream.deactivate(false);
_openMediaSource(time, false, false);
_openMediaSource({ seekTime, keepBuffers: false, streamActivated: false });
return;
}

Expand Down Expand Up @@ -685,10 +692,7 @@ function StreamController() {
let seamlessPeriodSwitch = _canSourceBuffersBeReused(nextStream, previousStream);

if (seamlessPeriodSwitch) {
const previousStreamProcessors = previousStream ? previousStream.getStreamProcessors() : [];
const representationsFromPreviousPeriod = previousStreamProcessors.map((streamProcessor) => {
return streamProcessor.getRepresentation();
})
const representationsFromPreviousPeriod = _getRepresentationsFromPreviousPeriod(previousStream);
nextStream.startPreloading(mediaSource, bufferSinks, representationsFromPreviousPeriod)
.then(() => {
preloadingStreams.push(nextStream);
Expand Down Expand Up @@ -1307,7 +1311,7 @@ function StreamController() {
function switchToVideoElement(seekTime) {
if (activeStream) {
playbackController.initialize(getActiveStreamInfo());
_openMediaSource(seekTime, false, true);
_openMediaSource({ seekTime, keepBuffers: false, streamActivated: true });
}
}

Expand Down Expand Up @@ -1383,13 +1387,13 @@ function StreamController() {
*/
function _handleMediaErrorDecode() {
logger.warn('A MEDIA_ERR_DECODE occured: Resetting the MediaSource');
const time = playbackController.getTime();
const seekTime = playbackController.getTime();
// Deactivate the current stream.
activeStream.deactivate(false);

// Reset MSE
logger.warn(`MediaSource has been resetted. Resuming playback from time ${time}`);
_openMediaSource(time, false, false);
logger.warn(`MediaSource has been resetted. Resuming playback from time ${seekTime}`);
_openMediaSource({ seekTime, keepBuffers: false, streamActivated: false });
}

function getActiveStreamInfo() {
Expand Down