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 = moq_mux::container::Producer::new(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 mut config = hang::catalog::VideoConfig::new(hang::catalog::VP9::default());
32		config.container = hang::catalog::Container::Legacy;
33		self.catalog
34			.lock()
35			.video
36			.renditions
37			.insert(self.track.track().name.clone(), config);
38		self.announced = true;
39	}
40}
41
42impl codec::Bridge for Bridge {
43	fn push(&mut self, frame: codec::Frame) -> Result<()> {
44		self.announce();
45		let pts = moq_mux::container::Timestamp::from_micros(frame.timestamp_us)
46			.map_err(|err| crate::Error::Other(anyhow::anyhow!("invalid timestamp: {err}")))?;
47		let keyframe = is_keyframe(&frame.payload);
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!("vp9 track write failed: {err}")))?;
56		Ok(())
57	}
58}
59
60/// Detect a VP9 keyframe from the uncompressed header's first byte (VP9 spec
61/// §6.2), reading bits MSB-first: `frame_marker(2)`, `profile_low(1)`,
62/// `profile_high(1)`, a `reserved(1)` bit only when profile == 3,
63/// `show_existing_frame(1)`, then `frame_type(1)` (0 == KEY_FRAME). A
64/// show-existing frame carries no frame_type and is never a keyframe.
65fn is_keyframe(payload: &[u8]) -> bool {
66	let Some(&b) = payload.first() else {
67		return false;
68	};
69	let profile = (((b >> 4) & 1) << 1) | ((b >> 5) & 1); // (high << 1) | low
70	// Bits consumed from the MSB: 2 (marker) + 2 (profile), plus profile 3's reserved bit.
71	let mut pos = 4;
72	if profile == 3 {
73		pos += 1;
74	}
75	let show_existing_frame = (b >> (7 - pos)) & 1;
76	if show_existing_frame == 1 {
77		return false;
78	}
79	pos += 1;
80	let frame_type = (b >> (7 - pos)) & 1;
81	frame_type == 0
82}
83
84impl Drop for Bridge {
85	fn drop(&mut self) {
86		self.catalog.lock().video.renditions.remove(&self.track.track().name);
87	}
88}
89
90#[cfg(test)]
91mod tests {
92	use super::is_keyframe;
93
94	// frame_marker = 0b10 in the top two bits for every well-formed header.
95	#[test]
96	fn profile0_keyframe_and_interframe() {
97		// profile 0, show_existing_frame = 0, frame_type = 0 (key) / 1 (inter).
98		assert!(is_keyframe(&[0b1000_0010]));
99		assert!(!is_keyframe(&[0b1000_0110]));
100	}
101
102	#[test]
103	fn profile0_show_existing_frame_is_not_keyframe() {
104		// profile 0, show_existing_frame = 1: no frame_type follows.
105		assert!(!is_keyframe(&[0b1000_1000]));
106	}
107
108	#[test]
109	fn profile3_keyframe_and_interframe() {
110		// profile 3 (both profile bits set) inserts a reserved bit before
111		// show_existing_frame, shifting frame_type one position right.
112		assert!(is_keyframe(&[0b1011_0000])); // reserved=0, show=0, frame_type=0
113		assert!(!is_keyframe(&[0b1011_0010])); // frame_type=1
114	}
115
116	#[test]
117	fn empty_payload_is_not_keyframe() {
118		assert!(!is_keyframe(&[]));
119	}
120}