dash.js

Open Source Media Player

Seamless and reliable DASH streaming on any browser-based device

View onGitHub
5507
1728

Sponsors

Trusted by the industry leaders

Latest updates from GitHub

Last TTML subtitle in segment not displayed when cue has no explicit end time##### Environment - [x] The MPD passes the DASH-IF Conformance Tool on https://conformance.dashif.org/ - [x] The stream has correct Access-Control-Allow-Origin headers (CORS) - [x] There are no network errors such as 404s in the browser console when trying to play the stream - [x] The issue observed is not mentioned on https://github.com/Dash-Industry-Forum/dash.js/wiki/FAQ - [x] The issue occurs in the latest reference client on http://reference.dashif.org/dash.js/ and not just on my page * Link to playable MPD file: N/A (see TTML source below) * Dash.js version: 5.2.0 (development branch, commit 3292b235) * Browser name/version: all (logic bug, not browser-specific) * OS name/version: all ##### Steps to reproduce 1. Create a DASH stream with this TTML as the subtitle track (packaged into fragmented MP4 with `stpp` codec): ```xml <?xml version="1.0" encoding="utf-8"?> <tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" xml:lang="en" ttp:timeBase="media" ttp:cellResolution="32 15"> <head> <styling> <style xml:id="s0" tts:fontFamily="sansSerif" tts:fontSize="100%" tts:textAlign="center" tts:color="#FFFFFF"/> <style xml:id="s1" tts:color="#00FF00" tts:backgroundColor="#000000"/> <style xml:id="s3" tts:color="#FF0000" tts:backgroundColor="#000000"/> </styling> <layout> <region xml:id="r0" tts:origin="10% 80%" tts:extent="80% 15%" tts:displayAlign="before"/> </layout> </head> <body style="s0"><div region="r0"> <p xml:id="sub1" begin="00:00:01.000" end="00:00:04.000"> <span style="s1">Subtitle 1 — has explicit end (4s)</span> </p> <p xml:id="sub2" begin="00:00:05.000" end="00:00:08.000"> <span style="s1">Subtitle 2 — has explicit end (8s)</span> </p> <p xml:id="sub3" begin="00:00:09.000"> <span style="s3">Subtitle 3 — NO explicit end</span> </p> </div></body> </tt> ``` 2. Play the stream with subtitles enabled. 3. Watch for the three subtitles at t=1s, t=5s, and t=9s. ##### Observed behavior Only the first two subtitles are displayed. The third one (starting at 9s) never appears. No error in the console. | Cue | begin | end | Result | |---|---|---|---| | Subtitle 1 | 1s | 4s | Displayed | | Subtitle 2 | 5s | 8s | Displayed | | Subtitle 3 | 9s | *none* | **Never displayed** | The third cue has no explicit `end` attribute in the TTML source. This is valid TTML but causes the parser to silently drop it. ##### Console output ``` No error — the bug is silent. ``` ##### Expected behavior All three subtitles should be displayed. The third cue should use the segment's end time as its end time. ##### Practical impact Standard broadcast content (DASH-IF test vectors, BBC Elephant's Dream, HbbTV Reference App) is not affected — their TTML cues all have explicit `end` attributes. The bug requires a `<p>` with no explicit `end`, which is valid TTML but uncommon in professional workflows.Opened by PascalThuet17 hours ago
parseXml: optimize DASH SegmentTimeline <S> parsing**Is your feature request related to a problem? Please describe.** Yes. Large DASH live DVR manifests block the main thread during parsing. This issue is about the XML parser project [`@svta/cml-xml`](https://github.com/streaming-video-technology-alliance/common-media-library/tree/main/libs/xml), not about changing dash.js itself. dash.js is only the workload used to measure the problem. On an XL synthetic manifest (50 Periods x 4 AdaptationSets x 500 `S` entries, about 102k XML nodes), the full dash.js manifest parse takes about 40 ms on an M1. The XML parser alone takes about 30.5 ms of that total, or 62%. About 98% of the nodes in these manifests are `SegmentTimeline` `<S>` entries. They are simple self-closing nodes with only integer attributes like `t`, `d`, `r`, and `k`, but they still go through the full generic parsing path. **Describe the solution you'd like** Add a specialized fast path in `parseXml()` for self-closing DASH `SegmentTimeline` `<S>` nodes. The proposed implementation should happen in [`@svta/cml-xml`](https://github.com/streaming-video-technology-alliance/common-media-library/tree/main/libs/xml), inside `parseXml()`. **Expected behavior** - detect `<S ... />` nodes while parsing - parse `t`, `d`, `r`, and `k` directly as integers - skip `unescapeHtml()` for numeric attributes - avoid unnecessary generic attribute parsing work for these nodes - reuse a shared empty `childNodes` array for self-closing nodes when safe - preserve the current output shape In a synthetic benchmark, a specialized eager parser for `<S>` nodes reduced the XML parsing cost from about 39.8 ms to about 19.1 ms on the XL manifest, a reduction of about 52% (`~2.1x` faster). **Describe alternatives you've considered** - Lazy parsing of `SegmentTimeline.S` Rejected. These entries are typically consumed immediately after manifest parsing for duration calculation, segment counting, and segment lookup. - A local XML parser variant inside dash.js Rejected. A local clone was slower than the current `@svta/cml-xml` implementation in synthetic benchmarks. - A downstream dash.js optimization in `DashParser.processNode()` Useful, but secondary. For example, replacing `arrayNodes.indexOf()` with `Set.has()` helps, but the main bottleneck is still `cmlParseXml()`. **Additional context** **Example hot-case nodes** ```xml <S t="123456" d="180000" /> <S d="180000" r="14" /> <S d="180000" r="-1" k="3" /> ``` **Reproduction** ```bash node test/bench-manifest-parsing.mjs node test/bench-parsexml.mjs ``` **Real public live DVR example** - `https://livesim2.dashif.org/livesim2/segtimeline_1/tsbd_21600/testpic_2s/Manifest.mpd` - verified on March 10, 2026 - `timeShiftBufferDepth="PT6H"` - about 170 KB - about 5402 literal `<S>` nodes in the fetched MPD **Larger multi-period variant** - `https://livesim2.dashif.org/livesim2/segtimeline_1/tsbd_21600/periods_60/continuous_1/testpic_2s/Manifest.mpd` - `timeShiftBufferDepth="PT6H"` - about 901 KB - 361 periods - about 5942 literal `<S>` nodes in the fetched MPD **Why this matters for users** - on desktop, `39.8 ms -> 19.1 ms` still means about 52% less XML parsing work on the main thread - on lower-end Smart TV / STB class hardware, that same reduction can translate to roughly 100-200 ms less blocking per manifest refresh - on live DVR streams, this cost repeats on every manifest update cycle, so users can experience recurring UI hitching rather than a one-time delay **Environment used for the measurements above** - dash.js v5.2.0 - [`@svta/cml-xml`](https://github.com/streaming-video-technology-alliance/common-media-library/tree/main/libs/xml) 1.0.1 **What would make this complete** - no observable output change for current consumers - measurable improvement on large DASH manifests with dense `SegmentTimeline` - no regression on non-DASH XML inputs - benchmark or test coverage proving both correctness and performance Opened by PascalThuet5 days ago
DVBSelector: last BaseURL never selected with equal weights (off-by-one in weighted random)##### Environment - [x] The MPD passes the DASH-IF Conformance Tool on https://conformance.dashif.org/ - [x] The stream has correct Access-Control-Allow-Origin headers (CORS) - [x] There are no network errors such as 404s in the browser console when trying to play the stream - [x] The issue observed is not mentioned on https://github.com/Dash-Industry-Forum/dash.js/wiki/FAQ - [x] The issue occurs in the latest reference client on http://reference.dashif.org/dash.js/ and not just on my page * Link to playable MPD file: `samples/advanced/mpds/basic-cmcd-config.mpd` (3 BaseURLs, no `dvb:weight`) — remove the `<ContentSteering>` element to expose the bug (Content Steering overrides DVB selection) * Dash.js version: 5.2.0 (development branch) * Browser name/version: all (logic bug, not browser-specific) * OS name/version: all ##### Steps to reproduce 1. Load an MPD with 2+ `<BaseURL>` elements at the same priority, without `dvb:weight` (defaults to 1) and without `<ContentSteering>` 2. Observe which CDN is selected across multiple sessions > **Note:** `samples/advanced/mpds/basic-cmcd-config.mpd` has 3 BaseURLs without `dvb:weight` but uses Content Steering, which overrides DVB selection and masks the bug. ##### Observed behavior The last BaseURL in a weighted group is never selected. `DVBSelector.js` line 114: ```javascript rn = Math.floor(Math.random() * (totalWeight - 1)); ``` `Math.random()` returns `[0, 1)`, so `Math.floor(Math.random() * totalWeight)` already produces `[0, totalWeight-1]`. The extra `- 1` shrinks the range and excludes the last CDN. **Example : 2 equal-weight CDNs** (most common multi-CDN setup): `totalWeight = 2`, `cumulWeights = [1, 2]`: ``` rn = Math.floor(Math.random() * 1) = always 0 → always cdn-a, cdn-b never selected ``` ##### Console output ``` No error — the bug is silent. ``` ##### Expected behavior Each BaseURL selected with probability proportional to its weight (RFC 2782). With equal weights: ~50/50 for 2 CDNs, ~33/33/33 for 3 CDNs. Opened by PascalThuet7 days ago

More news on

Contributors