1use crate::{Result, codec};
8
9pub struct Bridge {
11 import: codec::DeferredVideo<moq_mux::codec::vp8::Import>,
12}
13
14impl Bridge {
15 pub fn new(broadcast: moq_net::broadcast::Producer, catalog: moq_mux::catalog::Producer) -> Result<Self> {
17 let import = codec::DeferredVideo::new(broadcast, catalog, ".vp8")?;
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 bridge
50 .push(codec::Frame {
51 timestamp_us: 0,
52 payload: Bytes::from_static(&[0x10, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0x40, 0x01, 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
62 #[tokio::test]
63 async fn importer_creation_failure_preserves_abort_error() {
64 let mut broadcast = moq_net::broadcast::Info::new().produce();
65 let catalog = moq_mux::catalog::Producer::new(&mut broadcast).unwrap();
66 let _collision = broadcast.create_track("0.vp8.timeline.z", None).unwrap();
67 let consumer = broadcast.consume();
68 let mut bridge = super::Bridge::new(broadcast, catalog).unwrap();
69 let mut track = consumer.track("0.vp8").unwrap().subscribe(None).await.unwrap();
70
71 let result = bridge.push(codec::Frame {
72 timestamp_us: 0,
73 payload: Bytes::from_static(&[0x10, 0x00, 0x00, 0x9d, 0x01, 0x2a, 0x40, 0x01, 0xf0, 0x00]),
74 });
75 assert!(result.is_err(), "timeline collision must fail importer creation");
76
77 Box::new(bridge).abort(moq_net::Error::Transport("session failed".into()));
78 let Err(error) = track.recv_group().await else {
79 panic!("aborted track must fail");
80 };
81 assert!(matches!(
82 error,
83 moq_net::Error::Transport(message) if message == "session failed"
84 ));
85 }
86}