Skip to main content

adamo_sys/
lib.rs

1//! Raw FFI bindings to the Adamo SDK.
2//!
3//! This crate is a thin, `unsafe` interface — prefer the safe wrappers in
4//! [`adamo`](https://docs.rs/adamo) for normal use.
5//!
6//! All declarations match the C header generated by `cbindgen` for the
7//! `adamo-c` crate (`include/adamo/adamo.h`). Keep them in sync.
8
9#![allow(non_camel_case_types)]
10#![allow(clippy::missing_safety_doc)]
11
12use std::os::raw::{c_char, c_int, c_void};
13
14pub type adamo_protocol_t = u32;
15#[deprecated(note = "plain UDP is not publicly available; use ADAMO_PROTOCOL_QUIC")]
16pub const ADAMO_PROTOCOL_UDP: adamo_protocol_t = 0;
17pub const ADAMO_PROTOCOL_QUIC: adamo_protocol_t = 1;
18pub const ADAMO_PROTOCOL_TCP: adamo_protocol_t = 2;
19
20pub type adamo_regime_t = u32;
21pub const ADAMO_REGIME_STABLE: adamo_regime_t = 0;
22pub const ADAMO_REGIME_DEGRADING: adamo_regime_t = 1;
23pub const ADAMO_REGIME_VOLATILE: adamo_regime_t = 2;
24pub const ADAMO_REGIME_RECOVERING: adamo_regime_t = 3;
25
26pub type adamo_video_backend_t = i32;
27pub const ADAMO_VIDEO_BACKEND_AUTO: adamo_video_backend_t = 0;
28pub const ADAMO_VIDEO_BACKEND_GSTREAMER: adamo_video_backend_t = 1;
29pub const ADAMO_VIDEO_BACKEND_HW_PIPELINE: adamo_video_backend_t = 2;
30
31// Opaque handles. Zero-sized placeholders — callers only ever see pointers
32// to them. `PhantomData<*mut ()>` keeps them `!Send + !Sync` by default;
33// the safe wrapper re-asserts thread-safety where the C API documents it.
34#[repr(C)]
35pub struct adamo_session_t {
36    _priv: [u8; 0],
37    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
38}
39#[repr(C)]
40pub struct adamo_publisher_t {
41    _priv: [u8; 0],
42    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
43}
44#[repr(C)]
45pub struct adamo_subscriber_t {
46    _priv: [u8; 0],
47    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
48}
49#[repr(C)]
50pub struct adamo_cb_sub_t {
51    _priv: [u8; 0],
52    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
53}
54#[repr(C)]
55pub struct adamo_liveliness_token_t {
56    _priv: [u8; 0],
57    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
58}
59#[repr(C)]
60pub struct adamo_liveliness_sub_t {
61    _priv: [u8; 0],
62    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
63}
64#[repr(C)]
65pub struct adamo_state_t {
66    _priv: [u8; 0],
67    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
68}
69#[repr(C)]
70pub struct adamo_task_runner_t {
71    _priv: [u8; 0],
72    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
73}
74#[repr(C)]
75pub struct adamo_transport_core_t {
76    _priv: [u8; 0],
77    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
78}
79#[repr(C)]
80pub struct adamo_rlnc_decoder_t {
81    _priv: [u8; 0],
82    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
83}
84#[repr(C)]
85pub struct adamo_robot_t {
86    _priv: [u8; 0],
87    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
88}
89#[repr(C)]
90pub struct adamo_video_track_t {
91    _priv: [u8; 0],
92    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
93}
94#[repr(C)]
95pub struct adamo_video_receiver_t {
96    _priv: [u8; 0],
97    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
98}
99#[repr(C)]
100pub struct adamo_audio_receiver_t {
101    _priv: [u8; 0],
102    _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
103}
104
105#[repr(C)]
106pub struct adamo_sample_t {
107    pub key: *mut c_char,
108    pub payload: *mut u8,
109    pub payload_len: usize,
110    pub is_delete: i32,
111    pub timestamp_us: u64,
112}
113
114#[repr(C)]
115pub struct adamo_latency_stats_t {
116    pub regime: adamo_regime_t,
117    pub jitter_hint_ms: f32,
118    pub garch_sigma_ms: f32,
119    pub target_bitrate_kbps: u32,
120    pub loss_rate: f32,
121    pub queuing_delay_ms: f32,
122    pub timestamp_ms: u64,
123}
124
125#[repr(C)]
126#[derive(Clone, Copy)]
127pub struct adamo_video_receive_options_t {
128    pub codec: *const c_char,
129    pub decoder: *const c_char,
130    pub max_queued_frames: u32,
131}
132
133#[repr(C)]
134pub struct adamo_video_frame_t {
135    pub data: *mut u8,
136    pub data_len: usize,
137    pub width: u32,
138    pub height: u32,
139    pub seq: u64,
140    pub capture_unix_us: u64,
141    pub keyframe: i32,
142}
143
144#[repr(C)]
145#[derive(Clone, Copy)]
146pub struct adamo_video_frame_info_t {
147    pub width: u32,
148    pub height: u32,
149    pub seq: u64,
150    pub capture_unix_us: u64,
151    pub keyframe: i32,
152}
153
154#[repr(C)]
155#[derive(Clone, Copy)]
156pub struct adamo_video_receiver_stats_t {
157    pub packets: u64,
158    pub frames: u64,
159    pub keyframes: u64,
160    pub frames_decoded: u64,
161    pub frames_dropped: u64,
162    pub rlnc_recovered: u64,
163}
164
165#[repr(C)]
166pub struct adamo_transport_frame_t {
167    pub data: *mut u8,
168    pub data_len: usize,
169    pub frame_seq: u64,
170    pub capture_unix_us: u64,
171    pub keyframe: i32,
172    pub encrypted: i32,
173}
174
175#[repr(C)]
176pub struct adamo_transport_stats_t {
177    pub frames_emitted: u64,
178    pub chunks_received: u64,
179    pub duplicates_dropped: u64,
180    pub malformed_dropped: u64,
181    pub frames_expired: u64,
182}
183
184#[repr(C)]
185pub struct adamo_rlnc_recovery_t {
186    pub sequence: u64,
187    pub data: *mut u8,
188    pub data_len: usize,
189    pub repair_keys: *mut u16,
190    pub repair_key_count: usize,
191}
192
193#[repr(C)]
194#[derive(Clone, Copy)]
195pub struct adamo_video_options_t {
196    pub width: u32,
197    pub height: u32,
198    pub pixel_format: *const c_char,
199    pub codec: *const c_char,
200    pub encoder: *const c_char,
201    pub bitrate_kbps: u32,
202    pub adaptive_bitrate: i32,
203    pub min_bitrate_kbps: u32,
204    pub max_bitrate_kbps: u32,
205    pub bitrate_priority: f32,
206    pub fps: u32,
207    pub keyframe_distance: f64,
208    pub stereo: i32,
209    pub backend: adamo_video_backend_t,
210    /// Optional iceoryx2 service name to also publish every raw captured
211    /// frame to (V4L2 tracks only; null = disabled).
212    pub shm_publish: *const c_char,
213    /// Per-track video protocol (null = adamo default).
214    pub reliability: *const c_char,
215    /// Forward an already-encoded H.264/H.265 bitstream without re-encoding
216    /// (0 = off). Appended last to keep prior field offsets ABI-stable.
217    pub passthrough: i32,
218    /// Keep the robot running when this track fails (0 = required/default).
219    /// Appended last to keep prior field offsets ABI-stable.
220    pub allow_missing: i32,
221}
222
223/// Audio send options — mirrors the C ABI `adamo_audio_options_t`. Note the
224/// flag fields are `bool` (C `_Bool`) and `channels` is `u8`, matching the
225/// header cbindgen generates — unlike `adamo_video_options_t`, whose flags are
226/// `i32`.
227#[repr(C)]
228#[derive(Clone, Copy)]
229pub struct adamo_audio_options_t {
230    pub source_type: *const c_char,
231    pub device: *const c_char,
232    pub pipeline: *const c_char,
233    pub codec: *const c_char,
234    pub bitrate_kbps: u32,
235    pub sample_rate: u32,
236    pub channels: u8,
237    pub frame_ms: u32,
238    pub inband_fec: bool,
239    pub dtx: bool,
240    pub allow_missing: bool,
241}
242
243#[repr(C)]
244#[derive(Clone, Copy)]
245pub struct adamo_audio_receive_options_t {
246    pub max_queued_frames: u32,
247    pub reorder_deadline_ms: u32,
248}
249
250#[repr(C)]
251pub struct adamo_audio_frame_t {
252    pub samples: *mut i16,
253    pub sample_count: usize,
254    pub sample_rate: u32,
255    pub channels: u8,
256    pub seq: u64,
257    pub capture_unix_us: u64,
258}
259
260#[repr(C)]
261#[derive(Clone, Copy)]
262pub struct adamo_audio_receiver_stats_t {
263    pub packets: u64,
264    pub repairs: u64,
265    pub recovered: u64,
266    pub concealed: u64,
267    pub decoded: u64,
268    pub dropped: u64,
269}
270
271pub type adamo_sample_cb_t =
272    Option<unsafe extern "C" fn(sample: *const adamo_sample_t, user: *mut c_void)>;
273pub type adamo_liveliness_cb_t =
274    Option<unsafe extern "C" fn(key: *const c_char, alive: c_int, user: *mut c_void)>;
275
276// docs.rs builds can't link libadamo; stub the extern block out there so
277// rustdoc still succeeds.
278#[cfg(not(docsrs))]
279unsafe extern "C" {
280    pub fn adamo_transport_core_new(
281        playout_deadline_ms: u64,
282        keyframe_deadline_ms: u64,
283        dedup_capacity: usize,
284    ) -> *mut adamo_transport_core_t;
285    pub fn adamo_transport_core_free(core: *mut adamo_transport_core_t);
286    pub fn adamo_transport_core_ingest(
287        core: *mut adamo_transport_core_t,
288        packet: *const u8,
289        packet_len: usize,
290        packet_type: u8,
291        arrival_ms: u64,
292        delivery: u8,
293    ) -> *mut adamo_transport_frame_t;
294    pub fn adamo_transport_frame_free(frame: *mut adamo_transport_frame_t);
295    pub fn adamo_transport_core_expire(
296        core: *mut adamo_transport_core_t,
297        wall_unix_ms: u64,
298        arrival_ms: u64,
299    ) -> usize;
300    pub fn adamo_transport_core_stats(
301        core: *const adamo_transport_core_t,
302        out: *mut adamo_transport_stats_t,
303    ) -> i32;
304    pub fn adamo_rlnc_decoder_new(
305        max_equations: usize,
306        history_depth: u64,
307    ) -> *mut adamo_rlnc_decoder_t;
308    pub fn adamo_rlnc_decoder_free(decoder: *mut adamo_rlnc_decoder_t);
309    pub fn adamo_rlnc_decoder_add_source(
310        decoder: *mut adamo_rlnc_decoder_t,
311        sequence: u64,
312        packet: *const u8,
313        packet_len: usize,
314    ) -> i32;
315    pub fn adamo_rlnc_decoder_add_repair(
316        decoder: *mut adamo_rlnc_decoder_t,
317        packet: *const u8,
318        packet_len: usize,
319    ) -> i32;
320    pub fn adamo_rlnc_decoder_take_recovery(
321        decoder: *mut adamo_rlnc_decoder_t,
322    ) -> *mut adamo_rlnc_recovery_t;
323    pub fn adamo_rlnc_recovery_free(recovery: *mut adamo_rlnc_recovery_t);
324
325    pub fn adamo_last_error() -> *const c_char;
326
327    /// Category of the last error on this thread. 0 = OK, 1 = auth,
328    /// 2 = network, 3 = timeout, 4 = invalid argument, 5 = video,
329    /// 6 = internal, 7 = audio. Stable across releases.
330    pub fn adamo_last_error_code() -> core::ffi::c_int;
331    pub fn adamo_last_stream_error_code() -> *const c_char;
332    pub fn adamo_last_stream_error_stage() -> *const c_char;
333    pub fn adamo_last_stream_error_track() -> *const c_char;
334    pub fn adamo_last_stream_error_backend() -> *const c_char;
335    pub fn adamo_last_stream_error_source() -> *const c_char;
336
337    pub fn adamo_sample_free(sample: *mut adamo_sample_t);
338
339    pub fn adamo_open(api_key: *const c_char, protocol: adamo_protocol_t) -> *mut adamo_session_t;
340    pub fn adamo_open_default(api_key: *const c_char) -> *mut adamo_session_t;
341    #[deprecated(note = "adamo_open already uses mTLS; use adamo_open")]
342    pub fn adamo_open_mtls(
343        api_key: *const c_char,
344        protocol: adamo_protocol_t,
345    ) -> *mut adamo_session_t;
346    pub fn adamo_session_free(sess: *mut adamo_session_t);
347    pub fn adamo_session_org(sess: *const adamo_session_t) -> *const c_char;
348    pub fn adamo_session_org_len(sess: *const adamo_session_t) -> usize;
349
350    pub fn adamo_put(
351        sess: *const adamo_session_t,
352        key: *const c_char,
353        payload: *const u8,
354        payload_len: usize,
355        priority: u8,
356        express: i32,
357    ) -> i32;
358    pub fn adamo_log(
359        sess: *const adamo_session_t,
360        name: *const c_char,
361        message: *const c_char,
362        level: *const c_char,
363    ) -> i32;
364    pub fn adamo_set_pose(
365        sess: *const adamo_session_t,
366        name: *const c_char,
367        x: f64,
368        y: f64,
369        z: f64,
370        frame: *const c_char,
371        heading: f64,
372    ) -> i32;
373
374    pub fn adamo_publisher(
375        sess: *const adamo_session_t,
376        key: *const c_char,
377        priority: u8,
378        express: i32,
379        reliable: i32,
380    ) -> *mut adamo_publisher_t;
381    pub fn adamo_publisher_put(
382        pub_: *const adamo_publisher_t,
383        payload: *const u8,
384        payload_len: usize,
385    ) -> i32;
386    pub fn adamo_publisher_free(pub_: *mut adamo_publisher_t);
387
388    pub fn adamo_subscribe(
389        sess: *const adamo_session_t,
390        key: *const c_char,
391    ) -> *mut adamo_subscriber_t;
392    pub fn adamo_sub_recv(
393        sub: *const adamo_subscriber_t,
394        timeout_ms: u64,
395    ) -> *mut adamo_sample_t;
396    pub fn adamo_sub_try_recv(sub: *const adamo_subscriber_t) -> *mut adamo_sample_t;
397    pub fn adamo_sub_free(sub: *mut adamo_subscriber_t);
398
399    pub fn adamo_subscribe_cb(
400        sess: *const adamo_session_t,
401        key: *const c_char,
402        cb: adamo_sample_cb_t,
403        user: *mut c_void,
404    ) -> *mut adamo_cb_sub_t;
405    pub fn adamo_cb_sub_free(sub: *mut adamo_cb_sub_t);
406
407    pub fn adamo_get(
408        sess: *const adamo_session_t,
409        key: *const c_char,
410        timeout_ms: u64,
411        count_out: *mut usize,
412    ) -> *mut *mut adamo_sample_t;
413    pub fn adamo_get_replies_free(replies: *mut *mut adamo_sample_t, count: usize);
414
415    pub fn adamo_state_declare(
416        sess: *const adamo_session_t,
417        robot: *const c_char,
418    ) -> *mut adamo_state_t;
419    pub fn adamo_state_set(
420        state: *const adamo_state_t,
421        key: *const c_char,
422        payload: *const u8,
423        payload_len: usize,
424    ) -> i32;
425    pub fn adamo_state_get(
426        state: *const adamo_state_t,
427        key: *const c_char,
428        out_payload: *mut *mut u8,
429        out_len: *mut usize,
430    ) -> i32;
431    pub fn adamo_state_delete(state: *const adamo_state_t, key: *const c_char) -> i32;
432    pub fn adamo_state_bytes_free(payload: *mut u8, payload_len: usize);
433    pub fn adamo_state_free(state: *mut adamo_state_t);
434
435    pub fn adamo_task_runner_declare(
436        sess: *const adamo_session_t,
437        robot: *const c_char,
438    ) -> *mut adamo_task_runner_t;
439    pub fn adamo_task_runner_select(
440        runner: *const adamo_task_runner_t,
441        task_set_id: *const c_char,
442        name: *const c_char,
443        ids: *const *const c_char,
444        names: *const *const c_char,
445        count: usize,
446    ) -> i32;
447    pub fn adamo_task_runner_advance(runner: *const adamo_task_runner_t) -> i32;
448    pub fn adamo_task_runner_reset(runner: *const adamo_task_runner_t) -> i32;
449    pub fn adamo_task_runner_free(runner: *mut adamo_task_runner_t);
450
451    pub fn adamo_liveliness_declare(
452        sess: *const adamo_session_t,
453        token_key: *const c_char,
454    ) -> *mut adamo_liveliness_token_t;
455    pub fn adamo_liveliness_token_free(tok: *mut adamo_liveliness_token_t);
456    pub fn adamo_liveliness_get(
457        sess: *const adamo_session_t,
458        pattern: *const c_char,
459        count_out: *mut usize,
460    ) -> *mut *mut c_char;
461    pub fn adamo_liveliness_tokens_free(tokens: *mut *mut c_char, count: usize);
462    pub fn adamo_liveliness_subscribe(
463        sess: *const adamo_session_t,
464        pattern: *const c_char,
465        history: c_int,
466        cb: adamo_liveliness_cb_t,
467        user: *mut c_void,
468    ) -> *mut adamo_liveliness_sub_t;
469    pub fn adamo_liveliness_sub_free(sub: *mut adamo_liveliness_sub_t);
470
471    pub fn adamo_video_receive_options_default() -> adamo_video_receive_options_t;
472    pub fn adamo_video_receiver_open(
473        sess: *const adamo_session_t,
474        robot: *const c_char,
475        track: *const c_char,
476    ) -> *mut adamo_video_receiver_t;
477    pub fn adamo_video_receiver_open_configured(
478        sess: *const adamo_session_t,
479        robot: *const c_char,
480        track: *const c_char,
481        opts: *const adamo_video_receive_options_t,
482    ) -> *mut adamo_video_receiver_t;
483    pub fn adamo_video_receiver_recv(
484        rx: *const adamo_video_receiver_t,
485        timeout_ms: u64,
486    ) -> *mut adamo_video_frame_t;
487    pub fn adamo_video_receiver_try_recv(
488        rx: *const adamo_video_receiver_t,
489    ) -> *mut adamo_video_frame_t;
490    pub fn adamo_video_receiver_try_recv_cuda(
491        rx: *const adamo_video_receiver_t,
492        dst_device: *mut c_void,
493        dst_capacity: usize,
494        dst_pitch: usize,
495        expected_width: u32,
496        expected_height: u32,
497        out_info: *mut adamo_video_frame_info_t,
498    ) -> i32;
499    pub fn adamo_video_frame_free(frame: *mut adamo_video_frame_t);
500    pub fn adamo_video_receiver_request_keyframe(rx: *const adamo_video_receiver_t) -> i32;
501    pub fn adamo_video_receiver_stereo(rx: *const adamo_video_receiver_t) -> i32;
502    pub fn adamo_video_receiver_stats(
503        rx: *const adamo_video_receiver_t,
504        out: *mut adamo_video_receiver_stats_t,
505    ) -> i32;
506    pub fn adamo_video_receiver_free(rx: *mut adamo_video_receiver_t);
507
508    pub fn adamo_robot_new(
509        api_key: *const c_char,
510        name: *const c_char,
511        protocol: adamo_protocol_t,
512    ) -> *mut adamo_robot_t;
513    pub fn adamo_robot_new_default(
514        api_key: *const c_char,
515        name: *const c_char,
516    ) -> *mut adamo_robot_t;
517    pub fn adamo_video_options_default() -> adamo_video_options_t;
518    pub fn adamo_robot_video(
519        robot: *mut adamo_robot_t,
520        name: *const c_char,
521        width: u32,
522        height: u32,
523        pixel_format: *const c_char,
524        fps: u32,
525        bitrate_kbps: u32,
526        stereo: bool,
527    ) -> *mut adamo_video_track_t;
528    pub fn adamo_robot_video_with_options(
529        robot: *mut adamo_robot_t,
530        name: *const c_char,
531        width: u32,
532        height: u32,
533        pixel_format: *const c_char,
534        fps: u32,
535        bitrate_kbps: u32,
536        stereo: bool,
537        encoder: *const c_char,
538        hw_pipeline: bool,
539    ) -> *mut adamo_video_track_t;
540    pub fn adamo_robot_video_configured(
541        robot: *mut adamo_robot_t,
542        name: *const c_char,
543        opts: *const adamo_video_options_t,
544    ) -> *mut adamo_video_track_t;
545    pub fn adamo_robot_attach_video_v4l2(
546        robot: *mut adamo_robot_t,
547        name: *const c_char,
548        device: *const c_char,
549        width: u32,
550        height: u32,
551        fps: u32,
552        bitrate_kbps: u32,
553        stereo: bool,
554    ) -> i32;
555    pub fn adamo_robot_attach_video_v4l2_configured(
556        robot: *mut adamo_robot_t,
557        name: *const c_char,
558        device: *const c_char,
559        opts: *const adamo_video_options_t,
560    ) -> i32;
561    pub fn adamo_robot_attach_video_gst(
562        robot: *mut adamo_robot_t,
563        name: *const c_char,
564        pipeline: *const c_char,
565        width: u32,
566        height: u32,
567        fps: u32,
568        bitrate_kbps: u32,
569        stereo: bool,
570    ) -> i32;
571    pub fn adamo_robot_attach_video_gst_with_format(
572        robot: *mut adamo_robot_t,
573        name: *const c_char,
574        pipeline: *const c_char,
575        width: u32,
576        height: u32,
577        fps: u32,
578        bitrate_kbps: u32,
579        stereo: bool,
580        source_format: *const c_char,
581    ) -> i32;
582    pub fn adamo_robot_attach_video_gst_configured(
583        robot: *mut adamo_robot_t,
584        name: *const c_char,
585        pipeline: *const c_char,
586        opts: *const adamo_video_options_t,
587    ) -> i32;
588    pub fn adamo_robot_attach_video_shm(
589        robot: *mut adamo_robot_t,
590        name: *const c_char,
591        service: *const c_char,
592        width: u32,
593        height: u32,
594        pixel_format: *const c_char,
595        fps: u32,
596        bitrate_kbps: u32,
597        stereo: bool,
598    ) -> i32;
599    pub fn adamo_robot_attach_video_shm_configured(
600        robot: *mut adamo_robot_t,
601        name: *const c_char,
602        service: *const c_char,
603        opts: *const adamo_video_options_t,
604    ) -> i32;
605
606    // -- Audio send (source-driven). Requires libadamo built with `audio`. --
607    pub fn adamo_audio_options_default() -> adamo_audio_options_t;
608    pub fn adamo_robot_attach_audio_alsa(
609        robot: *mut adamo_robot_t,
610        name: *const c_char,
611        device: *const c_char,
612        bitrate_kbps: u32,
613        channels: u8,
614        sample_rate: u32,
615        frame_ms: u32,
616    ) -> i32;
617    pub fn adamo_robot_attach_audio_test(
618        robot: *mut adamo_robot_t,
619        name: *const c_char,
620    ) -> i32;
621    pub fn adamo_robot_attach_audio_gst(
622        robot: *mut adamo_robot_t,
623        name: *const c_char,
624        pipeline: *const c_char,
625    ) -> i32;
626    pub fn adamo_robot_attach_audio_configured(
627        robot: *mut adamo_robot_t,
628        name: *const c_char,
629        opts: *const adamo_audio_options_t,
630    ) -> i32;
631
632    pub fn adamo_video_track_send(
633        track: *mut adamo_video_track_t,
634        payload: *const u8,
635        payload_len: usize,
636    ) -> i32;
637    pub fn adamo_video_track_free(track: *mut adamo_video_track_t);
638    pub fn adamo_robot_enable_p2p(robot: *mut adamo_robot_t, port: u16) -> i32;
639    pub fn adamo_robot_enable_ros_control(robot: *mut adamo_robot_t, domain_id: u32) -> i32;
640    pub fn adamo_robot_run(robot: *mut adamo_robot_t) -> i32;
641    pub fn adamo_robot_stop(robot: *mut adamo_robot_t) -> i32;
642    pub fn adamo_robot_free(robot: *mut adamo_robot_t);
643
644    pub fn adamo_audio_receive_options_default() -> adamo_audio_receive_options_t;
645    pub fn adamo_audio_receiver_open(
646        sess: *const adamo_session_t,
647        robot: *const c_char,
648        track: *const c_char,
649    ) -> *mut adamo_audio_receiver_t;
650    pub fn adamo_audio_receiver_open_configured(
651        sess: *const adamo_session_t,
652        robot: *const c_char,
653        track: *const c_char,
654        opts: *const adamo_audio_receive_options_t,
655    ) -> *mut adamo_audio_receiver_t;
656    pub fn adamo_audio_receiver_recv(
657        rx: *const adamo_audio_receiver_t,
658        timeout_ms: u64,
659    ) -> *mut adamo_audio_frame_t;
660    pub fn adamo_audio_receiver_try_recv(
661        rx: *const adamo_audio_receiver_t,
662    ) -> *mut adamo_audio_frame_t;
663    pub fn adamo_audio_frame_free(frame: *mut adamo_audio_frame_t);
664    pub fn adamo_audio_receiver_stats(
665        rx: *const adamo_audio_receiver_t,
666        out: *mut adamo_audio_receiver_stats_t,
667    ) -> i32;
668    pub fn adamo_audio_receiver_free(rx: *mut adamo_audio_receiver_t);
669
670    pub fn adamo_detect_encoder() -> *const c_char;
671
672    pub fn adamo_fabric_now_us() -> u64;
673    pub fn adamo_fabric_synced() -> i32;
674    pub fn adamo_relay_rtt_us(sess: *const adamo_session_t, out_us: *mut u64) -> i32;
675
676    pub fn adamo_parse_heartbeat(
677        payload: *const u8,
678        payload_len: usize,
679        out: *mut adamo_latency_stats_t,
680    ) -> i32;
681    pub fn adamo_heartbeat_topic(
682        robot: *const c_char,
683        out: *mut c_char,
684        out_cap: usize,
685    ) -> isize;
686    pub fn adamo_ping_topic(
687        robot: *const c_char,
688        out: *mut c_char,
689        out_cap: usize,
690    ) -> isize;
691    pub fn adamo_pong_topic(
692        robot: *const c_char,
693        out: *mut c_char,
694        out_cap: usize,
695    ) -> isize;
696    pub fn adamo_measure_rtt(
697        sess: *const adamo_session_t,
698        robot: *const c_char,
699        timeout_ms: u64,
700        out_us: *mut u64,
701    ) -> i32;
702}
703
704#[cfg(test)]
705mod added_api_tests {
706    use super::*;
707    use std::ptr;
708
709    #[test]
710    fn all_added_functions_link_and_honor_basic_contracts() {
711        // Assigning every symbol to its exact C signature catches declaration
712        // drift at compile time; calling each one catches missing native exports.
713        let _: unsafe extern "C" fn(
714            *const adamo_session_t,
715            *const c_char,
716            *const c_char,
717            *const c_char,
718        ) -> i32 = adamo_log;
719        let _: unsafe extern "C" fn(
720            *const adamo_session_t,
721            *const c_char,
722            f64,
723            f64,
724            f64,
725            *const c_char,
726            f64,
727        ) -> i32 = adamo_set_pose;
728        let _: unsafe extern "C" fn(*mut adamo_robot_t, u32) -> i32 =
729            adamo_robot_enable_ros_control;
730
731        let video_defaults = unsafe { adamo_video_receive_options_default() };
732        assert!(video_defaults.codec.is_null());
733        assert!(video_defaults.decoder.is_null());
734        assert_eq!(video_defaults.max_queued_frames, 0);
735        unsafe {
736            assert!(adamo_video_receiver_open(ptr::null(), ptr::null(), ptr::null()).is_null());
737            assert!(
738                adamo_video_receiver_open_configured(
739                    ptr::null(),
740                    ptr::null(),
741                    ptr::null(),
742                    &video_defaults,
743                )
744                .is_null()
745            );
746            assert!(adamo_video_receiver_recv(ptr::null(), 1).is_null());
747            assert!(adamo_video_receiver_try_recv(ptr::null()).is_null());
748            assert_eq!(
749                adamo_video_receiver_try_recv_cuda(
750                    ptr::null(),
751                    ptr::null_mut(),
752                    0,
753                    0,
754                    0,
755                    0,
756                    ptr::null_mut(),
757                ),
758                -1
759            );
760            adamo_video_frame_free(ptr::null_mut());
761            assert_eq!(adamo_video_receiver_request_keyframe(ptr::null()), -1);
762            assert_eq!(adamo_video_receiver_stereo(ptr::null()), -1);
763            assert_eq!(adamo_video_receiver_stats(ptr::null(), ptr::null_mut()), -1);
764            adamo_video_receiver_free(ptr::null_mut());
765        }
766
767        let audio_defaults = unsafe { adamo_audio_receive_options_default() };
768        assert_eq!(audio_defaults.max_queued_frames, 0);
769        assert_eq!(audio_defaults.reorder_deadline_ms, 0);
770        unsafe {
771            assert!(adamo_audio_receiver_open(ptr::null(), ptr::null(), ptr::null()).is_null());
772            assert!(
773                adamo_audio_receiver_open_configured(
774                    ptr::null(),
775                    ptr::null(),
776                    ptr::null(),
777                    &audio_defaults,
778                )
779                .is_null()
780            );
781            assert!(adamo_audio_receiver_recv(ptr::null(), 1).is_null());
782            assert!(adamo_audio_receiver_try_recv(ptr::null()).is_null());
783            adamo_audio_frame_free(ptr::null_mut());
784            assert_eq!(adamo_audio_receiver_stats(ptr::null(), ptr::null_mut()), -1);
785            adamo_audio_receiver_free(ptr::null_mut());
786
787            assert_eq!(adamo_log(ptr::null(), ptr::null(), ptr::null(), ptr::null()), -1);
788            assert_eq!(
789                adamo_set_pose(
790                    ptr::null(),
791                    ptr::null(),
792                    0.0,
793                    0.0,
794                    0.0,
795                    ptr::null(),
796                    f64::NAN,
797                ),
798                -1
799            );
800            assert_eq!(adamo_robot_enable_ros_control(ptr::null_mut(), 0), -1);
801        }
802    }
803}
804
805// Silence `unused_imports` if someone pulls in the crate without calling
806// anything (e.g. docs builds).
807#[doc(hidden)]
808pub const _IMPORTS: (Option<c_int>, Option<*mut c_void>) = (None, None);