Skip to content

Commit

Permalink
Make AbrManager restrictions "soft"
Browse files Browse the repository at this point in the history
This change makes restrictions on AbrManager into "soft" restrictions.
This means that if these restrictions cannot be met, AbrManager will
still choose something instead of throwing an error.

This is in contrast to top-level restrictions or DRM-based
restrictions, which are "hard" restrictions that will stop playback if
necessary.

This change also fixes some minor issues with the SimpleAbrManager
tests, such as cross-test pollution in the restrictions object.

This is a lead-in to issue #855, where AbrManager's restrictions will
have their defaults changed when the "saveData" signal is present in
the browser.

Backported to v2.3.x

Change-Id: Icdb2ff5df7ceb0d94f1267bf81e59dede3c2baf9
  • Loading branch information
joeyparrish committed May 31, 2018
1 parent bb9b751 commit a48c0ef
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 43 deletions.
5 changes: 4 additions & 1 deletion externs/shaka/abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ shakaExtern.AbrManager.prototype.getBandwidthEstimate = function() {};


/**
* Sets the abr configurations.
* Sets the ABR configuration.
*
* It is the responsibility of the AbrManager implementation to implement the
* restrictions behavior described in shaka.extern.AbrConfiguration.
*
* @param {shakaExtern.AbrConfiguration} config
* @exportDoc
Expand Down
26 changes: 18 additions & 8 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,13 @@ shakaExtern.Track;
*
* @description
* An object describing application restrictions on what tracks can play. All
* restrictions must be fulfilled for a track to be playable. If a track does
* not meet the restrictions, it will not appear in the track list and it will
* not be played.
* restrictions must be fulfilled for a track to be playable/selectable.
* The restrictions system behaves somewhat differently at the ABR level and the
* player level, so please refer to the documentation for those specific
* settings.
*
* @see shakaExtern.PlayerConfiguration
* @see shakaExtern.AbrConfiguration
*
* @property {number} minWidth
* The minimum width of a video track, in pixels.
Expand Down Expand Up @@ -621,9 +625,13 @@ shakaExtern.StreamingConfiguration;
* 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)
* The restrictions to apply to ABR decisions. These are "soft" restrictions.
* Any track that fails to meet these restrictions will not be selected
* automatically, but will still appear in the track list and can still be
* selected via selectVariantTrack(). If no tracks meet these restrictions,
* AbrManager should not fail, but choose a low-res or low-bandwidth variant
* instead. It is the responsibiliy of AbrManager implementations to follow
* these rules and implement this behavior.
* @property {number} switchInterval
* The minimum amount of time that must pass between switches, in
* seconds. This keeps us from changing too often and annoying the user.
Expand Down Expand Up @@ -673,8 +681,10 @@ shakaExtern.AbrConfiguration;
* the text track will be shown.
* Changing this during playback will not affect the current playback.
* @property {shakaExtern.Restrictions} restrictions
* The application restrictions to apply to the tracks. The track must
* meet all the restrictions to be playable.
* The application restrictions to apply to the tracks. These are "hard"
* restrictions. Any track that fails to meet these restrictions will not
* appear in the track list. If no tracks meet these restrictions, playback
* will fail.
* @property {number} playRangeStart
* Optional playback and seek start time in seconds. Defaults to 0 if
* not provided.
Expand Down
40 changes: 25 additions & 15 deletions lib/abr/simple_abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ 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 @@ -119,10 +118,15 @@ shaka.abr.SimpleAbrManager.prototype.chooseVariant = function() {
this.config_.defaultBandwidthEstimate);

if (this.variants_.length && !sortedVariants.length) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.RESTRICTIONS_CANNOT_BE_MET);
// If we couldn't meet the ABR restrictions, we should still play something.
// These restrictions are not "hard" restrictions in the way that top-level
// or DRM-based restrictions are. Sort the variants without restrictions
// and keep just the first (lowest-bandwidth) one.
shaka.log.warning('No variants met the ABR restrictions. ' +
'Choosing a variant by lowest bandwidth.');
sortedVariants = SimpleAbrManager.filterAndSortVariants_(
/* restrictions */ null, this.variants_);
sortedVariants = [sortedVariants[0]];
}

