ABR-алгоритмы на основе пропускной способности

Автор: Николай СапуновОбновлено: август 202624 мин чтения
Содержание статьи +

TL;DR

ABR-алгоритмы на основе пропускной способности (throughput-based) выбирают следующий чанк видео так: оценивают, насколько быстро сеть доставляла байты последнее время, и берут максимальное качество, чей битрейт укладывается в эту оценку, поделённую на небольшой запасной коэффициент. Математика – одно деление и одно сравнение, поэтому это семейство доминировало первое десятилетие HTTP-стриминга и до сих пор остаётся дефолтом в нативном плеере iOS и в hls.js. К 2026 году командам это семейство интересно не из ностальгии: на стабильных сетях оно реально лучше, его проще всего отлаживать, и только его поведение можно предсказать на бумаге. В статье разобрано, как работает оценщик, три продакшен-варианта (rate-based, ELASTIC, PANDA), пять мест, где он ломается в реальном мире, и тюнинговые рычаги, которые превращают шумную реализацию в спокойную.

Кому и зачем это нужно

Throughput-based ABR – это алгоритм, который вы получаете «по умолчанию», если не выбрали другой явно. Нативный HLS-плеер Apple, open-source-библиотека hls.js (на ней работает большинство браузерных HLS-плееров вне Safari), ранние версии dash.js и почти каждый smart-TV-плеер, построенный до 2020 года, используют оценку пропускной способности как основной сигнал. Если вы выпускаете стриминговый продукт, скорее всего, у вас уже сейчас работает throughput-based – независимо от того, говорит ли это кто-то в команде вслух. Понимание того, как он ведёт себя – когда выбирает правильную ступень, а когда плохую, – отделяет чистый и быстрый просмотр от опыта с осцилляциями и буферизациями. Продакт, финансы и инженеры – все живут вниз по течению от этого одного решения.

Идея одним абзацем

Представьте: вы только что скачали кусок видео, и он пришёл по сети за 1.6 секунды. Если в чанке было 4 мегабита данных, сеть доставила примерно 4 ÷ 1.6 = 2.5 мегабита в секунду на протяжении этой загрузки. Плеер запоминает это число. Выбирая следующий чанк, он делает тот же расчёт для предыдущего, и того, что был до него, и объединяет их в одну оценку «насколько быстра сеть сейчас?». Затем он просит у меню – у лесенки битрейтов (bitrate ladder), списка заранее закодированных версий разного качества – максимальный битрейт, который комфортно ниже оценки. Это та ступень, которую он скачает следующей. Весь алгоритм укладывается в двадцать строк кода. Сложное – это что значит «комфортно ниже» и как объединить замеры, не паникуя при каждом провале.

Что делает алгоритм, шаг за шагом

Пройдём одну итерацию. Плеер только что докачал сегмент N. У него в памяти три факта: размер сегмента N в байтах, время скачивания в секундах и окно похожих замеров для сегментов N-1, N-2, N-3 и так далее.

Шаг 1 – посчитать пропускную способность за сегмент. Скачанные байты делим на прошедшие секунды и после перевода единиц получаем биты в секунду. По соглашению время измеряется от первого полученного байта до последнего, а не от момента отправки HTTP-запроса, потому что время отправки запроса зависит от RTT, а не от пропускной способности. Некоторые реализации включают задержку в знаменатель, что делает оценку пессимистичнее; другие – исключают.

Шаг 2 – сгладить окно. Пропускная способность одного сегмента шумна. Wi-Fi может пропасть на десятки миллисекунд, когда сосед включил микроволновку; CDN-edge может ненадолго отдать вам несвежее соединение; разгон TCP slow-start раздувает пропускную способность стартового сегмента сверх стационарного значения. Чтобы отфильтровать шум, плеер объединяет последние K замеров в одно число. Самый распространённый выбор – гармоническое среднее (harmonic mean), потому что оно сильнее взвешивает медленные замеры, чем быстрые – противоположно арифметическому среднему, – и эта консервативность совпадает с асимметричной ценой ошибки (недооценить – потратить часть полосы; переоценить – буферизироваться).

