惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

云风的 BLOG
云风的 BLOG
M
MIT News - Artificial intelligence
Recent Announcements
Recent Announcements
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
J
Java Code Geeks
Microsoft Azure Blog
Microsoft Azure Blog
罗磊的独立博客
博客园 - 【当耐特】
H
Help Net Security
腾讯CDC
大猫的无限游戏
大猫的无限游戏
GbyAI
GbyAI
Last Week in AI
Last Week in AI
Jina AI
Jina AI
博客园 - 聂微东
Blog — PlanetScale
Blog — PlanetScale
A
About on SuperTechFans
Apple Machine Learning Research
Apple Machine Learning Research
P
Proofpoint News Feed
Y
Y Combinator Blog
C
Check Point Blog
博客园 - 司徒正美
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Turn on CMCD: make your CDN logs explain rebuffering
Mason K · 2026-06-15 · via DEV Community

Mason K

TL;DR

CMCD (Common Media Client Data, CTA-5004) lets your player attach its state (buffer length, bitrate, "I just rebuffered") to every segment request, so your CDN's own logs can explain QoE. We'll enable it in hls.js and Shaka, choose header vs query mode, and write a tiny Node parser that turns the payload into a per-session story.

What we're building

  1. CMCD turned on in the player (hls.js + Shaka).
  2. A decision on transmission mode (header vs query) and why it matters.
  3. A server-side parser that reads the CMCD payload and groups by session.

Versions: hls.js 1.x, shaka-player 4.x, node 20.x.

The keys you'll actually use

CMCD sends short keys on each request. The important ones:

Key Meaning Why you care
sid session id the join key between player and CDN
cid content id group requests by asset
bl buffer length (ms) how close to starving
bs buffer starvation flag true = a rebuffer just happened
su startup flag request is part of initial buffering
mtp measured throughput (kbps) client's bandwidth estimate
br requested encoded bitrate which rendition
ot object type (v/a/m/i) video, audio, manifest, init

💡 Boolean keys like bs and su are sent as just the key name when true (no =value). Your parser has to handle that.

1. Enable CMCD in hls.js

Enabling the cmcd config object turns it on. Start with header mode:

// app/playerHls.js
import Hls from "hls.js";

const hls = new Hls({
  cmcd: {
    sessionId: crypto.randomUUID(),   // your sid
    contentId: "asset-12345",         // your cid
    useHeaders: true,                 // header mode (vs query string)
  },
});

hls.loadSource("https://cdn.example.com/video/master.m3u8");
hls.attachMedia(document.querySelector("video"));

With useHeaders: false, hls.js appends a CMCD query argument instead. Same data, different transport (see the trade-off below).

2. Enable CMCD in Shaka

Shaka's CmcdManager serializes per CTA-5004 in header or query mode:

// app/playerShaka.js
import shaka from "shaka-player";

const player = new shaka.Player(document.querySelector("video"));

player.configure({
  cmcd: {
    enabled: true,
    sessionId: crypto.randomUUID(),
    contentId: "asset-12345",
    useHeaders: true,
  },
});

await player.load("https://cdn.example.com/video/master.mpd");

3. The transmission-mode decision

This is the part that actually needs thought.

Mode Pros Cons
Header (CMCD-Request: ...) clean URLs, cache key untouched needs CORS Access-Control-Allow-Headers for CMCD-*; CDN must log custom headers
Query (?CMCD=...) works everywhere, easy to log changes the URL = changes the cache key unless the CDN ignores the CMCD arg

⚠️ Query mode can wreck your cache hit ratio: every unique CMCD payload looks like a different object. Configure the CDN to exclude the CMCD query arg from the cache key before you ship query mode.

For cross-origin segment hosts in header mode, you need this on the segment server:

Access-Control-Allow-Headers: CMCD-Request, CMCD-Object, CMCD-Session, CMCD-Status

4. Parse it server-side

CMCD values are a comma-separated key=value list, with quoted strings and bare keys for booleans. Here's a minimal parser:

