Skip to content

Commit d39cce3

Browse files
authored
Stschr/20250424 role main (Dash-Industry-Forum#4760)
* filter for RoleMain * fix unit test * implement main as default role * final fixes * one further unit test * resolve issues from review
1 parent 59f02fa commit d39cce3

File tree

4 files changed

+130
-5
lines changed

4 files changed

+130
-5
lines changed

index.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1771,6 +1771,8 @@ declare namespace dashjs {
17711771
audio?: TrackSwitchMode;
17721772
};
17731773
ignoreSelectionPriority?: boolean;
1774+
prioritizeRoleMain?: boolean;
1775+
assumeDefaultRoleAsMain?: boolean;
17741776
selectionModeForInitialTrack?: TrackSelectionMode;
17751777
fragmentRequestTimeout?: number;
17761778
fragmentRequestProgressTimeout?: number;

src/core/Settings.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ import Events from './events/Events.js';
189189
* video: Constants.TRACK_SWITCH_MODE_NEVER_REPLACE
190190
* },
191191
* ignoreSelectionPriority: false,
192+
* prioritizeRoleMain: true,
193+
* assumeDefaultRoleAsMain: true,
192194
* selectionModeForInitialTrack: Constants.TRACK_SELECTION_MODE_HIGHEST_EFFICIENCY,
193195
* fragmentRequestTimeout: 20000,
194196
* fragmentRequestProgressTimeout: -1,
@@ -1004,6 +1006,12 @@ import Events from './events/Events.js';
10041006
* @property {} [ignoreSelectionPriority: false]
10051007
* provides the option to disregard any signalled selectionPriority attribute. If disabled and if no initial media settings are set, track selection is accomplished as defined by selectionModeForInitialTrack.
10061008
*
1009+
* @property {} [prioritizeRoleMain: true]
1010+
* provides the option to disable prioritization of AdaptationSets with their Role set to Main
1011+
*
1012+
* @property {} [assumeDefaultRoleAsMain: true]
1013+
* when no Role descriptor is present, assume main per default
1014+
*
10071015
* @property {string} [selectionModeForInitialTrack="highestEfficiency"]
10081016
* Sets the selection mode for the initial track. This mode defines how the initial track will be selected if no initial media settings are set. If initial media settings are set this parameter will be ignored. Available options are:
10091017
*
@@ -1234,6 +1242,8 @@ function Settings() {
12341242
video: Constants.TRACK_SWITCH_MODE_NEVER_REPLACE
12351243
},
12361244
ignoreSelectionPriority: false,
1245+
prioritizeRoleMain: true,
1246+
assumeDefaultRoleAsMain: true,
12371247
selectionModeForInitialTrack: Constants.TRACK_SELECTION_MODE_HIGHEST_EFFICIENCY,
12381248
fragmentRequestTimeout: 20000,
12391249
fragmentRequestProgressTimeout: -1,

src/streaming/controllers/MediaController.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,18 +399,26 @@ function MediaController() {
399399
return notEmpty ? settings : null;
400400
}
401401

