use crate::{Error, State, ffi};
use std::ffi::c_char;
use std::ffi::c_void;
use std::str::FromStr;
use tracing::Level;
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_video_config {
pub name: *const c_char,
pub name_len: usize,
pub codec: *const c_char,
pub codec_len: usize,
pub description: *const u8,
pub description_len: usize,
pub coded_width: *const u32,
pub coded_height: *const u32,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_audio_config {
pub name: *const c_char,
pub name_len: usize,
pub codec: *const c_char,
pub codec_len: usize,
pub description: *const u8,
pub description_len: usize,
pub sample_rate: u32,
pub channel_count: u32,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_frame {
pub payload: *const u8,
pub payload_size: usize,
pub timestamp_us: u64,
pub keyframe: bool,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_announced {
pub path: *const c_char,
pub path_len: usize,
pub active: bool,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_log_level(level: *const c_char, level_len: usize) -> i32 {
ffi::enter(move || {
match unsafe { ffi::parse_str(level, level_len)? } {
"" => moq_native::Log::default(),
level => moq_native::Log::new(Level::from_str(level)?),
}
.init()
.map_err(|err| Error::InitFailed(std::sync::Arc::new(err)))?;
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_session_connect(
url: *const c_char,
url_len: usize,
origin_publish: u32,
origin_consume: u32,
on_status: Option<extern "C" fn(user_data: *mut c_void, code: i32)>,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let url = ffi::parse_url(url, url_len)?;
let mut state = State::lock();
let publish = ffi::parse_id_optional(origin_publish)?
.map(|id| state.origin.get(id))
.transpose()?
.map(|origin: &moq_net::OriginProducer| origin.consume());
let consume = ffi::parse_id_optional(origin_consume)?
.map(|id| state.origin.get(id))
.transpose()?
.cloned();
let on_status = unsafe { ffi::OnStatus::new(user_data, on_status) };
state.session.connect(url, publish, consume, on_status)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_session_close(session: u32) -> i32 {
ffi::enter(move || {
let session = ffi::parse_id(session)?;
State::lock().session.close(session)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_origin_create() -> i32 {
ffi::enter(move || State::lock().origin.create())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_publish(origin: u32, path: *const c_char, path_len: usize, broadcast: u32) -> i32 {
ffi::enter(move || {
let origin = ffi::parse_id(origin)?;
let path = unsafe { ffi::parse_str(path, path_len)? };
let broadcast = ffi::parse_id(broadcast)?;
let mut state = State::lock();
let broadcast = state.publish.get(broadcast)?.consume();
state.origin.publish(origin, path, broadcast)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_announced(
origin: u32,
on_announce: Option<extern "C" fn(user_data: *mut c_void, announced: i32)>,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let origin = ffi::parse_id(origin)?;
let on_announce = unsafe { ffi::OnStatus::new(user_data, on_announce) };
State::lock().origin.announced(origin, on_announce)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_announced_info(announced: u32, dst: *mut moq_announced) -> i32 {
ffi::enter(move || {
let announced = ffi::parse_id(announced)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().origin.announced_info(announced, dst)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_origin_announced_close(announced: u32) -> i32 {
ffi::enter(move || {
let announced = ffi::parse_id(announced)?;
State::lock().origin.announced_close(announced)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_consume(origin: u32, path: *const c_char, path_len: usize) -> i32 {
ffi::enter(move || {
let origin = ffi::parse_id(origin)?;
let path = unsafe { ffi::parse_str(path, path_len)? };
let mut state = State::lock();
let broadcast = state.origin.consume(origin, path)?;
state.consume.start(broadcast)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_consume_announced(
origin: u32,
path: *const c_char,
path_len: usize,
on_broadcast: Option<extern "C" fn(user_data: *mut c_void, broadcast: i32)>,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let origin = ffi::parse_id(origin)?;
let path = unsafe { ffi::parse_str(path, path_len)? }.to_string();
let on_broadcast = unsafe { ffi::OnStatus::new(user_data, on_broadcast) };
State::lock().origin.consume_announced(origin, path, on_broadcast)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_origin_consume_announced_close(task: u32) -> i32 {
ffi::enter(move || {
let task = ffi::parse_id(task)?;
State::lock().origin.consume_announced_close(task)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_origin_close(origin: u32) -> i32 {
ffi::enter(move || {
let origin = ffi::parse_id(origin)?;
State::lock().origin.close(origin)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_create() -> i32 {
ffi::enter(move || State::lock().publish.create())
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_close(broadcast: u32) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
State::lock().publish.close(broadcast)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_media_ordered(
broadcast: u32,
format: *const c_char,
format_len: usize,
init: *const u8,
init_size: usize,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let format = unsafe { ffi::parse_str(format, format_len)? };
let init = unsafe { ffi::parse_slice(init, init_size)? };
State::lock().publish.media_ordered(broadcast, format, init)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_media_close(export: u32) -> i32 {
ffi::enter(move || {
let export = ffi::parse_id(export)?;
State::lock().publish.media_close(export)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_media_frame(
media: u32,
payload: *const u8,
payload_size: usize,
timestamp_us: u64,
) -> i32 {
ffi::enter(move || {
let media = ffi::parse_id(media)?;
let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
let timestamp = hang::container::Timestamp::from_micros(timestamp_us)?;
State::lock().publish.media_frame(media, payload, timestamp)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_video_config(broadcast: u32, config: *const moq_video_config) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let name = unsafe { ffi::parse_str(config.name, config.name_len)? };
let codec = unsafe { ffi::parse_str(config.codec, config.codec_len)? };
let codec = hang::catalog::VideoCodec::from_str(codec).map_err(Error::Hang)?;
let mut video = hang::catalog::VideoConfig::new(codec);
if !config.description.is_null() {
let description = unsafe { ffi::parse_slice(config.description, config.description_len)? };
video.description = Some(bytes::Bytes::copy_from_slice(description));
}
video.coded_width = unsafe { config.coded_width.as_ref() }.copied();
video.coded_height = unsafe { config.coded_height.as_ref() }.copied();
State::lock().publish.video_config(broadcast, name, video)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_audio_config(broadcast: u32, config: *const moq_audio_config) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let name = unsafe { ffi::parse_str(config.name, config.name_len)? };
let codec = unsafe { ffi::parse_str(config.codec, config.codec_len)? };
let codec = hang::catalog::AudioCodec::from_str(codec).map_err(Error::Hang)?;
let mut audio = hang::catalog::AudioConfig::new(codec, config.sample_rate, config.channel_count);
if !config.description.is_null() {
let description = unsafe { ffi::parse_slice(config.description, config.description_len)? };
audio.description = Some(bytes::Bytes::copy_from_slice(description));
}
State::lock().publish.audio_config(broadcast, name, audio)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_video_remove(broadcast: u32, name: *const c_char, name_len: usize) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
State::lock().publish.video_remove(broadcast, name)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_audio_remove(broadcast: u32, name: *const c_char, name_len: usize) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
State::lock().publish.audio_remove(broadcast, name)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_track(broadcast: u32, name: *const c_char, name_len: usize) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
State::lock().publish.track(broadcast, name)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_track_group(track: u32) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().publish.track_group(track)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_track_frame(track: u32, payload: *const u8, payload_size: usize) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
State::lock().publish.track_frame(track, payload)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_track_close(track: u32) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().publish.track_finish(track)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_group_frame(group: u32, payload: *const u8, payload_size: usize) -> i32 {
ffi::enter(move || {
let group = ffi::parse_id(group)?;
let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
State::lock().publish.group_frame(group, payload)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_group_close(group: u32) -> i32 {
ffi::enter(move || {
let group = ffi::parse_id(group)?;
State::lock().publish.group_finish(group)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_catalog(
broadcast: u32,
on_catalog: Option<extern "C" fn(user_data: *mut c_void, catalog: i32)>,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let on_catalog = unsafe { ffi::OnStatus::new(user_data, on_catalog) };
State::lock().consume.catalog(broadcast, on_catalog)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_catalog_close(catalog: u32) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
State::lock().consume.catalog_close(catalog)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_catalog_free(catalog: u32) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
State::lock().consume.catalog_free(catalog)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_video_config(catalog: u32, index: u32, dst: *mut moq_video_config) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let index = index as usize;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.video_config(catalog, index, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_audio_config(catalog: u32, index: u32, dst: *mut moq_audio_config) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let index = index as usize;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.audio_config(catalog, index, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_video_ordered(
catalog: u32,
index: u32,
max_latency_ms: u64,
on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let index = index as usize;
let max_latency = std::time::Duration::from_millis(max_latency_ms);
let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame) };
State::lock()
.consume
.video_ordered(catalog, index, max_latency, on_frame)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_video_close(track: u32) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().consume.track_close(track)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_audio_ordered(
catalog: u32,
index: u32,
max_latency_ms: u64,
on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let index = index as usize;
let max_latency = std::time::Duration::from_millis(max_latency_ms);
let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame) };
State::lock()
.consume
.audio_ordered(catalog, index, max_latency, on_frame)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_audio_close(track: u32) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().consume.track_close(track)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_frame(frame: u32, dst: *mut moq_frame) -> i32 {
ffi::enter(move || {
let frame = ffi::parse_id(frame)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.frame(frame, dst)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_frame_close(frame: u32) -> i32 {
ffi::enter(move || {
let frame = ffi::parse_id(frame)?;
State::lock().consume.frame_close(frame)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_close(consume: u32) -> i32 {
ffi::enter(move || {
let consume = ffi::parse_id(consume)?;
State::lock().consume.close(consume)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_track(
broadcast: u32,
name: *const c_char,
name_len: usize,
on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame) };
State::lock().consume.raw_track(broadcast, name, on_frame)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_track_frame(frame: u32, dst: *mut moq_frame) -> i32 {
ffi::enter(move || {
let frame = ffi::parse_id(frame)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.raw_frame(frame, dst)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_track_frame_close(frame: u32) -> i32 {
ffi::enter(move || {
let frame = ffi::parse_id(frame)?;
State::lock().consume.raw_frame_close(frame)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_track_close(track: u32) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().consume.raw_track_close(track)
})
}