Two targeted performance improvements in aimed at reducing overhead on
older devices
1. Avoid intermediate array for representation ID uniqueness check
Previously, all representation IDs were pushed into a temporary ids[]
array, then a Set - this PR eliminates the array allocation entirely.
2. Re-parse only affected AdaptationSets for dependency streams
Previously, when any stream had a dependency stream, every AdaptationSet
was re-parsed from XML. Now, only the specific AdaptationSet nodes that
contain streams with dependencies are re-parsed. This avoids redundant
XML traversal and parsing for the common case where most adaptation sets
have no dependency streams.
A new integration test has been added.
The use of `stream` within `stream` in `periods.js` has been refactored
to simplify management and avoid duplicates.
---------
Co-authored-by: Wojciech Tyczyński <tykus160@gmail.com>
Adds period caching to speed up manifest parsing. The aim of this
feature is to improve parsing of length multi-period DASH manifests on
low power devices.
This initial support is complete but not efficient, as it involves
conversion to XML and normal processing. It should only be used for
testing purposes. Improved support will be added in the future.
Tested with https://github.com/Dash-Industry-Forum/dash-json-schema
Note: This is only added to the experimental build.
This PR modernizes and optimizes XLink handling in the DASH parser by
removing the legacy flag-based behavior and replacing it with a
standards‑aligned, fast, and deterministic workflow. The changes improve
performance on large MPDs, simplify configuration, and ensure correct
XLink expansion according to DASH/XLink rules.
XLink processing is now automatically enabled only when needed. If the
MPD contains no XLinks, the parser skips processXlinks entirely.
Added polyfills for `Map.getOrInsert()` and
`Map.getOrInsertComputed()` from the TC39 upsert proposal and refactor
the codebase to use them.
These methods replace the common "check if key exists, then set default"
pattern with a single atomic operation. This improves code readability
and eliminates redundant map lookups throughout the player.
---------
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
Use PSSH string to cache init data in order to avoid creating duplicated
init data buffers. On streams with many periods due to ad insertion it
can make a difference - i.e. on stream with 50 periods this change
reduces used memory by init data buffers from 40 KB to 1 KB.
In DASH, PSSH can often be a duplicated data between protected periods
and/or adaptation sets.
Keeping `this` references in DASH uri callbacks leads to keeping
DashParser instances in memory after unloading.
Luckily this is only the issue in uncompiled mode, Closure Compiler
handles it somehow. It is though better to fix it in case we change
tooling some day.
In LCEVC dual-track implementation, both tracks share the same
originalVideoId, which causes one to be dropped when `getVideoTracks()`
returns them as a Map. This PR assigns a unique originalVideoId to each
track to ensure both appear correctly in the resolution menu
We had an issue where in SSAI content, 708 data was being split by ad
periods. Currently, when this happens, we reset the 708 decoder, which
means that captions are lost. Instead, we want to cache this decoder for
a later time. This change keeps track of continuous periods and caches
the 708 decoder when a period change happens to a discontinuous period.
This is so that it could be later restored if we go back to a continuous
period.
---------
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
Co-authored-by: Wojciech Tyczyński <tykus160@gmail.com>
This PR introduces support for `ProducerReferenceTime` tags in DASH.
When finding any, it emits event, similar to inband PRFTs added in
#4389.
Additionally, calculated start date from `ProducerReferenceTime` is used
as program start date, as it's more accurate value than
`MPD#availabilityStartTime` used before.
There's devices out there that are not compliant with the MSE spec. Such
as halting MSE when a secondary init segment is appended (webOS 3), or
failing to transition from a plain to encrypted init segment (Tizen
2017). While we initially prefer content workarounds, it's a time
consuming and trial & error process. For some devices it might not be
worth investing time into finding a proper workaround due to low usage.
We're giving people an alternative by resetting MSE when needed
(configurable). dash.js offers somewhat similar behavior
[here](https://github.com/Dash-Industry-Forum/dash.js/blob/a656ec709e7f92f76b392bf196ee9883da7928ce/src/streaming/controllers/StreamController.js#L672),
where MSE is reset before applying an encrypted init segment.
This PR introduces `crossBoundaryStrategy` in `StreamingConfiguration`.
It can be configured as following:
- KEEP - we're keeping MSE active, this is the default and the current
behavior.
- RESET - we'll always reset MSE when it crosses a boundary.
- RESET_TO_ENCRYPTED - we reset MSE when it crosses an encrypted
boundary, and we keep MSE afterwards. Additionally, we're not going to
reset when we're crossing a plain to plain boundary.
Each initSegmentReference now holds an `encrypted` and `boundaryEnd`
value. When configured with a different value than KEEP,
`StreamingEngine` will be instructed to fetch and append segment
references up until the boundary of the currently applied init segment.
We detect whether we're at a boundary in a few ways:
- Listening to the HTML5 MediaElement's `waiting` event, this'll
indicate that we do not have enough buffer to advance. If we're pretty
close to the boundary, we assume we're at the boundary.
- Due to subtle differences in the segment alignments, waiting wasn't
reliable. When close to a boundary, a timer is fired with the assumption
that "we'll reach the boundary at soon". I've set the threshold to 1
second, when playhead is further than the threshold, we'll skip checking
whether an MSE reset is due.
The implementation relies on the added properties in the init segment
reference, and the concept of a "Period" is avoided in StreamingEngine
to ensure it's compatible with HLS too.
---------
Co-authored-by: Álvaro Velad Galván <ladvan91@hotmail.com>
Co-authored-by: Wojciech Tyczyński <tykus160@gmail.com>
Previously, if the presentation delay was not specified either by the
manifest or by configuration, we defaulted to 1.5 * minBufferTime. That
approach worked for most content, but in some cases it could cause live
streams to start out with a seekRangeEnd that was before the live edge.
This would lead to a brief pause in the beginning of the presentation,
while the live edge caught up.
This changes the DASH parser to use the segmentAvailabilityDuration if
it is lower than 1.5 * minBufferTime, which fixes that issue.
It also changes how that part of the code to be formatted, in order to
hopefully make the increasingly-complex logic for determining the
presentation delay more clear.
Adds a replacement for removed JSDoc checks from ESLint v9.
Additionally fixes lots of issues found in the JSDoc, such as:
- missing `@param`/`@return` annotations
- bad formatting
- params order
- param name in the same line as type definition (tried to disable it,
but it was causing other issues and we didn't have lots of places with
such formatting)
Minor fixes in code found by Closure Compiler after fixing JSDoc are
also included.