C server: background live DASH->HLS engine (port of the Swift worker) - #4
Merged
Merged
Conversation
The MPD was translated inside the request handler, which the Swift
LiveMPDToM3U8 worker never did. That broke playback three ways:
* EXT-X-MEDIA-SEQUENCE was derived from $Time$/duration, which does not
advance by exactly 1 per segment (RFC 8216 4.3.3.2) on a
SegmentTimeline with varying @d. Spec-literal clients read every
reload as a huge seek and reload/reseek forever instead of settling.
* No discontinuity detection, so a spliced timeline left MSE with a
hole it could never fill and the player stuck at the gap.
* No state across reloads, and every reload paid the origin's latency
on mongoose's single event loop -- which is what made the panel's
Start/Stop buttons feel dead.
rs_live restores the Swift design: per stream a director thread
(rendition discovery + master playlist) and one worker per
representation that polls the MPD, downloads segments with a bounded
parallel fan-out, CENC-decrypts them and holds them in a bounded
in-memory queue. Media sequence is the append counter, discontinuities
come from tfdt, and the live-edge hold-back is carried over as-is.
Playlist requests are now a string copy and segment requests a memcpy,
so no playback route touches the network.
The ClearKey is now chosen by matching the init segment's default KID
instead of always taking the first configured pair.
Start/stop only flip state and signal the engine; stopping flags and
returns, with the join deferred to the one-second timer. Running streams
resume by themselves after a restart. The old prefetch cache is removed,
superseded by the engine's queue.
Logging: the de-duplication window was 30s, so a stop-then-start left no
trace at all. It now collapses only exact repeats inside one second, with
lifecycle events exempt entirely, and the engine reports every poll,
segment, decrypt, discontinuity and prune. Ring grown to 20000 and
log_record is mutex-guarded, since workers write to it.
Built clean under cmake and swift build with -Wall -Wextra -Wconversion;
restream_selftest 201/201. Not yet checked against the Swift binary for
output parity.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
testingbetaversion
added a commit
that referenced
this pull request
Aug 20, 2026
…#4) The MPD was translated inside the request handler, which the Swift LiveMPDToM3U8 worker never did. That broke playback three ways: * EXT-X-MEDIA-SEQUENCE was derived from $Time$/duration, which does not advance by exactly 1 per segment (RFC 8216 4.3.3.2) on a SegmentTimeline with varying @d. Spec-literal clients read every reload as a huge seek and reload/reseek forever instead of settling. * No discontinuity detection, so a spliced timeline left MSE with a hole it could never fill and the player stuck at the gap. * No state across reloads, and every reload paid the origin's latency on mongoose's single event loop -- which is what made the panel's Start/Stop buttons feel dead. rs_live restores the Swift design: per stream a director thread (rendition discovery + master playlist) and one worker per representation that polls the MPD, downloads segments with a bounded parallel fan-out, CENC-decrypts them and holds them in a bounded in-memory queue. Media sequence is the append counter, discontinuities come from tfdt, and the live-edge hold-back is carried over as-is. Playlist requests are now a string copy and segment requests a memcpy, so no playback route touches the network. The ClearKey is now chosen by matching the init segment's default KID instead of always taking the first configured pair. Start/stop only flip state and signal the engine; stopping flags and returns, with the join deferred to the one-second timer. Running streams resume by themselves after a restart. The old prefetch cache is removed, superseded by the engine's queue. Logging: the de-duplication window was 30s, so a stop-then-start left no trace at all. It now collapses only exact repeats inside one second, with lifecycle events exempt entirely, and the engine reports every poll, segment, decrypt, discontinuity and prune. Ring grown to 20000 and log_record is mutex-guarded, since workers write to it. Built clean under cmake and swift build with -Wall -Wextra -Wconversion; restream_selftest 201/201. Not yet checked against the Swift binary for output parity. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The MPD was translated inside the request handler, which the Swift LiveMPDToM3U8 worker never did. That broke playback three ways:
rs_live restores the Swift design: per stream a director thread (rendition discovery + master playlist) and one worker per representation that polls the MPD, downloads segments with a bounded parallel fan-out, CENC-decrypts them and holds them in a bounded in-memory queue. Media sequence is the append counter, discontinuities come from tfdt, and the live-edge hold-back is carried over as-is. Playlist requests are now a string copy and segment requests a memcpy, so no playback route touches the network.
The ClearKey is now chosen by matching the init segment's default KID instead of always taking the first configured pair.
Start/stop only flip state and signal the engine; stopping flags and returns, with the join deferred to the one-second timer. Running streams resume by themselves after a restart. The old prefetch cache is removed, superseded by the engine's queue.
Logging: the de-duplication window was 30s, so a stop-then-start left no trace at all. It now collapses only exact repeats inside one second, with lifecycle events exempt entirely, and the engine reports every poll, segment, decrypt, discontinuity and prune. Ring grown to 20000 and log_record is mutex-guarded, since workers write to it.
Built clean under cmake and swift build with -Wall -Wextra -Wconversion; restream_selftest 201/201. Not yet checked against the Swift binary for output parity.