Содержание статьи +
- Кратко
- Почему это важно
- Что такое dash.js, а что нет
- Почему эта библиотека вообще существует
- Архитектура в одном абзаце
- Минимальный жизнеспособный плеер dash.js
- Расчёт задержки на конкретном примере
- dash.js против Shaka Player – семь осей, на которых решается выбор
- Где dash.js ведёт экосистему (CMCD, CMSD, content steering)
- Продакшен-паттерн обработки ошибок
- Что изменилось в v5 и что доставлять в 2026
- Частые ловушки
- Где здесь Фора Софт
- Ключевые выводы
- Что читать дальше
Кратко
Open-source библиотека dash.js – это эталонная реализация плеера MPEG-DASH от DASH Industry Forum, рассчитанная на браузеры с поддержкой Media Source Extensions и Encrypted Media Extensions. В 2026 году это единственный источник истины для того, как протокол DASH должен вести себя на клиентской стороне. Установленная база у dash.js меньше, чем у Shaka Player, и существенно меньше, чем у hls.js – около 331 000 еженедельных скачиваний в npm против 172 000 у Shaka и 4,1 миллиона у hls.js. Но dash.js занимает уникальную позицию в экосистеме: каждая новая функция DASH сначала приземляется здесь, каждый Internet-Draft по Common Media Client Data (CMCD), Common Media Server Data (CMSD) и content steering получает референсную реализацию в этой кодовой базе, и почти каждый другой DASH-плеер на рынке либо встраивает dash.js, либо повторяет его поведение, либо измеряется относительно него. Эта статья разбирает архитектуру – фасад MediaPlayer, FactoryMaker, который создаёт всё, ABR-ядро на правилах, четыре низколатентных движка (default и moof-parsing для оценки пропускной способности, default и LoL+ для catch-up), показывает семь строк, чтобы запустить плеер, конфигурацию, которая реально определяет adaptive bitrate и LL-DASH поведение, сравнивает dash.js с Shaka Player по семи осям, и рассказывает, что изменилось в v5.0 (февраль 2025) и v5.1 (ноябрь 2025).
Почему это важно
Если вы доставляете MPEG-DASH в браузер в 2026 году – для платного OTT-каталога, симулькаста телевещания, корпоративного вебкаста, ленты для live-ставок, smart-TV приложения, чей вендорский SDK включает dash.js, или для LL-DASH события с целевой задержкой 2–5 секунд glass-to-glass, – вопрос «dash.js или Shaka Player?» окажется на столе у кого-то в команде в первый же спринт проекта. Прочитав эту статью, продакт-менеджер сможет задать инженеру правильные вопросы о покрытии форматов, охвате DRM, низколатентной стратегии и долгосрочных рисках поддержки, а фронтенд-инженер получит полную ментальную модель библиотеки: фасад, конвейер правил, события, на которых строится продакшен-телеметрия, ручки конфигурации, меняющие поведение ABR без форка кода, и четыре класса ошибок, которые нужно обработать с первого дня. Предварительных знаний по стримингу не требуется; каждый термин определяется в момент появления. К концу вы будете знать, зачем существует dash.js, когда его выбирать вместо Shaka, когда Shaka вместо него, какая однострочная настройка включает низколатентный режим, и какие два алгоритма – L2A и LoL+ от DASH-IF – заменяют дефолтные ABR-правила, когда суб-двухсекундная задержка важнее пиковой битрейта.
Что такое dash.js, а что нет
Самое короткое точное определение такое. dash.js – это JavaScript-библиотека, которая читает манифест MPEG-DASH, скачивает указанные в нём видеочанки, передаёт их в браузерный API Media Source Extensions, согласует ключи дешифрации с Content Decryption Module браузера, когда поток защищён, и предоставляет поток событий, достаточный, чтобы поверх неё построить целый продакшен-UI плеера – всё это из open-source пакета под лицензией BSD-3-Clause, который вы ставите через npm install как любую другую зависимость (Dash-Industry-Forum/dash.js, README, доступ 2026-05-24). По умолчанию у неё нет UI, хотя поставляется опциональный sample-controlbar и совершенно новый эталонный UI, переписанный с использованием ИИ-инструментов в конце 2025 года. Это не транскодер: каждый байт, который отправляется в браузер, уже был закодирован вашим пакетировщиком. И это не мультиформатная библиотека: там, где Shaka Player играет DASH, HLS и Microsoft Smooth Streaming через один и тот же вызов player.initialize(), dash.js играет в первую очередь DASH и во вторую – Microsoft Smooth Streaming через выделенный MSS-обработчик. Пути для HLS в dash.js нет, и никогда не было в опубликованной roadmap проекта – структурно это причина, по которой продукт с DASH плюс HLS обращается к Shaka.
Список тем GitHub-репозитория проекта формулирует это одной строкой – «javascript · video · dash · drm · abr · eme · mss · cmaf · smooth-streaming · media-source-extensions · encrypted-media-extensions · adaptive-bitrate-streaming» (Dash-Industry-Forum/dash.js, темы репозитория, доступ 2026-05-24) – и эта строка отражает весь поверхностный функционал. dash.js был создан в DASH Industry Forum в 2012 году, открыт под лицензией BSD, более десяти лет ведётся внутри DASH-IF сообщества, и с 2023 года спонсируется SVTA (Streaming Video Technology Alliance). На май 2026 библиотека находится в линейке v5.1.1, опубликованной 23 декабря 2025; v5.0.0 вышел 17 февраля 2025 с многоформатной системой сборки, а v5.1.0 – 21 ноября 2025 с интеграцией LCEVC и переписанным sample UI (Dash-Industry-Forum/dash.js, страница релизов, доступ 2026-05-24). Еженедельные npm-скачивания держатся около 331 000 – намного меньше, чем 4,1 млн у hls.js, но чуть больше, чем 172 000 у Shaka Player, что делает dash.js вторым по скачиваниям самостоятельным веб-плеером DASH/HLS на npm в 2026 году.
Поддержку dash.js в свежей вкладке браузера можно проверить одной строкой: библиотека выставляет dashjs.supportsMediaSource(), и если она возвращает true, у браузера есть Media Source Extensions и dash.js может прицепиться к элементу <video>. Она возвращает true в Chrome, Edge, Firefox, Opera, современном Safari с ManagedMediaSource API, в Web Receiver Chromecast (когда receiver-приложение загружает dash.js), в smart-TV браузерах Tizen 2017+ и webOS 4.0+. Возвращает false в Safari на iPhone до iOS 17.1 (где ManagedMediaSource ещё не поставлялся) и на любом браузерном движке, у которого вообще нет MSE – что в 2026 году значит почти ничего на современном вебе.
Почему эта библиотека вообще существует
Три библиотеки доминируют на стороне браузерного стриминга: hls.js, Shaka Player и dash.js. Они появились примерно в одно время – dash.js в DASH Industry Forum в 2012, Shaka в Google в 2014–2015, hls.js в Dailymotion в 2015 – для решения смежных, но разных задач. dash.js построили как эталонную реализацию совершенно нового стандарта: ISO/IEC 23009-1, протокол MPEG-DASH, только что был опубликован в 2012 году, и органу стандартизации нужен был работающий JavaScript-клиент, чтобы доказать имплементируемость спецификации, выявить неоднозначности и дать DASH-экосистеме публичную точку старта. hls.js построили позже, чтобы залатать отсутствие HLS в не-Safari браузерах – HTTP Live Streaming от Apple работал нативно на iPhone и macOS, но был непригоден везде ещё. Shaka построили, чтобы стек стриминга Google – DASH, упакованный Shaka Packager, зашифрованный Widevine, отдаваемый через Google Cloud CDN, воспроизводимый в YouTube и Chromecast Web Receiver – стал пригоден для открытого веба.
Политический подтекст формирует roadmap dash.js. Первое обязательство проекта – следовать DASH-IF Implementation Guidelines, которые являются де-факто профилем реализации стандарта ISO/IEC 23009-1: когда DASH-IF задаёт новое поведение для content steering, CMCD v2, LL-DASH catch-up или приоритета DRM key-system, dash.js выпускает его раньше всех. Второе обязательство – тестовый корпус: dash.js – это плеер, против которого тестирует каждый коммерческий DASH-пакетировщик, что означает: баг в dash.js обычно проявляется как баг в экосистеме, а фикс в dash.js становится документацией, которую читают все остальные. Третье обязательство – исследования: dash.js – единственный массовый веб-плеер, чей движок правил ABR позволяет академическим группам внедрять новые алгоритмы – BOLA, L2A, LoL+ – в продакшен-кодовую базу, которой пользуются реальные команды.
В числе пользователей в продакшене – BBC (через Piers O'Hanlon в BBC R&D – стабильный контрибьютор работ по LL-DASH), Fraunhofer FOKUS (через Daniel Silhavy и группу Bentaleb, ведущих LoL+), несколько вещательных референсных развёртываний и сообщество smart-TV на Tizen и webOS через WebKit/Blink-браузеры этих платформ. GitHub-репозиторий имеет примерно 5500 звёзд и 1700 форков на декабрь 2025 – треть от числа звёзд hls.js, чуть меньше, чем у Shaka, но с заметно другой пользовательской базой. Пользователи dash.js – это смесь членов DASH-IF, исследовательских групп и OTT-инженерных команд, которым нужно эталонное поведение, которое они могут патчить; пользователи hls.js – длинный хвост вебкастов и обучающих видео; пользователи Shaka склоняются к платным OTT-сервисам с DRM, мульти-CDN и требованиями offline.
Архитектура в одном абзаце
Запущенный экземпляр dash.js – это небольшой граф сервисов, висящих на верхнеуровневом классе MediaPlayer – публичном фасаде, который вы получаете из dashjs.MediaPlayer().create(). Конструктор связывает сервисы через FactoryMaker – DI-контейнер, который различает class-фабрики (одна инстанция на каждый вызов create(), например, сам MediaPlayer) и singleton-фабрики (одна инстанция на context, например, MediaPlayerModel и Settings). В числе сервисов: Settings – модуль с полным деревом конфигурации (вы меняете его через player.updateSettings()), StreamController – владелец жизненного цикла потока (parse манифеста, переключение period, вставка рекламы, end-of-stream), ManifestModel и DashAdapter – превращают байты MPD во внутренние Variant-объекты, ProtectionController – драйвер EME для Widevine, PlayReady и ClearKey, ABRController – запускает упорядоченный набор Rules, чтобы выбрать следующую вариант, BufferController на каждый медиатип – владеет MSE SourceBuffer для своего трека, CatchupController – подстраивает скорость воспроизведения, чтобы держаться у live edge при LL-DASH, и ThroughputController – оценивает полосу пропускания по-разному в зависимости от того, низколатентный поток или steady-state. Вы вызываете player.initialize(videoElement, url, autoStart), чтобы запустить всё, и подписываетесь на события. Остальное – конфигурация.
Этот абзац – вся картина. Дальше – увеличение по каждому сервису с указанием конфигурации и переключателей, которые меняют поведение в продакшене.
Фасад MediaPlayer и FactoryMaker
MediaPlayer – единственная API-поверхность, которую трогают 90% приложений. Вы создаёте инстанс через dashjs.MediaPlayer().create(), прикрепляете к элементу <video> через player.initialize(videoElement, sourceUrl, autoPlay), меняете поведение через player.updateSettings({…}), слушаете события через player.on(EventName, callback) и уничтожаете через player.destroy(). Под капотом каждый другой модуль dash.js создаётся лениво FactoryMaker – собственным DI-контейнером dash.js.
Швы, которые открывает FactoryMaker, – это способ расширять dash.js без форка. FactoryMaker.extend(parentClassName, childClass, override, context) регистрирует подкласс любого dash.js-модуля. Флаг override управляет, заменяет ли потомок родителя (override=true) или частично переопределяет его (override=false, в этом случае непереопределённые методы проваливаются на родителя). Паттерн выглядит так (Dash-Industry-Forum/dash.js, Developer Getting Started Guide, доступ 2026-05-24):
import dashjs from 'dashjs';
const CustomThroughputRule = function () {
const context = this.context;
// Custom rule logic here.
return { getMaxIndex: () => /* index */ };
};
dashjs.FactoryMaker.extend('ThroughputRule', CustomThroughputRule, true, context);Это тот же шов, через который поставляются правила L2A и LoL+ – оба независимые *Rule-классы, зарегистрированные тем же механизмом, которым пользуется приложение. И это же способ для команд внедрять нейронный ABR (Pensieve, Comyco) поверх dash.js: реализуете правило, регистрируете, существующий ABR-конвейер принимает его как один вход из нескольких.
ABRController и конвейер правил
Самая отличительная часть архитектуры dash.js – это ABR на правилах. Там, где hls.js и Shaka поставляют один ABR-алгоритм с ручками настройки, dash.js поставляет ABR-конвейер, запускающий несколько правил в приоритетном порядке и выбирающий самую консервативную рекомендацию. Дефолтный стек правил содержит примерно: ThroughputRule (пик на основе пропускной способности), BolaRule (BOLA на основе буфера, алгоритм оптимизации Ляпунова из статьи Park & Chiang 2016 года), InsufficientBufferRule (страховка – снижает битрейт, когда буфер опасно мал), SwitchHistoryRule (фильтр стабильности, предотвращающий thrashing), DroppedFramesRule (срезает битрейт, когда декодер дропает кадры) и AbandonRequestRule (отменяет медленную загрузку сегмента и пробует более низкое разрешение). Каждое правило возвращает SwitchRequest с рекомендуемым индексом quality и приоритетом; ABRController выбирает самый низкий quality среди запросов с самым высоким приоритетом.
Этот конвейер – структурная причина, по которой dash.js может чисто принимать исследовательские алгоритмы. Алгоритм DYNAMIC, дефолтный с v2.6.0 (сентябрь 2017), сам по себе является поведением конвейера: DYNAMIC = (buffer level < 10s) ? THROUGHPUT : BOLA, то есть «если буфер маленький, побеждает throughput-правило; иначе – BOLA» (Spiteri, Sitaraman, Sparacio, From Theory to Practice: Improving Bitrate Adaptation in the DASH Reference Player, ACM Transactions on Multimedia Computing, 2019). DASH-IF сохранил оба алгоритма как отдельные правила, чтобы команды могли закреплять один или другой под конкретный воркфлоу – для steady-state VOD можно закрепить BOLA; для LL-DASH live – throughput плюс L2A; для кастомного воркфлоу – реализовать новое правило и дать конвейеру выбрать.
Ручки конфигурации, которые имеют значение, выставлены через дерево streaming.abr. Вы меняете весовое распределение оценок пропускной способности через streaming.abr.throughput.averageCalculationMode (arithmetic, geometric, EWMA), задаёте per-media safety factors через streaming.abr.bandwidthSafetyFactor, фиксируете min и max битрейты через streaming.abr.minBitrate и streaming.abr.maxBitrate и выбираете активную стратегию через streaming.abr.ABRStrategy (abrThroughput, abrBola, abrDynamic, abrL2A, abrLoLP).
ProtectionController и EME
ProtectionController – часть dash.js, которая занимается DRM. Когда манифест объявляет key system – Widevine через com.widevine.alpha, PlayReady через com.microsoft.playready или ClearKey для тестирования, – контроллер вызывает navigator.requestMediaKeySystemAccess с правильной MediaKeySystemConfiguration, открывает MediaKeySession, скачивает лицензию по URL из protection.servers и подаёт ключ в CDM браузера. BufferController не может append зашифрованных байтов до прихода ключа – поэтому медленный лицензионный сервер чаще всего вызывает «чёрный экран без ошибки» на платном потоке.
Стандартный паттерн конфигурации в 2026 – это один блок:
player.setProtectionData({
'com.widevine.alpha': {
serverURL: 'https://drm.example.com/widevine',
httpRequestHeaders: { 'X-AxDRM-Message': token }
},
'com.microsoft.playready': {
serverURL: 'https://drm.example.com/playready',
httpRequestHeaders: { 'X-AxDRM-Message': token }
}
});Это вся поверхность, которая нужна большинству продакшен-развёртываний для multi-DRM (Dash-Industry-Forum/dash.js, страница DRM, доступ 2026-05-24). dash.js официально поддерживает Widevine, PlayReady и ClearKey – пути для FairPlay нет, потому что FairPlay привязан к HLS-only экосистеме Apple, а dash.js не играет HLS. Для продукта, которому нужен DASH на Chrome/Edge/Firefox/Android плюс FairPlay-зашифрованный HLS на iOS Safari, каноническая архитектура 2026 – «dash.js (или Shaka) на вебе и Android, нативный AVPlayer на iOS». Выбор между dash.js и Shaka на стороне веба зависит от того, нужен ли вам HLS в том же плеере.
Тонкость, специфичная для dash.js: с v4.3.0 библиотека поддерживает конфигурацию systemStringPriority, которая позволяет указать, какой идентификатор key-system dash.js должен пробовать первым, когда браузер заявляет о поддержке нескольких вариантов одного DRM. Некоторые сборки Chromium анонсируют как com.widevine.alpha, так и com.widevine.alpha.experiment (путь L1 на аппаратном Widevine); фиксирование приоритета – это способ гарантировать L1-воспроизведение на устройствах, которые его поддерживают (Fraunhofer FOKUS Video-Dev, Following the .recommendation – Key system string priority in dash.js, доступ 2026-05-24).
BufferController и MSE
BufferController – это сервис на каждый медиатип, который владеет SourceBuffer браузера для своего трека. Есть один BufferController на каждый активный медиатип (видео, аудио, текст), и каждый управляет своей петлёй загрузки сегментов, очередью append, логикой удаления range и восстановлением после quota-exceeded. Контроллеры работают параллельно – видео-контроллер может append-ить 1080p-сегмент, пока аудио-контроллер append-ит англоязычную аудиодорожку; синхронизация двух происходит на MSE-слое через MediaSource.duration и playhead.
Интересное состояние, которое нужно мониторить в продакшене, – buffer-stall – когда BufferController думает, что добавляет байты, но playhead не двинулся дольше порога стола. dash.js выпускает событие PLAYBACK_STALLED и в зависимости от конфигурации либо чуть-чуть продвигает playhead, либо в low-latency режиме делает seek назад к live edge. Дефолты работают почти для каждого развёртывания, но ручки на месте.
На iOS Safari 17.1 и новее BufferController может использовать API ManagedMediaSource от Apple – подмножество MSE для iPhone-Safari, которое наконец позволяет JavaScript-плеерам воспроизводить DASH на iOS. У ManagedMediaSource более жёсткие правила относительно того, когда браузер может реклеймить память, и требуется обернуть source-элемент в <source>-потомок <video>. dash.js обнаруживает его автоматически при наличии.
ThroughputController и проблема LL-DASH
ThroughputController – сервис, само существование которого доказывает одну вещь: throughput-based ABR ломается на низкой латентности, и решение – не ручка, а другой алгоритм. Классическая оценка пропускной способности работает делением размера сегмента на время его загрузки. Эта формула в порядке, когда сегменты приходят пакетами – сегмент 6 Mbps длительностью 6 секунд, скачанный за 3 секунды, даёт оценку 12 Mbps, что примерно равно доступной полосе. Формула ломается в LL-DASH, потому что сегмент доставляется через HTTP/1.1 chunked transfer encoding по мере того, как энкодер его производит: сервер держит соединение открытым все 6 секунд сегмента и сцеживает байты по мере готовности каждого CMAF-чанка, поэтому «время загрузки» идентично длительности сегмента независимо от реальной полосы. Каждый сегмент выглядит совпадением с полосой 1×, даже на канале 100 Mbps (dashif.org/dash.js, Low Latency Streaming, доступ 2026-05-24).
dash.js решает это двумя режимами ThroughputController, которые вы выбираете через streaming.abr.throughput.lowLatencyDownloadTimeCalculationMode. Дефолтный режим записывает пары timestamp+байты каждый раз, когда приходят данные, фильтрует мелкие записи, представляющие промежутки между приходом чанков, и вычисляет эффективное время загрузки из оставшихся выборок – формула восстанавливает реалистичную оценку полосы, даже если wall-clock длительность сегмента бесполезна. Альтернативный режим – moof-parsing throughput: dash.js парсит приходящие байты, находит каждый CMAF-moof-бокс по мере прибытия и трактует время между последовательными moof-боксами как per-chunk время загрузки. Moof-parsing throughput точнее (он видит границы чанков напрямую) ценой CPU на потоке плеера.
Режим выбирается одним вызовом настроек:
player.updateSettings({
streaming: {
abr: {
throughput: {
lowLatencyDownloadTimeCalculationMode:
dashjs.Constants.LOW_LATENCY_DOWNLOAD_TIME_CALCULATION_MODE.MOOF_PARSING
}
}
}
});Дефолтный оценщик поставляется как default, потому что он дешевле; команды, которым нужна наиболее чистая LL-DASH ABR на коммодити-железе, переключаются на moof-parsing. В любом случае оба LL-DASH ABR-алгоритма (L2A и LoL+) потребляют вывод этого контроллера как вход полосы, так что выбор режима важнее, чем выбор потребляющего его правила.
CatchupController и live edge
CatchupController – это LL-DASH-специфичный сервис, который удерживает плеер близко к live edge. Задача сложнее, чем кажется: зритель, поставивший на паузу на десять секунд и возобновивший воспроизведение, теперь на десять секунд позади live edge и должен догнать; зритель, у которого Wi-Fi сделал rebuffer на две секунды, отстаёт на две секунды; чрезмерно агрессивный ABR-алгоритм, префетчивший высокобитрейтный сегмент, немного отстал из-за более долгой загрузки. Дефолтный механизм catch-up ускоряет воспроизведение до 50% (liveCatchup.playbackRate.max = 0.5, то есть скорость воспроизведения может вырасти до 1.5×), пока drift задержки не вернётся к цели, и замедляет до 50% (liveCatchup.playbackRate.min = -0.5, до 0.5×), когда буфер в опасности.
dash.js предоставляет два режима catch-up: default-режим (математически: newRate = (1 − cpr) + (cpr × 2) / (1 + e^−d), где cpr – настраиваемая скорость, а d – кратное дельты задержки) и LoL+, который добавляет к этому же уравнению буферный член, чтобы плеер замедлялся (а не просто зависал), когда буфер угрожает иссякнуть. Режим выбирается через streaming.liveCatchup.mode, целевая задержка задаётся через streaming.delay.liveDelay (в секундах), максимально допустимый drift – через streaming.liveCatchup.maxDrift (после которого dash.js делает seek назад к live edge вместо catch-up), а границы catch-up rate – через streaming.liveCatchup.playbackRate. Полная таблица дефолтного catchup задокументирована на странице low-latency.html в документации dash.js.
Тонкость: механизм catch-up срабатывает только когда текущая задержка находится в пределах streaming.liveCatchup.latencyThreshold от цели. Если плеер сильно ушёл от цели – например, после долгой паузы – dash.js сделает seek прямо к live edge, а не будет минуту догонять на 1.5×, что было бы и слышно зрителю, и неэффективно.
Минимальный жизнеспособный плеер dash.js
Семи строк ниже достаточно, чтобы загрузить DASH-поток в браузерную вкладку и начать воспроизведение. Они предполагают, что браузер достаточно современный для MSE и манифест публично доступен (Dash-Industry-Forum/dash.js, Quickstart, доступ 2026-05-24).
<video id="videoPlayer" controls></video>
<script src="https://cdn.dashjs.org/latest/modern/umd/dash.all.min.js"></script>
<script>
const url = 'https://dash.akamaized.net/envivio/EnvivioDash3/manifest.mpd';
const player = dashjs.MediaPlayer().create();
player.initialize(document.querySelector('#videoPlayer'), url, true);
</script>Форма API: MediaPlayer().create() возвращает свежий инстанс, initialize(video, url, autoPlay) делает всё (загрузка манифеста, MSE-биндинг, автоплей), а player.reset() всё освобождает. Если вы хотите подписаться на события для телеметрии – добавляете player.on(dashjs.MediaPlayer.events.PLAYBACK_STARTED, …), остальное по тому же паттерну. Библиотека выставляет около 70 именованных событий – вместе они позволяют построить полный QoE-телеметрический конвейер без нюхания DOM.
Если вы хотите включить low-latency режим – передайте правильные настройки до вызова initialize:
player.updateSettings({
streaming: {
delay: { liveDelay: 3 },
liveCatchup: { maxDrift: 0.5, playbackRate: { max: 0.5, min: -0.5 } },
abr: {
ABRStrategy: 'abrL2A',
throughput: {
lowLatencyDownloadTimeCalculationMode:
dashjs.Constants.LOW_LATENCY_DOWNLOAD_TIME_CALCULATION_MODE.MOOF_PARSING
}
}
}
});Это вся дельта в коде плеера, чтобы перейти от 24-секундной VOD-стиле задержки к суб-3-секундной LL-DASH (при условии, что энкодер производит CMAF-чанки, а CDN пробрасывает chunked transfer encoding). Сторона пакетировщика и CDN в LL-DASH – это более длинная история, разобранная в нашей статье LL-DASH и low-latency CMAF.
Расчёт задержки на конкретном примере
Частый вопрос в проекте 2026 года: «если мы переключимся с обычного DASH на LL-DASH с dash.js, сколько секунд glass-to-glass задержки мы сэкономим?» Вот арифметика на одном потоке.
Обычный DASH-поток использует 6-секундные сегменты, и целевой буфер плеера – три сегмента впереди playhead.
«Длительность сегмента × глубина буфера = 6 с × 3 = 18 с задержки со стороны плеера.»
Прибавьте около 4 секунд задержки сети и origin и 2 секунды задержки энкодера-пакетировщика, и получите glass-to-glass:
«18 с + 4 с + 2 с = 24 с.»
Это steady-state, на хорошо ведущем себя CDN. Хорошее число для «свадебной live-трансляции», но плохое для «in-play sports betting».
LL-DASH версия использует 2-секундные сегменты, кодируемые как CMAF-чанки по 200 мс, с availabilityTimeOffset, выставленным так, что плеер может запросить чанк почти сразу, как энкодер его произвёл. streaming.delay.liveDelay у dash.js выставлен в 3 секунды:
«Целевая задержка плеера = 3 с (задаётся streaming.delay.liveDelay).»
Задержка сети и origin остаётся около 0,8 с в LL-DASH случае, потому что CDN шилдит chunked-transfer-encoded ответы, а упаковка энкодера остаётся около 0,5 с.
«3 с + 0,8 с + 0,5 с ≈ 4,3 с glass-to-glass.»
Переключатель в dash.js, чтобы это включить, – блок updateSettings из предыдущей секции. Работа происходит на стороне пакетировщика (Shaka Packager, Bitmovin, AWS Elemental, FFmpeg с поддержкой LL-DASH или ваш энкодер по выбору) и на стороне CDN (chunked transfer encoding включено на origin и не отрезается промежуточными кешами). Когда кластер энкодеров сам оптимизирован, а CDN на edge – правило L2A или LoL+ держит плеер близко к цели в 3 секунды даже через Wi-Fi-джиттер; это эмпирический результат референсных тестов DASH-IF.
dash.js против Shaka Player – семь осей, на которых решается выбор
Вопрос «dash.js или Shaka?» возникает так часто, что сравнительная таблица – обязательное чтение. Обе библиотеки превосходны, под лицензиями BSD-3 / Apache-2.0, активно поддерживаются и используются в продакшене серьёзными командами. Они отличаются тем, что покрывают и как расширяются.
| Ось | dash.js | Shaka Player | Победитель по этой оси |
|---|---|---|---|
| Покрытие форматов | DASH + Microsoft Smooth Streaming | DASH + HLS + Smooth Streaming | Shaka, если вы доставляете HLS |
| Еженедельные npm-скачивания (май 2026) | ~331 000 | ~172 000 | dash.js – больше DASH-фокусированная база |
| Охват DRM | Widevine, PlayReady, ClearKey (без FairPlay) | Widevine, PlayReady, FairPlay, ClearKey | Shaka – полный multi-DRM |
| LL-DASH алгоритмы | L2A, LoL+, плюс default и moof-parsing throughput | Generic low-latency без LoL+/L2A примитивов | dash.js – со значительным отрывом |
| Offline-хранилище | Нет first-class offline | First-class Storage API на IndexedDB | Shaka |
| Chromecast | Cast через Video.js / собственную обвязку | Встроен в Google Cast Web Receiver SDK | Shaka |
| Статус референс-реализации | Официальный референс DASH-IF; новые DASH-IF фичи приземляются здесь первыми | Высококачественная реализация; не орган стандартов | dash.js – для DASH-IF-управляемых воркфлоу |
Дефолтное правило выбора, выпадающее из этой таблицы: берите dash.js, когда вы DASH-only и для вас важнее всего эталонное поведение, LL-DASH примитивы (L2A, LoL+, moof-parsing throughput) и нахождение на острие кривой DASH-IF; берите Shaka, когда вам нужен HLS в том же плеере, поддержка FairPlay, offline-воспроизведение или Chromecast «из коробки». Неправильный вопрос – «который лучше?»: они не в одном забеге. Правильный вопрос – «который меньший риск на ближайшие два года?», а ответ зависит от того, производит ли ваш пакетировщик HLS вдобавок к DASH, нужен ли в DRM FairPlay и насколько агрессивна ваша цель по задержке.
Для проекта Фора Софт чаще всего это OTT-продукт, который доставляет Widevine-зашифрованный DASH для Android-и-веба плюс FairPlay-зашифрованный HLS для iOS – такой стек почти всегда выбирает Shaka, потому что второй формат обязателен. Когда задача – «ультра-низколатентный DASH только, для live-betting или in-game-shopping продукта, с research-grade ABR» – мы доставляем dash.js с abrL2A или abrLoLP, потому что у альтернативы нет таких примитивов.
Где dash.js ведёт экосистему (CMCD, CMSD, content steering)
Три инициативы CTA-WAVE / DASH-IF / SVTA попали в продакшен через dash.js первыми. Common Media Client Data (CTA-5004) – стандарт, по которому клиент отправляет CDN контекст воспроизведения – длину буфера, битрейт, content ID, session ID, тип запроса – через HTTP request-заголовки, query-строки или JSON. dash.js поставляет полный CMCD v1, а начиная с v5.0.0 – v2-ключи ltc (live target latency) и msd (measured startup delay). Common Media Server Data (CTA-5006) – зеркало: CDN рассказывает клиенту о своей текущей нагрузке через HTTP response-заголовки, а dash.js выставляет распарсенный CMSD-ответ через события, на которые ваш код может реагировать. Content steering – спецификация 2022 года от DASH-IF и Apple HLS, позволяющая серверу указывать плееру, какой CDN использовать дальше, – приземлилась в dash.js через PR #4031 и является эталонной реализацией, против которой меряется каждый коммерческий steering-вендор.
Все три задокументированы под dashif.org/dash.js/pages/usage/ и настраиваются через player.updateSettings(). Дело не в том, что другие плееры не могут – могут, с задержкой, – а в том, что dash.js поставляет эти фичи первым, потому что процесс стандартизации и процесс имплементации делят контрибьюторов.
Продакшен-паттерн обработки ошибок
Каждое продакшен-развёртывание dash.js борется с одним и тем же семейством ошибок. Библиотека категоризирует их через dashjs.MediaPlayer.errors.* – важнейшие константы: MANIFEST_LOADER_LOADING_FAILURE_ERROR_CODE, FRAGMENT_LOADER_LOADING_FAILURE_ERROR_CODE, MEDIASOURCE_TYPE_UNSUPPORTED_CODE, KEY_SESSION_CREATED_ERROR_CODE, KEY_ERROR и catch-all MEDIA_SOURCE_ERROR_CODE. Паттерн ниже – то, что мы доставляем по умолчанию.
player.on(dashjs.MediaPlayer.events.ERROR, (event) => {
const { error } = event;
const { code, message, data } = error;
// Сначала телеметрия — каждая ошибка, recoverable или нет, это точка данных.
telemetry.track('dashjs_error', { code, message });
switch (code) {
case dashjs.MediaPlayer.errors.MANIFEST_LOADER_LOADING_FAILURE_ERROR_CODE:
ui.showError('Не удалось загрузить контент. Нажмите для повтора.');
break;
case dashjs.MediaPlayer.errors.FRAGMENT_LOADER_LOADING_FAILURE_ERROR_CODE:
// Проблема CDN — dash.js уже пытается повторить внутри loader.
ui.showToast('Переподключение…');
break;
case dashjs.MediaPlayer.errors.KEY_ERROR:
case dashjs.MediaPlayer.errors.KEY_SESSION_CREATED_ERROR_CODE:
ui.showError('Сессия истекла. Войдите снова.');
break;
case dashjs.MediaPlayer.errors.MEDIASOURCE_TYPE_UNSUPPORTED_CODE:
ui.showError('Ваш браузер не может воспроизвести этот формат.');
break;
default:
ui.showError('Воспроизведение не удалось. Нажмите для повтора.');
}
});Этот листенер – вся форма обработки ошибок, которая нужна большинству продакшен-развёртываний. Заметьте две вещи: каждая ошибка сообщается телеметрии до любого действия в UI, и recoverable-ошибки (здесь сбой fragment-loader, который dash.js уже повторяет) намеренно не показывают красный баннер – показ каждого временного сбоя – самая частая ошибка обработки, которую мы видим в код-ревью. Категории выше также чисто ложатся на четыре семейства операционных playbook'ов, которые нужны стриминг-продукту: playbook CDN/сети, playbook packaging/MSE, playbook license-сервера и playbook каталога ассетов.
Что изменилось в v5 и что доставлять в 2026
Линейка v5 – текущая ветка dash.js. v5.0.0 вышел 17 февраля 2025 года с тремя изменениями, которые замечают большинство команд (Dash-Industry-Forum/dash.js, Release v5.0.0, 17 февраля 2025; SVTA, dash.js v5.0.0 release, 17 февраля 2025). Система сборки была переписана и теперь поставляет три формата bundle – UMD legacy (для старых платформ), UMD modern (повседневная браузерная цель) и ESM modern (tree-shakeable import-путь для современных бандлеров), – что заметно уменьшило размер modern-bundle и привело dash.js в соответствие с тем, как реально работает build-tooling 2025 года. CMCD v2-ключи ltc (live target latency) и msd (measured startup delay) приземлились вместе с настройкой выбора, какие исходящие запросы несут CMCD-параметры, и приложения получили возможность отключать обработку CMCD-параметров, определённых в MPD. Новый метод seekToPresentationTime() присоединился к существующему seek(), так что приложения могут seek-ать на конкретное media presentation time, а не на оффсет в секундах от старта – маленький API, но важный при интеграции с тайм-кодами ad-сервера.
v5.1.0 вышел 21 ноября 2025 года с работой по LCEVC и эталонному UI, которую большинство команд в итоге адаптируют (Dash-Industry-Forum/dash.js, Release v5.1.0, 21 ноября 2025). Интеграция MPEG-5 Part 2 LCEVC (Low Complexity Enhancement Video Coding) пришла через путь LCEVC SEI, позволяющий правообладателю поставлять базовый слой в одном разрешении и небольшой enhancement-слой, который декодер dash.js / V-Nova апскейлит на клиенте. Поддержка элемента <Preselection> в MPD-документах созрела, слой обработки cue был переписан как Cue Interval Tree для быстрых лукапов при частой вставке рекламы, и появились API для конфигурации внешних субтитров, максимального числа EME KeySessions, приоритета ABR-правил и UTC time-sync оффсета. Эталонный UI, который вы видите на reference.dashif.org/dash.js/, – это переписанный с нуля UI, упомянутый в release notes, и он доступен наряду с legacy UI.
v5.1.1 вышел 23 декабря 2025 года и является текущей опубликованной версией на май 2026. Это патч-линия, на которую стоит таргетить новые развёртывания – только багфиксы поверх v5.1.0.
Частые ловушки
Каждый проект натыкается на небольшое число одних и тех же проблем с dash.js. Шесть стоит знать перед доставкой.
Таргетинг legacy UMD-bundle по умолчанию. dash.js v5 поставляет три bundle; дефолт для современных веб-бандлеров – modern/esm/dash.all.min.js, а не legacy UMD-путь. Выбор legacy на Webpack 5 / Vite проекте доставляет лишние 100 KB полифилов, которые вашим пользователям не нужны.
Использование seek(seconds) вместо seekToPresentationTime(t) против тайм-кода ad-сервера. Эти два метода звучат похоже; один таргетит wall-clock оффсет в секундах от старта потока, другой – media presentation timeline из MPD. Для вставки рекламы по SCTE-35 маркерам нужен второй вызов.
Смешивание throughput-режимов на LL-DASH и обвинение ABR-правила. Две настройки lowLatencyDownloadTimeCalculationMode (default и moof-parsing) дают разные оценки полосы. Если вы переключились с одного на другой и поведение ABR изменилось – это причина: правило читает другой вход, а не ведёт себя иначе.
Обработка каждого fragment-loader события как фатальной ошибки. dash.js делает retry фрагментов внутри loader с экспоненциальным backoff. Событие ошибки на уровне приложения срабатывает только после исчерпания retry-ев. Неправильный паттерн – «каждое fragment-loader retry показывает красный баннер»; правильный – «fragment-loader событие → toast, manifest-loader событие → баннер».
Не включение CMCD на платном CDN-развёртывании. Если вы платите за CDN, поддерживающий CMCD (Akamai, Fastly, Cloudflare, AWS CloudFront с кастомными правилами), включение streaming.cmcd.enabled = true даёт CDN per-session контекст, который материально улучшает cache-shielding и per-customer SLA-отчётность. Стоимость нулевая; выгода реальная.
Загрузка sample reference UI в продакшен. Эталонный UI на reference.dashif.org/dash.js/ – это developer-tool поверхность, выставляющая каждую ручку плеера. Поставка его конечным пользователям даёт им debugging cockpit, о котором они не просили. Опциональный sample controlbar (dash.all.min.js плюс sample CSS) – лёгкий вариант; для продакшен-UI большинство команд пишут собственный тонкий компонент-слой поверх событий player.on(…).
Где здесь Фора Софт
Мы доставляли dash.js в продакшен во многих наших стриминг-смежных практиках – OTT и Internet-TV каталоги, которым нужно DASH-воспроизведение против multi-DRM (Widevine + PlayReady) на открытом вебе, симулькаст-развёртывания вещателей, которым нужно эталонное DASH-IF поведение, e-learning платформы, чей контент DASH-only, surveillance-продукты, где одна и та же библиотека играет live и записанный контент, и небольшое число низколатентных интерактивных продуктов (live shopping, in-play sports), где L2A или LoL+ внутри dash.js были правильным примитивом. По этим вертикалям мы не раз были инженерной командой, построившей слой плеера end-to-end (конфигурация, override ABR, телеметрия ошибок, доступность). Когда аудитория чисто обслуживается Shaka – например, продукт, которому нужен HLS в том же плеере или first-class offline – мы рекомендуем Shaka и используем его; правильный плеер – тот, который соответствует roadmap проекта, а не тот, у которого крупнейшее DASH-сообщество на GitHub.
Ключевые выводы
- dash.js – эталонный плеер DASH-IF; каждая новая DASH-IF фича приземляется здесь первой.
- Архитектура – это граф сервисов вокруг фасада MediaPlayer, связанных через FactoryMaker.
- ABR построен на правилах: Throughput, BOLA, Insufficient-Buffer, SwitchHistory, Dropped-Frames, Abandon, L2A, LoL+.
- LL-DASH требует другого throughput-оценщика (default или moof-parsing) плюс L2A или LoL+, чтобы работать.
- v5.0 (фев 2025) добавил CMCD v2-ключи, ESM-bundle и seekToPresentationTime; v5.1 (ноя 2025) добавил LCEVC.
- Берите dash.js для DASH-only, эталонного поведения и LL-DASH примитивов; Shaka – для HLS, FairPlay, offline или Cast.
Что читать дальше
- Shaka Player: подробный разбор – мультиформатная альтернатива и её trade-off'ы.
- MPEG-DASH: подробный разбор – протокол, для воспроизведения которого создан dash.js.
- LL-DASH и low-latency CMAF – сторона пакетировщика и CDN в LL-DASH, парная этому плееру.
Обсудите с инженером по стримингу развёртывание dash.js, проект LL-DASH ABR или multi-DRM плеер. Посмотрите наши кейсы продакшен-стриминга и OTT-проектов. Скачайте чек-лист продакшен dash.js (PDF, 1 страница) – версии, сервисы, ABR-правила, LL-DASH конфиг, DRM-матрица, классы ошибок, частые ловушки.