// server/parseCmcd.js - node 20+
export function parseCmcd(raw) {
  // raw: e.g.  bl=21300,br=2500,bs,cid="asset-12345",mtp=48200,ot=v,sid="abc-123",su
  const out = {};
  if (!raw) return out;

  // split on commas that aren't inside quotes
  const parts = raw.match(/(?:[^,"]+|"[^"]*")+/g) || [];
  for (const part of parts) {
    const eq = part.indexOf("=");
    if (eq === -1) {
      out[part.trim()] = true;             // bare key = boolean true (e.g. bs, su)
      continue;
    }
    const key = part.slice(0, eq).trim();
    let val = part.slice(eq + 1).trim();
    if (val.startsWith('"') && val.endsWith('"')) {
      val = val.slice(1, -1);              // quoted string
    } else if (!Number.isNaN(Number(val))) {
      val = Number(val);                    // numeric
    }
    out[key] = val;
  }
  return out;
}

Read it from whichever transport you chose:

// server/ingest.js (Express)
import { parseCmcd } from "./parseCmcd.js";

app.use((req, _res, next) => {
  const fromHeader = req.headers["cmcd-request"];        // header mode
  const fromQuery = req.query.CMCD;                       // query mode
  req.cmcd = parseCmcd(fromHeader || fromQuery);
  next();
});

5. Turn it into a QoE signal

Now the payoff. Group your request logs by sid and the starvation flags light up the exact requests around a stall:

// server/sessionReport.js (sketch)
function sessionReport(logLines) {
  const bySession = {};
  for (const line of logLines) {
    const c = line.cmcd;
    if (!c?.sid) continue;
    (bySession[c.sid] ??= []).push({
      url: line.url,
      edgeCache: line.cacheStatus,   // HIT / MISS from your CDN log
      ttfb: line.ttfbMs,             // edge latency from your CDN log
      bufferMs: c.bl,
      starved: c.bs === true,        // the gold signal
      startup: c.su === true,
      bitrate: c.br,
    });
  }
  return bySession;
}

A starved: true row sitting next to a MISS with a high ttfb is a root cause you can act on, not a guess. That's the whole point: the player labeled the bad request, in the CDN's own log.

6. Confirm it in the browser before you touch the CDN

Before you go configuring edge logging, verify the player is actually emitting CMCD. Open DevTools, go to the Network tab, filter to your .ts or .m4s segment requests, and look at one.

In header mode, the request headers include a CMCD-Request (and possibly CMCD-Object, CMCD-Session, CMCD-Status) entry:

CMCD-Request: bl=21300,dl=21300,mtp=48200,su
CMCD-Object: br=2500,d=6000,ot=v,tb=4500
CMCD-Session: cid="asset-12345",sid="abc-123"

In query mode, the same data is URL-encoded on the segment URL:

GET /video/seg-042.m4s?CMCD=bl%3D21300%2Cbr%3D2500%2Ccid%3D%22asset-12345%22%2Cot%3Dv%2Csid%3D%22abc-123%22

Two things to sanity-check right here, before any server work:

  • [ ] sid is stable across every segment in one playback (same value), and different across page reloads. If it's changing mid-session, your join key is broken.
  • [ ] bs appears on requests right after a forced stall. Throttle your network to "Slow 3G" in DevTools, let it rebuffer, and watch for bs showing up. If it never appears, the player isn't tracking starvation the way you think it is.

💡 Catch these in the browser and you save yourself an afternoon of staring at CDN logs wondering why nothing groups correctly. The bug is almost always client-side config, not the log pipeline.

Version note

CMCD v1 is stable and supported across hls.js, Shaka, dash.js, ExoPlayer/Media3, and JWPlayer. CTA-5004 v2 (2026) adds new keys, an event-reporting mode, and stricter value encoding; as of early 2026 hls.js was adding optional v2 support. Pin a version so a v2 client doesn't send keys your v1 parser rejects.

What's next

  • Configure your CDN to log the CMCD header (or parse the query arg) and exclude CMCD from the cache key.
  • Start with three keys: sid, bl, bs. Trace one rebuffer end to end before adding the rest.
  • Add a CMCD validator in CI (the video-dev community maintains one) so malformed payloads fail fast.