1use crate::{ffi, Error, State};
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#[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 {
99 level: Level::from_str(level)?,
100 },
101 }
102 .init();
103
104 Ok(())
105 })
106}
107
108#[no_mangle]
127pub unsafe extern "C" fn moq_session_connect(
128 url: *const c_char,
129 url_len: usize,
130 origin_publish: u32,
131 origin_consume: u32,
132 on_status: Option<extern "C" fn(user_data: *mut c_void, code: i32)>,
133 user_data: *mut c_void,
134) -> i32 {
135 ffi::enter(move || {
136 let url = ffi::parse_url(url, url_len)?;
137
138 let mut state = State::lock();
139 let publish = ffi::parse_id_optional(origin_publish)?
140 .map(|id| state.origin.get(id))
141 .transpose()?
142 .map(|origin: &moq_lite::OriginProducer| origin.consume());
143 let consume = ffi::parse_id_optional(origin_consume)?
144 .map(|id| state.origin.get(id))
145 .transpose()?
146 .cloned();
147
148 let on_status = ffi::OnStatus::new(user_data, on_status);
149 state.session.connect(url, publish, consume, on_status)
150 })
151}
152
153#[no_mangle]
159pub extern "C" fn moq_session_close(session: u32) -> i32 {
160 ffi::enter(move || {
161 let session = ffi::parse_id(session)?;
162 State::lock().session.close(session)
163 })
164}
165
166#[no_mangle]
176pub extern "C" fn moq_origin_create() -> i32 {
177 ffi::enter(move || State::lock().origin.create())
178}
179
180#[no_mangle]
189pub unsafe extern "C" fn moq_origin_publish(origin: u32, path: *const c_char, path_len: usize, broadcast: u32) -> i32 {
190 ffi::enter(move || {
191 let origin = ffi::parse_id(origin)?;
192 let path = unsafe { ffi::parse_str(path, path_len)? };
193 let broadcast = ffi::parse_id(broadcast)?;
194
195 let mut state = State::lock();
196 let broadcast = state.publish.get(broadcast)?.consume();
197 state.origin.publish(origin, path, broadcast)
198 })
199}
200
201#[no_mangle]
213pub unsafe extern "C" fn moq_origin_announced(
214 origin: u32,
215 on_announce: Option<extern "C" fn(user_data: *mut c_void, announced: i32)>,
216 user_data: *mut c_void,
217) -> i32 {
218 ffi::enter(move || {
219 let origin = ffi::parse_id(origin)?;
220 let on_announce = ffi::OnStatus::new(user_data, on_announce);
221 State::lock().origin.announced(origin, on_announce)
222 })
223}
224
225#[no_mangle]
234pub unsafe extern "C" fn moq_origin_announced_info(announced: u32, dst: *mut moq_announced) -> i32 {
235 ffi::enter(move || {
236 let announced = ffi::parse_id(announced)?;
237 let dst = dst.as_mut().ok_or(Error::InvalidPointer)?;
238 State::lock().origin.announced_info(announced, dst)
239 })
240}
241
242#[no_mangle]
246pub extern "C" fn moq_origin_announced_close(announced: u32) -> i32 {
247 ffi::enter(move || {
248 let announced = ffi::parse_id(announced)?;
249 State::lock().origin.announced_close(announced)
250 })
251}
252
253#[no_mangle]
260pub unsafe extern "C" fn moq_origin_consume(origin: u32, path: *const c_char, path_len: usize) -> i32 {
261 ffi::enter(move || {
262 let origin = ffi::parse_id(origin)?;
263 let path = unsafe { ffi::parse_str(path, path_len)? };
264
265 let mut state = State::lock();
266 let broadcast = state.origin.consume(origin, path)?;
267 Ok(state.consume.start(broadcast.into()))
268 })
269}
270
271#[no_mangle]
275pub extern "C" fn moq_origin_close(origin: u32) -> i32 {
276 ffi::enter(move || {
277 let origin = ffi::parse_id(origin)?;
278 State::lock().origin.close(origin)
279 })
280}
281
282#[no_mangle]
286pub extern "C" fn moq_publish_create() -> i32 {
287 ffi::enter(move || State::lock().publish.create())
288}
289
290#[no_mangle]
294pub extern "C" fn moq_publish_close(broadcast: u32) -> i32 {
295 ffi::enter(move || {
296 let broadcast = ffi::parse_id(broadcast)?;
297 State::lock().publish.close(broadcast)
298 })
299}
300
301#[no_mangle]
312pub unsafe extern "C" fn moq_publish_media_ordered(
313 broadcast: u32,
314 format: *const c_char,
315 format_len: usize,
316 init: *const u8,
317 init_size: usize,
318) -> i32 {
319 ffi::enter(move || {
320 let broadcast = ffi::parse_id(broadcast)?;
321 let format = unsafe { ffi::parse_str(format, format_len)? };
322 let init = unsafe { ffi::parse_slice(init, init_size)? };
323
324 State::lock().publish.media_ordered(broadcast, format, init)
325 })
326}
327
328#[no_mangle]
332pub extern "C" fn moq_publish_media_close(export: u32) -> i32 {
333 ffi::enter(move || {
334 let export = ffi::parse_id(export)?;
335 State::lock().publish.media_close(export)
336 })
337}
338
339#[no_mangle]
349pub unsafe extern "C" fn moq_publish_media_frame(
350 media: u32,
351 payload: *const u8,
352 payload_size: usize,
353 timestamp_us: u64,
354) -> i32 {
355 ffi::enter(move || {
356 let media = ffi::parse_id(media)?;
357 let payload = unsafe { ffi::parse_slice(payload, payload_size)? };
358 let timestamp = hang::Timestamp::from_micros(timestamp_us)?;
359 State::lock().publish.media_frame(media, payload, timestamp)
360 })
361}
362
363#[no_mangle]
373pub unsafe extern "C" fn moq_consume_catalog(
374 broadcast: u32,
375 on_catalog: Option<extern "C" fn(user_data: *mut c_void, catalog: i32)>,
376 user_data: *mut c_void,
377) -> i32 {
378 ffi::enter(move || {
379 let broadcast = ffi::parse_id(broadcast)?;
380 let on_catalog = ffi::OnStatus::new(user_data, on_catalog);
381 State::lock().consume.catalog(broadcast, on_catalog)
382 })
383}
384
385#[no_mangle]
389pub extern "C" fn moq_consume_catalog_close(catalog: u32) -> i32 {
390 ffi::enter(move || {
391 let catalog = ffi::parse_id(catalog)?;
392 State::lock().consume.catalog_close(catalog)
393 })
394}
395
396#[no_mangle]
406pub unsafe extern "C" fn moq_consume_video_config(catalog: u32, index: u32, dst: *mut moq_video_config) -> i32 {
407 ffi::enter(move || {
408 let catalog = ffi::parse_id(catalog)?;
409 let index = index as usize;
410 let dst = dst.as_mut().ok_or(Error::InvalidPointer)?;
411 State::lock().consume.video_config(catalog, index, dst)
412 })
413}
414
415#[no_mangle]
425pub unsafe extern "C" fn moq_consume_audio_config(catalog: u32, index: u32, dst: *mut moq_audio_config) -> i32 {
426 ffi::enter(move || {
427 let catalog = ffi::parse_id(catalog)?;
428 let index = index as usize;
429 let dst = dst.as_mut().ok_or(Error::InvalidPointer)?;
430 State::lock().consume.audio_config(catalog, index, dst)
431 })
432}
433
434#[no_mangle]
444pub unsafe extern "C" fn moq_consume_video_ordered(
445 broadcast: u32,
446 index: u32,
447 max_latency_ms: u64,
448 on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
449 user_data: *mut c_void,
450) -> i32 {
451 ffi::enter(move || {
452 let broadcast = ffi::parse_id(broadcast)?;
453 let index = index as usize;
454 let max_latency = std::time::Duration::from_millis(max_latency_ms);
455 let on_frame = ffi::OnStatus::new(user_data, on_frame);
456 State::lock()
457 .consume
458 .video_ordered(broadcast, index, max_latency, on_frame)
459 })
460}
461
462#[no_mangle]
466pub extern "C" fn moq_consume_video_close(track: u32) -> i32 {
467 ffi::enter(move || {
468 let track = ffi::parse_id(track)?;
469 State::lock().consume.video_close(track)
470 })
471}
472
473#[no_mangle]
483pub unsafe extern "C" fn moq_consume_audio_ordered(
484 broadcast: u32,
485 index: u32,
486 max_latency_ms: u64,
487 on_frame: Option<extern "C" fn(user_data: *mut c_void, frame: i32)>,
488 user_data: *mut c_void,
489) -> i32 {
490 ffi::enter(move || {
491 let broadcast = ffi::parse_id(broadcast)?;
492 let index = index as usize;
493 let max_latency = std::time::Duration::from_millis(max_latency_ms);
494 let on_frame = ffi::OnStatus::new(user_data, on_frame);
495 State::lock()
496 .consume
497 .audio_ordered(broadcast, index, max_latency, on_frame)
498 })
499}
500
501#[no_mangle]
505pub extern "C" fn moq_consume_audio_close(track: u32) -> i32 {
506 ffi::enter(move || {
507 let track = ffi::parse_id(track)?;
508 State::lock().consume.audio_close(track)
509 })
510}
511
512#[no_mangle]
522pub unsafe extern "C" fn moq_consume_frame_chunk(frame: u32, index: u32, dst: *mut moq_frame) -> i32 {
523 ffi::enter(move || {
524 let frame = ffi::parse_id(frame)?;
525 let index = index as usize;
526 let dst = dst.as_mut().ok_or(Error::InvalidPointer)?;
527 State::lock().consume.frame_chunk(frame, index, dst)
528 })
529}
530
531#[no_mangle]
535pub extern "C" fn moq_consume_frame_close(frame: u32) -> i32 {
536 ffi::enter(move || {
537 let frame = ffi::parse_id(frame)?;
538 State::lock().consume.frame_close(frame)
539 })
540}
541
542#[no_mangle]
546pub extern "C" fn moq_consume_close(consume: u32) -> i32 {
547 ffi::enter(move || {
548 let consume = ffi::parse_id(consume)?;
549 State::lock().consume.close(consume)
550 })
551}