Гармоническое среднее трёх замеров: 3 ÷ (1/x₁ + 1/x₂ + 1/x₃). Для замеров 2.8, 3.1 и 2.5 Mbps получаем 3 ÷ (1/2.8 + 1/3.1 + 1/2.5) ≈ 2.78 Mbps. Сравните с арифметическим средним – (2.8 + 3.1 + 2.5) ÷ 3 = 2.80 Mbps – и видно, что гармоническое слегка тянет к самому медленному замеру. Разрыв шире, когда замеры расходятся сильнее.

Шаг 3 – применить запасной коэффициент. Деление сглаженной оценки на константу больше единицы – обычно 1.2, 1.25 или 1.5 – даёт потолок: максимальный битрейт, который плеер позволит себе выбрать. Запасной коэффициент существует потому, что оценка – это среднее по недавнему прошлому, и будущее не будет идентично. Коэффициент 1.25 говорит: «беру ступень, использующую 80% того, что сеть дала мне в прошлый раз».

Продолжая пример, 2.78 Mbps ÷ 1.25 = 2.22 Mbps. Это потолок.

Шаг 4 – выбрать максимальную ступень при битрейте ≤ потолок. Просканировать лесенку сверху вниз. Если лесенка 400, 750, 1500, 2500, 4000, 6000 kbps, максимальная ступень при битрейте ≤ 2,220 kbps – это 1,500 kbps. Её плеер и скачает следующей.

Шаг 5 – опционально: не дропать на одном плохом замере. Некоторые реализации требуют двух подряд низких оценок, прежде чем переключиться вниз, чтобы один сегмент-икота не вызвал заметный провал качества.

Это весь алгоритм. Повторяется каждый сегмент, для каждого зрителя, без других входов.

Рисунок 1. Одно решение на сегмент. Четыре вычисления, одно сравнение, одна выбранная ступень.

Три продакшен-варианта

Разные реализации различаются тем, какое окно замеров используют, какую функцию сглаживания применяют и как обрабатывают старт. Три варианта составляют большую часть продакшен-трафика в 2026 году.

Вариант 1 – простой rate-based (дефолт hls.js)

Самый простой вариант. Плеер держит K последних сегментов – обычно 4 или 5 – в скользящем окне, считает гармоническое среднее, делит на запасной коэффициент 1.0 (да, по умолчанию 1.0 в hls.js) и выбирает ступень. Команда hls.js выбрала более низкий запасной коэффициент на том основании, что гармоническое среднее уже само даёт консервативный сдвиг; дополнительный консерватизм сверху недозагружает сеть. В исходниках hls.js файл src/utils/ewma-bandwidth-estimator.ts показывает фактическую реализацию, которая комбинирует пропускную способность за сегмент с экспоненциально взвешенным скользящим средним – сокращённо EWMA – чтобы внутри окна давать больший вес недавним замерам.

EWMA работает так: каждый новый замер обновляет оценку по формуле new = α × sample + (1 − α) × old, где α между 0 и 1 управляет скоростью забывания старых замеров. При α = 0.5 оценка вдвое уменьшает вес каждого предыдущего замера на каждом шаге. При α = 0.1 оценка почти не двигается на любом одном замере. hls.js запускает два EWMA-оценщика параллельно – быстрый и медленный – и использует меньший из них, когда решает дропать, и больший – когда решает поднимать ступень.

Вариант 2 – ELASTIC

Опубликован в 2014 году De Cicco, Caldaralo, Palmisano и Mascolo как ELASTIC: A Client-Side Controller for Dynamic Adaptive Streaming over HTTP. Вариант надстраивает на оценку скорости PID-контроллер – петлю обратной связи из теории управления. Вместо выбора ступени за один шаг «поделить и сравнить» ELASTIC задаёт целевой уровень буфера и подаёт ошибку между текущим и целевым буфером в контроллер, который плавно подстраивает эффективный потолок. Преимущество перед обычным rate-based – стабильность: когда сеть колеблется около значения между двумя ступенями, ELASTIC остаётся на нижней, а не скачет на каждом сегменте. Недостаток – новая поверхность настройки: коэффициенты усиления PID нужно подбирать аккуратно, а плохие значения дают медленную, вялую реакцию на реальные провалы сети.

