1use std::ffi::{c_char, c_void};
15use std::time::Duration;
16
17use bytes::Bytes;
18use tokio::sync::oneshot;
19
20use crate::ffi::OnStatus;
21use crate::{Error, Id, NonZeroSlab, Shared, State, ffi};
22
23#[repr(C)]
34#[allow(non_camel_case_types)]
35#[derive(Clone, Copy, Debug)]
36pub enum moq_audio_format {
37 MOQ_AUDIO_FORMAT_U8 = 0,
38 MOQ_AUDIO_FORMAT_S16 = 1,
39 MOQ_AUDIO_FORMAT_S32 = 2,
40 MOQ_AUDIO_FORMAT_F32 = 3,
41 MOQ_AUDIO_FORMAT_U8_PLANAR = 4,
42 MOQ_AUDIO_FORMAT_S16_PLANAR = 5,
43 MOQ_AUDIO_FORMAT_S32_PLANAR = 6,
44 MOQ_AUDIO_FORMAT_F32_PLANAR = 7,
45}
46
47fn audio_format_from_u32(value: u32) -> Result<moq_audio::Format, Error> {
48 use moq_audio::Format;
49 Ok(match value {
50 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_U8 as u32 => Format::U8,
51 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_S16 as u32 => Format::S16,
52 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_S32 as u32 => Format::S32,
53 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_F32 as u32 => Format::F32,
54 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_U8_PLANAR as u32 => Format::U8Planar,
55 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_S16_PLANAR as u32 => Format::S16Planar,
56 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_S32_PLANAR as u32 => Format::S32Planar,
57 v if v == moq_audio_format::MOQ_AUDIO_FORMAT_F32_PLANAR as u32 => Format::F32Planar,
58 _ => return Err(Error::InvalidCode),
59 })
60}
61
62#[repr(C)]
64#[allow(non_camel_case_types)]
65pub struct moq_audio_encoder_input {
66 pub format: u32,
68 pub sample_rate: u32,
69 pub channels: u32,
70}
71
72#[repr(C)]
76#[allow(non_camel_case_types)]
77pub struct moq_audio_encoder_output {
78 pub codec: *const c_char,
80 pub codec_len: usize,
81 pub sample_rate: u32,
83 pub channels: u32,
85 pub bitrate: u32,
87 pub frame_duration_ms: u32,
91}
92
93#[repr(C)]
95#[allow(non_camel_case_types)]
96pub struct moq_audio_decoder_output {
97 pub format: u32,
98 pub sample_rate: u32,
100 pub channels: u32,
102 pub latency_max_ms: u64,
110}
111
112#[repr(C)]
118#[allow(non_camel_case_types)]
119pub struct moq_audio_frame {
120 pub timestamp_us: u64,
121 pub data: *const u8,
122 pub data_size: usize,
123}
124
125type AudioProducer = Shared<moq_audio::encode::Producer<moq_mux::catalog::hang::Extra>>;
130
131#[derive(Default)]
132pub struct Audio {
133 producers: NonZeroSlab<AudioProducer>,
134 consumer_tasks: NonZeroSlab<Option<AudioTaskEntry>>,
135 frames: NonZeroSlab<moq_audio::Frame>,
136}
137
138struct AudioTaskEntry {
144 close: Option<oneshot::Sender<()>>,
145 callback: OnStatus,
146}
147
148impl Audio {
149 pub fn publish(
150 &mut self,
151 broadcast: &mut moq_net::broadcast::Producer,
152 catalog: moq_mux::catalog::Producer<moq_mux::catalog::hang::Extra>,
153 input: moq_audio::encode::Input,
154 options: moq_audio::encode::Options,
155 ) -> Result<Id, Error> {
156 let producer = moq_audio::encode::Producer::new(broadcast, catalog, input, &options)?;
157 self.producers.insert(Shared::new(producer))
158 }
159
160 pub(crate) fn producer(&self, id: Id) -> Result<AudioProducer, Error> {
167 self.producers.get(id).cloned().ok_or(Error::MediaNotFound)
168 }
169
170 pub(crate) fn remove(&mut self, id: Id) -> Result<AudioProducer, Error> {
172 self.producers.remove(id).ok_or(Error::MediaNotFound)
173 }
174
175 pub fn consume(
176 &mut self,
177 broadcast: &moq_net::broadcast::Consumer,
178 catalog: &hang::catalog::AudioConfig,
179 name: &str,
180 config: moq_audio::decode::Config,
181 on_frame: OnStatus,
182 ) -> Result<Id, Error> {
183 let broadcast = broadcast.clone();
184 let catalog = catalog.clone();
185 let name = name.to_string();
186
187 let channel = oneshot::channel();
188 let entry = AudioTaskEntry {
189 close: Some(channel.0),
190 callback: on_frame,
191 };
192 let id = self.consumer_tasks.insert(Some(entry))?;
193
194 tokio::spawn(async move {
197 let res = async move {
198 let consumer = moq_audio::decode::Consumer::new(&broadcast, &catalog, name, config).await?;
199 Self::run(on_frame, consumer, channel.1).await
200 }
201 .await;
202
203 let entry = State::lock().audio.consumer_tasks.remove(id).flatten();
206 if let Some(entry) = entry {
207 entry.callback.call(res);
208 }
209 });
210
211 Ok(id)
212 }
213
214 async fn run(
215 callback: OnStatus,
216 mut consumer: moq_audio::decode::Consumer,
217 mut close: oneshot::Receiver<()>,
218 ) -> Result<(), Error> {
219 loop {
220 let frame = tokio::select! {
222 biased;
223 _ = &mut close => return Ok(()),
224 frame = consumer.read() => match frame {
225 Ok(Some(frame)) => frame,
226 Ok(None) => return Ok(()),
227 Err(moq_audio::Error::Decode(err)) => {
231 tracing::warn!(%err, "dropping an audio frame");
232 continue;
233 }
234 Err(err) => return Err(err.into()),
235 },
236 };
237
238 let frame_id = State::lock().audio.frames.insert(frame)?;
240 callback.call(Ok(frame_id));
241 }
242 }
243
244 pub fn consume_close(&mut self, id: Id) -> Result<(), Error> {
245 self.consumer_tasks
247 .get_mut(id)
248 .and_then(|entry| entry.as_mut())
249 .ok_or(Error::TrackNotFound)?
250 .close
251 .take()
252 .ok_or(Error::TrackNotFound)?;
253 Ok(())
254 }
255
256 pub fn frame_info(&self, id: Id, dst: &mut moq_audio_frame) -> Result<(), Error> {
257 let frame = self.frames.get(id).ok_or(Error::FrameNotFound)?;
258 *dst = moq_audio_frame {
259 timestamp_us: u64::try_from(frame.timestamp.as_micros()).unwrap_or(u64::MAX),
264 data: frame.data.as_ptr(),
265 data_size: frame.data.len(),
266 };
267 Ok(())
268 }
269
270 pub fn frame_free(&mut self, id: Id) -> Result<(), Error> {
271 self.frames.remove(id).ok_or(Error::FrameNotFound)?;
272 Ok(())
273 }
274}
275
276#[unsafe(no_mangle)]
291pub unsafe extern "C" fn moq_publish_audio_raw(
292 broadcast: u32,
293 name: *const c_char,
294 name_len: usize,
295 input: *const moq_audio_encoder_input,
296 output: *const moq_audio_encoder_output,
297) -> i32 {
298 ffi::enter(move || {
299 let broadcast = ffi::parse_id(broadcast)?;
300 let name = unsafe { ffi::parse_str(name, name_len)? }.to_string();
301 let raw_input = unsafe { input.as_ref() }.ok_or(Error::InvalidPointer)?;
302 let raw_output = unsafe { output.as_ref() }.ok_or(Error::InvalidPointer)?;
303 let codec_str = unsafe { ffi::parse_str(raw_output.codec, raw_output.codec_len)? };
304
305 let encoder_input = moq_audio::encode::Input {
306 format: audio_format_from_u32(raw_input.format)?,
307 sample_rate: raw_input.sample_rate,
308 channels: raw_input.channels,
309 };
310
311 let mut options = moq_audio::encode::Options::default();
314 options.track = Some(name);
315 options.codec = codec_str
316 .parse()
317 .map_err(|_| Error::UnknownFormat(codec_str.to_string()))?;
318 options.sample_rate = zeroable(raw_output.sample_rate);
319 options.channels = zeroable(raw_output.channels);
320 options.bitrate = zeroable(raw_output.bitrate);
321 options.frame_duration = Duration::from_millis(raw_output.frame_duration_ms.into());
322
323 let mut state = State::lock();
324 let State { publish, audio, .. } = &mut *state;
325 let (broadcast_producer, catalog) = publish.pair_mut(broadcast)?;
326
327 audio.publish(broadcast_producer, catalog.clone(), encoder_input, options)
328 })
329}
330
331fn zeroable(value: u32) -> Option<u32> {
334 (value != 0).then_some(value)
335}
336
337#[unsafe(no_mangle)]
346pub unsafe extern "C" fn moq_publish_audio_raw_frame(producer: u32, frame: *const moq_audio_frame) -> i32 {
347 ffi::enter(move || {
348 let producer = ffi::parse_id(producer)?;
349 let frame = unsafe { frame.as_ref() }.ok_or(Error::InvalidPointer)?;
350 let data = unsafe { ffi::parse_slice(frame.data, frame.data_size)? };
351
352 let owned = moq_audio::Frame {
353 timestamp: moq_net::Timestamp::from_micros(frame.timestamp_us).map_err(moq_audio::Error::from)?,
355 data: Bytes::copy_from_slice(data),
356 };
357
358 let producer = State::lock().audio.producer(producer)?;
359 producer.lock().as_mut().ok_or(Error::MediaNotFound)?.write(&owned)?;
360 Ok(())
361 })
362}
363
364#[unsafe(no_mangle)]
366pub extern "C" fn moq_publish_audio_raw_finish(producer: u32) -> i32 {
367 ffi::enter(move || {
368 let producer = ffi::parse_id(producer)?;
369 let producer = State::lock().audio.remove(producer)?;
372 producer.take().ok_or(Error::MediaNotFound)?.finish()?;
373 Ok(())
374 })
375}
376
377#[unsafe(no_mangle)]
399pub unsafe extern "C" fn moq_consume_audio_raw(
400 catalog: u32,
401 index: u32,
402 output: *const moq_audio_decoder_output,
403 on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
404 user_data: *mut c_void,
405) -> i32 {
406 ffi::enter(move || {
407 let catalog = ffi::parse_id(catalog)?;
408 let raw = unsafe { output.as_ref() }.ok_or(Error::InvalidPointer)?;
409
410 let mut config = moq_audio::decode::Config::default();
411 config.format = audio_format_from_u32(raw.format)?;
412 config.sample_rate = zeroable(raw.sample_rate);
413 config.channels = zeroable(raw.channels);
414 config.latency_max = (raw.latency_max_ms != 0).then(|| Duration::from_millis(raw.latency_max_ms));
415
416 let on_frame = unsafe { OnStatus::new(user_data, on_frame) };
417
418 let mut state = State::lock();
419 let (broadcast, audio_cfg, name) = state.consume.audio_rendition(catalog, index as usize)?;
420
421 let State { audio, .. } = &mut *state;
422 audio.consume(&broadcast, &audio_cfg, &name, config, on_frame)
423 })
424}
425
426#[unsafe(no_mangle)]
434pub extern "C" fn moq_consume_audio_raw_close(consumer: u32) -> i32 {
435 ffi::enter(move || {
436 let consumer = ffi::parse_id(consumer)?;
437 State::lock().audio.consume_close(consumer)
438 })
439}
440
441#[unsafe(no_mangle)]
449pub unsafe extern "C" fn moq_consume_audio_raw_frame(id: u32, dst: *mut moq_audio_frame) -> i32 {
450 ffi::enter(move || {
451 let id = ffi::parse_id(id)?;
452 let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
453 State::lock().audio.frame_info(id, dst)
454 })
455}
456
457#[unsafe(no_mangle)]
461pub extern "C" fn moq_consume_audio_raw_frame_free(id: u32) -> i32 {
462 ffi::enter(move || {
463 let id = ffi::parse_id(id)?;
464 State::lock().audio.frame_free(id)
465 })
466}