Skip to main content

moq_rtc/codec/
h264.rs

1//! H.264 bridge.
2//!
3//! str0m hands us reassembled Annex-B frames (start-code prefixed NALs with
4//! inline SPS/PPS), which is exactly what
5//! [`moq_mux::codec::h264::Import`] in Avc3 mode wants. We just convert the
6//! timestamp and stream NALs in.
7
8use crate::{Result, codec};
9
10pub struct Bridge {
11	split: moq_mux::codec::h264::Split,
12	import: moq_mux::codec::h264::Import,
13}
14
15impl Bridge {
16	pub fn new(mut broadcast: moq_net::broadcast::Producer, catalog: moq_mux::catalog::Producer) -> Result<Self> {
17		let track = broadcast.unique_track(".avc3", catalog.track_info())?;
18		let import = moq_mux::codec::h264::Import::new(track, catalog.reserve(), Default::default())?;
19		let split = moq_mux::codec::h264::Split::new();
20		Ok(Self { split, import })
21	}
22}
23
24impl codec::Bridge for Bridge {
25	fn push(&mut self, frame: codec::Frame) -> Result<()> {
26		let pts = moq_net::Timestamp::from_micros(frame.timestamp_us)
27			.map_err(|err| crate::Error::Other(anyhow::anyhow!("invalid timestamp: {err}")))?;
28		// str0m hands over one whole access unit per frame, so flush to emit it.
29		let mut frames = self.split.decode(&frame.payload, Some(pts))?;
30		frames.extend(self.split.flush(Some(pts))?);
31		self.import.decode(frames)?;
32		Ok(())
33	}
34
35	fn abort(self: Box<Self>, err: moq_net::Error) {
36		self.import.abort(err);
37	}
38}