Содержание статьи +
- Кратко
- Почему это важно
- Что такое 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-черновик по 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), приводятся семь строк кода для запуска плеера, рассматривается конфигурация, реально определяющая поведение адаптивного битрейта и LL-DASH, проводится сравнение dash.js с Shaka Player по семи параметрам, а также рассказывается, что изменилось в версиях v5.0 (февраль 2025) и v5.1 (ноябрь 2025).
Почему это важно
Если вы в 2026 году доставляете MPEG-DASH в браузер – для платного OTT-каталога, симулькаста телевещания, корпоративного вебкаста, ленты для live-ставок, приложения для smart TV, чей вендорский SDK включает dash.js, или для LL-DASH-события с целевой задержкой 2–5 секунд glass-to-glass – вопрос «dash.js или Shaka Player?» встанет перед кем-то из команды уже в первом спринте проекта. Прочитав эту статью, продакт-менеджер сможет задать инженеру правильные вопросы о поддержке форматов, охвате DRM, стратегии низкой задержки и долгосрочных рисках поддержки, а фронтенд-инженер получит полную ментальную модель библиотеки: фасад, конвейер правил, события, на которых строится продакшен-телеметрия, настройки, позволяющие изменять поведение ABR без форка кода, и четыре класса ошибок, которые нужно обработать с первого дня. Предварительные знания о стриминге не требуются – каждый термин объясняется при первом упоминании. К концу статьи вы поймёте, зачем существует dash.js, когда его стоит выбирать вместо Shaka, а когда – наоборот, какая однострочная настройка включает низколатентный режим, и какие два алгоритма – L2A и LoL+ от DASH-IF – заменяют стандартные ABR-правила, когда субдвухсекундная задержка важнее пиковой пропускной способности.
Что такое dash.js, а что нет
Самое короткое точное определение такое: dash.js – это JavaScript-библиотека, которая читает манифест MPEG-DASH, скачивает указанные в нём видеочанки, передаёт их в браузерный API Media Source Extensions, согласует ключи дешифрации с Content Decryption Module браузера, если поток защищён, и предоставляет поток событий, достаточный для построения полноценного пользовательского интерфейса плеера. Всё это – в open-source пакете под лицензией BSD-3-Clause, который устанавливается через npm install как обычная зависимость (Dash-Industry-Forum/dash.js, README, доступ 2026-05-24). По умолчанию у библиотеки нет UI, хотя в комплекте идёт опциональная панель управления sample-controlbar и совершенно новый эталонный интерфейс, переписанный с использованием ИИ-инструментов в конце 2025 года.
Это не транскодер: каждый байт, поступающий в браузер, уже был закодирован вашим пакетировщиком. И это не мультиформатная библиотека: в отличие от Shaka Player, который поддерживает DASH, HLS и Microsoft Smooth Streaming через единый вызов player.initialize(), dash.js в первую очередь работает с DASH, а во вторую – с Microsoft Smooth Streaming через отдельный MSS-обработчик. Поддержки HLS в dash.js нет и не было в опубликованной дорожной карте проекта – именно это структурное ограничение объясняет, почему решения, требующие 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-Дэш catch-up или приоритета DRM key-систем, 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-фабрики (одна инстанция на контекст, например, MediaPlayerModel и Settings). Среди сервисов: Settings – модуль, содержащий полное дерево конфигурации (изменяется через player.updateSettings()), StreamController – управляет жизненным циклом потока (парсинг манифеста, переключение периодов, вставка рекламы, завершение потока), ManifestModel и DashAdapter – преобразуют байты MPD во внутренние объекты Variant, ProtectionController – драйвер EME для Widevine, PlayReady и ClearKey, ABRController – запускает упорядоченный набор Rules для выбора следующего варианта, BufferController на каждый медиатип – управляет MSE-источником SourceBuffer для своего трека, CatchupController – корректирует скорость воспроизведения, чтобы оставаться у live edge при LL-стриминге, и 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, ставший стандартным с версии 2.6.0 (сентябрь 2017), сам по себе представляет собой поведение конвейера: DYNAMIC = (buffer level < 10s) ? THROUGHPUT : BOLA, то есть «если буфер мал, побеждает правило пропускной способности; иначе – BOLA» (Spiteri, Sitaraman, Sparacio, From Theory to Practice: Improving Bitrate Adaptation in the DASH Reference Player, ACM Transactions on Multimedia Computing, 2019). DASH-IF сохранил оба алгоритма как отдельные правила, чтобы команды могли выбирать один или другой в зависимости от конкретного сценария использования – для стабильного воспроизведения VOD можно использовать BOLA; для LL-DAsh в прямом эфире – правило пропускной способности плюс L2A; для кастомного сценария – реализовать новое правило и позволить конвейеру его применить.
Ручки конфигурации, имеющие значение, настраиваются через дерево streaming.abr. Вы задаёте способ расчёта весов пропускной способности в streaming.abr.throughput.averageCalculationMode (arithmetic, geometric, EWMA), указываете коэффициенты безопасности для каждого типа медиа в streaming.abr.bandwidthSafetyFactor, задаёте минимальные и максимальные битрейты через streaming.abr.minBitrate и streaming.abr.maxBitrate, а также выбираете активную стратегию в streaming.abr.ABRStrategy (abrThroughput, abrBola, abrDynamic, abrL2A, abrLoLP).
ProtectionController и EME
ProtectionController – часть dash.js, отвечающая за DRM. Когда манифест объявляет систему ключей – Widevine через com.widevine.alpha, PlayReady через com.microsoft.playready или ClearKey для тестирования, – контроллер вызывает navigator.requestMediaKeySystemAccess с правильным MediaKeySystemConfiguration, открывает MediaKeySession, скачивает лицензию по URL из protection.servers и передаёт ключ в CDM браузера. BufferController не может добавить зашифрованные байты до получения ключа – поэтому медленный лицензионный сервер чаще всего вызывает «чёрный экран без ошибки» на платном потоке.
Стандартный паттерн конфигурации в 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: начиная с версии 4.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), логику удаления диапазонов и восстановление после ошибки quota-exceeded. Контроллеры работают параллельно: например, видео-контроллер может добавлять сегмент 1080p, в то время как аудио-контроллер добавляет англоязычную дорожку; синхронизация между ними происходит на уровне 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-HLS
ThroughputController – сервис, само существование которого доказывает одну вещь: throughput-based ABR не работает при низкой латентности, и выход – не ручная настройка, а другой алгоритм. Классическая оценка пропускной способности строится на делении размера сегмента на время его загрузки. Эта формула корректна, когда сегменты приходят пакетами: например, сегмент объёмом 6 Мбит/с длительностью 6 секунд, скачанный за 3 секунды, даёт оценку 12 Мбит/с – что близко к реальной доступной полосе. Однако формула перестаёт работать в LL-DASH, потому что сегмент передаётся через HTTP/1.1 с chunked transfer encoding по мере готовности: сервер держит соединение открытым всё время – 6 секунд – и постепенно отправляет байты по мере появления каждого CMAF-чанка. В результате «время загрузки» оказывается равным длительности сегмента независимо от реальной пропускной способности канала. Каждый сегмент выглядит так, будто соответствует полосе 1×, даже при канале в 100 Мбит/с (dashif.org/dash.js, Low Latency Streaming, доступ 2026-05-24).
dash.js решает эту задачу с помощью двух режимов ThroughputController, которые можно выбрать через streaming.abr.throughput.lowLatencyDownloadTimeCalculationMode. По умолчанию используется режим, при котором записываются пары timestamp+байты каждый раз при поступлении данных, отфильтровываются мелкие записи, соответствующие паузам между чанками, а эффективное время загрузки вычисляется на основе оставшихся выборок – формула позволяет получить реалистичную оценку пропускной способности, даже если фактическая длительность сегмента по часам неинформативна. Альтернативный режим – moof-parsing throughput: dash.js анализирует поступающие байты, находит каждый CMAF-moof-бокс по мере его получения и интерпретирует время между последовательными moof-боксами как время загрузки отдельного чанка. Moof-parsing throughput более точен (он напрямую видит границы чанков), но требует дополнительных вычислительных ресурсов на потоке плеера.
Режим выбирается одним вызовом настроек:
player.updateSettings({
streaming: {
abr: {
throughput: {
lowLatencyDownloadTimeCalculationMode:
dashjs.Constants.LOW_LATENCY_DOWNLOAD_TIME_CALCULATION_MODE.MOOF_PARSING
}
}
}
});Дефолтный оценщик поставляется по умолчанию, потому что он дешевле; команды, которым нужна наиболее чистая LL-ABR на коммодити-оборудовании, переключаются на moof-распарсинг. В любом случае оба алгоритма LL-ABR (L2A и LoL+) используют выход этого контроллера как входную полосу пропускания, поэтому выбор режима важнее, чем выбор конкретного правила, его обрабатывающего.
CatchupController и live edge
CatchupController – это сервис, специфичный для LL-DASH, который поддерживает плеер в непосредственной близости от live edge. Задача оказывается сложнее, чем может показаться: зритель, поставивший воспроизведение на паузу на десять секунд и возобновивший его, теперь отстаёт от live edge на десять секунд и должен «догнать»; зритель, у которого Wi-Fi вызвал буферизацию на две секунды, отстаёт на две секунды; чрезмерно агрессивный ABR-алгоритм, скачавший сегмент с высоким битрейтом, немного отстаёт из-за более длительной загрузки. Стандартный механизм догоняния ускоряет воспроизведение на 50% (liveCatchup.playbackRate.max = 0.5, то есть скорость может вырасти до 1,5×), пока задержка не вернётся к целевому значению, и замедляет его на 50% (liveCatchup.playbackRate.min = -0.5, до 0,5×), когда буфер оказывается под угрозой.
dash.js поддерживает два режима синхронизации с прямым эфиром: базовый режим (математически: newRate = (1 − cpr) + (cpr × 2) / (1 + e^−d), где cpr – настраиваемая скорость, а d – кратное дельты задержки) и LoL+, который дополняет это уравнение буферным членом, чтобы плеер плавно замедлялся (а не просто зависал), когда уровень буфера становится критическим. Режим выбирается через streaming.liveCatchup.mode, целевая задержка задаётся через streaming.delay.liveDelay (в секундах), максимально допустимое отклонение – через streaming.liveCatchup.maxDrift (после которого dash.js выполняет seek назад к live edge вместо продолжения синхронизации), а границы скорости синхронизации – через streaming.liveCatchup.playbackRate. Полная таблица параметров по умолчанию для catch-up задокументирована на странице 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.
Если вы хотите включить режим с низкой задержкой – передайте правильные настройки до вызова 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-HLS (при условии, что энкодер генерирует CMAF-чанки, а CDN поддерживает chunked transfer encoding). Что касается сторон пакетировщика и CDN в LL-HLS – это более сложная тема, подробно рассмотренная в нашей статье LL-HLS и 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 с.»
Это стационарное состояние на хорошо работающем CDN. Хорошее значение для «свадебной прямой трансляции», но плохое для «ставки на спорт в режиме реального времени».
LL-версия DASH использует сегменты длительностью 2 секунды, которые кодируются в виде CMAF-чанков по 200 мс. Параметр availabilityTimeOffset настроен так, что плеер может запросить чанк практически сразу после его создания энкодером. В dash.js значение streaming.delay.liveDelay установлено в 3 секунды:
«Целевая задержка плеера – 3 с (задаётся streaming.delay.liveDelay).»
Задержка сети и origin остаётся около 0,8 с в случае LL-DASH, поскольку CDN блокирует chunked-transfer-encoded ответы, а упаковка энкодера составляет около 0,5 с.
«3 с + 0,8 с + 0,5 с ≈ 4,3 с – время «стекло-стекло».»
Переключатель в dash.js для включения этой функции – блок updateSettings из предыдущей секции. Обработка происходит на стороне пакетировщика (Shaka Packager, Bitmovin, AWS Elemental, FFmpeg с поддержкой LL-HLS или ваш энкодер по выбору) и на стороне CDN (включено chunked transfer encoding на origin и не блокируется промежуточными кешами). Когда кластер энкодеров оптимизирован, а CDN работает на edge, правило L2A или LoL+ позволяет плееру оставаться близко к целевой скорости загрузки даже при 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 и для вас критичны эталонное поведение, примитивы LL-DASH (L2A, LoL+, производительность парсинга moof) и актуальность в рамках DASH-IF; выбирайте Shaka, если вам нужен HLS в том же плеере, поддержка FairPlay, офлайн-воспроизведение или Chromecast «из коробки». Неправильный вопрос – «какой лучше?»: они не конкурируют в одной категории. Правильный вопрос – «какой вариант несёт меньший риск на ближайшие два года?», и ответ зависит от того, генерирует ли ваш пакетировщик HLS наряду с DASH, требуется ли DRM FairPlay и насколько агрессивны ваши цели по задержке.
Для проекта Фора Софт чаще всего используется OTT-продукт, который доставляет Widevine-зашифрованный DASH для Android и веба, а также FairPlay-зашифрованный HLS для iOS – такой стек почти всегда строится на Shaka, поскольку второй формат является обязательным. Когда задача – «ультра-низколатентный DASH исключительно для live-ставок или in-game-шоппинга с research-уровнем 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-заголовки, параметры строки запроса или JSON. dash.js поддерживает полный CMCD v1, а начиная с версии 5.0.0 – ключи v2: ltc (live target latency) и msd (measured startup delay). Common Media Server Data (CTA-5006) – зеркальный стандарт: CDN сообщает клиенту о текущей нагрузке через HTTP-заголовки ответа, а 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 и универсальную 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('Воспроизведение не удалось. Нажмите для повтора.');
}
});Этот листенер – вся обработка ошибок, которая нужна большинству продакшен-развёртываний. Обратите внимание на два момента: каждая ошибка сначала отправляется в телеметрию, а уже потом отражается в интерфейсе, и recoverable-ошибки (например, сбой загрузчика фрагментов, который dash.js уже повторяет автоматически) намеренно не сопровождаются красным баннером – ведь показ каждого временного сбоя – самая распространённая ошибка в обработке исключений, с которой мы сталкиваемся при код-ревью. Эти категории также чётко соответствуют четырём операционным playbook’ам, необходимым стриминговому продукту: playbook по CDN и сети, playbook по упаковке и MSE, playbook по лицензионным серверам и 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). Система сборки была переписана и теперь поставляет три формата бандлов: UMD legacy (для старых платформ), UMD modern (стандартная цель для современных браузеров) и ESM modern (поддерживает tree-shaking через import для современных бандлеров). Это значительно сократило размер modern-бандла и привело dash.js в соответствие с реальными практиками build-инструментов 2025 года. CMCD v2-ключи ltc (live target latency) и msd (measured startup delay) были добавлены вместе с возможностью выбора, какие исходящие запросы должны содержать CMCD-параметры, а приложения теперь могут отключать обработку CMCD-параметров, определённых в MPD. Новый метод seekToPresentationTime() дополнил существующий seek(), позволяя приложениям перемещаться к конкретному времени представления медиа, а не к смещению в секундах от начала – небольшой, но важный 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-времени. Эталонный UI, который вы видите на reference.dashif.org/dash.js/, – это полностью переписанный с нуля интерфейс, упомянутый в релизных заметках, и он доступен параллельно с legacy UI.
v5.1.1 вышел 23 декабря 2025 года и является текущей опубликованной версией на май 2026. Это патч-ветка, на которую следует ориентироваться при новых развёртываниях – только исправления ошибок поверх v5.1.0.
Частые ловушки
Каждый проект сталкивается с небольшим числом одних и тех же проблем в dash.js. Шесть из них стоит знать перед релизом.
Таргетинг legacy UMD-баунда по умолчанию. dash.js v5 поставляет три баунда; по умолчанию для современных веб-бандлеров используется modern/esm/dash.all.min.js, а не legacy UMD-путь. Выбор legacy-версии в проектах на Webpack 5 или Vite добавляет лишние 100 КБ полифилов, которые не нужны вашим пользователям.
Использование seek(seconds) вместо seekToPresentationTime(t) против тайм-кода ad-сервера. Эти два метода могут показаться похожими: один нацеливается на смещение по часовому времени в секундах от начала потока, другой – на временную шкалу представления медиа из MPD. Для вставки рекламы по маркерам SCTE-35 требуется второй вариант.
Смешивание throughput-режимов на LL-DASH и обвинение ABR-правила. Две настройки lowLatencyDownloadTimeCalculationMode (default и moof-parsing) дают разные оценки пропускной способности. Если при переключении между ними поведение ABR изменилось – причина в том, что правило получает разные входные данные, а не работает иначе.
Обработка каждого события fragment-loader как фатальной ошибки. dash.js выполняет повторные попытки загрузки фрагментов внутри loader с экспоненциальной задержкой. Событие ошибки на уровне приложения возникает только после исчерпания всех попыток повторной загрузки. Неправильный подход – «каждое повторное подключение fragment-loader отображает красный баннер»; правильный – «событие fragment-loader → уведомление (toast), событие manifest-loader → баннер».
Не включение CMCD на платном CDN-развёртывании. Если вы используете платный CDN, поддерживающий CMCD (например, Akamai, Fastly, Cloudflare, AWS CloudFront с кастомными правилами), включение streaming.cmcd.enabled = true предоставляет CDN контекст на уровне сессии, что существенно улучшает работу cache-шейдинга и отчётность по 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 и интернет-ТВ каталогах, где требуется DASH-воспроизведение с поддержкой multi-DRM (Widevine + PlayReady) на открытом вебе, в симулькаст-развёртываниях вещателей, которым необходимо эталонное поведение по стандартам DASH-IF, на e-learning платформах с DASH-эксклюзивным контентом, в системах видеонаблюдения, где одна и та же библиотека воспроизводит как прямой эфир, так и записанный контент, а также в небольшом числе низколатентных интерактивных продуктов (например, live shopping, in-play sports), где L2A или LoL+ внутри dash.js были подходящим решением. По этим направлениям мы не раз выступали в роли инженерной команды, реализовавшей плеер end-to-end: от настройки и переопределения ABR до телеметрии ошибок и обеспечения доступности. Когда аудитория обслуживается исключительно Shaka – например, продукт, которому нужен HLS в том же плеере или полноценная поддержка офлайн-режима – мы рекомендуем Shaka и используем его. Правильный плеер – это тот, который соответствует roadmap проекта, а не тот, у которого самое большое DASH-сообщество на GitHub.
Ключевые выводы
- dash.js – эталонный плеер DASH-IF; каждая новая фича DASH-IF появляется здесь первой.
- Архитектура представляет собой граф сервисов вокруг фасада MediaPlayer, связанных через FactoryMaker.
- ABR работает на основе правил: Throughput, BOLA, Insufficient-Buffer, SwitchHistory, Dropped-Frames, Abandon, L2A, LoL+.
- LL-Даш требует другого оценщика пропускной способности (default или moof-парсинг) плюс L2A или LoL+ для корректной работы.
- v5.0 (февраль 2025) добавил CMCD v2-ключи, ESM-баундл и seekToPresentationTime; v5.1 (ноябрь 2025) добавил LCEVC.
- Используйте dash.js для DASH-только, эталонного поведения и примитивов LL-Даш; Shaka – если нужна поддержка HLS, FairPlay, офлайн-воспроизведения или Cast.
Что читать дальше
- Shaka Player: подробный разбор – мультиформатная альтернатива и её компромиссы.
- 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, классы ошибок, типичные ловушки.