ELASTIC встречается в исследовательском коде и нескольких коммерческих продуктах; концептуально он предок каждого «сглаженного оценщика пропускной способности с гистерезисом», который вы видите в современных плеерах.

Вариант 3 – PANDA

Опубликован в 2014 году Li, Begen, Erfanian и Houdaille как PANDA: Probe and Adapt for HTTP Video Streaming. PANDA создавался, чтобы устранить конкретную ошибку обычного rate-based ABR: когда несколько плееров делят узкое место канала, обычные rate-based-оценщики сходятся в состоянии, где каждый плеер думает, что у него полосы больше, чем есть на самом деле, потому что замер загрязняется OFF-временем между сегментами. PANDA вводит probing rate (зондирующую скорость), которая обновляется законом управления независимо от сырой пропускной способности сегмента, и использует probing rate (а не пропускную способность) как основу выбора ступени. Результат – более вежливое поведение, когда десять зрителей делят аплинк маршрутизатора.

PANDA – алгоритм-эталон в академических сравнениях и встроен в несколько коммерческих ABR-помощников на стороне CDN. Он не встречается как дефолт ни в одном из основных open-source-плееров, но урок, который он даёт – «измеренная пропускная способность между сегментами» ≠ «доступная пропускная способность при скачивании сегмента» – теперь заложен в каждом серьёзном современном оценщике.

Числовой пример

Условия: 30-минутное видео, шестиступенчатая лесенка (400, 750, 1500, 2500, 4000, 6000 kbps), сегменты по 4 секунды, зритель на стабильной DSL-линии 3.2 Mbps.

После первых трёх сегментов плеер замерил пропускную способность 3.4, 2.9 и 3.0 Mbps (TCP slow-start раздул сегмент 1; сегменты 2 и 3 ближе к стационарному состоянию). Гармоническое среднее: 3 ÷ (1/3.4 + 1/2.9 + 1/3.0) ≈ 3.09 Mbps. С запасным коэффициентом 1.25 потолок – 2.47 Mbps. Максимальная ступень при битрейте ≤ 2,470 kbps – это 1,500 kbps. Заметьте: это заметно ниже фактической скорости линии, и это запасной коэффициент делает свою работу.

Плеер скачивает сегмент 4 на 1,500 kbps. Четырёхсекундный сегмент – это 750 KB, который приходит за 1.9 секунды на линии 3.2 Mbps. Буфер растёт на (4 − 1.9) = 2.1 секунды. Замеренная пропускная способность для этого сегмента – около 3.16 Mbps, близко к прошлому окну. Гармоническое среднее меняется чуть-чуть; потолок – чуть-чуть; алгоритм снова выбирает 1,500 kbps.

Это счастливое место алгоритма. На стабильной сети ступень не меняется. Пользователь видит ровное качество, пока сеть не качается.

Теперь введём провал на 10 секунд до 1.4 Mbps, начиная с 20-й секунды. Сегмент 6 (скачанный во время провала) даёт 1.4 Mbps. Новое гармоническое среднее: 3 ÷ (1/3.0 + 1/3.16 + 1/1.4) ≈ 2.06 Mbps. Потолок = 1.65 Mbps. Максимальная ступень при битрейте ≤ – всё ещё 1,500 kbps. Алгоритм остаётся на месте: защита гармонического среднего означает, что один плохой замер не переключит ступень.

Но если провал длится достаточно долго, чтобы два подряд сегмента пришли на 1.4 Mbps, гармоническое среднее падает до 3 ÷ (1/3.0 + 1/1.4 + 1/1.4) ≈ 1.71 Mbps. Потолок = 1.37 Mbps. Максимальная ступень при битрейте ≤ 1,370 kbps – это 750 kbps. Плеер дропает на ступень.

