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}