Содержание статьи +
- TL;DR
- Зачем это нужно
- Какую проблему решал EME
- Пять объектов, с которыми вы фактически работаете
- Каноничный обмен лицензией – в рабочем коде
- Три CDM, которые вы поставляете в 2026
- Схемы шифрования: `cenc` vs `cbcs` и почему обычно выбирают одну
- Robustness, статусы ключей и тихое падение качества
- Сессии, персистентность и офлайн-просмотр
- Где здесь Фора Софт
- Типичные ошибки
- Маленький кусочек арифметики, от и до
- Что меняется в нативном приложении вместо браузера
- Сверяем стандарт с реализациями
- Главное
- Что почитать дальше
- 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-модуль – и плагин гонял весь конвейер видео вне песочницы браузера: сеть, декодирование, рендеринг и расшифровка – всё в одном непрозрачном бинарнике. Модель работала, но имела три проблемы, с которыми современный веб жить не мог. Плагины были постоянным источником RCE-уязвимостей; крупные браузеры с 2014 по 2017 год активно удаляли поддержку плагинов. Плагины ломались на мобильных: Apple никогда не пускала Flash на iPhone, а Android к 2015 году у большинства операторов поставлялся без него. И плагины не давали странице чистого способа участвовать в обмене лицензией – плеер либо доверял зашитому в плагин 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 track; последняя ревизия на момент написания – 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-прокси для per-origin экземпляра CDM. Создаёте его через access.createMediaKeys() на полученном выше MediaKeySystemAccess и прикрепляете к элементу <video> через video.setMediaKeys(keys). После прикрепления keys-объект владеет видением CDM на video-элемент: каждая лицензия, каждый ключ и каждая сессия этого плеера принадлежат ему.
MediaKeySession – один разговор страницы с CDM об одной лицензии. Открываете его вызовом keys.createSession(type), где type – это temporary (лицензия умирает с вкладкой), persistent-license (лицензия хранится на диске для офлайн-просмотра, scoped по origin) или, на уровне W3C, persistent-usage-record (CDM запоминает факт использования лицензии – полезно для учёта аренды). В сессии происходит сам обмен: вы вызываете session.generateRequest(initDataType, initData), чтобы попросить у CDM blob лицензионного запроса, форвардите его на лицензионный сервер по HTTPS, вызываете session.update(serverResponse) с тем, что сервер прислал, и слушаете событие keystatuseschange, чтобы понять, каков теперь статус каждого ключа: usable, expired, output-restricted, output-downscaled, status-pending, internal-error или – добавленный в Working Draft от 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 скачать свой per-device сертификат, прежде чем выдавать настоящую лицензию).
Событие 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 выполняет всё медиа-handling – расшифровку, декодирование, вывод – внутри аппаратного Trusted Execution Environment (TEE), и это то, что студии требуют для 1080p и выше на Android. L2 оставляет расшифровку в TEE, но позволяет декодированному видео выйти за его пределы для декодирования обычным GPU; самый редкий тир, на потребительских устройствах сегодня почти не встречается. L3 гоняет весь CDM в software, без 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 playbook – «упакуй один раз в 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 deprecated и не даёт пути к capability SL3000. У PlayReady собственная система уровней безопасности: SL150 для незащищённого тестового контента, SL2000 для software-защищённого (потолок 1080p у большинства студий) и SL3000 для аппаратно-закреплённой защиты, где CDM работает в TEE процессора. PlayReady SL3000 – тир, разблокирующий 4K UHD на Edge на Windows для Netflix и на Xbox для каждой студии. SL3000 требует hardware-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, который должен реализовать каждый соответствующий стандарту браузер; принимает plaintext-ключи через JSON и предназначен для тестирования и редких сценариев без защиты. Платящим пользователям Clear Key не поставляют; его используют для локальной разработки против Shaka или dash.js, прежде чем направить плеер на настоящий лицензионный сервер.
Схемы шифрования: `cenc` vs `cbcs` и почему обычно выбирают одну
EME ничего не шифрует. Шифрование происходит на этапе упаковки, когда энкодер или пакеджер превращает закодированный мастер в сегменты и пишет метаданные Common Encryption в каждый MP4-бокс. Стандарт Common Encryption ISO/IEC 23001-7 (актуальна редакция 2023 года) определяет четыре protection schemes: cenc (AES-128 в counter mode, full-sample), cens (AES-128 counter, subsample), cbc1 (AES-128 cipher-block-chaining, full-sample) и cbcs (AES-128 CBC, subsample, с pattern encryption). В продакшене в 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-мастер – флот legacy set-top-box, чьи прошивки написаны до того, как вендор выпустил cbcs-aware обновление; если ваш флот только браузерный или современные smart-TV, ответ – cbcs.
Сторона EME в этой картине – член encryptionScheme словаря MediaKeySystemMediaCapability, который вы передаёте в requestMediaKeySystemAccess(). Атрибут добавили в пост-2017 maintenance-работе над спецификацией именно для того, чтобы плеер мог в runtime обнаружить поддержку cbcs у CDM до фиксации стратегии упаковки. 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' на современном CDMRobustness, статусы ключей и тихое падение качества
Самая частая продакшен-жалоба на EME-плеер: ассет играет в 540p там, где должен играть в 1080p, и в консоли ничего нет. Сигнал, который вы ищете – per-key статус в событии 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 говорит: «у меня есть ключ, но я расшифрую только нижние рендиции потока, потому что output-путь не удовлетворяет политике 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-status. Плеер может использовать это на splash-экране, чтобы предупредить: «ваш ТВ подключён по HDMI 1.4, а наши фильмы требуют HDCP 2.2; смените кабель или другое устройство», ещё до нажатия Play.
| Статус ключа | Смысл | Что должен сделать плеер |
|---|---|---|
| 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 не хранит саму лицензию, но записывает факт её использования с timestamps и key id. Так аренда-аккаунтинг может быть криптографически подтверждена – студия может аудитить, кто что смотрел и когда. W3C перечисляет это как отдельный тип сессии в §6.7 Working Draft; продакшен-распространение сконцентрировано в OTT-аренде и академических стриминг-платформах с обязанностями copyright-tracking.
Два режима ошибки, которые стоит знать. Первый – квота хранилища. 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 всплывали в дашборде раньше, чем в support-тикете. Где заказчики поставляют офлайн (распространённый паттерн в пилот-тренинге и корпоративном обучении), мы привязываем persistent-license сессии к quota-aware UI с самого начала: ретрофит истории квот после запуска – дорогой путь.
Типичные ошибки
Шесть режимов отказа покрывают большинство EME-багов, которые мы видели за более чем семь лет multi-DRM работы.
Упаковка только в cenc и обнаружение на запуске, что Safari не играет. Чинится в пакеджере: переключиться на cbcs, перешифровать, перезалить. Каждый современный плеер договаривается о cbcs на Widevine и PlayReady; только FairPlay ограничен им.
Отправка одного URL лицензионного сервера на все CDM. Widevine говорит на проводе на Widevine, PlayReady – на PlayReady, FairPlay – на SPC/CKC. Endpoint-ы выглядят похоже на HTTP, но не взаимозаменяемы. Используйте multi-DRM сервис лицензирования или три раздельных endpoint с общим upstream-policy-слоем.
Забыть, что 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-дашборд.
Считать байты message чем-то, кроме непрозрачных. event.message – wire-формат CDM. Форвардите. Не логируйте (может утекать per-device идентификатор). Не трансформируйте. Не ретраите на клиенте; пусть сервер вернёт ошибку и пусть сервер ретраит.
Путать события encrypted и keymessage. encrypted срабатывает на <video>, когда демуксер натыкается на неизвестный PSSH; message – на MediaKeySession, когда CDM имеет байты для лицензионного сервера. Подключение слушателей не к тем объектам – распространённый copy-paste баг, дающий плеер, который открывает сессии, но никогда не отправляет лицензионные запросы.
«Pitfall. Widevine-конфиг с robustness: 'HW_SECURE_ALL', падающий на Chromebook, – не баг; это Chromebook сообщает, что не может host 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», а «поставлять плеер, который знает, какой тир может поддержать устройство, и честно говорит об этом пользователю». Такой плеер получает и экономию egress на честно нижне-тировых устройствах, и удержание на верхне-тировых.
Что меняется в нативном приложении вместо браузера
EME – браузерная история. Те же три CDM существуют вне браузера, но у каждого свой нативный API.
В iOS и macOS приложениях FairPlay ходит через AVContentKeySession фреймворка AVFoundation. Форма почти изоморфна EME-flow: приложение получает запрос ключа от фреймворка, форвардит SPC на key-server, получает CKC, скармливает обратно – но API Objective-C / Swift, lifecycle сессии – это lifecycle фреймворка, а байты протокола key-server – те же SPC/CKC, что и на вебе. WWDC 2018 session 507 «AVContentKeySession Best Practices» всё ещё канонический референс по офлайн-арендному окну и dual-expiry паттерну.
На Android Widevine ходит через MediaDrm в платформенном медиа-стэке. ExoPlayer прячет почти всё за DefaultDrmSessionManager. Протокол лицензионного сервера – тот же Widevine wire-формат, что в браузере; уровни безопасности запрашиваются через 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 маппится на тот же robustness-контракт; требование к серверному SDK – та же базовая линия v3.0.2769.
Для наших целей – браузер-first статья в блоке player engineering – практический вывод такой: EME-flow, который вы строите в браузере, – самая переносимая ментальная модель из всех четырёх. Один раз поставив EME, нативные пути вы узнаёте как вариации того же разговора.
Сверяем стандарт с реализациями
Когда источники расходятся – побеждает спецификация. Два примера, которые стоит знать.
Текущий W3C Working Draft – Encrypted Media Extensions от 20 мая 2026 (W3C, working draft, https://www.w3.org/TR/encrypted-media-2/). Он несёт последний список key statuses (включая usable-in-future) и enum MediaKeySessionClosedReason. Последняя Recommendation – наиболее авторитетная форма документа W3C – всё ещё сентябрь 2017 (W3C, Encrypted Media Extensions, REC-encrypted-media-20170918). Несколько популярных постов описывают getStatusForPolicy() как «скоро»; он поставляется в Chrome с 2018 и теперь часть нормативного текста Working Draft в §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 одним мастером.
- Robustness, уровни безопасности и статус HDCP решают, какое качество CDM выдаст; показывайте output-downscaled и output-restricted пользователям.
- Используйте getStatusForPolicy() для предварительной проверки HDCP; используйте keystatuseschange, чтобы реагировать на изменения во время воспроизведения.
- Persistent-сессии включают офлайн-просмотр; объединяйте их с quota-aware UI до запуска, не после.
Что почитать дальше
- Media Source Extensions (MSE): как устроен любой веб-плеер изнутри
- DRM 101: почему три системы и почему вы поставляете все три
- Common Encryption (CENC) подробно
CTA
Выберите один из трёх вариантов:
- Поговорить со streaming-инженером – 30-минутный scoping-звонок с player-engineering лидом Фора Софт. → Записаться
- Посмотреть наши кейсы – multi-DRM, OTT, e-learning и телемедицина в продакшене. → Кейсы
- Скачать EME multi-DRM шпаргалку – одностраничный PDF: строки key system, схемы шифрования, тиры безопасности, типичные ошибки. → Скачать