Веб-воспроизведение: HTML5, MSE, EME, плееры

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

Кратко

Браузер – самый фрагментированный экран, на котором приходится играть вашему over-the-top сервису, и обычный тег HTML5 <video> сам по себе не умеет адаптивный стриминг: ему нужны два браузерных стандарта поверх. Media Source Extensions (MSE) – это стандарт, который позволяет JavaScript кормить плеер скачанными сегментами видео, а Encrypted Media Extensions (EME) – стандарт, который позволяет плееру обращаться к встроенному в браузер дешифрованию для защищённого контента. На каждом браузере, кроме Apple, вы оборачиваете open-source движок – Shaka Player, hls.js или dash.js – поверх MSE и EME; на Safari и iPhone вы обычно отдаёте воспроизведение нативному HLS от Apple, потому что iPhone получил рабочую форму MSE только в конце 2023 года. Эта статья объясняет четыре движущие части простыми словами, чтобы вы могли спроектировать веб-плеер, оценить подрядчика и избежать той самой особенности Apple, которая срывает больше запусков, чем любая другая.

Почему это важно

Если вы запускаете стриминговый сервис в открытом интернете, а не по кабелю, сайт почти всегда – первое место, где новый зритель нажимает play, ещё до того, как установит ваше приложение для телефона или ТВ. Веб – это и тот экран, который чаще всего вас подводит, потому что «браузер» – это на самом деле дюжина разных браузеров с разными правилами о том, что они согласны играть и как дешифруют защищённый контент. Для медиа-основателя или продакт-менеджера ловушка – считать веб простой платформой, потому что «это же просто тег <video>». Это не так: расстояние между веб-плеером, который играет ваш защищённый каталог везде, и тем, что работает в Chrome, но показывает чёрный экран на iPhone, – это недели инженерной работы и одно конкретное правило Apple, которое большинство команд узнаёт на собственных ошибках. Эта статья даёт вам словарь и модель мышления, чтобы принять эти решения до того, как они стоят вам запуска.

Браузер – самый сложный экран, а не самый простой

Начнём с того, что все представляют: элемент HTML5 <video>, тег, который вы вставляете в веб-страницу, чтобы показать видео. Укажите ему один файл – .mp4 фиксированного качества – и браузер скачает его и проиграет. Это прогрессивное скачивание, изначальный способ работы видео в вебе. Для реального сервиса у него тот же фатальный изъян, что и везде: он отдаёт одно качество всем, так что зритель на гостиничном Wi-Fi и зритель на оптике получают идентичный файл, и один из них мучается.

Современный стриминг решает это, нарезая каждый тайтл на множество коротких сегментов – по две–шесть секунд видео – и кодируя весь тайтл несколько раз в разных качествах; этот набор называется encoding ladder (лестница кодирования), и мы разбираем его в статье encoding ladder простыми словами. Маленький текстовый файл – манифест (плейлист .m3u8 для HLS, .mpd для DASH) – перечисляет все качества и все сегменты, а плеер секунда за секундой выбирает, сегмент какого качества скачать следующим. Этот непрерывный выбор называется адаптивный битрейт-стриминг, почти всегда сокращаемый до ABR. Концепции за ним – мозг ABR, буфер, восстановление после ошибок – это тема статьи-компаньона инженерия видеоплеера: ключевые концепции; математика самого алгоритма живёт в нашем разделе Video Streaming, в статье адаптивный битрейт-стриминг.

Вот загвоздка, которая делает веб особенным. Тег <video> сам по себе не делает ничего из этого посегментного переключения. Он играет один прогрессивный файл – или, только в браузерах Apple, один стриминговый формат нативно. Всё, что превращает голый тег в реальный адаптивный плеер, должно быть добавлено на JavaScript через два браузерных стандарта W3C: Media Source Extensions и Encrypted Media Extensions. Остаток статьи – это два этих стандарта, open-source движки, которые на них едут, и особенности браузеров, которые решают, какой путь вы выберете.

