moq_mux/codec/opus/
import.rs1use super::Config;
2use crate::catalog::hang::CatalogExt;
3use crate::container::Frame;
4
5pub struct Import {
18 track: crate::container::Producer<crate::catalog::hang::Container, hang::catalog::AudioConfig>,
19}
20
21impl Import {
22 pub fn new<E: CatalogExt>(
28 track: moq_net::track::Producer,
29 reserved: crate::catalog::Reserved<E>,
30 config: hang::catalog::AudioConfig,
31 ) -> crate::Result<Self> {
32 tracing::debug!(name = ?track.name(), ?config, "starting track");
33 let wire = crate::catalog::hang::Container::try_from(&config)?;
36 let track = reserved.audio(track, wire, config)?;
37 Ok(Self { track })
38 }
39
40 pub fn name(&self) -> &str {
42 self.track.track().name()
43 }
44
45 pub(crate) fn live_edge(&self) -> Option<moq_net::Timestamp> {
47 self.track.live_edge()
48 }
49
50 pub fn demand(&self) -> moq_net::track::Demand {
52 self.track.track().demand()
53 }
54
55 pub fn flush(&mut self, timestamp: moq_net::Timestamp, now: std::time::Instant) -> crate::Result<()> {
57 self.track.flush(timestamp, now)
58 }
59
60 pub fn finish(&mut self) -> crate::Result<()> {
62 self.track.finish()?;
63 Ok(())
64 }
65
66 pub fn abort(self, err: moq_net::Error) {
69 self.track.abort(err);
70 }
71
72 pub fn cut(&mut self, end: Option<moq_net::Timestamp>) -> crate::Result<()> {
76 self.track.cut(end)?;
77 Ok(())
78 }
79
80 pub fn discontinuity(&mut self) -> crate::Result<()> {
84 self.track.discontinuity()?;
85 Ok(())
86 }
87
88 pub fn seek(&mut self, sequence: u64) -> crate::Result<()> {
90 self.track.seek(sequence)?;
91 Ok(())
92 }
93
94 pub fn decode<B: moq_net::IntoBytes>(&mut self, frame: B, pts: Option<moq_net::Timestamp>) -> crate::Result<()> {
100 let timestamp = self.track.timestamp(pts)?;
101 let keyframe = self.track.needs_keyframe();
104 self.track.write(Frame {
105 timestamp,
106 payload: frame.into_bytes(),
107 keyframe,
108 duration: None,
109 })?;
110 Ok(())
111 }
112}
113
114pub fn config(init: &[u8]) -> crate::Result<hang::catalog::AudioConfig> {
118 let mut buf = init;
119 let mut config: hang::catalog::AudioConfig = Config::parse(&mut buf)?.into();
120 config.description = Some(bytes::Bytes::copy_from_slice(init));
122 Ok(config)
123}
124
125impl From<Config> for hang::catalog::AudioConfig {
126 fn from(config: Config) -> Self {
131 let mut audio = hang::catalog::AudioConfig::new(hang::catalog::AudioCodec::Opus, 48_000, config.channel_count);
132 audio.description = config.encode().ok();
133 audio.container = hang::catalog::Container::Legacy;
134 audio
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use std::time::Duration;
141
142 use moq_net::Timestamp;
143
144 #[test]
145 fn catalog_rate_is_decoder_rate() {
146 let head = super::Config::new(44_100, 2).encode().unwrap();
148 let config = super::config(&head).unwrap();
149 assert_eq!(config.sample_rate, 48_000);
150 assert_eq!(config.description.as_deref(), Some(head.as_ref()));
151
152 let config: hang::catalog::AudioConfig = super::Config::new(44_100, 2).into();
153 assert_eq!(config.sample_rate, 48_000);
154 }
155
156 #[tokio::test(start_paused = true)]
157 async fn a_loc_reservation_reaches_the_wire_and_the_catalog() {
158 let mut broadcast = moq_net::broadcast::Info::new().produce();
159 let catalog = crate::catalog::Producer::new(&mut broadcast, crate::catalog::Config::default()).unwrap();
160 let track = broadcast
161 .create_track("audio", hang::container::track_info(hang::catalog::PRIORITY.audio))
162 .unwrap();
163 let subscriber = track.subscribe(None);
164 let reserved = catalog.reserve();
165 let config = crate::codec::opus::Config::new(48_000, 2);
166 let mut config: hang::catalog::AudioConfig = config.into();
167 config.container = hang::catalog::Container::Loc;
168 let mut import = super::Import::new(track, reserved, config).unwrap();
169
170 let audio = catalog.snapshot().audio.renditions.get("audio").cloned().unwrap();
171 assert_eq!(audio.container, hang::catalog::Container::Loc);
172
173 let payload = b"opus payload";
174 import
175 .decode(payload, Some(Timestamp::from_micros(1_000).unwrap()))
176 .unwrap();
177 let mut media = crate::container::Consumer::new(
178 subscriber,
179 crate::catalog::hang::Container::Loc(crate::container::Kind::Data),
180 );
181 let frame = tokio::time::timeout(Duration::from_secs(1), media.read())
182 .await
183 .unwrap()
184 .unwrap()
185 .unwrap();
186 assert_eq!(frame.payload.as_ref(), payload);
187 assert_eq!(frame.timestamp, Timestamp::from_micros(1_000).unwrap());
188
189 import.finish().unwrap();
190 }
191}