Skip to content

C server: background live DASH->HLS engine (port of the Swift worker) - #4

Merged
testingbetaversion merged 1 commit into
mainfrom
c-server-live-dash-engine
Jul 30, 2026
Merged

C server: background live DASH->HLS engine (port of the Swift worker)#4
testingbetaversion merged 1 commit into
mainfrom
c-server-live-dash-engine

Conversation

@testingbetaversion

Copy link
Copy Markdown
Owner

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.

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
testingbetaversion merged commit a47beeb into main Jul 30, 2026
0 of 4 checks passed
@testingbetaversion
testingbetaversion deleted the c-server-live-dash-engine branch July 30, 2026 18:28
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant