1use std::ffi::{c_char, c_void};
15use std::sync::Arc;
16use std::time::Duration;
17
18use bytes::Bytes;
19use tokio::sync::oneshot;
20
21use crate::ffi::OnStatus;
22use crate::{Error, Id, NonZeroSlab, Shared, State, ffi};
23
24#[repr(C)]
35#[allow(non_camel_case_types)]
36#[derive(Clone, Copy, Debug)]
37pub enum moq_audio_sample_format {
38 MOQ_AUDIO_SAMPLE_FORMAT_U8 = 0,
39 MOQ_AUDIO_SAMPLE_FORMAT_S16 = 1,
40 MOQ_AUDIO_SAMPLE_FORMAT_S32 = 2,
41 MOQ_AUDIO_SAMPLE_FORMAT_F32 = 3,
42 MOQ_AUDIO_SAMPLE_FORMAT_U8_PLANAR = 4,
43 MOQ_AUDIO_SAMPLE_FORMAT_S16_PLANAR = 5,
44 MOQ_AUDIO_SAMPLE_FORMAT_S32_PLANAR = 6,
45 MOQ_AUDIO_SAMPLE_FORMAT_F32_PLANAR = 7,
46}
47
48fn audio_format_from_u32(value: u32) -> Result<moq_audio::Format, Error> {
49 use moq_audio::Format;
50 Ok(match value {
51 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_U8 as u32 => Format::U8,
52 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_S16 as u32 => Format::S16,
53 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_S32 as u32 => Format::S32,
54 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_F32 as u32 => Format::F32,
55 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_U8_PLANAR as u32 => Format::U8Planar,
56 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_S16_PLANAR as u32 => Format::S16Planar,
57 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_S32_PLANAR as u32 => Format::S32Planar,
58 v if v == moq_audio_sample_format::MOQ_AUDIO_SAMPLE_FORMAT_F32_PLANAR as u32 => Format::F32Planar,
59 _ => return Err(Error::InvalidCode),
60 })
61}
62
63#[repr(C)]
65#[allow(non_camel_case_types)]
66pub struct moq_audio_encoder_input {
67 pub format: u32,
69 pub sample_rate: u32,
70 pub channels: u32,
71}
72
73#[repr(C)]
77#[allow(non_camel_case_types)]
78pub struct moq_audio_encoder_output {
79 pub codec: *const c_char,
81 pub codec_len: usize,
82 pub sample_rate: u32,
84 pub channels: u32,
86 pub bitrate: u32,
88 pub frame_duration_us: u32,
92}
93
94#[repr(C)]
96#[allow(non_camel_case_types)]
97pub struct moq_audio_decoder_output {
98 pub format: u32,
99 pub sample_rate: u32,
101 pub channels: u32,
103 pub max_age_us: u64,
111}
112
113#[repr(C)]
119#[allow(non_camel_case_types)]
120pub struct moq_audio_frame {
121 pub timestamp_us: u64,
122 pub data: *const u8,
123 pub data_size: usize,
124}
125
126pub(crate) struct AudioEncoder {
131 producer: moq_audio::encode::Producer<moq_mux::catalog::hang::Extra>,
132 reservation: Option<Arc<moq_net::bandwidth::Reservation>>,
133}
134
135type AudioProducer = Shared<AudioEncoder>;
136
137#[derive(Default)]
138pub struct Audio {
139 producers: NonZeroSlab<AudioProducer>,
140 consumer_tasks: NonZeroSlab<Option<AudioTaskEntry>>,
141 frames: NonZeroSlab<moq_audio::Frame>,
142}
143
144struct AudioTaskEntry {
150 close: Option<oneshot::Sender<()>>,
151 callback: OnStatus,
152}
153
154impl Audio {
155 pub fn publish(
156 &mut self,
157 broadcast: &mut moq_net::broadcast::Producer,
158 catalog: moq_mux::catalog::Producer<moq_mux::catalog::hang::Extra>,
159 input: moq_audio::encode::Input,
160 options: moq_audio::encode::Options,
161 reserve: bool,
162 ) -> Result<Id, Error> {
163 let producer = moq_audio::encode::Producer::new(broadcast, catalog, input, &options)?;
164 let reservation = reserve.then(|| Arc::new(options.bandwidth.reserve(&producer.demand(), producer.bitrate())));
165 self.producers
166 .insert(Shared::new(AudioEncoder { producer, reservation }))
167 }
168
169 pub(crate) fn reservation(&self, id: Id) -> Result<Option<Arc<moq_net::bandwidth::Reservation>>, Error> {
170 Ok(self
171 .producer(id)?
172 .lock()
173 .as_ref()
174 .ok_or(Error::MediaNotFound)?
175 .reservation
176 .clone())
177 }
178
179 pub(crate) fn demand(&self, id: Id) -> Result<moq_net::track::Demand, Error> {
181 Ok(self
182 .producer(id)?
183 .lock()
184 .as_ref()
185 .ok_or(Error::MediaNotFound)?
186 .producer
187 .demand())
188 }
189
190 pub(crate) fn producer(&self, id: Id) -> Result<AudioProducer, Error> {
197 self.producers.get(id).cloned().ok_or(Error::MediaNotFound)
198 }
199
200 pub(crate) fn remove(&mut self, id: Id) -> Result<AudioProducer, Error> {
202 self.producers.remove(id).ok_or(Error::MediaNotFound)
203 }
204
205 pub fn consume(
206 &mut self,
207 broadcast: &moq_net::broadcast::Consumer,
208 catalog: &hang::catalog::AudioConfig,
209 name: &str,
210 config: moq_audio::decode::Options,
211 on_frame: OnStatus,
212 ) -> Result<Id, Error> {
213 let broadcast = broadcast.clone();
214 let catalog = catalog.clone();
215 let name = name.to_string();
216
217 let channel = oneshot::channel();
218 let entry = AudioTaskEntry {
219 close: Some(channel.0),
220 callback: on_frame,
221 };
222 let id = self.consumer_tasks.insert(Some(entry))?;
223
224 tokio::spawn(async move {
227 let res = async move {
228 let consumer = moq_audio::decode::Consumer::new(&broadcast, &catalog, name, config).await?;
229 Self::run(on_frame, consumer, channel.1).await
230 }
231 .await;
232
233 let entry = State::lock().audio.consumer_tasks.remove(id).flatten();
236 if let Some(entry) = entry {
237 entry.callback.call(res);
238 }
239 });
240
241 Ok(id)
242 }
243
244 async fn run(
245 callback: OnStatus,
246 mut consumer: moq_audio::decode::Consumer,
247 mut close: oneshot::Receiver<()>,
248 ) -> Result<(), Error> {
249 loop {
250 let frame = tokio::select! {
252 biased;
253 _ = &mut close => return Ok(()),
254 frame = consumer.read() => match frame {
255 Ok(Some(frame)) => frame,
256 Ok(None) => return Ok(()),
257 Err(moq_audio::Error::Decode(err)) => {
261 tracing::warn!(%err, "dropping an audio frame");
262 continue;
263 }
264 Err(err) => return Err(err.into()),
265 },
266 };
267
268 let frame_id = State::lock().audio.frames.insert(frame)?;
270 callback.call(Ok(frame_id));
271 }
272 }
273
274 pub fn consume_close(&mut self, id: Id) -> Result<(), Error> {
275 self.consumer_tasks
277 .get_mut(id)
278 .and_then(|entry| entry.as_mut())
279 .ok_or(Error::TrackNotFound)?
280 .close
281 .take()
282 .ok_or(Error::TrackNotFound)?;
283 Ok(())
284 }
285
286 pub fn frame_info(&self, id: Id, dst: &mut moq_audio_frame) -> Result<(), Error> {
287 let frame = self.frames.get(id).ok_or(Error::FrameNotFound)?;
288 *dst = moq_audio_frame {
289 timestamp_us: u64::try_from(frame.timestamp.as_micros()).unwrap_or(u64::MAX),
294 data: frame.data.as_ptr(),
295 data_size: frame.data.len(),
296 };
297 Ok(())
298 }
299
300 pub fn frame_free(&mut self, id: Id) -> Result<(), Error> {
301 self.frames.remove(id).ok_or(Error::FrameNotFound)?;
302 Ok(())
303 }
304}
305
306#[unsafe(no_mangle)]
323pub unsafe extern "C" fn moq_encode_audio(
324 broadcast: u32,
325 name: *const c_char,
326 name_len: usize,
327 input: *const moq_audio_encoder_input,
328 output: *const moq_audio_encoder_output,
329 bandwidth: u32,
330) -> i32 {
331 ffi::enter(move || {
332 let broadcast = ffi::parse_id(broadcast)?;
333 let name = unsafe { ffi::parse_str(name, name_len)? }.to_string();
334 let raw_input = unsafe { input.as_ref() }.ok_or(Error::InvalidPointer)?;
335 let raw_output = unsafe { output.as_ref() }.ok_or(Error::InvalidPointer)?;
336 let codec_str = unsafe { ffi::parse_str(raw_output.codec, raw_output.codec_len)? };
337
338 let layout = moq_audio::Layout::from_channels(raw_input.channels)?;
339 let mut encoder_input = moq_audio::encode::Input::new(raw_input.sample_rate, layout);
340 encoder_input.format = audio_format_from_u32(raw_input.format)?;
341
342 let mut options = moq_audio::encode::Options::default();
345 options.track = Some(name);
346 let codec = codec_str
347 .parse()
348 .map_err(|_| Error::UnknownFormat(codec_str.to_string()))?;
349 options.settings = moq_audio::encode::Settings::from_input(codec, &encoder_input);
350 if let Some(sample_rate) = zeroable(raw_output.sample_rate) {
351 options.settings.sample_rate = sample_rate;
352 }
353 if let Some(channels) = zeroable(raw_output.channels) {
354 options.settings.layout = moq_audio::Layout::from_channels(channels)?;
355 }
356 options.settings.bitrate =
357 zeroable(raw_output.bitrate).map(|bps| moq_net::bandwidth::Rate::from_bps(bps.into()));
358 if let Some(micros) = zeroable(raw_output.frame_duration_us) {
359 options.settings.frame_duration = Duration::from_micros(micros.into());
360 }
361
362 let bandwidth = ffi::parse_id_optional(bandwidth)?;
363 let mut state = State::lock();
364 if let Some(id) = bandwidth {
365 options.bandwidth = state.bandwidth.allocator(id)?;
366 }
367 let State { publish, audio, .. } = &mut *state;
368 let (broadcast_producer, catalog) = publish.pair_mut(broadcast)?;
369
370 audio.publish(
371 broadcast_producer,
372 catalog.clone(),
373 encoder_input,
374 options,
375 bandwidth.is_some(),
376 )
377 })
378}
379
380#[unsafe(no_mangle)]
385pub extern "C" fn moq_encode_audio_reservation(producer: u32) -> i32 {
386 ffi::enter(move || {
387 let producer = ffi::parse_id(producer)?;
388 let mut state = State::lock();
389 match state.audio.reservation(producer)? {
390 Some(reservation) => Ok(i32::from(state.bandwidth.hold(reservation)?)),
391 None => Ok(0),
392 }
393 })
394}
395
396#[unsafe(no_mangle)]
406pub unsafe extern "C" fn moq_encode_audio_demand(
407 producer: u32,
408 on_demand: crate::moq_status_callback,
409 user_data: *mut c_void,
410) -> i32 {
411 ffi::enter(move || {
412 let producer = ffi::parse_id(producer)?;
413 let on_demand = unsafe { OnStatus::new(user_data, on_demand)? };
414 let mut state = State::lock();
415 let demand = state.audio.demand(producer)?;
416 state.publish.demand(demand, on_demand)
417 })
418}
419
420fn zeroable(value: u32) -> Option<u32> {
423 (value != 0).then_some(value)
424}
425
426#[unsafe(no_mangle)]
435pub unsafe extern "C" fn moq_encode_audio_frame(producer: u32, frame: *const moq_audio_frame) -> i32 {
436 ffi::enter(move || {
437 let producer = ffi::parse_id(producer)?;
438 let frame = unsafe { frame.as_ref() }.ok_or(Error::InvalidPointer)?;
439 let data = unsafe { ffi::parse_slice(frame.data, frame.data_size)? };
440
441 let timestamp = moq_net::Timestamp::from_micros(frame.timestamp_us).map_err(moq_audio::Error::from)?;
443 let owned = moq_audio::Frame::new(Bytes::copy_from_slice(data), timestamp);
444
445 let producer = State::lock().audio.producer(producer)?;
446 producer
447 .lock()
448 .as_mut()
449 .ok_or(Error::MediaNotFound)?
450 .producer
451 .write(&owned)?;
452 Ok(())
453 })
454}
455
456#[unsafe(no_mangle)]
458pub extern "C" fn moq_encode_audio_finish(producer: u32) -> i32 {
459 ffi::enter(move || {
460 let producer = ffi::parse_id(producer)?;
461 let producer = State::lock().audio.remove(producer)?;
464 let mut producer = producer.take().ok_or(Error::MediaNotFound)?.producer;
465 producer.finish()?;
466 Ok(())
467 })
468}
469
470#[unsafe(no_mangle)]
494pub unsafe extern "C" fn moq_decode_audio(
495 catalog: u32,
496 index: u32,
497 output: *const moq_audio_decoder_output,
498 on_frame: crate::moq_status_callback,
499 user_data: *mut c_void,
500) -> i32 {
501 ffi::enter(move || {
502 let catalog = ffi::parse_id(catalog)?;
503 let raw = unsafe { output.as_ref() }.ok_or(Error::InvalidPointer)?;
504
505 let mut config = moq_audio::decode::Options::default();
506 config.start = moq_audio::decode::Start::Latest;
507 config.output.format = audio_format_from_u32(raw.format)?;
508 config.output.sample_rate = zeroable(raw.sample_rate);
509 config.output.layout = zeroable(raw.channels)
510 .map(moq_audio::Layout::from_channels)
511 .transpose()?;
512 config.max_age = Duration::from_micros(raw.max_age_us);
513
514 let on_frame = unsafe { OnStatus::new(user_data, on_frame)? };
515
516 let mut state = State::lock();
517 let (broadcast, audio_cfg, name) = state.consume.audio_rendition(catalog, index as usize)?;
518
519 let State { audio, .. } = &mut *state;
520 audio.consume(&broadcast, &audio_cfg, &name, config, on_frame)
521 })
522}
523
524#[unsafe(no_mangle)]
532pub extern "C" fn moq_decode_audio_cancel(consumer: u32) -> i32 {
533 ffi::enter(move || {
534 let consumer = ffi::parse_id(consumer)?;
535 State::lock().audio.consume_close(consumer)
536 })
537}
538
539#[unsafe(no_mangle)]
547pub unsafe extern "C" fn moq_decode_audio_frame(id: u32, dst: *mut moq_audio_frame) -> i32 {
548 ffi::enter(move || {
549 let id = ffi::parse_id(id)?;
550 let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
551 State::lock().audio.frame_info(id, dst)
552 })
553}
554
555#[unsafe(no_mangle)]
559pub extern "C" fn moq_decode_audio_frame_free(id: u32) -> i32 {
560 ffi::enter(move || {
561 let id = ffi::parse_id(id)?;
562 State::lock().audio.frame_free(id)
563 })
564}