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.

Change-Id: Icdb2ff5df7ceb0d94f1267bf81e59dede3c2baf9
  • Loading branch information
joeyparrish committed May 31, 2018
1 parent 962ec0a commit ed0f37b
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 38 deletions.
5 changes: 4 additions & 1 deletion externs/shaka/abr_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,10 @@ shaka.extern.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 {shaka.extern.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 @@ -280,9 +280,13 @@ shaka.extern.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 shaka.extern.PlayerConfiguration
* @see shaka.extern.AbrConfiguration
*
* @property {number} minWidth
* The minimum width of a video track, in pixels.
Expand Down Expand Up @@ -638,9 +642,13 @@ shaka.extern.StreamingConfiguration;
* The default bandwidth estimate to use if there is not enough data, in
* bit/sec.
* @property {shaka.extern.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 @@ -699,8 +707,10 @@ shaka.extern.AbrConfiguration;
* @property {number} preferredAudioChannelCount
* The preferred number of audio channels.
* @property {shaka.extern.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 @@ -118,10 +117,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 @@ -257,22 +261,28 @@ shaka.abr.SimpleAbrManager.prototype.suggestStreams_ = function() {


/**
* @param {shaka.extern.Restrictions} restrictions
* @param {?shaka.extern.Restrictions} restrictions
* @param {!Array.<shaka.extern.Variant>} variants
* @return {!Array.<shaka.extern.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;
});
};

47 changes: 33 additions & 14 deletions test/abr/simple_abr_manager_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,6 @@
describe('SimpleAbrManager', function() {
const sufficientBWMultiplier = 1.06;
const defaultBandwidthEstimate = 500e3; // 500kbps
const defaultRestrictions = {
minWidth: 0,
maxWidth: Infinity,
minHeight: 0,
maxHeight: Infinity,
minPixels: 0,
maxPixels: Infinity,
minBandwidth: 0,
maxBandwidth: Infinity
};

/** @type {shaka.extern.AbrConfiguration} */
let config;
Expand Down Expand Up @@ -82,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 @@ -350,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 @@ -359,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 ed0f37b

Please sign in to comment.