Skip to content

Commit

Permalink
feat(FEC-10980): Enable setting ABR settings on-the-fly (#134)
Browse files Browse the repository at this point in the history
add an API applyABRRestriction to apply ABR restrictions and show only available tracks after restriction.
  • Loading branch information
Yuvalke authored May 13, 2021
1 parent f859fea commit a400399
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 62 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"@babel/preset-env": "^7.10.4",
"@babel/preset-flow": "^7.10.4",
"@babel/register": "^7.10.5",
"@playkit-js/playkit-js": "0.68.0-canary.3b1d78e",
"@playkit-js/playkit-js": "0.71.0-canary.a8a3e9f",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.1.0",
"babel-plugin-istanbul": "^6.0.0",
Expand Down
154 changes: 98 additions & 56 deletions src/dash-adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import {
Utils,
VideoTrack,
ImageTrack,
ThumbnailInfo
ThumbnailInfo,
PKABRRestrictionObject,
filterTracksByRestriction
} from '@playkit-js/playkit-js';
import {Widevine} from './drm/widevine';
import {PlayReady} from './drm/playready';
Expand Down Expand Up @@ -254,14 +256,7 @@ export default class DashAdapter extends BaseMediaSourceAdapter {
adapterConfig.shakaConfig.abr.defaultBandwidthEstimate = abr.defaultBandwidthEstimate;
}
if (abr.restrictions) {
if (abr.restrictions.minBitrate > 0) {
adapterConfig.shakaConfig.abr.restrictions.minBandwidth = abr.restrictions.minBitrate;
}
if (abr.restrictions.maxBitrate < Infinity) {
//You can either set capping by size or bitrate, if bitrate is set then disable size capping
adapterConfig.capLevelToPlayerSize = false;
adapterConfig.shakaConfig.abr.restrictions.maxBandwidth = abr.restrictions.maxBitrate;
}
Utils.Object.createPropertyPath(adapterConfig, 'abr.restrictions', abr.restrictions);
}
}

