Skip to main content

moq_rtc/codec/
vp9.rs

1//! VP9 bridge.
2//!
3//! str0m hands us complete VP9 frames, which is exactly the raw shape that
4//! [`moq_mux::codec::vp9::Import`] consumes. The shared importer parses keyframes
5//! so the catalog carries the encoded dimensions and stays in sync if they change.
6
7use crate::{Result, codec};
8
9/// Bridges str0m VP9 frames into a MoQ VP9 track.
10pub struct Bridge {
11	import: codec::DeferredVideo<moq_mux::codec::vp9::Import>,
12}
13
14impl Bridge {
15	/// Publish a `.vp9` track on `broadcast`, adding the catalog rendition once config is known.
16	pub fn new(broadcast: moq_net::broadcast::Producer, catalog: moq_mux::catalog::Producer) -> Result<Self> {
17		let import = codec::DeferredVideo::new(broadcast, catalog, ".vp9")?;
18		Ok(Self { import })
19	}
20}
21
22impl codec::Bridge for Bridge {
23	fn push(&mut self, frame: codec::Frame) -> Result<()> {
24		let pts = moq_net::Timestamp::from_micros(frame.timestamp_us)
25			.map_err(|err| crate::Error::Other(anyhow::anyhow!("invalid timestamp: {err}")))?;
26		self.import.decode(frame.payload, pts)
27	}
28
29	fn abort(self: Box<Self>, err: moq_net::Error) {
30		self.import.abort(err);
31	}
32}
33
34#[cfg(test)]
35mod tests {
36	use bytes::Bytes;
37
38	use crate::codec::{self, Bridge as _};
39
40	#[test]
41	fn keyframe_publishes_catalog_dimensions() {
42		let mut broadcast = moq_net::broadcast::Info::new().produce();
43		let catalog = moq_mux::catalog::Producer::new(&mut broadcast).unwrap();
44		let mut bridge = super::Bridge::new(broadcast, catalog.clone()).unwrap();
45
46		assert!(catalog.snapshot().video.renditions.is_empty());
47
48		// VP9 profile 0 keyframe header for 320x240.
49		bridge
50			.push(codec::Frame {
51				timestamp_us: 0,
52				payload: Bytes::from_static(&[0x82, 0x49, 0x83, 0x42, 0x20, 0x13, 0xf0, 0x0e, 0xf0, 0x00]),
53			})
54			.unwrap();
55
56		let snapshot = catalog.snapshot();
57		let config = snapshot.video.renditions.values().next().unwrap();
58		assert_eq!(config.coded_width, Some(320));
59		assert_eq!(config.coded_height, Some(240));
60	}
61}