use crate::ffi::ReturnCode;
use crate::{Connect, Error, State, ffi, moq_protocol_error};
use std::ffi::c_char;
use std::ffi::c_void;
use std::str::FromStr;
use tracing::Level;
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
pub enum moq_container_kind {
MOQ_CONTAINER_KIND_LEGACY = 0,
MOQ_CONTAINER_KIND_CMAF = 1,
MOQ_CONTAINER_KIND_LOC = 2,
MOQ_CONTAINER_KIND_UNKNOWN = 3,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub struct moq_container {
pub kind: u32,
pub init: *const u8,
pub init_len: usize,
}
impl Default for moq_container {
fn default() -> Self {
Self {
kind: moq_container_kind::MOQ_CONTAINER_KIND_LEGACY as u32,
init: std::ptr::null(),
init_len: 0,
}
}
}
pub(crate) unsafe fn parse_container(container: &moq_container) -> Result<hang::catalog::Container, Error> {
use hang::catalog::Container;
Ok(match container.kind {
v if v == moq_container_kind::MOQ_CONTAINER_KIND_LEGACY as u32 => Container::Legacy,
v if v == moq_container_kind::MOQ_CONTAINER_KIND_CMAF as u32 => {
let init = unsafe { ffi::parse_slice(container.init, container.init_len)? };
if init.is_empty() {
return Err(Error::InvalidPointer);
}
Container::Cmaf {
init: bytes::Bytes::copy_from_slice(init),
}
}
v if v == moq_container_kind::MOQ_CONTAINER_KIND_LOC as u32 => Container::Loc,
_ => return Err(Error::InvalidCode),
})
}
pub(crate) fn borrow_container(container: &hang::catalog::Container) -> moq_container {
use hang::catalog::Container;
let (kind, init) = match container {
Container::Legacy => (moq_container_kind::MOQ_CONTAINER_KIND_LEGACY, None),
Container::Cmaf { init } => (moq_container_kind::MOQ_CONTAINER_KIND_CMAF, Some(init)),
Container::Loc => (moq_container_kind::MOQ_CONTAINER_KIND_LOC, None),
Container::Unknown(_) => (moq_container_kind::MOQ_CONTAINER_KIND_UNKNOWN, None),
};
moq_container {
kind: kind as u32,
init: init.map_or(std::ptr::null(), |init| init.as_ptr()),
init_len: init.map_or(0, |init| init.len()),
}
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum moq_audio_format {
MOQ_AUDIO_FORMAT_AAC = 0,
MOQ_AUDIO_FORMAT_OPUS = 1,
MOQ_AUDIO_FORMAT_FLAC = 2,
MOQ_AUDIO_FORMAT_MP3 = 3,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum moq_video_format {
MOQ_VIDEO_FORMAT_AVC1 = 0,
MOQ_VIDEO_FORMAT_AVC3 = 1,
MOQ_VIDEO_FORMAT_HVC1 = 2,
MOQ_VIDEO_FORMAT_HEV1 = 3,
MOQ_VIDEO_FORMAT_AV01 = 4,
MOQ_VIDEO_FORMAT_VP8 = 5,
MOQ_VIDEO_FORMAT_VP9 = 6,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum moq_container_format {
MOQ_CONTAINER_FORMAT_FMP4 = 0,
MOQ_CONTAINER_FORMAT_MKV = 1,
MOQ_CONTAINER_FORMAT_TS = 2,
MOQ_CONTAINER_FORMAT_FLV = 3,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_audio_init {
pub format: u32,
pub init: *const u8,
pub init_len: usize,
pub label: *const c_char,
pub label_len: usize,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_video_init {
pub format: u32,
pub init: *const u8,
pub init_len: usize,
pub label: *const c_char,
pub label_len: usize,
pub hint: moq_video_hint,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Default)]
pub struct moq_video_hint {
pub coded_width: u32,
pub coded_height: u32,
pub has_coded: bool,
pub bitrate: u64,
pub has_bitrate: bool,
pub framerate: f64,
pub has_framerate: bool,
pub optimize_for_latency: bool,
pub has_optimize_for_latency: bool,
}
impl moq_video_hint {
fn resolve(&self) -> moq_mux::catalog::VideoHint {
let mut out = moq_mux::catalog::VideoHint::default();
if self.has_coded {
out.coded_width = Some(self.coded_width);
out.coded_height = Some(self.coded_height);
}
if self.has_bitrate {
out.bitrate = Some(self.bitrate);
}
if self.has_framerate {
out.framerate = Some(self.framerate);
}
if self.has_optimize_for_latency {
out.optimize_for_latency = Some(self.optimize_for_latency);
}
out
}
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_container_init {
pub format: u32,
pub init: *const u8,
pub init_len: usize,
}
fn audio_format_from_u32(value: u32) -> Result<moq_mux::import::AudioFormat, Error> {
use moq_mux::import::AudioFormat;
Ok(match value {
v if v == moq_audio_format::MOQ_AUDIO_FORMAT_AAC as u32 => AudioFormat::Aac,
v if v == moq_audio_format::MOQ_AUDIO_FORMAT_OPUS as u32 => AudioFormat::Opus,
v if v == moq_audio_format::MOQ_AUDIO_FORMAT_FLAC as u32 => AudioFormat::Flac,
v if v == moq_audio_format::MOQ_AUDIO_FORMAT_MP3 as u32 => AudioFormat::Mp3,
_ => return Err(Error::InvalidCode),
})
}
fn video_format_from_u32(value: u32) -> Result<moq_mux::import::VideoFormat, Error> {
use moq_mux::import::VideoFormat;
Ok(match value {
v if v == moq_video_format::MOQ_VIDEO_FORMAT_AVC1 as u32 => VideoFormat::Avc1,
v if v == moq_video_format::MOQ_VIDEO_FORMAT_AVC3 as u32 => VideoFormat::Avc3,
v if v == moq_video_format::MOQ_VIDEO_FORMAT_HVC1 as u32 => VideoFormat::Hvc1,
v if v == moq_video_format::MOQ_VIDEO_FORMAT_HEV1 as u32 => VideoFormat::Hev1,
v if v == moq_video_format::MOQ_VIDEO_FORMAT_AV01 as u32 => VideoFormat::Av01,
v if v == moq_video_format::MOQ_VIDEO_FORMAT_VP8 as u32 => VideoFormat::Vp8,
v if v == moq_video_format::MOQ_VIDEO_FORMAT_VP9 as u32 => VideoFormat::Vp9,
_ => return Err(Error::InvalidCode),
})
}
fn container_format_from_u32(value: u32) -> Result<moq_mux::import::ContainerFormat, Error> {
use moq_mux::import::ContainerFormat;
Ok(match value {
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_FMP4 as u32 => ContainerFormat::Fmp4,
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_MKV as u32 => ContainerFormat::Mkv,
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_TS as u32 => ContainerFormat::Ts,
v if v == moq_container_format::MOQ_CONTAINER_FORMAT_FLV as u32 => ContainerFormat::Flv,
_ => return Err(Error::InvalidCode),
})
}
#[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: u32,
pub coded_height: u32,
pub container: moq_container,
pub label: *const c_char,
pub label_len: usize,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Default)]
pub struct moq_video_properties {
pub display_width: u32,
pub display_height: u32,
pub has_display: bool,
pub rotation: f64,
pub has_rotation: bool,
pub flip: bool,
pub has_flip: bool,
}
#[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,
pub container: moq_container,
pub label: *const c_char,
pub label_len: usize,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_json_snapshot_config {
pub delta_ratio: u32,
pub compression: bool,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_json_stream_config {
pub compression: bool,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_binary_config {
pub compression: bool,
pub mime: *const c_char,
pub mime_len: usize,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_json_value {
pub json: *const c_char,
pub json_len: usize,
}
#[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_datagram {
pub payload: *const u8,
pub payload_size: usize,
pub timestamp_us: u64,
pub sequence: u64,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_track_info {
pub priority: u8,
pub max_age_us: u64,
pub max_age_present: bool,
pub timescale: u64,
pub timescale_present: bool,
}
impl TryFrom<&moq_track_info> for moq_net::track::Info {
type Error = Error;
fn try_from(info: &moq_track_info) -> Result<Self, Self::Error> {
let mut out = moq_net::track::Info::default()
.with_timescale(moq_net::Timescale::MICRO)
.with_priority(info.priority);
if info.max_age_present {
out = out.with_max_age(std::time::Duration::from_micros(info.max_age_us));
}
if info.timescale_present {
out = out.with_timescale(moq_net::Timescale::new(info.timescale)?);
}
Ok(out)
}
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
pub enum moq_demand {
MOQ_DEMAND_USED = 1,
MOQ_DEMAND_UNUSED = 2,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_subscription {
pub priority: u8,
pub max_age_us: u64,
pub group_start: u64,
pub group_start_present: bool,
pub group_end: u64,
pub group_end_present: bool,
}
impl From<&moq_subscription> for moq_net::track::Subscription {
fn from(subscription: &moq_subscription) -> Self {
let mut out = moq_net::track::Subscription::default()
.with_priority(subscription.priority)
.with_max_age(std::time::Duration::from_micros(subscription.max_age_us));
if subscription.group_start_present {
out = out.with_start(moq_net::track::Position::group(subscription.group_start));
}
if subscription.group_end_present {
out = out.with_end(moq_net::track::Position::group(subscription.group_end));
}
out
}
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub struct moq_string {
pub data: *const c_char,
pub len: usize,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_section {
pub name: *const c_char,
pub name_len: usize,
pub json: *const c_char,
pub json_len: usize,
}
#[repr(C)]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy)]
pub struct moq_route {
pub hops: *const u64,
pub hops_len: usize,
pub cost: u64,
pub cold: u64,
pub has_cold: bool,
}
impl Default for moq_route {
fn default() -> Self {
Self {
hops: std::ptr::null(),
hops_len: 0,
cost: 0,
cold: 0,
has_cold: false,
}
}
}
unsafe fn parse_route(route: *const moq_route) -> Result<moq_net::origin::Route, Error> {
let Some(route) = (unsafe { route.as_ref() }) else {
return Ok(moq_net::origin::Route::default());
};
let cold = if route.has_cold { route.cold } else { route.cost };
let mut route_hops = moq_net::Hops::new();
if route.hops_len > 0 {
if route.hops.is_null() {
return Err(Error::InvalidPointer);
}
let hops = unsafe { std::slice::from_raw_parts(route.hops, route.hops_len) };
for id in hops {
let hop = if *id == 0 {
moq_net::Hop::UNKNOWN
} else {
moq_net::Hop::new(*id).map_err(|e| Error::InvalidConfig(e.to_string()))?
};
route_hops.push(hop).map_err(|e| Error::InvalidConfig(e.to_string()))?;
}
}
Ok(moq_net::origin::Route::default()
.with_cost(moq_net::origin::Cost { warm: route.cost, cold })
.with_hops(route_hops))
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_announce_update {
pub prefix: *const c_char,
pub prefix_len: usize,
pub captures: *const moq_string,
pub captures_len: usize,
pub has_captures: bool,
pub active: bool,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_connection_snapshot {
pub stats: moq_connection_stats,
pub protocol: moq_string,
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_connection_stats {
pub rtt_us: u64,
pub rtt_valid: bool,
pub estimated_send_rate_bps: u64,
pub estimated_send_rate_valid: bool,
pub estimated_recv_rate_bps: u64,
pub estimated_recv_rate_valid: bool,
pub bytes_sent: u64,
pub bytes_sent_valid: bool,
pub bytes_received: u64,
pub bytes_received_valid: bool,
pub bytes_lost: u64,
pub bytes_lost_valid: bool,
pub packets_sent: u64,
pub packets_sent_valid: bool,
pub packets_received: u64,
pub packets_received_valid: bool,
pub packets_lost: u64,
pub packets_lost_valid: bool,
}
impl From<&moq_net::session::Stats> for moq_connection_stats {
fn from(stats: &moq_net::session::Stats) -> Self {
fn split(value: Option<u64>) -> (u64, bool) {
(value.unwrap_or(0), value.is_some())
}
let (rtt_us, rtt_valid) = split(stats.rtt.map(|d| d.as_micros() as u64));
let (estimated_send_rate_bps, estimated_send_rate_valid) =
split(stats.estimated_send_rate.map(moq_net::bandwidth::Rate::as_bps));
let (estimated_recv_rate_bps, estimated_recv_rate_valid) =
split(stats.estimated_recv_rate.map(moq_net::bandwidth::Rate::as_bps));
let (bytes_sent, bytes_sent_valid) = split(stats.bytes_sent);
let (bytes_received, bytes_received_valid) = split(stats.bytes_received);
let (bytes_lost, bytes_lost_valid) = split(stats.bytes_lost);
let (packets_sent, packets_sent_valid) = split(stats.packets_sent);
let (packets_received, packets_received_valid) = split(stats.packets_received);
let (packets_lost, packets_lost_valid) = split(stats.packets_lost);
Self {
rtt_us,
rtt_valid,
estimated_send_rate_bps,
estimated_send_rate_valid,
estimated_recv_rate_bps,
estimated_recv_rate_valid,
bytes_sent,
bytes_sent_valid,
bytes_received,
bytes_received_valid,
bytes_lost,
bytes_lost_valid,
packets_sent,
packets_sent_valid,
packets_received,
packets_received_valid,
packets_lost,
packets_lost_valid,
}
}
}
#[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_tokio::Log::default(),
level => moq_tokio::Log::new(Level::from_str(level)?),
}
.init()?;
Ok(())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_error() -> *const c_char {
ffi::last_error_ptr()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_error_protocol(out: *mut moq_protocol_error) -> i32 {
if out.is_null() {
return Error::InvalidPointer.code();
}
if ffi::last_protocol(unsafe { &mut *out }) {
0
} else {
Error::NotFound.code()
}
}
static VERSION_NAMES: std::sync::LazyLock<Vec<String>> =
std::sync::LazyLock::new(|| moq_net::Versions::all().iter().map(|v| v.to_string()).collect());
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_versions(dst: *mut moq_string, count: usize) -> i32 {
ffi::enter(move || {
if !dst.is_null() {
let dst = unsafe { std::slice::from_raw_parts_mut(dst, count) };
for (slot, name) in dst.iter_mut().zip(VERSION_NAMES.iter()) {
slot.data = name.as_ptr().cast::<c_char>();
slot.len = name.len();
}
} else if count != 0 {
return Err(Error::InvalidPointer);
}
Ok(VERSION_NAMES.len())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_qlog_supported() -> bool {
moq_tokio::qlog_supported()
}
fn micros(duration: std::time::Duration) -> u64 {
duration.as_micros().min(u64::MAX as u128) as u64
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_client_config {
pub versions: *const moq_string,
pub versions_len: usize,
pub bind: *const c_char,
pub bind_len: usize,
pub connect_timeout_us: u64,
pub has_connect_timeout: bool,
pub failover_delay_us: u64,
pub has_failover_delay: bool,
pub resolution_delay_us: u64,
pub has_resolution_delay: bool,
pub websocket_enabled: bool,
pub has_websocket_enabled: bool,
pub websocket_delay_us: u64,
pub has_websocket_delay: bool,
pub tls_disable_verify: bool,
pub tls_system_roots: bool,
pub has_tls_system_roots: bool,
pub tls_roots: *const moq_string,
pub tls_roots_len: usize,
pub tls_fingerprints: *const moq_string,
pub tls_fingerprints_len: usize,
pub tls_host_name: *const c_char,
pub tls_host_name_len: usize,
pub tls_cert: *const c_char,
pub tls_cert_len: usize,
pub tls_key: *const c_char,
pub tls_key_len: usize,
pub backoff_initial_us: u64,
pub has_backoff_initial: bool,
pub backoff_multiplier: u32,
pub has_backoff_multiplier: bool,
pub backoff_max_us: u64,
pub has_backoff_max: bool,
pub backoff_timeout_us: u64,
pub has_backoff_timeout: bool,
pub quic_max_streams: u64,
pub has_quic_max_streams: bool,
pub quic_idle_timeout_us: u64,
pub has_quic_idle_timeout: bool,
pub quic_keep_alive_us: u64,
pub has_quic_keep_alive: bool,
pub quic_gso: bool,
pub has_quic_gso: bool,
pub quic_mtu_discovery: bool,
pub has_quic_mtu_discovery: bool,
pub quic_congestion_control: *const c_char,
pub quic_congestion_control_len: usize,
pub quic_qlog: *const c_char,
pub quic_qlog_len: usize,
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_client_defaults() -> moq_client_config {
let mut dst: moq_client_config = unsafe { std::mem::zeroed() };
let filled = std::panic::catch_unwind(|| {
let mut dst: moq_client_config = unsafe { std::mem::zeroed() };
let config = crate::client::Config::default();
let connect = config.connect.resolve();
dst.connect_timeout_us = micros(connect.timeout);
dst.has_connect_timeout = true;
dst.failover_delay_us = micros(connect.race);
dst.has_failover_delay = true;
dst.resolution_delay_us = micros(connect.resolution_delay);
dst.has_resolution_delay = true;
let websocket = config.connect.websocket.resolve();
dst.websocket_enabled = websocket.enabled;
dst.has_websocket_enabled = true;
dst.websocket_delay_us = micros(websocket.delay);
dst.has_websocket_delay = true;
dst.backoff_initial_us = micros(config.connect.backoff.initial);
dst.has_backoff_initial = true;
dst.backoff_multiplier = config.connect.backoff.multiplier;
dst.has_backoff_multiplier = true;
dst.backoff_max_us = micros(config.connect.backoff.max);
dst.has_backoff_max = true;
dst.backoff_timeout_us = micros(config.connect.backoff.timeout);
dst.has_backoff_timeout = true;
let quic = config.quic.resolve();
dst.quic_max_streams = quic.max_streams;
dst.has_quic_max_streams = true;
dst.quic_idle_timeout_us = micros(quic.idle_timeout);
dst.has_quic_idle_timeout = true;
if let Some(keep_alive) = quic.keep_alive {
dst.quic_keep_alive_us = micros(keep_alive);
dst.has_quic_keep_alive = true;
}
dst
});
if let Ok(value) = filled {
dst = value;
}
dst
}
unsafe fn connect_session(
url: *const c_char,
url_len: usize,
config: *const moq_client_config,
origin_publish: u32,
origin_consume: u32,
on_status: ffi::moq_status_callback,
user_data: *mut c_void,
) -> Result<crate::Id, Error> {
let url = ffi::parse_url(url, url_len)?;
let origin_publish = ffi::parse_id_optional(origin_publish)?;
let origin_consume = ffi::parse_id_optional(origin_consume)?;
let config = unsafe { crate::parse_client(config.as_ref())? };
let (publish, consume) = {
let state = State::lock();
let publish = origin_publish.map(|id| state.origin.get(id)).transpose()?.cloned();
let consume = origin_consume.map(|id| state.origin.get(id)).transpose()?.cloned();
(publish, consume)
};
let callback = unsafe { ffi::OnStatus::new(user_data, on_status)? };
let request = Connect {
config,
url,
publish,
consume,
callback,
}
.prepare()?;
State::lock().session.connect(request)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_session_connect(
url: *const c_char,
url_len: usize,
config: *const moq_client_config,
origin_publish: u32,
origin_consume: u32,
on_status: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || unsafe {
connect_session(
url,
url_len,
config,
origin_publish,
origin_consume,
on_status,
user_data,
)
})
}
#[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 unsafe extern "C" fn moq_session_stats(session: u32, dst: *mut moq_connection_stats) -> i32 {
ffi::enter(move || {
let session = ffi::parse_id(session)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
let stats = State::lock().session.stats(session)?;
*dst = moq_connection_stats::from(&stats);
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_session_snapshot(session: u32, dst: *mut moq_connection_snapshot) -> i32 {
ffi::enter(move || {
let session = ffi::parse_id(session)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
let (stats, version) = State::lock().session.snapshot(session)?;
let name = version.as_str();
*dst = moq_connection_snapshot {
stats: moq_connection_stats::from(&stats),
protocol: moq_string {
data: name.as_ptr().cast::<c_char>(),
len: name.len(),
},
};
Ok(())
})
}
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct moq_server_config {
pub bind: *const c_char,
pub bind_len: usize,
pub tls_cert: *const moq_string,
pub tls_cert_len: usize,
pub tls_key: *const moq_string,
pub tls_key_len: usize,
pub tls_generate: *const moq_string,
pub tls_generate_len: usize,
}
unsafe fn parse_server(config: &moq_server_config) -> Result<moq_tokio::Server, Error> {
let mut listen = moq_tokio::listen::Config::default();
if let Some(bind) = unsafe { ffi::parse_str_optional(config.bind, config.bind_len)? } {
let bind = moq_tokio::listen::Bind::from_str(bind)
.map_err(|_| Error::InvalidConfig(format!("invalid bind address: {bind}")))?;
listen.bind = Some(bind);
}
listen.tls.cert = unsafe { ffi::parse_strings(config.tls_cert, config.tls_cert_len)? }
.into_iter()
.map(Into::into)
.collect();
listen.tls.key = unsafe { ffi::parse_strings(config.tls_key, config.tls_key_len)? }
.into_iter()
.map(Into::into)
.collect();
listen.tls.generate = unsafe { ffi::parse_strings(config.tls_generate, config.tls_generate_len)? };
listen
.init(Default::default())
.map_err(|err| Error::InvalidConfig(err.to_string()))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_server_listen(
config: *const moq_server_config,
on_request: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let on_request = unsafe { ffi::OnStatus::new(user_data, on_request)? };
let server = unsafe { parse_server(config)? };
State::lock().server.listen(server, on_request)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_server_addr(server: u32, dst: *mut moq_string) -> i32 {
ffi::enter(move || {
let server = ffi::parse_id(server)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().server.addr(server, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_server_fingerprints(server: u32, dst: *mut moq_string, count: usize) -> i32 {
ffi::enter(move || {
let server = ffi::parse_id(server)?;
let dst = if count == 0 {
&mut [][..]
} else {
if dst.is_null() {
return Err(Error::InvalidPointer);
}
unsafe { std::slice::from_raw_parts_mut(dst, count) }
};
State::lock().server.fingerprints(server, dst)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_server_close(server: u32) -> i32 {
ffi::enter(move || {
let server = ffi::parse_id(server)?;
State::lock().server.close(server)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_session_request_path(request: u32, dst: *mut moq_string) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().server.request_path(request, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_session_request_query(request: u32, dst: *mut moq_string) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().server.request_query(request, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_session_request_accept(
request: u32,
origin_publish: u32,
origin_consume: u32,
on_status: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let origin_publish = ffi::parse_id_optional(origin_publish)?;
let origin_consume = ffi::parse_id_optional(origin_consume)?;
let callback = unsafe { ffi::OnStatus::new(user_data, on_status)? };
let mut state = State::lock();
let publish = origin_publish.map(|id| state.origin.get(id)).transpose()?.cloned();
let consume = origin_consume.map(|id| state.origin.get(id)).transpose()?.cloned();
let request = state.server.request_take(request)?;
state.session.accept(request, publish, consume, callback)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_session_request_reject(request: u32, code: u16) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let request = State::lock().server.request_take(request)?;
let reject = match code {
401 => moq_tokio::server::Reject::Unauthorized,
403 => moq_tokio::server::Reject::Forbidden,
code => moq_tokio::server::Reject::App(code),
};
Ok::<_, Error>(pollster::block_on(request.reject(reject))?)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_session_request_free(request: u32) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
State::lock().server.request_take(request)?;
Ok(())
})
}
#[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_create_broadcast(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.create_broadcast(origin, path)?;
state.publish.create(broadcast)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_dynamic(
origin: u32,
prefix: *const c_char,
prefix_len: usize,
route: *const moq_route,
on_request: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let origin = ffi::parse_id(origin)?;
let prefix = unsafe { ffi::parse_str(prefix, prefix_len)? };
let route = unsafe { parse_route(route)? };
let on_request = unsafe { ffi::OnStatus::new(user_data, on_request)? };
State::lock().origin.dynamic(origin, prefix, route, on_request)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_dynamic_update(dynamic: u32, route: *const moq_route) -> i32 {
ffi::enter(move || {
let dynamic = ffi::parse_id(dynamic)?;
let route = unsafe { parse_route(route)? };
State::lock().origin.dynamic_update(dynamic, route)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_origin_dynamic_cancel(dynamic: u32) -> i32 {
ffi::enter(move || {
let dynamic = ffi::parse_id(dynamic)?;
State::lock().origin.dynamic_close(dynamic)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_broadcast_request_path(request: u32, dst: *mut moq_string) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().origin.broadcast_request_path(request, dst)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_broadcast_request_accept(request: u32, broadcast: u32) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let broadcast = ffi::parse_id(broadcast)?;
let mut state = State::lock();
let pending = state.origin.broadcast_request_take(request)?;
let consumer = state.publish.producer(broadcast)?.consume();
pending.accept(&consumer);
Ok(())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_broadcast_request_reject(request: u32, error_code: u16) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let pending = State::lock().origin.broadcast_request_take(request)?;
pending.reject(moq_net::Error::App(error_code));
Ok(())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_broadcast_request_free(request: u32) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
State::lock().origin.broadcast_request_take(request)?;
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_announced(
origin: u32,
prefix: *const c_char,
prefix_len: usize,
filter: *const c_char,
filter_len: usize,
on_announce: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let origin = ffi::parse_id(origin)?;
let prefix = unsafe { ffi::parse_str(prefix, prefix_len)? }.to_string();
let filter = if filter.is_null() {
None
} else {
Some(unsafe { ffi::parse_str(filter, filter_len)? }.to_string())
};
let on_announce = unsafe { ffi::OnStatus::new(user_data, on_announce)? };
State::lock().origin.announced(origin, prefix, filter, on_announce)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_announced_info(announced: u32, dst: *mut moq_announce_update) -> 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_free(announced: u32) -> i32 {
ffi::enter(move || {
let announced = ffi::parse_id(announced)?;
State::lock().origin.announced_free(announced)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_origin_announced_cancel(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_announced_broadcast(
origin: u32,
path: *const c_char,
path_len: usize,
on_broadcast: ffi::moq_status_callback,
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_announced_broadcast_cancel(task: u32) -> i32 {
ffi::enter(move || {
let task = ffi::parse_id(task)?;
State::lock().origin.consume_announced_close(task)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_origin_request(
origin: u32,
path: *const c_char,
path_len: usize,
on_broadcast: ffi::moq_status_callback,
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.request(origin, path, on_broadcast)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_origin_request_cancel(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 unsafe extern "C" fn moq_publish_announce(broadcast: u32, route: *const moq_route) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let route = unsafe { parse_route(route)? };
State::lock().publish.announce(broadcast, route)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_unannounce(broadcast: u32) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
State::lock().publish.unannounce(broadcast)
})
}
#[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 extern "C" fn moq_publish_finish(broadcast: u32) -> i32 {
moq_publish_close(broadcast)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_audio(broadcast: u32, config: *const moq_audio_init) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let audio = unsafe { parse_audio_init(config)? };
State::lock().publish.audio(broadcast, audio)
})
}
unsafe fn parse_audio_init(config: *const moq_audio_init) -> Result<moq_mux::import::AudioInit, Error> {
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let init = unsafe { ffi::parse_slice(config.init, config.init_len)? };
let label = unsafe { ffi::parse_str_optional(config.label, config.label_len)? };
let mut audio = moq_mux::import::AudioInit::new(audio_format_from_u32(config.format)?, init.to_vec());
audio.label = label.map(str::to_string);
Ok(audio)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_video(broadcast: u32, config: *const moq_video_init) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let video = unsafe { parse_video_init(config)? };
State::lock().publish.video(broadcast, video)
})
}
unsafe fn parse_video_init(config: *const moq_video_init) -> Result<moq_mux::import::VideoInit, Error> {
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let init = unsafe { ffi::parse_slice(config.init, config.init_len)? };
let label = unsafe { ffi::parse_str_optional(config.label, config.label_len)? };
let mut video = moq_mux::import::VideoInit::new(video_format_from_u32(config.format)?, init.to_vec());
video.label = label.map(str::to_string);
video.hint = config.hint.resolve();
Ok(video)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_container(broadcast: u32, config: *const moq_container_init) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let init = unsafe { ffi::parse_slice(config.init, config.init_len)? };
let container = moq_mux::import::ContainerInit::new(container_format_from_u32(config.format)?, init.to_vec());
State::lock().publish.container(broadcast, container)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_media_cut(media: u32) -> i32 {
ffi::enter(move || {
let media = ffi::parse_id(media)?;
State::lock().publish.media_cut(media)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_media_seek(media: u32, sequence: u64) -> i32 {
ffi::enter(move || {
let media = ffi::parse_id(media)?;
State::lock().publish.media_seek(media, sequence)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_media_finish(export: u32) -> i32 {
ffi::enter(move || {
let export = ffi::parse_id(export)?;
State::lock().publish.media_finish(export)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_media_demand(
media: u32,
on_demand: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let media = ffi::parse_id(media)?;
let on_demand = unsafe { ffi::OnStatus::new(user_data, on_demand)? };
let mut state = State::lock();
let demand = state.publish.media_demand(media)?;
state.publish.demand(demand, on_demand)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_demand_cancel(watcher: u32) -> i32 {
ffi::enter(move || {
let watcher = ffi::parse_id(watcher)?;
State::lock().publish.demand_close(watcher)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_container_write(container: u32, payload: *const u8, payload_size: usize) -> i32 {
ffi::enter(move || {
let container = ffi::parse_id(container)?;
let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
State::lock().publish.container_write(container, payload)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_container_cut(container: u32) -> i32 {
ffi::enter(move || {
let container = ffi::parse_id(container)?;
State::lock().publish.container_cut(container)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_container_seek(container: u32, sequence: u64) -> i32 {
ffi::enter(move || {
let container = ffi::parse_id(container)?;
State::lock().publish.container_seek(container, sequence)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_container_finish(container: u32) -> i32 {
ffi::enter(move || {
let container = ffi::parse_id(container)?;
State::lock().publish.container_finish(container)
})
}
#[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 extern "C" fn moq_publish_media_flush(media: u32, timestamp_us: u64) -> i32 {
ffi::enter(move || {
let media = ffi::parse_id(media)?;
let timestamp = hang::container::Timestamp::from_micros(timestamp_us)?;
State::lock().publish.media_flush(media, timestamp)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_media_discontinuity(media: u32) -> i32 {
ffi::enter(move || {
let media = ffi::parse_id(media)?;
State::lock().publish.media_discontinuity(media)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_video_properties(broadcast: u32, properties: *const moq_video_properties) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let properties = unsafe { properties.as_ref() }.ok_or(Error::InvalidPointer)?;
let mut value = hang::catalog::VideoProperties::default();
value.display = properties.has_display.then_some(hang::catalog::Display {
width: properties.display_width,
height: properties.display_height,
});
value.rotation = properties.has_rotation.then_some(properties.rotation);
value.flip = properties.has_flip.then_some(properties.flip);
State::lock().publish.video_properties(broadcast, value)
})
}
#[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 label = unsafe { ffi::parse_str_optional(config.label, config.label_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);
video.label = label.map(str::to_string);
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 = (config.coded_width > 0).then_some(config.coded_width);
video.coded_height = (config.coded_height > 0).then_some(config.coded_height);
video.container = unsafe { parse_container(&config.container)? };
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 label = unsafe { ffi::parse_str_optional(config.label, config.label_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);
audio.label = label.map(str::to_string);
audio.container = unsafe { parse_container(&config.container)? };
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_catalog_section(
broadcast: u32,
name: *const c_char,
name_len: usize,
json: *const c_char,
json_len: usize,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let json = unsafe { ffi::parse_str(json, json_len)? };
let value: serde_json::Value = serde_json::from_str(json)?;
State::lock().publish.catalog_section_set(broadcast, name, value)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_catalog_section_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.catalog_section_remove(broadcast, name)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_track(
broadcast: u32,
name: *const c_char,
name_len: usize,
info: *const moq_track_info,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let info = unsafe { parse_track_info(info)? };
State::lock().publish.track(broadcast, name, Some(info))
})
}
unsafe fn parse_track_info(info: *const moq_track_info) -> Result<moq_net::track::Info, Error> {
match unsafe { info.as_ref() } {
Some(info) => moq_net::track::Info::try_from(info),
None => Ok(moq_net::track::Info::default().with_timescale(moq_net::Timescale::MICRO)),
}
}
#[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 extern "C" fn moq_publish_track_group_at(track: u32, sequence: u64) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().publish.track_group_at(track, sequence)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_track_frame(
track: u32,
payload: *const u8,
payload_size: usize,
timestamp_us: u64,
) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
let timestamp = moq_net::Timestamp::from_micros(timestamp_us)?;
State::lock().publish.track_frame(track, timestamp, payload)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_track_datagram(
track: u32,
payload: *const u8,
payload_size: usize,
timestamp_us: u64,
out_sequence: *mut u64,
) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
let sequence = State::lock().publish.track_datagram(track, timestamp_us, payload)?;
if let Some(out) = unsafe { out_sequence.as_mut() } {
*out = sequence;
}
Ok(())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_track_finish(track: u32) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().publish.track_finish(track)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_track_finish_at(track: u32, final_sequence: u64) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().publish.track_finish_at(track, final_sequence)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_track_abort(track: u32, error_code: u16) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().publish.track_abort(track, error_code)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_track_demand(
track: u32,
on_demand: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
let on_demand = unsafe { ffi::OnStatus::new(user_data, on_demand)? };
let mut state = State::lock();
let demand = state.publish.track_demand(track)?;
state.publish.demand(demand, on_demand)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_dynamic(
broadcast: u32,
on_request: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let on_request = unsafe { ffi::OnStatus::new(user_data, on_request)? };
State::lock().publish.dynamic(broadcast, on_request)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_track_dynamic(
track: u32,
on_group: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
let on_group = unsafe { ffi::OnStatus::new(user_data, on_group)? };
State::lock().publish.track_dynamic(track, on_group)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_dynamic_cancel(dynamic: u32) -> i32 {
ffi::enter(move || {
let dynamic = ffi::parse_id(dynamic)?;
State::lock().publish.dynamic_close(dynamic)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_track_request_name(request: u32, dst: *mut moq_string) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().publish.track_request_name(request, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_track_request_dynamic(
request: u32,
on_group: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let on_group = unsafe { ffi::OnStatus::new(user_data, on_group)? };
State::lock().publish.track_request_dynamic(request, on_group)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_track_request_accept(request: u32, info: *const moq_track_info) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let info = unsafe { parse_track_info(info)? };
State::lock().publish.track_request_accept(request, info)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_track_request_audio(request: u32, config: *const moq_audio_init) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let audio = unsafe { parse_audio_init(config)? };
State::lock().publish.track_request_audio(request, audio)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_track_request_video(request: u32, config: *const moq_video_init) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let video = unsafe { parse_video_init(config)? };
State::lock().publish.track_request_video(request, video)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_track_request_abort(request: u32, error_code: u16) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
State::lock().publish.track_request_abort(request, error_code)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_track_request_free(request: u32) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
State::lock().publish.track_request_free(request)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_group_request_sequence(request: u32, dst: *mut u64) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
*dst = State::lock().publish.group_request_info(request)?.0;
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_group_request_priority(request: u32, dst: *mut u8) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
*dst = State::lock().publish.group_request_info(request)?.1;
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_group_request_frame_start(request: u32, dst: *mut u64) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
*dst = State::lock().publish.group_request_info(request)?.2;
Ok(())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_group_request_accept(request: u32) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
State::lock().publish.group_request_accept(request)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_group_request_abort(request: u32, error_code: u16) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
State::lock().publish.group_request_abort(request, error_code)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_group_request_free(request: u32) -> i32 {
ffi::enter(move || {
let request = ffi::parse_id(request)?;
State::lock().publish.group_request_free(request)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_group_frame(
group: u32,
payload: *const u8,
payload_size: usize,
timestamp_us: u64,
) -> i32 {
ffi::enter(move || {
let group = ffi::parse_id(group)?;
let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
let timestamp = moq_net::Timestamp::from_micros(timestamp_us)?;
State::lock().publish.group_frame(group, timestamp, payload)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_group_finish(group: u32) -> i32 {
ffi::enter(move || {
let group = ffi::parse_id(group)?;
State::lock().publish.group_finish(group)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_group_abort(group: u32, error_code: u16) -> i32 {
ffi::enter(move || {
let group = ffi::parse_id(group)?;
State::lock().publish.group_abort(group, error_code)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_json_snapshot(
broadcast: u32,
name: *const c_char,
name_len: usize,
config: *const moq_json_snapshot_config,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let producer = moq_mux::json::Config::default()
.with_compression(config.compression)
.with_delta_ratio(config.delta_ratio);
State::lock().publish.json_snapshot(broadcast, name, producer)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_json_snapshot_update(json: u32, value: *const c_char, value_len: usize) -> i32 {
ffi::enter(move || {
let json = ffi::parse_id(json)?;
let value = unsafe { ffi::parse_slice(value.cast::<u8>(), value_len)? };
let value = serde_json::from_slice(value)?;
State::lock().publish.json_snapshot_update(json, value)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_json_snapshot_finish(json: u32) -> i32 {
ffi::enter(move || {
let json = ffi::parse_id(json)?;
State::lock().publish.json_snapshot_finish(json)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_json_stream(
broadcast: u32,
name: *const c_char,
name_len: usize,
config: *const moq_json_stream_config,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let producer = moq_mux::json::Config::default().with_compression(config.compression);
State::lock().publish.json_stream(broadcast, name, producer)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_json_stream_append(stream: u32, value: *const c_char, value_len: usize) -> i32 {
ffi::enter(move || {
let stream = ffi::parse_id(stream)?;
let value = unsafe { ffi::parse_slice(value.cast::<u8>(), value_len)? };
let value = serde_json::from_slice(value)?;
State::lock().publish.json_stream_append(stream, value)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_json_stream_finish(stream: u32) -> i32 {
ffi::enter(move || {
let stream = ffi::parse_id(stream)?;
State::lock().publish.json_stream_finish(stream)
})
}
unsafe fn binary_config(config: *const moq_binary_config) -> Result<moq_mux::binary::Config, Error> {
let config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let mut binary = moq_mux::binary::Config::default().with_compression(config.compression);
if let Some(mime) = unsafe { ffi::parse_str_optional(config.mime, config.mime_len)? } {
binary = binary.with_mime(mime);
}
Ok(binary)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_binary_snapshot(
broadcast: u32,
name: *const c_char,
name_len: usize,
config: *const moq_binary_config,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let config = unsafe { binary_config(config)? };
State::lock().publish.binary_snapshot(broadcast, name, config)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_binary_snapshot_update(
binary: u32,
payload: *const u8,
payload_len: usize,
) -> i32 {
ffi::enter(move || {
let binary = ffi::parse_id(binary)?;
let payload = unsafe { ffi::parse_slice(payload, payload_len)? };
State::lock().publish.binary_snapshot_update(binary, payload)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_binary_snapshot_finish(binary: u32) -> i32 {
ffi::enter(move || {
let binary = ffi::parse_id(binary)?;
State::lock().publish.binary_snapshot_finish(binary)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_binary_stream(
broadcast: u32,
name: *const c_char,
name_len: usize,
config: *const moq_binary_config,
) -> i32 {
ffi::enter(move || {
let broadcast = ffi::parse_id(broadcast)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let config = unsafe { binary_config(config)? };
State::lock().publish.binary_stream(broadcast, name, config)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_publish_binary_stream_append(stream: u32, payload: *const u8, payload_len: usize) -> i32 {
ffi::enter(move || {
let stream = ffi::parse_id(stream)?;
let payload = unsafe { ffi::parse_slice(payload, payload_len)? };
State::lock().publish.binary_stream_append(stream, payload)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_publish_binary_stream_finish(stream: u32) -> i32 {
ffi::enter(move || {
let stream = ffi::parse_id(stream)?;
State::lock().publish.binary_stream_finish(stream)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_catalog(
broadcast: u32,
on_catalog: ffi::moq_status_callback,
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_cancel(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_video_stalled(catalog: u32, index: u32, dst: *mut bool) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
if dst.is_null() {
return Err(Error::InvalidPointer);
}
let stalled = State::lock().consume.video_stalled(catalog, index as usize)?;
unsafe { dst.write(stalled) };
Ok(())
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_video_properties(catalog: u32, dst: *mut moq_video_properties) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.video_properties(catalog, 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 extern "C" fn moq_consume_catalog_section_count(catalog: u32) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
State::lock().consume.catalog_section_count(catalog)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_catalog_section_at(catalog: u32, index: u32, dst: *mut moq_section) -> 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.catalog_section_at(catalog, index, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_catalog_section(
catalog: u32,
name: *const c_char,
name_len: usize,
dst: *mut moq_string,
) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let name = unsafe { ffi::parse_str(name, name_len)? };
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.catalog_section_get(catalog, name, dst)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_video(
catalog: u32,
index: u32,
max_age_us: u64,
on_frame: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let index = index as usize;
let max_age = std::time::Duration::from_micros(max_age_us);
let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame)? };
State::lock().consume.video(catalog, index, max_age, on_frame)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_video_cancel(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(
catalog: u32,
index: u32,
max_age_us: u64,
on_frame: ffi::moq_status_callback,
user_data: *mut c_void,
) -> i32 {
ffi::enter(move || {
let catalog = ffi::parse_id(catalog)?;
let index = index as usize;
let max_age = std::time::Duration::from_micros(max_age_us);
let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame)? };
State::lock().consume.audio(catalog, index, max_age, on_frame)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_audio_cancel(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_free(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,
subscription: *const moq_subscription,
on_frame: ffi::moq_status_callback,
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 subscription = unsafe { subscription.as_ref() }.map(moq_net::track::Subscription::from);
let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame)? };
State::lock().consume.raw_track(broadcast, name, subscription, on_frame)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_track_update(track: u32, subscription: *const moq_subscription) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
let subscription = unsafe { subscription.as_ref() }.map(moq_net::track::Subscription::from);
State::lock().consume.raw_track_update(track, subscription)
})
}
#[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_free(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_cancel(track: u32) -> i32 {
ffi::enter(move || {
let track = ffi::parse_id(track)?;
State::lock().consume.raw_track_close(track)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_datagrams(
broadcast: u32,
name: *const c_char,
name_len: usize,
on_datagram: ffi::moq_status_callback,
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_datagram = unsafe { ffi::OnStatus::new(user_data, on_datagram)? };
State::lock().consume.datagram_track(broadcast, name, on_datagram)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_datagram(datagram: u32, dst: *mut moq_datagram) -> i32 {
ffi::enter(move || {
let datagram = ffi::parse_id(datagram)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.datagram(datagram, dst)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_datagram_free(datagram: u32) -> i32 {
ffi::enter(move || {
let datagram = ffi::parse_id(datagram)?;
State::lock().consume.datagram_close(datagram)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_datagrams_cancel(task: u32) -> i32 {
ffi::enter(move || {
let task = ffi::parse_id(task)?;
State::lock().consume.datagram_track_close(task)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_json_snapshot(
broadcast: u32,
name: *const c_char,
name_len: usize,
config: *const moq_json_snapshot_config,
on_value: ffi::moq_status_callback,
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 config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let mut consumer = moq_json::snapshot::consumer::Config::default();
consumer.compression = if config.compression {
moq_json::Compression::Deflate
} else {
moq_json::Compression::None
};
let on_value = unsafe { ffi::OnStatus::new(user_data, on_value)? };
State::lock().consume.json_snapshot(broadcast, name, consumer, on_value)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_json_stream(
broadcast: u32,
name: *const c_char,
name_len: usize,
config: *const moq_json_stream_config,
on_value: ffi::moq_status_callback,
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 config = unsafe { config.as_ref() }.ok_or(Error::InvalidPointer)?;
let mut consumer = moq_json::stream::Config::default();
if config.compression {
consumer.compression = moq_json::Compression::Deflate;
}
let on_value = unsafe { ffi::OnStatus::new(user_data, on_value)? };
State::lock().consume.json_stream(broadcast, name, consumer, on_value)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn moq_consume_json_value(value: u32, dst: *mut moq_json_value) -> i32 {
ffi::enter(move || {
let value = ffi::parse_id(value)?;
let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
State::lock().consume.json_value(value, dst)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_json_value_free(value: u32) -> i32 {
ffi::enter(move || {
let value = ffi::parse_id(value)?;
State::lock().consume.json_value_close(value)
})
}
#[unsafe(no_mangle)]
pub extern "C" fn moq_consume_json_cancel(task: u32) -> i32 {
ffi::enter(move || {
let task = ffi::parse_id(task)?;
State::lock().consume.json_close(task)
})
}