Рисунок 1. Два пути к одному экрану. Браузеры Apple могут играть HLS нативно в элементе `<video>`; везде ещё JavaScript-движок кормит элемент через Media Source Extensions.

Media Source Extensions: как JavaScript кормит плеер

Чтобы передать видео элементу <video> из JavaScript – а не просто указать ему один файл – плеер использует стандарт W3C под названием Media Source Extensions (MSE). MSE добавляет на страницу два объекта. MediaSource играет роль источника видео и привязывается к элементу; один или несколько объектов SourceBuffer – это входящие ящики, в которые плеер добавляет скачанные сегменты. Элемент затем играет из того, что лежит в этих буферах. Первая версия MSE стала рекомендацией W3C 17 ноября 2016 года; черновик второй редакции (неформально «MSE версии 2») всё ещё остаётся W3C Working Draft, последняя публикация – 4 ноября 2025 года, так что API стабилен, но активно развивается. Спецификация прямо заявляет свою цель: MSE расширяет media element, чтобы JavaScript мог генерировать медиапотоки, что «упрощает множество сценариев – адаптивный стриминг и сдвиг по времени для live-потоков».

Минимальный набросок показывает форму. Это иллюстрация, а не продакшен-код:

// Привязываем MediaSource к обычному элементу <video>.
const video = document.querySelector('video');
const mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);

mediaSource.addEventListener('sourceopen', () => {
  // По одному SourceBuffer на трек; строка кодека должна совпадать с сегментами.
  const buffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.640028"');

  // Логика ABR решает, какой URL сегмента скачать следующим.
  fetch(nextSegmentUrl())
    .then((r) => r.arrayBuffer())
    .then((bytes) => buffer.appendBuffer(bytes)); // передаём байты плееру
});

Думайте про MSE как про погрузочную платформу за экраном: JavaScript-плеер подгоняет к платформе грузовик свежескачанных сегментов, складывает их в SourceBuffer, а элемент <video> играет со стопки, даже не зная, откуда взялись байты. Три практических факта про эту платформу спасают команды от реальных багов.

Во-первых, у платформы конечный размер. SourceBuffer держит память, и бесконечный append без очистки рано или поздно бросит QuotaExceededError. Поэтому реальный плеер вытесняет уже просмотренное видео – спецификация определяет шаг «coded frame eviction» – держа окно вокруг текущей позиции, а не весь тайтл. Забыть об этом – классическая причина падений в длинных сессиях на устройствах с малой памятью.

Во-вторых, MSE не обещает, что любой кодек проиграется. Прежде чем выбрать качество, плеер вызывает MediaSource.isTypeSupported(), чтобы спросить браузер, может ли тот обработать именно эту комбинацию формата и кодека; MSE намеренно оставляет реальный список кодеков каждому браузеру. Более богатая современная проверка, navigator.mediaCapabilities.decodingInfo() (W3C Working Draft API), идёт дальше и сообщает не только поддерживается ли формат, но и проиграется ли он плавно и энергоэффективно – что важно на ноутбуках и телефонах. Глубокая история про кодеки живёт в стратегии кодеков для OTT.

В-третьих, у MSE появились две новые способности, которые стоит знать по имени. MSE-in-Workers позволяет плееру гонять всю буферизационную машину в фоновом потоке, чтобы загруженный главный поток не дёргал воспроизведение. А ManagedMediaSource – это новый, энергоосознанный вариант, который позволяет самому браузеру решать, когда скачивать и когда вытеснять, вместо того чтобы страница микроменеджила это – и именно эта дверь наконец впустила в эту историю iPhone.

Рисунок 2. Конвейер MSE. JavaScript-движок скачивает и добавляет сегменты в SourceBuffer; браузер декодирует и рендерит из MediaSource; плеер вытесняет просмотренное видео, чтобы остаться под лимитом памяти.

Проблема Apple: нативный HLS и история MSE на iPhone

