Extended manifest format
Write and validate extended manifests, the JSON documents that describe defense plans.
Just want to use Dodge? See install. New to video fingerprinting? Start with learn.
Overview
An extended manifest is a JSON file that wraps a standard DASH MPD with a per-representation defense plan. It is the way defenses are expressed in Dodge: a single extended manifest embeds one video's MPD and describes which representations from that MPD are defended, and the download schedule for each representation.
Download schedules consist of arrays of cycles, which are Dodge's generalization of segment downloads. A cycle can be a full or partial segment download. Its contents may be discarded immediately (padding); otherwise, they are held in an intermediate buffer until a cycle with an explicit buffer directive flushes them to the playback buffer.
An MPD on its own plays as standard DASH. An MPD plus its extended manifest plays as Dodge-defended DASH. MPDs can often be embedded as is, without modification; the extended manifest adds a layer the player consults at fetch time.
Basic example
{
"start": {
"mpd": "<MPD xmlns='urn:mpeg:dash:schema:mpd:2011' ...>",
"base_uri": "https://example.com/segments/"
},
"streams": [
{
"label": "video_1000k",
"period": 0,
"init": [
{ "range": "0-449" },
{ "range": "450-704", "buffer": true }
],
"data": [
{ "index": 0, "range": "0-391827" },
{ "index": 0, "range": "366413-758240" },
{ "index": 0, "range": "366413-758240", "buffer": true },
{ "index": 1, "range": "0-391827" },
{ "index": 5, "range": "0-43999", "padding": true }
]
}
]
} Top-level fields
start
mpd(string, required): the original MPD XML, embedded as a string. Dynamic (live) MPDs are rejected.base_uri(string, required): the segment server's base URI, used to resolve relative URLs in the MPD. Set to a placeholder value if unused.
streams[]
An array of one or more stream entries. Each entry describes a defense plan for one representation, optionally scoped to a period.
label(string, required): the representation ID this stream defends. Whether it actually corresponds to a representation in the MPD is checked at request time, not at manifest load time. Strict mode protects if there's a mismatch.period(non-negative integer, optional): scopes the entry to a specific period in multi-period MPDs. When absent, the entry matches any period. Multiple stream entries may share the samelabelwith differentperiodvalues.init[](array of init cycles, optional)data[](array of data cycles, optional)
At least one of init and data must be non-empty. Either can be omitted entirely (e.g., a stream with only init cycles, such as non-fragmented text tracks; or only data cycles, in the case of self-initializing streams).
Cycles
A cycle is one entry in an init or data array. Each cycle is a single download the player will perform; the cycle array as a whole determines the wire shape.
Common fields
range(string, optional): the byte range to fetch, of the form"start-end", as in the HTTP Range header. Either bound may be omitted:"-855"means "from the start through byte 855", and"44-"means "from byte 44 to end of resource". When the field is absent entirely, the cycle uses the segment's full extent.buffer(boolean or array, optional): see below. Arrays are allowed only on data cycles.padding(boolean, optional): see below. Allowed on both init and data cycles.quality(string or non-negative integer, optional): see below.
Data cycle-only field
index(non-negative integer, required): which segment in the representation this cycle downloads.
Cycle directives
buffer
Marks a flush point. The player accumulates cycles in an intermediate buffer and assembles them into playable media at each cycle with the buffer flag set.
Two forms:
buffer: true: flush every pending segment downloaded since the last flush.buffer: [idx1, idx2, …]: selective flush. Only assemble and buffer the listed segment indices, leaving any others pending for a later flush. Every index in the array must have already appeared in an earlier cycle, or the extended manifest is rejected.
The array form lets a defense interleave segments from different playback positions and flush them in groups, instead of in strict download order. It should be used sparingly.
Absent means false: a cycle with no buffer field is not a flush point. That governs when a segment reaches the playback buffer, not whether it ever does. A cycle that completes a segment still assembles it and holds it pending, so the effect of omitting buffer is to defer delivery to the next cycle that has it. A flush releases everything pending for the stream, init segments included, so a buffer: true on a data cycle also delivers any init segments still waiting.
One default applies on top of that. If no init cycle in a stream carries buffer: true, and none carries a quality override, the last init cycle is treated as a flush point anyway, so a single-representation defense delivers its init segment at the end of the init sequence rather than waiting for the first data flush. The default is skipped once quality is in play, because with several representations' init segments in the stream the right flush point is a design decision rather than a guess; those init segments simply ride along to the first data cycle that flushes.
padding
Marks the cycle as padding traffic. The bytes are fetched to shape the wire pattern but discarded immediately and never assembled into playable media.
quality
Fetches the cycle from a different representation in the same adaptation set, useful for concealing the size of a large segment by fetching it at a lower quality. Accepted values:
- A string matching the target representation's
id(e.g.,"quality": "video_500k"). - A non-negative integer, interpreted as an index into the adaptation set's representation list.
Numeric strings (e.g., "3") are kept as strings and treated as representation IDs; a warning is logged at manifest load time to flag the ambiguity. Use a number if an index is intended.
On init cycles, quality pre-fetches the alternate representation's init segment so that it can be cached for later use in quality override data cycles. The defense designer is responsible for adding init cycles with quality for every alternate representation used in data cycles.
What's validated at manifest load
The defense registry checks structural correctness only:
start.mpdandstart.base_uriare present and string-typed; the MPD isn't dynamic.streamsis present; each stream has a stringlabel, a validperiodif present, and at least one non-empty cycle array (initordata).- Each cycle's
range, if present, parses as a well-formed byte range. - Each data cycle's
indexis a non-negative integer. buffer,padding, andqualityhave valid types.- Selective
bufferarrays only reference segment indices that have appeared earlier in the cycle array.
What the registry does not check: that label resolves to a real representation in the MPD, that period resolves to a real period in the MPD, that index resolves to a real segment, or that a quality override resolves to a real representation. Those resolutions happen later, at request time. If a referenced representation or segment is missing, the player stalls rather than skipping or falling back to preserve the defense traffic shape.
Strict mode
The dash.js module's dodge.strictMode setting controls how aggressively undefended traffic is blocked when a defense is active. The four levels:
'representation'(default): block undefended representations when an extended manifest is active. Plain DASH still works when a vanilla MPD is loaded.'manifest': also refuse to play when the source URL is not an extended manifest. A defense must be used for playback to proceed.'max': also reject extended manifests with thumbnail tracks, non-fragmented text, or XLink references; warn about retries, background UTC sync, and content steering settings that could leak signal outside the defense.false: disable enforcement (fall back to vanilla DASH on a per-representation basis; not recommended for production).
In any mode other than false, embedded MPDs containing DRM, CMCD telemetry, content steering, or DVB reporting trigger informational warnings. These features may not interact safely with traffic analysis defenses, and the defense designer should verify their behavior in context. They are not disabled by default, as they are important in the streaming ecosystem, must be available to certain content providers, and we assume they are less likely to pose a traffic analysis threat than other features.