1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! `media-plane` — the media-plane integration layer.
//!
//! The workspace's ingress/egress architecture
//! (`docs/superpowers/specs/2026-07-26-media-plane-architecture.md` in the
//! `rust-broadcast` repository) is four layers, not one pipeline glued
//! together per protocol:
//!
//! ```text
//! Dialer|Listener ──► [ByteStage]* ──► IngestSession ──► [IrTransform]* ──► TrunkWriter
//! (N sources) byte→byte demux IR→IR │
//! ▼
//! ┌──────── Trunk ────────┐
//! │ sample ring │
//! │ segment log │
//! │ EVENT log (90 kHz) │
//! └───────────────────────┘
//! subscribe() ─► SampleCursor ─► PushEgress (defined shape; no in-tree impl)
//! subscribe() ─► SegmentCursor ─► SegmentEgress (DVR, MABR, ROUTE, Smooth)
//! resolve() ─────────────────► ServedEgress (LL-HLS, DASH, catch-up)
//! ```
//!
//! `media-plane` is where that shape lives in code: it is the crate that ties
//! ingress, the byte layer, container demux (`transmux`), IR transforms, the
//! `Trunk`, and the three egress shapes together into one runnable pipeline.
//! It depends on `broadcast-common` for the shared drive contract
//! ([`broadcast_common::Stage`]) and clock/backpressure types
//! ([`broadcast_common::Timestamp`], [`broadcast_common::Demand`]).
//!
//! The byte layer, the whole [`Trunk`] (samples, segments, the 90 kHz event
//! log, live parts, reader wake), [`ingress`] (`Dialer`/`Listener`/
//! `IngestSession`/[`IngestDriver`]), and [`retention`] (hot/cold tiering over
//! a caller-supplied [`SegmentSink`]) are all exercised end to end by real
//! callers. Of the three egress shapes: [`ServedEgress`] and [`SegmentEgress`]
//! each have real production implementors — `hls-runtime`'s `HlsOrigin`
//! (`ServedEgress`) and multimux's `DashOrigin`/`LlDashOrigin`/
//! `SmoothManifestOrigin`/`SmoothFragmentOrigin`/`DvrRecorder`
//! (`ServedEgress`/`SegmentEgress`). [`PushEgress`] is a defined trait shape
//! with **no in-tree production implementor** — its only impls
//! (`RecordingPushEgress`, `WhepLikePushEgress`) live in this module's own
//! `#[cfg(test)]` block. multimux's real push path (SRT/RTMP/RTSP relay,
//! issue #744) does not use this trait at all; it drives its own
//! `multimux::push::PushTransport` directly off a `Trunk`'s sample cursor.
//! Step 3f added the acceptance furniture this doc describes (fuzz targets,
//! examples, the release lane) without changing any of that behaviour.
//!
//! # `no_std` note — the byte layer only, not the whole crate
//!
//! **Only [`byte_stage`]/[`byte_tap`]/[`byte_merge`] are `no_std` + `alloc`.**
//! [`Trunk`] and everything built on it ([`ingress`], [`egress`],
//! [`retention`]) are gated behind, and require, the `std` feature —
//! [`Trunk`] itself needs `std::sync::Mutex`/`Arc`/`Condvar` for cross-thread
//! sharing (see the [`trunk`] module docs for why that beats a `no_std`
//! spinlock crate here). Saying just "the plane is `no_std`-capable" without
//! this qualifier would be true of a third of the crate and false of the
//! rest, so it is stated plainly here rather than implied by the crate-level
//! `no_std` attribute alone. `std` is a default feature; `--no-default-features`
//! builds only the byte layer.
//!
//! # Recorded deviations (not defects — read before filing one)
//!
//! - [`byte_merge::MergePolicy`] deliberately has **no** `Hitless2022_7`
//! variant yet — SMPTE ST 2022-7 seamless switching needs an RTP
//! sequence-number parse this layer does not have; see the
//! [`byte_merge`] module docs. Tracked as #752.
//! - Pull sources (HLS/DASH/Smooth) are request-driven, not stream-driven,
//! and [`IngestSession::poll_transmit`] has no way to express "issue a GET
//! for this URL" yet — a recorded seam, not solved here; see the
//! [`ingress`] module docs' "Known seam" section.
//!
//! # The byte layer ([`byte_stage`], [`byte_tap`], [`byte_merge`])
//!
//! A byte stage is pre-demux, byte-to-byte, deadline-driven work: CAM
//! descramble, TS continuity/PCR repair, T2-MI/BBFrame inner-TS recovery,
//! program-PID filtering. See the [`byte_stage`] module docs for why it is
//! defined as a `Stage` specialisation rather than a second trait, and for the
//! exact form that was validated to compile.
//!
//! [`ByteTap`] sits alongside the byte stages, not in their chain: a
//! non-blocking positional observer that lets analysis (`dvb-conformance`,
//! `media-doctor watch`, #737's T-STD) see bytes a demuxer would reject. See
//! the [`byte_tap`] module docs for the non-blocking/`Lagged` trade and why it
//! is not a `Stage`.
//!
//! [`ByteMerge`] is the one place `N` byte sources reduce to one stream —
//! everything above the byte layer stays strictly single-input. See the
//! [`byte_merge`] module docs for why it operates on discrete messages, its
//! two policies, and why ST 2022-7 hitless switching is deliberately absent
//! rather than stubbed.
//!
//! # `Trunk`, the writer, and the cursors ([`trunk`], `std`-only)
//!
//! Above the byte layer and demux sits [`Trunk`]: the bounded sample ring and
//! segment log one [`TrunkWriter`] publishes into and any number of
//! [`SampleCursor`]/[`SegmentCursor`]s read from. It requires the `std`
//! feature (`Arc`/`Mutex`/`Condvar` for cross-thread sharing) — see the
//! [`trunk`] module docs for why that is the right line to draw rather than
//! reaching for a `no_std` spinlock crate, the benchmark
//! (`spikes/trunk-bench`) that shaped the design, and — critically, before
//! calling [`Trunk::subscribe`]/[`Trunk::subscribe_segments`] once per
//! connection — why supported reader count is single-digit by design.
//!
//! The segment log resolves a real contradiction: a DVR/archive consumer
//! must never miss a segment, but the writer must never block. See the
//! [`trunk`] module docs' "DVR contradiction" section for why the answer is
//! retention (a pinning cursor from [`Trunk::pin_segments`]), not
//! back-pressure, and for the three-way [`ArchiveOverrun`] trade a pinning
//! cursor's caller makes explicit when the retention bound is finally hit.
//!
//! The event log carries [`timed_metadata::TimedEvent`] on the trunk's own
//! 90 kHz absolute clock, addressable both by media time
//! ([`Trunk::events_between`]) and by segment
//! ([`Trunk::events_in_segment`]) — and, critically, never fabricates a
//! media time for an event that is only segment-relative (`emsg` v0) or
//! wall-clock-only (SCTE-35 `splice_schedule`) until the boundary or
//! [`timed_metadata::TimeAnchor`] it actually needs arrives. See the
//! [`trunk`] module docs' event-log section for the full B1 story.
//!
//! # Retention and `SegmentSink` ([`retention`], `std`-only)
//!
//! [`Retention`] is the hot/cold archive policy layered on top of the
//! segment log — [`Retention::HotOnly`] (the segment log alone) or
//! [`Retention::Tiered`], where a [`RetentionDriver`] drains a pinning
//! segment cursor into a caller-supplied, sans-IO [`SegmentSink`]. The
//! concrete disk/object-store adapter behind that sink is deliberately **not**
//! this crate's job — staying sans-IO is what lets the retention engine be
//! driven and tested without touching a filesystem, so the caller supplies
//! the IO. See the
//! [`retention`] module docs for why this reuses [`ArchiveOverrun`] verbatim
//! rather than inventing a parallel policy, why the pending hand-off queue
//! is bounded to exactly one in-flight segment, and the "cold, ask the
//! sink" answer [`RetentionDriver::locate`] gives for a catch-up request
//! against an evicted-from-hot segment (issue #746, DVR/catch-up).
extern crate alloc;
pub use ;
pub use ByteStage;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;