Skip to main content

moq_rtc/codec/
vp9.rs

1//! VP9 bridge.
2//!
3//! Keyframes are detected from the frame_type bit (RFC 8741 §3 / VP9 spec §6.2:
4//! the second bit of the uncompressed header).
5
6use crate::{Result, codec};
7
8/// Forwards str0m's VP9 frames to a `.vp9` track, detecting keyframes inline.
9pub struct Bridge {
10	catalog: moq_mux::catalog::Producer,
11	track: moq_mux::container::Producer<moq_mux::catalog::hang::Container>,
12	announced: bool,
13}
14
15impl Bridge {
16	/// Publish a `.vp9` track on `broadcast`; the catalog rendition is added on the first frame.
17	pub fn new(mut broadcast: moq_net::BroadcastProducer, catalog: moq_mux::catalog::Producer) -> Result<Self> {
18		let track = broadcast.unique_track(".vp9")?;
19		let producer = catalog.media_producer(track, moq_mux::catalog::hang::Container::Legacy);
20		Ok(Self {
21			catalog,
22			track: producer,
23			announced: false,
24		})
25	}
26
27	fn announce(&mut self) {
28		if self.announced {
29			return;
30		}
31		let name = self.track.track().name.clone();
32		let mut config = hang::catalog::VideoConfig::new(hang::catalog::VP9::default());
33		config.container = hang::catalog::Container::Legacy;
34		config.timeline = Some(self.catalog.timeline_section(&name));
35		self.catalog.lock().video.renditions.insert(name, config);
36		self.announced = true;
37	}
38}
39
40impl codec::Bridge for Bridge {
41	fn push(&mut self, frame: codec::Frame) -> Result<()> {
42		self.announce();
43		let pts = moq_mux::container::Timestamp::from_micros(frame.timestamp_us)
44			.map_err(|err| crate::Error::Other(anyhow::anyhow!("invalid timestamp: {err}")))?;
45		let keyframe = is_keyframe(&frame.payload);
46		self.track
47			.write(moq_mux::container::Frame {
48				timestamp: pts,
49				payload: frame.payload,
50				keyframe,
51				duration: None,
52			})
53			.map_err(|err| crate::Error::Other(anyhow::anyhow!("vp9 track write failed: {err}")))?;
54		Ok(())
55	}
56}
57
58/// Detect a VP9 keyframe from the uncompressed header's first byte (VP9 spec
59/// §6.2), reading bits MSB-first: `frame_marker(2)`, `profile_low(1)`,
60/// `profile_high(1)`, a `reserved(1)` bit only when profile == 3,
61/// `show_existing_frame(1)`, then `frame_type(1)` (0 == KEY_FRAME). A
62/// show-existing frame carries no frame_type and is never a keyframe.
63fn is_keyframe(payload: &[u8]) -> bool {
64	let Some(&b) = payload.first() else {
65		return false;
66	};
67	let profile = (((b >> 4) & 1) << 1) | ((b >> 5) & 1); // (high << 1) | low
68	// Bits consumed from the MSB: 2 (marker) + 2 (profile), plus profile 3's reserved bit.
69	let mut pos = 4;
70	if profile == 3 {
71		pos += 1;
72	}
73	let show_existing_frame = (b >> (7 - pos)) & 1;
74	if show_existing_frame == 1 {
75		return false;
76	}
77	pos += 1;
78	let frame_type = (b >> (7 - pos)) & 1;
79	frame_type == 0
80}
81
82impl Drop for Bridge {
83	fn drop(&mut self) {
84		self.catalog.lock().video.renditions.remove(&self.track.track().name);
85	}
86}
87
88#[cfg(test)]
89mod tests {
90	use super::is_keyframe;
91
92	// frame_marker = 0b10 in the top two bits for every well-formed header.
93	#[test]
94	fn profile0_keyframe_and_interframe() {
95		// profile 0, show_existing_frame = 0, frame_type = 0 (key) / 1 (inter).
96		assert!(is_keyframe(&[0b1000_0010]));
97		assert!(!is_keyframe(&[0b1000_0110]));
98	}
99
100	#[test]
101	fn profile0_show_existing_frame_is_not_keyframe() {
102		// profile 0, show_existing_frame = 1: no frame_type follows.
103		assert!(!is_keyframe(&[0b1000_1000]));
104	}
105
106	#[test]
107	fn profile3_keyframe_and_interframe() {
108		// profile 3 (both profile bits set) inserts a reserved bit before
109		// show_existing_frame, shifting frame_type one position right.
110		assert!(is_keyframe(&[0b1011_0000])); // reserved=0, show=0, frame_type=0
111		assert!(!is_keyframe(&[0b1011_0010])); // frame_type=1
112	}
113
114	#[test]
115	fn empty_payload_is_not_keyframe() {
116		assert!(!is_keyframe(&[]));
117	}
118}