// Start by assuming that we will use the first Stream.
Expand Down Expand Up @@ -256,22 +260,28 @@ shaka.abr.SimpleAbrManager.prototype.suggestStreams_ = function() {


/**
* @param {shakaExtern.Restrictions} restrictions
* @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.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;
});
if (restrictions) {
variants = variants.filter((variant) => {
// This was already checked in another scope, but the compiler doesn't
// seem to understand that.
goog.asserts.assert(restrictions, 'Restrictions should exist!');

return shaka.util.StreamUtils.meetsRestrictions(
variant, restrictions,
/* maxHwRes */ {width: Infinity, height: Infinity});
});
}

return variants.sort((v1, v2) => {
return v1.bandwidth - v2.bandwidth;
});
};

54 changes: 35 additions & 19 deletions test/abr/simple_abr_manager_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,8 @@
*/

describe('SimpleAbrManager', function() {
/** @const */
var sufficientBWMultiplier = 1.06;
/** @const */
var defaultBandwidthEstimate = 500e3; // 500kbps
/** @const */
var defaultRestrictions = {
minWidth: 0,
maxWidth: Infinity,
minHeight: 0,
maxHeight: Infinity,
minPixels: 0,
maxPixels: Infinity,
minBandwidth: 0,
maxBandwidth: Infinity
};
const sufficientBWMultiplier = 1.06;
const defaultBandwidthEstimate = 500e3; // 500kbps

/** @type {shakaExtern.AbrConfiguration} */
var config;
Expand Down Expand Up @@ -85,15 +72,22 @@ describe('SimpleAbrManager', function() {
switchInterval: 8,
bandwidthUpgradeTarget: 0.85,
bandwidthDowngradeTarget: 0.95,
restrictions: defaultRestrictions
restrictions: { // Must be inline to avoid cross-test pollution!
minWidth: 0,
maxWidth: Infinity,
minHeight: 0,
maxHeight: Infinity,
minPixels: 0,
maxPixels: Infinity,
minBandwidth: 0,
maxBandwidth: Infinity
},
};

variants = manifest.periods[0].variants;

abrManager = new shaka.abr.SimpleAbrManager();
abrManager.init(shaka.test.Util.spyFunc(switchCallback));
config.defaultBandwidthEstimate = defaultBandwidthEstimate;
config.restrictions = defaultRestrictions;
abrManager.configure(config);
abrManager.setVariants(variants);
});
Expand Down Expand Up @@ -353,7 +347,7 @@ describe('SimpleAbrManager', function() {
.addVariant(0).bandwidth(1e5)
.addVideo(0).size(50, 50)
.addVariant(1).bandwidth(2e5)
.addVideo(2).size(200, 200)
.addVideo(1).size(200, 200)
.build();

abrManager.setVariants(manifest.periods[0].variants);
Expand All @@ -362,6 +356,28 @@ describe('SimpleAbrManager', function() {

config.restrictions.maxWidth = 100;
abrManager.configure(config);

chosen = abrManager.chooseVariant();
expect(chosen.id).toBe(0);
});

it('uses lowest-bandwidth variant when restrictions cannot be met', () => {
manifest = new shaka.test.ManifestGenerator()
.addPeriod(0)
.addVariant(0).bandwidth(1e5)
.addVideo(0).size(50, 50)
.addVariant(1).bandwidth(2e5)
.addVideo(1).size(200, 200)
.build();

abrManager.setVariants(manifest.periods[0].variants);
let chosen = abrManager.chooseVariant();
expect(chosen.id).toBe(1);

// This restriction cannot be met, but we shouldn't fail.
config.restrictions.maxWidth = 1;
abrManager.configure(config);

chosen = abrManager.chooseVariant();
expect(chosen.id).toBe(0);
});
Expand Down

0 comments on commit a48c0ef

Please sign in to comment.