Содержание статьи +
- Кратко
- Зачем это вам
- Что Shaka Player делает и чем он не является
- Почему эта библиотека вообще существует
- Архитектура в одном абзаце
- Минимально жизнеспособный плеер на Shaka
- Разбор задержки на цифрах
- Shaka Player против hls.js – восемь осей выбора
- Продакшен-схема восстановления после ошибок
- Что изменилось в v5 и что отгружать в 2026
- Частые ошибки
- Где здесь Фора Софт
- Ключевые выводы
- Что почитать дальше
- CTA
Кратко
Открытая библиотека Shaka Player – это JavaScript-видеоплеер от Google для открытого веба, и в 2026 году это единственный массовый клиент, который говорит и на MPEG-DASH, и на HTTP Live Streaming через один и тот же API, единственный со стабильной историей офлайн-проигрывания премиум-контента и плеер, который загружается внутрь Cast Web Receiver от Google каждый раз, когда устройство Chromecast играет DASH. Внутри это граф «движков»: NetworkingEngine, который владеет каждым HTTP-запросом, ManifestParser на каждый формат, DrmEngine, который драйвит Encrypted Media Extensions, StreamingEngine, который наполняет буфер, и подключаемый AbrManager, выбирающий, какой вариант скачать следующим, – всё это поверх браузерного Media Source Extensions. В этой статье мы пройдём архитектуру по движкам, покажем семь строк кода для запуска базового плеера, разберём настройки, которые реально влияют на адаптивный битрейт, low-latency live и multi-DRM, сравним Shaka с hls.js по восьми осям, по которым принимают реальный продакшен-выбор, и расскажем, какие фичи v5 – в том числе ABR с учётом dropped frames, поддержка TiVo OS и Titan OS и экспериментальные авто-переведённые субтитры – ваша команда, возможно, ещё не отгрузила.
Зачем это вам
Если вы доставляете видео в браузер, мобильный веб, Chromecast, smart-TV или любую их комбинацию в 2026 году, вопрос «Shaka Player или hls.js?» падает кому-то на стол в первом же спринте – и ответ определяет два года инженерной работы. Эта статья должна позволить продакт-менеджеру задать инженеру правильные вопросы про охват форматов, DRM, офлайн и smart-TV, а frontend-, мобильному- или smart-TV-инженеру – получить полную ментальную модель библиотеки: движки, события для продакшен-телеметрии, конфиги, которые меняют поведение ABR без форка, и четыре семейства ошибок, которые надо обработать в первый же день. Никаких предварительных знаний по стримингу не требуется; каждое понятие объясняется по ходу. К концу вы поймёте, зачем существует Shaka Player, когда использовать его вместо hls.js, когда – вместо dash.js, и какая одна настройка включает low-latency DASH с 3-секундным live без переписывания плеера.
Что Shaka Player делает и чем он не является
Самое короткое точное определение такое. Shaka Player – это JavaScript-библиотека, которая читает стриминг-манифест в формате MPEG-DASH или HLS, скачивает указанные в нём видео-чанки, передаёт их в API Media Source Extensions, договаривается о ключах расшифровки с Content Decryption Module браузера, когда поток защищён, и выставляет поток событий, достаточный, чтобы поверх него собрать целый продакшен-плеер с UI, – и всё это из открытого пакета под лицензией Apache 2.0, который ставится через npm install, как любая другая зависимость. По умолчанию это не UI, хотя есть опциональная доступная UI-библиотека. Это не транскодер: каждый байт уже был закодирован пакейджером. И это не HLS-плеер: там, где hls.js играет только HLS, Shaka Player играет и DASH, и HLS через один и тот же вызов player.load().
README самого проекта формулирует это одной строкой – «JavaScript player library · DASH & HLS client · MSE-EME player» (shaka-project/shaka-player, README, accessed 2026-05-24) – и эта строка целиком описывает продукт. Shaka появился в Google в 2014 году внутри команды Widevine как референсный DASH-плеер для экосистемы Widevine DRM, был открыт в 2015, перешёл из организации Google на GitHub в комьюнити-организацию shaka-project и уже больше десяти лет ведётся Joey Parrish'ем из Google. По состоянию на май 2026 библиотека – на линии v5.1: v5.0 вышел в Q1 2026, v5.1.0 – 15 апреля 2026, v5.1.4 – 20 мая 2026 (npm registry, shaka-player, accessed 2026-05-24). Еженедельные скачивания на npm – около 205 000: существенно меньше многомиллионных у hls.js, но это другая аудитория. hls.js – дефолт для «мне нужен HLS в браузере»; Shaka – дефолт для «мне нужен DASH, или и то и другое, или офлайн, или Chromecast, или плеер, который не зависит от road map Apple».
Поддержку Shaka в свежей вкладке можно подтвердить двумя строками: shaka.polyfill.installAll() и затем shaka.Player.isBrowserSupported(). Первая ставит полифиллы поверх различий в реализациях MSE и EME в разных браузерах; вторая возвращает true в Chrome, Edge, Firefox, Opera, современном Safari с API ManagedMediaSource, на firmware Chromecast в Web Receiver, в браузере Tizen 2017+, в WebOS с 4.0 и теперь (в v5.1) в TiVo OS и Titan OS. Возвращает false на движках без MSE – то есть в 2026 практически нигде в современном вебе.
Почему эта библиотека вообще существует
В мире браузерного стриминга доминируют две библиотеки: hls.js и Shaka Player. Они появились почти одновременно (hls.js в Dailymotion в 2015, Shaka в Google в 2014–2015) и решают смежные, но разные задачи. hls.js закрывал отсутствие HLS вне Safari – HTTP Live Streaming Apple работал нативно на iPhone и macOS и не работал больше нигде. Shaka делал стек Google – DASH, упакованный Shaka Packager, зашифрованный Widevine, отгруженный через Google Cloud CDN, играющий в YouTube и в Cast Web Receiver – рабочим в открытом вебе. Две библиотеки росли параллельно, с разными взглядами на слои, ABR и роль UI.
Политический подтекст формирует road map. У Shaka нет мнения, расходящегося со спецификацией: когда ISO/IEC выпускает новую ревизию DASH, Shaka следует; когда Apple обновляет HLS Authoring Specification, Shaka следует; когда W3C уточняет Encrypted Media Extensions, Shaka следует. Что Shaka добавляет поверх спек – это операционные вещи: NetworkingEngine, позволяющий перехватить и переписать каждый запрос, слой офлайн-хранилища, который даёт пользователю скачать фильм перед самолётом, мост Chromecast, который выводит тот же контент на большой экран по одной кнопке, и подключаемый AbrManager, который позволяет отгружать алгоритм, на который указывают ваши данные. Где hls.js реализует видение HLS от Apple в браузерах, которые Apple не контролирует, Shaka реализует объединение DASH и HLS на всех браузерах плюс на тех платформах (Chromecast, smart-TV), где открытый веб исторически с трудом доезжает.
Среди продакшен-пользователей библиотеки – Google (Cast Web Receiver SDK загружает Shaka для каждого DASH-стрима, который играет Chromecast), Vimeo, BBC, множество OTT-сервисов через интеграции с JW Player и THEO Technologies, а также всё сообщество smart-TV на Tizen 2017+ через форк Telefónica stv-shaka-player. У репозитория на GitHub около 8 000 звёзд и 1 000 форков по состоянию на май 2026: на порядок меньше, чем у hls.js, но с заметно другим профилем – пользователи Shaka куда чаще оказываются платными OTT-сервисами с DRM, multi-CDN и офлайн-хранилищем, тогда как пользователи hls.js покрывают длинный хвост вебинаров, обучающих видео и небольших live-каналов.
Архитектура в одном абзаце
Работающий экземпляр Shaka Player – это небольшой граф движков, висящих на классе shaka.Player верхнего уровня. Конструктор собирает движки: NetworkingEngine, который владеет каждым исходящим HTTP-запросом через систему «плагин на схему URI»; экземпляр ManifestParser на каждый формат (один для DASH, один для HLS, один для MSS), который превращает байты манифеста во внутренние объекты Variant; DrmEngine, который драйвит браузерный API EME и стоящий за ним CDM; StreamingEngine, который гоняет state machine скачивания сегментов и кладёт байты в MSE SourceBuffer; подключаемый AbrManager, выбирающий следующий вариант; опциональный слой Storage для офлайн-скачивания и опциональный shaka.ui.Overlay, который рисует кнопки и контролы. Вы вызываете player.attach(video), чтобы привязать экземпляр к <video>, потом player.load(url), чтобы начать скачивать манифест, и слушаете события, чтобы драйвить UI. Всё остальное – конфигурация.
Этот абзац – вся картина. Дальше статья увеличивает каждый движок, называет события, которые он эмитит, и говорит, какие настройки имеют значение.
NetworkingEngine
NetworkingEngine оборачивает каждый исходящий HTTP-запрос: получение манифестов, сегментов, лицензий, side-loaded субтитров, синхронизацию серверного времени, офлайн-скачивания. Делает он это через модель «плагин на схему»: каждая URI-схема (http, https, data, blob, любая пользовательская) регистрируется через shaka.net.NetworkingEngine.registerScheme(scheme, plugin), и на схему – не более одного плагина. По умолчанию идут плагины HTTP, HTTPS, data и blob; вы их заменяете, когда нужно добавить OAuth-заголовки, проксировать через сервис подписи токенов или гонять fetch через service worker.
Запрос проходит через три кольца фильтров. Request filter запускается до fetch – это правильное место добавить Authorization-заголовок, переписать хост или подменить URL целиком. Response filter запускается после ответа – там вы расшифровываете обёрнутую FairPlay-лицензию, парсите кастомный конверт ошибки или меняете content-type. Между ними – машинерия ретраев, управляемая через RetryParameters: maxAttempts (по умолчанию 2), baseDelay (1000 мс), backoffFactor (2.0), fuzzFactor (0.5), timeout (30 000 мс), stallTimeout (5 000 мс) и connectionTimeout (10 000 мс). Каждый ретрай ждёт baseDelay × backoffFactor^attempt, fuzz'нутый на ±50%, чтобы парк плееров не штурмовал восстанавливающийся origin одновременно (shaka-project/shaka-player, Network and Buffering Configuration, accessed 2026-05-24).
Логика ретраев заслуживает абзаца, потому что её часто недооценивают в продакшене. На три класса трафика – манифесты, сегменты (streaming) и DRM-лицензии – три отдельных объекта retryParameters. Можно слушать событие retry у движка и вызывать preventDefault(), чтобы отменить дальнейшие попытки на 401 (где проблема – аутентификация, а не транзиентный сбой). В свежих версиях есть флаг infiniteRetriesForLiveStreams, по умолчанию true для live и false для VOD, потому что failure mode'ы разные: 30-секундный обрыв на live'е может сам восстановиться, когда кодировщик догонит, а такой же обрыв на VOD-ассете обычно означает навсегда сломанный URL сегмента (shaka-project/shaka-player, Error Handling tutorial; PR #842, accessed 2026-05-24).
ManifestParser
ManifestParser превращает байты манифеста во внутреннюю модель Shaka – объект shaka.extern.Manifest с массивом Period (только в DASH), внутри каждого – массив Variant (одна на сочетание audio + video + text), а каждый Variant ссылается на Stream с таймлайном сегментов. По умолчанию идут три парсера: DASH (MPD по ISO/IEC 23009-1), HLS (m3u8 по IETF RFC 8216 и Apple HLS Authoring Specification) и Microsoft Smooth Streaming для легаси IIS-серверов. Новый парсер регистрируется через shaka.media.ManifestParser.registerParserByMime – так комьюнити-форк с HESP добавляет свой формат.
DASH-парсер – исходный и более обкатанный. Он понимает multi-period MPD, content steering по DASH-IF, полный набор SegmentTemplate / SegmentList / SegmentBase, новый low-latency механизм availabilityTimeOffset, multi-DRM через <ContentProtection>, несколько аудиодорожек и side-loaded субтитры в WebVTT или IMSC. HLS-парсер появился позже, но догнал: multi-variant плейлисты с EXT-X-STREAM-INF, multi-audio через EXT-X-MEDIA, ключи через EXT-X-KEY и EXT-X-SESSION-KEY, byte-range сегменты, теги Discontinuity, EXT-X-DATERANGE для SCTE-35 ad markers и LL-HLS-расширения (parts, preload hints, blocking reload, rendition reports), которые Apple описала в HLS Authoring Specification.
Важно: после парсинга плеер использует один код-путь. StreamingEngine и AbrManager не знают, из MPD пришёл Variant или из m3u8 – они видят внутренние объекты Variant и гоняют ту же state machine. Это структурная причина, почему Shaka умеет оба формата без раздувания API: формат-специфичная поверхность живёт внутри ManifestParser, а не в остальной части движка.
DrmEngine и EME
DrmEngine – это часть Shaka, отвечающая за Digital Rights Management. Когда ManifestParser сообщает, что Variant декларирует key system – Widevine через com.widevine.alpha, PlayReady через com.microsoft.playready или FairPlay через com.apple.fps, – DrmEngine перехватывает append сегмента в StreamingEngine, вызывает navigator.requestMediaKeySystemAccess с нужным MediaKeySystemConfiguration, открывает MediaKeySession, получает лицензию по URL из drm.servers и подаёт ключ в браузерный Content Decryption Module. StreamingEngine не может класть шифрованные байты, пока не пришёл ключ, – поэтому медленный лицензионный сервер чаще всего и вызывает «чёрный экран без ошибки» на платном стриме.
Стандартная конфигурация в 2026 году – один блок:
player.configure({
drm: {
servers: {
'com.widevine.alpha': 'https://drm.example.com/widevine',
'com.microsoft.playready': 'https://drm.example.com/playready',
'com.apple.fps': 'https://drm.example.com/fairplay',
},
advanced: {
'com.apple.fps': {
serverCertificateUri: 'https://drm.example.com/fairplay/cert',
},
},
},
});Это вся поверхность, которая нужна большинству продакшен-деплоев (shaka-project/shaka-player, DRM Configuration tutorial, accessed 2026-05-24). FairPlay – исключение по синтаксису: ключ-система Apple требует сертификат, выданный сервером, до обмена лицензией; Shaka выставляет это полем serverCertificateUri внутри блока advanced. Для multi-DRM-потока один вызов player.load() работает на все три системы – Shaka выбирает нужную по тому, что поддерживает браузер, и вы не пишете ветвлений в коде.
DrmEngine выставляет и более низкоуровневые швы для кейсов, которые дефолтами не покрываются. Колбэк drm.initDataTransform позволяет переписать PSSH-боксы (байты, идентифицирующие, каким ключом зашифрован сегмент) до того, как они уйдут в CDM – полезно, когда пакейджер и лицензионный сервер расходятся по формату KID. Request filter на NetworkingEngine даёт подписывать запросы лицензий, прикреплять токен сессии или проксировать через сервис ротации ключей. А блок drm.advanced на каждую key system выставляет полную поверхность MediaKeySystemConfiguration – videoRobustness, audioRobustness, persistentState, distinctiveIdentifier – для деплоев, где Widevine должен быть заперт на HW_SECURE_ALL на smart-TV.
Тонкость, специфичная для Shaka: у DrmEngine свои retryParameters, отдельные от ретраев манифестов и сегментов (см. §NetworkingEngine), – поэтому глючный лицензионный сервер откатывается по другому расписанию, чем глючный origin сегментов. В продакшене это важно, потому что для зрителя оба failure mode'а выглядят одинаково («чёрный экран»), а оперативные playbook'и разные: обрыв сегментов – обычно CDN-инцидент, обрыв лицензий – проблема лицензионного сервера или ротации ключей.
StreamingEngine и MSE
StreamingEngine – сердце библиотеки. Он владеет циклом получения сегментов на каждый Variant, вызывает AbrManager, когда пора выбрать новый вариант, кладёт скачанные сегменты в объекты SourceBuffer MSE и обрабатывает все грязные кейсы, которые авторы спецификации оставили клиентам: когда переключать SourceBuffer'ы при seamless quality change (ответ – SourceBuffer.changeType() для кодек-свитчей), когда сбрасывать буфер впереди playhead'а (во время быстрого down-shift, чтобы не тратить трафик на байты, которые пользователь не увидит), когда пропустить отсутствующий сегмент на таймлайне (live-стримы периодически рожают дыры, когда кодировщик перезапускается) и когда наконец объявить фатальную ошибку, а не ретраить бесконечно.
Спецификация MSE-2 (W3C, Media Source Extensions™ 2, Editor's Draft отслеживается на протяжении 2025) добавила более чистую поддержку changeType и appendEncodedChunks, и Shaka использует обе там, где они доступны; на браузерах с MSE-1 StreamingEngine эмулирует то же поведение через аккуратную последовательность remove + appendBuffer. Интересное состояние для мониторинга в продакшене – «stall»: StreamingEngine думает, что добавляет байты, а playhead не двигается дольше stallSkip секунд. Shaka эмитит событие stalldetected и по умолчанию слегка дёргает playhead вперёд, чтобы обойти известный баг браузера, когда MSE молча роняет sample. Дефолты работают почти везде, но ручки есть.
На iOS Safari 17 и выше StreamingEngine может использовать API ManagedMediaSource – подмножество MSE для iPhone Safari от Apple, которое наконец-то даёт JavaScript-плеерам играть DASH на iOS. У ManagedMediaSource более жёсткие правила, когда браузер может забрать память назад, и он требует, чтобы source-элемент был внутри <source> внутри <video>. Shaka детектит его автоматически и роутится через него, когда он есть. В итоге кросс-платформенный стек впервые не требует «iOS-ветки» – но на практике многие команды всё равно предпочитают нативный AVFoundation-путь на iOS, если контент – HLS, потому что нативный путь аппаратно ускорен от начала до конца.
AbrManager
AbrManager – часть Shaka, выбирающая следующий вариант. Реализация по умолчанию – эвристика на основе throughput: отслеживается полоса последних сегментов через экспоненциально взвешенное скользящее среднее, применяется safety factor и выбирается самый высокий вариант, у которого декларированный битрейт ниже safety-скорректированной оценки. Асимметричные safety-факторы (bandwidthDowngradeTarget и bandwidthUpgradeTarget) – намеренные: переключение вниз на джиттере стоит несколько секунд качества, но слишком агрессивное переключение вверх рискует ребуфером, который стоит минут просмотра (shaka-project/shaka-player, Network and Buffering Configuration, accessed 2026-05-24).
В v5.1 приземлились две доработки ABR, на которые стоит обратить внимание. Первая – low-latency-aware ABR: Shaka теперь передаёт хинт «это low-latency стрим?» в AbrManager, чтобы алгоритм мог агрессивнее держаться live-edge, а не отращивать буфер в сторону steady-state. Вторая – dropped-frames-aware ABR: Shaka мониторит HTMLVideoElement.getVideoPlaybackQuality() и подаёт счётчик dropped frames в AbrManager, поэтому smart-TV, не вытягивающий 4K HEVC на 60 fps, упадёт на вариант, который вытягивает железо, вместо того чтобы вечно дёргаться. Оба фичи – дефолтные и не требуют конфигурации (shaka-project/shaka-player, Release v5.1.0, 15 April 2026).
Замена AbrManager – самый чистый шов в плеере. Вы реализуете shaka.extern.AbrManager – init, chooseVariant, enable, disable, segmentDownloaded, playbackRateChanged, getBandwidthEstimate, setVariants, configure – и передаёте фабрику через player.configure({abrFactory: () => new MyAbrManager()}). Так команды отгружают learning-based ABR (Pensieve, Comyco или собственный алгоритм) поверх Shaka без форка библиотеки.
Офлайн-хранилище
Слой офлайн-хранилища Shaka Player – фича, которая резче всего отделяет его от hls.js. Через API shaka.offline.Storage можно скачать манифест, все указанные в нём сегменты и DRM-лицензию, которая их расшифровывает, сложить всё это в IndexedDB браузера и проиграть позже офлайн. Хранение лицензии работает через тип EME-сессии «persistent-license», который поддерживают Widevine и PlayReady на большинстве платформ; офлайн-FairPlay существует, но требует больше прикладной обвязки на iOS (shaka-project/shaka-player, Offline Storage and Playback tutorial, accessed 2026-05-24).
const storage = new shaka.offline.Storage(player);
storage.configure({
offline: {
progressCallback: (content, progress) => console.log(progress),
},
});
const stored = await storage.store(manifestUri).promise;
// потом, даже офлайн:
await player.load(stored.offlineUri);Этих строк хватит, чтобы добавить кнопку «Скачать офлайн» в сервис «кино на дальнемагистральный перелёт». Этот же API питает офлайн-фичу в нескольких OTT-приложениях, отгружающих Shaka через webview-обёртку, а у класса Storage есть свой прогресс и lifecycle, которым управляет приложение.
UI-библиотека и мост Cast
Shaka отгружает опциональную UI-библиотеку отдельным бандлом (shaka-player.ui.js). UI даёт доступные локализованные RTL-aware контролы – play, pause, scrub bar, громкость, меню субтитров, меню аудиодорожек, меню качества, fullscreen, picture-in-picture и кнопку Cast, которая загорается, когда в сети рядом есть Chromecast. UI – opt-in: можно остаться на базовом shaka-player.compiled.js, если у вас свои контролы.
Мост Cast достоин абзаца, потому что он уникален именно для Shaka. Базовая библиотека работает и как sender (страница в браузере пользователя), и как receiver (JavaScript-приложение на firmware Chromecast). Когда пользователь нажимает Cast, Shaka сериализует текущее состояние плеера – URL манифеста, позицию, DRM-конфигурацию, язык, субтитры, ABR-target – на ресивер, который загружает собственный экземпляр Shaka Player и продолжает стрим на телевизоре. Cast Web Receiver SDK от Google уже включает Shaka для DASH-стримов, поэтому в деплое с Shaka на стороне sender'а часто не нужно отгружать собственное receiver-приложение – поставляемый Google ресивер работает «из коробки».
Минимально жизнеспособный плеер на Shaka
Семи строк ниже достаточно, чтобы загрузить DASH- или HLS-стрим в вкладку и начать воспроизведение. Предполагается, что браузер достаточно современный для MSE, а манифест публично доступен.
import shaka from 'shaka-player';
shaka.polyfill.installAll();
if (!shaka.Player.isBrowserSupported()) {
throw new Error('Browser not supported by Shaka Player');
}
const video = document.getElementById('video');
const player = new shaka.Player();
await player.attach(video);
player.addEventListener('error', e => console.error('Shaka error', e.detail));
await player.load('https://cdn.example.com/asset.mpd');Обратите внимание на симметрию API: player.attach(video) привязывает плеер к <video>, player.load(url) начинает скачивать манифест, player.unload() всё освобождает. Тот же код играет .mpd (DASH), .m3u8 (HLS) или .ism (Smooth Streaming) – Shaka выбирает парсер по MIME-типу или расширению. Чтобы добавить UI, меняется одна строка:
const ui = new shaka.ui.Overlay(player, container, video);Это инстанцирует опциональный бандл shaka-player.ui.js и навешивает дефолтную панель управления. Всё остальное (DRM, офлайн, Cast, ABR-override) – вызовы player.configure(), накладываемые поверх этих семи строк.
Разбор задержки на цифрах
Частый вопрос на проекте в 2026: «если мы перейдём с обычного DASH на LL-DASH в Shaka, сколько мы сэкономим glass-to-glass?» Арифметика на одном потоке.
Обычный DASH-стрим: сегменты по 6 секунд, целевой буфер плеера – три сегмента впереди playhead'а.
«Длина сегмента × глубина буфера = 6 с × 3 = 18 с задержки на стороне плеера.»
Добавьте около 4 секунд сетевой и origin-задержки и около 2 секунд упаковки кодировщиком – получаем glass-to-glass:
«18 с + 4 с + 2 с = 24 с.»
Это steady-state на дружелюбной CDN. Нормально для «свадебная трансляция», плохо для «интерактивные хайлайты спортивного матча».
LL-DASH-версия: сегменты по 2 секунды, закодированные как CMAF-чанки по 200 мс, с availabilityTimeOffset, выставленным так, чтобы плеер запрашивал чанк почти сразу после того, как кодировщик его произведёт. Целевой буфер плеера падает примерно до одного сегмента впереди live-edge:
«Длина сегмента × глубина буфера = 2 с × 1 = 2 с задержки на плеере.»
Сетевая и origin-задержка падает примерно до 1 с в LL-DASH, потому что CDN шилдит chunked-transfer-ответы, упаковка кодировщиком – примерно 0,5 с.
«2 с + 1 с + 0,5 с = 3,5 с glass-to-glass.»
Переключатель на стороне Shaka – один вызов конфигурации:
player.configure({
streaming: {
lowLatencyMode: true,
inaccurateManifestTolerance: 0,
rebufferingGoal: 0.01,
},
});Это вся дельта в коде плеера. Работа делается на стороне пакейджера (Shaka Packager, Bitmovin, AWS Elemental или ваш кодировщик) и на стороне CDN (chunked transfer encoding включён на origin'е и не срезается промежуточными кэшами). Для аналогичного LL-HLS-пути с EXT-X-PART и preload hints блок конфигурации идентичен – lowLatencyMode: true переключает поведение для обоих форматов – это одно из структурных преимуществ мультиформатного плеера.
Shaka Player против hls.js – восемь осей выбора
Вопрос «Shaka или hls.js?» прилетает так часто, что сравнительная таблица обязательна. Обе библиотеки отличные, MIT/Apache-лицензии, активно сопровождаются и используются в продакшене серьёзными командами. Они различаются охватом и расширяемостью.
| Ось | Shaka Player | hls.js | Победитель |
|---|---|---|---|
| Охват форматов | DASH + HLS + Smooth | Только HLS | Shaka, если у вас вообще есть DASH |
| Скачивания на npm в неделю (май 2026) | ~205 000 | ~5,8 млн | hls.js – больше экосистема |
| Звёзды на GitHub (май 2026) | ~8 000 | ~16 500 | hls.js – больше комьюнити |
| Охват DRM | Widevine, PlayReady, FairPlay, ClearKey, multi-DRM в одном конфиге | Widevine, PlayReady, FairPlay через drmSystems (v1.3+) | Ничья – оба отгружают multi-DRM в 2026 |
| Офлайн-хранилище | Первоклассное Storage API на IndexedDB | Нет первоклассного офлайна | Shaka – с большим отрывом |
| Chromecast | Зашит в Cast Web Receiver SDK от Google | Cast через Video.js или собственную обвязку | Shaka – из коробки |
| Охват smart-TV | Tizen 2017+, WebOS 4.0+, TiVo OS, Titan OS (v5.1) | Tizen / WebOS через комьюнити-форки | Shaka – официальное покрытие |
| iOS Safari (ManagedMediaSource) | Авто-детект, поддержка с v4+ | Авто-детект с v1.5 | Ничья – оба отгружают |
Правило выбора по умолчанию, которое выпадает из таблицы: берите hls.js, если отгружаете только HLS и хотите самое большое комьюнити и самый маленький бандл; берите Shaka, если у вас вообще есть DASH или если нужны офлайн, Chromecast или smart-TV-охват. Неправильный вопрос – «что лучше?»: они не в одной весовой категории. Правильный вопрос – «что меньший залог на следующие два года?», и ответ зависит от того, что отдаёт ваш пакейджер и какие устройства надо покрыть.
В типичном проекте Фора Софт форма обычно такая: OTT-продукт уже имеет DASH с Widevine для Android-и-веба плюс HLS с FairPlay для iOS, хочет Chromecast с запуска и планирует офлайн-скачивания во втором квартале. Этот стек почти всегда выбирает Shaka, а hls.js мы отгружаем только на редких проектах, где web-часть только HLS и нет планов добавлять остальное. Обратный кейс – обучающая видеоплатформа только на HLS без планов на DRM и Cast – выбирает hls.js ради меньшего бандла и большего отладочного сообщества.
Продакшен-схема восстановления после ошибок
Каждый продакшен-деплой Shaka сталкивается с одним и тем же семейством ошибок. Библиотека их категоризирует через shaka.util.Error.Category – NETWORK, MEDIA, DRM, MANIFEST, STREAMING, TEXT, STORAGE, CAST, PLAYER – и shaka.util.Error.Severity – RECOVERABLE или CRITICAL (shaka-project/shaka-player, Error Handling tutorial, accessed 2026-05-24). Паттерн ниже – то, что мы отгружаем по умолчанию.
player.addEventListener('error', event => {
const error = event.detail;
const { category, code, severity, data } = error;
// Сначала телеметрия — каждая ошибка, recoverable или нет, это data point.
telemetry.track('shaka_error', { category, code, severity });
if (severity === shaka.util.Error.Severity.RECOVERABLE) {
// Shaka уже ретраит. Показываем маленький toast и молчим.
ui.showToast('Переподключение…');
return;
}
// CRITICAL: Shaka сдался. Решаем по категории.
switch (category) {
case shaka.util.Error.Category.NETWORK:
ui.showError('Проблема с сетью. Нажмите, чтобы повторить.');
break;
case shaka.util.Error.Category.MEDIA:
// Декодер: сбрасываем MSE и перезагружаем.
player.unload().then(() => player.load(currentUrl));
break;
case shaka.util.Error.Category.DRM:
// Лицензия: приложение должно переаутентифицировать пользователя.
ui.showError('Сессия истекла. Войдите снова.');
break;
case shaka.util.Error.Category.MANIFEST:
// URL манифеста сломан или ассет снят.
ui.showError('Этот контент сейчас недоступен.');
break;
default:
ui.showError('Воспроизведение не удалось. Нажмите, чтобы повторить.');
}
});Этого listener'а хватает большинству продакшен-деплоев. Обратите внимание на две вещи: каждая ошибка идёт в телеметрию до любого пользовательского действия, и на recoverable-ошибках намеренно ничего пользователю не показывается, потому что Shaka уже ретраит – выкатывать баннер на каждый recoverable-блип сети – самая частая ошибка в код-ревью, которые мы видим. Категории выше также чисто маппятся на четыре семейства операционных playbook'ов, нужных стриминг-продукту: CDN-сетевой playbook, packaging-и-MSE playbook, license-сервер playbook и asset-catalogue playbook.
Что изменилось в v5 и что отгружать в 2026
Линия v5 – текущая для Shaka Player. v5.0 вышел в Q1 2026 с тремя изменениями, которые заметит большинство команд. Экспериментальные авто-переведённые субтитры – выключенные по умолчанию – превращают аудио-дорожку с языком из EXT-X-MEDIA в синтезированную дорожку субтитров на локали зрителя, что даёт мгновенный «минимально жизнеспособный путь к доступности» на контенте без человеческих субтитров. Request filter'ы теперь могут вызываться несколько раз за запрос, что даёт пере-подписывать URL лицензии на ротации токена без перезапуска плеера. И унифицированный API выбора текстовой дорожки теперь принимает null, чтобы отключить субтитры – это убрало старый паттерн ветвлений в прикладном коде.
v5.1.0 вышел 15 апреля 2026 и это линия, на которую стоит метить новые деплои. Три изменения двигают апгрейд. AbrManager теперь получает «low-latency-хинт» от StreamingEngine, и кастомный ABR может различать live и VOD без сниффинга манифеста. AbrManager также видит getVideoPlaybackQuality().droppedVideoFrames, и счётчик dropped frames питает выбор варианта – smart-TV, не вытягивающий 4K HEVC на 60 fps, упадёт на 1080p вместо постоянных дёрганий. И v5.1 добавляет базовую поддержку двух новых smart-TV-платформ – TiVo OS и Titan OS, – закрывая пробел старых версий по длинному хвосту телевизоров (shaka-project/shaka-player, Release v5.1.0, 15 April 2026).
Патч-линия v5.1.4 (текущая опубликованная версия по состоянию на 20 мая 2026) – безопасный выбор. Работа над v5.2 в road map'е на Q3 2026: глубже интегрировать WebCodecs для software-decode фолбэка на браузерах без аппаратного HEVC и переписать слой офлайн-хранилища ради конкурентных скачиваний.
Частые ошибки
Каждый проект ловит небольшой набор тех же проблем со Shaka. Шесть, о которых стоит знать до отгрузки.
Забыть polyfill.installAll. Без него Safari тихо ломается на десятке edge-кейсов (состояние audio context, события fullscreen, детект ManagedMediaSource, Intl.Segmenter для рендеринга субтитров) и симптомы выглядят случайными. Полифилл ставится до создания плеера.
Перепутать порядок attach и load на ретраях. Частый паттерн после CRITICAL-ошибки: await player.unload(); await player.load(url). Работает, потому что attach нужно вызвать один раз на <video>. Вызов attach после unload на том же элементе – либо no-op, либо бросает исключение, в зависимости от версии. Правильно держать attach на lifecycle страницы и делать только unload + load на ретраях.
Показывать recoverable-ошибки пользователю. Shaka эмитит error и на RECOVERABLE, и на CRITICAL. Recoverable означает «я уже ретраю»; выкатывать красный баннер на каждую – значит делать рабочий плеер визуально сломанным. Паттерн выше – toast на recoverable, баннер на critical – это пол.
Включить lowLatencyMode без поддержки пакейджера. Установка lowLatencyMode: true на потоке, где пакейджер не даёт CMAF-чанков (DASH) или EXT-X-PART частей (HLS), заставит Shaka запрашивать несуществующие чанки и зависнуть на старте. Конфиг должен совпадать с пакейджером.
Игнорировать DRM-retry-параметры. Манифест и сегменты – не единственный класс ретраев. Лицензионный сервер – отдельный сервис со своим SLA и своими failure mode'ами; настройте drm.retryParameters отдельно и стройте по ним отдельные графики в QoE-дашборде.
Пытаться отрендерить UI-библиотеку без её CSS. Опциональный бандл shaka-player.ui.js требует controls.css, чтобы рендериться правильно. Самый частый визуальный баг после переключения на UI – «кнопки – это unstyled rectangles», потому что CSS не подключили. И JS, и CSS идут в npm-пакете.
Где здесь Фора Софт
Мы отгружали Shaka Player в продакшен почти во всех стриминг-смежных практиках – OTT- и Internet-TV-каталогах, которым нужны DRM, мультиформат и Chromecast с запуска; e-learning-платформах, где нужны офлайн-скачивания для студентов на неустойчивой связности; surveillance-продуктах, где та же библиотека играет live и архив; и в нескольких telemedicine-продуктах, где нужна EME-class защита клинических записей. По этим вертикалям мы не раз были той инженерной командой, которая собирала плеерный слой от и до (конфигурация, ABR-override, error-телеметрия, доступность). Там, где аудиторию чисто покрывает hls.js – например, вебкаст-платформа с каталогом только в HLS и без ближних DRM-планов – мы рекомендуем hls.js и используем его; правильный плеер – тот, что совпадает с road map'ом проекта, а не тот, у которого больше звёзд на GitHub.
Ключевые выводы
- Shaka Player – open-source плеер от Google; говорит на DASH, HLS и Smooth Streaming через один API.
- Архитектура – граф движков: NetworkingEngine, ManifestParser, DrmEngine, StreamingEngine, AbrManager.
- Multi-DRM, офлайн и Chromecast – первоклассные; это главный отрыв от hls.js.
- lowLatencyMode: true – единственная строка, нужная на стороне плеера для LL-DASH или LL-HLS.
- В v5.1 добавлены dropped-frames-aware ABR и поддержка TiVo OS и Titan OS.
- Берите hls.js на HLS-only деплой; Shaka – если есть DASH, офлайн, Cast или smart-TV.
Что почитать дальше
- hls.js: подробный разбор – другой большой браузерный плеер и дефолт на HLS-only стеках.
- Media Source Extensions (MSE) – API браузера, в который кормит каждый веб-плеер.
- Encrypted Media Extensions (EME) – API, с которым общается DrmEngine.
CTA
- Поговорите со стриминг-инженером – назначьте 30-минутный звонок про ваш плеерный стек.
- Посмотрите наши кейсы – OTT, surveillance, e-learning, telemedicine, отгруженные на Shaka и hls.js.
- Скачайте чек-лист Shaka Player для продакшена – версии, движки, ABR-ручки, LL-* конфиг, DRM-матрица, категории ошибок, частые ошибки.