Closed dzianis-dashkevich closed 7 months ago
Attention: Patch coverage is 96.72131%
with 6 lines
in your changes are missing coverage. Please review.
Project coverage is 86.26%. Comparing base (
75f7b1a
) to head (bad97ec
). Report is 1 commits behind head on main.
Files | Patch % | Lines |
---|---|---|
src/util/media-sequence-sync.js | 96.29% | 4 Missing :warning: |
src/segment-loader.js | 95.91% | 2 Missing :warning: |
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
The Problem
The player repeats segments sometimes.
To understand better the root cause of the problem, we should understand the following concepts:
A transport stream needs timing information to ensure Video and Audio are shown at the right time and in sync. The most important timecodes are:
The basic unit of a timecode is the Tick. There are 90,000 Ticks in a second (90kHz).
mux.js reads, parses PES packets, and extracts DTS and PTS values. Both values are 33-bit. These timestamps monotonically increase until they hit a 33-bit boundary, which is 8589934592 (2^33). Once they hit 2^33, they roll over to 0, and this process repeats. This "feature" is called timestamp rollover.
Timestamp rollover happens every ~26.5 hours:
mux.js stores the first-encountered DTS value within one timeline and uses it as a reference value to handle rollovers for upcoming timestamps. It adjusts timestamp values based on the direction (backward/forward). Forward adjustment: PTS/DTS value + 2^33. Backward adjustment: PTS/DTS value - 2^33.
Now that we know about PTS, DTS, PCR, and timestampRollover, we need to understand two more concepts: discontinuities(or timelines) and timestamp offsets.
Discontinuities signal that we should reset the decoder since we are switching to different content (e.g., it was encoded Differently, it has different timestamps, etc...). The following examples are common discontinuities cases:
vhs uses the internal term "timeline" since when we hit discontinuity, we switch to a different timeline because PTS/DTS timestamps are completely different from the content before discontinuity.
Now we should understand
timestampOffset
concept:The player has a progress bar with a predictable timeline for a user. Segment's PTS and DTS values do not align with the player's time, and they may "jump" during discontinuities. That is why we use
timestampOffset
to map the segment's timestamps to the time in our player.So, Segments PTS/DTS +
timestampOffset
= player's time for a segment.Ok, now let's put everything together:
Now, the same stream may contain different renditions. Some renditions may have timestamp rollover, while others may not. This means that even if we switch from one rendition to another within the same timeline (no discontinuity), we may still have completely different timestamps. Since we already use timestampOffset adjustment, the easiest and most obvious fix would be re-calculating timestampOffset after each rendition switch.
VHS uses SyncController in order to understand which segment to load after the rendition switch. Sometimes, we don't have enough information to make a proper decision because of the many factors, such as:
Sometimes, the player may select segments already appended to the source buffer. Previously - such appends did not affect the source buffer. Since timestampOffset stays the same, and PTS/DTS values are the same - a browser simply "replaces" already appended data with new data.
Unfortunatelly, re-calculating timestamp offset on every rendition switch makes us less resilient to such appends since now the browser places such segments on top of the buffer (instead of the same place as it was).
This PR is another try to improve the syncing mechanism to avoid selecting already appended data between rendition switches.
Specific Changes proposed
markAppended
for each segment for the mediaSequence sync strategyPossible Alternative to avoid using
timestampOffsets
Testing Matrix
Requirements Checklist