use std::sync::Arc;
use std::time::Duration;
use crate::consumer::MoqBroadcastConsumer;
use crate::error::MoqError;
use crate::ffi::Task;
use crate::producer::MoqBroadcastProducer;
#[derive(Clone, Copy, uniffi::Enum)]
pub enum MoqAudioFormat {
U8,
S16,
S32,
F32,
U8Planar,
S16Planar,
S32Planar,
F32Planar,
}
impl From<MoqAudioFormat> for moq_audio::AudioFormat {
fn from(f: MoqAudioFormat) -> Self {
match f {
MoqAudioFormat::U8 => Self::U8,
MoqAudioFormat::S16 => Self::S16,
MoqAudioFormat::S32 => Self::S32,
MoqAudioFormat::F32 => Self::F32,
MoqAudioFormat::U8Planar => Self::U8Planar,
MoqAudioFormat::S16Planar => Self::S16Planar,
MoqAudioFormat::S32Planar => Self::S32Planar,
MoqAudioFormat::F32Planar => Self::F32Planar,
}
}
}
#[derive(Clone, Copy, uniffi::Enum)]
pub enum MoqAudioCodec {
Opus,
}
impl From<MoqAudioCodec> for moq_audio::Codec {
fn from(c: MoqAudioCodec) -> Self {
match c {
MoqAudioCodec::Opus => Self::Opus,
}
}
}
#[derive(uniffi::Record)]
pub struct MoqAudioEncoderInput {
pub format: MoqAudioFormat,
pub sample_rate: u32,
pub channels: u32,
}
#[derive(uniffi::Record)]
pub struct MoqAudioEncoderOutput {
pub codec: MoqAudioCodec,
pub sample_rate: Option<u32>,
pub channels: Option<u32>,
pub bitrate: Option<u32>,
pub frame_duration_ms: u32,
}
#[derive(uniffi::Record)]
pub struct MoqAudioDecoderOutput {
pub format: MoqAudioFormat,
pub sample_rate: Option<u32>,
pub channels: Option<u32>,
pub latency_max_ms: Option<u64>,
}
#[derive(uniffi::Record)]
pub struct MoqAudioFrame {
pub timestamp_us: u64,
pub data: Vec<u8>,
}
impl From<moq_audio::Frame> for MoqAudioFrame {
fn from(f: moq_audio::Frame) -> Self {
Self {
timestamp_us: f.timestamp_us,
data: f.data.to_vec(),
}
}
}
impl From<MoqAudioFrame> for moq_audio::Frame {
fn from(f: MoqAudioFrame) -> Self {
Self {
timestamp_us: f.timestamp_us,
data: f.data.into(),
}
}
}
#[derive(uniffi::Object)]
pub struct MoqAudioProducer {
inner: std::sync::Mutex<Option<moq_audio::AudioProducer>>,
}
#[uniffi::export]
impl MoqAudioProducer {
pub fn write(&self, frame: MoqAudioFrame) -> Result<(), MoqError> {
let _guard = crate::ffi::RUNTIME.enter();
let mut guard = self.inner.lock().unwrap();
let producer = guard.as_mut().ok_or(MoqError::Closed)?;
producer.write(&frame.into())?;
Ok(())
}
pub fn finish(&self) -> Result<(), MoqError> {
let _guard = crate::ffi::RUNTIME.enter();
let producer = self.inner.lock().unwrap().take().ok_or(MoqError::Closed)?;
producer.finish()?;
Ok(())
}
}
#[uniffi::export]
impl MoqBroadcastProducer {
pub fn publish_audio(
&self,
name: String,
input: MoqAudioEncoderInput,
output: MoqAudioEncoderOutput,
) -> Result<Arc<MoqAudioProducer>, MoqError> {
let _guard = crate::ffi::RUNTIME.enter();
let producer = self.with_state(|state| {
moq_audio::AudioProducer::new(
&mut state.broadcast,
state.catalog.clone(),
name,
moq_audio::EncoderInput {
format: input.format.into(),
sample_rate: input.sample_rate,
channels: input.channels,
},
moq_audio::EncoderOutput {
codec: output.codec.into(),
sample_rate: output.sample_rate,
channels: output.channels,
bitrate: output.bitrate,
frame_duration: Duration::from_millis(output.frame_duration_ms.into()),
},
)
.map_err(Into::into)
})?;
Ok(Arc::new(MoqAudioProducer {
inner: std::sync::Mutex::new(Some(producer)),
}))
}
}
struct ConsumerInner {
consumer: moq_audio::AudioConsumer,
}
impl ConsumerInner {
async fn next(&mut self) -> Result<Option<MoqAudioFrame>, MoqError> {
Ok(self.consumer.read().await?.map(Into::into))
}
}
#[derive(uniffi::Object)]
pub struct MoqAudioConsumer {
task: Task<ConsumerInner>,
}
#[uniffi::export]
impl MoqAudioConsumer {
pub async fn next(&self) -> Result<Option<MoqAudioFrame>, MoqError> {
self.task.run(|mut state| async move { state.next().await }).await
}
pub fn cancel(&self) {
self.task.cancel();
}
}
#[uniffi::export]
impl MoqBroadcastConsumer {
pub fn subscribe_audio(
&self,
name: String,
catalog_audio: crate::media::MoqAudio,
output: MoqAudioDecoderOutput,
) -> Result<Arc<MoqAudioConsumer>, MoqError> {
let _guard = crate::ffi::RUNTIME.enter();
let mut cfg = hang::catalog::AudioConfig::new(
hang::catalog::AudioCodec::Opus,
catalog_audio.sample_rate,
catalog_audio.channel_count,
);
cfg.bitrate = catalog_audio.bitrate;
cfg.description = catalog_audio.description.map(Into::into);
cfg.container = catalog_audio.container.into();
let consumer = moq_audio::AudioConsumer::new(
self.inner(),
&cfg,
name,
moq_audio::DecoderOutput {
format: output.format.into(),
sample_rate: output.sample_rate,
channels: output.channels,
latency_max: output.latency_max_ms.map(Duration::from_millis),
},
)?;
Ok(Arc::new(MoqAudioConsumer {
task: Task::new(ConsumerInner { consumer }),
}))
}
}