402-
function filterTracksBySettings(tracks, filterFn, settings) {
402+
function filterTracksBySettings(tracks, filterFn, preferences) {
403403
let tracksAfterMatcher = [];
404404
tracks.forEach(function (track) {
405-
if (filterFn(settings, track)) {
405+
if (filterFn(preferences, track)) {
406406
tracksAfterMatcher.push(track);
407407
}
408408
});
409409
if (tracksAfterMatcher.length !== 0) {
410410
return tracksAfterMatcher;
411-
} else {
412-
logger.info('Filter-Function (' + filterFn.name + ') resulted in no tracks; setting ignored');
413411
}
412+
413+
if (filterFn === matchSettingsRole && settings.get().streaming.assumeDefaultRoleAsMain && _compareDescriptorType(preferences.role, {schemeIdUri:Constants.DASH_ROLE_SCHEME_ID, value:DashConstants.MAIN} )) {
414+
logger.info('no track with Role set to main - assuming main as default and searching again');
415+
tracksAfterMatcher = filterTracksBySettings(tracks, _matchRoleAbsent, null);
416+
if (tracksAfterMatcher.length !== 0) {
417+
return tracksAfterMatcher;
418+
}
419+
}
420+
421+
logger.info('Filter-Function (' + filterFn.name + ') resulted in no tracks; setting ignored');
414422
return tracks;
415423
}
416424

@@ -441,12 +449,19 @@ function MediaController() {
441449
}
442450

443451
function matchSettingsRole(settings, track, isTrackActive = false) {
452+
if ( !track.roles) {
453+
return false;
454+
}
444455
const matchRole = !settings.role || !!track.roles.filter(function (item) {
445456
return _compareDescriptorType(item, settings.role);
446457
})[0];
447458
return (matchRole || (track.type === Constants.AUDIO && isTrackActive));
448459
}
449460

461+
function _matchRoleAbsent(_, track) {
462+
return (!track.roles) || (track.roles.length === 0);
463+
}
464+
450465
function matchSettingsAccessibility(settings, track) {
451466
let matchAccessibility;
452467
if (!settings.accessibility) {
@@ -685,9 +700,15 @@ function MediaController() {
685700
// Use the track selection function that is defined in the settings
686701
else {
687702
if (!settings.get().streaming.ignoreSelectionPriority) {
703+
logger.info('Trying to find track with highest selectionPriority');
688704
tmpArr = _trackSelectionModeHighestSelectionPriority(tmpArr);
689705
}
706+
if (tmpArr.length > 1 && settings.get().streaming.prioritizeRoleMain) {
707+
logger.info('Trying to find a main track');
708+
tmpArr = filterTracksBySettings(tmpArr, matchSettingsRole, {role: {schemeIdUri:Constants.DASH_ROLE_SCHEME_ID, value:DashConstants.MAIN} });
709+
}
690710
if (tmpArr.length > 1) {
711+
logger.info('Selecting track based on selectionModeForInitialTrack');
691712
let mode = settings.get().streaming.selectionModeForInitialTrack;
692713
switch (mode) {
693714
case Constants.TRACK_SELECTION_MODE_HIGHEST_BITRATE:

test/unit/test/streaming/streaming.controllers.MediaController.js

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,20 @@ describe('MediaController', function () {
700700
expect(objectUtils.areEqual(currentTrack, frTrack)).to.be.true;
701701
});
702702

703+
it('should check initial media settings to choose initial track based on role', function () {
704+
mediaController.addTrack(enTrack);
705+
mediaController.addTrack(esTrack);
706+
mediaController.addTrack(enADTrack);
707+
708+
mediaController.setInitialSettings(trackType, {
709+
role: { schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }
710+
});
711+
mediaController.setInitialMediaSettingsForType(trackType, streamInfo);
712+
713+
let currentTrack = mediaController.getCurrentTrackFor(trackType, streamInfo.id);
714+
expect(objectUtils.areEqual(currentTrack, esTrack)).to.be.true;
715+
});
716+
703717
it('should not check initial media settings to choose initial track when it has already selected a track', function () {
704718
mediaController.addTrack(frTrack);
705719
mediaController.addTrack(qtzTrack);
@@ -775,7 +789,8 @@ describe('MediaController', function () {
775789
representationCount: track.bitrateList.length,
776790
audioChannelConfiguration: track.audioChannelConfiguration ? track.audioChannelConfiguration : [],
777791
selectionPriority: !isNaN(track.selectionPriority) ? track.selectionPriority : 1,
778-
supplementalProperties: track.supplementalProperties ? track.supplementalProperties : []
792+
supplementalProperties: track.supplementalProperties ? track.supplementalProperties : [],
793+
roles: track.roles ? track.roles : []
779794
};
780795
});
781796
const selection = mediaController.selectInitialTrack(type, tracks);
@@ -805,6 +820,83 @@ describe('MediaController', function () {
805820
});
806821
})
807822

823+
describe('roleMain flag' ,function () {
824+
beforeEach(function () {
825+
settings.update({
826+
streaming: {
827+
selectionModeForInitialTrack: Constants.TRACK_SELECTION_MODE_HIGHEST_BITRATE,
828+
prioritizeRoleMain: true
829+
}
830+
});
831+
});
832+
833+
it('should select track with role set to main if no selectionPriority is provided', function () {
834+
testSelectInitialTrack(
835+
'video',
836+
{ bitrateList: [{ bandwidth: 1000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'main' }] },
837+
{ bitrateList: [{ bandwidth: 2000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }] }
838+
);
839+
});
840+
841+
it('should select track with role set to main if other tracks have no role', function () {
842+
testSelectInitialTrack(
843+
'video',
844+
{ bitrateList: [{ bandwidth: 1000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'main' }] },
845+
{ bitrateList: [{ bandwidth: 2000 }], roles: [] }
846+
);
847+
});
848+
849+
it('should select track with role set to main with multiple role descriptors', function () {
850+
testSelectInitialTrack(
851+
'video',
852+
{ bitrateList: [{ bandwidth: 1000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'main' },{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }] },
853+
{ bitrateList: [{ bandwidth: 2000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }] }
854+
);
855+
});
856+
857+
it('should select track with no role if other tracks have role not main', function () {
858+
testSelectInitialTrack(
859+
'video',
860+
{ bitrateList: [{ bandwidth: 1000 }], roles: [] },
861+
{ bitrateList: [{ bandwidth: 2000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }] }
862+
);
863+
});
864+
865+
it('should not select track with no role if other tracks have role not main, when disabled in Settings', function () {
866+
settings.update({
867+
streaming: {
868+
assumeDefaultRoleAsMain: false
869+
}
870+
});
871+
testSelectInitialTrack(
872+
'video',
873+
{ bitrateList: [{ bandwidth: 2000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }] },
874+
{ bitrateList: [{ bandwidth: 1000 }], roles: [] }
875+
);
876+
});
877+
878+
it('should select track based on selectionPriority, if provided, and disregard role main', function () {
879+
testSelectInitialTrack(
880+
'video',
881+
{ bitrateList: [{ bandwidth: 1000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }], selectionPriority: 2 },
882+
{ bitrateList: [{ bandwidth: 2000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'main' }], selectionPriority: 1 }
883+
);
884+
});
885+
886+
it('should select track based on selectionModeForInitialTrack if roleMain flag is false', function () {
887+
settings.update({ streaming: {
888+
prioritizeRoleMain: false,
889+
selectionModeForInitialTrack: Constants.TRACK_SELECTION_MODE_HIGHEST_BITRATE }
890+
});
891+
892+
testSelectInitialTrack(
893+
'video',
894+
{ bitrateList: [{ bandwidth: 2000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'dub' }] },
895+
{ bitrateList: [{ bandwidth: 1000 }], roles: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'main' }] }
896+
);
897+
});
898+
})
899+
808900
describe('"highestSelectionPriority" mode', function () {
809901
beforeEach(function () {
810902
settings.update({ streaming: { selectionModeForInitialTrack: Constants.TRACK_SELECTION_MODE_HIGHEST_EFFICIENCY } });

0 commit comments

Comments
 (0)