Skip to content

Commit 891ad54

Browse files
committed
Add support for SegmentSequenceProperties
1 parent 4d71a47 commit 891ad54

File tree

4 files changed

+174
-25
lines changed

4 files changed

+174
-25
lines changed

package-lock.json

Lines changed: 4 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/streaming/controllers/AbrController.js

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -671,13 +671,13 @@ function AbrController() {
671671

672672
const streamProcessor = streamProcessorDict[streamId][type];
673673
const lastSegment = streamProcessor.getLastSegment();
674-
const canPerformQualitySwitch = _canPerformQualitySwitch(lastSegment);
674+
const currentRepresentation = streamProcessor.getRepresentation();
675+
const canSwitchQuality = canPerformQualitySwitch(lastSegment, currentRepresentation);
675676

676-
if (!settings.get().streaming.abr.autoSwitchBitrate[type] || !canPerformQualitySwitch) {
677+
if (!settings.get().streaming.abr.autoSwitchBitrate[type] || !canSwitchQuality) {
677678
return false;
678679
}
679680

680-
const currentRepresentation = streamProcessor.getRepresentation();
681681
const rulesContext = _createRulesContext(streamProcessor);
682682
const switchRequest = abrRulesCollection.getBestPossibleSwitchRequest(rulesContext);
683683

@@ -703,12 +703,36 @@ function AbrController() {
703703
* When playing Segment Sequence Representations we can not simply switch from one partial segment to another partial segment or another full segment.
704704
* @private
705705
*/
706-
function _canPerformQualitySwitch(lastSegment) {
707-
// For now we only allow a switch when the last partial segment has been loaded and a new "full segment" possibly consisting of multiple partial segments is about to be loaded
706+
function canPerformQualitySwitch(lastSegment, currentRepresentation) {
708707
if (!lastSegment || !lastSegment.isPartialSegment || isNaN(lastSegment.totalNumberOfPartialSegments) || isNaN(lastSegment.replacementSubNumber)) {
709708
return true
710709
}
711-
return lastSegment.replacementSubNumber === (lastSegment.totalNumberOfPartialSegments - 1);
710+
711+
// For partial segments without SegmentSequenceProperties we can only switch if we are at the end of a segment sequence
712+
if (lastSegment.replacementSubNumber === (lastSegment.totalNumberOfPartialSegments - 1)) {
713+
return true;
714+
}
715+
716+
return _segmentSequencePropertiesAllowsSwitching(lastSegment, currentRepresentation);
717+
}
718+
719+
function _segmentSequencePropertiesAllowsSwitching(lastSegment, currentRepresentation) {
720+
if (!currentRepresentation || !currentRepresentation.segmentSequenceProperties || currentRepresentation.segmentSequenceProperties.length <= 0) {
721+
return false;
722+
}
723+
const segmentSequencePropertiesWithTargetSapType = currentRepresentation.segmentSequenceProperties.filter((ssp) => {
724+
return !isNaN(ssp.sapType) && ssp.sapType <= 1;
725+
})
726+
727+
if (segmentSequencePropertiesWithTargetSapType.length === 0) {
728+
return false;
729+
}
730+
731+
let nextPartialSubNumber = (lastSegment.replacementSubNumber + 1) % lastSegment.totalNumberOfPartialSegments;
732+
733+
return segmentSequencePropertiesWithTargetSapType.some((ssp) => {
734+
return nextPartialSubNumber % ssp.cadence === 0
735+
})
712736
}
713737

714738
function _addDroppedFramesHistoryEntry(streamId) {
@@ -754,9 +778,10 @@ function AbrController() {
754778
function manuallySetPlaybackQuality(type, streamInfo, representation, reason = {}) {
755779
const streamProcessor = streamProcessorDict[streamInfo.id][type];
756780
const lastSegment = streamProcessor.getLastSegment();
757-
const canPerformQualitySwitch = _canPerformQualitySwitch(lastSegment);
781+
const currentRepresentation = streamProcessor.getRepresentation();
782+
const canSwitchQuality = canPerformQualitySwitch(lastSegment, currentRepresentation);
758783

759-
if (!canPerformQualitySwitch) {
784+
if (!canSwitchQuality) {
760785
_queueManualQualitySwitch(type, streamInfo, representation, reason);
761786
return;
762787
}
@@ -776,27 +801,28 @@ function AbrController() {
776801

777802
function handlePendingManualQualitySwitch(streamId, mediaType) {
778803
try {
779-
const streamProcessor = streamProcessorDict[streamId][mediaType];
780-
const lastSegment = streamProcessor.getLastSegment();
781-
const canPerformQualitySwitch = _canPerformQualitySwitch(lastSegment);
804+
const manualQualitySwitchKey = _getManualQualitySwitchKey(streamId, mediaType);
805+
let switchRequest = null;
806+
if (queuedManualQualitySwitches.has(manualQualitySwitchKey)) {
807+
switchRequest = queuedManualQualitySwitches.get(manualQualitySwitchKey);
808+
}
782809

783-
if (!canPerformQualitySwitch) {
810+
if (!switchRequest) {
784811
return false
785812
}
786813

787-
const key = _getManualQualitySwitchKey(streamId, mediaType);
788-
let switchRequest = null;
789-
if (queuedManualQualitySwitches.has(key)) {
790-
switchRequest = queuedManualQualitySwitches.get(key);
791-
}
814+
const streamProcessor = streamProcessorDict[streamId][mediaType];
815+
const lastSegment = streamProcessor.getLastSegment();
816+
const currentRepresentation = streamProcessor.getRepresentation();
817+
const canSwitchQuality = canPerformQualitySwitch(lastSegment, currentRepresentation);
792818

793-
if (!switchRequest) {
819+
if (!canSwitchQuality) {
794820
return false
795821
}
796822

797823
const { type, streamInfo, representation, reason } = switchRequest;
798824

799-
queuedManualQualitySwitches.delete(key);
825+
queuedManualQualitySwitches.delete(manualQualitySwitchKey);
800826
setPlaybackQuality(type, streamInfo, representation, reason);
801827
return true;
802828
} catch (e) {
@@ -957,6 +983,7 @@ function AbrController() {
957983

958984

959985
instance = {
986+
canPerformQualitySwitch,
960987
checkPlaybackQuality,
961988
clearDataForStream,
962989
getAbandonmentStateFor,
@@ -988,3 +1015,4 @@ AbrController.__dashjs_factory_name = 'AbrController';
9881015
const factory = FactoryMaker.getSingletonFactory(AbrController);
9891016
FactoryMaker.updateSingletonFactory(AbrController.__dashjs_factory_name, factory);
9901017
export default factory;
1018+

test/unit/test/dash/dash.models.DashManifestModel.getRepresentationsForAdaptation.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ describe('getRepresentationsForAdaptation', function () {
118118
expect(r.essentialProperties[0].schemeIdUri).to.equal('urn:foo');
119119
expect(r.supplementalProperties[0].schemeIdUri).to.equal('urn:supp');
120120
expect(r.segmentSequenceProperties[0].cadence).to.equal(2);
121-
expect(r.segmentSequenceProperties[0].type).to.equal(1);
121+
expect(r.segmentSequenceProperties[0].sapType).to.equal(0);
122122
expect(r.segmentSequenceProperties[0].event).to.equal(false);
123123
expect(r.segmentSequenceProperties[0].alignment).to.equal('id1');
124124
expect(r.index).to.equal(0);

test/unit/test/streaming/streaming.controllers.AbrController.js

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import EventBus from '../../../../src/core/EventBus.js';
1919
import MediaPlayerEvents from '../../../../src/streaming/MediaPlayerEvents.js';
2020
import sinon from 'sinon';
2121
import CapabilitiesMock from '../../mocks/CapabilitiesMock.js';
22+
import SegmentSequenceProperties from '../../../../src/dash/vo/SegmentSequenceProperties.js';
2223

2324
describe('AbrController', function () {
2425
const context = {};
@@ -435,6 +436,127 @@ describe('AbrController', function () {
435436
});
436437
})
437438

439+
describe('abrCtrl.canPerformQualitySwitch()', function () {
440+
441+
it('should return true if lastSegment is undefined', function() {
442+
expect(abrCtrl.canPerformQualitySwitch(undefined, {})).to.be.true;
443+
});
444+
445+
it('should return true if lastSegment is not a partial segment', function() {
446+
expect(abrCtrl.canPerformQualitySwitch({ isPartialSegment: false }, {})).to.be.true;
447+
});
448+
449+
it('should return true if lastSegment has no information about total number of segments', function() {
450+
expect(abrCtrl.canPerformQualitySwitch({
451+
isPartialSegment: true,
452+
totalNumberOfPartialSegments: NaN,
453+
replacementSubNumber: 0
454+
}, {})).to.be.true;
455+
});
456+
457+
it('should return true if lastSegment has no information about replacementSubNumber', function() {
458+
expect(abrCtrl.canPerformQualitySwitch({
459+
isPartialSegment: true,
460+
totalNumberOfPartialSegments: 2,
461+
replacementSubNumber: NaN
462+
}, {})).to.be.true;
463+
});
464+
465+
it('should return true if replacementSubNumber is at the end of the sequence', function() {
466+
expect(abrCtrl.canPerformQualitySwitch({
467+
isPartialSegment: true,
468+
totalNumberOfPartialSegments: 3,
469+
replacementSubNumber: 2
470+
}, {})).to.be.true;
471+
});
472+
473+
it('should return false if no segmentSequenceProperties are defined', function() {
474+
expect(abrCtrl.canPerformQualitySwitch({
475+
isPartialSegment: true,
476+
totalNumberOfPartialSegments: 3,
477+
replacementSubNumber: 1
478+
}, {
479+
segmentSequenceProperties: []
480+
})).to.be.false;
481+
});
482+
483+
it('should return false if segmentSequenceProperties are defined but no segmentSequenceProperties with SAP type 0 or 1 are available', function() {
484+
const ssp = new SegmentSequenceProperties();
485+
ssp.sapType = 2
486+
expect(abrCtrl.canPerformQualitySwitch({
487+
isPartialSegment: true,
488+
totalNumberOfPartialSegments: 4,
489+
replacementSubNumber: 1
490+
}, {
491+
segmentSequenceProperties: [ssp]
492+
})).to.be.false;
493+
});
494+
495+
it('should return false if next partial segment number does not have the right SAP type', function() {
496+
const ssp = new SegmentSequenceProperties();
497+
ssp.sapType = 1;
498+
ssp.cadence = 10
499+
expect(abrCtrl.canPerformQualitySwitch({
500+
isPartialSegment: true,
501+
totalNumberOfPartialSegments: 4,
502+
replacementSubNumber: 0
503+
}, {
504+
segmentSequenceProperties: [ssp]
505+
})).to.be.false;
506+
});
507+
508+
it('should return true if all partial segments have the right SAP type', function() {
509+
const ssp = new SegmentSequenceProperties();
510+
expect(abrCtrl.canPerformQualitySwitch({
511+
isPartialSegment: true,
512+
totalNumberOfPartialSegments: 4,
513+
replacementSubNumber: 2
514+
}, {
515+
segmentSequenceProperties: [ssp]
516+
})).to.be.true;
517+
});
518+
519+
it('should return true if next partial segment number has the right SAP type', function() {
520+
const ssp = new SegmentSequenceProperties();
521+
ssp.sapType = 1;
522+
ssp.cadence = 2;
523+
expect(abrCtrl.canPerformQualitySwitch({
524+
isPartialSegment: true,
525+
totalNumberOfPartialSegments: 4,
526+
replacementSubNumber: 1
527+
}, {
528+
segmentSequenceProperties: [ssp]
529+
})).to.be.true;
530+
});
531+
532+
it('should return false if next partial segment number has not the right SAP type for high number of partial segments', function() {
533+
const ssp = new SegmentSequenceProperties();
534+
ssp.sapType = 1;
535+
ssp.cadence = 16;
536+
expect(abrCtrl.canPerformQualitySwitch({
537+
isPartialSegment: true,
538+
totalNumberOfPartialSegments: 16,
539+
replacementSubNumber: 14
540+
}, {
541+
segmentSequenceProperties: [ssp]
542+
})).to.be.false;
543+
});
544+
545+
it('should return true if next partial segment number has the right SAP type for high number of partial segments', function() {
546+
const ssp = new SegmentSequenceProperties();
547+
ssp.sapType = 1;
548+
ssp.cadence = 16;
549+
expect(abrCtrl.canPerformQualitySwitch({
550+
isPartialSegment: true,
551+
totalNumberOfPartialSegments: 32,
552+
replacementSubNumber: 15
553+
}, {
554+
segmentSequenceProperties: [ssp]
555+
})).to.be.true;
556+
});
557+
558+
})
559+
438560
describe('Additional Tests', function () {
439561
it('should return null when attempting to get abandonment state when abandonmentStateDict array is empty', function () {
440562
const state = abrCtrl.getAbandonmentStateFor('1', Constants.AUDIO);

0 commit comments

Comments
 (0)