Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add an option to calculate bandwidth by using Chromium's networkInfo API #1218

Merged
merged 9 commits into from
Nov 8, 2021
5 changes: 5 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,11 @@
<label class="form-check-label" for="pixel-diff-selector">[EXPERIMENTAL] Use the Pixel difference resolution selector (reloads player)</label>
</div>

<div class="form-check">
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
<input id=network-info type="checkbox" class="form-check-input">
<label class="form-check-label" for="network-info">[EXPERIMENTAL] Use networkInfo API for bandwidth estimations (reloads player)</label>
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
</div>

<div class="form-check">
<input id=override-native type="checkbox" class="form-check-input" checked>
<label class="form-check-label" for="override-native">Override Native (reloads player)</label>
Expand Down
5 changes: 4 additions & 1 deletion scripts/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@
'buffer-water',
'exact-manifest-timings',
'pixel-diff-selector',
'network-info',
'override-native',
'preload',
'mirror-source'
Expand Down Expand Up @@ -499,6 +500,7 @@
'override-native',
'liveui',
'pixel-diff-selector',
'network-info',
'exact-manifest-timings'
].forEach(function(name) {
stateEls[name].addEventListener('change', function(event) {
Expand Down Expand Up @@ -565,7 +567,8 @@
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
experimentalLLHLS: getInputValue(stateEls.llhls),
experimentalExactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
experimentalLeastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector'])
experimentalLeastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector']),
useNetworkInformation: getInputValue(stateEls['network-info'])
}
}
});
Expand Down
28 changes: 27 additions & 1 deletion src/videojs-http-streaming.js
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,7 @@ class VhsHandler extends Component {
this.source_ = source;
this.stats = {};
this.ignoreNextSeekingEvent_ = false;
this.effectiveBandwidthInBits_ = 0;
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
this.setOptions_();

if (this.options_.overrideNative &&
Expand Down Expand Up @@ -617,6 +618,20 @@ class VhsHandler extends Component {
});

this.on(this.tech_, 'play', this.play);

if (this.options_.useNetworkInformation) {
this.networkInformation = window.navigator.connection || window.navigator.mozConnection || window.navigator.webkitConnection;

if (this.networkInformation) {
this.networkInformation.addEventListener('change', () => this.setNetworkInformationStats_());
evanfarina marked this conversation as resolved.
Show resolved Hide resolved

this.setNetworkInformationStats_();
}
}
}

setNetworkInformationStats_() {
this.effectiveBandwidthInBits_ = this.networkInformation.downlink * 1000 * 1000;
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
}

setOptions_() {
Expand All @@ -630,6 +645,7 @@ class VhsHandler extends Component {
typeof this.source_.useBandwidthFromLocalStorage !== 'undefined' ?
this.source_.useBandwidthFromLocalStorage :
this.options_.useBandwidthFromLocalStorage || false;
this.options_.useNetworkInformation = this.options_.useNetworkInformation || false;
this.options_.customTagParsers = this.options_.customTagParsers || [];
this.options_.customTagMappers = this.options_.customTagMappers || [];
this.options_.cacheEncryptionKeys = this.options_.cacheEncryptionKeys || false;
Expand Down Expand Up @@ -682,6 +698,7 @@ class VhsHandler extends Component {
'experimentalBufferBasedABR',
'liveRangeSafeTimeDelta',
'experimentalLLHLS',
'useNetworkInformation',
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
'experimentalExactManifestTimings',
'experimentalLeastPixelDiffSelector'
].forEach((option) => {
Expand Down Expand Up @@ -813,7 +830,16 @@ class VhsHandler extends Component {
*/
systemBandwidth: {
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
get() {
const invBandwidth = 1 / (this.bandwidth || 1);
let bandwidthEst = this.bandwidth;

// NetworkInfo.downlink maxes out at 10Mb/s. In the event that the player estimates a bandwidth
// greater than 10Mb, use the larger value to ensure that high quality streams are not
// accidentally filtered out
if (this.options_.useNetworkInformation) {
bandwidthEst = Math.max(bandwidthEst, this.effectiveBandwidthInBits_);
}

const invBandwidth = 1 / (bandwidthEst || 1);
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
let invThroughput;

if (this.throughput > 0) {
Expand Down
93 changes: 93 additions & 0 deletions test/videojs-http-streaming.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,99 @@ QUnit.test(
}
);

QUnit.test(
'systemBandwidth retrieves bandwidth from networkInformation when option is enabled',
function(assert) {
this.player = createPlayer({ html5: { vhs: { useNetworkInformation: true } } });
this.player.src({
src: 'manifest/master.m3u8',
type: 'application/vnd.apple.mpegurl'
});

this.clock.tick(1);

this.player.tech_.vhs.bandwidth = 10e10;
this.player.tech_.vhs.effectiveBandwidthInBits_ = 20e10;
this.player.tech_.vhs.throughput = 20e10;
// 1 / ( 1 / 20e10 + 1 / 20e10) = 10e10
assert.strictEqual(
this.player.tech_.vhs.systemBandwidth,
10e10,
'systemBandwidth is the combination of networkInfo.downlink and throughput'
);
}
);

QUnit.test(
'systemBandwidth uses player-estimated bandwidth when its value is greater than networkInformation.downLink',
function(assert) {
this.player = createPlayer({ html5: { vhs: { useNetworkInformation: true } } });
this.player.src({
src: 'manifest/master.m3u8',
type: 'application/vnd.apple.mpegurl'
});

this.clock.tick(1);

this.player.tech_.vhs.bandwidth = 20e10;
this.player.tech_.vhs.effectiveBandwidthInBits_ = 10e10;
this.player.tech_.vhs.throughput = 20e10;
// 1 / ( 1 / 20e10 + 1 / 20e10) = 10e10
assert.strictEqual(
this.player.tech_.vhs.systemBandwidth,
10e10,
'systemBandwidth is the combination of player bandwidth and throughput'
);
}
);

QUnit.test(
'effectiveBandwidthInBits_ default',
function(assert) {
this.player.src({
src: 'manifest/master.m3u8',
type: 'application/vnd.apple.mpegurl'
});

this.clock.tick(1);

assert.strictEqual(
this.player.tech_.vhs.effectiveBandwidthInBits_,
0,
'effectiveBandwidthInBits_ defaults to 0'
evanfarina marked this conversation as resolved.
Show resolved Hide resolved
);
}
);

QUnit.test(
'systemBandwidth uses player-estimated bandwidth when networkInformation is not supported',
function(assert) {
const ogNavigator = window.navigator;

// Need to delete the property before setting since navigator doesn't have a setter
delete window.navigator;
window.navigator = {};
this.player = createPlayer({ html5: { vhs: { useNetworkInformation: true } } });
this.player.src({
src: 'manifest/master.m3u8',
type: 'application/vnd.apple.mpegurl'
});

this.clock.tick(1);

this.player.tech_.vhs.bandwidth = 20e10;
this.player.tech_.vhs.throughput = 20e10;
// 1 / ( 1 / 20e10 + 1 / 20e10) = 10e10
assert.strictEqual(
this.player.tech_.vhs.systemBandwidth,
10e10,
'systemBandwidth is the combination of player bandwidth and throughput since networkInformation is not supported'
);

window.navigator = ogNavigator;
}
);

QUnit.test('requests a reasonable rendition to start', function(assert) {
this.player.src({
src: 'manifest/master.m3u8',
Expand Down