Skip to main content

moq_rtc/codec/
h265.rs

1//! H.265 bridge.
2//!
3//! str0m hands us reassembled Annex-B frames (start-code prefixed NALs with
4//! inline VPS/SPS/PPS), which is the `hev1` shape
5//! [`moq_mux::codec::h265::Import`] wants. We convert timestamps and stream
6//! NALs through the shared splitter/importer.
7
8use crate::{Result, codec};
9
10/// Bridges str0m H.265 Annex-B access units into a MoQ H.265 track.
11pub struct Bridge {
12	split: moq_mux::codec::h265::Split,
13	import: moq_mux::codec::h265::Import,
14}
15
16impl Bridge {
17	/// Publish a `.hev1` track on `broadcast`, adding the catalog rendition once config is known.
18	pub fn new(mut broadcast: moq_net::BroadcastProducer, catalog: moq_mux::catalog::Producer) -> Result<Self> {
19		let track = moq_mux::import::unique_track(&mut broadcast, ".hev1")?;
20		let import = moq_mux::codec::h265::Import::new(track, catalog);
21		let split = moq_mux::codec::h265::Split::new();
22		Ok(Self { split, import })
23	}
24}
25
26impl codec::Bridge for Bridge {
27	fn push(&mut self, frame: codec::Frame) -> Result<()> {
28		let pts = moq_mux::container::Timestamp::from_micros(frame.timestamp_us)
29			.map_err(|err| crate::Error::Other(anyhow::anyhow!("invalid timestamp: {err}")))?;
30		// str0m hands over one whole access unit per frame, so flush to emit it.
31		let mut frames = self.split.decode(&frame.payload, Some(pts))?;
32		frames.extend(self.split.flush(Some(pts))?);
33		self.import.decode(frames)?;
34		Ok(())
35	}
36}