Когда сеть восстановится, происходит обратное. После трёх подряд сегментов на 3.2 Mbps гармоническое среднее снова на уровне 2.5+ Mbps, и плеер поднимается обратно к 1,500 kbps.

Эта симметрия – быстро дропать при устойчивых плохих замерах, медленно поднимать при устойчивых хороших – и есть поведенческая подпись throughput-based ABR. Buffer-based и гибридные алгоритмы делают другие компромиссы; throughput-based – самое чистое выражение принципа «совпадай с сетью, как измерил».

Когда throughput-based ABR – правильный выбор

Три формы развёртывания совпадают с сильными сторонами throughput-based ABR и прощают его слабости.

Форма 1 – стабильные, предсказуемые сети. Офисный Wi-Fi, домашнее оптоволокно, спутник и большинство проводных подключений выдают пропускную способность, которая меняется в шкале секунд-минут, а не миллисекунд. Подход throughput-based ABR «окно и среднее» точно ложится сюда. Он подбирает правильную ступень за два-три сегмента и удерживает её.

Форма 2 – короткий контент. 30-секундное продуктовое видео не выигрывает от терпения buffer-based-алгоритма. К тому моменту, когда buffer-based доберётся до верхней ступени, видео закончится. Более быстрый разгон throughput-based ABR выигрывает.

Форма 3 – плееры с ограниченными ресурсами. Встраиваемые set-top box, старые smart-TV и просмотрщики IoT-камер ходят с ограниченной памятью и CPU. Throughput-based-алгоритму нужно помнить последние 4–5 замеров и сделать одно деление на сегмент. Buffer-based отслеживает непрерывную модель буфера и функцию полезности; гибридный гоняет обе. На CPU 100 MHz и 2 MB RAM throughput-based ABR – единственный, который влезает.

Bitmovin Video Developer Report 2024 всё ещё фиксирует, что примерно 40% опрошенных стриминговых инженеров шипят throughput-based как основной алгоритм, а buffer-based и hybrid делят остальное. Доля год от года снижается, поскольку Shaka Player и dash.js забирают рынок, но не обрушилась – и не зря.

Рисунок 2. Три формы развёртывания, где throughput-based ABR реально обгоняет более сложные алгоритмы.

Где throughput-based ABR ломается

Пять режимов отказа объясняют почти каждую продакшен-жалобу на throughput-based-плеер. У каждого есть фикс.

Отказ 1 – нестабильный Wi-Fi. Пропускная способность домашнего Wi-Fi меняется в 5–10 раз за секунду, когда другие устройства активны. Чистый rate-based-оценщик следует за качками и колеблет ступень. Пользователь видит, как картинка скачет между 720p и 1080p каждые 8 секунд. Фикс: поднять EWMA α к 0.1 (более медленное забывание) или перейти на buffer-based-алгоритм для заведомо нестабильных сред.

Отказ 2 – хендоверы мобильной сети. Когда телефон переключается с Wi-Fi на LTE или с LTE на 5G, пропускная способность прыгает в 5 раз или падает в 5 раз за один сегмент. Гармоническое среднее не видит этого два-три сегмента. К моменту, когда алгоритм дропнет, буфер уже опустошён. Фикс: спарить оценщик с жёстким нижним порогом (немедленно дропать на нижнюю ступень, если буфер < 2 секунд) и медленным подъёмом обратно после восстановления.

Отказ 3 – CMAF chunked transfer encoding. Когда энкодер использует Common Media Application Format, сокращённо CMAF (ISO/IEC 23000-19), с chunked transfer, частичные сегменты приходят в плеер по мере того, как энкодер их производит – ровно на битрейте ступени, а не на доступной пропускной способности сети. Наивный оценщик делит байты на секунды и заключает, что сеть точно такая же быстрая, как текущая ступень. Результат: плеер никогда не поднимается, даже на линии 1 Gbps. Фикс: измерять пропускную способность только в межчанковом простое (когда сеть была свободна) или читать метрику простоя HTTP-загрузчика. Это один из более сложных багов в инженерии плееров с низкой задержкой, и встречается в продакшене он часто. Статьи LL-HLS: подробный разбор и LL-DASH и CMAF chunked на практике разбирают арифметику chunked transfer подробнее.

