Skip to main content

moq_rtc/codec/
vp8.rs

1//! VP8 bridge.
2//!
3//! VP8 carries no out-of-band config record. str0m hands us complete frames
4//! and we forward them to a `.vp8` track with the matching catalog entry.
5//! Keyframes are detected from the first byte (P-frame bit, RFC 6386 §9.1).
6
7use crate::{Result, codec};
8
9/// Forwards str0m's VP8 frames to a `.vp8` track, detecting keyframes inline.
10pub struct Bridge {
11	catalog: moq_mux::catalog::Producer,
12	track: moq_mux::container::Producer<moq_mux::catalog::hang::Container>,
13	announced: bool,
14}
15
16impl Bridge {
17	/// Publish a `.vp8` track on `broadcast`; the catalog rendition is added on the first frame.
18	pub fn new(mut broadcast: moq_net::BroadcastProducer, catalog: moq_mux::catalog::Producer) -> Result<Self> {
19		let track = broadcast.unique_track(".vp8")?;
20		let producer = catalog.media_producer(track, moq_mux::catalog::hang::Container::Legacy);
21		Ok(Self {
22			catalog,
23			track: producer,
24			announced: false,
25		})
26	}
27
28	fn announce(&mut self) {
29		if self.announced {
30			return;
31		}
32		let name = self.track.track().name.clone();
33		let mut config = hang::catalog::VideoConfig::new(hang::catalog::VideoCodec::VP8);
34		config.container = hang::catalog::Container::Legacy;
35		config.timeline = Some(self.catalog.timeline_section(&name));
36		self.catalog.lock().video.renditions.insert(name, config);
37		self.announced = true;
38	}
39}
40
41impl codec::Bridge for Bridge {
42	fn push(&mut self, frame: codec::Frame) -> Result<()> {
43		self.announce();
44		let pts = moq_mux::container::Timestamp::from_micros(frame.timestamp_us)
45			.map_err(|err| crate::Error::Other(anyhow::anyhow!("invalid timestamp: {err}")))?;
46		// VP8: first byte bit 0 == 0 means keyframe (RFC 6386 §9.1).
47		let keyframe = frame.payload.first().map(|b| b & 0x01 == 0).unwrap_or(false);
48		self.track
49			.write(moq_mux::container::Frame {
50				timestamp: pts,
51				payload: frame.payload,
52				keyframe,
53				duration: None,
54			})
55			.map_err(|err| crate::Error::Other(anyhow::anyhow!("vp8 track write failed: {err}")))?;
56		Ok(())
57	}
58}
59
60impl Drop for Bridge {
61	fn drop(&mut self) {
62		self.catalog.lock().video.renditions.remove(&self.track.track().name);
63	}
64}