Files
shaka-player/lib/offline/stream_bandwidth_estimator.js
T
Joey Parrish 562a2d567b chore: Strictly require jsdoc
This enables the eslint rule requiring jsdocs on all class
declarations, function declarations, and methods.

Unfortunately, there are two problems with this:

1. We don't use class _declarations_, we use class _expressions_,
which are not covered by this rule.  So it does not enforce jsdoc at
the class level.
2. We tend to document a class at the class-level, rather than at the
constructor.  But a constructor counts as a method for eslint, so it
requires docs on the constructor.  There is no way to configure it to
make an exception for trivial constructors.

So for all trivial (no-argument) constructors, we add empty jsdocs:
  /** */
  constructor() {

This was quicker and easier than setting up some alternative plugin in
eslint to make an exception for us.

The good news is that this rule caught several undocumented parameters
and places where the jsdoc comment was malformed.  So fixing those
also improves the compiler's ability to enforce types.

Change-Id: Icbc46ed690c94e53d354648a883119524f8fca45
2021-01-09 02:00:31 +00:00

174 lines
4.6 KiB
JavaScript

/*! @license
* Shaka Player
* Copyright 2016 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
goog.provide('shaka.offline.StreamBandwidthEstimator');
goog.require('shaka.log');
goog.requireType('shaka.media.SegmentReference');
/**
* A utility class to help estimate the size of streams based on stream and
* variant bandwidths. This class's main purpose is to isolate the logic in
* creating non-zero bandwidth estimates for all streams so that each stream
* will have some influence over the progress of the download.
*/
shaka.offline.StreamBandwidthEstimator = class {
/** */
constructor() {
/** @private {!Object.<number, number>} */
this.estimateByStreamId_ = {};
}
/**
* Add a new variant to the estimator. This will update the estimates for all
* streams in the variant.
*
* @param {shaka.extern.Variant} variant
*/
addVariant(variant) {
// Three cases:
// 1 - Only Audio
// 2 - Only Video
// 3 - Audio and Video
const audio = variant.audio;
const video = variant.video;
// Case 1
if (audio && !video) {
const audioBitRate = audio.bandwidth || variant.bandwidth;
this.setBitrate_(audio.id, audioBitRate);
}
// Case 2
if (!audio && video) {
const videoBitRate = video.bandwidth || variant.bandwidth;
this.setBitrate_(video.id, videoBitRate);
}
// Case 3
if (audio && video) {
// Get the audio's bandwidth. If it is missing, default to our default
// audio bandwidth.
const audioBitRate =
audio.bandwidth ||
shaka.offline.StreamBandwidthEstimator.DEFAULT_AUDIO_BITRATE_;
// Get the video's bandwidth. If it is missing, use the variant bandwidth
// less the audio. If we get a negative bit rate, fall back to our
// default video bandwidth.
let videoBitRate = video.bandwidth || (variant.bandwidth - audioBitRate);
if (videoBitRate <= 0) {
shaka.log.warning(
'Audio bit rate consumes variants bandwidth. Setting video ' +
'bandwidth to match variant\'s bandwidth.');
videoBitRate = variant.bandwidth;
}
this.setBitrate_(audio.id, audioBitRate);
this.setBitrate_(video.id, videoBitRate);
}
}
/**
* @param {number} stream
* @param {number} bitRate
* @private
*/
setBitrate_(stream, bitRate) {
this.estimateByStreamId_[stream] = bitRate;
}
/**
* Create an estimate for the text stream.
*
* @param {shaka.extern.Stream} text
*/
addText(text) {
this.estimateByStreamId_[text.id] =
shaka.offline.StreamBandwidthEstimator.DEFAULT_TEXT_BITRATE_;
}
/**
* Get the estimate for a segment that is part of a stream that has already
* added to the estimator.
*
* @param {number} id
* @param {!shaka.media.SegmentReference} segment
* @return {number}
*/
getSegmentEstimate(id, segment) {
const duration = segment.endTime - segment.startTime;
return this.getEstimate_(id) * duration;
}
/**
* Get the estimate for an init segment for a stream that has already
* added to the estimator.
*
* @param {number} id
* @return {number}
*/
getInitSegmentEstimate(id) {
// Assume that the init segment is worth approximately half a second of
// content.
const duration = 0.5;
return this.getEstimate_(id) * duration;
}
/**
* @param {number} id
* @return {number}
* @private
*/
getEstimate_(id) {
let bitRate = this.estimateByStreamId_[id];
if (bitRate == null) {
bitRate = 0;
shaka.log.error(
'Asking for bitrate of stream not given to the estimator');
}
if (bitRate == 0) {
shaka.log.warning(
'Using bitrate of 0, this stream won\'t affect progress');
}
return bitRate;
}
};
/**
* Since audio bandwidth does not vary much, we are going to use a constant
* approximation for audio bit rate allowing use to more accurately guess at
* the video bitrate.
*
* YouTube's suggested bitrate for stereo audio is 384 kbps so we are going to
* assume that: https://support.google.com/youtube/answer/1722171?hl=en
*
* @const {number}
* @private
*/
shaka.offline.StreamBandwidthEstimator.DEFAULT_AUDIO_BITRATE_ = 393216;
/**
* Since we don't normally get the bitrate for text, we still want to create
* some approximation so that it can influence progress. This will use the
* bitrate from "Tears of Steal" to give some kind of data-driven result.
*
* The file size for English subtitles is 4.7 KB. The video is 12:14 long,
* which means that the text's bit rate is around 52 bps.
*
* @const {number}
* @private
*/
shaka.offline.StreamBandwidthEstimator.DEFAULT_TEXT_BITRATE_ = 52;