dashpipe is one data plane with three stages under a single context.Context,
wired by pipeline.Run:
┌──────────────────────┐ Fragment ┌──────────────┐ decrypted ┌──────────────┐
live MPD/HLS ─► │ dash.Reader / hls.Reader │ ─(chan)─► │ cenc.Decrypt │ ───samples──►│ output.TSMux │ ──► io.Writer
└──────────────────────┘ └──────────────┘ └──────────────┘
live edge, AES-CTR/CBC in-proc, fMP4 → MPEG-TS,
refresh, retry, key supplied rebase + splice
drop ad Periods discontinuity
The reader is selected by pipeline.Config.Engine ("dash" default, or "hls").
Both readers satisfy the same fragmentReader interface (Run(ctx, chan<- Fragment)),
so the decrypt and mux stages are identical regardless of input protocol.
- Parses the MPD subset live DAI streams use (
mpd.go), expandsSegmentTimelineruns into concrete segments (timeline.go), selects the video Representation byWxHand an audio track (select.go). - Holds a cursor
~LiveEdgeTargetbehind the newest segment so it never requests an unpublished segment, and refreshes atminimumUpdatePeriod(reader.go). - Emits an ordered
Fragmentstream (init + media, per kind, tagged with Period/Rep, timescale, PTO). Failed inits/segments are non-fatal — it logs and advances so an ad-break splice can't kill the stream. - Content-only: clear DAI ad Periods are dropped (
DropAds);InAdBreak()lets the consumer tell when the live edge currently sits in a break.
- Parses the master playlist (variants + default audio rendition) and per-rendition
media playlists (
playlist.go), selecting the video variant byWxH. - fMP4 segments only: reads the
#EXT-X-MAPinit, extracts the media timescale viamp4.MediaTimescale, and emits the sameFragmentstream asdash(reader.go). - Holds audio and video in lockstep at a shared live-edge boundary and emits in sequence order, so a progressive player isn't starved of audio behind a video preroll.
- No ad Periods (single synthetic Period
"hls"), so there is noDropAds/slate path.
- Minimal ISO-BMFF box walk (
moof/traf/senc/saio/saiz/trun/tenc/schm) over the encrypted subsample ranges, using onlycrypto/aes+crypto/cipher. - Both CENC schemes:
cenc(AES-CTR) andcbcs(AES-CBC with the crypt:skip pattern and a constant IV — Apple SAMPLE-AES, used by HLS fMP4). DecryptFragment(media, key, Init)mutates the fragment's mdat in place.ParseInitreads theschm/tencboxes (scheme, KID, IV size, cbcs pattern + constant IV) from an init segment.- It decrypts with a supplied key. It never contacts a license server.
TSMuxerconverts decrypted samples to MPEG-TS viago-astits, doing H.264 AVCC→Annex B (re-inserting SPS/PPS at IDRs), AAC→ADTS, and PTS/DTS/PCR.- Both tracks are rebased onto one monotonic 90 kHz timeline (a single shared offset preserves A/V sync). At a Period change it continues the timeline, marks a TS discontinuity, and lets the next IDR carry fresh SPS/PPS — this is what makes an ad-break splice resume cleanly.
KeepAliveimplements the hold/slate gap strategies;Serveis the single-client HTTP TS sink.
- Owns the goroutine running the reader, the decrypt step, the muxer, the keep-alive ticker, and clean teardown on context cancel.
- Resolves keys: a static
Key, or aKeyProvidercallback for in-process rotation. - Surfaces terminal conditions (
ErrKeyRotation,ErrDecrypt) for the caller to map.
pipeline.KeyProvider— inject your DRM key source. The library stays free of any DRM client; this is where Widevine/PlayReady/etc. would live in your code.pipeline.GapStrategy— chooseGapDrop/GapHoldLastFrame/GapSlate.output.Serve/ a plainio.Writer— choose the sink.
Ad muxing, DRM license acquisition, HEVC, and a fMP4 output mux are deliberately out of scope. See the README support matrix.