Expand Down Expand Up @@ -552,48 +547,89 @@ export default class DashAdapter extends BaseMediaSourceAdapter {
*/
_maybeApplyAbrRestrictions(): void {
if (this._config.capLevelToPlayerSize) {
const videoTracks = this._getVideoTracks();
const getMinDimensions = (dim): number =>
Math.min.apply(
null,
videoTracks.map(variant => variant[dim])
);
//Get minimal allowed dimensions
const minWidth = getMinDimensions('width');
const minHeight = getMinDimensions('height');
const updateAbrRestrictions = () => {
const curHeight = this._videoHeight;
const curWidth = this._videoWidth;
if (typeof curWidth === 'number' && typeof curHeight === 'number') {
//check if current player size is smaller than smallest rendition
//setting restriction below smallest rendition size will result in shaka emitting restriction unmet error
if (curHeight >= minHeight && curWidth >= minWidth) {
DashAdapter._logger.debug(`applying dimension restriction: width < ${curWidth}, height < ${curHeight}`);
this._shaka.configure({
abr: {
restrictions: {
maxHeight: curHeight,
maxWidth: curWidth
}
}
});
} else {
DashAdapter._logger.debug(`applying dimension restriction: width < ${minHeight}, height < ${minWidth}`);
this._shaka.configure({
abr: {
restrictions: {
maxHeight: minHeight,
maxWidth: minWidth
}
}
});
}
}
const getRestrictions = () => {
return {
minHeight: 0,
maxHeight: this._videoHeight,
minWidth: 0,
maxWidth: this._videoWidth,
minBitrate: 0,
maxBitrate: Infinity
};
};
this._clearVideoUpdateTimer();
this._videoSizeUpdateTimer = setInterval(updateAbrRestrictions, ABR_RESTRICTION_UPDATE_INTERVAL);
updateAbrRestrictions();
this._videoSizeUpdateTimer = setInterval(() => this._updateRestriction(getRestrictions()), ABR_RESTRICTION_UPDATE_INTERVAL);
this._updateRestriction(getRestrictions());
} else {
this._clearVideoUpdateTimer();
if (Utils.Object.hasPropertyPath(this._config, 'abr.restrictions')) {
this._updateRestriction(this._config.abr.restrictions);
if (!this.isAdaptiveBitrateEnabled()) {
const videoTracks = this._getParsedVideoTracks();
const availableTracks = filterTracksByRestriction(videoTracks, this._config.abr.restrictions);
if (availableTracks.length) {
const activeTrackInRange = availableTracks.find(track => track.active);
if (!activeTrackInRange) {
this.selectVideoTrack(availableTracks[0]);
}
}
}
}
}
}

/**
* apply ABR restrictions by size
* @private
* @param {PKABRRestrictionObject} restrictions - abr restrictions config
* @returns {void}
*/
_updateRestriction(restrictions: PKABRRestrictionObject): void {
const shakaRestrictionsConfig = this._getRestrictionShakaConfig(restrictions);
this._shaka.configure({
abr: {
restrictions: shakaRestrictionsConfig
}
});
}

_getRestrictionShakaConfig(restrictions: PKABRRestrictionObject): Object {
const getMinDimensions = (dim): number => {
const videoTracks = this._getVideoTracks();
return Math.min.apply(
null,
videoTracks.map(variant => variant[dim])
);
};
let restrictionsShakaConfig = {};
if (restrictions) {
let {maxHeight, maxWidth, maxBitrate, minHeight, minWidth, minBitrate} = restrictions;
const minHeightValue = Math.max(minHeight, 0);
const maxHeightValue = Math.max(maxHeight, getMinDimensions('height'));
if (maxHeightValue >= minHeightValue) {
restrictionsShakaConfig.minHeight = minHeightValue;
restrictionsShakaConfig.maxHeight = maxHeightValue;
} else {
DashAdapter._logger.warn('Invalid maxHeight restriction, maxHeight must be greater than minHeight', minHeight, maxHeight);
}
const minWidthValue = Math.max(minWidth, 0);
const maxWidthValue = Math.max(maxWidth, getMinDimensions('width'));
if (maxWidthValue >= minWidthValue) {
restrictionsShakaConfig.minWidth = minWidthValue;
restrictionsShakaConfig.maxWidth = maxWidthValue;
} else {
DashAdapter._logger.warn('Invalid maxWidth restriction, maxWidth must be greater than minWidth', minWidth, maxWidth);
}
const minBitrateValue = Math.max(minBitrate, 0);
const maxBitrateValue = Math.max(maxBitrate, getMinDimensions('bandwidth'));
if (maxBitrateValue >= minBitrateValue) {
restrictionsShakaConfig.minBandwidth = minBitrateValue;
restrictionsShakaConfig.maxBandwidth = maxBitrateValue;
} else {
DashAdapter._logger.warn('Invalid maxBitrate restriction, maxBitrate must be greater than minBitrate', minBitrate, maxBitrate);
}
}
return restrictionsShakaConfig;
}

/**
Expand Down Expand Up @@ -743,7 +779,6 @@ export default class DashAdapter extends BaseMediaSourceAdapter {
})
.then(() => {
const data = {tracks: this._getParsedTracks()};
this._maybeApplyAbrRestrictions();
DashAdapter._logger.debug('The source has been loaded successfully');
resolve(data);
})
Expand Down Expand Up @@ -835,9 +870,7 @@ export default class DashAdapter extends BaseMediaSourceAdapter {
}

_getActiveTrack(): Object {
return this._shaka.getVariantTracks().filter(variantTrack => {
return variantTrack.active;
})[0];
return this._shaka.getVariantTracks().find(variantTrack => variantTrack.active);
}

/**
Expand Down Expand Up @@ -1070,6 +1103,18 @@ export default class DashAdapter extends BaseMediaSourceAdapter {
return false;
}

/**
* Apply ABR restriction.
* @function applyABRRestriction
* @param {PKABRRestrictionObject} restrictions - abr restrictions config
* @returns {void}
* @public
*/
applyABRRestriction(restrictions: PKABRRestrictionObject): void {
Utils.Object.createPropertyPath(this._config, 'abr.restrictions', restrictions);
this._maybeApplyAbrRestrictions();
}

/**
* Returns the live edge
* @returns {number} - live edge
Expand Down Expand Up @@ -1111,10 +1156,7 @@ export default class DashAdapter extends BaseMediaSourceAdapter {
* @private
*/
_onAdaptation(): void {
let selectedVideoTrack = this._getParsedVideoTracks().filter(function (videoTrack) {
return videoTrack.active;
})[0];
DashAdapter._logger.debug('Video track changed', selectedVideoTrack);
let selectedVideoTrack = this._getParsedVideoTracks().find(videoTrack => videoTrack.active);
this._onTrackChanged(selectedVideoTrack);
}

Expand Down
2 changes: 1 addition & 1 deletion test/src/dash-adapter.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1107,7 +1107,7 @@ describe('DashAdapter: _getLiveEdge', () => {
.load()
.then(() => {
try {
dashInstance._getLiveEdge().should.equal(dashInstance._shaka.seekRange().end);
Math.floor(Math.abs(dashInstance._getLiveEdge() - dashInstance._shaka.seekRange().end)).should.equal(0);
done();
} catch (e) {
done(e);
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -853,10 +853,10 @@
resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd"
integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==

"@playkit-js/playkit-js@0.68.0-canary.3b1d78e":
version "0.68.0-canary.3b1d78e"
resolved "https://registry.yarnpkg.com/@playkit-js/playkit-js/-/playkit-js-0.68.0-canary.3b1d78e.tgz#1a28e435742e3f2aeca40a724b69cec0b9d015cb"
integrity sha512-u5rkeodIhhNWw145BTyppydEiMAl3rVDF4GUMV7z2eGKwwUNqM11bgVfB6gkam5RwvMUyoA5Hsy71mWOt1wuIA==
"@playkit-js/playkit-js@0.71.0-canary.a8a3e9f":
version "0.71.0-canary.a8a3e9f"
resolved "https://registry.yarnpkg.com/@playkit-js/playkit-js/-/playkit-js-0.71.0-canary.a8a3e9f.tgz#067d7b53ed39fcb6fd20d25f530759e61fafd56f"
integrity sha512-wyaS7+hsfclNgnVLEPsdKe2yldpsIFc8cjZQz5QFFxgOp7zMawYlaf+rx3XGaEHrI/BrJy+XoxMNrtLJ7SO8zQ==
dependencies:
js-logger "^1.6.0"
ua-parser-js "^0.7.21"
Expand Down

0 comments on commit a400399

Please sign in to comment.