From 41746933ad4bd3e477435510d97bc7eb4b487049 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 a247aa3edf..01c82bf32b 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'); @@ -142,15 +141,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), @@ -158,7 +163,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); });