Skip to main content

media_pp/elements/driver/webrtc/
track.rs

1use std::{
2    sync::{
3        Arc, Mutex,
4        atomic::{AtomicU64, Ordering},
5    },
6    time::Duration,
7};
8
9use ffmpeg_next as ffmpeg;
10
11use crate::pp_log::{PpLog, pp_error, pp_info};
12use crossbeam_channel::{Receiver, RecvTimeoutError, Sender, TrySendError, select};
13use str0m::{
14    change::{SdpAnswer, SdpOffer},
15    format::Codec,
16    media::{Direction, MediaKind, Mid},
17};
18
19use crate::{
20    buffer::MediaBuffer,
21    bus::{Bus, BusEvent},
22    contract::{InputContract, OutputContract, PortContract},
23    control::{
24        ControlMsg, ControlReceiver, RequestKind, apply_finish, apply_one, drain_control,
25        wait_out_pause,
26    },
27    element::{Element, ElementType, Sink, Source, SourceElement, element_pp_log},
28    error::Result,
29    pad::SrcPad,
30};
31
32use super::{
33    command::{Command, TrackId, WebRtcError},
34    stream_info::{WebRtcStreamInfo, annex_b_nalus, str0m_codec},
35};
36
37/// The encoded kind a str0m track of `kind` carries. Both media flow
38/// through `MediaBuffer::Packet`, so this is the only thing that tells an
39/// audio track apart from a video one at wiring time.
40fn packet_kind(kind: MediaKind) -> crate::contract::MediaKind {
41    match kind {
42        MediaKind::Audio => crate::contract::MediaKind::AudioPacket,
43        MediaKind::Video => crate::contract::MediaKind::VideoPacket,
44    }
45}
46
47/// The four-byte Annex-B start code. The three-byte form is equally valid
48/// and is recognized on input, but nothing here has a reason to emit it.
49const START_CODE: [u8; 4] = [0, 0, 0, 1];
50
51/// Whether `data` opens an Annex-B access unit. Every Annex-B NAL unit is
52/// introduced by a start code, keyframe or not, so one look at the front of
53/// the first payload settles which form a track carries.
54fn starts_with_start_code(data: &[u8]) -> bool {
55    data.starts_with(&START_CODE) || data.starts_with(&START_CODE[1..])
56}
57
58/// Whether RTP carries `codec` as an Annex-B byte stream whose parameter
59/// sets travel in the stream itself. Only these have headers to put in front
60/// of a keyframe; another codec's extradata configures a decoder and has no
61/// business in the bitstream.
62fn annex_b_codec(codec: Codec) -> bool {
63    matches!(codec, Codec::H264 | Codec::H265 | Codec::H266)
64}
65
66/// Whether an access unit already carries the parameter sets a receiver
67/// needs to start decoding, or `None` for a codec whose NAL header layout
68/// this does not read.
69fn carries_parameter_sets(payload: &[u8], codec: Codec) -> Option<bool> {
70    let (sps, pps) = match codec {
71        Codec::H264 => (7, 8),
72        Codec::H265 => (33, 34),
73        Codec::H266 => (15, 16),
74        _ => return None,
75    };
76    // Three codecs, three places to read one field from. H.264 keeps the
77    // type in the low five bits of a one-byte header; HEVC widened the
78    // header to two bytes and took six bits of the first for the type; VVC
79    // kept two bytes but moved the type into the top five bits of the
80    // *second*, the first now being nothing but the layer id.
81    let nal_type = |nalu: &&[u8]| match codec {
82        Codec::H264 => nalu.first().map(|byte| byte & 0x1f),
83        Codec::H266 => nalu.get(1).map(|byte| byte >> 3),
84        _ => nalu.first().map(|byte| (byte >> 1) & 0x3f),
85    };
86    let present: Vec<u8> = annex_b_nalus(payload).iter().filter_map(nal_type).collect();
87    Some(present.contains(&sps) && present.contains(&pps))
88}
89
90/// Reads an `AVCDecoderConfigurationRecord` — what a container demuxer puts
91/// in `extradata` — as Annex-B parameter sets plus the NAL length prefix
92/// size its packets use.
93///
94/// The outer `None` is for anything that is not such a record, which is how
95/// Annex-B extradata, an unrelated codec's configuration, and HEVC's
96/// differently laid out `hvcC` all keep the verbatim handling they had
97/// before. The inner one is a record that holds no parameter sets, which is
98/// a different thing entirely and still worth having: the two facts a record
99/// carries are separate fields, so one can be absent while the other is
100/// exactly what a caller needs. Such a file keeps its parameter sets in the
101/// bitstream, and refusing the record would leave nothing to state the
102/// length prefix with — the packets are still length-prefixed either way.
103fn avcc_parameter_sets(config: &[u8]) -> Option<(Option<Vec<u8>>, usize)> {
104    // configurationVersion(1) profile(3) lengthSizeMinusOne(1) numOfSPS(1),
105    // then each parameter set as a 16-bit length and its bytes, SPS first.
106    const HEADER: usize = 6;
107    if config.len() < HEADER || config[0] != 1 || starts_with_start_code(config) {
108        return None;
109    }
110    let nal_length_size = (config[4] & 0x03) as usize + 1;
111    let mut parameter_sets = Vec::new();
112    let mut offset = HEADER - 1;
113    // The SPS count is five bits (the top three are reserved ones); the PPS
114    // count that follows them is a whole byte.
115    for count_mask in [0x1f_u8, 0xff] {
116        let count = config.get(offset)? & count_mask;
117        offset += 1;
118        for _ in 0..count {
119            let length =
120                u16::from_be_bytes([*config.get(offset)?, *config.get(offset + 1)?]) as usize;
121            offset += 2;
122            let end = offset.checked_add(length)?;
123            if end > config.len() || length == 0 {
124                return None;
125            }
126            parameter_sets.extend_from_slice(&START_CODE);
127            parameter_sets.extend_from_slice(&config[offset..end]);
128            offset = end;
129        }
130    }
131    Some((
132        (!parameter_sets.is_empty()).then_some(parameter_sets),
133        nal_length_size,
134    ))
135}
136
137/// Rewrites a length-prefixed access unit as Annex-B, replacing each NAL
138/// unit's length with a start code.
139///
140/// Returns `None` when the payload does not consume exactly — a truncated
141/// unit, or a prefix size that does not match the one the `avcC` record
142/// declared. Guessing at either would emit a stream that looks well formed
143/// and decodes to nothing.
144fn length_prefixed_to_annex_b(payload: &[u8], nal_length_size: usize) -> Option<Vec<u8>> {
145    let mut annex_b = Vec::with_capacity(payload.len() + START_CODE.len());
146    let mut offset = 0;
147    while offset < payload.len() {
148        let prefix = payload.get(offset..offset + nal_length_size)?;
149        let length = prefix
150            .iter()
151            .fold(0usize, |value, byte| (value << 8) | usize::from(*byte));
152        offset += nal_length_size;
153        let end = offset.checked_add(length)?;
154        if length == 0 || end > payload.len() {
155            return None;
156        }
157        annex_b.extend_from_slice(&START_CODE);
158        annex_b.extend_from_slice(&payload[offset..end]);
159        offset = end;
160    }
161    (!annex_b.is_empty()).then_some(annex_b)
162}
163
164/// Rebuilds `packet` around a new payload, carrying every field the RTP
165/// write needs with it.
166///
167/// The time base above all: `Packet::copy` leaves it 0/0, and a packet str0m
168/// cannot build a `MediaTime` from is dropped rather than refused — the
169/// failure would be a peer that simply receives nothing.
170fn rewritten_packet(packet: &ffmpeg::Packet, payload: &[u8]) -> MediaBuffer {
171    let mut rewritten = ffmpeg::Packet::copy(payload);
172    rewritten.set_time_base(packet.time_base());
173    rewritten.set_pts(packet.pts());
174    rewritten.set_dts(packet.dts());
175    rewritten.set_stream(packet.stream());
176    rewritten.set_flags(packet.flags());
177    rewritten.set_duration(packet.duration());
178    MediaBuffer::Packet(Arc::new(rewritten))
179}
180
181/// The endpoints a track actually has, which is exactly what its
182/// negotiated [`Direction`] allows — a `SendOnly` track carries no
183/// `WebRtcTrackSource` because nothing will ever arrive on it, and a
184/// `RecvOnly` one carries no [`WebRtcTrackSink`] because str0m has no
185/// send capability for it.
186///
187/// The variant *is* the direction, so there is no separate field the two
188/// could disagree with. Pushing into a sink that does not exist, or
189/// waiting on a source that does not, is a compile error rather than
190/// something that silently does nothing.
191///
192/// Fixed for the life of the track: these are handed out once, when the
193/// track attaches, and a remote peer that later renegotiates a different
194/// direction is reported on the [`Bus`] instead (see
195/// [`WebRtcHandle::next_track`]).
196pub enum TrackEndpoints {
197    /// `Direction::SendOnly` — outbound only.
198    Send(WebRtcTrackSink),
199    /// `Direction::RecvOnly` — inbound only.
200    Recv(WebRtcTrackSource),
201    /// `Direction::SendRecv` — both, on the one track.
202    SendRecv(WebRtcTrackSink, WebRtcTrackSource),
203    /// `Direction::Inactive` — neither, for now. Still handed out: the
204    /// track exists and its `mid` is negotiated, so a caller matching
205    /// attachments against its own [`WebRtcHandle::add_track`] calls has
206    /// to see it.
207    Inactive,
208}
209
210/// One newly-attached track, from [`WebRtcHandle::next_track`].
211pub struct AttachedTrack {
212    /// Matches what [`WebRtcHandle::add_track`] returned for a track this
213    /// side requested. A track the *remote* peer added has an id issued
214    /// here that the caller has never seen before — which is how the two
215    /// are told apart.
216    pub id: TrackId,
217    /// The `mid` str0m assigned during the SDP exchange.
218    pub mid: Mid,
219    /// Audio or video.
220    pub kind: MediaKind,
221    /// What can actually be done with this track — see [`TrackEndpoints`].
222    pub endpoints: TrackEndpoints,
223}
224
225/// Cheaply-cloneable handle for requesting new tracks, completing
226/// renegotiation, and picking up newly-attached tracks — same spirit as
227/// [`crate::elements::AppSourceHandle`]. Cloning shares one queue of
228/// pending [`WebRtcHandle::next_track`] results, same as any other
229/// multi-consumer channel — only one clone's call actually receives a
230/// given track, so in practice only one place in the app should be
231/// draining it.
232#[derive(Clone)]
233pub struct WebRtcHandle {
234    pub(super) next_id: Arc<AtomicU64>,
235    pub(super) command_tx: Sender<Command>,
236    pub(super) new_track_rx: Receiver<AttachedTrack>,
237}
238
239impl WebRtcHandle {
240    /// Requests a new track of `kind`/`direction`. Blocks only while the
241    /// peer's bounded command queue is full; once the command is accepted,
242    /// returns the locally assigned [`TrackId`]. This does not mean SDP
243    /// negotiation has completed — receive the attached track through
244    /// [`WebRtcHandle::next_track`]. Returns [`WebRtcError::Closed`] without
245    /// yielding a `TrackId` if the peer loop has already stopped.
246    ///
247    /// `codec` is what [`WebRtcTrackSink::consume`] on the resulting track
248    /// will actually be fed (an encoder's output, or a packet relayed
249    /// verbatim from another track) — used to pick the matching payload
250    /// type out of whatever this connection negotiates for the track,
251    /// instead of guessing. If this connection does not negotiate `codec`,
252    /// consuming a packet returns
253    /// [`WebRtcError::OutboundCodecNotNegotiated`].
254    ///
255    /// Declaring one codec and pushing another is not detected anywhere:
256    /// str0m packetizes whatever bytes it is handed under the payload type
257    /// chosen here, so the mismatch leaves as a well-formed stream that no
258    /// receiver can decode. Audio is where this bites — WebRTC negotiates
259    /// Opus, and there is no AAC payload type to fall back to.
260    pub fn add_track(
261        &self,
262        kind: MediaKind,
263        direction: Direction,
264        codec: Codec,
265    ) -> Result<TrackId> {
266        let id = TrackId(self.next_id.fetch_add(1, Ordering::Relaxed));
267        self.command_tx
268            .send(Command::AddTrack(id, kind, direction, codec))
269            .map_err(|_| WebRtcError::Closed)?;
270        Ok(id)
271    }
272
273    /// Blocks until the next track attaches — either one requested via
274    /// [`WebRtcHandle::add_track`] (on either side) once its `Mid` exists,
275    /// or one the remote peer added on its own. `Err` once `WebRtcPeer`
276    /// (and its `run`) is gone and every already-attached track has been
277    /// drained.
278    ///
279    /// Which track this is has to be established from
280    /// [`AttachedTrack::id`]: a remote peer adding a track of its own is
281    /// delivered through this same queue, so "the call right after my
282    /// `add_track`" is not a guarantee of anything. Match the id against
283    /// what `add_track` returned.
284    ///
285    /// [`AttachedTrack::endpoints`] carries only what the track's
286    /// negotiated direction actually permits. That direction is read once,
287    /// as the track attaches, and the endpoints are never re-issued — so a
288    /// remote peer that renegotiates a different direction afterwards
289    /// makes them wrong. That case is reported as
290    /// [`WebRtcError::DirectionChanged`] on the [`Bus`] rather than
291    /// silently tolerated; recovering from it means tearing the track down
292    /// and adding a new one.
293    ///
294    /// Both endpoints expose their currently negotiated codec lists. A send
295    /// endpoint for a track this side requested already selects the codec
296    /// passed to [`WebRtcHandle::add_track`]. For a track the remote side
297    /// added, choose the application's encoder output from
298    /// [`WebRtcTrackSink::negotiated_codecs`] and pass it to
299    /// [`WebRtcTrackSink::set_codec`] before pushing packets. The matching
300    /// source separately reports the codec actually received once RTP starts.
301    pub fn next_track(&self) -> Result<AttachedTrack> {
302        self.new_track_rx
303            .recv()
304            .map_err(|_| WebRtcError::Closed.into())
305    }
306
307    /// Feeds a remote answer back in, completing a renegotiation started by
308    /// [`WebRtcHandle::add_track`]. A no-op if `WebRtcPeer` (and its `run`)
309    /// is already gone.
310    pub fn set_answer(&self, answer: SdpAnswer) {
311        let _ = self.command_tx.send(Command::SetAnswer(answer));
312    }
313
314    /// Accepts a fresh offer from the *remote* peer (their own
315    /// renegotiation) and returns the resulting answer for the caller to
316    /// ship back over its own signaling transport. Blocks until
317    /// `WebRtcPeer::run` has actually applied it.
318    pub fn accept_remote_offer(&self, offer: SdpOffer) -> Result<SdpAnswer> {
319        let (reply_tx, reply_rx) = crossbeam_channel::bounded(0);
320        self.command_tx
321            .send(Command::AcceptOffer(offer, reply_tx))
322            .map_err(|_| WebRtcError::Closed)?;
323        reply_rx
324            .recv()
325            .map_err(|_| WebRtcError::Closed)?
326            .map_err(Into::into)
327    }
328}
329
330/// One outbound track. A plain [`Sink`] — no bespoke push API, it links
331/// into a [`crate::pipeline::ChainBuilder`] exactly like
332/// [`crate::elements::RtspSink`] or any other terminal sink.
333/// `consume()` only ever hands off to `WebRtcPeer::run`'s own thread via a
334/// channel send; the actual str0m write happens over there.
335///
336/// Its negotiated codec capabilities are available immediately through
337/// [`WebRtcTrackSink::negotiated_codecs`]. The outbound selection is initialized
338/// automatically for a track created by [`WebRtcHandle::add_track`]. A
339/// send-capable track added by the remote peer instead requires one validated
340/// [`WebRtcTrackSink::set_codec`] call before packets are consumed; omitting it
341/// returns a typed error rather than guessing an RTP payload type.
342pub struct WebRtcTrackSink {
343    pp_log: PpLog,
344    id: TrackId,
345    /// What this track was negotiated to carry — audio or video.
346    kind: MediaKind,
347    codec: Option<Codec>,
348    negotiated_codecs: Arc<Mutex<Vec<Codec>>>,
349    command_tx: Sender<Command>,
350    /// libavcodec may express encoder delay as a negative first PTS (Opus is
351    /// a common example), while RTP media time is unsigned. The first packet
352    /// establishes one track-wide shift so relative timing is preserved.
353    timestamp_offset: Option<i64>,
354    /// The Annex-B codec headers to put back in front of every keyframe —
355    /// see [`WebRtcTrackSink::set_source_parameters`].
356    parameter_sets: Option<Vec<u8>>,
357    /// The NAL length prefix size of incoming payloads, set when
358    /// [`WebRtcTrackSink::set_source_parameters`] was given an `avcC` record
359    /// instead of Annex-B headers. Its presence is what says every payload
360    /// has to be rewritten before RTP.
361    nal_length_size: Option<usize>,
362    /// Whether the first packet's bitstream form has been examined. A track
363    /// does not change form part-way through, so the check costs one
364    /// comparison per track rather than one per packet.
365    bitstream_checked: bool,
366    /// Whether a keyframe has been seen to carry its own parameter sets.
367    /// Only consulted while this sink has none of its own to prepend.
368    parameter_sets_checked: bool,
369}
370
371impl WebRtcTrackSink {
372    pub(super) fn new(
373        id: TrackId,
374        kind: MediaKind,
375        codec: Option<Codec>,
376        negotiated_codecs: Arc<Mutex<Vec<Codec>>>,
377        command_tx: Sender<Command>,
378    ) -> Self {
379        Self {
380            id,
381            kind,
382            codec,
383            negotiated_codecs,
384            command_tx,
385            timestamp_offset: None,
386            parameter_sets: None,
387            nal_length_size: None,
388            bitstream_checked: false,
389            parameter_sets_checked: false,
390            pp_log: element_pp_log(
391                ElementType::WebRtcPeer,
392                &format!("webrtc-track-{}", id.0),
393                None,
394            ),
395        }
396    }
397
398    /// Returns the distinct codec families this track can currently send
399    /// after SDP negotiation. The order is informational; select the codec
400    /// produced by the application's encoder.
401    ///
402    /// A locally-created endpoint is handed out while its offer is still
403    /// pending, so its initial value is the offered list and is narrowed when
404    /// [`WebRtcHandle::set_answer`] applies the answer. A remotely-created
405    /// endpoint is already negotiated when it is handed out.
406    pub fn negotiated_codecs(&self) -> Vec<Codec> {
407        self.negotiated_codecs.lock().unwrap().clone()
408    }
409
410    /// Declares what feeds this sink, from the parameters of whatever does —
411    /// an encoder, a demuxer's stream, or another track's
412    /// [`WebRtcStreamInfo::codec_parameters`].
413    ///
414    /// Everything this sink needs is in that one value, so nothing is asked
415    /// for twice: the RTP payload type comes from the codec the parameters
416    /// name, the headers to put in front of keyframes from their extradata,
417    /// and whether payloads arrive length-prefixed from the shape of that
418    /// extradata.
419    ///
420    /// # Why the headers have to travel
421    ///
422    /// An encoder opened with `AV_CODEC_FLAG_GLOBAL_HEADER` — which every
423    /// encoder in this crate is, so that a container has a `CodecPrivate` to
424    /// write — moves its SPS/PPS out of the bitstream and into
425    /// `parameters()`. A file is then complete, because the container carries
426    /// them; an RTP stream is not, because nothing in it does. The receiving
427    /// half of this driver builds its decoder parameters by watching for
428    /// SPS/PPS to go past (see `stream_info`), so without them a peer never
429    /// learns what it is being sent and simply times out waiting. They go in
430    /// front of every keyframe rather than once, which is what lets a peer
431    /// that joins late — or that lost the first of them — start decoding at
432    /// the next one.
433    ///
434    /// # A demuxer's parameters
435    ///
436    /// A container demuxer describes H.264 with an `avcC` record, and its
437    /// packets are length-prefixed to match rather than Annex-B. Passing
438    /// those parameters is therefore two statements at once: the parameter
439    /// sets are these, and the payloads to come are length-prefixed. Both are
440    /// read out of the one record, and every payload is rewritten as Annex-B
441    /// on its way to RTP.
442    ///
443    /// # Errors
444    ///
445    /// [`WebRtcError::OutboundCodecNotNegotiated`] when this connection did
446    /// not retain the codec the parameters name,
447    /// [`WebRtcError::SourceCodecUnsupported`] when WebRTC does not carry it
448    /// at all, and [`WebRtcError::ParameterSetsNotSupported`] for HEVC or VVC
449    /// configuration in `hvcC`/`vvcC` form, which this sink cannot convert.
450    /// A failed call changes nothing, so the previous declaration stays
451    /// usable and already-enqueued packets keep the one they were sent with.
452    ///
453    /// Parameters carrying no extradata are accepted as they are: an encoder
454    /// that still writes its headers in-band needs none prepended, and most
455    /// codecs have none to prepend.
456    pub fn set_source_parameters(&mut self, parameters: &ffmpeg::codec::Parameters) -> Result<()> {
457        let id = parameters.id();
458        let codec = str0m_codec(id).ok_or(WebRtcError::SourceCodecUnsupported(id))?;
459        let negotiated = self.negotiated_codecs();
460        if !negotiated.contains(&codec) {
461            return Err(WebRtcError::OutboundCodecNotNegotiated {
462                track_id: self.id,
463                codec,
464                negotiated,
465            }
466            .into());
467        }
468        // SAFETY: `parameters` is a live `AVCodecParameters`; `extradata` and
469        // `extradata_size` are plain fields of it, and the slice is copied
470        // out before this borrow ends.
471        let bytes = unsafe {
472            let raw = parameters.as_ptr();
473            let size = usize::try_from((*raw).extradata_size).unwrap_or(0);
474            match ((*raw).extradata.is_null() || size == 0).then_some(()) {
475                Some(()) => Vec::new(),
476                None => std::slice::from_raw_parts((*raw).extradata, size).to_vec(),
477            }
478        };
479        // Decided before anything is written, so a refusal leaves the
480        // previous declaration whole.
481        let (parameter_sets, nal_length_size) = match () {
482            // Only the Annex-B codecs prepend anything. Another codec's
483            // extradata describes a decoder rather than introducing a
484            // keyframe — `OpusHead` in front of every Opus packet would be
485            // corruption, not configuration.
486            _ if !annex_b_codec(codec) => (None, None),
487            _ if bytes.is_empty() => (None, None),
488            _ if starts_with_start_code(&bytes) => (Some(bytes), None),
489            _ if codec == Codec::H264 => match avcc_parameter_sets(&bytes) {
490                // A record with no parameter sets still says the packets are
491                // length-prefixed. Nothing is left to prepend, so a keyframe
492                // that turns out not to carry them in-band either is caught
493                // by `prepare_bitstream` — at the only place that can see it.
494                Some((annex_b, length_size)) => (annex_b, Some(length_size)),
495                None => {
496                    return Err(WebRtcError::ParameterSetsNotSupported {
497                        track_id: self.id,
498                        codec,
499                    }
500                    .into());
501                }
502            },
503            // `hvcC`/`vvcC`: a real configuration record this sink has no
504            // conversion for. Refused rather than prepended verbatim, which
505            // would put a decoder configuration into the bitstream.
506            _ => {
507                return Err(WebRtcError::ParameterSetsNotSupported {
508                    track_id: self.id,
509                    codec,
510                }
511                .into());
512            }
513        };
514        self.codec = Some(codec);
515        self.parameter_sets = parameter_sets;
516        self.nal_length_size = nal_length_size;
517        self.forget_what_was_checked();
518        Ok(())
519    }
520
521    /// Declares only the codec, for a caller with no parameters to hand —
522    /// one pushing packets it assembled itself rather than an encoder's or a
523    /// demuxer's.
524    ///
525    /// Prefer [`WebRtcTrackSink::set_source_parameters`] wherever the source
526    /// has `parameters()`: this leaves the sink with no headers to put in
527    /// front of keyframes, which for H.264, HEVC and VVC means the packets
528    /// themselves must carry their parameter sets in-band. [`Sink::consume`]
529    /// checks that on the first keyframe rather than letting a peer wait for
530    /// configuration that is never coming.
531    ///
532    /// Declaring only the codec means exactly that, including after a
533    /// [`WebRtcTrackSink::set_source_parameters`] that said more: whatever
534    /// that call left — headers to prepend, a length prefix to rewrite — is
535    /// dropped here. Keeping it would apply one source's shape to another's
536    /// packets, which for the length prefix means rejecting every Annex-B
537    /// packet that follows.
538    ///
539    /// Returns [`WebRtcError::OutboundCodecNotNegotiated`] without changing
540    /// the previous selection when `codec` is unavailable.
541    pub fn set_codec(&mut self, codec: Codec) -> Result<()> {
542        let negotiated = self.negotiated_codecs();
543        if !negotiated.contains(&codec) {
544            return Err(WebRtcError::OutboundCodecNotNegotiated {
545                track_id: self.id,
546                codec,
547                negotiated,
548            }
549            .into());
550        }
551        self.codec = Some(codec);
552        self.parameter_sets = None;
553        self.nal_length_size = None;
554        self.forget_what_was_checked();
555        Ok(())
556    }
557
558    /// Puts the once-per-track checks back to their unexamined state.
559    ///
560    /// Called by both declarations, because "once per track" is really once
561    /// per source: what feeds a sink is exactly what those checks are about,
562    /// and a sink told about a new one has examined nothing yet.
563    fn forget_what_was_checked(&mut self) {
564        self.bitstream_checked = false;
565        self.parameter_sets_checked = false;
566    }
567}
568
569impl Element for WebRtcTrackSink {
570    fn name(&self) -> Arc<str> {
571        format!("webrtc-track-{}", self.id.0).into()
572    }
573
574    fn element_type(&self) -> ElementType {
575        ElementType::WebRtcPeer
576    }
577
578    fn pp_log(&self) -> &PpLog {
579        &self.pp_log
580    }
581
582    fn pp_log_mut(&mut self) -> &mut PpLog {
583        &mut self.pp_log
584    }
585}
586
587impl Sink for WebRtcTrackSink {
588    /// A track carries encoded media to the peer; this sink has no
589    /// encoder of its own, so a decoded frame has no route through it.
590    fn input_contract(&self) -> InputContract {
591        // Path-qualified: `MediaKind` in this module is str0m's own.
592        InputContract::Fixed(PortContract::packet(packet_kind(self.kind)))
593    }
594
595    fn consume(&mut self, buf: MediaBuffer) -> Result<()> {
596        if !matches!(buf, MediaBuffer::Packet(_) | MediaBuffer::Eos) {
597            let kind = match buf {
598                MediaBuffer::Video(_) => "Video",
599                MediaBuffer::Audio(_) => "Audio",
600                MediaBuffer::Packet(_) | MediaBuffer::Eos => unreachable!("matched above"),
601            };
602            pp_error!(self, "unsupported buffer: {kind}");
603            return Err(WebRtcError::UnsupportedBuffer(kind).into());
604        }
605        if matches!(buf, MediaBuffer::Packet(_)) && self.codec.is_none() {
606            pp_error!(self, "outbound codec is not declared");
607            return Err(WebRtcError::OutboundCodecNotDeclared(self.id).into());
608        }
609        if let MediaBuffer::Packet(_) = &buf {
610            let codec = self.codec.expect("checked above");
611            let negotiated = self.negotiated_codecs();
612            if !negotiated.contains(&codec) {
613                pp_error!(self, "outbound codec {codec:?} is not negotiated");
614                return Err(WebRtcError::OutboundCodecNotNegotiated {
615                    track_id: self.id,
616                    codec,
617                    negotiated,
618                }
619                .into());
620            }
621        }
622        let buf = self.prepare_bitstream(buf)?;
623        let buf = self.prepend_parameter_sets(buf);
624        let buf = self.normalize_packet_timestamp(buf)?;
625        // `WebRtcPeer::run` gone (channel disconnected) means this track is
626        // dead — surface it as `Err` rather than swallowing it, so whatever
627        // pipeline this `Sink` is plugged into (its own `Queue`, its own
628        // `Bus`) actually learns about it instead of silently sending into
629        // a void forever. Non-fatal by the same convention as any other
630        // `Sink::consume` failure (see `Queue`'s own docs) — just no longer
631        // an invisible one.
632        //
633        // A full channel (`WebRtcPeer::run` backed up) drops the newest
634        // buffer instead — same as an unopened track (see `add_track`'s
635        // docs) — but isn't reported on a `Bus`: unlike `WebRtcPeer::run`,
636        // which only ever borrows a `Bus` for the duration of one `run()`
637        // call, `WebRtcTrackSink` is a handle the caller can keep past
638        // `Driver::stop()`, so storing one here would keep that `Bus`'s
639        // channel open indefinitely — including past whatever's waiting on
640        // `BusReceiver::iter()` to finish once every sender is gone.
641        match self
642            .command_tx
643            .try_send(Command::Push(self.id, self.codec, buf))
644        {
645            Ok(()) | Err(TrySendError::Full(_)) => Ok(()),
646            Err(TrySendError::Disconnected(_)) => {
647                pp_error!(self, "WebRtcPeer::run gone — track is dead");
648                Err(WebRtcError::Closed.into())
649            }
650        }
651    }
652
653    fn control(&mut self, _msg: ControlMsg) -> Result<()> {
654        // Terminal, same as AppSink/RtspSink: nothing buffered or
655        // downstream to flush/forward for any ControlMsg.
656        Ok(())
657    }
658}
659
660impl WebRtcTrackSink {
661    /// Puts the codec headers in front of a keyframe, if this sink was given
662    /// any — see [`WebRtcTrackSink::set_source_parameters`].
663    ///
664    /// Only in front of keyframes, and only when the payload does not open
665    /// with them already: an encoder that was *not* opened with a global
666    /// header still writes them in-band, and doubling them costs bytes on
667    /// every keyframe for nothing.
668    fn prepend_parameter_sets(&self, buf: MediaBuffer) -> MediaBuffer {
669        let Some(headers) = self.parameter_sets.as_deref() else {
670            return buf;
671        };
672        let MediaBuffer::Packet(packet) = &buf else {
673            return buf;
674        };
675        let Some(payload) = packet.data() else {
676            return buf;
677        };
678        if !packet.is_key() || payload.starts_with(headers) {
679            return buf;
680        }
681        let mut joined = Vec::with_capacity(headers.len() + payload.len());
682        joined.extend_from_slice(headers);
683        joined.extend_from_slice(payload);
684        rewritten_packet(packet, &joined)
685    }
686
687    /// Puts an Annex-B codec's payload into the form RTP carries, and refuses
688    /// what cannot reach a decoder.
689    ///
690    /// Two things can be wrong, and both are invisible without this. str0m
691    /// splits an outbound payload on Annex-B start codes, so a
692    /// length-prefixed one is packetized as a single NAL unit whose type byte
693    /// is really the first byte of a length. And a keyframe with no parameter
694    /// sets — neither in-band nor prepended — is valid RTP that no receiver
695    /// can configure a decoder from. Either way every packet leaves, nothing
696    /// reports an error, and the symptom belongs to the far end: a wait for
697    /// SPS/PPS that never ends.
698    ///
699    /// Each check runs once per track, since neither the bitstream form nor
700    /// where an encoder keeps its headers changes part-way through.
701    fn prepare_bitstream(&mut self, buf: MediaBuffer) -> Result<MediaBuffer> {
702        let Some(codec) = self.codec.filter(|codec| annex_b_codec(*codec)) else {
703            return Ok(buf);
704        };
705        let MediaBuffer::Packet(packet) = &buf else {
706            return Ok(buf);
707        };
708        let Some(payload) = packet.data() else {
709            return Ok(buf);
710        };
711
712        let converted = match self.nal_length_size {
713            // Already in the form RTP carries, whatever the record said. A
714            // caller can declare a demuxer's parameters — for the parameter
715            // sets, which are only there — while what reaches this sink has
716            // been converted on the way. Read as length-prefixed, such a
717            // payload's leading start code parses as a one-byte NAL unit and
718            // the packet is refused as malformed, which names neither the
719            // cause nor the fix. Four bytes to rule out, per packet rather
720            // than once, because this is the one thing about a payload that
721            // something upstream can change without redeclaring anything.
722            Some(_) if starts_with_start_code(payload) => None,
723            Some(nal_length_size) => {
724                let Some(annex_b) = length_prefixed_to_annex_b(payload, nal_length_size) else {
725                    pp_error!(
726                        self,
727                        "outbound packet is not a valid length-prefixed access unit"
728                    );
729                    return Err(WebRtcError::MalformedLengthPrefixedPacket(self.id).into());
730                };
731                Some(annex_b)
732            }
733            None => {
734                // Not marked checked on failure: a `Queue` reports this and
735                // carries on with the next buffer, and refusing only the
736                // first of them would put the silent failure back for every
737                // packet after it. The same goes for the check below.
738                if !self.bitstream_checked {
739                    if !starts_with_start_code(payload) {
740                        pp_error!(self, "outbound packet is not Annex-B");
741                        return Err(WebRtcError::NotAnnexB(self.id).into());
742                    }
743                    self.bitstream_checked = true;
744                }
745                None
746            }
747        };
748
749        if packet.is_key() && self.parameter_sets.is_none() && !self.parameter_sets_checked {
750            let outgoing = converted.as_deref().unwrap_or(payload);
751            if carries_parameter_sets(outgoing, codec) == Some(false) {
752                pp_error!(self, "outbound keyframe carries no parameter sets");
753                return Err(WebRtcError::MissingParameterSets(self.id).into());
754            }
755            self.parameter_sets_checked = true;
756        }
757
758        match converted {
759            Some(annex_b) => Ok(rewritten_packet(packet, &annex_b)),
760            None => Ok(buf),
761        }
762    }
763
764    fn normalize_packet_timestamp(&mut self, buf: MediaBuffer) -> Result<MediaBuffer> {
765        let MediaBuffer::Packet(packet) = buf else {
766            return Ok(buf);
767        };
768        let Some(pts) = packet.pts() else {
769            return Ok(MediaBuffer::Packet(packet));
770        };
771        let offset = match self.timestamp_offset {
772            Some(offset) => offset,
773            None if pts < 0 => {
774                pts.checked_neg()
775                    .ok_or(WebRtcError::PacketTimestampNormalizationOverflow {
776                        value: pts,
777                        offset: 0,
778                    })?
779            }
780            None => 0,
781        };
782        self.timestamp_offset = Some(offset);
783        if offset == 0 {
784            return Ok(MediaBuffer::Packet(packet));
785        }
786
787        let shifted = |value: i64| {
788            value
789                .checked_add(offset)
790                .ok_or(WebRtcError::PacketTimestampNormalizationOverflow { value, offset })
791        };
792        let mut normalized = (*packet).clone();
793        normalized.set_pts(Some(shifted(pts)?));
794        normalized.set_dts(packet.dts().map(shifted).transpose()?);
795        Ok(MediaBuffer::Packet(Arc::new(normalized)))
796    }
797}
798
799/// One inbound track — the mirror image of [`WebRtcTrackSink`]. A plain
800/// [`SourceElement`], same shape as [`crate::elements::AppSource`]: it
801/// links into its own [`crate::pipeline::Pipeline`] via `src_pads()` like
802/// any other source. The difference from `AppSource` is only *who* feeds
803/// it — instead of an [`crate::elements::AppSourceHandle`] the app calls
804/// itself, [`crate::driver::Driver::run`] pushes into the sending half of this same
805/// channel internally, from its own thread, for every `Event::MediaData`
806/// on this track's `Mid`. Nothing here ever calls back into caller-supplied
807/// code from `WebRtcPeer::run`'s own thread — that thread only ever touches
808/// this crate's own types (see the module docs for why `WebRtcPeer` hands
809/// tracks out through [`WebRtcHandle::next_track`] instead of a callback).
810pub struct WebRtcTrackSource {
811    id: TrackId,
812    pp_log: PpLog,
813    name: Arc<str>,
814    pad: SrcPad,
815    data_rx: Receiver<MediaBuffer>,
816    codec: Arc<Mutex<Option<Codec>>>,
817    negotiated_codecs: Arc<Mutex<Vec<Codec>>>,
818    stream_info: Mutex<StreamInfoState>,
819}
820
821struct StreamInfoState {
822    rx: Receiver<WebRtcStreamInfo>,
823    cached: Option<WebRtcStreamInfo>,
824}
825
826impl WebRtcTrackSource {
827    pub(super) fn new(
828        id: TrackId,
829        kind: MediaKind,
830        name: impl Into<String>,
831        data_rx: Receiver<MediaBuffer>,
832        codec: Arc<Mutex<Option<Codec>>>,
833        negotiated_codecs: Arc<Mutex<Vec<Codec>>>,
834        stream_info_rx: Receiver<WebRtcStreamInfo>,
835    ) -> Self {
836        let name: Arc<str> = name.into().into();
837        let pp_log = element_pp_log(ElementType::WebRtcPeer, &name, None);
838        // An inbound track always delivers encoded media; which codec is
839        // negotiated with the peer at runtime, but the kind never varies.
840        let pad = SrcPad::with_contract(
841            format!("{name}_src"),
842            OutputContract::Fixed(PortContract::packet(packet_kind(kind))),
843        );
844        Self {
845            id,
846            name,
847            pp_log,
848            pad,
849            data_rx,
850            codec,
851            negotiated_codecs,
852            stream_info: Mutex::new(StreamInfoState {
853                rx: stream_info_rx,
854                cached: None,
855            }),
856        }
857    }
858
859    /// Blocks for at most `timeout` until actual RTP media confirms enough
860    /// stream parameters to construct downstream consumers. Most codecs are
861    /// known from the first payload; H.264 waits until both SPS and PPS have
862    /// arrived. The returned [`WebRtcStreamInfo`] can derive the RTP time base
863    /// and FFmpeg parameters for a decoder or supported muxer.
864    ///
865    /// A timeout returns [`WebRtcError::StreamInfoTimeout`] without consuming
866    /// or invalidating anything, so the caller may retry. Once confirmed, the
867    /// value is cached and every later call returns it immediately. If the
868    /// peer closes before the required media information arrives, this returns
869    /// [`WebRtcError::Closed`]. This method does not consume media packets:
870    /// they remain buffered for [`SourceElement::run`].
871    pub fn wait_stream_info(&self, timeout: Duration) -> Result<WebRtcStreamInfo> {
872        let mut state = self.stream_info.lock().unwrap();
873        if let Some(info) = &state.cached {
874            return Ok(info.clone());
875        }
876
877        match state.rx.recv_timeout(timeout) {
878            Ok(info) => {
879                state.cached = Some(info.clone());
880                Ok(info)
881            }
882            Err(RecvTimeoutError::Timeout) => Err(WebRtcError::StreamInfoTimeout {
883                track_id: self.id,
884                timeout,
885            }
886            .into()),
887            Err(RecvTimeoutError::Disconnected) => Err(WebRtcError::Closed.into()),
888        }
889    }
890
891    /// Returns the distinct codec families this track can currently receive
892    /// after SDP negotiation. The order is informational.
893    ///
894    /// This is available as soon as the source is created. For a source on
895    /// the side that originated the media section, the initial offered list
896    /// is narrowed when [`WebRtcHandle::set_answer`] applies the answer.
897    /// [`WebRtcTrackSource::codec`] remains separate: it reports which codec
898    /// the remote sender actually chose once media starts arriving.
899    pub fn negotiated_codecs(&self) -> Vec<Codec> {
900        self.negotiated_codecs.lock().unwrap().clone()
901    }
902
903    /// The codec this track is actually carrying, as seen on the most
904    /// recently received packet's RTP payload type — `None` until the
905    /// first one arrives. Unlike [`WebRtcHandle::add_track`]'s `codec`
906    /// (which the *caller* declares up front for an outbound track), an
907    /// inbound track's codec isn't knowable ahead of time: SDP negotiation
908    /// can accept several codecs for one `m=` line, and only the packets
909    /// actually arriving say which one the remote side picked (see
910    /// `Event::MediaData`'s own `params` field). Whatever's downstream
911    /// (e.g. a decoder) needs a keyframe before it can do anything useful
912    /// anyway, so waiting for the first packet to learn the codec isn't an
913    /// extra constraint in practice. Use [`Self::wait_stream_info`] when the
914    /// downstream graph must be configured before this source starts running.
915    pub fn codec(&self) -> Option<Codec> {
916        *self.codec.lock().unwrap()
917    }
918}
919
920impl Element for WebRtcTrackSource {
921    fn name(&self) -> Arc<str> {
922        self.name.clone()
923    }
924
925    fn element_type(&self) -> ElementType {
926        ElementType::WebRtcPeer
927    }
928
929    fn pp_log(&self) -> &PpLog {
930        &self.pp_log
931    }
932
933    fn pp_log_mut(&mut self) -> &mut PpLog {
934        &mut self.pp_log
935    }
936}
937
938impl Source for WebRtcTrackSource {
939    fn src_pads(&mut self) -> &mut [SrcPad] {
940        std::slice::from_mut(&mut self.pad)
941    }
942}
943
944impl SourceElement for WebRtcTrackSource {
945    fn is_live(&self) -> bool {
946        true
947    }
948
949    fn is_seekable(&self) -> bool {
950        false
951    }
952
953    /// Identical shape to [`crate::elements::AppSource::run`]: selects on
954    /// `control` and its own data channel together, so `Stop`/`Pause`
955    /// never wait behind a remote peer that's gone quiet. The data channel
956    /// disconnecting — `WebRtcPeer` gone, whether from `Stop` or the
957    /// connection dying on its own — ends this the same way `AppSource`
958    /// ends when every `AppSourceHandle` is dropped: one final `Eos`, no
959    /// error.
960    fn run(&mut self, control: &ControlReceiver, bus: &Bus) -> Result<()> {
961        pp_info!(self, "started");
962        loop {
963            if drain_control(control, self, bus)?.stopped {
964                pp_info!(self, "stopped");
965                return Ok(());
966            }
967
968            select! {
969                recv(control.rx) -> req => {
970                    match req {
971                        Ok(req) => {
972                            match req.kind {
973                                RequestKind::Finish => {
974                                    apply_finish(self, bus, &req.ack);
975                                    pp_info!(self, "finished");
976                                    return Ok(());
977                                }
978                                RequestKind::Control(msg) => {
979                                    if apply_one(self, bus, &msg, &req.ack)? {
980                                        pp_info!(self, "stopped");
981                                        return Ok(());
982                                    }
983                                    if msg == ControlMsg::Pause
984                                        && wait_out_pause(control, self, bus)?
985                                    {
986                                        pp_info!(self, "stopped");
987                                        return Ok(());
988                                    }
989                                }
990                            }
991                        }
992                        // The Pipeline itself is gone — nothing left to drive this.
993                        Err(_) => {
994                            pp_info!(self, "run: control channel gone, ending");
995                            return Ok(());
996                        }
997                    }
998                }
999                recv(self.data_rx) -> buf => {
1000                    match buf {
1001                        Ok(buf) if buf.is_eos() => {
1002                            pp_info!(self, "event=eos phase=source_received");
1003                            break;
1004                        }
1005                        Ok(buf) => {
1006                            if let Err(error) = self.pad.push(buf) {
1007                                bus.post(
1008                                    &self.pp_log,
1009                                    BusEvent::Error {
1010                                        element_type: ElementType::WebRtcPeer,
1011                                        name: self.name.clone(),
1012                                        error,
1013                                    },
1014                                );
1015                            }
1016                        }
1017                        // `WebRtcPeer` gone — this track (or the whole peer) is done.
1018                        Err(_) => {
1019                            pp_info!(self, "run: WebRtcPeer gone, ending");
1020                            break;
1021                        }
1022                    }
1023                }
1024            }
1025        }
1026        // The data channel ending (above) can race a `Stop` sent at the
1027        // same moment — e.g. stopping the *upstream* `WebRtcPeer` (via its
1028        // `DriverRunner`) disconnects this exact channel, and a caller
1029        // stopping this `Pipeline` too, right after, can land its `Stop` in
1030        // `control`'s queue after `select!` already picked the data arm.
1031        // Ack it (a no-op otherwise) so `ControlSender::send`'s rendezvous
1032        // never blocks forever waiting for an ack this thread would
1033        // otherwise never get around to sending.
1034        while let Some((_msg, ack)) = control.try_recv() {
1035            let _ = ack.send(());
1036        }
1037        self.pad.push_eos(&self.pp_log)
1038    }
1039
1040    /// No timeline of its own — same reasoning as
1041    /// [`crate::elements::AppSource::seek`]: a WebRTC connection has
1042    /// nothing to reposition.
1043    fn seek(&mut self, target: Duration) -> Result<Duration> {
1044        Ok(target)
1045    }
1046}