Это самый важный факт о веб-воспроизведении, и большинство статей его пропускают. Браузеры Apple ведут себя не как остальные, и ошибка здесь – самая частая причина, по которой веб-плеер, идеально работающий в Chrome, показывает чёрный экран на iPhone.

На платформах Apple Safari может играть HLS нативно – вы задаёте источник элемента <video> на URL .m3u8, и медиа-стек операционной системы делает адаптивный стриминг за вас, без всякого JavaScript-движка. Это действительно проще, и поэтому столько сервисов отдают HLS устройствам Apple и доверяют платформе. Сложность начинается, когда вы хотите JavaScript-движок на Apple – для DASH, для своей логики или ради единого кодбейза. Годами версия Safari для iPhone вообще не имела Media Source Extensions, а значит движки на базе MSE там просто не могли работать. Ответом Apple стал ManagedMediaSource, тот самый энергоосознанный вариант. Он впервые вышел в Safari 17.0 на iPad и Mac в сентябре 2023 года и добрался до iPhone в Safari 17.1, выпущенной 25 октября 2023 года.

Деталь, которая срывает запуски, сидит на уровень глубже. Именно на iPhone ManagedMediaSource активируется только когда страница также предлагает AirPlay-альтернативу через <source>, или явно отключает remote playback. Пропустите это условие – и ваш аккуратно собранный MSE-плеер молча не запустится ровно на том устройстве, что держит в руках половина вашей аудитории. Безопасное правило для веба в 2026 году: отдавайте нативный HLS браузерам Apple там, где можете, а когда обязаны гнать JavaScript-движок на iPhone, осознанно подключайте условие «AirPlay-source или отключённый remote playback» и тестируйте на реальном устройстве. Современный движок вроде hls.js уже умеет передавать воспроизведение нативному HLS; протестировать это всё равно придётся вам.

«Частая ошибка: считать, что MSE одинаков во всех браузерах. Команды собирают и тестируют веб-плеер в десктопном Chrome, видят, что он работает, и релизят. На iPhone путь MSE либо недоступен, либо отказывается стартовать без условия AirPlay/remote playback, и зритель получает чёрный прямоугольник. Всегда тестируйте путь Apple на реальном iPhone и реальном Safari, решайте по каждому браузеру, используете ли вы нативный HLS или JavaScript-движок, и никогда не считайте, что десктопный результат обобщается.»

Encrypted Media Extensions: DRM в браузере

Для бесплатного каталога достаточно одного MSE. Для премиум-каталога браузер должен ещё и дешифровать контент, который ему нельзя отдать странице в открытом виде, – и это работа второго стандарта, Encrypted Media Extensions (EME). EME стала рекомендацией W3C 18 сентября 2017 года – первым стандартом про DRM, который W3C вообще опубликовал; черновик второй редакции в работе (Working Draft, 26 ноября 2025). Ключевая идея дизайна в том, что EME сама по себе не система DRM. Это тонкий стандартный набор крючков, который позволяет плееру общаться с тем модулем дешифрования, что есть в браузере, а страница лишь пересылает непрозрачные сообщения между этим модулем и лицензионным сервером. Страница никогда не видит ключи.

Модуль дешифрования называется Content Decryption Module (CDM), и то, какой из них есть в браузере, решает всё. Chrome, Edge на базе Chromium и Firefox несут Widevine от Google; Edge на Windows дополнительно несёт PlayReady от Microsoft; Safari несёт FairPlay от Apple. Единственная система, которую EME требует поддерживать от каждого браузера, – это намеренно небезопасный базовый вариант Clear Key, предназначенный для тестов, а не для защиты реального каталога. На практике ландшафт браузеров ложится на те же три системы DRM, что и везде, и вы отдаёте ту, которую понимает браузер посетителя, – паттерн «encrypt once, license many» (зашифруй один раз, лицензируй многократно), который мы объясняем в multi-DRM: один воркфлоу, любое устройство, построенный на схеме cbcs Common Encryption (ISO/IEC 23001-7). Как EME управляет этим обменом внутри браузера, шаг за шагом, – тема статьи Encrypted Media Extensions и браузерный DRM-стек.