Отказ 4 – несколько плееров на одном узком месте. Это и есть PANDA-отказ. Пять зрителей на одном домашнем маршрутизаторе видят примерно по 1/5 аплинка, но замер каждого плеера предполагает, что он один владеет каналом. Кумулятивная картина запросов жёсткая для очереди маршрутизатора, и все пять плееров колеблются вместе. Фикс: использовать PANDA-стиль зондирования или координировать на уровне приложения (редко на практике).

Отказ 5 – первый сегмент. С нулевой историей плеер вынужден откуда-то стартовать. Выбрать нижнюю ступень – выглядеть плохо первые 5 секунд (период, в который зритель решает, продолжать ли смотреть). Выбрать слишком высокую – сразу буферизироваться. Фикс: сохранять пропускную способность прошлой сессии между запусками и использовать консервативную среднюю ступень, если истории нет. Большинство современных плееров делают и то, и другое.

Тюнинговые рычаги – реально важные ручки

Если вы выпускаете throughput-based-плеер и нужно его настроить, четыре ручки делают почти всю работу.

Ручка 1 – размер окна K. Сколько недавних замеров учитывает оценщик. K = 3 реагирует быстро и колеблется; K = 8 сглажен, но медленный. По умолчанию – K = 5 для VoD, K = 3 для лайва.

Ручка 2 – запасной коэффициент. Делитель потолка. 1.0 (без запаса) – дефолт hls.js, работает потому, что гармоническое среднее уже консервативно. 1.25 – самое распространённое «реальное» значение. 1.5 – для сетей, которым вообще не доверяете (международные мобильные, низкоуровневый спутник). Выше 2.0 – тратит слишком много полосы.

Ручка 3 – EWMA α. Имеет значение, только если вы используете экспоненциальное сглаживание вместо плоского окна. Меньшее α (например, 0.1) – медленнее забывание и устойчивые оценки; большее α (например, 0.5) – быстрее забывание и реактивные оценки. Распространённый продакшен-паттерн: два EWMA параллельно, α = 0.1 (медленный) и α = 0.5 (быстрый), и использовать медленный для решений о подъёме, быстрый – для решений о дропе. Так делает hls.js.

Ручка 4 – гистерезис дропа. Требовать ли один плохой замер, два подряд или пересечение скользящего среднего, прежде чем переключиться вниз. Один замер – реактивно; два – самое распространённое; три – консервативно. Пара́ это с правилом нижнего порога буфера из «Отказ 2», чтобы устойчивый провал не вызвал буферизацию.

Таблица ниже показывает разумные значения по умолчанию для трёх типичных развёртываний.

РазвёртываниеK (окно)Запасной коэф.EWMA α (медл. / быстр.)Гистерезис дропа
VoD на домашнем оптоволокне51.250.10 / 0.502 сегмента
Лайв на смешанных сетях31.40.15 / 0.501 сегмент
Mobile-first OTT-приложение41.50.10 / 0.401 сегмент (+ нижний порог)

Это отправные точки, а не финальные ответы. Каждому продукту нужна хотя бы одна итерация настройки после замера фактического распределения сетей зрителей.

Сравнение с альтернативами

Throughput-based – одно из четырёх семейств ABR. Компромиссы в одной таблице.

СемействоГлавный сигналСильные стороныСлабые стороныГде шипится
Throughput-basedНедавняя скорость скачиванияПростой, предсказуемый, быстрый стартДёргается на нестабильных сетях, слеп к состоянию буфераhls.js, нативный iOS, старый dash.js, большинство smart-TV
Buffer-basedГлубина буфера в секундахСглажен, устойчив к джиттеруМедленный подъём, математически плотныйdash.js (BOLA) с 2018, опция в Shaka Player
HybridОба, плюс функция полезностиЛучший QoE в продакшенеСложно отлаживать, большое пространство параметровNetflix, YouTube, большинство премиум-стримеров
NeuralВыученная политика из данныхЛучший в бенчмаркахТяжёлая тренировка, переобучения, непрозрачноИсследования, два-три топ-стримера

