Skip to content

Commit

Permalink
Add separate restrictions to AbrManager.
Browse files Browse the repository at this point in the history
This adds a second Restrictions object to the Player configuration to
restrict ABR decisions.  The original restrictions will remove any
non-matching streams from the manifest, like before.  This new
configuration will only apply to AbrManager choices.  Any restricted
track can still be explicitly selected using Player.selectTrack.

Closes #565

Change-Id: I52379b096e81f97df22b6683669c5787694d87e7
  • Loading branch information
TheModMaker committed Jan 9, 2017
1 parent 8afadb4 commit b28f84c
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 36 deletions.
8 changes: 8 additions & 0 deletions externs/shaka/abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,11 @@ shakaExtern.AbrManager.prototype.getBandwidthEstimate = function() {};
*/
shakaExtern.AbrManager.prototype.setDefaultEstimate = function(estimate) {};


/**
* Sets the restrictions that AbrManager will use when choosing streams.
*
* @param {shakaExtern.Restrictions} restrictions
* @exportDoc
*/
shakaExtern.AbrManager.prototype.setRestrictions = function(restrictions) {};
7 changes: 6 additions & 1 deletion externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,8 @@ shakaExtern.StreamingConfiguration;
* @typedef {{
* manager: shakaExtern.AbrManager,
* enabled: boolean,
* defaultBandwidthEstimate: number
* defaultBandwidthEstimate: number,
* restrictions: shakaExtern.Restrictions
* }}
*
* @property {shakaExtern.AbrManager} manager
Expand All @@ -384,6 +385,10 @@ shakaExtern.StreamingConfiguration;
* @property {number} defaultBandwidthEstimate
* The default bandwidth estimate to use if there is not enough data, in
* bit/sec.
* @property {shakaExtern.Restrictions} restrictions
* The restrictions to apply to ABR decisions. The AbrManager will not
* choose any streams that do not meet these restrictions. (Note that
* they can still be chosen by the application)
* @exportDoc
*/
shakaExtern.AbrConfiguration;
Expand Down
61 changes: 49 additions & 12 deletions lib/abr/simple_abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ goog.provide('shaka.abr.SimpleAbrManager');
goog.require('goog.asserts');
goog.require('shaka.abr.EwmaBandwidthEstimator');
goog.require('shaka.log');
goog.require('shaka.util.Error');
goog.require('shaka.util.StreamUtils');



Expand Down Expand Up @@ -63,6 +65,18 @@ shaka.abr.SimpleAbrManager = function() {
* @private {?number}
*/
this.lastTimeChosenMs_ = null;

/** @private {shakaExtern.Restrictions} */
this.restrictions_ = {
minWidth: 0,
maxWidth: Infinity,
minHeight: 0,
maxHeight: Infinity,
minPixels: 0,
maxPixels: Infinity,
minBandwidth: 0,
maxBandwidth: Infinity
};
};


Expand Down Expand Up @@ -200,6 +214,15 @@ shaka.abr.SimpleAbrManager.prototype.setDefaultEstimate = function(estimate) {
};


/**
* @override
* @export
*/
shaka.abr.SimpleAbrManager.prototype.setRestrictions = function(restrictions) {
this.restrictions_ = restrictions;
};