Упрощённый поток короткий. Когда плеер встречает зашифрованные сегменты, элемент бросает событие encrypted. Страница вызывает requestMediaKeySystemAccess(), чтобы подтвердить, что браузер поддерживает нужные DRM и кодеки, создаёт объект MediaKeys на базе CDM, открывает сессию и получает сообщение-запрос лицензии. Она шлёт это сообщение вашему лицензионному серверу, получает лицензию обратно и передаёт её CDM, который теперь может дешифровать. С этого момента защищённые кадры текут к декодеру, и страница ни разу не касается ключа.

Рисунок 3. DRM в браузере через EME. Страница пересылает непрозрачные сообщения между Content Decryption Module браузера и вашим лицензионным сервером; ключи остаются внутри CDM и никогда не видны JavaScript.

Есть последствие для качества, которое основатели недооценивают, и оно не про код. У Widevine три уровня безопасности – L1, L2 и L3 – в зависимости от того, сколько дешифрования происходит внутри защищённого железа. Большинство десктопных браузеров, включая десктопный Chrome, дают только L3, программный уровень. Поскольку студии и крупные сервисы привязывают свои высшие разрешения к аппаратному L1, веб на типичном десктопе для премиум-контента часто урезан ниже HD – тот же тайтл, что стримится в 4K на сертифицированном ТВ, в десктопном браузере может быть ограничен примерно 480p–720p. Это политика правообладателя, а не баг, но она определяет, что вы можете обещать веб-зрителям, и часто становится сюрпризом на студийных security-ревью. Механику уровней безопасности мы разбираем в трёх системах DRM.

Open-source плееры: Shaka, hls.js, dash.js и Video.js

Поскольку MSE и EME – это лишь сырые браузерные крючки, почти никто не подключает их вручную. Вместо этого вы оборачиваете open-source движок плеера, который уже превращает эти крючки в рабочий адаптивный плеер – разбор манифеста, ABR, буферизация, восстановление и плумбинг EME, – и тратите инженерные силы на интеграцию и тюнинг, а не на переизобретение ядра. Четыре имени покрывают подавляющее большинство веба.

Shaka Player (поддерживается Google) – самый широкий из четырёх: играет и DASH, и HLS через MSE, управляет всеми тремя DRM через EME, поддерживает offline-скачивание через хранилище браузера и умеет low-latency. hls.js (проект сообщества video-dev) делает одно дело отлично – играет HLS поверх MSE в браузерах без нативного HLS и чисто передаёт воспроизведение нативному HLS на Safari; он также поддерживает low-latency HLS и DRM через EME и остаётся одной из самых распространённых видеобиблиотек в вебе. dash.js – это референсный плеер DASH Industry Forum для MPEG-DASH; именно на нём сначала обкатывают новое поведение DASH, и он несёт ABR-правила throughput, buffer-based (BOLA) и гибридное (его «dynamic») вместе с multi-DRM через EME. Video.js – белая ворона и самый недопонятый: это в первую очередь UI-фреймворк – скин, элементы управления, экосистема плагинов, – а сам стриминг делает его встроенный движок Video.js HTTP Streaming (VHS), который сам по себе на базе MSE. Выбрать Video.js – значит выбрать UI-слой, а затем стриминговый движок под ним.

Таблица делает покрытие конкретным. «Поддерживается» здесь означает, что движок обрабатывает этот случай напрямую, в 2026 году, согласно собственной документации каждого проекта.

