Skip to content

Commit

Permalink
feat: enable LLHLS support by default and remove experimental prefix …
Browse files Browse the repository at this point in the history
…on options (#1301)

* `experimentalBufferBasedABR` becomes `bufferBasedABR`
* `experimentalLLHLS` becomes `llhls` (and is now `true` by default)
* `experimentalExactManifestTimings` becomes `exactManifestTimings`
* `experimentalLeastPixelDiffSelector` becomes `leastPixelDiffSelector`

BREAKING CHANGE: This renames four experimental options to no longer be experimental and enables Low Latency HLS support by default (`llhls: false` will still disable it, if desired).
  • Loading branch information
kchang-brightcove committed Aug 19, 2022
1 parent c7aa9c1 commit 02c3c77
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 60 deletions.
22 changes: 18 additions & 4 deletions scripts/index.js
Expand Up @@ -512,6 +512,20 @@
});
});

[
'llhls'
].forEach(function(name) {
stateEls[name].checked = true;
});

[
'exact-manifest-timings',
'pixel-diff-selector',
'buffer-water'
].forEach(function(name) {
stateEls[name].checked = false;
});

stateEls.debug.addEventListener('change', function(event) {
saveState();
window.videojs.log.level(event.target.checked ? 'debug' : 'info');
Expand Down Expand Up @@ -566,10 +580,10 @@
html5: {
vhs: {
overrideNative: getInputValue(stateEls['override-native']),
experimentalBufferBasedABR: getInputValue(stateEls['buffer-water']),
experimentalLLHLS: getInputValue(stateEls.llhls),
experimentalExactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
experimentalLeastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector']),
bufferBasedABR: getInputValue(stateEls['buffer-water']),
llhls: getInputValue(stateEls.llhls),
exactManifestTimings: getInputValue(stateEls['exact-manifest-timings']),
leastPixelDiffSelector: getInputValue(stateEls['pixel-diff-selector']),
useNetworkInformationApi: getInputValue(stateEls['network-info']),
useDtsForTimestampOffset: getInputValue(stateEls['dts-offset'])
}
Expand Down
6 changes: 3 additions & 3 deletions src/manifest.js
Expand Up @@ -23,7 +23,7 @@ export const createPlaylistID = (index, uri) => {
* An array of custom tag parsers for the m3u8-parser instance
* @param {Object[]} [customTagMappers]
* An array of custom tag mappers for the m3u8-parser instance
* @param {boolean} [experimentalLLHLS=false]
* @param {boolean} [llhls]
* Whether to keep ll-hls features in the manifest after parsing.
* @return {Object}
* The manifest object
Expand All @@ -34,7 +34,7 @@ export const parseManifest = ({
manifestString,
customTagParsers = [],
customTagMappers = [],
experimentalLLHLS
llhls
}) => {
const parser = new M3u8Parser();

Expand All @@ -55,7 +55,7 @@ export const parseManifest = ({

// remove llhls features from the parsed manifest
// if we don't want llhls support.
if (!experimentalLLHLS) {
if (!llhls) {
[
'preloadSegment',
'skip',
Expand Down
32 changes: 16 additions & 16 deletions src/playlist-controller.js
Expand Up @@ -53,7 +53,7 @@ const shouldSwitchToMedia = function({
bufferLowWaterLine,
bufferHighWaterLine,
duration,
experimentalBufferBasedABR,
bufferBasedABR,
log
}) {
// we have no other playlist to switch to
Expand Down Expand Up @@ -93,7 +93,7 @@ const shouldSwitchToMedia = function({
}

const forwardBuffer = Ranges.timeAheadOf(buffered, currentTime);
const maxBufferLowWaterLine = experimentalBufferBasedABR ?
const maxBufferLowWaterLine = bufferBasedABR ?
Config.EXPERIMENTAL_MAX_BUFFER_LOW_WATER_LINE : Config.MAX_BUFFER_LOW_WATER_LINE;

// For the same reason as LIVE, we ignore the low water line when the VOD
Expand All @@ -108,10 +108,10 @@ const shouldSwitchToMedia = function({

// when switching down, if our buffer is lower than the high water line,
// we can switch down
if (nextBandwidth < currBandwidth && (!experimentalBufferBasedABR || forwardBuffer < bufferHighWaterLine)) {
if (nextBandwidth < currBandwidth && (!bufferBasedABR || forwardBuffer < bufferHighWaterLine)) {
let logLine = `${sharedLogLine} as next bandwidth < current bandwidth (${nextBandwidth} < ${currBandwidth})`;

if (experimentalBufferBasedABR) {
if (bufferBasedABR) {
logLine += ` and forwardBuffer < bufferHighWaterLine (${forwardBuffer} < ${bufferHighWaterLine})`;
}
log(logLine);
Expand All @@ -120,10 +120,10 @@ const shouldSwitchToMedia = function({

// and if our buffer is higher than the low water line,
// we can switch up
if ((!experimentalBufferBasedABR || nextBandwidth > currBandwidth) && forwardBuffer >= bufferLowWaterLine) {
if ((!bufferBasedABR || nextBandwidth > currBandwidth) && forwardBuffer >= bufferLowWaterLine) {
let logLine = `${sharedLogLine} as forwardBuffer >= bufferLowWaterLine (${forwardBuffer} >= ${bufferLowWaterLine})`;

if (experimentalBufferBasedABR) {
if (bufferBasedABR) {
logLine += ` and next bandwidth > current bandwidth (${nextBandwidth} > ${currBandwidth})`;
}
log(logLine);
Expand Down Expand Up @@ -159,8 +159,8 @@ export class PlaylistController extends videojs.EventTarget {
enableLowInitialPlaylist,
sourceType,
cacheEncryptionKeys,
experimentalBufferBasedABR,
experimentalLeastPixelDiffSelector,
bufferBasedABR,
leastPixelDiffSelector,
captionServices
} = options;

Expand All @@ -176,8 +176,8 @@ export class PlaylistController extends videojs.EventTarget {

Vhs = externVhs;

this.experimentalBufferBasedABR = Boolean(experimentalBufferBasedABR);
this.experimentalLeastPixelDiffSelector = Boolean(experimentalLeastPixelDiffSelector);
this.bufferBasedABR = Boolean(bufferBasedABR);
this.leastPixelDiffSelector = Boolean(leastPixelDiffSelector);
this.withCredentials = withCredentials;
this.tech_ = tech;
this.vhs_ = tech.vhs;
Expand Down Expand Up @@ -253,7 +253,7 @@ export class PlaylistController extends videojs.EventTarget {
cacheEncryptionKeys,
sourceUpdater: this.sourceUpdater_,
timelineChangeController: this.timelineChangeController_,
experimentalExactManifestTimings: options.experimentalExactManifestTimings
exactManifestTimings: options.exactManifestTimings
};

// The source type check not only determines whether a special DASH playlist loader
Expand Down Expand Up @@ -287,7 +287,7 @@ export class PlaylistController extends videojs.EventTarget {

this.setupSegmentLoaderListeners_();

if (this.experimentalBufferBasedABR) {
if (this.bufferBasedABR) {
this.mainPlaylistLoader_.one('loadedplaylist', () => this.startABRTimer_());
this.tech_.on('pause', () => this.stopABRTimer_());
this.tech_.on('play', () => this.startABRTimer_());
Expand Down Expand Up @@ -750,7 +750,7 @@ export class PlaylistController extends videojs.EventTarget {
bufferLowWaterLine,
bufferHighWaterLine,
duration: this.duration(),
experimentalBufferBasedABR: this.experimentalBufferBasedABR,
bufferBasedABR: this.bufferBasedABR,
log: this.logger_
});
}
Expand All @@ -761,7 +761,7 @@ export class PlaylistController extends videojs.EventTarget {
* @private
*/
setupSegmentLoaderListeners_() {
if (!this.experimentalBufferBasedABR) {
if (!this.bufferBasedABR) {
this.mainSegmentLoader_.on('bandwidthupdate', () => {
const nextPlaylist = this.selectPlaylist();

Expand Down Expand Up @@ -809,7 +809,7 @@ export class PlaylistController extends videojs.EventTarget {

this.mainSegmentLoader_.on('earlyabort', (event) => {
// never try to early abort with the new ABR algorithm
if (this.experimentalBufferBasedABR) {
if (this.bufferBasedABR) {
return;
}

Expand Down Expand Up @@ -1971,7 +1971,7 @@ export class PlaylistController extends videojs.EventTarget {
const max = Math.max(initial, Config.MAX_BUFFER_LOW_WATER_LINE);
const newMax = Math.max(initial, Config.EXPERIMENTAL_MAX_BUFFER_LOW_WATER_LINE);

return Math.min(initial + currentTime * rate, this.experimentalBufferBasedABR ? newMax : max);
return Math.min(initial + currentTime * rate, this.bufferBasedABR ? newMax : max);
}

bufferHighWaterLine() {
Expand Down
11 changes: 3 additions & 8 deletions src/playlist-loader.js
Expand Up @@ -395,12 +395,7 @@ export default class PlaylistLoader extends EventTarget {

this.customTagParsers = (vhsOptions && vhsOptions.customTagParsers) || [];
this.customTagMappers = (vhsOptions && vhsOptions.customTagMappers) || [];
this.experimentalLLHLS = (vhsOptions && vhsOptions.experimentalLLHLS) || false;

// force experimentalLLHLS for IE 11
if (videojs.browser.IE_VERSION) {
this.experimentalLLHLS = false;
}
this.llhls = vhsOptions && vhsOptions.llhls;

// initialize the loader state
this.state = 'HAVE_NOTHING';
Expand All @@ -419,7 +414,7 @@ export default class PlaylistLoader extends EventTarget {

let uri = resolveUrl(this.main.uri, media.uri);

if (this.experimentalLLHLS) {
if (this.llhls) {
uri = addLLHLSQueryDirectives(uri, media);
}
this.state = 'HAVE_CURRENT_METADATA';
Expand Down Expand Up @@ -477,7 +472,7 @@ export default class PlaylistLoader extends EventTarget {
manifestString,
customTagParsers: this.customTagParsers,
customTagMappers: this.customTagMappers,
experimentalLLHLS: this.experimentalLLHLS
llhls: this.llhls
});
}

Expand Down
2 changes: 1 addition & 1 deletion src/playlist-selectors.js
Expand Up @@ -289,7 +289,7 @@ export let simpleSelector = function(
// If this selector proves to be better than others,
// resolutionPlusOneRep and resolutionBestRep and all
// the code involving them should be removed.
if (playlistController.experimentalLeastPixelDiffSelector) {
if (playlistController.leastPixelDiffSelector) {
// find the variant that is closest to the player's pixel size
const leastPixelDiffList = haveResolution.map((rep) => {
rep.pixelDiff = Math.abs(rep.width - playerWidth) + Math.abs(rep.height - playerHeight);
Expand Down
6 changes: 3 additions & 3 deletions src/playlist.js
Expand Up @@ -420,7 +420,7 @@ export const getMediaInfoForTime = function({
startingSegmentIndex,
startingPartIndex,
startTime,
experimentalExactManifestTimings
exactManifestTimings
}) {

let time = currentTime - startTime;
Expand Down Expand Up @@ -453,7 +453,7 @@ export const getMediaInfoForTime = function({

time += partAndSegment.duration;

if (experimentalExactManifestTimings) {
if (exactManifestTimings) {
if (time < 0) {
continue;
}
Expand Down Expand Up @@ -507,7 +507,7 @@ export const getMediaInfoForTime = function({

time -= partAndSegment.duration;

if (experimentalExactManifestTimings) {
if (exactManifestTimings) {
if (time > 0) {
continue;
}
Expand Down
4 changes: 2 additions & 2 deletions src/segment-loader.js
Expand Up @@ -561,7 +561,7 @@ export default class SegmentLoader extends videojs.EventTarget {
this.parse708captions_ = settings.parse708captions;
this.useDtsForTimestampOffset_ = settings.useDtsForTimestampOffset;
this.captionServices_ = settings.captionServices;
this.experimentalExactManifestTimings = settings.experimentalExactManifestTimings;
this.exactManifestTimings = settings.exactManifestTimings;

// private instance variables
this.checkBufferTimeout_ = null;
Expand Down Expand Up @@ -1445,7 +1445,7 @@ export default class SegmentLoader extends videojs.EventTarget {
} else {
// Find the segment containing the end of the buffer or current time.
const {segmentIndex, startTime, partIndex} = Playlist.getMediaInfoForTime({
experimentalExactManifestTimings: this.experimentalExactManifestTimings,
exactManifestTimings: this.exactManifestTimings,
playlist: this.playlist_,
currentTime: this.fetchAtBuffer_ ? bufferedEnd : this.currentTime_(),
startingPartIndex: this.syncPoint_.partIndex,
Expand Down
12 changes: 7 additions & 5 deletions src/videojs-http-streaming.js
Expand Up @@ -597,6 +597,8 @@ class VhsHandler extends Component {
this.options_.customTagParsers = this.options_.customTagParsers || [];
this.options_.customTagMappers = this.options_.customTagMappers || [];
this.options_.cacheEncryptionKeys = this.options_.cacheEncryptionKeys || false;
this.options_.llhls = this.options_.llhls === false ? false : true;
this.options_.bufferBasedABR = this.options_.bufferBasedABR || false;

if (typeof this.options_.playlistExclusionDuration !== 'number') {
this.options_.playlistExclusionDuration = 5 * 60;
Expand Down Expand Up @@ -639,13 +641,13 @@ class VhsHandler extends Component {
'cacheEncryptionKeys',
'playlistSelector',
'initialPlaylistSelector',
'experimentalBufferBasedABR',
'bufferBasedABR',
'liveRangeSafeTimeDelta',
'experimentalLLHLS',
'llhls',
'useNetworkInformationApi',
'useDtsForTimestampOffset',
'experimentalExactManifestTimings',
'experimentalLeastPixelDiffSelector'
'exactManifestTimings',
'leastPixelDiffSelector'
].forEach((option) => {
if (typeof this.source_[option] !== 'undefined') {
this.options_[option] = this.source_[option];
Expand Down Expand Up @@ -706,7 +708,7 @@ class VhsHandler extends Component {
player.error(error);
});

const defaultSelector = this.options_.experimentalBufferBasedABR ?
const defaultSelector = this.options_.bufferBasedABR ?
Vhs.movingAverageBandwidthSelector(0.55) : Vhs.STANDARD_PLAYLIST_SELECTOR;

// `this` in selectPlaylist should be the VhsHandler for backwards
Expand Down
24 changes: 12 additions & 12 deletions test/playlist-controller.test.js
Expand Up @@ -5702,12 +5702,12 @@ QUnit.test('filter subtitle works', function(assert) {
assert.deepEqual(this.calls, this.expected, 'calls as expected');
});

QUnit.module('PlaylistController experimentalBufferBasedABR', {
QUnit.module('PlaylistController bufferBasedABR', {
beforeEach(assert) {
this.playerOptions = {
html5: {
vhs: {
experimentalBufferBasedABR: true
bufferBasedABR: true
}
}
};
Expand Down Expand Up @@ -5851,11 +5851,11 @@ QUnit.test('maxPlaylistRetries is set when zero is passed as the option\'s value
player.dispose();
});

QUnit.test('true duration < 16 with experimentalBufferBasedABR', function(assert) {
QUnit.test('true duration < 16 with bufferBasedABR', function(assert) {
const pc = this.playlistController;
const nextPlaylist = {id: 'foo', endList: true};

pc.experimentalBufferBasedABR = true;
pc.bufferBasedABR = true;

pc.duration = () => 15;
pc.mainPlaylistLoader_.media = () => ({endList: true, id: 'bar'});
Expand All @@ -5873,14 +5873,14 @@ QUnit.test('true if bandwidth decreases', function(assert) {
assert.ok(pc.shouldSwitchToMedia_(nextPlaylist), 'should switch');
});

QUnit.test('true if bandwidth decreases, experimentalBufferBasedABR, and forwardBuffer < bufferHighWaterLine', function(assert) {
QUnit.test('true if bandwidth decreases, bufferBasedABR, and forwardBuffer < bufferHighWaterLine', function(assert) {
const pc = this.playlistController;
const nextPlaylist = {id: 'foo', endList: true, attributes: {BANDWIDTH: 1}};

// 0 forward buffer
pc.tech_.buffered = () => videojs.createTimeRange();
pc.tech_.currentTime = () => 0;
pc.experimentalBufferBasedABR = true;
pc.bufferBasedABR = true;
pc.duration = () => 40;
pc.mainPlaylistLoader_.media = () => ({endList: true, id: 'bar', attributes: {BANDWIDTH: 2}});

Expand All @@ -5900,42 +5900,42 @@ QUnit.test('true if forwardBuffer >= bufferLowWaterLine', function(assert) {
assert.ok(pc.shouldSwitchToMedia_(nextPlaylist), 'should switch');
});

QUnit.test('true if forwardBuffer >= bufferLowWaterLine, experimentalBufferBasedABR, and bandwidth increase', function(assert) {
QUnit.test('true if forwardBuffer >= bufferLowWaterLine, bufferBasedABR, and bandwidth increase', function(assert) {
const pc = this.playlistController;
const nextPlaylist = {id: 'foo', endList: true, attributes: {BANDWIDTH: 3}};

// zero forward buffer and zero buffer low water line
pc.tech_.buffered = () => videojs.createTimeRange();
pc.tech_.currentTime = () => 0;
pc.experimentalBufferBasedABR = true;
pc.bufferBasedABR = true;
pc.duration = () => 40;
pc.mainPlaylistLoader_.media = () => ({endList: true, id: 'bar', attributes: {BANDWIDTH: 2}});

assert.ok(pc.shouldSwitchToMedia_(nextPlaylist), 'should switch');
});

QUnit.test('false if nextPlaylist bandwidth lower, experimentalBufferBasedABR, and forwardBuffer > bufferHighWaterLine', function(assert) {
QUnit.test('false if nextPlaylist bandwidth lower, bufferBasedABR, and forwardBuffer > bufferHighWaterLine', function(assert) {
const pc = this.playlistController;
const nextPlaylist = {id: 'foo', endList: true, attributes: {BANDWIDTH: 1}};

// 31s forwardBuffer
pc.tech_.buffered = () => videojs.createTimeRange(0, 31);
pc.tech_.currentTime = () => 0;
pc.experimentalBufferBasedABR = true;
pc.bufferBasedABR = true;
pc.duration = () => 40;
pc.mainPlaylistLoader_.media = () => ({endList: true, id: 'bar', attributes: {BANDWIDTH: 2}});

assert.notOk(pc.shouldSwitchToMedia_(nextPlaylist), 'should not switch');
});

QUnit.test('false if nextPlaylist bandwidth same, experimentalBufferBasedABR, and forwardBuffer >= bufferLowWaterLine', function(assert) {
QUnit.test('false if nextPlaylist bandwidth same, bufferBasedABR, and forwardBuffer >= bufferLowWaterLine', function(assert) {
const pc = this.playlistController;
const nextPlaylist = {id: 'foo', endList: true, attributes: {BANDWIDTH: 2}};

// 31s forwardBuffer
pc.tech_.buffered = () => videojs.createTimeRange();
pc.tech_.currentTime = () => 0;
pc.experimentalBufferBasedABR = true;
pc.bufferBasedABR = true;
pc.duration = () => 40;
pc.mainPlaylistLoader_.media = () => ({endList: true, id: 'bar', attributes: {BANDWIDTH: 2}});

Expand Down

0 comments on commit 02c3c77

Please sign in to comment.