/**
* @override
* @export
Expand Down Expand Up @@ -259,26 +282,31 @@ shaka.abr.SimpleAbrManager.prototype.suggestStreams_ = function() {
/**
* Chooses a Variant with an optimal bandwidth.
*
* @param {!Array.<!shakaExtern.Variant>} variants
* @return {!shakaExtern.Variant}
* @param {!Array.<shakaExtern.Variant>} variants
* @return {shakaExtern.Variant}
* @private
*/
shaka.abr.SimpleAbrManager.prototype.chooseVariant_ = function(variants) {
// Alias.
var SimpleAbrManager = shaka.abr.SimpleAbrManager;

// Get sorted Streams.
var sortedVariants = SimpleAbrManager.sortVariantsByBandwidth_(variants);
var sortedVariants = SimpleAbrManager.filterAndSortVariants_(
this.restrictions_, variants);
var currentBandwidth = this.bandwidthEstimator_.getBandwidthEstimate();

if (variants.length && !sortedVariants.length) {
throw new shaka.util.Error(
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.RESTRICTIONS_CANNOT_BE_MET);
}

// Start by assuming that we will use the first Stream.
var chosen = sortedVariants[0];

for (var i = 0; i < sortedVariants.length; ++i) {
var variant = sortedVariants[i];
var nextVariant = (i + 1 < sortedVariants.length) ?
sortedVariants[i + 1] :
{bandwidth: Infinity};
var nextVariant = sortedVariants[i + 1] || {bandwidth: Infinity};

var minBandwidth = variant.bandwidth /
SimpleAbrManager.BANDWIDTH_DOWNGRADE_TARGET_;
Expand All @@ -298,13 +326,22 @@ shaka.abr.SimpleAbrManager.prototype.chooseVariant_ = function(variants) {


/**
* @param {!Array.<!shakaExtern.Variant>} variants
* @return {!Array.<!shakaExtern.Variant>} variants sorted
* in ascending order of bandwidth.
* @param {shakaExtern.Restrictions} restrictions
* @param {!Array.<shakaExtern.Variant>} variants
* @return {!Array.<shakaExtern.Variant>} variants filtered according to
* |restrictions| and sorted in ascending order of bandwidth.
* @private
*/
shaka.abr.SimpleAbrManager.sortVariantsByBandwidth_ = function(variants) {
return variants.slice(0)
.sort(function(v1, v2) { return v1.bandwidth - v2.bandwidth; });
shaka.abr.SimpleAbrManager.filterAndSortVariants_ = function(
restrictions, variants) {
return variants
.filter(function(variant) {
return shaka.util.StreamUtils.meetsRestrictions(
variant, restrictions,
/* maxHwRes */ {width: Infinity, height: Infinity});
})
.sort(function(v1, v2) {
return v1.bandwidth - v2.bandwidth;
});
};

20 changes: 18 additions & 2 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ shaka.Player.prototype.applyConfig_ = function() {

this.config_.abr.manager.setDefaultEstimate(
this.config_.abr.defaultBandwidthEstimate);
this.config_.abr.manager.setRestrictions(this.config_.abr.restrictions);
};


Expand Down Expand Up @@ -1313,7 +1314,17 @@ shaka.Player.prototype.defaultConfig_ = function() {
manager: this.defaultAbrManager_,
enabled: true,
defaultBandwidthEstimate:
shaka.abr.EwmaBandwidthEstimator.DEFAULT_ESTIMATE
shaka.abr.EwmaBandwidthEstimator.DEFAULT_ESTIMATE,
restrictions: {
minWidth: 0,
maxWidth: Infinity,
minHeight: 0,
maxHeight: Infinity,
minPixels: 0,
maxPixels: Infinity,
minBandwidth: 0,
maxBandwidth: Infinity
}
},
preferredAudioLanguage: '',
preferredTextLanguage: '',
Expand Down Expand Up @@ -1513,7 +1524,12 @@ shaka.Player.prototype.chooseStreams_ =

if (needsUpdate.length > 0) {
shaka.log.debug('Choosing new streams for', needsUpdate);
var chosen = this.config_.abr.manager.chooseStreams(needsUpdate);
var chosen = {};
try {
chosen = this.config_.abr.manager.chooseStreams(needsUpdate);
} catch (err) {
this.onError_(err);
}

return chosen;
} else {
Expand Down
54 changes: 33 additions & 21 deletions lib/util/stream_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,41 +26,53 @@ goog.require('shaka.util.LanguageUtils');


/**
* @param {shakaExtern.Period} period
* @param {shakaExtern.Variant} variant
* @param {shakaExtern.Restrictions} restrictions
* Configured restrictions from the user.
* @param {{width: number, height: number}} maxHwRes
* The maximum resolution the hardware can handle.
* This is applied separately from user restrictions because the setting
* should not be easily replaced by the user's configuration.
* @return {boolean}
*/
shaka.util.StreamUtils.meetsRestrictions = function(
variant, restrictions, maxHwRes) {
var video = variant.video;
if (video) {
if (video.width < restrictions.minWidth ||
video.width > restrictions.maxWidth || video.width > maxHwRes.width ||
video.height < restrictions.minHeight ||
video.height > restrictions.maxHeight ||
video.height > maxHwRes.height ||
(video.width * video.height) < restrictions.minPixels ||
(video.width * video.height) > restrictions.maxPixels) {
return false;
}
}

if (variant.bandwidth < restrictions.minBandwidth ||
variant.bandwidth > restrictions.maxBandwidth) {
return false;
}

return true;
};


/**
* @param {shakaExtern.Period} period
* @param {shakaExtern.Restrictions} restrictions
* @param {{width: number, height: number}} maxHwRes
* @return {boolean} Whether the tracks changed.
*/
shaka.util.StreamUtils.applyRestrictions =
function(period, restrictions, maxHwRes) {
var tracksChanged = false;

period.variants.forEach(function(variant) {
var video = variant.video;
var originalAllowed = variant.allowedByApplication;
variant.allowedByApplication = true;

if (video) {
if (video.width < restrictions.minWidth ||
video.width > restrictions.maxWidth ||
video.width > maxHwRes.width ||
video.height < restrictions.minHeight ||
video.height > restrictions.maxHeight ||
video.height > maxHwRes.height ||
(video.width * video.height) < restrictions.minPixels ||
(video.width * video.height) > restrictions.maxPixels) {
variant.allowedByApplication = false;
}
}

if (variant.bandwidth < restrictions.minBandwidth ||
variant.bandwidth > restrictions.maxBandwidth) {
variant.allowedByApplication = false;
}
variant.allowedByApplication = shaka.util.StreamUtils.meetsRestrictions(
variant, restrictions, maxHwRes);

if (originalAllowed != variant.allowedByApplication) {
tracksChanged = true;
Expand Down
18 changes: 18 additions & 0 deletions test/abr/simple_abr_manager_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -331,4 +331,22 @@ describe('SimpleAbrManager', function() {
// cleared.
expect(switchCallback).toHaveBeenCalledWith(jasmine.any(Object));
});

it('will respect restrictions', function() {
manifest = new shaka.test.ManifestGenerator()
.addPeriod(0)
.addVariant(0).bandwidth(1e5)
.addVideo(0).size(50, 50)
.addVariant(1).bandwidth(2e5)
.addVideo(2).size(200, 200)
.build();

abrManager.setVariants(manifest.periods[0].variants);
var chosen = abrManager.chooseStreams(['video']);
expect(chosen['video'].id).toBe(2);

abrManager.setRestrictions({maxWidth: 100});
chosen = abrManager.chooseStreams(['video']);
expect(chosen['video'].id).toBe(0);
});
});
5 changes: 5 additions & 0 deletions test/test/util/simple_fakes.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ shaka.test.FakeAbrManager = function() {
spyOn(this, 'segmentDownloaded');
spyOn(this, 'getBandwidthEstimate');
spyOn(this, 'setDefaultEstimate');
spyOn(this, 'setRestrictions');
spyOn(this, 'setTextStreams').and.callThrough();
spyOn(this, 'setVariants').and.callThrough();
};
Expand Down Expand Up @@ -80,6 +81,10 @@ shaka.test.FakeAbrManager.prototype.getBandwidthEstimate = function() {};
shaka.test.FakeAbrManager.prototype.setDefaultEstimate = function() {};


/** @override */
shaka.test.FakeAbrManager.prototype.setRestrictions = function() {};


/** @override */
shaka.test.FakeAbrManager.prototype.chooseStreams = function(
mediaTypesToUpdate) {
Expand Down

0 comments on commit b28f84c

Please sign in to comment.