A DASH stream is described by an .mpd manifest: an XML file that lists every video quality, every audio language and every subtitle track as separate Representations. This page reads that file, plays the stream when it is not DRM-protected, and turns the tracks you pick into an ffmpeg command that saves them together as one MP4 file.
Play a DASH stream online
Press Analyze, then Play preview. The page loads Shaka Player 5.2.12, Google's open source DASH player (Apache 2.0 license), served from getvidora.com and fetched only when you press Play. The player switches quality on its own as your bandwidth changes; when you pick a video quality and an audio track in the lists, the preview switches to that pair if the player offers it.
The preview plays what your browser can decode. H.264 and AAC play in every current browser; H.265 plays in Chrome and Edge only with hardware decoding, and older devices skip VP9 or AV1. A quality the browser cannot play is still listed and can still be downloaded, because ffmpeg copies it without decoding.
No .mpd address yet? Record the page's network traffic as a HAR file in DevTools and drop it into the M3U8 and MPD link sniffer: it lists every manifest the player loaded, with the Referer it sent.
Live stats and player errors
While the preview plays, the Live stats panel refreshes every second and pauses with the video. It reads Shaka Player's own counters: the real picture size, the bitrate the manifest declares for the pair playing, the bandwidth the player measures, the seconds buffered ahead, dropped and decoded frames, the time spent playing and waiting for data, the codecs and, on a live stream, the latency. Two graphs follow the buffer and the bandwidth estimate over the last 60 seconds of playback. A bandwidth estimate below the variant bitrate, with a buffer that keeps falling to zero, means the connection cannot hold that quality: pick a lower one in the Video quality list.
The Player events log under the diagnosis records quality switches, buffering and every Shaka error, translated into a probable cause and a next step. The codes you will meet most often:
| Shaka code | Usual cause |
|---|---|
1001 BAD_HTTP_STATUS | A server answered with an error: 403 for an expired signed URL or a missing Referer or cookie, 404 or 410 for a removed file or a segment already out of the live window |
1002 HTTP_ERROR | No answer could be read: the segment server does not allow other sites (CORS), or the network failed |
1003 TIMEOUT | The server did not answer in time |
4001 DASH_INVALID_XML | The URL returned something other than an MPD, often a web page |
| 4012 and 4032 | No quality uses a codec this browser can decode (RESTRICTIONS_CANNOT_BE_MET, CONTENT_UNSUPPORTED_BY_BROWSER) |
| 6000 to 6999 | DRM: the stream needs a license, and this page never requests one |
Copy share link puts the manifest URL in a link to this page, so someone else can open the same analysis and run the same preview.
What the MPD analyzer reads
| Part of the MPD | What you learn |
|---|---|
<MPD type> | On-demand (static) or live (dynamic), total duration, buffer and rewind window |
<Period> | How many Periods there are and how long each one is: several Periods often mean inserted ads |
<AdaptationSet> and <Representation> | Each video quality (resolution, bitrate, codec, frame rate), each audio track (language, codec, channels, sample rate) and each subtitle track. The track type is read from contentType, then mimeType, then the codec name, at both levels |
<ContentProtection> | The DRM system, the encryption scheme (cenc or cbcs) and the default key ID. One such element anywhere in the file (MPD, Period, AdaptationSet or Representation, or a ref to one) marks the stream as encrypted |
SegmentTemplate, SegmentTimeline, SegmentBase, SegmentList, BaseURL | How the video is cut: the number and length of segments, or one file read with byte ranges, and the resolved URL of the first segment |
Convert MPD to MP4 with ffmpeg
In DASH, picture and sound are always separate tracks, and each audio language is a track of its own. ffmpeg reads the manifest and fetches the segments itself, but it has to be told which tracks to keep. The command this page prints names them by their Representation id:
ffmpeg -i "https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd" -map 0:v:m:id:14 -map 0:a:m:id:19 -c copy angel-one.mp4
0:v:m:id:14 means "the video stream of input 0 whose id tag is 14": ffmpeg's DASH reader copies each Representation id into that tag. -c copy keeps the original encoding, so the conversion takes as long as the download and loses nothing. When ids are missing, repeated or contain characters that break the specifier (such as :), the page falls back to m:variant_bitrate (the declared bandwidth) or to the position of the track. By default it picks the best video up to 1080p and the audio in your browser's language; 4K and the other qualities stay in the list.
Why not a plain ffmpeg -i manifest.mpd -c copy out.mp4? Without -map, ffmpeg keeps one video stream and one audio stream that it picks itself. On the multi-language sample above it kept the Spanish track. With the mapping, the file has the language you chose.
We ran the printed commands with ffmpeg 6.1 (limited to 6 seconds with -t 6) and checked each file with ffprobe:
| Sample and picks | File | ffprobe |
|---|---|---|
| Big Buck Bunny, default pick (1920x1080) and the AAC track | .mp4 | H.264 1920x1080, HE-AAC 48 kHz stereo |
| Angel One, 600x480 and French AAC | .mp4 | H.264 600x480, AAC; the mapped input stream is the one tagged fr |
| Angel One, VP9 720x576 and German Opus | .mkv | VP9 720x576, Opus, language de |
| DASH-IF live simulator | .ts | H.264 640x360, AAC, 6.2 s recorded |
Tracks in WebM (VP9 video, Opus audio) are written to .mkv, which holds any codec found in DASH. The same pick works with yt-dlp: for a direct .mpd URL its format ids are the Representation ids, so yt-dlp -f 14+19 --merge-output-format mp4 URL downloads the same pair. yt-dlp needs ffmpeg installed to merge them. The guide to downloading DASH MPD streams compares these methods with the others.
Manifests with several Periods
ffmpeg's DASH reader keeps a single Period. On a two-Period test manifest it started at the second, longer Period and skipped the 250 seconds of the first one. The analyzer warns about it and lists the qualities of the Period ffmpeg will read. yt-dlp joins Periods whose formats match, which is why the yt-dlp line uses -f "bv*+ba" for those manifests.
DRM in DASH: what the KID and pssh tell you
An encrypted stream declares <ContentProtection> elements. The one with schemeIdUri="urn:mpeg:dash:mp4protection:2011" gives the encryption scheme: cenc (AES-CTR, the common one for Widevine and PlayReady) or cbcs (AES-CBC with a pattern, used by FairPlay and more and more by the others). Its cenc:default_KID attribute is the identifier of the key, not the key itself.
The other elements name a DRM system by its UUID and often carry a <cenc:pssh> box: data the player sends to the license server to ask for the key. The analyzer recognizes these systems:
| System | UUID in schemeIdUri |
|---|---|
| Widevine (Google) | edef8ba9-79d6-4ace-a3c8-27dcd51d21ed |
| PlayReady (Microsoft) | 9a04f079-9840-4286-ab92-e65be0885f95 |
| FairPlay (Apple) | 94ce86fb-07ff-4f43-adb8-93d2fa968ca2 |
| ClearKey | e2719d58-a985-b3c9-781a-b030af78d30e or 1077efec-c0b2-4d02-ace3-3c1e52e2fb4b |
The key is delivered by the license server to a licensed module inside the browser and never leaves it. A web page, ffmpeg or yt-dlp never sees it. That is why the analyzer stops at naming the system: it never contacts a license server, does not play encrypted streams and prints no command. Circumventing DRM is illegal in most countries.
Live MPD streams
A manifest with type="dynamic" is live. It changes while you watch: minimumUpdatePeriod says how often the player reloads it, and timeShiftBufferDepth how far back you can rewind. The preview starts near the live edge. The printed ffmpeg command records from now until you press q, into a .ts file that stays playable even if the recording is cut. Nothing older than the rewind window can be recovered. We checked it on the DASH-IF live simulator, which the Live sample uses.
Limits
- CORS. A web page can only read a manifest whose server allows it. When the request fails, the page sends a second request that does not need permission: if the server answers it, CORS is the cause and the stream may be fine; if it does not, the server is unreachable (wrong address, network, certificate). Pasting the XML avoids the first problem, not the second. The player has the same limit for every segment.
- Referer, cookies and tokens. Browsers do not let a page choose the Referer or cookies of its requests. Streams that check them play on their own site only; ffmpeg can send them with
-refererand-headers. - DRM. Named, never decrypted, never played.
- What is not read. Remote Periods (
xlink:href) are not followed. ffmpeg does not copy subtitles out of a DASH manifest, so the command keeps picture and sound only; plain WebVTT subtitle files are listed with their URL, so you can save them and convert VTT to SRT. - Only the manifest. Analyzing downloads the .mpd file and nothing else. Segments are fetched only by the player, after you press Play.
For HLS streams (.m3u8), use the M3U8 tester, which plays them and reads their first segment.