Skip to content

Commit

Permalink
Fix/rerequest quality switch (#4144)
Browse files Browse the repository at this point in the history
* WiP: Use segment alignment

* Wait for ongoing requests to be finished before performing standard ABR switch without replacement

* Fix linting errors

* Revert log level change

* Fix unit tests

* Remove console.log

* Add missing fields to index.d.ts

* Fix a race condition in Stream.js caused by ignoring promise of selectMediaInfo
  • Loading branch information
dsilhavy committed Mar 21, 2023
1 parent 6e640ae commit 935b20d
Show file tree
Hide file tree
Showing 14 changed files with 228 additions and 94 deletions.
9 changes: 8 additions & 1 deletion index.d.ts
Expand Up @@ -91,6 +91,7 @@ declare namespace dashjs {
//Promise.all(iterable) can be solved promise, asynchronous promise, pending promise

getCurrentRepresentation(): object;
getCurrentRepresentationInfo(): RepresentationInfo;

getRepresentationForQuality(quality: number): object | null;

Expand Down Expand Up @@ -241,6 +242,8 @@ declare namespace dashjs {
getServiceDescriptions(manifest: object): serviceDescriptions;

getSupplementalProperties(adaptation: object): object;
getSegmentAlignment(adaptation: object): boolean;
getSubSegmentAlignment(adaptation: object): boolean;
}

export interface PatchManifestModel {
Expand Down Expand Up @@ -436,6 +439,8 @@ declare namespace dashjs {
isEmbedded: any | null;
selectionPriority: number;
supplementalProperties: object;
segmentAlignment: boolean;
subSegmentAlignment: boolean;
}

export interface Mpd {
Expand Down Expand Up @@ -1976,7 +1981,7 @@ declare namespace dashjs {

prepareForReplacementTrackSwitch(codec: string): Promise<any>;

prepareForReplacementQualitySwitch(): Promise<any>;
prepareForForceReplacementQualitySwitch(representationInfo: RepresentationInfo): Promise<any>;

prepareForNonReplacementTrackSwitch(codec: string): Promise<any>;

Expand Down Expand Up @@ -2172,6 +2177,8 @@ declare namespace dashjs {

setInitSegmentRequired(value: object): void;

setLastInitializedQuality(value: number): void;

reset(): void;
}

Expand Down
2 changes: 2 additions & 0 deletions src/dash/DashAdapter.js
Expand Up @@ -1046,6 +1046,8 @@ function DashAdapter() {
mediaInfo.lang = dashManifestModel.getLanguageForAdaptation(realAdaptation);
viewpoint = dashManifestModel.getViewpointForAdaptation(realAdaptation);
mediaInfo.viewpoint = viewpoint ? viewpoint.value : undefined;
mediaInfo.segmentAlignment = dashManifestModel.getSegmentAlignment(realAdaptation);
mediaInfo.subSegmentAlignment = dashManifestModel.getSubSegmentAlignment(realAdaptation);
mediaInfo.accessibility = dashManifestModel.getAccessibilityForAdaptation(realAdaptation).map(function (accessibility) {
let accessibilityValue = accessibility.value;
let accessibilityData = accessibilityValue;
Expand Down
2 changes: 2 additions & 0 deletions src/dash/constants/DashConstants.js
Expand Up @@ -162,6 +162,8 @@ class DashConstants {
CAPTURED: 'captured',
APPLICATION: 'application'
}
this.SEGMENT_ALIGNMENT = 'segmentAlignment';
this.SUB_SEGMENT_ALIGNMENT = 'subsegmentAlignment'
}

constructor () {
Expand Down
9 changes: 9 additions & 0 deletions src/dash/controllers/RepresentationController.js
Expand Up @@ -47,11 +47,13 @@ function RepresentationController(config) {
const dashConstants = config.dashConstants;
const segmentsController = config.segmentsController;
const isDynamic = config.isDynamic;
const adapter = config.adapter;

let instance,
realAdaptation,
updating,
voAvailableRepresentations,
currentRepresentationInfo,
currentVoRepresentation;

function setup() {
Expand Down Expand Up @@ -86,10 +88,15 @@ function RepresentationController(config) {
return currentVoRepresentation;
}

function getCurrentRepresentationInfo() {
return currentRepresentationInfo
}

function resetInitialSettings() {
realAdaptation = null;
updating = true;
voAvailableRepresentations = [];
currentRepresentationInfo = null;
}

function reset() {
Expand Down Expand Up @@ -301,6 +308,7 @@ function RepresentationController(config) {

function _setCurrentVoRepresentation(value) {
currentVoRepresentation = value;
currentRepresentationInfo = adapter.convertRepresentationToRepresentationInfo(currentVoRepresentation);
}

function onManifestValidityChanged(e) {
Expand All @@ -320,6 +328,7 @@ function RepresentationController(config) {
isUpdating,
updateData,
getCurrentRepresentation,
getCurrentRepresentationInfo,
getRepresentationForQuality,
prepareQualityChange,
reset
Expand Down
22 changes: 20 additions & 2 deletions src/dash/models/DashManifestModel.js
Expand Up @@ -359,6 +359,22 @@ function DashManifestModel() {
return adaptation && adaptation.Representation_asArray && adaptation.Representation_asArray.length > 0 ? adaptation.Representation_asArray[0].mimeType : null;
}

function getSegmentAlignment(adaptation) {
if (adaptation && adaptation.hasOwnProperty(DashConstants.SEGMENT_ALIGNMENT)) {
return adaptation[DashConstants.SEGMENT_ALIGNMENT] === 'true'
}

return false
}

function getSubSegmentAlignment(adaptation) {
if (adaptation && adaptation.hasOwnProperty(DashConstants.SUB_SEGMENT_ALIGNMENT)) {
return adaptation[DashConstants.SUB_SEGMENT_ALIGNMENT] === 'true'
}

return false
}

function getKID(adaptation) {
if (!adaptation || !adaptation.hasOwnProperty(DashConstants.CENC_DEFAULT_KID)) {
return null;
Expand Down Expand Up @@ -1130,7 +1146,7 @@ function DashManifestModel() {
const element = manifest[DashConstants.CONTENT_STEERING_AS_ARRAY][0];
const entry = new ContentSteering();

entry.serverUrl = element.__text;
entry.serverUrl = element.__text;

if (element.hasOwnProperty(DashConstants.DEFAULT_SERVICE_LOCATION)) {
entry.defaultServiceLocation = element[DashConstants.DEFAULT_SERVICE_LOCATION];
Expand Down Expand Up @@ -1323,7 +1339,9 @@ function DashManifestModel() {
getAvailabilityStartTime,
getServiceDescriptions,
getSupplementalProperties,
setConfig
setConfig,
getSegmentAlignment,
getSubSegmentAlignment
};

setup();
Expand Down
2 changes: 2 additions & 0 deletions src/dash/vo/MediaInfo.js
Expand Up @@ -55,6 +55,8 @@ class MediaInfo {
this.isEmbedded = null;
this.selectionPriority = 1;
this.supplementalProperties = {};
this.segmentAlignment = false;
this.subSegmentAlignment = false;
}

}
Expand Down
26 changes: 16 additions & 10 deletions src/streaming/Stream.js
Expand Up @@ -278,15 +278,18 @@ function Stream(config) {


let element = videoModel.getElement();

const promises = [];
MEDIA_TYPES.forEach((mediaType) => {
// If we are preloading without a video element we can not start texttrack handling.
if (!(mediaType === Constants.TEXT && !mediaSource) && (mediaType !== Constants.VIDEO || (!element || (element && (/^VIDEO$/i).test(element.nodeName))))) {
_initializeMediaForType(mediaType, mediaSource);
promises.push(_initializeMediaForType(mediaType, mediaSource));
}
});

_createBufferSinks(previousBufferSinks)
Promise.all(promises)
.then(() => {
return _createBufferSinks(previousBufferSinks)
})
.then((bufferSinks) => {
isUpdating = false;

Expand Down Expand Up @@ -318,8 +321,10 @@ function Stream(config) {
*/
function initializeForTextWithMediaSource(mediaSource) {
return new Promise((resolve, reject) => {
_initializeMediaForType(Constants.TEXT, mediaSource);
createBufferSinkForText()
_initializeMediaForType(Constants.TEXT, mediaSource)
.then(() => {
return createBufferSinkForText()
})
.then(() => {
textController.createTracks(streamInfo);
resolve()
Expand All @@ -345,7 +350,7 @@ function Stream(config) {

if (!allMediaForType || allMediaForType.length === 0) {
logger.info('No ' + type + ' data.');
return;
return Promise.resolve();
}

if (type === Constants.VIDEO) {
Expand Down Expand Up @@ -378,7 +383,7 @@ function Stream(config) {
return !mediaInfo.isEmbedded;
});
if (allMediaForType.length === 0) {
return;
return Promise.resolve();
}

if (type === Constants.IMAGE) {
Expand All @@ -395,7 +400,7 @@ function Stream(config) {
segmentBaseController: config.segmentBaseController
});
thumbnailController.initialize();
return;
return Promise.resolve();
}

eventBus.trigger(Events.STREAM_INITIALIZING, {
Expand All @@ -412,9 +417,10 @@ function Stream(config) {
if (initialMediaInfo) {
abrController.updateTopQualityIndex(initialMediaInfo);
// In case of mixed fragmented and embedded text tracks, check if initial selected text track is not an embedded track
streamProcessor.selectMediaInfo((type !== Constants.TEXT || !initialMediaInfo.isEmbedded) ? initialMediaInfo : allMediaForType[0]);
return streamProcessor.selectMediaInfo((type !== Constants.TEXT || !initialMediaInfo.isEmbedded) ? initialMediaInfo : allMediaForType[0]);
}

return Promise.resolve();
}

function _isMediaSupported(mediaInfo) {
Expand Down Expand Up @@ -929,7 +935,7 @@ function Stream(config) {
let processor = getProcessorForMediaInfo(trackChangedEvent.oldMediaInfo);
if (!processor) return;
promises.push(processor.prepareTrackSwitch());
processor.selectMediaInfo(mediaInfo);
promises.push(processor.selectMediaInfo(mediaInfo));
}

return Promise.all(promises)
Expand Down

0 comments on commit 935b20d

Please sign in to comment.