Главная статья-пиллар ABR-стриминг: подробное объяснение разбирает четыре семейства в контексте. Throughput-based – не «худший», а «правильный в трёх конкретных формах и неправильный за их пределами».

Типичные ошибки при шипинге throughput-based-плеера

«Ошибка 1 – доверие сегменту 1. TCP slow-start раздувает замеренную пропускную способность первого сегмента в 2–3 раза. Отбросьте или ослабьте вес сегмента 1 перед подачей в оценщик.»
«Ошибка 2 – не мерить переключения. Throughput-based-плеер может колебаться между ступенями без буферизаций, и QoE-урон прячется от метрики rebuffer ratio. Трекайте переключения в минуту явно. Выше 1 переключения в 60 секунд – алгоритм слишком реактивен.»
«Ошибка 3 – игнорировать буфер совсем. Чистый throughput-based будет лезть в буферизацию, если сеть падает быстрее окна сглаживания. Правило «нижний порог буфера» (жёстко дропать, если буфер < 2 с) – небольшое изменение с большим QoE-выигрышем.»
«Ошибка 4 – жёстко вшитые потолки. Ограничить алгоритм «не выше 4 Mbps для мобильных» звучит разумно, пока вы не тестируете на 5G-телефоне с линией 500 Mbps. Используйте сигнал типа сети плеера, чтобы переключать потолки, а не задавать один.»
«Ошибка 5 – не разделять логику подъёма и дропа. Подъём и дроп имеют асимметричные цены – слишком быстрый подъём буферизирует; слишком медленный дроп тоже буферизирует; слишком медленный подъём тратит полосу; слишком быстрый дроп выглядит плохо. Используйте два оценщика (быстрый для дропа, медленный для подъёма) вместо одного.»

Где здесь Фора Софт

Мы выпустили 239+ видео-продуктов с 2005 года и видели throughput-based ABR почти в каждой кодовой базе, которую унаследовали – обычно потому, что продукт стартовал с hls.js или нативного iOS-плеера и никогда не пересматривал это решение. Большинство выигрышей приходит не от замены алгоритма, а от настройки его четырёх ручек под фактическое распределение сетей зрителей. В e-learning мы оставляем throughput-based, потому что сеть стабильная, а цена буферизации высокая; в mobile-first OTT добавляем правило нижнего порога буфера и память пропускной способности сессии; в телемедицине переходим на гибрид, потому что клиническим экранам нельзя терпеть провал качества во время диагностического вопроса. Правильный алгоритм зависит от аудитории, а не от того, что сейчас модно в докладах на конференциях.

Ключевые выводы

  • Throughput-based ABR выбирает следующую ступень, сглаживая недавние скорости скачивания и деля на запасной коэффициент.
  • Это дефолт в hls.js, нативном iOS-плеере и большинстве smart-TV-плееров – вы, вероятно, шипите его не зная.
  • Четыре ручки делают почти всю настройку: размер окна, запасной коэффициент, EWMA α, гистерезис дропа.
  • Выигрывает на стабильных сетях, коротком контенте, плеерах с малыми ресурсами; проигрывает на нестабильном Wi-Fi и CMAF chunked transfer.
  • Самый частый продакшен-баг – CMAF-чанк приходит ровно на битрейте ступени и не даёт плееру подняться.
  • Пара́ throughput-based с жёстким правилом нижнего порога буфера, чтобы алгоритм не лез в буферизацию.

Что читать дальше

CTA

  • Поговорить со стриминговым инженером – забронируйте 30-минутный созвон с нашей стриминг-командой.
  • Посмотреть наши кейсы – как мы строили ABR для OTT, e-learning, телемедицины и видеонаблюдения.
  • Скачать: настроечный лист throughput-based ABR – одностраничный справочник по четырём ручкам, их дефолтам и режимам отказа, которые каждая закрывает. Скачать настроечный лист.

Строите такую систему?

Подберём параметры кодирования под ваш контент и посчитаем стоимость доставки до старта разработки.