From a6d8610241dc7c8abd56cf7f0d48993d6139dcae Mon Sep 17 00:00:00 2001 From: wjywbs Date: Thu, 17 Feb 2022 16:59:08 -0500 Subject: [PATCH] fix: Select first of identical audio streams (#3869) If a manifest lists 2 audio streams, select the first acceptable stream instead of the last one. For example below, previously the 2nd stream was selected, and now the first stream will be selected. Other players like roku, video.js and exoplayer select the first one. ``` #EXT-X-MEDIA:TYPE=AUDIO,URI="stream_1.m3u8",GROUP-ID="default-audio-group",NAME="128k",AUTOSELECT=YES,CHANNELS="2" #EXT-X-MEDIA:TYPE=AUDIO,URI="stream_2.m3u8",GROUP-ID="default-audio-group",NAME="64k",CHANNELS="2" (video streams snipped) ``` --- lib/abr/simple_abr_manager.js | 18 ++++++++++++------ test/abr/simple_abr_manager_unit.js | 4 ++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/abr/simple_abr_manager.js b/lib/abr/simple_abr_manager.js index 4e2d295b3d..e13abea12e 100644 --- a/lib/abr/simple_abr_manager.js +++ b/lib/abr/simple_abr_manager.js @@ -9,7 +9,6 @@ goog.provide('shaka.abr.SimpleAbrManager'); goog.require('goog.asserts'); goog.require('shaka.abr.EwmaBandwidthEstimator'); goog.require('shaka.log'); -goog.require('shaka.util.Iterables'); goog.require('shaka.util.StreamUtils'); @@ -145,15 +144,21 @@ shaka.abr.SimpleAbrManager = class { // Start by assuming that we will use the first Stream. let chosen = sortedVariants[0] || null; - const enumerate = (it) => shaka.util.Iterables.enumerate(it); - for (const {item, next} of enumerate(sortedVariants)) { + for (let i = 0; i < sortedVariants.length; i++) { + const item = sortedVariants[i]; const playbackRate = !isNaN(this.playbackRate_) ? Math.abs(this.playbackRate_) : 1; const itemBandwidth = playbackRate * item.bandwidth; const minBandwidth = itemBandwidth / this.config_.bandwidthDowngradeTarget; - const nextBandwidth = - playbackRate * (next || {bandwidth: Infinity}).bandwidth; + let next = {bandwidth: Infinity}; + for (let j = i + 1; j < sortedVariants.length; j++) { + if (item.bandwidth != sortedVariants[j].bandwidth) { + next = sortedVariants[j]; + break; + } + } + const nextBandwidth = playbackRate * next.bandwidth; const maxBandwidth = nextBandwidth / this.config_.bandwidthUpgradeTarget; shaka.log.v2('Bandwidth ranges:', (itemBandwidth / 1e6).toFixed(3), @@ -161,7 +166,8 @@ shaka.abr.SimpleAbrManager = class { (maxBandwidth / 1e6).toFixed(3)); if (currentBandwidth >= minBandwidth && - currentBandwidth <= maxBandwidth) { + currentBandwidth <= maxBandwidth && + chosen.bandwidth != item.bandwidth) { chosen = item; } } diff --git a/test/abr/simple_abr_manager_unit.js b/test/abr/simple_abr_manager_unit.js index ae95c68b04..28c3b07e7c 100644 --- a/test/abr/simple_abr_manager_unit.js +++ b/test/abr/simple_abr_manager_unit.js @@ -91,7 +91,7 @@ describe('SimpleAbrManager', () => { config.defaultBandwidthEstimate = 3e6; abrManager.configure(config); const chosen = abrManager.chooseVariant(); - expect(chosen.id).toBe(104); + expect(chosen.id).toBe(103); }); it('can handle empty variants', () => { @@ -208,7 +208,7 @@ describe('SimpleAbrManager', () => { abrManager.segmentDownloaded(1000, bytesPerSecond); // Expect variants 4 to be chosen - const expectedVariant = variants[4]; + const expectedVariant = variants[3]; expect(switchCallback).toHaveBeenCalledWith(expectedVariant); });