Содержание статьи +
- TL;DR
- Зачем это нужно
- Какую проблему решал EME
- Пять объектов, с которыми вы фактически работаете
- Каноничный обмен лицензией – в рабочем коде
- Три CDM, которые вы поставляете в 2026 году
- Схемы шифрования: `cenc` vs `cbcs` и почему обычно выбирают одну
- Надёжность, статусы ключей и тихое снижение качества
- Сессии, персистентность и офлайн-просмотр
- Где здесь Фора Софт
- Типичные ошибки
- Маленький кусочек арифметики, от начала до конца
- Что меняется в нативном приложении вместо браузера
- Сверяем стандарт с реализациями
- Главное
- Что почитать дальше
- CTA
TL;DR
Encrypted Media Extensions (сокращённо EME) – небольшой веб-стандарт W3C, позволяющий JavaScript-плееру согласовать ключи расшифровки с модулем расшифровки контента (Content Decryption Module, CDM), встроенным в браузер. В 2026 году каждый браузер, воспроизводящий платный фильм, использует EME. Сам API минимален (одна точка входа на основе Promise на navigator, один объект ключей, одна сессия на лицензию), но вокруг него действует множество правил: три конкурирующих CDM (Google Widevine, Apple FairPlay, Microsoft PlayReady) – у каждого свой протокол лицензионного сервера; три уровня robustness, определяющих, будет ли вообще доступен 1080p и 4K; две схемы шифрования (cenc и cbcs), которые нужно выбрать на этапе упаковки; и система уровней безопасности (Widevine L1/L2/L3, PlayReady SL150/2000/3000, FairPlay hardware vs software), чей режим отказа – тихо понизить разрешение до 480p. В статье подробно описывается state-машина EME от начала до конца, перечислены все CDM, с которыми она взаимодействует в 2026 году, показан обмен лицензией в реальном коде и чётко указано, какой параметр изменить, если премиум-контент отказывается воспроизводиться. К концу вы поймёте, почему телефон с Widevine L3 показывает чёрный экран вместо фильма, почему FairPlay требует упаковки cbcs, тогда как Widevine поддерживает обе схемы, и что передать в вызов requestMediaKeySystemAccess(), чтобы запустить multi-DRM с первого раза.
Зачем это нужно
Если вы продаёте доступ к видео в открытом вебе – подписочный стриминг, OTT-приложение, e-learning, телемедицинский портал с записями консультаций или любой продукт, где контент ценнее бесплатного, – вы обязаны использовать DRM. В браузере это означает EME. Контракт студии, разрешающий Netflix или Disney+ показывать фильм в 4K, требует аппаратно-обеспеченной обработки ключей, которую даёт только доверенный CDM в рамках EME. Без него вы либо показываете видео в разрешении 540p, либо не показываете вообще.
После прочтения этой статьи продакт-менеджер сможет задать инженеру правильный вопрос, если лицензионный запрос упадёт по таймауту в 23:00 в день запуска. А senior frontend-инженер получит полную ментальную модель API – включая обновления 2026 года: getStatusForPolicy() для проверки HDCP, capability-детекцию encryptionScheme в requestMediaKeySystemAccess(), новый статус ключа usable-in-future из майского Working Draft 2026 года и enum MediaKeySessionClosedReason, позволяющий отличить сбой CDM от истечения лицензии.
Предварительные знания о DRM не требуются: каждый термин объясняется по мере появления. К концу статьи вы поймёте, почему три CDM – оптимальное число, как W3C разработал стандарт, устраивающий студии без предпочтения одному вендору, и какая однострочная правка в упаковке разблокирует Safari на том же мастере, который уже работает в Chrome.
Какую проблему решал EME
До появления EME у браузеров не было стандартного способа воспроизводить зашифрованное видео. Если студия хотела использовать DRM в вебе, страница загружала плагин – Adobe Flash с его Access DRM, Microsoft Silverlight с PlayReady или вендорский NPAPI-модуль – и весь конвейер видео: сеть, декодирование, рендеринг и расшифровка – обрабатывался вне песочницы браузера, в одном непрозрачном бинарнике. Модель работала, но имела три проблемы, с которыми современный веб уже не мог мириться. Плагины постоянно становились источником уязвимостей, позволяющих выполнить произвольный код; крупные браузеры с 2014 по 2017 год постепенно устраняли поддержку плагинов. На мобильных устройствах они тоже не работали: Apple никогда не позволяла запускать Flash на iPhone, а к 2015 году большинство версий Android поставлялись без него. И, наконец, плагины не давали странице надёжного способа участвовать в обмене лицензией – плеер либо доверял жёстко заданному в плагине URL лицензионного сервера, либо приходилось изобретать костыли, и ни один из этих подходов не масштабировался на множество студий.
Поэтому в 2013 году группа инженеров из Google, Microsoft, Netflix и Apple собралась в W3C и разработала новую спецификацию. Основное правило, которое они установили, было строгим: стандарт браузера описывает лишь интерфейс обмена сообщениями между JavaScript и модулем расшифровки и ничего не говорит о том, как устроен этот модуль, какие алгоритмы он использует и кто выдаёт ему лицензии. Стандарт не упоминает Widevine, FairPlay или PlayReady – он лишь определяет requestMediaKeySystemAccess() и позволяет каждому вендору зарегистрировать свою строку key system. Encrypted Media Extensions стал Recommendation W3C 18 сентября 2017 года (W3C, Encrypted Media Extensions, REC-encrypted-media-20170918), а продолжение – Encrypted Media Extensions (часто называемое EME 2) – с тех пор движется по пути к статусу Recommendation; последняя ревизия на момент написания – Working Draft W3C от 20 мая 2026 года.
Модель оказалась политически удачной и технически чистой. Студии получили защищённый медиа-путь, не требуя от браузеров поддержки конкретного DRM. Браузеры избавились от плагинов. Разработчики приложений получили единый API-интерфейс, который с подходящей хелпер-библиотекой позволяет транслировать один и тот же зашифрованный мастер-контент в Chrome, Edge, Firefox и Safari – при этом под капотом используются три разных CDM.
Пять объектов, с которыми вы фактически работаете
Поверхность EME небольшая. Рабочий плеер взаимодействует примерно с семью методами из пяти объектов: MediaKeySystemAccess, MediaKeys, MediaKeySession, MediaKeyMessageEvent и события encrypted, которое <video> генерирует, когда демультиплексор сталкивается с key id, для которого ещё не существует ключа.
MediaKeySystemAccess – ручка, которую возвращает успешный запрос capabilities. Вы спрашиваете браузер вызовом navigator.requestMediaKeySystemAccess(keySystem, configurations), есть ли у него CDM с поддержкой названного key system (com.widevine.alpha, com.apple.fps, com.microsoft.playready.recommendation) под перечисленными ограничениями (codec strings, схема шифрования, тип сессии temporary vs persistent, тиры robustness для аудио и видео, требования HDCP). Если подходящий CDM есть, Promise разрешается MediaKeySystemAccess, у которого keySystem и getConfiguration() сообщают, какое подмножество ваших ограничений браузер принял. Если нет – Promise отклоняется. Браузеру разрешено торговаться: вы попросили AES-CTR (cenc) и AES-CBC subsample (cbcs), он отдаёт одно из них, но не оба. Всегда проверяйте, что вернулось. Вызов requestMediaKeySystemAccess() – горлышко, через которое проходит каждый multi-DRM плеер.
MediaKeys – JavaScript-прокси для экземпляра CDM, привязанного к конкретному источнику (origin). Создаётся с помощью access.createMediaKeys() на основе ранее полученного MediaKeySystemAccess и подключается к элементу <video> через video.setMediaKeys(keys). После подключения объект ключей получает контроль над CDM в отношении видеоэлемента: все лицензии, ключи и сессии этого плеера становятся его собственностью.
MediaKeySession – это один разговор на уровне страницы с CDM по поводу одной лицензии. Открыть его можно вызовом keys.createSession(type), где type – это temporary (лицензия уничтожается при закрытии вкладки), persistent-license (лицензия сохраняется на диске для офлайн-просмотра, привязана к источнику) или, на уровне спецификации W3C, persistent-usage-record (CDM запоминает факт использования лицензии – полезно для учёта аренды). В рамках сессии происходит сам обмен: вы вызываете session.generateRequest(initDataType, initData), чтобы получить от CDM blob с запросом на лицензию, отправляете его на сервер лицензирования по HTTPS, затем передаёте ответ сервера CDM через вызов session.update(serverResponse) и отслеживаете событие keystatuseschange, чтобы узнать текущий статус каждого ключа: usable, expired, output-restricted, output-downscaled, status-pending, internal-error или – добавленное в черновик спецификации от 20 мая 2026 года – usable-in-future.
MediaKeyMessageEvent срабатывает при каждой сессии, когда CDM пытается отправить сообщение лицензионному серверу. Самое важное поле – event.message, ArrayBuffer, содержащее непрозрачные байты, которые следует передавать без изменений. Это сообщение никогда не парсится: байты представляют собой протокол Widevine, PlayReady или FairPlay, а не ваш собственный. Перечисление (enum) event.messageType указывает тип сообщения – license-request, license-renewal, license-renewal-acknowledgement или individualization-request (последний используется, когда Widevine просит CDM загрузить персональный сертификат устройства перед выдачей настоящей лицензии).
Событие encrypted на элементе <video> – это отправная точка всего процесса. Когда демуксер читает зашифрованный MP4-бокс и обнаруживает PSSH (Protection System Specific Header), для которого ещё нет ключа, он генерирует на видео событие encrypted с двумя полями: initDataType (строка реестра – cenc, keyids или webm) и initData (сами байты PSSH). Ваш обработчик создаёт MediaKeySession и передаёт ему эти байты через generateRequest(). Большинство продакшен-плееров также отслеживают события keymessage, keystatuseschange и новый Promise MediaKeySession.closed, чтобы различать корректно завершённую сессию и ту, которую CDM завершил из-за отзыва сертификата.
Каноничный обмен лицензией – в рабочем коде
Поток от «пользователь нажал Play» до «первый расшифрованный кадр на экране» одинаков в каждом плеере. Девять шагов, всегда в одном порядке.
// 1. Feature-detect и просим CDM, подходящий мастеру.
const KEY_SYSTEM = 'com.widevine.alpha'; // 'com.apple.fps' в Safari; 'com.microsoft.playready.recommendation' в Edge.
const VIDEO_MIME = 'video/mp4; codecs="avc1.640028"';
const AUDIO_MIME = 'audio/mp4; codecs="mp4a.40.2"';
const LICENSE_URL = 'https://drm.example.com/widevine/license';
const config = [{
initDataTypes: ['cenc'],
videoCapabilities: [{ contentType: VIDEO_MIME, encryptionScheme: 'cenc', robustness: 'SW_SECURE_DECODE' }],
audioCapabilities: [{ contentType: AUDIO_MIME, encryptionScheme: 'cenc', robustness: 'SW_SECURE_CRYPTO' }],
sessionTypes: ['temporary'],
persistentState: 'optional',
distinctiveIdentifier: 'optional',
}];
const access = await navigator.requestMediaKeySystemAccess(KEY_SYSTEM, config);
// 2. Строим per-origin MediaKeys и прикрепляем к video.
const mediaKeys = await access.createMediaKeys();
await video.setMediaKeys(mediaKeys);
// 3. Отдаём демуксеру мастер; событие 'encrypted' сработает, когда тот наткнётся на неизвестный PSSH.
video.src = '/streams/protected.mpd';
video.addEventListener('encrypted', async (e) => {
// 4. Открываем временную сессию. Одна сессия на одну лицензию.
const session = mediaKeys.createSession('temporary');
// 5. Слушаем исходящие сообщения CDM.
session.addEventListener('message', async (msg) => {
// 6. Форвардим непрозрачные байты CDM на лицензионный сервер. Не парсим.
const resp = await fetch(LICENSE_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/octet-stream', 'Authorization': bearer() },
body: msg.message,
});
const license = await resp.arrayBuffer();
// 7. Возвращаем ответ обратно в CDM.
await session.update(license);
});
session.addEventListener('keystatuseschange', () => {
for (const [_, status] of session.keyStatuses) {
if (status === 'output-downscaled') warnUser('Премиум-качество заблокировано: вывод недостаточно защищён.');
if (status === 'output-restricted') warnUser('Воспроизведение заблокировано: HDCP не согласован.');
}
});
// 8. Просим у CDM blob лицензионного запроса; событие 'message' срабатывает как следствие.
await session.generateRequest(e.initDataType, e.initData);
// 9. (Опционально) При выгрузке закрываем сессию, чтобы CDM освободил ключи.
window.addEventListener('beforeunload', () => session.close());
});Три ключевые детали, к которым приходит любая рабочая реализация. URL лицензионного сервера специфичен для каждой key system: Widevine, FairPlay и PlayReady не могут использовать один и тот же endpoint, потому что байты в msg.message представляют собой wire-формат соответствующего протокола, а ответ сервера возвращается в том же формате. Вызов update() асинхронный и возвращает Promise, который отклоняется с информативным сообщением, если CDM не может распарсить лицензию – логируйте его, и инженеры поддержки сэкономят часы. Слушатель keystatuseschange – это канал, через который поступают сигналы output-downscaled и output-restricted: это два события, означающие, что «CDM имеет ключ, но отказывается выдавать ключи полного качества из-за неподходящего вывода», и плеер, который их не обрабатывает, будет транслировать фильм в 540p без единой ошибки в консоли.
Три CDM, которые вы поставляете в 2026 году
Стандарт W3C не определяет, какие CDM существуют. На практике важно три, и вы поставляете все три.
Google Widevine – это CDM, поставляемый в Chrome, Chromium Edge, Firefox, Opera и на всех Android-устройствах с Google Mobile Services. Строка key system – com.widevine.alpha. Widevine определяет три уровня безопасности: L1 выполняет всю обработку медиа – расшифровку, декодирование и вывод – внутри аппаратного Trusted Execution Environment (TEE). Именно этот уровень требуется студиями для воспроизведения видео в разрешении 1080p и выше на Android. L2 оставляет расшифровку внутри TEE, но позволяет декодированному видео покидать его границы для обработки обычным GPU. Это самый редкий уровень – на потребительских устройствах сегодня он практически не встречается. L3 выполняет весь CDM в программном режиме, без использования TEE. Именно такой уровень сообщает desktop Chrome на Windows, macOS и Linux почти каждому пользователю. Обычно студии ограничивают воспроизведение на уровне L3 разрешением 480p или 720p. Выбор между L1, L2 и L3 запрашивается браузером через строку robustness в вашем вызове requestMediaKeySystemAccess() – Widevine распознаёт значения SW_SECURE_CRYPTO, SW_SECURE_DECODE, HW_SECURE_CRYPTO, HW_SECURE_DECODE и HW_SECURE_ALL, упорядоченные по возрастанию строгости.
Apple FairPlay Streaming (сокращённо FPS) – это CDM в Safari на macOS, в Safari на iPhone (начиная с iOS 11.2), на iPad, на Apple TV через tvOS и внутри нативных фреймворков macOS и iOS (AVContentKeySession). Строка key system на вебе – com.apple.fps. FairPlay отличается одной архитектурной особенностью: в то время как Widevine и PlayReady поддерживают стандартные схемы MPEG Common Encryption, FairPlay работает только со схемой cbcs (AES-128 CBC, subsample), определённой в ISO/IEC 23001-7:2023. Если вы упаковали мастер-ключ в cenc (AES-CTR) и отправили его в Safari, FairPlay не сможет его расшифровать; если же переупаковать в cbcs, тот же контент будет воспроизводиться на всех CDM (Widevine с версии 16 и PlayReady с 4.0 поддерживают cbcs). Поэтому современный подход к multi-DRM – «упаковать один раз в cbcs и отправить во все три CDM». Форма обмена лицензией на вебе остаётся идентичной – requestMediaKeySystemAccess, encrypted, generateRequest, message, update – но протокол лицензионного сервера отличается: используется формат Apple SPC (Server Playback Context) / CKC (Content Key Context), а не Widevine или PlayReady. Apple App ID и сертификат FairPlay выдаются Apple через программу FairPlay developer programme.
Microsoft PlayReady – это CDM в браузере Chromium Edge на Windows, на консолях Xbox One и Series, в большинстве smart TV (Tizen, webOS, Vidaa) и в десятках set-top box, построенных кабельной индустрией на базе PlayReady. Современная строка key system – com.microsoft.playready.recommendation; старая com.microsoft.playready устарела и не обеспечивает поддержку уровня безопасности SL3000. У PlayReady своя система уровней защиты: SL150 – для незащищённого тестового контента, SL2000 – для программного (software) шифрования (максимум 1080p у большинства студий) и SL3000 – для аппаратно изолированной защиты, при которой CDM работает в доверенной среде выполнения (TEE) процессора. PlayReady SL3000 – ключевой уровень, разблокирующий 4K UHD в Edge на Windows для Netflix и на Xbox для всех студий. Для SL3000 требуется аппаратная поддержка DRM на стороне клиента и серверный SDK версии 3.0.2769 или выше. Рекомендации dash.js / Shaka Player гласят: «всегда сначала запрашивай com.microsoft.playready.recommendation и переходи к простому com.microsoft.playready только если recommendation недоступен», поскольку именно recommendation активирует SL3000.
| CDM | Строка key system | Браузеры / платформы | Схемы шифрования | Тиры безопасности | Кто выдаёт |
|---|---|---|---|---|---|
| Google Widevine | com.widevine.alpha | Chrome, Edge, Firefox, Opera, Android, ChromeOS, Chromecast | cenc, cbcs | L1 / L2 / L3 | Google (бесплатно) |
| Apple FairPlay Streaming | com.apple.fps | Safari (macOS, iOS, iPadOS), Apple TV, нативные iOS/macOS | только cbcs | hardware (Secure Enclave) / software | Apple developer programme |
| Microsoft PlayReady | com.microsoft.playready.recommendation | Edge на Windows, Xbox, Tizen, webOS, Vidaa, STB | cenc, cbcs | SL150 / SL2000 / SL3000 | Microsoft (платно) |
Табл. 1. Три CDM, актуальные в 2026 году, строки для requestMediaKeySystemAccess() и платформы, которые они разблокируют.
Формулировка Wikipedia о «четырёх CDM» обычно включает в качестве четвёртого обязательный Clear Key от W3C. Clear Key – это минимальный CDM, который должен поддерживать каждый браузер, соответствующий стандарту; он принимает ключи в открытом виде через JSON и предназначен для тестирования и редких сценариев без защиты. Платящим пользователям Clear Key не предоставляется; его используют для локальной разработки с Shaka или dash.js, прежде чем подключить плеер к настоящему лицензионному серверу.
Схемы шифрования: `cenc` vs `cbcs` и почему обычно выбирают одну
EME ничего не шифрует. Шифрование происходит на этапе упаковки, когда энкодер или пакер преобразует закодированный мастер в сегменты и записывает метаданные Common Encryption в каждый MP4-бокс. Стандарт Common Encryption ISO/IEC 23001-7 (актуальная редакция – 2023 год) определяет четыре схемы защиты: cenc (AES-128 в режиме счётчика, полный образец), cens (AES-128 в режиме счётчика, частичный образец), cbc1 (AES-128 в режиме шифрования с обратной связью по блоку, полный образец) и cbcs (AES-128 CBC, частичный образец, с паттерновым шифрованием). В продакшене 2026 года используются только две из них: cenc и cbcs.
Разделение существует по одной архитектурной причине: FairPlay построен на основе AES-CBC, в то время как остальная индустрия перешла на AES-CTR, а эти режимы шифрования несовместимы на уровне байтов. Долгое время это означало, что мастер, упакованный для Widevine и PlayReady (cenc, CTR), приходилось заново упаковывать для FairPlay (CBC). Ревизия ISO/IEC 23001-7 2016 года ввела cbcs – CBC-Subsample-with-Pattern, которую смогли реализовать и Apple, и другие платформы, – и к 2020 году каждый крупный CDM поддерживал cbcs. Сегодня правило простое: упакуйте один раз в cbcs – и один и тот же мастер будет работать в Widevine, FairPlay и PlayReady. Упакуйте в cenc – вы ничего не выиграете и потеряете поддержку Safari. Единственная современная причина держать отдельный cenc-мастер – наличие устаревших set-top box, прошивки которых не обновлялись до выпуска cbcs-совместимых версий; если ваш парк устройств – только браузеры или современные smart TV, ответ однозначен: cbcs.
Сторона EME в этой схеме – это член encryptionScheme словаря MediaKeySystemMediaCapability, который вы передаёте в requestMediaKeySystemAccess(). Атрибут был добавлен в рамках работ по поддержке спецификации после 2017 года специально для того, чтобы плеер мог во время выполнения определить, поддерживает ли CDM cbcs, ещё до фиксации стратегии упаковки. W3C перечисляет хорошо известные значения cenc, cbcs, cbcs-1-9 и cens в последнем Working Draft; на практике вы запрашиваете cbcs и переключаетесь на cenc, если cbcs не поддерживается (в браузерах 2026 года это происходит практически никогда).
const config = [{
videoCapabilities: [
{ contentType: 'video/mp4; codecs="avc1.640028"', encryptionScheme: 'cbcs' },
{ contentType: 'video/mp4; codecs="avc1.640028"', encryptionScheme: 'cenc' }, // fallback
],
audioCapabilities: [{ contentType: 'audio/mp4; codecs="mp4a.40.2"', encryptionScheme: 'cbcs' }],
sessionTypes: ['temporary'],
}];
const access = await navigator.requestMediaKeySystemAccess('com.widevine.alpha', config);
const used = access.getConfiguration().videoCapabilities[0].encryptionScheme; // 'cbcs' на современном CDMНадёжность, статусы ключей и тихое снижение качества
Самая частая жалоба в продакшене на EME-плеер: ассет воспроизводится в 540p, хотя должен быть в 1080p, а в консоли – тишина. Искомый сигнал – статус по ключу в событии keystatuseschange, а причина почти всегда в несовпадении между robustness, который вы запросили, и robustness, который устройство может предоставить.
Map keyStatuses на MediaKeySession – приговор CDM по каждому ключу. Значения, определённые W3C на момент Working Draft от 20 мая 2026 года: usable, expired, released, output-restricted, output-downscaled, status-pending, internal-error и новый usable-in-future. usable – то, чего вы хотите. output-downscaled – CDM говорит: «ключ у меня есть, но я расшифрую только нижние рендиции потока, потому что путь вывода не соответствует политике HDCP студии». output-restricted – то же сообщение, но эскалированное: на этом выводе вообще ничего не будет проигрываться. usable-in-future – новое: лицензия, действительная только в пределах арендного окна, может сообщить «ключ у меня, но использовать пока нельзя», не заставляя плеер выдавать ошибку и перезапрашивать ключ.
Способ избежать тихого падения – честно указывать уровень robustness на входе. В Widevine значимы следующие значения: SW_SECURE_CRYPTO (software-крипто, без TEE; это L3), SW_SECURE_DECODE (software-decode, software-крипто), HW_SECURE_CRYPTO (hardware-крипто, software-decode; класс L2), HW_SECURE_DECODE (hardware-decode и крипто; L1 на устройствах с TEE-видеоконвейером) и HW_SECURE_ALL (hardware-крипто, decode и output; L1 с secure path). В PlayReady эквиваленты – 150, 2000 и 3000 для трёх уровней безопасности. В FairPlay веб-API вообще не выставляет строку robustness – CDM договаривается внутренне с Secure Enclave, и вы получаете то, что предоставляет платформа.
Прагматичный рецепт multi-DRM плеера – передать браузеру небольшую последовательность значений robustness сверху вниз, чтобы он выбрал наиболее подходящий тир, доступный устройству:
const widevineConfig = [{
videoCapabilities: ['HW_SECURE_ALL', 'HW_SECURE_DECODE', 'SW_SECURE_DECODE'].map(r => ({
contentType: 'video/mp4; codecs="avc1.640028"',
encryptionScheme: 'cbcs',
robustness: r,
})),
}];Если устройство возвращает HW_SECURE_ALL, ваш лицензионный сервер может выдавать ключи для рендеринга 4K; если возвращает SW_SECURE_DECODE – policy-движок на сервере должен выдавать только ключи для рендеринга 720p и ниже. Контракт обеспечивается на стороне сервера, а не в плеере – плеер лишь сообщает, какой уровень был фактически выдан.
Хелпер 2026 года для этого цикла – MediaKeys.getStatusForPolicy(), позволяющий странице проверить, как CDM отреагирует на заданный минимальный уровень HDCP, не открывая сессию и не запрашивая реальную лицензию. Передаётся { minHdcpVersion: '2.2' }, и Promise разрешается одним из тех же значений key-статуса. Плеер может использовать это на экране приветствия, чтобы предупредить: «ваш телевизор подключён по HDMI 1.4, а наши фильмы требуют HDCP 2.2; смените кабель или используйте другое устройство» – ещё до нажатия кнопки «Воспроизвести».
| Статус ключа | Смысл | Что должен сделать плеер |
|---|---|---|
| usable | CDM имеет ключ, готов декодировать сейчас. | Продолжить воспроизведение. |
| expired | Ключ был валиден; окно лицензии прошло. | Перезапросить лицензию; показать «обновление доступа», если идёт более секунды. |
| released | Persistent-сессия выпустила ключ через remove(). | Пересоздать сессию. |
| output-restricted | CDM не выдаёт ключи: output небезопасен. | Показать «Не воспроизводится на этом выводе (HDCP)»; предложить другое устройство. |
| output-downscaled | CDM выдаёт ключи только для нижних рендиций. | Зажать ABR на максимальной разрешённой; показать «играем в сниженном качестве». |
| status-pending | CDM ещё не оценил ключ. | Подождать; не ретраить. |
| internal-error | CDM встретил внутренний сбой. | Закрыть сессию, пересоздать; залогировать. |
| usable-in-future | Ключ загружен, но окно аренды ещё не открылось. | Показать таймер; не ретраить. (Добавлен в W3C WD 2026-05-20.) |
Табл. 2. Восемь статусов ключа: что они означают и какие действия должен выполнить плеер.
Сессии, персистентность и офлайн-просмотр
Третья ось EME – после выбора CDM и схемы шифрования – это тип сессии. MediaKeySession создаётся с одной из трёх строк-типов, определённых W3C: temporary, persistent-license и persistent-usage-record. Выбор определяет, будет ли лицензия действовать после перезагрузки страницы, а также хранит ли CDM состояние на диске.
Temporary-сессия – это значение по умолчанию и самый простой вариант. CDM загружает ключи в память, расшифровывает сегменты по мере воспроизведения и удаляет всё при закрытии страницы или при выполнении Promise close() сессии. Такой подход подходит для прямых трансляций, короткого on-demand контента и любых случаев, когда пользователь онлайн, а лицензию можно легко перезапросить. Подавляющее большинство трафика EME в продакшене – temporary.
Persistent-license-сессия просит CDM записать лицензию в origin-scoped хранилище на диске, чтобы тот же контент можно было воспроизводить офлайн позже. Это модель за кнопкой Netflix «Available for download» и за каждым предполётным видеолокером авиакомпании. Чтобы открыть persistent-сессию, браузер должен разрешать persistent-state по origin: член persistentState вашего конфига requestMediaKeySystemAccess() должен включать required, и user agent имеет право показать permission-промпт. Когда пользователь хочет контент офлайн, плеер вызывает keys.createSession('persistent-license'), гоняет обычный обмен лицензии, и CDM пишет ключи на диск под квоту origin. Когда устройство ушло в офлайн и пользователь открыл ассет, плеер достаёт ту же сессию по её ID через session.load(sessionId) вместо createSession(), и CDM отдаёт закэшированные ключи без хождения в сеть.
Persistent-usage-record-сессия – более редкий вариант. CDM не хранит саму лицензию, но фиксирует факт её использования с временными метками и идентификатором ключа. Благодаря этому аренда-аккаунтинг может быть криптографически подтверждена: студия может проводить аудит – кто, что и когда смотрел. W3C выделяет этот тип сессии как отдельный в §6.7 Working Draft; в продакшене он в основном применяется в OTT-аренде и академических стриминговых платформах, где требуется отслеживание авторских прав.
Два режима ошибки, которые стоит знать. Первый – квота хранилища. CDM хранит лицензии под persistent-квоту origin, и в крупных браузерах квота ограничена – когда у пользователя накопилось несколько сотен persistent-лицензий, новые вызовы createSession('persistent-license') начинают падать с QuotaExceededError. Плеер, поставляющий офлайн, должен вызвать keys.getStatusForPolicy() (теперь широко доступен) и navigator.storage.estimate(), чтобы давление по хранилищу было видимо пользователю, и должен поддерживать UI «очистить скачанное», вызывающий session.remove() на ненужных лицензиях. Второй – закрытие сессии. Promise MediaKeySession.closed (добавлен в Working Draft 2026 вместе с enum MediaKeySessionClosedReason) резолвится с причиной: internal-error, closed-by-application, release-acknowledged, hardware-context-reset, resource-evicted. Плеер, следящий за этим Promise, может отличить штатное user-driven закрытие от падения CDM и запустить правильный путь восстановления.
Где здесь Фора Софт
Фора Софт внедряет EME-воспроизведение в продукты видеостриминга с тех пор, как Recommendation W3C стал практически применимым в 2018 году. Наши инженерные команды разрабатывают multi-DRM-стэки для заказчиков из сфер OTT, e-learning, телемедицины и видеонаблюдения на платформах Widevine, FairPlay и PlayReady.
На каждом новом продукте мы придерживаемся следующего ритма внедрения: с первого дня упаковываем мастер-стрим в cbcs, нормализуем воспроизведение на уровне плеера через Shaka Player или hls.js, интегрируем один из multi-DRM-сервисов лицензирования (или развёртываем self-hosted Axinom или castLabs, если заказчику необходимо держать путь лицензирования внутри собственной инфраструктуры) и инструментируем каждое событие keystatuseschange в QoE-пайплайн, чтобы падения до 540p без ошибок сразу отображались в дашборде – раньше, чем попадут в тикет поддержки.
В тех случаях, когда заказчики используют офлайн-режим (распространённый сценарий в пилотном обучении и корпоративном обучении), мы с самого начала привязываем persistent-license сессии к интерфейсу с учётом квот: ретрофит истории квот после запуска – слишком дорогой путь.
Типичные ошибки
Шесть режимов отказа охватывают большинство багов EME, с которыми мы столкнулись за более чем семь лет работы с multi-DRM.
Упаковка только в cenc и обнаружение на запуске, что Safari не играет. Чинится в пакеджере: переключиться на cbcs, перешифровать, перезалить. Каждый современный плеер договаривается о cbcs на Widevine и PlayReady; только FairPlay ограничен им.
Отправка одного URL лицензионного сервера на все CDM. Widevine общается с Widevine, PlayReady – с PlayReady, FairPlay – с SPC/CKC. Конечные точки выглядят похоже на HTTP, но не взаимозаменяемы. Используйте мульти-DRM сервис лицензирования или три отдельных endpoint с общим слоем политики upstream.
Забыть, что Edge нужен com.microsoft.playready.recommendation, а не com.microsoft.playready. Простая строка key system предшествует SL3000, и браузер направляет её на старый CDM; запрашивайте recommendation первым, и ваш 4K-ассет разблокируется в Edge на Windows.
Не показывать output-downscaled. Пользователи не подают баг-репорты на enum CDM – они отписываются. Пробрасывайте key-status события в user-visible баннер («Премиум-качество требует сертифицированного устройства вывода») и в QoE-дашборд.
Не интерпретировать байты сообщения иначе, чем непрозрачные данные. event.message – wire-формат CDM. Передавайте как есть. Не логируйте (может утекать идентификатор устройства). Не преобразовывайте. Не повторяйте попытки на стороне клиента; пусть сервер возвращает ошибку и сам выполняет повторные попытки.
Путать события encrypted и keymessage. encrypted срабатывает на <video>, когда демуксер натыкается на неизвестный PSSH; message – на MediaKeySession, когда CDM имеет байты для лицензионного сервера. Подключение слушателей не к тем объектам – распространённая ошибка copy-paste, из-за которой плеер открывает сессии, но никогда не отправляет лицензионные запросы.
«Pitfall. Widevine-конфиг с robustness: 'HW_SECURE_ALL', который не проходит на Chromebook, – это не ошибка; Chromebook сообщает, что не может обеспечить hardware-secure pipeline для запрошенного ассета. Перехватите отклонение Promise requestMediaKeySystemAccess() и повторите запрос с 'SW_SECURE_DECODE' – не оставляйте пользователя без попытки на нижнем уровне.»
Маленький кусочек арифметики, от начала до конца
Вот back-of-envelope стоимость забывания о robustness. Представьте OTT-продукт с 10 миллионами DAU, из которых 3 миллиона пытаются посмотреть 4K-тайтл. Допустим, 20% этих пользователей – 600 тысяч – на устройствах, рапортующих только SW_SECURE_DECODE и тихо получивших 720p-поток, потому что плеер не объявил лестницу robustness.
Сначала стоимость стриминга. 4K-рендиция в среднем 18 Mbps; 720p – 4 Mbps. 600 000 пользователей по 90 минут на 4 Mbps это 600 000 × 90 × 60 × 4 / 8 = 1 620 000 000 мегабайт ≈ 1,62 петабайт сэкономленных в день относительно тех же пользователей на 18 Mbps. При типичной крупномасштабной CDN-цене $0,005 за GB это $8 100 в день, около $3 миллионов в год, которые оператор не тратит на egress.
Теперь вычтите стоимость потери качества. Если 1% этих 600 тысяч заметит потолок 720p и отпишется при $10 ARPU в месяц, это 6 000 × $10 × 12 = $720 000 в год потерянной подписочной выручки. Арифметика балансирует в пользу оператора – но только в порядке величины и только потому, что пример предположил 1% churn. При 5% churn оператор уже теряет $0,6 млн в год за поставку плеера с тихим даунскейлом.
Число, на которое математика на самом деле указывает – не «поставлять 720p» или «поставлять 4K», а «поставлять плеер, который знает, какой титр может поддержать устройство, и честно говорит об этом пользователю». Такой плеер получает и экономию трафика на устройствах с низкой производительностью, и удержание аудитории на мощных устройствах.
Что меняется в нативном приложении вместо браузера
EME – это браузерная технология. Те же три CDM существуют и вне браузера, но у каждого из них свой нативный API.
В iOS и macOS приложениях FairPlay работает через AVContentKeySession фреймворка AVFoundation. Процесс почти изоморфен EME-потоку: приложение получает запрос ключа от фреймворка, передаёт SPC на сервер ключей, получает CKC и возвращает его обратно – но API на Objective-C / Swift, жизненный цикл сессии управляется фреймворком, а байты протокола сервера ключей – те же SPC/CKC, что и в вебе. Сессия WWDC 2018 №507 «AVContentKeySession Best Practices» по-прежнему остаётся каноническим руководством по офлайн-окну аренды и паттерну dual-экспайра.
На Android Widevine работает через MediaDrm в платформенном медиа-стеке. ExoPlayer скрывает почти всё за DefaultDrmSessionManager. Протокол лицензионного сервера использует тот же wire-формат Widevine, что и в браузере; уровни безопасности запрашиваются через MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL и связанные компоненты – той же последовательностью HW_SECURE_ALL / HW_SECURE_DECODE / SW_SECURE_DECODE, что и в браузере.
В нативных Windows-приложениях PlayReady работает через PlayReadyContentResolver и более широкий PlayReady DRM Client SDK. Различия между SL150, 2000 и 3000 отображаются на один и тот же контракт устойчивости; требования к серверному SDK остаются на уровне базовой версии v3.0.2769.
Для наших целей – статьи в блоке player engineering с акцентом на браузер – практический вывод таков: EME-поток, который вы реализуете в браузере, является наиболее переносимой ментальной моделью из всех четырёх. Освоив EME один раз, нативные пути вы будете воспринимать как вариации одного и того же подхода.
Сверяем стандарт с реализациями
Когда источники расходятся – побеждает спецификация. Вот два примера, которые стоит знать.
Текущий черновик W3C – Encrypted Media Extensions от 20 мая 2026 года (W3C, working draft, https://www.w3.org/TR/encrypted-media-2/). В нём содержится актуальный список состояний ключей (включая usable-in-future) и перечисление MediaKeySessionClosedReason. Последняя Recommendation – наиболее авторитетная форма документа W3C – остаётся сентябрьской 2017 года (W3C, Encrypted Media Extensions, REC-encrypted-media-20170918). Несколько популярных постов описывают getStatusForPolicy() как «скоро»; он появился в Chrome в 2018 году и теперь вошёл в нормативный текст черновика в разделе §6.6.
Текущий стандарт Common Encryption – ISO/IEC 23001-7:2023. Редакция 2016 года впервые ввела схему cbcs; несколько старых постов описывают cbcs как Apple-only. К 2026 году каждый совместимый CDM будет поддерживать cbcs, и практическое правило – «упаковывайте в cbcs». Если вендорская документация, которую вы нашли, говорит обратное – проверьте дату публикации.
Главное
- EME – это тонкий API передачи сообщений от W3C; CDM отвечает за расшифровку, а спецификация не указывает конкретного поставщика DRM.
- Три CDM, используемых в продакшене: Google Widevine, Apple FairPlay и Microsoft PlayReady; для поддержки открытого веба необходимо обеспечить совместимость со всеми тремя.
- Шифрование выполняет пакеджер, а не EME; используйте упаковку в cbcs, чтобы одним мастер-ключом покрыть все CDM.
- Уровень защищённости, требования к безопасности и статус HDCP определяют, какое качество видео выдаст CDM; показывайте пользователям информацию из output-downscaled и output-restricted.
- Используйте getStatusForPolicy() для предварительной проверки поддержки HDCP; применяйте keystatuseschange, чтобы реагировать на изменения во время воспроизведения.
- Постоянные (persistent) сессии позволяют просматривать контент в офлайн-режиме; интегрируйте их с интерфейсом, учитывающим квоту, до запуска воспроизведения, а не после.
Что почитать дальше
- Media Source Extensions (MSE): как устроен любой веб-плеер изнутри
- DRM 101: почему существует три системы и почему нужно поддерживать все три
- Common Encryption (CENC) подробно
CTA
Выберите один из трёх вариантов:
- Поговорить со streaming-инженером – 30-минутный звонок по обсуждению задач с лидом по player-инжинирингу Фора Софт. → Записаться
- Посмотреть наши кейсы – решения для multi-DRM, OTT, e-learning и телемедицины в продакшене. → Кейсы
- Скачать шпаргалку по EME multi-DRM – одностраничный PDF с key system, схемами шифрования, уровнями безопасности и типичными ошибками. → Скачать