DRM and Content Protection: CENC, Widevine, PlayReady, FairPlay
How multi-DRM works: CENC cenc vs cbcs encryption, Widevine/PlayReady/FairPlay, EME and the CDM, the license flow, and AES-128 vs SAMPLE-AES.
If you license premium content, the studios will require DRM — and not just encryption, but a specific set of DRM systems with specific robustness rules. The good news is that modern DRM is built on a shared encryption standard, so you encrypt once and serve every platform. The bad news is that the details (cenc vs cbcs, three CDMs, license flows) are where integrations break. This post maps the territory. You can experiment with license requests and key delivery in the player's DRM panel.
Encryption vs DRM
It helps to separate two layers:
- Encryption scrambles the media so it's useless without a key.
- DRM is the system that securely delivers the key to an authorized device and enforces usage rules (output protection, expiry, persistence).
You can have encryption without full DRM — that's essentially what HLS AES-128 is — but premium content requires a real DRM system with a hardware-backed key store.
Common Encryption (CENC)
CENC (ISO/IEC 23001-7) is what lets you encrypt once for multiple DRMs. It defines how to encrypt fragmented MP4 / CMAF media and where to store key IDs, so Widevine, PlayReady, and FairPlay can all decrypt the same file given the right key. CENC has two protection schemes, and this choice matters more than almost anything else in a DRM integration:
cenc— AES-128 in CTR (counter) mode. Historically used by Widevine and PlayReady.cbcs— AES-128 in CBC mode with pattern encryption (e.g. encrypt 1 of every 10 blocks). Required by FairPlay, and now supported by Widevine and PlayReady too.
Because cbcs is the one scheme all three support, the modern best practice is
encrypt once with cbcs and serve a single CMAF set to every platform. The
old world required two encrypts (cenc for Widevine/PlayReady, cbcs for
FairPlay) and double storage. Mixing schemes on the same content will fail to
decrypt, so verify your packager's output.
The three DRM systems
No single DRM covers every device, so you ship all three:
- Widevine (Google) — Chrome, Android, Android TV, many smart TVs, Firefox. Has security levels: L1 (hardware-backed, required for HD/4K on most studios) and L3 (software, often capped to SD).
- PlayReady (Microsoft) — Edge, Windows, Xbox, and a huge install base of smart TVs and set-top boxes. Has hardware (SL3000) and software (SL2000) levels.
- FairPlay Streaming (Apple) — Safari, iOS, iPadOS, tvOS. Requires
cbcsand uses its own key-delivery flow (SPC/CKC, below).
Your DRM license service (Axinom, EZDRM, Pallycon, BuyDRM, or a cloud provider's offering) typically fronts all three behind one set of policies.
EME and the CDM in the browser
In the browser, DRM runs through EME (Encrypted Media Extensions) — a W3C JavaScript API — talking to a CDM (Content Decryption Module), the black-box component that actually holds keys and decrypts. The CDM is shipped by the browser/OS (Widevine in Chrome, PlayReady in Edge, FairPlay in Safari).
The EME handshake, simplified:
- The player feeds an encrypted segment to the media element; the browser fires
an
encryptedevent carrying the PSSH (Protection System Specific Header) init data. - The player creates a
MediaKeyssession and callsgenerateRequest(initDataType, initData). - The CDM emits a
messageevent — the license request (a challenge), which the player POSTs to the license server. - The license server validates entitlement and returns a license (the keys, wrapped for that CDM).
- The player calls
session.update(license); the CDM installs the keys and decryption proceeds.
const keySystem = "com.widevine.alpha"; // or com.microsoft.playready,
// com.apple.fps
const access = await navigator.requestMediaKeySystemAccess(keySystem, config);
const mediaKeys = await access.createMediaKeys();
await video.setMediaKeys(mediaKeys);
video.addEventListener("encrypted", async (e) => {
const session = mediaKeys.createSession();
session.addEventListener("message", async (ev) => {
const license = await fetchLicense(ev.message); // POST to license server
await session.update(license);
});
await session.generateRequest(e.initDataType, e.initData);
});
FairPlay deviates: instead of a generic license request it uses an SPC
(Server Playback Context) request and a CKC (Content Key Context) response,
and Safari historically used the webkit-prefixed FairPlay API before aligning
closer to EME.
Multi-DRM packaging
A single CMAF asset carries init data for all systems:
- HLS references the keys via
EXT-X-KEY/EXT-X-SESSION-KEYwith the appropriateKEYFORMAT(e.g.urn:uuid:...for Widevine,com.apple.streamingkeydeliveryfor FairPlay). - DASH declares one
<ContentProtection>element per system in theAdaptationSet, each with its system UUID and a<cenc:pssh>box:
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cbcs"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAA...Widevine...</cenc:pssh>
</ContentProtection>
<ContentProtection schemeIdUri="urn:uuid:9a04f079-9840-4286-ab92-e65be0885f95">
<cenc:pssh>AAAA...PlayReady...</cenc:pssh>
</ContentProtection>
The player picks whichever system its platform supports and ignores the rest.
AES-128 vs SAMPLE-AES
HLS also offers two non-CENC encryption methods that are not full DRM but are often confused with it:
METHOD=AES-128— encrypts the entire TS segment as one AES-128-CBC blob. Simple, but the key is delivered over HTTPS with no hardware-backed protection. Decrypt happens before demux, so it can't be combined with hardware decode of the encrypted bytes. Good for casual obfuscation, not for studio content.METHOD=SAMPLE-AES— encrypts only the media samples (leaving headers clear), enabling thecbcspattern. This is the path that bridges to FairPlay and real DRM, because the encrypted bytes can be handed to a secure decoder.
If a studio is asking for DRM, plain AES-128 won't satisfy them — they want a
licensed CDM with a hardware key path. Use the player's DRM panel to
inspect license requests and confirm your key system, PSSH, and cbcs scheme
line up across platforms.
Takeaways
Encrypt once with cbcs, package all three systems' init data into one CMAF
asset, and let EME/the CDM negotiate per platform. The recurring failure modes
are scheme mismatches (cenc content on a FairPlay device), wrong PSSH, and
robustness gaps (requesting HD on Widevine L3). Get those right and DRM becomes
boring — which is exactly what you want.
Try it yourself
Reading about streaming is one thing — pull apart a real manifest, play a stream, and measure its QoE with the toolkit.
Browse all tools