cubeb_backend/
capi.rs

1// Copyright © 2017 Mozilla Foundation
2//
3// This program is made available under an ISC-style license.  See the
4// accompanying file LICENSE for details
5
6use cubeb_core::{
7    ffi, DeviceInfo, DeviceRef, DeviceType, InputProcessingParams, StreamParams, StreamParamsRef,
8};
9use std::ffi::CStr;
10use std::mem;
11use std::os::raw::{c_char, c_int, c_void};
12use {ContextOps, StreamOps};
13
14// Helper macro for unwrapping `Result` values from rust-api calls
15// while returning early with a c-api error code if the value of the
16// expression is `Err`.
17macro_rules! _try(
18    ($e:expr) => (match $e {
19        Ok(e) => e,
20        Err(e) => return e as c_int
21    })
22);
23
24macro_rules! as_opt_ref {
25    ($e:expr) => {
26        if $e.is_null() {
27            None
28        } else {
29            Some(StreamParamsRef::from_ptr($e))
30        }
31    };
32}
33
34#[macro_export]
35macro_rules! capi_new(
36    ($ctx:ident, $stm:ident) => (
37        Ops {
38            init: Some($crate::capi::capi_init::<$ctx>),
39            get_backend_id: Some($crate::capi::capi_get_backend_id::<$ctx>),
40            get_max_channel_count: Some($crate::capi::capi_get_max_channel_count::<$ctx>),
41            get_min_latency: Some($crate::capi::capi_get_min_latency::<$ctx>),
42            get_preferred_sample_rate: Some($crate::capi::capi_get_preferred_sample_rate::<$ctx>),
43            get_supported_input_processing_params:
44                Some($crate::capi::capi_get_supported_input_processing_params::<$ctx>),
45            enumerate_devices: Some($crate::capi::capi_enumerate_devices::<$ctx>),
46            device_collection_destroy: Some($crate::capi::capi_device_collection_destroy::<$ctx>),
47            destroy: Some($crate::capi::capi_destroy::<$ctx>),
48            stream_init: Some($crate::capi::capi_stream_init::<$ctx>),
49            stream_destroy: Some($crate::capi::capi_stream_destroy::<$stm>),
50            stream_start: Some($crate::capi::capi_stream_start::<$stm>),
51            stream_stop: Some($crate::capi::capi_stream_stop::<$stm>),
52            stream_get_position: Some($crate::capi::capi_stream_get_position::<$stm>),
53            stream_get_latency: Some($crate::capi::capi_stream_get_latency::<$stm>),
54            stream_get_input_latency: Some($crate::capi::capi_stream_get_input_latency::<$stm>),
55            stream_set_volume: Some($crate::capi::capi_stream_set_volume::<$stm>),
56            stream_set_name: Some($crate::capi::capi_stream_set_name::<$stm>),
57            stream_get_current_device: Some($crate::capi::capi_stream_get_current_device::<$stm>),
58            stream_set_input_mute: Some($crate::capi::capi_stream_set_input_mute::<$stm>),
59            stream_set_input_processing_params:
60                Some($crate::capi::capi_stream_set_input_processing_params::<$stm>),
61            stream_device_destroy: Some($crate::capi::capi_stream_device_destroy::<$stm>),
62            stream_register_device_changed_callback:
63                Some($crate::capi::capi_stream_register_device_changed_callback::<$stm>),
64            register_device_collection_changed:
65                Some($crate::capi::capi_register_device_collection_changed::<$ctx>)
66        }));
67
68/// # Safety
69///
70/// Entry point from C code.
71///
72/// This function is unsafe because it dereferences the given `c` and `context` pointers.
73/// The caller should ensure those pointers are valid.
74pub unsafe extern "C" fn capi_init<CTX: ContextOps>(
75    c: *mut *mut ffi::cubeb,
76    context_name: *const c_char,
77) -> c_int {
78    let anchor = &();
79    let context_name = opt_cstr(anchor, context_name);
80    let context = _try!(CTX::init(context_name));
81    c.write(Box::into_raw(context) as *mut _);
82    ffi::CUBEB_OK
83}
84
85/// # Safety
86///
87/// Entry point from C code.
88///
89/// This function is unsafe because it dereferences the given `c` pointer.
90/// The caller should ensure that pointer is valid.
91pub unsafe extern "C" fn capi_get_backend_id<CTX: ContextOps>(c: *mut ffi::cubeb) -> *const c_char {
92    let ctx = &mut *(c as *mut CTX);
93    ctx.backend_id().as_ptr()
94}
95
96/// # Safety
97///
98/// Entry point from C code.
99///
100/// This function is unsafe because it dereferences the given `c` and `max_channels` pointers.
101/// The caller should ensure those pointers are valid.
102pub unsafe extern "C" fn capi_get_max_channel_count<CTX: ContextOps>(
103    c: *mut ffi::cubeb,
104    max_channels: *mut u32,
105) -> c_int {
106    let ctx = &mut *(c as *mut CTX);
107
108    *max_channels = _try!(ctx.max_channel_count());
109    ffi::CUBEB_OK
110}
111
112/// # Safety
113///
114/// Entry point from C code.
115///
116/// This function is unsafe because it dereferences the given `c` and `latency_frames` pointers.
117/// The caller should ensure those pointers are valid.
118pub unsafe extern "C" fn capi_get_min_latency<CTX: ContextOps>(
119    c: *mut ffi::cubeb,
120    param: ffi::cubeb_stream_params,
121    latency_frames: *mut u32,
122) -> c_int {
123    let ctx = &mut *(c as *mut CTX);
124    let param = StreamParams::from(param);
125    *latency_frames = _try!(ctx.min_latency(param));
126    ffi::CUBEB_OK
127}
128
129/// # Safety
130///
131/// Entry point from C code.
132///
133/// This function is unsafe because it dereferences the given `c` and `rate` pointers.
134/// The caller should ensure those pointers are valid.
135pub unsafe extern "C" fn capi_get_preferred_sample_rate<CTX: ContextOps>(
136    c: *mut ffi::cubeb,
137    rate: *mut u32,
138) -> c_int {
139    let ctx = &mut *(c as *mut CTX);
140
141    *rate = _try!(ctx.preferred_sample_rate());
142    ffi::CUBEB_OK
143}
144
145/// # Safety
146///
147/// Entry point from C code.
148///
149/// This function is unsafe because it dereferences the given `c` and `params` pointers.
150/// The caller should ensure those pointers are valid.
151pub unsafe extern "C" fn capi_get_supported_input_processing_params<CTX: ContextOps>(
152    c: *mut ffi::cubeb,
153    params: *mut ffi::cubeb_input_processing_params,
154) -> c_int {
155    let ctx = &mut *(c as *mut CTX);
156    *params = _try!(ctx.supported_input_processing_params()).bits();
157    ffi::CUBEB_OK
158}
159
160/// # Safety
161///
162/// Entry point from C code.
163///
164/// This function is unsafe because it dereferences the given `c` and `collection` pointers.
165/// The caller should ensure those pointers are valid.
166pub unsafe extern "C" fn capi_enumerate_devices<CTX: ContextOps>(
167    c: *mut ffi::cubeb,
168    devtype: ffi::cubeb_device_type,
169    collection: *mut ffi::cubeb_device_collection,
170) -> c_int {
171    debug_assert!(!c.is_null());
172    debug_assert!(!collection.is_null());
173
174    let ctx = &mut *(c as *mut CTX);
175    let devtype = DeviceType::from_bits_truncate(devtype);
176
177    let coll = Box::into_raw(_try!(ctx.enumerate_devices(devtype)));
178    collection.write(ffi::cubeb_device_collection {
179        device: coll as *mut _,
180        count: coll.len(),
181    });
182    ffi::CUBEB_OK
183}
184
185/// # Safety
186///
187/// Entry point from C code.
188///
189/// This function is unsafe because it dereferences the given `c` and `collection` pointers.
190/// The caller should ensure those pointers are valid.
191pub unsafe extern "C" fn capi_device_collection_destroy<CTX: ContextOps>(
192    c: *mut ffi::cubeb,
193    collection: *mut ffi::cubeb_device_collection,
194) -> c_int {
195    debug_assert!(!c.is_null());
196    debug_assert!(!collection.is_null());
197
198    let ctx = &mut *(c as *mut CTX);
199    let collection = &mut *(collection);
200
201    let coll = Box::from_raw(std::ptr::slice_from_raw_parts_mut(
202        collection.device as *mut DeviceInfo,
203        collection.count,
204    ));
205    _try!(ctx.device_collection_destroy(coll));
206    collection.device = std::ptr::null_mut();
207    collection.count = 0;
208    ffi::CUBEB_OK
209}
210
211/// # Safety
212///
213/// Entry point from C code.
214///
215/// This function is unsafe because it dereferences the given `c` pointer.
216/// The caller should ensure that pointer is valid.
217pub unsafe extern "C" fn capi_destroy<CTX>(c: *mut ffi::cubeb) {
218    let _: Box<CTX> = Box::from_raw(c as *mut _);
219}
220
221/// # Safety
222///
223/// Entry point from C code.
224///
225/// This function is unsafe because it dereferences the given `c`, `s`, `stream_name`, `input_stream_params`,
226/// `output_stream_params`, `data_callback`, `state_callback`, and `user_ptr` pointers.
227/// The caller should ensure those pointers are valid.
228pub unsafe extern "C" fn capi_stream_init<CTX: ContextOps>(
229    c: *mut ffi::cubeb,
230    s: *mut *mut ffi::cubeb_stream,
231    stream_name: *const c_char,
232    input_device: ffi::cubeb_devid,
233    input_stream_params: *mut ffi::cubeb_stream_params,
234    output_device: ffi::cubeb_devid,
235    output_stream_params: *mut ffi::cubeb_stream_params,
236    latency_frames: u32,
237    data_callback: ffi::cubeb_data_callback,
238    state_callback: ffi::cubeb_state_callback,
239    user_ptr: *mut c_void,
240) -> c_int {
241    let ctx = &mut *(c as *mut CTX);
242    let anchor = &(); // for lifetime of stream_name as CStr
243
244    let input_stream_params = as_opt_ref!(input_stream_params);
245    let output_stream_params = as_opt_ref!(output_stream_params);
246
247    let stream = _try!(ctx.stream_init(
248        opt_cstr(anchor, stream_name),
249        input_device,
250        input_stream_params,
251        output_device,
252        output_stream_params,
253        latency_frames,
254        data_callback,
255        state_callback,
256        user_ptr
257    ));
258    *s = stream.as_ptr();
259    // Leaking pointer across C FFI
260    mem::forget(stream);
261    ffi::CUBEB_OK
262}
263
264/// # Safety
265///
266/// Entry point from C code.
267///
268/// This function is unsafe because it dereferences the given `s` pointer.
269/// The caller should ensure that pointer is valid.
270pub unsafe extern "C" fn capi_stream_destroy<STM>(s: *mut ffi::cubeb_stream) {
271    let _ = Box::from_raw(s as *mut STM);
272}
273
274/// # Safety
275///
276/// Entry point from C code.
277///
278/// This function is unsafe because it dereferences the given `s` pointer.
279/// The caller should ensure that pointer is valid.
280pub unsafe extern "C" fn capi_stream_start<STM: StreamOps>(s: *mut ffi::cubeb_stream) -> c_int {
281    let stm = &mut *(s as *mut STM);
282
283    _try!(stm.start());
284    ffi::CUBEB_OK
285}
286
287/// # Safety
288///
289/// Entry point from C code.
290///
291/// This function is unsafe because it dereferences the given `s` pointer.
292/// The caller should ensure that pointer is valid.
293pub unsafe extern "C" fn capi_stream_stop<STM: StreamOps>(s: *mut ffi::cubeb_stream) -> c_int {
294    let stm = &mut *(s as *mut STM);
295
296    _try!(stm.stop());
297    ffi::CUBEB_OK
298}
299
300/// # Safety
301///
302/// Entry point from C code.
303///
304/// This function is unsafe because it dereferences the given `s` and `position` pointers.
305/// The caller should ensure those pointers are valid.
306pub unsafe extern "C" fn capi_stream_get_position<STM: StreamOps>(
307    s: *mut ffi::cubeb_stream,
308    position: *mut u64,
309) -> c_int {
310    let stm = &mut *(s as *mut STM);
311
312    *position = _try!(stm.position());
313    ffi::CUBEB_OK
314}
315
316/// # Safety
317///
318/// Entry point from C code.
319///
320/// This function is unsafe because it dereferences the given `s` and `latency` pointers.
321/// The caller should ensure those pointers are valid.
322pub unsafe extern "C" fn capi_stream_get_latency<STM: StreamOps>(
323    s: *mut ffi::cubeb_stream,
324    latency: *mut u32,
325) -> c_int {
326    let stm = &mut *(s as *mut STM);
327
328    *latency = _try!(stm.latency());
329    ffi::CUBEB_OK
330}
331
332/// # Safety
333///
334/// Entry point from C code.
335///
336/// This function is unsafe because it dereferences the given `s` and `latency` pointers.
337/// The caller should ensure those pointers are valid.
338pub unsafe extern "C" fn capi_stream_get_input_latency<STM: StreamOps>(
339    s: *mut ffi::cubeb_stream,
340    latency: *mut u32,
341) -> c_int {
342    let stm = &mut *(s as *mut STM);
343
344    *latency = _try!(stm.input_latency());
345    ffi::CUBEB_OK
346}
347
348/// # Safety
349///
350/// Entry point from C code.
351///
352/// This function is unsafe because it dereferences the given `s` pointer.
353/// The caller should ensure that pointer is valid.
354pub unsafe extern "C" fn capi_stream_set_volume<STM: StreamOps>(
355    s: *mut ffi::cubeb_stream,
356    volume: f32,
357) -> c_int {
358    let stm = &mut *(s as *mut STM);
359
360    _try!(stm.set_volume(volume));
361    ffi::CUBEB_OK
362}
363
364/// # Safety
365///
366/// Entry point from C code.
367///
368/// This function is unsafe because it dereferences the given `s` and `name` pointers.
369/// The caller should ensure those pointers are valid.
370pub unsafe extern "C" fn capi_stream_set_name<STM: StreamOps>(
371    s: *mut ffi::cubeb_stream,
372    name: *const c_char,
373) -> c_int {
374    let stm = &mut *(s as *mut STM);
375    let anchor = &();
376    if let Some(name) = opt_cstr(anchor, name) {
377        _try!(stm.set_name(name));
378        ffi::CUBEB_OK
379    } else {
380        ffi::CUBEB_ERROR_INVALID_PARAMETER
381    }
382}
383
384/// # Safety
385///
386/// Entry point from C code.
387///
388/// This function is unsafe because it dereferences the given `s` and `device` pointers.
389/// The caller should ensure those pointers are valid.
390pub unsafe extern "C" fn capi_stream_get_current_device<STM: StreamOps>(
391    s: *mut ffi::cubeb_stream,
392    device: *mut *mut ffi::cubeb_device,
393) -> i32 {
394    let stm = &mut *(s as *mut STM);
395
396    *device = _try!(stm.current_device()).as_ptr();
397    ffi::CUBEB_OK
398}
399
400/// # Safety
401///
402/// Entry point from C code.
403///
404/// This function is unsafe because it dereferences the given `s` pointer.
405/// The caller should ensure those pointers are valid.
406pub unsafe extern "C" fn capi_stream_set_input_mute<STM: StreamOps>(
407    s: *mut ffi::cubeb_stream,
408    mute: c_int,
409) -> c_int {
410    let stm = &mut *(s as *mut STM);
411    _try!(stm.set_input_mute(mute != 0));
412    ffi::CUBEB_OK
413}
414
415/// # Safety
416///
417/// Entry point from C code.
418///
419/// This function is unsafe because it dereferences the given `s` pointer.
420/// The caller should ensure those pointers are valid.
421pub unsafe extern "C" fn capi_stream_set_input_processing_params<STM: StreamOps>(
422    s: *mut ffi::cubeb_stream,
423    params: ffi::cubeb_input_processing_params,
424) -> c_int {
425    let stm = &mut *(s as *mut STM);
426    _try!(stm.set_input_processing_params(InputProcessingParams::from_bits_truncate(params)));
427    ffi::CUBEB_OK
428}
429
430/// # Safety
431///
432/// Entry point from C code.
433///
434/// This function is unsafe because it dereferences the given `s` and `device` pointers.
435/// The caller should ensure those pointers are valid.
436pub unsafe extern "C" fn capi_stream_device_destroy<STM: StreamOps>(
437    s: *mut ffi::cubeb_stream,
438    device: *mut ffi::cubeb_device,
439) -> c_int {
440    let stm = &mut *(s as *mut STM);
441    if device.is_null() {
442        return ffi::CUBEB_ERROR_INVALID_PARAMETER;
443    }
444    let device = DeviceRef::from_ptr(device);
445    let _ = stm.device_destroy(device);
446    ffi::CUBEB_OK
447}
448
449/// # Safety
450///
451/// Entry point from C code.
452///
453/// This function is unsafe because it dereferences the given `s` and `device_changed_callback` pointers.
454/// The caller should ensure those pointers are valid.
455pub unsafe extern "C" fn capi_stream_register_device_changed_callback<STM: StreamOps>(
456    s: *mut ffi::cubeb_stream,
457    device_changed_callback: ffi::cubeb_device_changed_callback,
458) -> c_int {
459    let stm = &mut *(s as *mut STM);
460
461    _try!(stm.register_device_changed_callback(device_changed_callback));
462    ffi::CUBEB_OK
463}
464
465/// # Safety
466///
467/// Entry point from C code.
468///
469/// This function is unsafe because it dereferences the given `s`, `collection_changed_callback`, and
470/// `user_ptr` pointers.
471/// The caller should ensure those pointers are valid.
472pub unsafe extern "C" fn capi_register_device_collection_changed<CTX: ContextOps>(
473    c: *mut ffi::cubeb,
474    devtype: ffi::cubeb_device_type,
475    collection_changed_callback: ffi::cubeb_device_collection_changed_callback,
476    user_ptr: *mut c_void,
477) -> i32 {
478    let ctx = &mut *(c as *mut CTX);
479    let devtype = DeviceType::from_bits_truncate(devtype);
480    _try!(ctx.register_device_collection_changed(devtype, collection_changed_callback, user_ptr));
481    ffi::CUBEB_OK
482}
483
484fn opt_cstr<T>(_anchor: &T, ptr: *const c_char) -> Option<&CStr> {
485    if ptr.is_null() {
486        None
487    } else {
488        Some(unsafe { CStr::from_ptr(ptr) })
489    }
490}