Low Overhead CMAF for MoQ
A new wire format for CMAF over MoQ — including DRM — with the overhead of LOC
✨ Why LOCMAF
CMAF carries the metadata that DRM needs (per-sample IVs, subsample maps,
tenc defaults). LOC is compact but strips this away. LOCMAF keeps
the CMAF structure intact and compresses the redundant moof bytes
out of the wire — so the standard MSE / EME path on the receiver plays
protected content as if it had arrived as plain CMAF.
- Sample-level objects (one frame per MoQ object)
- Delta
moofas small as 2 bytes - Functionally lossless reconstruction
- Works with Widevine, PlayReady, FairPlay, ClearKey
📊 Measured Compression
On a 250-fragment AVC group from the moqlivemock test asset:
- CMAF
mooftotal: 26 040 B - LOCMAF object total: 592 B
moof-header ratio: 45 : 1- 128 kbps AAC wire bitrate: 171.5 → 131.9 kbps (−23 %)
- 128 kbps AAC under
cbcsDRM: 191.4 → 131.9 kbps (−31 %)
LOCMAF saves more relative to CMAF under DRM than in clear,
because senc / saio / saiz make
the CMAF moof grow while LOCMAF only emits what it needs.
See it yourself: after connecting MOQ Player to the server,
click the Show catalog toggle to inspect each track's total wire
bitrate (framing included) and compare a CMAF track to its
LOCMAF counterpart — both renditions live in the same cmsf/* catalog.
🔗 LOCMAF Resources
Everything you need to understand and implement LOCMAF:
-
locmaf.devoverview, diagrams, measurements
-
draft-einarsson-moq-locmafIETF Internet-Draft — normative LOCMAF spec (wire version 0.3)
-
Eyevinn/locmafGo reference codec (encode/decode + golden vectors)
-
Slide deck20-slide MARP deck adapted from the spec
-
Eyevinn/moqlivemockMoQ test publisher/subscriber (uses the locmaf codec)
-
Eyevinn/warp-playerbrowser MoQ player with LOCMAF + EME
Catalog signal: "packaging": "locmaf" with
"locmafVersion": "0.3" in the CMSF Track entry. The matching
"packaging": "cmaf" rendition shares the same init data through an
initRef, so both variants sit side by side in one draft-01 catalog.
Try LOCMAF Live
Both the publisher and the browser player have shipped LOCMAF since v0.13. Open MOQ Player,
connect to a cmsf/* namespace and pick a LOCMAF track from the catalog,
and the receiver will rebuild the CMAF chunks and feed them to MSE / EME for you —
clear, ClearKey/ECCP, and commercial DRM all work side by side.
In-band CTA-608 Captions
Captions carried inside the video elementary stream — through every packaging, and inside the ciphertext
💬 What is Carried
The publisher injects one self-contained CTA-608 CC1 caption per MoQ group, generated with Eyevinn/go-608. Row 13 carries the UTC clock, row 14 the group number, so what you read on screen is directly checkable against the burnt-in overlay.
- AVC and HEVC — an SEI NAL unit before the first VCL NALU
- AV1 — a
metadata_itu_t_t35OBU before the first frame OBU - Present in CMAF, LOCMAF, LOC and moq-mi
- Spliced in before encryption, so captions ride inside the ciphertext
- Each group is independent — a caption comes from that group's samples alone
🎛️ Presentation Modes
mlmpub -cc608mode selects how a caption reaches the screen, covering all
three CTA-608 presentation styles. Measured at 25 fps in a 25-frame group:
paint-on(default) — grows two characters per frame; first characters on frame 4, complete on frame 17, so the caption is displayed over the second it namesroll-up2/roll-up3/roll-up4— types the same way but scrolls the window up before each line, finishing two frames laterpop-on— builds invisibly and flips on whole at frame 18
The roll-up sizes differ in the RU2/RU3/RU4 mode code on the wire but display identically here, since a group holds one cue and the window is reset per group — which is exactly what makes them useful for exercising a decoder.
▶️ Rendering in MOQ Player
MOQ Player decodes the captions from the in-band SEI on both render engines — MSE (CMAF and LOCMAF) and WebCodecs (LOC) — and paints them on the true 32×15 grid inside the CTA-608 safe area, with colours, background boxes and the full pop-on / roll-up / paint-on screen model.
- CC toggle enabled only when the track advertises
urn:scte:dash:cc:cea-608:2015and its codec can carry CTA-608 - Encrypted namespaces work without a key — the caption SEI sits in the clear subsample leader
- Built on a general timed-text overlay seam that WebVTT, IMSC-1 and ograf renderers can reuse
Not yet on AV1: AV1 renditions play on the WebCodecs pipeline, but their captions travel in a metadata OBU rather than an SEI NAL unit, so the player does not render them — the CC toggle stays struck through on those tracks.
See the Captions
Captions are enabled on this demo service. Open MOQ Player, pick an AVC or HEVC video
track in any cmsf/* or msf/clear namespace — clear or
encrypted — and switch the CC toggle on.
Why Use MoqLiveMock?
Test your MoQ client implementations against a reliable, always-available server speaking MoQ Transport draft-18. MoqLiveMock simulates a true live MoQ streaming service with UTC-aligned content delivery — all clients receive synchronized streams regardless of join time. Stream 10-second loops of synchronized multi-bitrate AVC, HEVC and AV1 content across CMAF, LOCMAF, LOC, and moq-mi packaging modes, with clear and DRM-protected variants of each, and in-band CTA-608 captions in every video track.
Available Streams
Specially prepared relatively low-bitrate content for testing
🎥 Video Tracks
H.264 (AVC), HEVC and AV1 encoded video streams at multiple bitrates for adaptive streaming, each carrying in-band CTA-608 captions
- 400 kbps AVC
- 600 kbps AVC
- 900 kbps AVC
- 400 kbps HEVC
- 600 kbps HEVC
- 900 kbps HEVC
- 400 kbps AV1
- 600 kbps AV1
- 900 kbps AV1
All three codecs are served in the cmsf/* namespaces (CMAF and LOCMAF)
and in LOC; moq-mi/clear video is AVC only. The AV1 assets are encoded
in low-delay CBR mode (I/P only, no reordering) so they match the AVC/HEVC structure.
🎵 Audio Tracks
Easily discernable audio tracks in AAC, Opus, and AC-3 with synchronized second beeps
- Monotonic AAC (128 kbps)
- Scale AAC (128 kbps)
- Monotonic Opus (128 kbps)
- Scale Opus (128 kbps)
- Monotonic AC-3 (192 kbps)
- Scale AC-3 (192 kbps)
📝 Text Tracks
Out-of-band subtitle tracks in CMAF-compatible formats, plus captions carried inside the video itself
- WVTT (WebVTT in CMAF)
- STPP (TTML in CMAF)
- CTA-608 CC1, in-band in every video track
The CTA-608 captions are signaled per video track with an
accessibility descriptor
(urn:scte:dash:cc:cea-608:2015, CC1=eng) in the MSF / CMSF
catalog; catalogless moq-mi signals nothing and is in-band only.
See the captions section.
🔒 Content Protection
Multiple content protection modes served as separate namespaces, each catalog carrying both CMAF and LOCMAF packaging
cmsf/clear— unencryptedcmsf/drm-cbcs— Widevine, PlayReady, FairPlaycmsf/eccp-cbcs— ClearKey (ECCP)
Each namespace has its own CMSF draft-01 catalog, with every rendition in both CMAF and LOCMAF packaging. LOCMAF carries the same per-sample encryption metadata as CMAF, so the same EME / CDM path works for either.
Signaling follows the released CMSF draft-01 specification, also supported by Shaka Player.
📦 Packaging Modes
Side-by-side packaging modes so clients can compare wire-cost and pipelines
cmsf/*— CMAF and LOCMAF (v0.3) in one catalog, sharing init data viainitRefmsf/clear— LOC, raw codec frames per objectmoq-mi/clear— moq-mi, catalogless interop
LOCMAF gives near-LOC overhead while keeping CMAF's metadata path, so DRM survives the round-trip. LOC and moq-mi carry raw codec frames played via WebCodecs.
⚡ Technical Specs
Modern streaming protocols and formats
- MoQ Transport draft-18
- ALPN-based version selection (
moqt-18) - AVC, HEVC and AV1 video; AAC, Opus and AC-3 audio
- CMAF chunks, LOCMAF delta moofs, LOC raw frames
- In-band CTA-608 CC1 captions (SEI for AVC/HEVC, T.35 OBU for AV1)
- MSE / EME and WebCodecs render pipelines in MOQ Player
- 10-second loops, wall-clock synchronized
- MSF / CMSF catalog per namespace
- moq-mi catalogless namespace
- Multiple namespaces announced from one publisher
Live Streaming Simulation
UTC-aligned streaming for consistent playback across all clients
🕐 Wall-clock Alignment
MoqLiveMock simulates a live MOQ streaming service with precise UTC time alignment. This ensures all subscribers receive synchronized content regardless of when they join or when the publisher was started.
Two-Level Time Alignment
seconds % 10 == 0.
This ensures every subscriber joining at the same wall-clock time receives the same content.
Unix_epoch_ms / 1000,
so group boundaries fall on exact second boundaries.
How to Use
Multiple ways to connect and stream content
🌐 Browser Player (Recommended)
Use MOQ Player (warp-player v0.14) for instant playback in Chrome, Edge, Firefox, or Safari 26.4+. No installation required. It speaks MoQ Transport draft-18 and ships an MSE / EME engine that plays both CMAF and LOCMAF tracks (the LOCMAF decoder reconstructs CMAF chunks before they reach MSE) plus a WebCodecs engine for LOC and moq-mi tracks — selectable from the UI. The CC toggle renders in-band CTA-608 captions on either engine for AVC and HEVC tracks.
💻 Command Line Client
For developers and advanced users, use mlmsub to stream from the terminal:
# Install the client go install github.com/Eyevinn/moqlivemock/cmd/mlmsub@latest # Stream clear CMAF (default namespace: cmsf/clear) mlmsub -addr moqlivemock.demo.osaas.io -videoname _avc -audioname _aac -muxout - | ffplay - # Dump the unified cmsf/clear catalog — each rendition appears as a CMAF track # and a LOCMAF track (the '_locmaf' suffix), sharing init data via initRef mlmsub -addr moqlivemock.demo.osaas.io -namespace cmsf/clear -catalogout - # Stream the LOCMAF variant — chunks are reconstructed into CMAF on the receiver mlmsub -addr moqlivemock.demo.osaas.io -namespace cmsf/clear -videoname _avc_locmaf -audioname _aac_locmaf -muxout - | ffplay - # Stream the AV1 rendition (CMAF packaging, AV1 + AAC) mlmsub -addr moqlivemock.demo.osaas.io -videoname _av1 -audioname _aac -muxout - | ffplay - # Stream LOCMAF under ClearKey/ECCP (cbcs) mlmsub -addr moqlivemock.demo.osaas.io -namespace cmsf/eccp-cbcs -videoname _eccp_locmaf -audioname _eccp_locmaf -muxout - | ffplay - # Stream LOC-packaged content (raw frames, AVC + AAC) and write to files mlmsub -addr moqlivemock.demo.osaas.io -namespace msf/clear -videoout video.h264 -audioout audio.aac # Discover announced namespaces on a relay mlmsub -addr moqlivemock.demo.osaas.io -discover # Stream with WebTransport (selected by the https:// scheme) and debug logging mlmsub -addr https://moqlivemock.demo.osaas.io:443 -loglevel debug -muxout - | ffplay -
The cmd/locmaf tool in moqlivemock includes a roundtrip subcommand
that encodes a fragmented MP4 through the LOCMAF encoder/decoder and verifies sample-level
fidelity — handy for running the LOCMAF wire-cost measurements yourself.
🔧 Run Your Own Server
Clone the repository and run your own MoqLiveMock instance:
# Clone and build git clone https://github.com/Eyevinn/moqlivemock cd moqlivemock go build ./cmd/mlmpub # Option 1: Using mkcert (recommended for development) mkcert -key-file key.pem -cert-file cert.pem localhost 127.0.0.1 ::1 mkcert -install ./mlmpub -cert cert.pem -key key.pem -addr localhost:4443 # Option 2: Using fingerprint (no cert installation needed) ./mlmpub -sideport 8081 # With in-band CTA-608 captions (this demo runs with -cc608) ./mlmpub -sideport 8081 -cc608 # Pick the CTA-608 presentation style (default: paint-on) ./mlmpub -sideport 8081 -cc608 -cc608mode roll-up2 # With ClearKey/ECCP encryption ./mlmpub -sideport 8081 \ -kid 00112233445566778899aabbccddeeff \ -iv 00000000000000000000000000000001 \ -scheme cbcs -laurl http://localhost:8081/clearkey
When using fingerprint mode, connect with MOQ Player using:
• Server URL: https://localhost:4443/moq
• Fingerprint URL: http://localhost:8081/fingerprint
The -sideport enables an HTTP server for both /fingerprint and /clearkey endpoints.