1use bytes::Bytes;
4
5use super::decoder::{Config, Decoder};
6use crate::resample::{Resampler, remix};
7use crate::{Error, Frame};
8
9pub struct Consumer {
16 decoder: Decoder,
17 track: moq_mux::container::Consumer<moq_mux::catalog::hang::Container>,
18 resampler: Option<Resampler>,
19 config: Config,
20 resolved_sample_rate: u32,
21 resolved_channels: u32,
22}
23
24impl Consumer {
25 pub async fn new(
28 broadcast: &moq_net::broadcast::Consumer,
29 catalog: &hang::catalog::AudioConfig,
30 name: impl Into<String>,
31 config: Config,
32 ) -> Result<Self, Error> {
33 let decoder = Decoder::new(catalog)?;
34 let sample_rate = config.sample_rate.unwrap_or_else(|| decoder.sample_rate());
35 let channels = config.channels.unwrap_or_else(|| decoder.channel_count());
36 crate::opus::validate_channels(channels)?;
37
38 let resampler = if sample_rate == decoder.sample_rate() {
39 None
40 } else {
41 let chunk_frames = (decoder.sample_rate() as usize * 20) / 1000;
42 Some(Resampler::new(
43 decoder.sample_rate(),
44 sample_rate,
45 decoder.channel_count(),
46 chunk_frames,
47 )?)
48 };
49
50 let name = name.into();
51 let track = broadcast
52 .track(&name)?
53 .subscribe(moq_net::track::Subscription::default().with_priority(hang::catalog::PRIORITY.audio))
54 .await?;
55 let container = moq_mux::catalog::hang::Container::try_from(&catalog.container)?;
59 let mut track = moq_mux::container::Consumer::new(track, container);
60 if let Some(latency) = config.latency_max {
61 track = track.with_latency(latency);
62 }
63
64 Ok(Self {
65 decoder,
66 track,
67 resampler,
68 config,
69 resolved_sample_rate: sample_rate,
70 resolved_channels: channels,
71 })
72 }
73
74 pub fn config(&self) -> &Config {
76 &self.config
77 }
78
79 pub fn sample_rate(&self) -> u32 {
82 self.resolved_sample_rate
83 }
84
85 pub fn channels(&self) -> u32 {
88 self.resolved_channels
89 }
90
91 pub async fn read(&mut self) -> Result<Option<Frame>, Error> {
93 let Some(mux_frame) = self.track.read().await? else {
94 return Ok(None);
95 };
96
97 let decoded = self.decoder.decode(&mux_frame.payload)?;
98 let pcm = match self.resampler.as_mut() {
99 Some(r) => r.process(&decoded)?,
100 None => decoded,
101 };
102 let pcm = if self.decoder.channel_count() == self.resolved_channels {
103 pcm
104 } else {
105 remix(&pcm, self.decoder.channel_count(), self.resolved_channels)?
106 };
107
108 let bytes = self.config.format.from_interleaved_f32(&pcm, self.resolved_channels)?;
109 Ok(Some(Frame {
110 timestamp: mux_frame.timestamp,
111 data: Bytes::from(bytes),
112 }))
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use moq_net::Timestamp;
119
120 use super::*;
121 use crate::Format;
122 use crate::encode::{Encoder, Input, Options, Producer};
123
124 #[tokio::test]
125 async fn remixes_mono_stream_to_stereo_output() {
126 let mut broadcast = moq_net::broadcast::Info::new().produce();
127 let catalog = moq_mux::catalog::Producer::new(&mut broadcast).unwrap();
128 let subscriber = broadcast.consume();
129 let input = Input {
130 format: Format::F32,
131 sample_rate: 48_000,
132 channels: 1,
133 };
134 let options = Options {
135 track: Some("audio".to_string()),
136 ..Options::default()
137 };
138 let mut producer = Producer::new(&mut broadcast, catalog, input.clone(), &options).unwrap();
139 let catalog = Encoder::new(&crate::encode::Config::new(input)).unwrap().catalog();
140 let mut consumer = Consumer::new(
141 &subscriber,
142 &catalog,
143 "audio",
144 Config {
145 channels: Some(2),
146 ..Config::new()
147 },
148 )
149 .await
150 .unwrap();
151
152 let samples = vec![0.1f32; 960];
153 let mut data = Vec::with_capacity(samples.len() * size_of::<f32>());
154 for sample in samples {
155 data.extend_from_slice(&sample.to_le_bytes());
156 }
157 producer
158 .write(&Frame {
159 timestamp: Timestamp::ZERO,
160 data: data.into(),
161 })
162 .unwrap();
163
164 let frame = consumer.read().await.unwrap().expect("decoded frame");
165 let samples = Format::F32.as_interleaved_f32(&frame.data, 2).unwrap();
166 assert_eq!(samples.len(), (960 - 312) * 2);
167 for pair in samples.chunks_exact(2) {
168 assert_eq!(pair[0], pair[1]);
169 }
170 }
171
172 #[tokio::test]
173 async fn reads_the_container_the_catalog_declares() {
174 let mut broadcast = moq_net::broadcast::Info::new().produce();
175 let track = broadcast.create_track("audio", hang::container::track_info()).unwrap();
176 let subscriber = broadcast.consume();
177
178 let mut catalog = hang::catalog::AudioConfig::new(hang::catalog::AudioCodec::Pcm, 48_000, 1);
179 catalog.container = hang::catalog::Container::Loc;
180
181 let mut producer = moq_mux::container::Producer::new(track, moq_mux::catalog::hang::Container::Loc);
182 let mut consumer = Consumer::new(
183 &subscriber,
184 &catalog,
185 "audio",
186 Config {
187 format: Format::F32,
188 ..Config::new()
189 },
190 )
191 .await
192 .unwrap();
193
194 let samples = [0.25f32, -0.5, 0.75, -1.0];
195 let payload: Vec<u8> = samples.iter().flat_map(|sample| sample.to_le_bytes()).collect();
196 producer
197 .write(moq_mux::container::Frame {
198 timestamp: Timestamp::ZERO,
199 duration: None,
200 payload: payload.into(),
201 keyframe: true,
202 })
203 .unwrap();
204
205 let frame = consumer.read().await.unwrap().expect("decoded frame");
206 assert_eq!(
207 Format::F32.as_interleaved_f32(&frame.data, 1).unwrap().as_ref(),
208 samples
209 );
210 }
211
212 #[tokio::test]
217 async fn decodes_a_cmaf_framed_track() {
218 let input = Input {
219 format: Format::F32,
220 sample_rate: 48_000,
221 channels: 2,
222 };
223
224 let mut encoder = Encoder::new(&crate::encode::Config::new(input.clone())).unwrap();
226 let mut catalog = encoder.catalog();
227 let pcm = vec![0.0f32; encoder.frame_size() * encoder.codec_channels() as usize];
228 let packet = encoder.encode(&pcm).unwrap();
229
230 let muxer = moq_mux::container::fmp4::Muxer::audio(&catalog).unwrap();
232 let init = muxer.init().unwrap().expect("an out-of-band codec has an init segment");
233 catalog.container = hang::catalog::Container::Cmaf { init };
234
235 let mut broadcast = moq_net::broadcast::Info::new().produce();
236 let subscriber = broadcast.consume();
237 let track = broadcast.create_track("audio", hang::container::track_info()).unwrap();
238 let container = moq_mux::catalog::hang::Container::try_from(&catalog.container).unwrap();
239 let mut producer = moq_mux::container::Producer::new(track, container);
240
241 let mut consumer = Consumer::new(&subscriber, &catalog, "audio", Config::new())
242 .await
243 .unwrap();
244
245 producer
246 .write(moq_mux::container::Frame {
247 timestamp: Timestamp::ZERO,
248 payload: packet,
249 keyframe: true,
250 duration: None,
251 })
252 .unwrap();
253 producer.cut(None).unwrap();
254
255 let frame = consumer.read().await.unwrap().expect("decoded frame");
259 assert_eq!(frame.timestamp.as_micros(), 0);
262 let samples = Format::F32.as_interleaved_f32(&frame.data, 2).unwrap();
263 assert_eq!(samples.len(), (960 - 312) * 2);
264 }
265}