Skip to content

Commit

Permalink
Fix/filter update (#4475)
Browse files Browse the repository at this point in the history
* Provide all Representations to the RepresentationController in case the filters like min/max bitrate change over time. Otherwise, we run into an issue with changing the quality.

* Change JSDoc
  • Loading branch information
dsilhavy committed May 8, 2024
1 parent b03393e commit de94e50
Show file tree
Hide file tree
Showing 16 changed files with 82 additions and 58 deletions.
11 changes: 9 additions & 2 deletions samples/dash-if-reference-player/app/main.js
Expand Up @@ -429,8 +429,11 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
var bitrate = Math.round(e.currentRepresentation.bandwidth / 1000);
var availableRepresentations = $scope.player.getRepresentationsByType(e.mediaType)
var maxIndex = availableRepresentations ? availableRepresentations.length : 0;
var pendingIndex = availableRepresentations.findIndex(function (element) {
return element.id === e.currentRepresentation.id
});

$scope[e.mediaType + 'PendingIndex'] = e.currentRepresentation.absoluteIndex + 1;
$scope[e.mediaType + 'PendingIndex'] = pendingIndex + 1;
$scope[e.mediaType + 'PendingMaxIndex'] = maxIndex;
$scope[e.mediaType + 'Bitrate'] = bitrate;
$scope.plotPoint('pendingIndex', e.mediaType, e.newQuality + 1, getTimeForPlot());
Expand All @@ -443,7 +446,11 @@ app.controller('DashController', ['$scope', '$window', 'sources', 'contributors'
}, $scope);

$scope.player.on(dashjs.MediaPlayer.events.QUALITY_CHANGE_RENDERED, function (e) {
$scope[e.mediaType + 'Index'] = e.newRepresentation.absoluteIndex + 1;
var availableRepresentations = $scope.player.getRepresentationsByType(e.mediaType)
var index = availableRepresentations.findIndex(function (element) {
return element.id === e.newRepresentation.id
});
$scope[e.mediaType + 'Index'] = index + 1;
$scope.plotPoint('index', e.mediaType, e.newQuality + 1, getTimeForPlot());
$scope.safeApply();
}, $scope);
Expand Down
12 changes: 7 additions & 5 deletions src/streaming/MediaPlayer.js
Expand Up @@ -1475,9 +1475,11 @@ function MediaPlayer() {
/**
* Sets the current quality for media type instead of letting the ABR Heuristics automatically selecting it.
* This value will be overwritten by the ABR rules unless autoSwitchBitrate is set to false.
* Note that you need to specify a relative index based on the position of the target entry in the return value of getRepresentationsByType().
* Do NOT use representation.absoluteIndex here as this index was assigned prior to applying any filter function. If you want to select a specific representation then use setRepresentationForTypeById() instead.
*
* @param {MediaType} type - 'video', 'audio' or 'image'
* @param {number} value - the quality index, 0 corresponding to the lowest absolute index
* @param {number} value - the quality index, 0 corresponding to the lowest possible index
* @param {boolean} forceReplace - true if segments have to be replaced by segments of the new quality
* @memberof module:MediaPlayer
* @throws {@link module:MediaPlayer~STREAMING_NOT_INITIALIZED_ERROR STREAMING_NOT_INITIALIZED_ERROR} if called before initializePlayback function
Expand Down Expand Up @@ -2398,7 +2400,7 @@ function MediaPlayer() {
return null
}
// do not require Protection as dependencies as this is optional and intended to be loaded separately
let detectedProtection = dashjs.Protection;
let detectedProtection = dashjs.Protection;
if (typeof detectedProtection === 'function') { //TODO need a better way to register/detect plugin components
let protection = detectedProtection(context).create();
Events.extend(detectedProtection.events);
Expand Down Expand Up @@ -2434,7 +2436,7 @@ function MediaPlayer() {
return;
}
// do not require MetricsReporting as dependencies as this is optional and intended to be loaded separately
let detectedMetricsReporting = dashjs.MetricsReporting;
let detectedMetricsReporting = dashjs.MetricsReporting;
if (typeof detectedMetricsReporting === 'function') { //TODO need a better way to register/detect plugin components
let metricsReporting = detectedMetricsReporting(context).create();

Expand All @@ -2458,7 +2460,7 @@ function MediaPlayer() {
}

// do not require MssHandler as dependencies as this is optional and intended to be loaded separately
let detectedMssHandler = dashjs.MssHandler;
let detectedMssHandler = dashjs.MssHandler;
if (typeof detectedMssHandler === 'function') { //TODO need a better way to register/detect plugin components
Errors.extend(detectedMssHandler.errors);
mssHandler = detectedMssHandler(context).create({
Expand Down Expand Up @@ -2496,7 +2498,7 @@ function MediaPlayer() {
}

// do not require Offline as dependencies as this is optional and intended to be loaded separately
let detectedOfflineController = dashjs.OfflineController;
let detectedOfflineController = dashjs.OfflineController;

if (typeof detectedOfflineController === 'function') { //TODO need a better way to register/detect plugin components
Events.extend(detectedOfflineController.events);
Expand Down
6 changes: 3 additions & 3 deletions src/streaming/Stream.js
Expand Up @@ -679,7 +679,7 @@ function Stream(config) {
return thumbnailController.getPossibleVoRepresentations();
}
const mediaInfo = getMediaInfo(type);
return abrController.getPossibleVoRepresentations(mediaInfo, true);
return abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
}

/**
Expand All @@ -698,7 +698,7 @@ function Stream(config) {
possibleVoRepresentations = thumbnailController.getPossibleVoRepresentations();
} else {
const mediaInfo = getMediaInfo(type);
possibleVoRepresentations = abrController.getPossibleVoRepresentations(mediaInfo, true);
possibleVoRepresentations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
}

if (!possibleVoRepresentations || possibleVoRepresentations.length === 0) {
Expand Down Expand Up @@ -727,7 +727,7 @@ function Stream(config) {
possibleVoRepresentations = thumbnailController.getPossibleVoRepresentations();
} else {
const mediaInfo = getMediaInfo(type);
possibleVoRepresentations = abrController.getPossibleVoRepresentations(mediaInfo, true);
possibleVoRepresentations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
}

index = Math.max(Math.min(index, possibleVoRepresentations.length - 1), 0)
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/StreamProcessor.js
Expand Up @@ -654,7 +654,7 @@ function StreamProcessor(config) {
targetRepresentation = representationController.getCurrentRepresentation()
}

// Update Representation Controller with the new data
// Update Representation Controller with the new data. Note we do not filter any Representations here as the filter values might change over time.
const voRepresentations = abrController.getPossibleVoRepresentations(currentMediaInfo, false);
const representationId = targetRepresentation.id;
return representationController.updateData(voRepresentations, currentMediaInfo.isFragmented, representationId)
Expand Down
29 changes: 20 additions & 9 deletions src/streaming/controllers/AbrController.js
Expand Up @@ -216,7 +216,7 @@ function AbrController() {
}

function getOptimalRepresentationForBitrate(mediaInfo, bitrate, includeCompatibleMediaInfos = true) {
const possibleVoRepresentations = getPossibleVoRepresentations(mediaInfo, includeCompatibleMediaInfos);
const possibleVoRepresentations = getPossibleVoRepresentationsFilteredBySettings(mediaInfo, includeCompatibleMediaInfos);

if (!possibleVoRepresentations || possibleVoRepresentations.length === 0) {
return null;
Expand Down Expand Up @@ -246,14 +246,27 @@ function AbrController() {
return null;
}

const possibleVoRepresentations = getPossibleVoRepresentations(mediaInfo, includeCompatibleMediaInfos);
const possibleVoRepresentations = getPossibleVoRepresentationsFilteredBySettings(mediaInfo, includeCompatibleMediaInfos);

return possibleVoRepresentations.find((rep) => {
return rep.absoluteIndex === absoluteIndex
})
}

function getPossibleVoRepresentations(mediaInfo, includeCompatibleMediaInfos = true) {
return _getPossibleVoRepresentations(mediaInfo, includeCompatibleMediaInfos)
}

function getPossibleVoRepresentationsFilteredBySettings(mediaInfo, includeCompatibleMediaInfos = true) {
let voRepresentations = _getPossibleVoRepresentations(mediaInfo, includeCompatibleMediaInfos);

// Filter the list of options based on the provided settings
voRepresentations = _filterByAllowedSettings(voRepresentations)

return voRepresentations;
}

function _getPossibleVoRepresentations(mediaInfo, includeCompatibleMediaInfos) {
let voRepresentations = [];
if (!mediaInfo) {
return voRepresentations;
Expand All @@ -271,10 +284,7 @@ function AbrController() {
// Now sort by quality (usually simply by bitrate)
voRepresentations = _sortByCalculatedQualityRank(voRepresentations);

// Filter the list of options based on the provided settings
voRepresentations = _filterByAllowedSettings(voRepresentations)

// Add an absolute index after filtering
// Add an absolute index
voRepresentations.forEach((rep, index) => {
rep.absoluteIndex = index
})
Expand All @@ -287,7 +297,7 @@ function AbrController() {
})
}

return voRepresentations;
return voRepresentations
}

function _getPossibleMediaInfos(mediaInfo) {
Expand Down Expand Up @@ -746,7 +756,7 @@ function AbrController() {
* @returns {boolean}
*/
function isPlayingAtLowestQuality(representation) {
const voRepresentations = getPossibleVoRepresentations(representation.mediaInfo, true);
const voRepresentations = getPossibleVoRepresentationsFilteredBySettings(representation.mediaInfo, true);

return voRepresentations[0].id === representation.id
}
Expand All @@ -760,7 +770,7 @@ function AbrController() {
if (!representation) {
return true;
}
const voRepresentations = getPossibleVoRepresentations(representation.mediaInfo, true);
const voRepresentations = getPossibleVoRepresentationsFilteredBySettings(representation.mediaInfo, true);

return voRepresentations[voRepresentations.length - 1].id === representation.id;
}
Expand Down Expand Up @@ -791,6 +801,7 @@ function AbrController() {
getInitialBitrateFor,
getOptimalRepresentationForBitrate,
getPossibleVoRepresentations,
getPossibleVoRepresentationsFilteredBySettings,
getRepresentationByAbsoluteIndex,
initialize,
isPlayingAtLowestQuality,
Expand Down
16 changes: 8 additions & 8 deletions src/streaming/models/CmcdModel.js
Expand Up @@ -241,12 +241,12 @@ function CmcdModel() {
invalidRequests.map((k) => {
logger.warn(`request type ${k} is not supported.`);
});

return true;
}

function _checkAvailableKeys(cmcdParametersFromManifest){
const defaultAvailableKeys = Constants.CMCD_AVAILABLE_KEYS;
const defaultAvailableKeys = Constants.CMCD_AVAILABLE_KEYS;
const enabledCMCDKeys = cmcdParametersFromManifest.version ? cmcdParametersFromManifest.keys : settings.get().streaming.cmcd.enabledKeys;
const invalidKeys = enabledCMCDKeys.filter(k => !defaultAvailableKeys.includes(k));

Expand All @@ -267,7 +267,7 @@ function CmcdModel() {
const serviceDescription = serviceDescriptionController.getServiceDescriptionSettings();
if (
settings.get().streaming.cmcd.applyParametersFromMpd &&
serviceDescription.clientDataReporting &&
serviceDescription.clientDataReporting &&
serviceDescription.clientDataReporting.cmcdParameters
) {
cmcdParametersFromManifest = serviceDescription.clientDataReporting.cmcdParameters;
Expand Down Expand Up @@ -299,7 +299,7 @@ function CmcdModel() {
function getCmcdData(request) {
try {
let cmcdData = null;

_updateLastMediaTypeRequest(request.type, request.mediaType);

if (_isIncludedInRequestFilter(request.type)) {
Expand Down Expand Up @@ -334,7 +334,7 @@ function CmcdModel() {

function _getCmcdDataForSteering(request) {
const data = !_lastMediaTypeRequest ? _getGenericCmcdData(request) : _getCmcdDataForMediaSegment(request, _lastMediaTypeRequest);

data.ot = CmcdObjectType.OTHER;

return data;
Expand Down Expand Up @@ -481,9 +481,9 @@ function CmcdModel() {

let cid = settings.get().streaming.cmcd.cid ? settings.get().streaming.cmcd.cid : internalData.cid;
cid = cmcdParametersFromManifest.contentID ? cmcdParametersFromManifest.contentID : cid;

data.v = CMCD_VERSION;

data.sid = settings.get().streaming.cmcd.sid ? settings.get().streaming.cmcd.sid : internalData.sid;
data.sid = cmcdParametersFromManifest.sessionID ? cmcdParametersFromManifest.sessionID : data.sid;

Expand Down Expand Up @@ -518,7 +518,7 @@ function CmcdModel() {

function _getTopBitrateByType(mediaInfo) {
try {
const bitrates = abrController.getPossibleVoRepresentations(mediaInfo).map((rep) => {
const bitrates = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo).map((rep) => {
return rep.bitrateInKbit
});
return Math.max(...bitrates)
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/rules/abr/BolaRule.js
Expand Up @@ -157,7 +157,7 @@ function BolaRule(config) {
function _getInitialBolaState(rulesContext) {
const initialState = {};
const mediaInfo = rulesContext.getMediaInfo();
const representations = abrController.getPossibleVoRepresentations(mediaInfo, true);
const representations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
const bitrates = representations.map(r => r.bandwidth);
let utilities = bitrates.map(b => Math.log(b));
utilities = utilities.map(u => u - utilities[0] + 1); // normalize
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/rules/abr/DroppedFramesRule.js
Expand Up @@ -31,7 +31,7 @@ function DroppedFramesRule() {

let droppedFrames = 0;
let totalFrames = 0;
const representations = abrController.getPossibleVoRepresentations(mediaInfo, true);
const representations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
let newRepresentation = null;

//No point in measuring dropped frames for the first index.
Expand Down
4 changes: 2 additions & 2 deletions src/streaming/rules/abr/L2ARule.js
Expand Up @@ -301,7 +301,7 @@ function L2ARule(config) {
const representation = abrController.getOptimalRepresentationForBitrate(mediaInfo, safeThroughput, true);//During strat-up phase abr.controller is responsible for bitrate decisions.
const bufferLevel = dashMetrics.getCurrentBufferLevel(mediaType, true);
const l2AParameter = l2AParameterDict[mediaType];
const possibleRepresentations = abrController.getPossibleVoRepresentations(mediaInfo, true);
const possibleRepresentations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);

switchRequest.representation = representation;
switchRequest.reason.throughput = safeThroughput;
Expand Down Expand Up @@ -350,7 +350,7 @@ function L2ARule(config) {
//Main adaptation logic of L2A-LL
const abrController = rulesContext.getAbrController();
const mediaInfo = rulesContext.getMediaInfo();
const possibleRepresentations = abrController.getPossibleVoRepresentations(mediaInfo, true);
const possibleRepresentations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
const videoModel = rulesContext.getVideoModel();
let currentPlaybackRate = videoModel.getPlaybackRate();
const alpha = Math.max(Math.pow(HORIZON, 1), VL * Math.sqrt(HORIZON));// Step size, used for gradient descent exploration granularity
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/rules/abr/SwitchHistoryRule.js
Expand Up @@ -22,7 +22,7 @@ function SwitchHistoryRule() {
const switchRequests = switchRequestHistory ? switchRequestHistory.getSwitchRequests(streamId, mediaType) : {};
const abrController = rulesContext.getAbrController();
const mediaInfo = rulesContext.getMediaInfo();
const representations = abrController.getPossibleVoRepresentations(mediaInfo, true);
const representations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
let drops = 0;
let noDrops = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/streaming/rules/abr/lolp/LearningAbrController.js
Expand Up @@ -385,7 +385,7 @@ function LearningAbrController() {
function _setSomBitrateNeurons(mediaInfo, abrController) {
if (!somBitrateNeurons) {
somBitrateNeurons = [];
const possibleRepresentations = abrController.getPossibleVoRepresentations(mediaInfo, true);
const possibleRepresentations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
const bitrateList = possibleRepresentations.map((r) => r.bandwidth);
minBitrate = Math.min(...bitrateList);
bitrateNormalizationFactor = _getMagnitude(bitrateList);
Expand Down
2 changes: 1 addition & 1 deletion src/streaming/rules/abr/lolp/LoLpRule.js
Expand Up @@ -103,7 +103,7 @@ function LoLPRule(config) {
}

// QoE parameters
const possibleRepresentations = abrController.getPossibleVoRepresentations(mediaInfo, true);
const possibleRepresentations = abrController.getPossibleVoRepresentationsFilteredBySettings(mediaInfo, true);
let bandwidths = possibleRepresentations.map(r => r.bandwidth);
let segmentDuration = rulesContext.getRepresentation().fragmentDuration;
let minBitrateKbps = Math.min(...bandwidths) / 1000.0; // min bitrate level
Expand Down
4 changes: 4 additions & 0 deletions test/unit/mocks/AbrControllerMock.js
Expand Up @@ -110,6 +110,10 @@ function AbrControllerMock () {
this.getPossibleVoRepresentations = function () {
return []
}

this.getPossibleVoRepresentationsFilteredBySettings = function () {
return []
}
}

export default AbrControllerMock;
2 changes: 1 addition & 1 deletion test/unit/mocks/RulesContextMock.js
Expand Up @@ -30,7 +30,7 @@ function RulesContextMock() {
};
this.getAbrController = function () {
return {
getPossibleVoRepresentations: function () {
getPossibleVoRepresentationsFilteredBySettings: function () {
return [{ id: 1 }]
}
};
Expand Down

0 comments on commit de94e50

Please sign in to comment.