ВозможностьShaka Playerhls.jsdash.jsVideo.js (+ VHS)
Играет HLS (.m3u8)ДаДа (его фокус)НетДа (через VHS)
Играет DASH (.mpd)ДаНетДа (его фокус)Да (через VHS)
Multi-DRM через EMEДа (все три)ДаДаЧерез движок/плагин
Передаёт нативному HLS на SafariОпц.Даn/aДа
Offline-скачиваниеДаНет (add-on)Add-onНет (add-on)
Low-latency стримингДа (LL-HLS/DASH)Да (LL-HLS)Да (LL-DASH)Через движок
Встроенный UI / скинБазовая UI-библиотекаНет (UI ваш)Базовый референс-UIДа (его фокус)
В первую очередь это…полный движокHLS-движокреференс-движок DASHUI-фреймворк

Практическое решение – меньше про фичи и больше про ваши форматы и команду. Если вы отдаёте только HLS и хотите самый компактный, проверенный вариант – hls.js по умолчанию. Если отдаёте DASH – вам нужен Shaka или dash.js. Если отдаёте оба формата из одного кодбейза – Shaka покрывает оба. Если приоритет – отполированный, кастомизируемый интерфейс, и вы готовы положить движок под него, Video.js заслуживает места. Ни один из них не ошибка; ошибка – писать свой.

Небольшой расчёт: скольких зрителей достигает каждый путь?

Числа делают архитектурное решение конкретным. Допустим, ваша веб-аудитория делится примерно как мировой рынок браузеров в середине 2026 года: около 65% Chrome, 18% Safari, 5% Edge, остальное – Firefox и прочие. Теперь спросим, какую долю этой аудитории достигает каждый путь воспроизведения.

Если вы отдаёте только нативный HLS в теге <video>, вы достигаете долю Apple, которая играет HLS нативно – примерно те 18% на Safari, – а все остальные не получают ничего, потому что Chrome, Edge и Firefox не играют HLS нативно. Если вы отдаёте только движок на базе MSE вроде hls.js или Shaka и забываете про условие iPhone, вы достигаете примерно 82% на не-Apple браузерах, но рискуете чёрным экраном на iPhone Safari. Арифметика проста: 18% при «только нативный» или около 82% при «только MSE» против 100%, которые вам реально нужны. Единственный путь, который достигает всех, – комбинированный: MSE-движок для не-Apple браузеров и либо нативный HLS, либо корректно настроенный ManagedMediaSource на Apple. Поэтому любой серьёзный веб-плеер под капотом – двухпутевой. Арифметика тривиальна; урок в том, что ни одного пути недостаточно, и движок, который вы выбираете, должен покрывать оба.

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

Фора Софт с 2005 года строит софт для видеостриминга, OTT и Internet-TV, конференций, e-learning, телемедицины и видеонаблюдения – более 250 завершённых проектов для 400+ клиентов. Веб-воспроизведение в масштабе – ровно та задача фрагментации, под которую мы заточены: защищённый каталог, который должен быстро стартовать и корректно дешифроваться в Chrome, Edge, Firefox и на неудобном пути Apple, для сотен тысяч одновременных зрителей, требует, чтобы плумбинг MSE, интеграция multi-DRM через EME, передача нативному HLS и условие ManagedMediaSource на iPhone были сделаны правильно – а не просто работали в одном десктопном браузере. Мы вендоронезависимы: настраиваем и тюним Shaka, hls.js и dash.js под задачу, а не продаём один движок, и ведём с требования по масштабу и стоимости, а не с перечня фич.

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

  • Тег HTML5 <video> сам по себе не делает адаптивный стриминг; его добавляют MSE и EME.
  • MSE кормит скачанные сегменты в SourceBuffer; плеер обязан вытеснять просмотренное, чтобы избежать ошибки памяти.
  • Браузеры Apple играют HLS нативно; iPhone получил MSE (ManagedMediaSource) только в Safari 17.1, с условием AirPlay/remote playback.
  • EME – это не система DRM, а браузерный крючок к Widevine, PlayReady или FairPlay; страница не видит ключей.
  • Десктопные браузеры обычно дают только Widevine L3, поэтому премиум-веб часто урезан ниже HD.
  • Не пишите плеер: оборачивайте Shaka, hls.js или dash.js и стройте двухпутевое (нативный + MSE) покрытие.

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

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

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