1use crate::{Error, State, ffi};
2
3use std::ffi::c_char;
4use std::ffi::c_void;
5use std::str::FromStr;
6
7use tracing::Level;
8
9#[repr(C)]
11#[allow(non_camel_case_types)]
12pub struct moq_video_config {
13 pub name: *const c_char,
15 pub name_len: usize,
16
17 pub codec: *const c_char,
19 pub codec_len: usize,
20
21 pub description: *const u8,
26 pub description_len: usize,
27
28 pub coded_width: *const u32,
30 pub coded_height: *const u32,
31}
32
33#[repr(C)]
35#[allow(non_camel_case_types)]
36pub struct moq_audio_config {
37 pub name: *const c_char,
39 pub name_len: usize,
40
41 pub codec: *const c_char,
43 pub codec_len: usize,
44
45 pub description: *const u8,
47 pub description_len: usize,
48
49 pub sample_rate: u32,
51
52 pub channel_count: u32,
54}
55
56#[repr(C)]
58#[allow(non_camel_case_types)]
59pub struct moq_frame {
60 pub payload: *const u8,
62 pub payload_size: usize,
63
64 pub timestamp_us: u64,
66
67 pub keyframe: bool,
69}
70
71#[repr(C)]
73#[allow(non_camel_case_types)]
74pub struct moq_announced {
75 pub path: *const c_char,
77 pub path_len: usize,
78
79 pub active: bool,
82}
83
84#[unsafe(no_mangle)]
94pub unsafe extern "C" fn moq_log_level(level: *const c_char, level_len: usize) -> i32 {
95 ffi::enter(move || {
96 match unsafe { ffi::parse_str(level, level_len)? } {
97 "" => moq_native::Log::default(),
98 level => moq_native::Log::new(Level::from_str(level)?),
99 }
100 .init();
101
102 Ok(())
103 })
104}
105
106#[unsafe(no_mangle)]
125pub unsafe extern "C" fn moq_session_connect(
126 url: *const c_char,
127 url_len: usize,
128 origin_publish: u32,
129 origin_consume: u32,
130 on_status: Option<extern "C" fn(user_data: *mut c_void, code: i32)>,
131 user_data: *mut c_void,
132) -> i32 {
133 ffi::enter(move || {
134 let url = ffi::parse_url(url, url_len)?;
135
136 let mut state = State::lock();
137 let publish = ffi::parse_id_optional(origin_publish)?
138 .map(|id| state.origin.get(id))
139 .transpose()?
140 .map(|origin: &moq_lite::OriginProducer| origin.consume());
141 let consume = ffi::parse_id_optional(origin_consume)?
142 .map(|id| state.origin.get(id))
143 .transpose()?
144 .cloned();
145
146 let on_status = unsafe { ffi::OnStatus::new(user_data, on_status) };
147 state.session.connect(url, publish, consume, on_status)
148 })
149}
150
151#[unsafe(no_mangle)]
157pub extern "C" fn moq_session_close(session: u32) -> i32 {
158 ffi::enter(move || {
159 let session = ffi::parse_id(session)?;
160 State::lock().session.close(session)
161 })
162}
163
164#[unsafe(no_mangle)]
174pub extern "C" fn moq_origin_create() -> i32 {
175 ffi::enter(move || State::lock().origin.create())
176}
177
178#[unsafe(no_mangle)]
187pub unsafe extern "C" fn moq_origin_publish(origin: u32, path: *const c_char, path_len: usize, broadcast: u32) -> i32 {
188 ffi::enter(move || {
189 let origin = ffi::parse_id(origin)?;
190 let path = unsafe { ffi::parse_str(path, path_len)? };
191 let broadcast = ffi::parse_id(broadcast)?;
192
193 let mut state = State::lock();
194 let broadcast = state.publish.get(broadcast)?.consume();
195 state.origin.publish(origin, path, broadcast)
196 })
197}
198
199#[unsafe(no_mangle)]
211pub unsafe extern "C" fn moq_origin_announced(
212 origin: u32,
213 on_announce: Option<extern "C" fn(user_data: *mut c_void, announced: i32)>,
214 user_data: *mut c_void,
215) -> i32 {
216 ffi::enter(move || {
217 let origin = ffi::parse_id(origin)?;
218 let on_announce = unsafe { ffi::OnStatus::new(user_data, on_announce) };
219 State::lock().origin.announced(origin, on_announce)
220 })
221}
222
223#[unsafe(no_mangle)]
232pub unsafe extern "C" fn moq_origin_announced_info(announced: u32, dst: *mut moq_announced) -> i32 {
233 ffi::enter(move || {
234 let announced = ffi::parse_id(announced)?;
235 let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
236 State::lock().origin.announced_info(announced, dst)
237 })
238}
239
240#[unsafe(no_mangle)]
244pub extern "C" fn moq_origin_announced_close(announced: u32) -> i32 {
245 ffi::enter(move || {
246 let announced = ffi::parse_id(announced)?;
247 State::lock().origin.announced_close(announced)
248 })
249}
250
251#[unsafe(no_mangle)]
258pub unsafe extern "C" fn moq_origin_consume(origin: u32, path: *const c_char, path_len: usize) -> i32 {
259 ffi::enter(move || {
260 let origin = ffi::parse_id(origin)?;
261 let path = unsafe { ffi::parse_str(path, path_len)? };
262
263 let mut state = State::lock();
264 let broadcast = state.origin.consume(origin, path)?;
265 Ok(state.consume.start(broadcast.into()))
266 })
267}
268
269#[unsafe(no_mangle)]
273pub extern "C" fn moq_origin_close(origin: u32) -> i32 {
274 ffi::enter(move || {
275 let origin = ffi::parse_id(origin)?;
276 State::lock().origin.close(origin)
277 })
278}
279
280#[unsafe(no_mangle)]
284pub extern "C" fn moq_publish_create() -> i32 {
285 ffi::enter(move || State::lock().publish.create())
286}
287
288#[unsafe(no_mangle)]
292pub extern "C" fn moq_publish_close(broadcast: u32) -> i32 {
293 ffi::enter(move || {
294 let broadcast = ffi::parse_id(broadcast)?;
295 State::lock().publish.close(broadcast)
296 })
297}
298
299#[unsafe(no_mangle)]
310pub unsafe extern "C" fn moq_publish_media_ordered(
311 broadcast: u32,
312 format: *const c_char,
313 format_len: usize,
314 init: *const u8,
315 init_size: usize,
316) -> i32 {
317 ffi::enter(move || {
318 let broadcast = ffi::parse_id(broadcast)?;
319 let format = unsafe { ffi::parse_str(format, format_len)? };
320 let init = unsafe { ffi::parse_slice(init, init_size)? };
321
322 State::lock().publish.media_ordered(broadcast, format, init)
323 })
324}
325
326#[unsafe(no_mangle)]
330pub extern "C" fn moq_publish_media_close(export: u32) -> i32 {
331 ffi::enter(move || {
332 let export = ffi::parse_id(export)?;
333 State::lock().publish.media_close(export)
334 })
335}
336
337#[unsafe(no_mangle)]
347pub unsafe extern "C" fn moq_publish_media_frame(
348 media: u32,
349 payload: *const u8,
350 payload_size: usize,
351 timestamp_us: u64,
352) -> i32 {
353 ffi::enter(move || {
354 let media = ffi::parse_id(media)?;
355 let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
356 let timestamp = hang::Timestamp::from_micros(timestamp_us)?;
357 State::lock().publish.media_frame(media, payload, timestamp)
358 })
359}
360
361#[unsafe(no_mangle)]
371pub unsafe extern "C" fn moq_consume_catalog(
372 broadcast: u32,
373 on_catalog: Option<extern "C" fn(user_data: *mut c_void, catalog: i32)>,
374 user_data: *mut c_void,
375) -> i32 {
376 ffi::enter(move || {
377 let broadcast = ffi::parse_id(broadcast)?;
378 let on_catalog = unsafe { ffi::OnStatus::new(user_data, on_catalog) };
379 State::lock().consume.catalog(broadcast, on_catalog)
380 })
381}
382
383#[unsafe(no_mangle)]
387pub extern "C" fn moq_consume_catalog_close(catalog: u32) -> i32 {
388 ffi::enter(move || {
389 let catalog = ffi::parse_id(catalog)?;
390 State::lock().consume.catalog_close(catalog)
391 })
392}
393
394#[unsafe(no_mangle)]
404pub unsafe extern "C" fn moq_consume_video_config(catalog: u32, index: u32, dst: *mut moq_video_config) -> i32 {
405 ffi::enter(move || {
406 let catalog = ffi::parse_id(catalog)?;
407 let index = index as usize;
408 let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
409 State::lock().consume.video_config(catalog, index, dst)
410 })
411}
412
413#[unsafe(no_mangle)]
423pub unsafe extern "C" fn moq_consume_audio_config(catalog: u32, index: u32, dst: *mut moq_audio_config) -> i32 {
424 ffi::enter(move || {
425 let catalog = ffi::parse_id(catalog)?;
426 let index = index as usize;
427 let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
428 State::lock().consume.audio_config(catalog, index, dst)
429 })
430}
431
432#[unsafe(no_mangle)]
442pub unsafe extern "C" fn moq_consume_video_ordered(
443 broadcast: u32,
444 index: u32,
445 max_latency_ms: u64,
446 on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
447 user_data: *mut c_void,
448) -> i32 {
449 ffi::enter(move || {
450 let broadcast = ffi::parse_id(broadcast)?;
451 let index = index as usize;
452 let max_latency = std::time::Duration::from_millis(max_latency_ms);
453 let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame) };
454 State::lock()
455 .consume
456 .video_ordered(broadcast, index, max_latency, on_frame)
457 })
458}
459
460#[unsafe(no_mangle)]
464pub extern "C" fn moq_consume_video_close(track: u32) -> i32 {
465 ffi::enter(move || {
466 let track = ffi::parse_id(track)?;
467 State::lock().consume.video_close(track)
468 })
469}
470
471#[unsafe(no_mangle)]
481pub unsafe extern "C" fn moq_consume_audio_ordered(
482 broadcast: u32,
483 index: u32,
484 max_latency_ms: u64,
485 on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
486 user_data: *mut c_void,
487) -> i32 {
488 ffi::enter(move || {
489 let broadcast = ffi::parse_id(broadcast)?;
490 let index = index as usize;
491 let max_latency = std::time::Duration::from_millis(max_latency_ms);
492 let on_frame = unsafe { ffi::OnStatus::new(user_data, on_frame) };
493 State::lock()
494 .consume
495 .audio_ordered(broadcast, index, max_latency, on_frame)
496 })
497}
498
499#[unsafe(no_mangle)]
503pub extern "C" fn moq_consume_audio_close(track: u32) -> i32 {
504 ffi::enter(move || {
505 let track = ffi::parse_id(track)?;
506 State::lock().consume.audio_close(track)
507 })
508}
509
510#[unsafe(no_mangle)]
520pub unsafe extern "C" fn moq_consume_frame_chunk(frame: u32, index: u32, dst: *mut moq_frame) -> i32 {
521 ffi::enter(move || {
522 let frame = ffi::parse_id(frame)?;
523 let index = index as usize;
524 let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?;
525 State::lock().consume.frame_chunk(frame, index, dst)
526 })
527}
528
529#[unsafe(no_mangle)]
533pub extern "C" fn moq_consume_frame_close(frame: u32) -> i32 {
534 ffi::enter(move || {
535 let frame = ffi::parse_id(frame)?;
536 State::lock().consume.frame_close(frame)
537 })
538}
539
540#[unsafe(no_mangle)]
544pub extern "C" fn moq_consume_close(consume: u32) -> i32 {
545 ffi::enter(move || {
546 let consume = ffi::parse_id(consume)?;
547 State::lock().consume.close(consume)
548 })
549}