diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/Cargo.toml.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/Cargo.toml --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/Cargo.toml.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/Cargo.toml 2017-08-04 13:37:46.383821740 +0200 @@ -7,7 +7,11 @@ description = "Cubeb backed for PulseAud [features] pulse-dlopen = ["pulse-ffi/dlopen"] +[lib] +crate-type = ["staticlib", "rlib"] + [dependencies] cubeb-ffi = { path = "cubeb-ffi" } pulse-ffi = { path = "pulse-ffi" } +pulse = { path = "pulse-rs" } semver = "^0.6" diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs 2017-08-04 13:37:46.384821737 +0200 @@ -11,45 +11,45 @@ pub enum Context {} pub enum Stream {} // These need to match cubeb_sample_format -pub const SAMPLE_S16LE: c_int = 0; -pub const SAMPLE_S16BE: c_int = 1; -pub const SAMPLE_FLOAT32LE: c_int = 2; -pub const SAMPLE_FLOAT32BE: c_int = 3; pub type SampleFormat = c_int; +pub const SAMPLE_S16LE: SampleFormat = 0; +pub const SAMPLE_S16BE: SampleFormat = 1; +pub const SAMPLE_FLOAT32LE: SampleFormat = 2; +pub const SAMPLE_FLOAT32BE: SampleFormat = 3; #[cfg(target_endian = "little")] -pub const SAMPLE_S16NE: c_int = SAMPLE_S16LE; +pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16LE; #[cfg(target_endian = "little")] -pub const SAMPLE_FLOAT32NE: c_int = SAMPLE_FLOAT32LE; +pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32LE; #[cfg(target_endian = "big")] -pub const SAMPLE_S16NE: c_int = SAMPLE_S16BE; +pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16BE; #[cfg(target_endian = "big")] -pub const SAMPLE_FLOAT32NE: c_int = SAMPLE_FLOAT32BE; +pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32BE; pub type DeviceId = *const c_void; // These need to match cubeb_channel_layout -pub const LAYOUT_UNDEFINED: c_int = 0; -pub const LAYOUT_DUAL_MONO: c_int = 1; -pub const LAYOUT_DUAL_MONO_LFE: c_int = 2; -pub const LAYOUT_MONO: c_int = 3; -pub const LAYOUT_MONO_LFE: c_int = 4; -pub const LAYOUT_STEREO: c_int = 5; -pub const LAYOUT_STEREO_LFE: c_int = 6; -pub const LAYOUT_3F: c_int = 7; -pub const LAYOUT_3F_LFE: c_int = 8; -pub const LAYOUT_2F1: c_int = 9; -pub const LAYOUT_2F1_LFE: c_int = 10; -pub const LAYOUT_3F1: c_int = 11; -pub const LAYOUT_3F1_LFE: c_int = 12; -pub const LAYOUT_2F2: c_int = 13; -pub const LAYOUT_2F2_LFE: c_int = 14; -pub const LAYOUT_3F2: c_int = 15; -pub const LAYOUT_3F2_LFE: c_int = 16; -pub const LAYOUT_3F3R_LFE: c_int = 17; -pub const LAYOUT_3F4_LFE: c_int = 18; -pub const LAYOUT_MAX: c_int = 19; pub type ChannelLayout = c_int; +pub const LAYOUT_UNDEFINED: ChannelLayout = 0; +pub const LAYOUT_DUAL_MONO: ChannelLayout = 1; +pub const LAYOUT_DUAL_MONO_LFE: ChannelLayout = 2; +pub const LAYOUT_MONO: ChannelLayout = 3; +pub const LAYOUT_MONO_LFE: ChannelLayout = 4; +pub const LAYOUT_STEREO: ChannelLayout = 5; +pub const LAYOUT_STEREO_LFE: ChannelLayout = 6; +pub const LAYOUT_3F: ChannelLayout = 7; +pub const LAYOUT_3F_LFE: ChannelLayout = 8; +pub const LAYOUT_2F1: ChannelLayout = 9; +pub const LAYOUT_2F1_LFE: ChannelLayout = 10; +pub const LAYOUT_3F1: ChannelLayout = 11; +pub const LAYOUT_3F1_LFE: ChannelLayout = 12; +pub const LAYOUT_2F2: ChannelLayout = 13; +pub const LAYOUT_2F2_LFE: ChannelLayout = 14; +pub const LAYOUT_3F2: ChannelLayout = 15; +pub const LAYOUT_3F2_LFE: ChannelLayout = 16; +pub const LAYOUT_3F3R_LFE: ChannelLayout = 17; +pub const LAYOUT_3F4_LFE: ChannelLayout = 18; +pub const LAYOUT_MAX: ChannelLayout = 256; #[repr(C)] #[derive(Clone, Copy, Debug)] @@ -77,11 +77,11 @@ impl Default for Device { } // These need to match cubeb_state -pub const STATE_STARTED: c_int = 0; -pub const STATE_STOPPED: c_int = 1; -pub const STATE_DRAINED: c_int = 2; -pub const STATE_ERROR: c_int = 3; pub type State = c_int; +pub const STATE_STARTED: State = 0; +pub const STATE_STOPPED: State = 1; +pub const STATE_DRAINED: State = 2; +pub const STATE_ERROR: State = 3; pub const OK: i32 = 0; pub const ERROR: i32 = -1; @@ -249,32 +249,42 @@ pub struct LayoutMap { } // cubeb_mixer.h +pub type Channel = c_int; // These need to match cubeb_channel -pub const CHANNEL_INVALID: c_int = -1; -pub const CHANNEL_MONO: c_int = 0; -pub const CHANNEL_LEFT: c_int = 1; -pub const CHANNEL_RIGHT: c_int = 2; -pub const CHANNEL_CENTER: c_int = 3; -pub const CHANNEL_LS: c_int = 4; -pub const CHANNEL_RS: c_int = 5; -pub const CHANNEL_RLS: c_int = 6; -pub const CHANNEL_RCENTER: c_int = 7; -pub const CHANNEL_RRS: c_int = 8; -pub const CHANNEL_LFE: c_int = 9; -pub const CHANNEL_MAX: c_int = 256; -pub type Channel = c_int; +pub const CHANNEL_INVALID: Channel = -1; +pub const CHANNEL_MONO: Channel = 0; +pub const CHANNEL_LEFT: Channel = 1; +pub const CHANNEL_RIGHT: Channel = 2; +pub const CHANNEL_CENTER: Channel = 3; +pub const CHANNEL_LS: Channel = 4; +pub const CHANNEL_RS: Channel = 5; +pub const CHANNEL_RLS: Channel = 6; +pub const CHANNEL_RCENTER: Channel = 7; +pub const CHANNEL_RRS: Channel = 8; +pub const CHANNEL_LFE: Channel = 9; +pub const CHANNEL_MAX: Channel = 10; #[repr(C)] +#[derive(Clone, Copy, Debug)] pub struct ChannelMap { pub channels: c_uint, - pub map: [Channel; 256], + pub map: [Channel; CHANNEL_MAX as usize], } impl ::std::default::Default for ChannelMap { fn default() -> Self { ChannelMap { channels: 0, - map: unsafe { ::std::mem::zeroed() }, + map: [CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID, + CHANNEL_INVALID], } } } diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_funcs.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_funcs.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_funcs.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_funcs.rs 2017-08-04 13:37:46.384821737 +0200 @@ -8,8 +8,8 @@ macro_rules! cstr { #[cfg(not(feature = "dlopen"))] mod static_fns { - use std::os::raw::{c_char, c_double, c_int, c_float, c_uint, c_void}; use super::*; + use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void}; #[link(name = "pulse")] extern "C" { @@ -62,6 +62,7 @@ mod static_fns { userdata: *mut c_void) -> *mut pa_operation; pub fn pa_context_set_state_callback(c: *mut pa_context, cb: pa_context_notify_cb_t, userdata: *mut c_void); + pub fn pa_context_errno(c: *mut pa_context) -> c_int; pub fn pa_context_set_subscribe_callback(c: *mut pa_context, cb: pa_context_subscribe_cb_t, userdata: *mut c_void); @@ -70,6 +71,7 @@ mod static_fns { cb: pa_context_success_cb_t, userdata: *mut c_void) -> *mut pa_operation; + pub fn pa_context_ref(c: *mut pa_context) -> *mut pa_context; pub fn pa_context_unref(c: *mut pa_context); pub fn pa_cvolume_set(a: *mut pa_cvolume, channels: c_uint, v: pa_volume_t) -> *mut pa_cvolume; pub fn pa_cvolume_set_balance(v: *mut pa_cvolume, @@ -80,12 +82,20 @@ mod static_fns { pub fn pa_mainloop_api_once(m: *mut pa_mainloop_api, callback: pa_mainloop_api_once_cb_t, userdata: *mut c_void); - pub fn pa_operation_get_state(o: *const pa_operation) -> pa_operation_state_t; + pub fn pa_strerror(error: pa_error_code_t) -> *const c_char; + pub fn pa_operation_ref(o: *mut pa_operation) -> *mut pa_operation; pub fn pa_operation_unref(o: *mut pa_operation); + pub fn pa_operation_cancel(o: *mut pa_operation); + pub fn pa_operation_get_state(o: *const pa_operation) -> pa_operation_state_t; + pub fn pa_operation_set_state_callback(o: *mut pa_operation, + cb: pa_operation_notify_cb_t, + userdata: *mut c_void); pub fn pa_proplist_gets(p: *mut pa_proplist, key: *const c_char) -> *const c_char; pub fn pa_rtclock_now() -> pa_usec_t; pub fn pa_stream_begin_write(p: *mut pa_stream, data: *mut *mut c_void, nbytes: *mut usize) -> c_int; pub fn pa_stream_cancel_write(p: *mut pa_stream) -> c_int; + pub fn pa_stream_is_suspended(s: *const pa_stream) -> c_int; + pub fn pa_stream_is_corked(s: *const pa_stream) -> c_int; pub fn pa_stream_connect_playback(s: *mut pa_stream, dev: *const c_char, attr: *const pa_buffer_attr, @@ -112,6 +122,7 @@ mod static_fns { pub fn pa_stream_get_latency(s: *const pa_stream, r_usec: *mut pa_usec_t, negative: *mut c_int) -> c_int; pub fn pa_stream_get_sample_spec(s: *const pa_stream) -> *const pa_sample_spec; pub fn pa_stream_get_state(p: *const pa_stream) -> pa_stream_state_t; + pub fn pa_stream_get_context(s: *const pa_stream) -> *mut pa_context; pub fn pa_stream_get_time(s: *const pa_stream, r_usec: *mut pa_usec_t) -> c_int; pub fn pa_stream_new(c: *mut pa_context, name: *const c_char, @@ -123,6 +134,7 @@ mod static_fns { pub fn pa_stream_set_state_callback(s: *mut pa_stream, cb: pa_stream_notify_cb_t, userdata: *mut c_void); pub fn pa_stream_set_write_callback(p: *mut pa_stream, cb: pa_stream_request_cb_t, userdata: *mut c_void); pub fn pa_stream_set_read_callback(p: *mut pa_stream, cb: pa_stream_request_cb_t, userdata: *mut c_void); + pub fn pa_stream_ref(s: *mut pa_stream) -> *mut pa_stream; pub fn pa_stream_unref(s: *mut pa_stream); pub fn pa_stream_update_timing_info(p: *mut pa_stream, cb: pa_stream_success_cb_t, @@ -148,8 +160,6 @@ mod static_fns { pub fn pa_threaded_mainloop_unlock(m: *mut pa_threaded_mainloop); pub fn pa_threaded_mainloop_wait(m: *mut pa_threaded_mainloop); pub fn pa_usec_to_bytes(t: pa_usec_t, spec: *const pa_sample_spec) -> usize; - pub fn pa_xfree(ptr: *mut c_void); - pub fn pa_xstrdup(str: *const c_char) -> *mut c_char; pub fn pa_xrealloc(ptr: *mut c_void, size: usize) -> *mut c_void; } } @@ -159,9 +169,9 @@ pub use self::static_fns::*; #[cfg(feature = "dlopen")] mod dynamic_fns { - use std::os::raw::{c_char, c_double, c_int, c_float, c_uint, c_void}; - use libc::{dlclose, dlopen, dlsym, RTLD_LAZY}; use super::*; + use libc::{RTLD_LAZY, dlclose, dlopen, dlsym}; + use std::os::raw::{c_char, c_double, c_float, c_int, c_uint, c_void}; #[derive(Debug)] pub struct LibLoader { @@ -287,6 +297,13 @@ mod dynamic_fns { } fp }; + PA_CONTEXT_ERRNO = { + let fp = dlsym(h, cstr!("pa_context_errno")); + if fp.is_null() { + return None; + } + fp + }; PA_CONTEXT_SET_SUBSCRIBE_CALLBACK = { let fp = dlsym(h, cstr!("pa_context_set_subscribe_callback")); if fp.is_null() { @@ -301,6 +318,13 @@ mod dynamic_fns { } fp }; + PA_CONTEXT_REF = { + let fp = dlsym(h, cstr!("pa_context_ref")); + if fp.is_null() { + return None; + } + fp + }; PA_CONTEXT_UNREF = { let fp = dlsym(h, cstr!("pa_context_unref")); if fp.is_null() { @@ -336,8 +360,15 @@ mod dynamic_fns { } fp }; - PA_OPERATION_GET_STATE = { - let fp = dlsym(h, cstr!("pa_operation_get_state")); + PA_STRERROR = { + let fp = dlsym(h, cstr!("pa_strerror")); + if fp.is_null() { + return None; + } + fp + }; + PA_OPERATION_REF = { + let fp = dlsym(h, cstr!("pa_operation_ref")); if fp.is_null() { return None; } @@ -350,6 +381,27 @@ mod dynamic_fns { } fp }; + PA_OPERATION_CANCEL = { + let fp = dlsym(h, cstr!("pa_operation_cancel")); + if fp.is_null() { + return None; + } + fp + }; + PA_OPERATION_GET_STATE = { + let fp = dlsym(h, cstr!("pa_operation_get_state")); + if fp.is_null() { + return None; + } + fp + }; + PA_OPERATION_SET_STATE_CALLBACK = { + let fp = dlsym(h, cstr!("pa_operation_set_state_callback")); + if fp.is_null() { + return None; + } + fp + }; PA_PROPLIST_GETS = { let fp = dlsym(h, cstr!("pa_proplist_gets")); if fp.is_null() { @@ -378,6 +430,20 @@ mod dynamic_fns { } fp }; + PA_STREAM_IS_SUSPENDED = { + let fp = dlsym(h, cstr!("pa_stream_is_suspended")); + if fp.is_null() { + return None; + } + fp + }; + PA_STREAM_IS_CORKED = { + let fp = dlsym(h, cstr!("pa_stream_is_corked")); + if fp.is_null() { + return None; + } + fp + }; PA_STREAM_CONNECT_PLAYBACK = { let fp = dlsym(h, cstr!("pa_stream_connect_playback")); if fp.is_null() { @@ -462,6 +528,13 @@ mod dynamic_fns { } fp }; + PA_STREAM_GET_CONTEXT = { + let fp = dlsym(h, cstr!("pa_stream_get_context")); + if fp.is_null() { + return None; + } + fp + }; PA_STREAM_GET_TIME = { let fp = dlsym(h, cstr!("pa_stream_get_time")); if fp.is_null() { @@ -511,6 +584,13 @@ mod dynamic_fns { } fp }; + PA_STREAM_REF = { + let fp = dlsym(h, cstr!("pa_stream_ref")); + if fp.is_null() { + return None; + } + fp + }; PA_STREAM_UNREF = { let fp = dlsym(h, cstr!("pa_stream_unref")); if fp.is_null() { @@ -623,20 +703,6 @@ mod dynamic_fns { } fp }; - PA_XFREE = { - let fp = dlsym(h, cstr!("pa_xfree")); - if fp.is_null() { - return None; - } - fp - }; - PA_XSTRDUP = { - let fp = dlsym(h, cstr!("pa_xstrdup")); - if fp.is_null() { - return None; - } - fp - }; PA_XREALLOC = { let fp = dlsym(h, cstr!("pa_xrealloc")); if fp.is_null() { @@ -837,6 +903,12 @@ mod dynamic_fns { *mut c_void)>(PA_CONTEXT_SET_STATE_CALLBACK))(c, cb, userdata) } + static mut PA_CONTEXT_ERRNO: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_context_errno(c: *mut pa_context) -> c_int { + (::std::mem::transmute::<_, extern "C" fn(*mut pa_context) -> c_int>(PA_CONTEXT_ERRNO))(c) + } + static mut PA_CONTEXT_SET_SUBSCRIBE_CALLBACK: *mut ::libc::c_void = 0 as *mut _; #[inline] pub unsafe fn pa_context_set_subscribe_callback(c: *mut pa_context, @@ -863,6 +935,12 @@ mod dynamic_fns { -> *mut pa_operation>(PA_CONTEXT_SUBSCRIBE))(c, m, cb, userdata) } + static mut PA_CONTEXT_REF: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_context_ref(c: *mut pa_context) -> *mut pa_context { + (::std::mem::transmute::<_, extern "C" fn(*mut pa_context) -> *mut pa_context>(PA_CONTEXT_REF))(c) + } + static mut PA_CONTEXT_UNREF: *mut ::libc::c_void = 0 as *mut _; #[inline] pub unsafe fn pa_context_unref(c: *mut pa_context) { @@ -907,6 +985,30 @@ mod dynamic_fns { *mut c_void)>(PA_MAINLOOP_API_ONCE))(m, callback, userdata) } + static mut PA_STRERROR: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_strerror(error: pa_error_code_t) -> *const c_char { + (::std::mem::transmute::<_, extern "C" fn(pa_error_code_t) -> *const c_char>(PA_STRERROR))(error) + } + + static mut PA_OPERATION_REF: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_operation_ref(o: *mut pa_operation) -> *mut pa_operation { + (::std::mem::transmute::<_, extern "C" fn(*mut pa_operation) -> *mut pa_operation>(PA_OPERATION_REF))(o) + } + + static mut PA_OPERATION_UNREF: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_operation_unref(o: *mut pa_operation) { + (::std::mem::transmute::<_, extern "C" fn(*mut pa_operation)>(PA_OPERATION_UNREF))(o) + } + + static mut PA_OPERATION_CANCEL: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_operation_cancel(o: *mut pa_operation) { + (::std::mem::transmute::<_, extern "C" fn(*mut pa_operation)>(PA_OPERATION_CANCEL))(o) + } + static mut PA_OPERATION_GET_STATE: *mut ::libc::c_void = 0 as *mut _; #[inline] pub unsafe fn pa_operation_get_state(o: *const pa_operation) -> pa_operation_state_t { @@ -915,10 +1017,15 @@ mod dynamic_fns { -> pa_operation_state_t>(PA_OPERATION_GET_STATE))(o) } - static mut PA_OPERATION_UNREF: *mut ::libc::c_void = 0 as *mut _; + static mut PA_OPERATION_SET_STATE_CALLBACK: *mut ::libc::c_void = 0 as *mut _; #[inline] - pub unsafe fn pa_operation_unref(o: *mut pa_operation) { - (::std::mem::transmute::<_, extern "C" fn(*mut pa_operation)>(PA_OPERATION_UNREF))(o) + pub unsafe fn pa_operation_set_state_callback(o: *mut pa_operation, + cb: pa_operation_notify_cb_t, + userdata: *mut c_void) { + (::std::mem::transmute::<_, + extern "C" fn(*mut pa_operation, + pa_operation_notify_cb_t, + *mut c_void)>(PA_OPERATION_SET_STATE_CALLBACK))(o, cb, userdata) } static mut PA_PROPLIST_GETS: *mut ::libc::c_void = 0 as *mut _; @@ -951,6 +1058,18 @@ mod dynamic_fns { (::std::mem::transmute::<_, extern "C" fn(*mut pa_stream) -> c_int>(PA_STREAM_CANCEL_WRITE))(p) } + static mut PA_STREAM_IS_SUSPENDED: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_stream_is_suspended(s: *const pa_stream) -> c_int { + (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> c_int>(PA_STREAM_IS_SUSPENDED))(s) + } + + static mut PA_STREAM_IS_CORKED: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_stream_is_corked(s: *const pa_stream) -> c_int { + (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> c_int>(PA_STREAM_IS_CORKED))(s) + } + static mut PA_STREAM_CONNECT_PLAYBACK: *mut ::libc::c_void = 0 as *mut _; #[inline] pub unsafe fn pa_stream_connect_playback(s: *mut pa_stream, @@ -1066,6 +1185,12 @@ mod dynamic_fns { (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> pa_stream_state_t>(PA_STREAM_GET_STATE))(p) } + static mut PA_STREAM_GET_CONTEXT: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_stream_get_context(s: *const pa_stream) -> *mut pa_context { + (::std::mem::transmute::<_, extern "C" fn(*const pa_stream) -> *mut pa_context>(PA_STREAM_GET_CONTEXT))(s) + } + static mut PA_STREAM_GET_TIME: *mut ::libc::c_void = 0 as *mut _; #[inline] pub unsafe fn pa_stream_get_time(s: *const pa_stream, r_usec: *mut pa_usec_t) -> c_int { @@ -1132,6 +1257,12 @@ mod dynamic_fns { *mut c_void)>(PA_STREAM_SET_READ_CALLBACK))(p, cb, userdata) } + static mut PA_STREAM_REF: *mut ::libc::c_void = 0 as *mut _; + #[inline] + pub unsafe fn pa_stream_ref(s: *mut pa_stream) -> *mut pa_stream { + (::std::mem::transmute::<_, extern "C" fn(*mut pa_stream) -> *mut pa_stream>(PA_STREAM_REF))(s) + } + static mut PA_STREAM_UNREF: *mut ::libc::c_void = 0 as *mut _; #[inline] pub unsafe fn pa_stream_unref(s: *mut pa_stream) { @@ -1253,18 +1384,6 @@ mod dynamic_fns { spec) } - static mut PA_XFREE: *mut ::libc::c_void = 0 as *mut _; - #[inline] - pub unsafe fn pa_xfree(ptr: *mut c_void) { - (::std::mem::transmute::<_, extern "C" fn(*mut c_void)>(PA_XFREE))(ptr) - } - - static mut PA_XSTRDUP: *mut ::libc::c_void = 0 as *mut _; - #[inline] - pub unsafe fn pa_xstrdup(str: *const c_char) -> *mut c_char { - (::std::mem::transmute::<_, extern "C" fn(*const c_char) -> *mut c_char>(PA_XSTRDUP))(str) - } - static mut PA_XREALLOC: *mut ::libc::c_void = 0 as *mut _; #[inline] pub unsafe fn pa_xrealloc(ptr: *mut c_void, size: usize) -> *mut c_void { diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_types.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_types.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_types.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-ffi/src/ffi_types.rs 2017-08-04 13:37:46.384821737 +0200 @@ -1,6 +1,6 @@ #![allow(non_camel_case_types)] -use std::os::raw::{c_char, c_int, c_long, c_ulong, c_void}; +use std::os::raw::{c_char, c_int, c_long, c_uint, c_ulong, c_void}; /* automatically generated by rust-bindgen */ pub const PA_RATE_MAX: u32 = 48000 * 8; @@ -74,10 +74,10 @@ pub const PA_OPERATION_DONE: c_int = 1; pub const PA_OPERATION_CANCELLED: c_int = 2; pub type pa_operation_state_t = c_int; -pub const PA_CONTEXT_NOFLAGS: c_int = 0; -pub const PA_CONTEXT_NOAUTOSPAWN: c_int = 1; -pub const PA_CONTEXT_NOFAIL: c_int = 2; -pub type pa_context_flags_t = c_int; +pub const PA_CONTEXT_NOFLAGS: c_uint = 0; +pub const PA_CONTEXT_NOAUTOSPAWN: c_uint = 1; +pub const PA_CONTEXT_NOFAIL: c_uint = 2; +pub type pa_context_flags_t = c_uint; pub const PA_DIRECTION_OUTPUT: c_int = 1; pub const PA_DIRECTION_INPUT: c_int = 2; @@ -93,28 +93,28 @@ pub const PA_STREAM_RECORD: c_int = 2; pub const PA_STREAM_UPLOAD: c_int = 3; pub type pa_stream_direction_t = c_int; -pub const PA_STREAM_NOFLAGS: c_int = 0x0_0000; -pub const PA_STREAM_START_CORKED: c_int = 0x0_0001; -pub const PA_STREAM_INTERPOLATE_TIMING: c_int = 0x0_0002; -pub const PA_STREAM_NOT_MONOTONIC: c_int = 0x0_0004; -pub const PA_STREAM_AUTO_TIMING_UPDATE: c_int = 0x0_0008; -pub const PA_STREAM_NO_REMAP_CHANNELS: c_int = 0x0_0010; -pub const PA_STREAM_NO_REMIX_CHANNELS: c_int = 0x0_0020; -pub const PA_STREAM_FIX_FORMAT: c_int = 0x0_0040; -pub const PA_STREAM_FIX_RATE: c_int = 0x0_0080; -pub const PA_STREAM_FIX_CHANNELS: c_int = 0x0_0100; -pub const PA_STREAM_DONT_MOVE: c_int = 0x0_0200; -pub const PA_STREAM_VARIABLE_RATE: c_int = 0x0_0400; -pub const PA_STREAM_PEAK_DETECT: c_int = 0x0_0800; -pub const PA_STREAM_START_MUTED: c_int = 0x0_1000; -pub const PA_STREAM_ADJUST_LATENCY: c_int = 0x0_2000; -pub const PA_STREAM_EARLY_REQUESTS: c_int = 0x0_4000; -pub const PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND: c_int = 0x0_8000; -pub const PA_STREAM_START_UNMUTED: c_int = 0x1_0000; -pub const PA_STREAM_FAIL_ON_SUSPEND: c_int = 0x2_0000; -pub const PA_STREAM_RELATIVE_VOLUME: c_int = 0x4_0000; -pub const PA_STREAM_PASSTHROUGH: c_int = 0x8_0000; -pub type pa_stream_flags_t = c_int; +pub const PA_STREAM_NOFLAGS: c_uint = 0x0_0000; +pub const PA_STREAM_START_CORKED: c_uint = 0x0_0001; +pub const PA_STREAM_INTERPOLATE_TIMING: c_uint = 0x0_0002; +pub const PA_STREAM_NOT_MONOTONIC: c_uint = 0x0_0004; +pub const PA_STREAM_AUTO_TIMING_UPDATE: c_uint = 0x0_0008; +pub const PA_STREAM_NO_REMAP_CHANNELS: c_uint = 0x0_0010; +pub const PA_STREAM_NO_REMIX_CHANNELS: c_uint = 0x0_0020; +pub const PA_STREAM_FIX_FORMAT: c_uint = 0x0_0040; +pub const PA_STREAM_FIX_RATE: c_uint = 0x0_0080; +pub const PA_STREAM_FIX_CHANNELS: c_uint = 0x0_0100; +pub const PA_STREAM_DONT_MOVE: c_uint = 0x0_0200; +pub const PA_STREAM_VARIABLE_RATE: c_uint = 0x0_0400; +pub const PA_STREAM_PEAK_DETECT: c_uint = 0x0_0800; +pub const PA_STREAM_START_MUTED: c_uint = 0x0_1000; +pub const PA_STREAM_ADJUST_LATENCY: c_uint = 0x0_2000; +pub const PA_STREAM_EARLY_REQUESTS: c_uint = 0x0_4000; +pub const PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND: c_uint = 0x0_8000; +pub const PA_STREAM_START_UNMUTED: c_uint = 0x1_0000; +pub const PA_STREAM_FAIL_ON_SUSPEND: c_uint = 0x2_0000; +pub const PA_STREAM_RELATIVE_VOLUME: c_uint = 0x4_0000; +pub const PA_STREAM_PASSTHROUGH: c_uint = 0x8_0000; +pub type pa_stream_flags_t = c_uint; #[repr(C)] #[derive(Clone, Copy, Debug)] @@ -162,19 +162,19 @@ pub const PA_ERR_BUSY: c_int = 26; pub const PA_ERR_MAX: c_int = 27; pub type pa_error_code_t = c_int; -pub const PA_SUBSCRIPTION_MASK_NULL: c_int = 0; -pub const PA_SUBSCRIPTION_MASK_SINK: c_int = 1; -pub const PA_SUBSCRIPTION_MASK_SOURCE: c_int = 2; -pub const PA_SUBSCRIPTION_MASK_SINK_INPUT: c_int = 4; -pub const PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT: c_int = 8; -pub const PA_SUBSCRIPTION_MASK_MODULE: c_int = 16; -pub const PA_SUBSCRIPTION_MASK_CLIENT: c_int = 32; -pub const PA_SUBSCRIPTION_MASK_SAMPLE_CACHE: c_int = 64; -pub const PA_SUBSCRIPTION_MASK_SERVER: c_int = 128; -pub const PA_SUBSCRIPTION_MASK_AUTOLOAD: c_int = 256; -pub const PA_SUBSCRIPTION_MASK_CARD: c_int = 512; -pub const PA_SUBSCRIPTION_MASK_ALL: c_int = 767; -pub type pa_subscription_mask_t = c_int; +pub const PA_SUBSCRIPTION_MASK_NULL: c_uint = 0x0; +pub const PA_SUBSCRIPTION_MASK_SINK: c_uint = 0x1; +pub const PA_SUBSCRIPTION_MASK_SOURCE: c_uint = 0x2; +pub const PA_SUBSCRIPTION_MASK_SINK_INPUT: c_uint = 0x4; +pub const PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT: c_uint = 0x8; +pub const PA_SUBSCRIPTION_MASK_MODULE: c_uint = 0x10; +pub const PA_SUBSCRIPTION_MASK_CLIENT: c_uint = 0x20; +pub const PA_SUBSCRIPTION_MASK_SAMPLE_CACHE: c_uint = 0x40; +pub const PA_SUBSCRIPTION_MASK_SERVER: c_uint = 0x80; +pub const PA_SUBSCRIPTION_MASK_AUTOLOAD: c_uint = 0x100; +pub const PA_SUBSCRIPTION_MASK_CARD: c_uint = 0x200; +pub const PA_SUBSCRIPTION_MASK_ALL: c_uint = 0x3FF; +pub type pa_subscription_mask_t = c_uint; pub const PA_SUBSCRIPTION_EVENT_SINK: c_int = 0; pub const PA_SUBSCRIPTION_EVENT_SOURCE: c_int = 1; @@ -244,17 +244,17 @@ pub const PA_SEEK_RELATIVE_ON_READ: c_in pub const PA_SEEK_RELATIVE_END: c_int = 3; pub type pa_seek_mode_t = c_int; -pub const PA_SINK_NOFLAGS: c_int = 0; -pub const PA_SINK_HW_VOLUME_CTRL: c_int = 1; -pub const PA_SINK_LATENCY: c_int = 2; -pub const PA_SINK_HARDWARE: c_int = 4; -pub const PA_SINK_NETWORK: c_int = 8; -pub const PA_SINK_HW_MUTE_CTRL: c_int = 16; -pub const PA_SINK_DECIBEL_VOLUME: c_int = 32; -pub const PA_SINK_FLAT_VOLUME: c_int = 64; -pub const PA_SINK_DYNAMIC_LATENCY: c_int = 128; -pub const PA_SINK_SET_FORMATS: c_int = 256; -pub type pa_sink_flags_t = c_int; +pub const PA_SINK_NOFLAGS: c_uint = 0x000; +pub const PA_SINK_HW_VOLUME_CTRL: c_uint = 0x001; +pub const PA_SINK_LATENCY: c_uint = 0x002; +pub const PA_SINK_HARDWARE: c_uint = 0x004; +pub const PA_SINK_NETWORK: c_uint = 0x008; +pub const PA_SINK_HW_MUTE_CTRL: c_uint = 0x010; +pub const PA_SINK_DECIBEL_VOLUME: c_uint = 0x020; +pub const PA_SINK_FLAT_VOLUME: c_uint = 0x040; +pub const PA_SINK_DYNAMIC_LATENCY: c_uint = 0x080; +pub const PA_SINK_SET_FORMATS: c_uint = 0x100; +pub type pa_sink_flags_t = c_uint; pub const PA_SINK_INVALID_STATE: c_int = -1; pub const PA_SINK_RUNNING: c_int = 0; @@ -264,16 +264,16 @@ pub const PA_SINK_INIT: c_int = -2; pub const PA_SINK_UNLINKED: c_int = -3; pub type pa_sink_state_t = c_int; -pub const PA_SOURCE_NOFLAGS: c_int = 0x00; -pub const PA_SOURCE_HW_VOLUME_CTRL: c_int = 0x01; -pub const PA_SOURCE_LATENCY: c_int = 0x02; -pub const PA_SOURCE_HARDWARE: c_int = 0x04; -pub const PA_SOURCE_NETWORK: c_int = 0x08; -pub const PA_SOURCE_HW_MUTE_CTRL: c_int = 0x10; -pub const PA_SOURCE_DECIBEL_VOLUME: c_int = 0x20; -pub const PA_SOURCE_DYNAMIC_LATENCY: c_int = 0x40; -pub const PA_SOURCE_FLAT_VOLUME: c_int = 0x80; -pub type pa_source_flags_t = c_int; +pub const PA_SOURCE_NOFLAGS: c_uint = 0x00; +pub const PA_SOURCE_HW_VOLUME_CTRL: c_uint = 0x01; +pub const PA_SOURCE_LATENCY: c_uint = 0x02; +pub const PA_SOURCE_HARDWARE: c_uint = 0x04; +pub const PA_SOURCE_NETWORK: c_uint = 0x08; +pub const PA_SOURCE_HW_MUTE_CTRL: c_uint = 0x10; +pub const PA_SOURCE_DECIBEL_VOLUME: c_uint = 0x20; +pub const PA_SOURCE_DYNAMIC_LATENCY: c_uint = 0x40; +pub const PA_SOURCE_FLAT_VOLUME: c_uint = 0x80; +pub type pa_source_flags_t = c_uint; pub const PA_SOURCE_INVALID_STATE: c_int = -1; pub const PA_SOURCE_RUNNING: c_int = 0; diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/Cargo.toml.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/Cargo.toml --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/Cargo.toml.cubeb-pulse-arm 2017-08-04 13:37:46.384821737 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/Cargo.toml 2017-08-04 13:37:46.384821737 +0200 @@ -0,0 +1,8 @@ +[package] +name = "pulse" +version = "0.1.0" +authors = ["Dan Glastonbury "] + +[dependencies] +bitflags = "^0.7.0" +pulse-ffi = { path = "../pulse-ffi" } diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/context.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/context.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/context.rs.cubeb-pulse-arm 2017-08-04 13:37:46.385821734 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/context.rs 2017-08-04 13:37:46.385821734 +0200 @@ -0,0 +1,394 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ::*; +use ffi; +use std::ffi::CStr; +use std::os::raw::{c_int, c_void}; +use std::ptr; +use util::UnwrapCStr; + +// A note about `wrapped` functions +// +// C FFI demands `unsafe extern fn(*mut pa_context, ...) -> i32`, etc, +// but we want to allow such callbacks to be safe. This means no +// `unsafe` or `extern`, and callbacks should be called with a safe +// wrapper of `*mut pa_context`. Since the callback doesn't take +// ownership, this is `&Context`. `fn wrapped(...)` defines a +// function that converts from our safe signature to the unsafe +// signature. +// +// Currently, we use a property of Rust, namely that each function +// gets its own unique type. These unique types can't be written +// directly, so we use generic and a type parameter, and let the Rust +// compiler fill in the name for us: +// +// fn get_sink_input_info(&self, ..., _: CB, ...) -> ... +// where CB: Fn(&Context, *const SinkInputInfo, i32, *mut c_void) +// +// Because we aren't storing or passing any state, we assert, at run-time :-(, +// that our functions are zero-sized: +// +// assert!(mem::size_of::() == 0); +// +// We need to obtain a value of type F in order to call it. Since we +// can't name the function, we have to unsafely construct that value +// somehow - we do this using mem::uninitialized. Then, we call that +// function with a reference to the Context, and save the result: +// +// | generate value || call it | +// let result = ::std::mem::uninitialized::()(&mut object); +// +// Lastly, since our Object is an owned type, we need to avoid +// dropping it, then return the result we just generated. +// +// mem::forget(object); +// result + +// Aid in returning Operation from callbacks +macro_rules! op_or_err { + ($self_:ident, $e:expr) => {{ + let o = unsafe { $e }; + if o.is_null() { + Err(ErrorCode::from_error_code($self_.errno())) + } else { + Ok(unsafe { operation::from_raw_ptr(o) }) + } + }} +} + +#[repr(C)] +#[derive(Debug)] +pub struct Context(*mut ffi::pa_context); + +impl Context { + pub fn new<'a, OPT>(api: &MainloopApi, name: OPT) -> Option + where OPT: Into> + { + let ptr = unsafe { ffi::pa_context_new(api.raw_mut(), name.unwrap_cstr()) }; + if ptr.is_null() { + None + } else { + Some(Context(ptr)) + } + } + + #[doc(hidden)] + pub fn raw_mut(&self) -> &mut ffi::pa_context { + unsafe { &mut *self.0 } + } + + pub fn unref(self) { + unsafe { + ffi::pa_context_unref(self.raw_mut()); + } + } + + pub fn clear_state_callback(&self) { + unsafe { + ffi::pa_context_set_state_callback(self.raw_mut(), None, ptr::null_mut()); + } + } + + pub fn set_state_callback(&self, _: CB, userdata: *mut c_void) + where CB: Fn(&Context, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, userdata: *mut c_void) + where F: Fn(&Context, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, userdata); + forget(ctx); + + result + } + + unsafe { + ffi::pa_context_set_state_callback(self.raw_mut(), Some(wrapped::), userdata); + } + } + + pub fn errno(&self) -> ffi::pa_error_code_t { + unsafe { ffi::pa_context_errno(self.raw_mut()) } + } + + pub fn get_state(&self) -> ContextState { + ContextState::try_from(unsafe { + ffi::pa_context_get_state(self.raw_mut()) + }).expect("pa_context_get_state returned invalid ContextState") + } + + pub fn connect<'a, OPT>(&self, server: OPT, flags: ContextFlags, api: *const ffi::pa_spawn_api) -> Result<()> + where OPT: Into> + { + let r = unsafe { + ffi::pa_context_connect(self.raw_mut(), + server.into().unwrap_cstr(), + flags.into(), + api) + }; + error_result!((), r) + } + + pub fn disconnect(&self) { + unsafe { + ffi::pa_context_disconnect(self.raw_mut()); + } + } + + + pub fn drain(&self, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Context, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, userdata: *mut c_void) + where F: Fn(&Context, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_drain(self.raw_mut(), Some(wrapped::), userdata)) + } + + pub fn rttime_new(&self, usec: USec, _: CB, userdata: *mut c_void) -> *mut ffi::pa_time_event + where CB: Fn(&MainloopApi, *mut ffi::pa_time_event, &TimeVal, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(a: *mut ffi::pa_mainloop_api, + e: *mut ffi::pa_time_event, + tv: *const TimeVal, + userdata: *mut c_void) + where F: Fn(&MainloopApi, *mut ffi::pa_time_event, &TimeVal, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let api = mainloop_api::from_raw_ptr(a); + let timeval = &*tv; + let result = uninitialized::()(&api, e, timeval, userdata); + forget(api); + + result + } + + unsafe { ffi::pa_context_rttime_new(self.raw_mut(), usec, Some(wrapped::), userdata) } + } + + pub fn get_server_info(&self, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Context, &ServerInfo, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, i: *const ffi::pa_server_info, userdata: *mut c_void) + where F: Fn(&Context, &ServerInfo, *mut c_void) + { + use std::mem::{forget, uninitialized}; + debug_assert_ne!(i, ptr::null_mut()); + let info = &*i; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, info, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_get_server_info(self.raw_mut(), Some(wrapped::), userdata)) + } + + pub fn get_sink_info_by_name(&self, name: &CStr, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Context, *const SinkInfo, i32, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, + info: *const ffi::pa_sink_info, + eol: c_int, + userdata: *mut c_void) + where F: Fn(&Context, *const SinkInfo, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, info, eol, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_get_sink_info_by_name(self.raw_mut(), name.as_ptr(), Some(wrapped::), userdata)) + } + + pub fn get_sink_info_list(&self, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Context, *const SinkInfo, i32, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, + info: *const ffi::pa_sink_info, + eol: c_int, + userdata: *mut c_void) + where F: Fn(&Context, *const SinkInfo, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, info, eol, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_get_sink_info_list(self.raw_mut(), Some(wrapped::), userdata)) + } + + pub fn get_sink_input_info(&self, idx: u32, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Context, *const SinkInputInfo, i32, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, + info: *const ffi::pa_sink_input_info, + eol: c_int, + userdata: *mut c_void) + where F: Fn(&Context, *const SinkInputInfo, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, info, eol, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_get_sink_input_info(self.raw_mut(), idx, Some(wrapped::), userdata)) + } + + pub fn get_source_info_list(&self, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Context, *const SourceInfo, i32, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, + info: *const ffi::pa_source_info, + eol: c_int, + userdata: *mut c_void) + where F: Fn(&Context, *const SourceInfo, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, info, eol, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_get_source_info_list(self.raw_mut(), Some(wrapped::), userdata)) + } + + pub fn set_sink_input_volume(&self, + idx: u32, + volume: &CVolume, + _: CB, + userdata: *mut c_void) + -> Result + where CB: Fn(&Context, i32, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, success: c_int, userdata: *mut c_void) + where F: Fn(&Context, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, success, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_set_sink_input_volume(self.raw_mut(), idx, volume, Some(wrapped::), userdata)) + } + + pub fn subscribe(&self, m: SubscriptionMask, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Context, i32, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, success: c_int, userdata: *mut c_void) + where F: Fn(&Context, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let result = uninitialized::()(&ctx, success, userdata); + forget(ctx); + + result + } + + op_or_err!(self, + ffi::pa_context_subscribe(self.raw_mut(), m.into(), Some(wrapped::), userdata)) + } + + pub fn clear_subscribe_callback(&self) { + unsafe { + ffi::pa_context_set_subscribe_callback(self.raw_mut(), None, ptr::null_mut()); + } + } + + pub fn set_subscribe_callback(&self, _: CB, userdata: *mut c_void) + where CB: Fn(&Context, SubscriptionEvent, u32, *mut c_void) + { + debug_assert_eq!(::std::mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(c: *mut ffi::pa_context, + t: ffi::pa_subscription_event_type_t, + idx: u32, + userdata: *mut c_void) + where F: Fn(&Context, SubscriptionEvent, u32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let ctx = context::from_raw_ptr(c); + let event = SubscriptionEvent::try_from(t) + .expect("pa_context_subscribe_cb_t passed invalid pa_subscription_event_type_t"); + let result = uninitialized::()(&ctx, event, idx, userdata); + forget(ctx); + + result + } + + unsafe { + ffi::pa_context_set_subscribe_callback(self.raw_mut(), Some(wrapped::), userdata); + } + } +} + +#[doc(hidden)] +pub unsafe fn from_raw_ptr(ptr: *mut ffi::pa_context) -> Context { + Context(ptr) +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/error.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/error.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/error.rs.cubeb-pulse-arm 2017-08-04 13:37:46.385821734 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/error.rs 2017-08-04 13:37:46.385821734 +0200 @@ -0,0 +1,56 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ffi; +use std::ffi::CStr; + +#[macro_export] +macro_rules! error_result { + ($t:expr, $err:expr) => { + if $err >= 0 { + Ok($t) + } else { + Err(ErrorCode::from_error_result($err)) + } + } +} + +#[derive(Debug, PartialEq)] +pub struct ErrorCode { + err: ffi::pa_error_code_t, +} + +impl ErrorCode { + pub fn from_error_result(err: i32) -> Self { + debug_assert!(err < 0); + ErrorCode { + err: (-err) as ffi::pa_error_code_t, + } + } + + pub fn from_error_code(err: ffi::pa_error_code_t) -> Self { + debug_assert!(err > 0); + ErrorCode { + err: err, + } + } + + fn desc(&self) -> &'static str { + let cstr = unsafe { CStr::from_ptr(ffi::pa_strerror(self.err)) }; + cstr.to_str().unwrap() + } +} + +impl ::std::error::Error for ErrorCode { + fn description(&self) -> &str { + self.desc() + } +} + +impl ::std::fmt::Display for ErrorCode { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{:?}: {}", self, self.desc()) + } +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/lib.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/lib.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/lib.rs.cubeb-pulse-arm 2017-08-04 13:37:46.385821734 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/lib.rs 2017-08-04 13:37:46.385821734 +0200 @@ -0,0 +1,653 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +#[macro_use] +extern crate bitflags; +extern crate pulse_ffi as ffi; + +#[macro_use] +mod error; +mod context; +mod mainloop_api; +mod operation; +mod proplist; +mod stream; +mod threaded_mainloop; +mod util; + +pub use context::Context; +pub use error::ErrorCode; +pub use ffi::pa_buffer_attr as BufferAttr; +pub use ffi::pa_channel_map as ChannelMap; +pub use ffi::pa_cvolume as CVolume; +pub use ffi::pa_sample_spec as SampleSpec; +pub use ffi::pa_server_info as ServerInfo; +pub use ffi::pa_sink_info as SinkInfo; +pub use ffi::pa_sink_input_info as SinkInputInfo; +pub use ffi::pa_source_info as SourceInfo; +pub use ffi::pa_usec_t as USec; +pub use ffi::pa_volume_t as Volume; +pub use ffi::timeval as TimeVal; +pub use mainloop_api::MainloopApi; +pub use operation::Operation; +pub use proplist::Proplist; +use std::os::raw::{c_char, c_uint}; +pub use stream::Stream; +pub use threaded_mainloop::ThreadedMainloop; + +#[allow(non_camel_case_types)] +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SampleFormat { + Invalid = ffi::PA_SAMPLE_INVALID, + U8 = ffi::PA_SAMPLE_U8, + Alaw = ffi::PA_SAMPLE_ALAW, + Ulaw = ffi::PA_SAMPLE_ULAW, + Signed16LE = ffi::PA_SAMPLE_S16LE, + Signed16BE = ffi::PA_SAMPLE_S16BE, + Float32LE = ffi::PA_SAMPLE_FLOAT32LE, + Float32BE = ffi::PA_SAMPLE_FLOAT32BE, + Signed32LE = ffi::PA_SAMPLE_S32LE, + Signed32BE = ffi::PA_SAMPLE_S32BE, + Signed24LE = ffi::PA_SAMPLE_S24LE, + Signed24BE = ffi::PA_SAMPLE_S24BE, + Signed24_32LE = ffi::PA_SAMPLE_S24_32LE, + Signed23_32BE = ffi::PA_SAMPLE_S24_32BE, +} + +impl Default for SampleFormat { + fn default() -> Self { + SampleFormat::Invalid + } +} + +impl Into for SampleFormat { + fn into(self) -> ffi::pa_sample_format_t { + self as ffi::pa_sample_format_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ContextState { + Unconnected = ffi::PA_CONTEXT_UNCONNECTED, + Connecting = ffi::PA_CONTEXT_CONNECTING, + Authorizing = ffi::PA_CONTEXT_AUTHORIZING, + SettingName = ffi::PA_CONTEXT_SETTING_NAME, + Ready = ffi::PA_CONTEXT_READY, + Failed = ffi::PA_CONTEXT_FAILED, + Terminated = ffi::PA_CONTEXT_TERMINATED, +} + +impl ContextState { + // This function implements the PA_CONTENT_IS_GOOD macro from pulse/def.h + // It must match the version from PA headers. + pub fn is_good(self) -> bool { + match self { + ContextState::Connecting | + ContextState::Authorizing | + ContextState::SettingName | + ContextState::Ready => true, + _ => false, + } + } + + pub fn try_from(x: ffi::pa_context_state_t) -> Option { + if x >= ffi::PA_CONTEXT_UNCONNECTED && x <= ffi::PA_CONTEXT_TERMINATED { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Default for ContextState { + fn default() -> Self { + ContextState::Unconnected + } +} + +impl Into for ContextState { + fn into(self) -> ffi::pa_context_state_t { + self as ffi::pa_context_state_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StreamState { + Unconnected = ffi::PA_STREAM_UNCONNECTED, + Creating = ffi::PA_STREAM_CREATING, + Ready = ffi::PA_STREAM_READY, + Failed = ffi::PA_STREAM_FAILED, + Terminated = ffi::PA_STREAM_TERMINATED, +} + +impl StreamState { + // This function implements the PA_STREAM_IS_GOOD macro from pulse/def.h + // It must match the version from PA headers. + pub fn is_good(self) -> bool { + match self { + StreamState::Creating | StreamState::Ready => true, + _ => false, + } + } + + pub fn try_from(x: ffi::pa_stream_state_t) -> Option { + if x >= ffi::PA_STREAM_UNCONNECTED && x <= ffi::PA_STREAM_TERMINATED { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Default for StreamState { + fn default() -> Self { + StreamState::Unconnected + } +} + +impl Into for StreamState { + fn into(self) -> ffi::pa_stream_state_t { + self as ffi::pa_stream_state_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum OperationState { + Running = ffi::PA_OPERATION_RUNNING, + Done = ffi::PA_OPERATION_DONE, + Cancelled = ffi::PA_OPERATION_CANCELLED, +} + +impl OperationState { + pub fn try_from(x: ffi::pa_operation_state_t) -> Option { + if x >= ffi::PA_OPERATION_RUNNING && x <= ffi::PA_OPERATION_CANCELLED { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into for OperationState { + fn into(self) -> ffi::pa_operation_state_t { + self as ffi::pa_operation_state_t + } +} + +bitflags! { + pub flags ContextFlags: u32 { + const CONTEXT_FLAGS_NOAUTOSPAWN = ffi::PA_CONTEXT_NOAUTOSPAWN, + const CONTEXT_FLAGS_NOFAIL = ffi::PA_CONTEXT_NOFAIL, + } +} + +impl Into for ContextFlags { + fn into(self) -> ffi::pa_context_flags_t { + self.bits() as ffi::pa_context_flags_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DeviceType { + Sink = ffi::PA_DEVICE_TYPE_SINK, + Source = ffi::PA_DEVICE_TYPE_SOURCE, +} + +impl DeviceType { + pub fn try_from(x: ffi::pa_device_type_t) -> Option { + if x >= ffi::PA_DEVICE_TYPE_SINK && x <= ffi::PA_DEVICE_TYPE_SOURCE { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into for DeviceType { + fn into(self) -> ffi::pa_device_type_t { + self as ffi::pa_device_type_t + } +} + + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StreamDirection { + NoDirection = ffi::PA_STREAM_NODIRECTION, + Playback = ffi::PA_STREAM_PLAYBACK, + Record = ffi::PA_STREAM_RECORD, + StreamUpload = ffi::PA_STREAM_UPLOAD, +} + +impl StreamDirection { + pub fn try_from(x: ffi::pa_stream_direction_t) -> Option { + if x >= ffi::PA_STREAM_NODIRECTION && x <= ffi::PA_STREAM_UPLOAD { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into for StreamDirection { + fn into(self) -> ffi::pa_stream_direction_t { + self as ffi::pa_stream_direction_t + } +} + +bitflags! { + pub flags StreamFlags : u32 { + const STREAM_START_CORKED = ffi::PA_STREAM_START_CORKED, + const STREAM_INTERPOLATE_TIMING = ffi::PA_STREAM_INTERPOLATE_TIMING, + const STREAM_NOT_MONOTONIC = ffi::PA_STREAM_NOT_MONOTONIC, + const STREAM_AUTO_TIMING_UPDATE = ffi::PA_STREAM_AUTO_TIMING_UPDATE, + const STREAM_NO_REMAP_CHANNELS = ffi::PA_STREAM_NO_REMAP_CHANNELS, + const STREAM_NO_REMIX_CHANNELS = ffi::PA_STREAM_NO_REMIX_CHANNELS, + const STREAM_FIX_FORMAT = ffi::PA_STREAM_FIX_FORMAT, + const STREAM_FIX_RATE = ffi::PA_STREAM_FIX_RATE, + const STREAM_FIX_CHANNELS = ffi::PA_STREAM_FIX_CHANNELS, + const STREAM_DONT_MOVE = ffi::PA_STREAM_DONT_MOVE, + const STREAM_VARIABLE_RATE = ffi::PA_STREAM_VARIABLE_RATE, + const STREAM_PEAK_DETECT = ffi::PA_STREAM_PEAK_DETECT, + const STREAM_START_MUTED = ffi::PA_STREAM_START_MUTED, + const STREAM_ADJUST_LATENCY = ffi::PA_STREAM_ADJUST_LATENCY, + const STREAM_EARLY_REQUESTS = ffi::PA_STREAM_EARLY_REQUESTS, + const STREAM_DONT_INHIBIT_AUTO_SUSPEND = ffi::PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND, + const STREAM_START_UNMUTED = ffi::PA_STREAM_START_UNMUTED, + const STREAM_FAIL_ON_SUSPEND = ffi::PA_STREAM_FAIL_ON_SUSPEND, + const STREAM_RELATIVE_VOLUME = ffi::PA_STREAM_RELATIVE_VOLUME, + const STREAM_PASSTHROUGH = ffi::PA_STREAM_PASSTHROUGH, + } +} + +impl StreamFlags { + pub fn try_from(x: ffi::pa_stream_flags_t) -> Option { + if (x & + !(ffi::PA_STREAM_NOFLAGS | ffi::PA_STREAM_START_CORKED | ffi::PA_STREAM_INTERPOLATE_TIMING | + ffi::PA_STREAM_NOT_MONOTONIC | ffi::PA_STREAM_AUTO_TIMING_UPDATE | + ffi::PA_STREAM_NO_REMAP_CHANNELS | + ffi::PA_STREAM_NO_REMIX_CHANNELS | ffi::PA_STREAM_FIX_FORMAT | ffi::PA_STREAM_FIX_RATE | + ffi::PA_STREAM_FIX_CHANNELS | + ffi::PA_STREAM_DONT_MOVE | ffi::PA_STREAM_VARIABLE_RATE | ffi::PA_STREAM_PEAK_DETECT | + ffi::PA_STREAM_START_MUTED | ffi::PA_STREAM_ADJUST_LATENCY | + ffi::PA_STREAM_EARLY_REQUESTS | + ffi::PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND | + ffi::PA_STREAM_START_UNMUTED | ffi::PA_STREAM_FAIL_ON_SUSPEND | + ffi::PA_STREAM_RELATIVE_VOLUME | ffi::PA_STREAM_PASSTHROUGH)) == 0 { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into for StreamFlags { + fn into(self) -> ffi::pa_stream_flags_t { + self.bits() as ffi::pa_stream_flags_t + } +} + +bitflags!{ + pub flags SubscriptionMask : u32 { + const SUBSCRIPTION_MASK_SINK = ffi::PA_SUBSCRIPTION_MASK_SINK, + const SUBSCRIPTION_MASK_SOURCE = ffi::PA_SUBSCRIPTION_MASK_SOURCE, + const SUBSCRIPTION_MASK_SINK_INPUT = ffi::PA_SUBSCRIPTION_MASK_SINK_INPUT, + const SUBSCRIPTION_MASK_SOURCE_OUTPUT = ffi::PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, + const SUBSCRIPTION_MASK_MODULE = ffi::PA_SUBSCRIPTION_MASK_MODULE, + const SUBSCRIPTION_MASK_CLIENT = ffi::PA_SUBSCRIPTION_MASK_CLIENT, + const SUBSCRIPTION_MASK_SAMPLE_CACHE = ffi::PA_SUBSCRIPTION_MASK_SAMPLE_CACHE, + const SUBSCRIPTION_MASK_SERVER = ffi::PA_SUBSCRIPTION_MASK_SERVER, + const SUBSCRIPTION_MASK_AUTOLOAD = ffi::PA_SUBSCRIPTION_MASK_AUTOLOAD, + const SUBSCRIPTION_MASK_CARD = ffi::PA_SUBSCRIPTION_MASK_CARD, + } +} + +impl SubscriptionMask { + pub fn try_from(x: ffi::pa_subscription_mask_t) -> Option { + if (x & !ffi::PA_SUBSCRIPTION_MASK_ALL) == 0 { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into for SubscriptionMask { + fn into(self) -> ffi::pa_subscription_mask_t { + self.bits() as ffi::pa_subscription_mask_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SubscriptionEventFacility { + Sink = ffi::PA_SUBSCRIPTION_EVENT_SINK, + Source = ffi::PA_SUBSCRIPTION_EVENT_SOURCE, + SinkInput = ffi::PA_SUBSCRIPTION_EVENT_SINK_INPUT, + SourceOutput = ffi::PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT, + Module = ffi::PA_SUBSCRIPTION_EVENT_MODULE, + Client = ffi::PA_SUBSCRIPTION_EVENT_CLIENT, + SampleCache = ffi::PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE, + Server = ffi::PA_SUBSCRIPTION_EVENT_SERVER, + Autoload = ffi::PA_SUBSCRIPTION_EVENT_AUTOLOAD, + Card = ffi::PA_SUBSCRIPTION_EVENT_CARD, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SubscriptionEventType { + New, + Change, + Remove, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SubscriptionEvent(ffi::pa_subscription_event_type_t); +impl SubscriptionEvent { + pub fn try_from(x: ffi::pa_subscription_event_type_t) -> Option { + if (x & !(ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK | ffi::PA_SUBSCRIPTION_EVENT_FACILITY_MASK)) == 0 { + Some(SubscriptionEvent(x)) + } else { + None + } + } + + pub fn event_facility(self) -> SubscriptionEventFacility { + unsafe { ::std::mem::transmute(self.0 & ffi::PA_SUBSCRIPTION_EVENT_FACILITY_MASK) } + } + + pub fn event_type(self) -> SubscriptionEventType { + unsafe { ::std::mem::transmute(((self.0 & ffi::PA_SUBSCRIPTION_EVENT_TYPE_MASK) >> 4)) } + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SeekMode { + Relative = ffi::PA_SEEK_RELATIVE, + Absolute = ffi::PA_SEEK_ABSOLUTE, + RelativeOnRead = ffi::PA_SEEK_RELATIVE_ON_READ, + RelativeEnd = ffi::PA_SEEK_RELATIVE_END, +} + +impl SeekMode { + pub fn try_from(x: ffi::pa_seek_mode_t) -> Option { + if x >= ffi::PA_SEEK_RELATIVE && x <= ffi::PA_SEEK_RELATIVE_END { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into for SeekMode { + fn into(self) -> ffi::pa_seek_mode_t { + self as ffi::pa_seek_mode_t + } +} + +bitflags! { + pub flags SinkFlags: u32 { + const SINK_HW_VOLUME_CTRL = ffi::PA_SINK_HW_VOLUME_CTRL, + const SINK_LATENCY = ffi::PA_SINK_LATENCY, + const SINK_HARDWARE = ffi::PA_SINK_HARDWARE, + const SINK_NETWORK = ffi::PA_SINK_NETWORK, + const SINK_HW_MUTE_CTRL = ffi::PA_SINK_HW_MUTE_CTRL, + const SINK_DECIBEL_VOLUME = ffi::PA_SINK_DECIBEL_VOLUME, + const SINK_FLAT_VOLUME = ffi::PA_SINK_FLAT_VOLUME, + const SINK_DYNAMIC_LATENCY = ffi::PA_SINK_DYNAMIC_LATENCY, + const SINK_SET_FORMATS = ffi::PA_SINK_SET_FORMATS, + } +} + +impl SinkFlags { + pub fn try_from(x: ffi::pa_sink_flags_t) -> Option { + if (x & + !(ffi::PA_SOURCE_NOFLAGS | ffi::PA_SOURCE_HW_VOLUME_CTRL | ffi::PA_SOURCE_LATENCY | + ffi::PA_SOURCE_HARDWARE | ffi::PA_SOURCE_NETWORK | ffi::PA_SOURCE_HW_MUTE_CTRL | + ffi::PA_SOURCE_DECIBEL_VOLUME | + ffi::PA_SOURCE_DYNAMIC_LATENCY | ffi::PA_SOURCE_FLAT_VOLUME)) == 0 { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SinkState { + InvalidState = ffi::PA_SINK_INVALID_STATE, + Running = ffi::PA_SINK_RUNNING, + Idle = ffi::PA_SINK_IDLE, + Suspended = ffi::PA_SINK_SUSPENDED, + Init = ffi::PA_SINK_INIT, + Unlinked = ffi::PA_SINK_UNLINKED, +} + +bitflags!{ + pub flags SourceFlags: u32 { + const SOURCE_FLAGS_HW_VOLUME_CTRL = ffi::PA_SOURCE_HW_VOLUME_CTRL, + const SOURCE_FLAGS_LATENCY = ffi::PA_SOURCE_LATENCY, + const SOURCE_FLAGS_HARDWARE = ffi::PA_SOURCE_HARDWARE, + const SOURCE_FLAGS_NETWORK = ffi::PA_SOURCE_NETWORK, + const SOURCE_FLAGS_HW_MUTE_CTRL = ffi::PA_SOURCE_HW_MUTE_CTRL, + const SOURCE_FLAGS_DECIBEL_VOLUME = ffi::PA_SOURCE_DECIBEL_VOLUME, + const SOURCE_FLAGS_DYNAMIC_LATENCY = ffi::PA_SOURCE_DYNAMIC_LATENCY, + const SOURCE_FLAGS_FLAT_VOLUME = ffi::PA_SOURCE_FLAT_VOLUME, + } +} + +impl Into for SourceFlags { + fn into(self) -> ffi::pa_source_flags_t { + self.bits() as ffi::pa_source_flags_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SourceState { + InvalidState = ffi::PA_SOURCE_INVALID_STATE, + Running = ffi::PA_SOURCE_RUNNING, + Idle = ffi::PA_SOURCE_IDLE, + Suspended = ffi::PA_SOURCE_SUSPENDED, + Init = ffi::PA_SOURCE_INIT, + Unlinked = ffi::PA_SOURCE_UNLINKED, +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PortAvailable { + Unknown = ffi::PA_PORT_AVAILABLE_UNKNOWN, + No = ffi::PA_PORT_AVAILABLE_NO, + Yes = ffi::PA_PORT_AVAILABLE_YES, +} + +impl PortAvailable { + pub fn try_from(x: ffi::pa_port_available_t) -> Option { + if x >= ffi::PA_PORT_AVAILABLE_UNKNOWN && x <= ffi::PA_PORT_AVAILABLE_YES { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Into for PortAvailable { + fn into(self) -> ffi::pa_port_available_t { + self as ffi::pa_port_available_t + } +} + +#[repr(i32)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum ChannelPosition { + Invalid = ffi::PA_CHANNEL_POSITION_INVALID, + Mono = ffi::PA_CHANNEL_POSITION_MONO, + FrontLeft = ffi::PA_CHANNEL_POSITION_FRONT_LEFT, + FrontRight = ffi::PA_CHANNEL_POSITION_FRONT_RIGHT, + FrontCenter = ffi::PA_CHANNEL_POSITION_FRONT_CENTER, + RearCenter = ffi::PA_CHANNEL_POSITION_REAR_CENTER, + RearLeft = ffi::PA_CHANNEL_POSITION_REAR_LEFT, + RearRight = ffi::PA_CHANNEL_POSITION_REAR_RIGHT, + LowFreqEffects = ffi::PA_CHANNEL_POSITION_LFE, + FrontLeftOfCenter = ffi::PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, + FrontRightOfCenter = ffi::PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, + SideLeft = ffi::PA_CHANNEL_POSITION_SIDE_LEFT, + SideRight = ffi::PA_CHANNEL_POSITION_SIDE_RIGHT, + Aux0 = ffi::PA_CHANNEL_POSITION_AUX0, + Aux1 = ffi::PA_CHANNEL_POSITION_AUX1, + Aux2 = ffi::PA_CHANNEL_POSITION_AUX2, + Aux3 = ffi::PA_CHANNEL_POSITION_AUX3, + Aux4 = ffi::PA_CHANNEL_POSITION_AUX4, + Aux5 = ffi::PA_CHANNEL_POSITION_AUX5, + Aux6 = ffi::PA_CHANNEL_POSITION_AUX6, + Aux7 = ffi::PA_CHANNEL_POSITION_AUX7, + Aux8 = ffi::PA_CHANNEL_POSITION_AUX8, + Aux9 = ffi::PA_CHANNEL_POSITION_AUX9, + Aux10 = ffi::PA_CHANNEL_POSITION_AUX10, + Aux11 = ffi::PA_CHANNEL_POSITION_AUX11, + Aux12 = ffi::PA_CHANNEL_POSITION_AUX12, + Aux13 = ffi::PA_CHANNEL_POSITION_AUX13, + Aux14 = ffi::PA_CHANNEL_POSITION_AUX14, + Aux15 = ffi::PA_CHANNEL_POSITION_AUX15, + Aux16 = ffi::PA_CHANNEL_POSITION_AUX16, + Aux17 = ffi::PA_CHANNEL_POSITION_AUX17, + Aux18 = ffi::PA_CHANNEL_POSITION_AUX18, + Aux19 = ffi::PA_CHANNEL_POSITION_AUX19, + Aux20 = ffi::PA_CHANNEL_POSITION_AUX20, + Aux21 = ffi::PA_CHANNEL_POSITION_AUX21, + Aux22 = ffi::PA_CHANNEL_POSITION_AUX22, + Aux23 = ffi::PA_CHANNEL_POSITION_AUX23, + Aux24 = ffi::PA_CHANNEL_POSITION_AUX24, + Aux25 = ffi::PA_CHANNEL_POSITION_AUX25, + Aux26 = ffi::PA_CHANNEL_POSITION_AUX26, + Aux27 = ffi::PA_CHANNEL_POSITION_AUX27, + Aux28 = ffi::PA_CHANNEL_POSITION_AUX28, + Aux29 = ffi::PA_CHANNEL_POSITION_AUX29, + Aux30 = ffi::PA_CHANNEL_POSITION_AUX30, + Aux31 = ffi::PA_CHANNEL_POSITION_AUX31, + TopCenter = ffi::PA_CHANNEL_POSITION_TOP_CENTER, + TopFrontLeft = ffi::PA_CHANNEL_POSITION_TOP_FRONT_LEFT, + TopFrontRight = ffi::PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, + TopFrontCenter = ffi::PA_CHANNEL_POSITION_TOP_FRONT_CENTER, + TopRearLeft = ffi::PA_CHANNEL_POSITION_TOP_REAR_LEFT, + TopRearRight = ffi::PA_CHANNEL_POSITION_TOP_REAR_RIGHT, + TopRearCenter = ffi::PA_CHANNEL_POSITION_TOP_REAR_CENTER, +} + +impl ChannelPosition { + pub fn try_from(x: ffi::pa_channel_position_t) -> Option { + if x >= ffi::PA_CHANNEL_POSITION_INVALID && x < ffi::PA_CHANNEL_POSITION_MAX { + Some(unsafe { ::std::mem::transmute(x) }) + } else { + None + } + } +} + +impl Default for ChannelPosition { + fn default() -> Self { + ChannelPosition::Invalid + } +} + +impl Into for ChannelPosition { + fn into(self) -> ffi::pa_channel_position_t { + self as ffi::pa_channel_position_t + } +} +pub type Result = ::std::result::Result; + +pub trait CVolumeExt { + fn set(&mut self, channels: c_uint, v: Volume); + fn set_balance(&mut self, map: &ChannelMap, new_balance: f32); +} + +impl CVolumeExt for CVolume { + fn set(&mut self, channels: c_uint, v: Volume) { + unsafe { + ffi::pa_cvolume_set(self, channels, v); + } + } + + fn set_balance(&mut self, map: &ChannelMap, new_balance: f32) { + unsafe { + ffi::pa_cvolume_set_balance(self, map, new_balance); + } + } +} + +pub trait ChannelMapExt { + fn init() -> ChannelMap; + fn can_balance(&self) -> bool; +} + +impl ChannelMapExt for ChannelMap { + fn init() -> ChannelMap { + let mut cm = ChannelMap::default(); + unsafe { + ffi::pa_channel_map_init(&mut cm); + } + cm + } + fn can_balance(&self) -> bool { + unsafe { ffi::pa_channel_map_can_balance(self) > 0 } + } +} + +pub trait ProplistExt { + fn proplist(&self) -> Proplist; +} + +impl ProplistExt for SinkInfo { + fn proplist(&self) -> Proplist { + unsafe { proplist::from_raw_ptr(self.proplist) } + } +} + +impl ProplistExt for SourceInfo { + fn proplist(&self) -> Proplist { + unsafe { proplist::from_raw_ptr(self.proplist) } + } +} + +pub trait SampleSpecExt { + fn frame_size(&self) -> usize; +} + +impl SampleSpecExt for SampleSpec { + fn frame_size(&self) -> usize { + unsafe { ffi::pa_frame_size(self) } + } +} + +pub trait USecExt { + fn to_bytes(self, spec: &SampleSpec) -> usize; +} + +impl USecExt for USec { + fn to_bytes(self, spec: &SampleSpec) -> usize { + unsafe { ffi::pa_usec_to_bytes(self, spec) } + } +} + +pub fn library_version() -> *const c_char { + unsafe { ffi::pa_get_library_version() } +} + +pub fn sw_volume_from_linear(vol: f64) -> Volume { + unsafe { ffi::pa_sw_volume_from_linear(vol) } +} + +pub fn rtclock_now() -> USec { + unsafe { ffi::pa_rtclock_now() } +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/mainloop_api.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/mainloop_api.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/mainloop_api.rs.cubeb-pulse-arm 2017-08-04 13:37:46.385821734 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/mainloop_api.rs 2017-08-04 13:37:46.385821734 +0200 @@ -0,0 +1,58 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ffi; +use std::mem; +use std::os::raw::c_void; + + +#[allow(non_camel_case_types)] +type pa_once_cb_t = Option; +fn wrap_once_cb(_: F) -> pa_once_cb_t + where F: Fn(&MainloopApi, *mut c_void) +{ + assert!(mem::size_of::() == 0); + + unsafe extern "C" fn wrapped(m: *mut ffi::pa_mainloop_api, userdata: *mut c_void) + where F: Fn(&MainloopApi, *mut c_void) + { + let api = from_raw_ptr(m); + let result = mem::transmute::<_, &F>(&())(&api, userdata); + mem::forget(api); + result + } + + Some(wrapped::) +} + +pub struct MainloopApi(*mut ffi::pa_mainloop_api); + +impl MainloopApi { + pub fn raw_mut(&self) -> &mut ffi::pa_mainloop_api { + unsafe { &mut *self.0 } + } + + pub fn once(&self, cb: CB, userdata: *mut c_void) + where CB: Fn(&MainloopApi, *mut c_void) + { + let wrapped = wrap_once_cb(cb); + unsafe { + ffi::pa_mainloop_api_once(self.raw_mut(), wrapped, userdata); + } + } + + pub fn time_free(&self, e: *mut ffi::pa_time_event) { + unsafe { + if let Some(f) = self.raw_mut().time_free { + f(e); + } + } + } +} + +pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_mainloop_api) -> MainloopApi { + MainloopApi(raw) +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/operation.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/operation.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/operation.rs.cubeb-pulse-arm 2017-08-04 13:37:46.385821734 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/operation.rs 2017-08-04 13:37:46.385821734 +0200 @@ -0,0 +1,43 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ffi; + +#[derive(Debug)] +pub struct Operation(*mut ffi::pa_operation); + +impl Operation { + pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_operation) -> Operation { + Operation(raw) + } + + pub fn cancel(&mut self) { + unsafe { + ffi::pa_operation_cancel(self.0); + } + } + + pub fn get_state(&self) -> ffi::pa_operation_state_t { + unsafe { ffi::pa_operation_get_state(self.0) } + } +} + +impl Clone for Operation { + fn clone(&self) -> Self { + Operation(unsafe { ffi::pa_operation_ref(self.0) }) + } +} + +impl Drop for Operation { + fn drop(&mut self) { + unsafe { + ffi::pa_operation_unref(self.0); + } + } +} + +pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_operation) -> Operation { + Operation::from_raw_ptr(raw) +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/proplist.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/proplist.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/proplist.rs.cubeb-pulse-arm 2017-08-04 13:37:46.385821734 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/proplist.rs 2017-08-04 13:37:46.385821734 +0200 @@ -0,0 +1,31 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ffi; +use std::ffi::{CStr, CString}; + +#[derive(Debug)] +pub struct Proplist(*mut ffi::pa_proplist); + +impl Proplist { + pub fn gets(&self, key: T) -> Option<&CStr> + where T: Into> + { + let key = match CString::new(key) { + Ok(k) => k, + _ => return None, + }; + let r = unsafe { ffi::pa_proplist_gets(self.0, key.as_ptr()) }; + if r.is_null() { + None + } else { + Some(unsafe { CStr::from_ptr(r) }) + } + } +} + +pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_proplist) -> Proplist { + return Proplist(raw); +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs.cubeb-pulse-arm 2017-08-04 13:37:46.386821731 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/stream.rs 2017-08-04 13:37:46.386821731 +0200 @@ -0,0 +1,367 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ::*; +use context; +use ffi; +use operation; +use std::ffi::CStr; +use std::mem; +use std::os::raw::{c_int, c_void}; +use std::ptr; +use util::*; + +#[derive(Debug)] +pub struct Stream(*mut ffi::pa_stream); + +impl Stream { + pub fn new<'a, CM>(c: &Context, name: &::std::ffi::CStr, ss: &SampleSpec, map: CM) -> Option + where CM: Into> + { + let ptr = unsafe { + ffi::pa_stream_new(c.raw_mut(), + name.as_ptr(), + ss as *const _, + to_ptr(map.into())) + }; + if ptr.is_null() { + None + } else { + Some(Stream(ptr)) + } + } + + #[doc(hidden)] + pub fn raw_mut(&self) -> &mut ffi::pa_stream { + unsafe { &mut *self.0 } + } + + pub fn unref(self) { + unsafe { + ffi::pa_stream_unref(self.raw_mut()); + } + } + + pub fn get_state(&self) -> StreamState { + StreamState::try_from(unsafe { + ffi::pa_stream_get_state(self.raw_mut()) + }).expect("pa_stream_get_state returned invalid StreamState") + } + + pub fn get_context(&self) -> Option { + let ptr = unsafe { ffi::pa_stream_get_context(self.raw_mut()) }; + if ptr.is_null() { + return None; + } + + let ctx = unsafe { context::from_raw_ptr(ptr) }; + Some(ctx) + } + + pub fn get_index(&self) -> u32 { + unsafe { ffi::pa_stream_get_index(self.raw_mut()) } + } + + pub fn get_device_name<'a>(&'a self) -> Result<&'a CStr> { + let r = unsafe { ffi::pa_stream_get_device_name(self.raw_mut()) }; + if r.is_null() { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(unsafe { CStr::from_ptr(r) }) + } + + pub fn is_suspended(&self) -> Result { + let r = unsafe { ffi::pa_stream_is_suspended(self.raw_mut()) }; + error_result!(r != 0, r) + } + + pub fn is_corked(&self) -> Result { + let r = unsafe { ffi::pa_stream_is_corked(self.raw_mut()) }; + error_result!(r != 0, r) + } + + pub fn connect_playback<'a, D, A, V, S>(&self, + dev: D, + attr: A, + flags: StreamFlags, + volume: V, + sync_stream: S) + -> Result<()> + where D: Into>, + A: Into>, + V: Into>, + S: Into> + { + let r = unsafe { + ffi::pa_stream_connect_playback(self.raw_mut(), + str_to_ptr(dev.into()), + to_ptr(attr.into()), + flags.into(), + to_ptr(volume.into()), + map_to_mut_ptr(sync_stream.into(), |p| p.0)) + }; + error_result!((), r) + } + + pub fn connect_record<'a, D, A>(&self, dev: D, attr: A, flags: StreamFlags) -> Result<()> + where D: Into>, + A: Into> + { + let r = unsafe { + ffi::pa_stream_connect_record(self.raw_mut(), + str_to_ptr(dev.into()), + to_ptr(attr.into()), + flags.into()) + }; + error_result!((), r) + } + + pub fn disconnect(&self) -> Result<()> { + let r = unsafe { ffi::pa_stream_disconnect(self.raw_mut()) }; + error_result!((), r) + } + + pub fn begin_write(&self, req_bytes: usize) -> Result<(*mut c_void, usize)> { + let mut data: *mut c_void = ptr::null_mut(); + let mut nbytes = req_bytes; + let r = unsafe { ffi::pa_stream_begin_write(self.raw_mut(), &mut data, &mut nbytes) }; + error_result!((data, nbytes), r) + } + + pub fn cancel_write(&self) -> Result<()> { + let r = unsafe { ffi::pa_stream_cancel_write(self.raw_mut()) }; + error_result!((), r) + } + + pub fn write(&self, data: *const c_void, nbytes: usize, offset: i64, seek: SeekMode) -> Result<()> { + let r = unsafe { ffi::pa_stream_write(self.raw_mut(), data, nbytes, None, offset, seek.into()) }; + error_result!((), r) + } + + pub unsafe fn peek(&self, data: *mut *const c_void, length: *mut usize) -> Result<()> { + let r = ffi::pa_stream_peek(self.raw_mut(), data, length); + error_result!((), r) + } + + pub fn drop(&self) -> Result<()> { + let r = unsafe { ffi::pa_stream_drop(self.raw_mut()) }; + error_result!((), r) + } + + pub fn writable_size(&self) -> Result { + let r = unsafe { ffi::pa_stream_writable_size(self.raw_mut()) }; + if r == ::std::usize::MAX { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(r) + } + + pub fn readable_size(&self) -> Result { + let r = unsafe { ffi::pa_stream_readable_size(self.raw_mut()) }; + if r == ::std::usize::MAX { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(r) + } + + pub fn update_timing_info(&self, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Stream, i32, *mut c_void) + { + debug_assert_eq!(mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(s: *mut ffi::pa_stream, success: c_int, userdata: *mut c_void) + where F: Fn(&Stream, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let mut stm = stream::from_raw_ptr(s); + let result = uninitialized::()(&mut stm, success, userdata); + forget(stm); + + result + } + + let r = unsafe { ffi::pa_stream_update_timing_info(self.raw_mut(), Some(wrapped::), userdata) }; + if r.is_null() { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(unsafe { operation::from_raw_ptr(r) }) + } + + pub fn clear_state_callback(&self) { + unsafe { + ffi::pa_stream_set_state_callback(self.raw_mut(), None, ptr::null_mut()); + } + } + + pub fn set_state_callback(&self, _: CB, userdata: *mut c_void) + where CB: Fn(&Stream, *mut c_void) + { + debug_assert_eq!(mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(s: *mut ffi::pa_stream, userdata: *mut c_void) + where F: Fn(&Stream, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let mut stm = stream::from_raw_ptr(s); + let result = uninitialized::()(&mut stm, userdata); + forget(stm); + + result + } + + unsafe { + ffi::pa_stream_set_state_callback(self.raw_mut(), Some(wrapped::), userdata); + } + } + + pub fn clear_write_callback(&self) { + unsafe { + ffi::pa_stream_set_write_callback(self.raw_mut(), None, ptr::null_mut()); + } + } + + pub fn set_write_callback(&self, _: CB, userdata: *mut c_void) + where CB: Fn(&Stream, usize, *mut c_void) + { + debug_assert_eq!(mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(s: *mut ffi::pa_stream, nbytes: usize, userdata: *mut c_void) + where F: Fn(&Stream, usize, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let mut stm = stream::from_raw_ptr(s); + let result = uninitialized::()(&mut stm, nbytes, userdata); + forget(stm); + + result + } + + unsafe { + ffi::pa_stream_set_write_callback(self.raw_mut(), Some(wrapped::), userdata); + } + } + + pub fn clear_read_callback(&self) { + unsafe { + ffi::pa_stream_set_read_callback(self.raw_mut(), None, ptr::null_mut()); + } + } + + pub fn set_read_callback(&self, _: CB, userdata: *mut c_void) + where CB: Fn(&Stream, usize, *mut c_void) + { + debug_assert_eq!(mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(s: *mut ffi::pa_stream, nbytes: usize, userdata: *mut c_void) + where F: Fn(&Stream, usize, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let mut stm = stream::from_raw_ptr(s); + let result = uninitialized::()(&mut stm, nbytes, userdata); + forget(stm); + + result + } + + unsafe { + ffi::pa_stream_set_read_callback(self.raw_mut(), Some(wrapped::), userdata); + } + } + + pub fn cork(&self, b: i32, _: CB, userdata: *mut c_void) -> Result + where CB: Fn(&Stream, i32, *mut c_void) + { + debug_assert_eq!(mem::size_of::(), 0); + + // See: A note about `wrapped` functions + unsafe extern "C" fn wrapped(s: *mut ffi::pa_stream, success: c_int, userdata: *mut c_void) + where F: Fn(&Stream, i32, *mut c_void) + { + use std::mem::{forget, uninitialized}; + let mut stm = stream::from_raw_ptr(s); + let result = uninitialized::()(&mut stm, success, userdata); + forget(stm); + + result + } + + let r = unsafe { ffi::pa_stream_cork(self.raw_mut(), b, Some(wrapped::), userdata) }; + if r.is_null() { + let err = if let Some(c) = self.get_context() { + c.errno() + } else { + ffi::PA_ERR_UNKNOWN + }; + return Err(ErrorCode::from_error_code(err)); + } + Ok(unsafe { operation::from_raw_ptr(r) }) + } + + pub fn get_time(&self) -> Result<(u64)> { + let mut usec: u64 = 0; + let r = unsafe { ffi::pa_stream_get_time(self.raw_mut(), &mut usec) }; + error_result!(usec, r) + } + + pub fn get_latency(&self) -> Result<(u64, bool)> { + let mut usec: u64 = 0; + let mut negative: i32 = 0; + let r = unsafe { ffi::pa_stream_get_latency(self.raw_mut(), &mut usec, &mut negative) }; + error_result!((usec, negative != 0), r) + } + + pub fn get_sample_spec(&self) -> &SampleSpec { + unsafe { + let ptr = ffi::pa_stream_get_sample_spec(self.raw_mut()); + debug_assert!(!ptr.is_null()); + &*ptr + } + } + + pub fn get_channel_map(&self) -> &ChannelMap { + unsafe { + let ptr = ffi::pa_stream_get_channel_map(self.raw_mut()); + debug_assert!(!ptr.is_null()); + &*ptr + } + } + + pub fn get_buffer_attr(&self) -> &BufferAttr { + unsafe { + let ptr = ffi::pa_stream_get_buffer_attr(self.raw_mut()); + debug_assert!(!ptr.is_null()); + &*ptr + } + } +} + +#[doc(hidden)] +pub unsafe fn from_raw_ptr(ptr: *mut ffi::pa_stream) -> Stream { + Stream(ptr) +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/threaded_mainloop.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/threaded_mainloop.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/threaded_mainloop.rs.cubeb-pulse-arm 2017-08-04 13:37:46.386821731 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/threaded_mainloop.rs 2017-08-04 13:37:46.386821731 +0200 @@ -0,0 +1,92 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use ErrorCode; +use Result; +use ffi; +use mainloop_api; +use mainloop_api::MainloopApi; + +#[derive(Debug)] +pub struct ThreadedMainloop(*mut ffi::pa_threaded_mainloop); + +impl ThreadedMainloop { + pub unsafe fn from_raw_ptr(raw: *mut ffi::pa_threaded_mainloop) -> Self { + ThreadedMainloop(raw) + } + + pub fn new() -> Self { + unsafe { ThreadedMainloop::from_raw_ptr(ffi::pa_threaded_mainloop_new()) } + } + + pub fn raw_mut(&self) -> &mut ffi::pa_threaded_mainloop { + unsafe { &mut *self.0 } + } + + pub fn is_null(&self) -> bool { + self.0.is_null() + } + + pub fn start(&self) -> Result<()> { + match unsafe { ffi::pa_threaded_mainloop_start(self.raw_mut()) } { + 0 => Ok(()), + _ => Err(ErrorCode::from_error_code(ffi::PA_ERR_UNKNOWN)), + } + } + + pub fn stop(&self) { + unsafe { + ffi::pa_threaded_mainloop_stop(self.raw_mut()); + } + } + + pub fn lock(&self) { + unsafe { + ffi::pa_threaded_mainloop_lock(self.raw_mut()); + } + } + + pub fn unlock(&self) { + unsafe { + ffi::pa_threaded_mainloop_unlock(self.raw_mut()); + } + } + + pub fn wait(&self) { + unsafe { + ffi::pa_threaded_mainloop_wait(self.raw_mut()); + } + } + + pub fn signal(&self) { + unsafe { + ffi::pa_threaded_mainloop_signal(self.raw_mut(), 0); + } + } + + pub fn get_api(&self) -> MainloopApi { + unsafe { mainloop_api::from_raw_ptr(ffi::pa_threaded_mainloop_get_api(self.raw_mut())) } + } + + pub fn in_thread(&self) -> bool { + unsafe { ffi::pa_threaded_mainloop_in_thread(self.raw_mut()) != 0 } + } +} + +impl ::std::default::Default for ThreadedMainloop { + fn default() -> Self { + ThreadedMainloop(::std::ptr::null_mut()) + } +} + +impl ::std::ops::Drop for ThreadedMainloop { + fn drop(&mut self) { + if !self.is_null() { + unsafe { + ffi::pa_threaded_mainloop_free(self.raw_mut()); + } + } + } +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/util.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/util.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/util.rs.cubeb-pulse-arm 2017-08-04 13:37:46.386821731 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/pulse-rs/src/util.rs 2017-08-04 13:37:46.386821731 +0200 @@ -0,0 +1,41 @@ +// Copyright © 2017 Mozilla Foundation +// +// This program is made available under an ISC-style license. See the +// accompanying file LICENSE for details. + +use std::ffi::CStr; +use std::os::raw::c_char; +use std::ptr; + +pub trait UnwrapCStr { + fn unwrap_cstr(self) -> *const c_char; +} + +impl<'a, U> UnwrapCStr for U + where U: Into> +{ + fn unwrap_cstr(self) -> *const c_char { + self.into().map(|o| o.as_ptr()).unwrap_or(0 as *const _) + } +} + +pub fn map_to_mut_ptr *mut U>(t: Option<&mut T>, f: F) -> *mut U { + match t { + Some(x) => f(x), + None => ptr::null_mut(), + } +} + +pub fn str_to_ptr(s: Option<&CStr>) -> *const c_char { + match s { + Some(x) => x.as_ptr(), + None => ptr::null(), + } +} + +pub fn to_ptr(t: Option<&T>) -> *const T { + match t { + Some(x) => x as *const T, + None => ptr::null(), + } +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/README.md.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/README.md --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/README.md.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/README.md 2017-08-04 13:37:46.383821740 +0200 @@ -3,3 +3,4 @@ Implementation of PulseAudio backend for Cubeb written in Rust. [![Travis Build Status](https://travis-ci.org/djg/cubeb-pulse-rs.svg?branch=master)](https://travis-ci.org/djg/cubeb-pulse-rs) +[![Travis Build Status](https://travis-ci.org/djg/cubeb-pulse-rs.svg?branch=dev)](https://travis-ci.org/djg/cubeb-pulse-rs) diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/README_MOZILLA.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/README_MOZILLA --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/README_MOZILLA.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/README_MOZILLA 2017-08-04 13:37:46.383821740 +0200 @@ -5,4 +5,4 @@ Makefile.in build files for the Mozilla The cubeb-pulse-rs git repository is: https://github.com/djg/cubeb-pulse-rs.git -The git commit ID used was dbcd7f96aea8d249a4b78f9a7597768c9dff22eb (2017-04-25 11:42:10 +1000) +The git commit ID used was 64515819cdf54a16626df5dce5f5c7cb1220d53b (2017-06-19 17:41:30 +1000) diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/context.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/context.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/context.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/context.rs 2017-08-04 13:50:38.145480458 +0200 @@ -4,67 +4,60 @@ // accompanying file LICENSE for details. use backend::*; -use backend::cork_state::CorkState; use capi::PULSE_OPS; use cubeb; +use pulse::{self, ProplistExt}; use pulse_ffi::*; use semver; use std::default::Default; -use std::ffi::CStr; +use std::ffi::{CStr, CString}; use std::mem; -use std::os::raw::{c_char, c_int, c_void}; +use std::os::raw::{c_char, c_void}; use std::ptr; -macro_rules! dup_str { - ($Dst: expr, $Src: expr) => { - if !$Dst.is_null() { - pa_xfree($Dst as *mut _); - } - - $Dst = pa_xstrdup($Src); - } -} - -fn pa_channel_to_cubeb_channel(channel: pa_channel_position_t) -> cubeb::Channel { - assert_ne!(channel, PA_CHANNEL_POSITION_INVALID); +fn pa_channel_to_cubeb_channel(channel: pulse::ChannelPosition) -> cubeb::Channel { + use pulse::ChannelPosition; + assert_ne!(channel, ChannelPosition::Invalid); match channel { - PA_CHANNEL_POSITION_MONO => cubeb::CHANNEL_MONO, - PA_CHANNEL_POSITION_FRONT_LEFT => cubeb::CHANNEL_LEFT, - PA_CHANNEL_POSITION_FRONT_RIGHT => cubeb::CHANNEL_RIGHT, - PA_CHANNEL_POSITION_FRONT_CENTER => cubeb::CHANNEL_CENTER, - PA_CHANNEL_POSITION_SIDE_LEFT => cubeb::CHANNEL_LS, - PA_CHANNEL_POSITION_SIDE_RIGHT => cubeb::CHANNEL_RS, - PA_CHANNEL_POSITION_REAR_LEFT => cubeb::CHANNEL_RLS, - PA_CHANNEL_POSITION_REAR_CENTER => cubeb::CHANNEL_RCENTER, - PA_CHANNEL_POSITION_REAR_RIGHT => cubeb::CHANNEL_RRS, - PA_CHANNEL_POSITION_LFE => cubeb::CHANNEL_LFE, + ChannelPosition::Mono => cubeb::CHANNEL_MONO, + ChannelPosition::FrontLeft => cubeb::CHANNEL_LEFT, + ChannelPosition::FrontRight => cubeb::CHANNEL_RIGHT, + ChannelPosition::FrontCenter => cubeb::CHANNEL_CENTER, + ChannelPosition::SideLeft => cubeb::CHANNEL_LS, + ChannelPosition::SideRight => cubeb::CHANNEL_RS, + ChannelPosition::RearLeft => cubeb::CHANNEL_RLS, + ChannelPosition::RearCenter => cubeb::CHANNEL_RCENTER, + ChannelPosition::RearRight => cubeb::CHANNEL_RRS, + ChannelPosition::LowFreqEffects => cubeb::CHANNEL_LFE, _ => cubeb::CHANNEL_INVALID, } } -fn channel_map_to_layout(cm: &pa_channel_map) -> cubeb::ChannelLayout { +fn channel_map_to_layout(cm: &pulse::ChannelMap) -> cubeb::ChannelLayout { + use pulse::ChannelPosition; let mut cubeb_map: cubeb::ChannelMap = Default::default(); cubeb_map.channels = cm.channels as u32; for i in 0usize..cm.channels as usize { - cubeb_map.map[i] = pa_channel_to_cubeb_channel(cm.map[i]); + cubeb_map.map[i] = pa_channel_to_cubeb_channel(ChannelPosition::try_from(cm.map[i]) + .unwrap_or(ChannelPosition::Invalid)); } unsafe { cubeb::cubeb_channel_map_to_layout(&cubeb_map) } } #[derive(Debug)] pub struct DefaultInfo { - pub sample_spec: pa_sample_spec, - pub channel_map: pa_channel_map, - pub flags: pa_sink_flags_t, + pub sample_spec: pulse::SampleSpec, + pub channel_map: pulse::ChannelMap, + pub flags: pulse::SinkFlags, } #[derive(Debug)] pub struct Context { pub ops: *const cubeb::Ops, - pub mainloop: *mut pa_threaded_mainloop, - pub context: *mut pa_context, + pub mainloop: pulse::ThreadedMainloop, + pub context: Option, pub default_sink_info: Option, - pub context_name: *const c_char, + pub context_name: Option, pub collection_changed_callback: cubeb::DeviceCollectionChangedCallback, pub collection_changed_user_ptr: *mut c_void, pub error: bool, @@ -82,7 +75,7 @@ impl Drop for Context { impl Context { #[cfg(feature = "pulse-dlopen")] - fn _new(name: *const i8) -> Result> { + fn _new(name: Option) -> Result> { let libpulse = unsafe { open() }; if libpulse.is_none() { return Err(cubeb::ERROR); @@ -91,12 +84,12 @@ impl Context { let ctx = Box::new(Context { ops: &PULSE_OPS, libpulse: libpulse.unwrap(), - mainloop: unsafe { pa_threaded_mainloop_new() }, - context: 0 as *mut _, + mainloop: pulse::ThreadedMainloop::new(), + context: None, default_sink_info: None, context_name: name, collection_changed_callback: None, - collection_changed_user_ptr: 0 as *mut _, + collection_changed_user_ptr: ptr::null_mut(), error: true, version_0_9_8: false, version_2_0_0: false, @@ -106,15 +99,15 @@ impl Context { } #[cfg(not(feature = "pulse-dlopen"))] - fn _new(name: *const i8) -> Result> { + fn _new(name: Option) -> Result> { Ok(Box::new(Context { ops: &PULSE_OPS, - mainloop: unsafe { pa_threaded_mainloop_new() }, - context: 0 as *mut _, + mainloop: pulse::ThreadedMainloop::new(), + context: None, default_sink_info: None, context_name: name, collection_changed_callback: None, - collection_changed_user_ptr: 0 as *mut _, + collection_changed_user_ptr: ptr::null_mut(), error: true, version_0_9_8: false, version_2_0_0: false, @@ -122,53 +115,66 @@ impl Context { } pub fn new(name: *const c_char) -> Result> { + fn server_info_cb(context: &pulse::Context, info: &pulse::ServerInfo, u: *mut c_void) { + fn sink_info_cb(_: &pulse::Context, i: *const pulse::SinkInfo, eol: i32, u: *mut c_void) { + let mut ctx = unsafe { &mut *(u as *mut Context) }; + if eol == 0 { + let info = unsafe { &*i }; + let flags = pulse::SinkFlags::try_from(info.flags).expect("SinkInfo contains invalid flags"); + ctx.default_sink_info = Some(DefaultInfo { + sample_spec: info.sample_spec, + channel_map: info.channel_map, + flags: flags, + }); + } + ctx.mainloop.signal(); + } + + let _ = context.get_sink_info_by_name(unsafe { CStr::from_ptr(info.default_sink_name) }, + sink_info_cb, + u); + } + + let name = super::try_cstr_from(name).map(|s| s.to_owned()); let mut ctx = try!(Context::_new(name)); - unsafe { pa_threaded_mainloop_start(ctx.mainloop) }; + if ctx.mainloop.start().is_err() { + ctx.destroy(); + return Err(cubeb::ERROR); + } - if ctx.pulse_context_init() != cubeb::OK { + if ctx.context_init() != cubeb::OK { ctx.destroy(); return Err(cubeb::ERROR); } - unsafe { - /* server_info_callback performs a second async query, - * which is responsible for initializing default_sink_info - * and signalling the mainloop to end the wait. */ - pa_threaded_mainloop_lock(ctx.mainloop); - let o = pa_context_get_server_info(ctx.context, - Some(server_info_callback), - ctx.as_mut() as *mut Context as *mut _); - if !o.is_null() { - ctx.operation_wait(ptr::null_mut(), o); - pa_operation_unref(o); + ctx.mainloop.lock(); + /* server_info_callback performs a second async query, + * which is responsible for initializing default_sink_info + * and signalling the mainloop to end the wait. */ + let user_data: *mut c_void = ctx.as_mut() as *mut _ as *mut _; + if let Some(ref context) = ctx.context { + if let Ok(o) = context.get_server_info(server_info_cb, user_data) { + ctx.operation_wait(None, &o); } - pa_threaded_mainloop_unlock(ctx.mainloop); - assert!(ctx.default_sink_info.is_some()); } + assert!(ctx.default_sink_info.is_some()); + ctx.mainloop.unlock(); // Return the result. Ok(ctx) } pub fn destroy(&mut self) { - if !self.context.is_null() { - unsafe { self.pulse_context_destroy() }; - } - assert!(self.context.is_null()); + self.context_destroy(); if !self.mainloop.is_null() { - unsafe { - pa_threaded_mainloop_stop(self.mainloop); - pa_threaded_mainloop_free(self.mainloop); - self.mainloop = ptr::null_mut(); - } + self.mainloop.stop(); } - assert!(self.mainloop.is_null()); } pub fn new_stream(&mut self, - stream_name: *const c_char, + stream_name: &CStr, input_device: cubeb::DeviceId, input_stream_params: Option, output_device: cubeb::DeviceId, @@ -178,7 +184,7 @@ impl Context { state_callback: cubeb::StateCallback, user_ptr: *mut c_void) -> Result> { - if self.error && self.pulse_context_init() != 0 { + if self.error && self.context_init() != 0 { return Err(cubeb::ERROR); } @@ -221,41 +227,151 @@ impl Context { } pub fn enumerate_devices(&self, devtype: cubeb::DeviceType) -> Result { - let mut user_data: PulseDevListData = Default::default(); - user_data.context = self as *const _ as *mut _; + fn add_output_device(_: &pulse::Context, i: *const pulse::SinkInfo, eol: i32, user_data: *mut c_void) { + if eol != 0 { + return; + } - unsafe { - pa_threaded_mainloop_lock(self.mainloop); + debug_assert!(!i.is_null()); + debug_assert!(!user_data.is_null()); + + let mut list_data = unsafe { &mut *(user_data as *mut PulseDevListData) }; + let info = unsafe { &*i }; + + let group_id = match info.proplist().gets("sysfs.path") { + Some(p) => p.to_owned().into_raw(), + _ => ptr::null_mut(), + }; + + let vendor_name = match info.proplist().gets("device.vendor.name") { + Some(p) => p.to_owned().into_raw(), + _ => ptr::null_mut(), + }; + + let info_name = unsafe { CStr::from_ptr(info.name) }.to_owned(); + let info_description = unsafe { CStr::from_ptr(info.description) }.to_owned(); + + let preferred = if info_name == list_data.default_sink_name { + cubeb::DEVICE_PREF_ALL + } else { + cubeb::DevicePref::empty() + }; + + let ctx = &(*list_data.context); + + let device_id = info_name.into_raw(); + let friendly_name = info_description.into_raw(); + let devinfo = cubeb::DeviceInfo { + device_id: device_id, + devid: device_id as cubeb::DeviceId, + friendly_name: friendly_name, + group_id: group_id, + vendor_name: vendor_name, + devtype: cubeb::DEVICE_TYPE_OUTPUT, + state: ctx.state_from_port(info.active_port), + preferred: preferred, + format: cubeb::DeviceFmt::all(), + default_format: pulse_format_to_cubeb_format(info.sample_spec.format), + max_channels: info.channel_map.channels as u32, + min_rate: 1, + max_rate: PA_RATE_MAX, + default_rate: info.sample_spec.rate, + latency_lo: 0, + latency_hi: 0, + }; + list_data.devinfo.push(devinfo); + + ctx.mainloop.signal(); + } + + fn add_input_device(_: &pulse::Context, i: *const pulse::SourceInfo, eol: i32, user_data: *mut c_void) { + if eol != 0 { + return; + } + + debug_assert!(!user_data.is_null()); + debug_assert!(!i.is_null()); + + let mut list_data = unsafe { &mut *(user_data as *mut PulseDevListData) }; + let info = unsafe { &*i }; + + let group_id = match info.proplist().gets("sysfs.path") { + Some(p) => p.to_owned().into_raw(), + _ => ptr::null_mut(), + }; + + let vendor_name = match info.proplist().gets("device.vendor.name") { + Some(p) => p.to_owned().into_raw(), + _ => ptr::null_mut(), + }; - let o = pa_context_get_server_info(self.context, - Some(pulse_server_info_cb), - &mut user_data as *mut _ as *mut _); - if !o.is_null() { - self.operation_wait(ptr::null_mut(), o); - pa_operation_unref(o); + let info_name = unsafe { CStr::from_ptr(info.name) }.to_owned(); + let info_description = unsafe { CStr::from_ptr(info.description) }.to_owned(); + + let preferred = if info_name == list_data.default_source_name { + cubeb::DEVICE_PREF_ALL + } else { + cubeb::DevicePref::empty() + }; + + let ctx = &(*list_data.context); + let device_id = info_name.into_raw(); + let friendly_name = info_description.into_raw(); + let devinfo = cubeb::DeviceInfo { + device_id: device_id, + devid: device_id as cubeb::DeviceId, + friendly_name: friendly_name, + group_id: group_id, + vendor_name: vendor_name, + devtype: cubeb::DEVICE_TYPE_INPUT, + state: ctx.state_from_port(info.active_port), + preferred: preferred, + format: cubeb::DeviceFmt::all(), + default_format: pulse_format_to_cubeb_format(info.sample_spec.format), + max_channels: info.channel_map.channels as u32, + min_rate: 1, + max_rate: PA_RATE_MAX, + default_rate: info.sample_spec.rate, + latency_lo: 0, + latency_hi: 0, + }; + + list_data.devinfo.push(devinfo); + + ctx.mainloop.signal(); + } + + fn default_device_names(_: &pulse::Context, info: &pulse::ServerInfo, user_data: *mut c_void) { + let list_data = unsafe { &mut *(user_data as *mut PulseDevListData) }; + + list_data.default_sink_name = unsafe { CStr::from_ptr(info.default_sink_name) }.to_owned(); + list_data.default_source_name = unsafe { CStr::from_ptr(info.default_source_name) }.to_owned(); + + (*list_data.context).mainloop.signal(); + } + + let mut user_data = PulseDevListData::new(self); + + if let Some(ref context) = self.context { + self.mainloop.lock(); + + if let Ok(o) = context.get_server_info(default_device_names, &mut user_data as *mut _ as *mut _) { + self.operation_wait(None, &o); } if devtype == cubeb::DEVICE_TYPE_OUTPUT { - let o = pa_context_get_sink_info_list(self.context, - Some(pulse_sink_info_cb), - &mut user_data as *mut _ as *mut _); - if !o.is_null() { - self.operation_wait(ptr::null_mut(), o); - pa_operation_unref(o); + if let Ok(o) = context.get_sink_info_list(add_output_device, &mut user_data as *mut _ as *mut _) { + self.operation_wait(None, &o); } } if devtype == cubeb::DEVICE_TYPE_INPUT { - let o = pa_context_get_source_info_list(self.context, - Some(pulse_source_info_cb), - &mut user_data as *mut _ as *mut _); - if !o.is_null() { - self.operation_wait(ptr::null_mut(), o); - pa_operation_unref(o); + if let Ok(o) = context.get_source_info_list(add_input_device, &mut user_data as *mut _ as *mut _) { + self.operation_wait(None, &o); } } - pa_threaded_mainloop_unlock(self.mainloop); + self.mainloop.unlock(); } // Extract the array of cubeb_device_info from @@ -282,16 +398,16 @@ impl Context { coll.count); for dev in devices.iter_mut() { if !dev.device_id.is_null() { - pa_xfree(dev.device_id as *mut _); + let _ = CString::from_raw(dev.device_id as *mut _); } if !dev.group_id.is_null() { - pa_xfree(dev.group_id as *mut _); + let _ = CString::from_raw(dev.group_id as *mut _); } if !dev.vendor_name.is_null() { - pa_xfree(dev.vendor_name as *mut _); + let _ = CString::from_raw(dev.vendor_name as *mut _); } if !dev.friendly_name.is_null() { - pa_xfree(dev.friendly_name as *mut _); + let _ = CString::from_raw(dev.friendly_name as *mut _); } } } @@ -302,115 +418,125 @@ impl Context { cb: cubeb::DeviceCollectionChangedCallback, user_ptr: *mut c_void) -> i32 { - unsafe extern "C" fn subscribe_success(_: *mut pa_context, success: i32, user_data: *mut c_void) { - let ctx = &*(user_data as *mut Context); + fn update_collection(_: &pulse::Context, event: pulse::SubscriptionEvent, index: u32, user_data: *mut c_void) { + let mut ctx = unsafe { &mut *(user_data as *mut Context) }; + + let (f, t) = (event.event_facility(), event.event_type()); + match f { + pulse::SubscriptionEventFacility::Source | + pulse::SubscriptionEventFacility::Sink => { + match t { + pulse::SubscriptionEventType::Remove | + pulse::SubscriptionEventType::New => { + if cubeb::log_enabled() { + let op = if t == pulse::SubscriptionEventType::New { + "Adding" + } else { + "Removing" + }; + let dev = if f == pulse::SubscriptionEventFacility::Sink { + "sink" + } else { + "source " + }; + log!("{} {} index {}", op, dev, index); + + unsafe { + ctx.collection_changed_callback.unwrap()(ctx as *mut _ as *mut _, + ctx.collection_changed_user_ptr); + } + } + }, + _ => {}, + } + }, + _ => {}, + } + } + + fn success(_: &pulse::Context, success: i32, user_data: *mut c_void) { + let ctx = unsafe { &*(user_data as *mut Context) }; debug_assert_ne!(success, 0); - pa_threaded_mainloop_signal(ctx.mainloop, 0); + ctx.mainloop.signal(); } self.collection_changed_callback = cb; self.collection_changed_user_ptr = user_ptr; - unsafe { - pa_threaded_mainloop_lock(self.mainloop); + let user_data: *mut c_void = self as *mut _ as *mut _; + if let Some(ref context) = self.context { + self.mainloop.lock(); - let mut mask: pa_subscription_mask_t = PA_SUBSCRIPTION_MASK_NULL; + let mut mask = pulse::SubscriptionMask::empty(); if self.collection_changed_callback.is_none() { // Unregister subscription - pa_context_set_subscribe_callback(self.context, None, ptr::null_mut()); + context.clear_subscribe_callback(); } else { - pa_context_set_subscribe_callback(self.context, - Some(pulse_subscribe_callback), - self as *mut _ as *mut _); - if devtype == cubeb::DEVICE_TYPE_INPUT { - mask |= PA_SUBSCRIPTION_MASK_SOURCE + context.set_subscribe_callback(update_collection, user_data); + if devtype.contains(cubeb::DEVICE_TYPE_INPUT) { + mask |= pulse::SUBSCRIPTION_MASK_SOURCE }; - if devtype == cubeb::DEVICE_TYPE_OUTPUT { - mask |= PA_SUBSCRIPTION_MASK_SOURCE + if devtype.contains(cubeb::DEVICE_TYPE_OUTPUT) { + mask = pulse::SUBSCRIPTION_MASK_SINK }; } - let o = pa_context_subscribe(self.context, - mask, - Some(subscribe_success), - self as *const _ as *mut _); - if o.is_null() { + if let Ok(o) = context.subscribe(mask, success, self as *const _ as *mut _) { + self.operation_wait(None, &o); + } else { log!("Context subscribe failed"); return cubeb::ERROR; } - self.operation_wait(ptr::null_mut(), o); - pa_operation_unref(o); - pa_threaded_mainloop_unlock(self.mainloop); + self.mainloop.unlock(); } cubeb::OK } - // - - pub fn pulse_stream_cork(&self, stream: *mut pa_stream, state: CorkState) { - unsafe extern "C" fn cork_success(_: *mut pa_stream, _: i32, u: *mut c_void) { - let mainloop = u as *mut pa_threaded_mainloop; - pa_threaded_mainloop_signal(mainloop, 0); + pub fn context_init(&mut self) -> i32 { + fn error_state(c: &pulse::Context, u: *mut c_void) { + let mut ctx = unsafe { &mut *(u as *mut Context) }; + if !c.get_state().is_good() { + ctx.error = true; + } + ctx.mainloop.signal(); } - if stream.is_null() { - return; + if self.context.is_some() { + debug_assert!(self.error); + self.context_destroy(); } - let o = unsafe { - pa_stream_cork(stream, - state.is_cork() as i32, - Some(cork_success), - self.mainloop as *mut _) + self.context = { + let name = match self.context_name.as_ref() { + Some(s) => Some(s.as_ref()), + None => None, + }; + pulse::Context::new(&self.mainloop.get_api(), name) }; - if !o.is_null() { - self.operation_wait(stream, o); - unsafe { pa_operation_unref(o) }; + let context_ptr: *mut c_void = self as *mut _ as *mut _; + if self.context.is_none() { + return cubeb::ERROR; } - } - pub fn pulse_context_init(&mut self) -> i32 { - unsafe extern "C" fn error_state(c: *mut pa_context, u: *mut c_void) { - let mut ctx = &mut *(u as *mut Context); - if !PA_CONTEXT_IS_GOOD(pa_context_get_state(c)) { - ctx.error = true; - } - pa_threaded_mainloop_signal(ctx.mainloop, 0); + self.mainloop.lock(); + if let Some(ref context) = self.context { + context.set_state_callback(error_state, context_ptr); + let _ = context.connect(None, pulse::ContextFlags::empty(), ptr::null()); } - if !self.context.is_null() { - debug_assert!(self.error); - unsafe { self.pulse_context_destroy() }; + if !self.wait_until_context_ready() { + self.mainloop.unlock(); + self.context_destroy(); + return cubeb::ERROR; } - unsafe { - self.context = pa_context_new(pa_threaded_mainloop_get_api(self.mainloop), - self.context_name); - - if self.context.is_null() { - return cubeb::ERROR; - } - - pa_context_set_state_callback(self.context, Some(error_state), self as *mut _ as *mut _); - - pa_threaded_mainloop_lock(self.mainloop); - pa_context_connect(self.context, ptr::null(), 0, ptr::null()); - - if !self.wait_until_context_ready() { - pa_threaded_mainloop_unlock(self.mainloop); - self.pulse_context_destroy(); - assert!(self.context.is_null()); - return cubeb::ERROR; - } + self.mainloop.unlock(); - pa_threaded_mainloop_unlock(self.mainloop); - } - - let version_str = unsafe { CStr::from_ptr(pa_get_library_version()) }; - if let Ok(version) = semver::Version::parse(version_str.to_string_lossy().as_ref()) { + let version_str = unsafe { CStr::from_ptr(pulse::library_version()) }; + if let Ok(version) = semver::Version::parse(&version_str.to_string_lossy()) { self.version_0_9_8 = version >= semver::Version::parse("0.9.8").expect("Failed to parse version"); self.version_2_0_0 = version >= semver::Version::parse("2.0.0").expect("Failed to parse version"); } @@ -420,34 +546,42 @@ impl Context { cubeb::OK } - unsafe fn pulse_context_destroy(&mut self) { - unsafe extern "C" fn drain_complete(_c: *mut pa_context, u: *mut c_void) { - let mainloop = u as *mut pa_threaded_mainloop; - pa_threaded_mainloop_signal(mainloop, 0); - } - - pa_threaded_mainloop_lock(self.mainloop); - let o = pa_context_drain(self.context, Some(drain_complete), self.mainloop as *mut _); - if !o.is_null() { - self.operation_wait(ptr::null_mut(), o); - pa_operation_unref(o); - } - pa_context_set_state_callback(self.context, None, ptr::null_mut()); - pa_context_disconnect(self.context); - pa_context_unref(self.context); - self.context = ptr::null_mut(); - pa_threaded_mainloop_unlock(self.mainloop); + fn context_destroy(&mut self) { + fn drain_complete(_: &pulse::Context, u: *mut c_void) { + let ctx = unsafe { &*(u as *mut Context) }; + ctx.mainloop.signal(); + } + + let context_ptr: *mut c_void = self as *mut _ as *mut _; + match self.context.take() { + Some(ctx) => { + self.mainloop.lock(); + if let Ok(o) = ctx.drain(drain_complete, context_ptr) { + self.operation_wait(None, &o); + } + ctx.clear_state_callback(); + ctx.disconnect(); + ctx.unref(); + self.mainloop.unlock(); + }, + _ => {}, + } } - pub fn operation_wait(&self, stream: *mut pa_stream, o: *mut pa_operation) -> bool { - unsafe { - while pa_operation_get_state(o) == PA_OPERATION_RUNNING { - pa_threaded_mainloop_wait(self.mainloop); - if !PA_CONTEXT_IS_GOOD(pa_context_get_state(self.context)) { + pub fn operation_wait<'a, S>(&self, s: S, o: &pulse::Operation) -> bool + where S: Into> + { + let stream = s.into(); + while o.get_state() == PA_OPERATION_RUNNING { + self.mainloop.wait(); + if let Some(ref context) = self.context { + if !context.get_state().is_good() { return false; } + } - if !stream.is_null() && !PA_STREAM_IS_GOOD(pa_stream_get_state(stream)) { + if let Some(stm) = stream { + if !stm.get_state().is_good() { return false; } } @@ -457,36 +591,23 @@ impl Context { } pub fn wait_until_context_ready(&self) -> bool { - loop { - let state = unsafe { pa_context_get_state(self.context) }; - if !PA_CONTEXT_IS_GOOD(state) { - return false; - } - if state == PA_CONTEXT_READY { - break; - } - unsafe { - pa_threaded_mainloop_wait(self.mainloop); + if let Some(ref context) = self.context { + loop { + let state = context.get_state(); + if !state.is_good() { + return false; + } + if state == pulse::ContextState::Ready { + break; + } + self.mainloop.wait(); } } true } - fn state_from_sink_port(&self, i: *const pa_port_info) -> cubeb::DeviceState { - if !i.is_null() { - let info = unsafe { *i }; - if self.version_2_0_0 && info.available == PA_PORT_AVAILABLE_NO { - cubeb::DeviceState::Unplugged - } else { - cubeb::DeviceState::Enabled - } - } else { - cubeb::DeviceState::Enabled - } - } - - fn state_from_source_port(&self, i: *mut pa_port_info) -> cubeb::DeviceState { + fn state_from_port(&self, i: *const pa_port_info) -> cubeb::DeviceState { if !i.is_null() { let info = unsafe { *i }; if self.version_2_0_0 && info.available == PA_PORT_AVAILABLE_NO { @@ -500,62 +621,30 @@ impl Context { } } -// Callbacks -unsafe extern "C" fn server_info_callback(context: *mut pa_context, info: *const pa_server_info, u: *mut c_void) { - unsafe extern "C" fn sink_info_callback(_context: *mut pa_context, - info: *const pa_sink_info, - eol: i32, - u: *mut c_void) { - let mut ctx = &mut *(u as *mut Context); - if eol == 0 { - let info = *info; - ctx.default_sink_info = Some(DefaultInfo { - sample_spec: info.sample_spec, - channel_map: info.channel_map, - flags: info.flags, - }); - } - pa_threaded_mainloop_signal(ctx.mainloop, 0); - } - - let o = pa_context_get_sink_info_by_name(context, - (*info).default_sink_name, - Some(sink_info_callback), - u); - if !o.is_null() { - pa_operation_unref(o); - } -} - -struct PulseDevListData { - default_sink_name: *mut c_char, - default_source_name: *mut c_char, +struct PulseDevListData<'a> { + default_sink_name: CString, + default_source_name: CString, devinfo: Vec, - context: *mut Context, + context: &'a Context, } -impl Drop for PulseDevListData { - fn drop(&mut self) { - if !self.default_sink_name.is_null() { - unsafe { - pa_xfree(self.default_sink_name as *mut _); - } - } - if !self.default_source_name.is_null() { - unsafe { - pa_xfree(self.default_source_name as *mut _); - } +impl<'a> PulseDevListData<'a> { + pub fn new<'b>(context: &'b Context) -> Self + where 'b: 'a + { + PulseDevListData { + default_sink_name: CString::default(), + default_source_name: CString::default(), + devinfo: Vec::new(), + context: context, } } } -impl Default for PulseDevListData { - fn default() -> Self { - PulseDevListData { - default_sink_name: ptr::null_mut(), - default_source_name: ptr::null_mut(), - devinfo: Vec::new(), - context: ptr::null_mut(), +impl<'a> Drop for PulseDevListData<'a> { + fn drop(&mut self) { + for elem in &mut self.devinfo { + let _ = unsafe { Box::from_raw(elem) }; } } } @@ -566,192 +655,7 @@ fn pulse_format_to_cubeb_format(format: PA_SAMPLE_S16BE => cubeb::DEVICE_FMT_S16BE, PA_SAMPLE_FLOAT32LE => cubeb::DEVICE_FMT_F32LE, PA_SAMPLE_FLOAT32BE => cubeb::DEVICE_FMT_F32BE, - _ => { - panic!("Invalid format"); - }, + // Unsupported format, return F32NE + _ => cubeb::CUBEB_FMT_F32NE, } } - -unsafe extern "C" fn pulse_sink_info_cb(_context: *mut pa_context, - i: *const pa_sink_info, - eol: i32, - user_data: *mut c_void) { - if eol != 0 || i.is_null() { - return; - } - - debug_assert!(!user_data.is_null()); - - let info = *i; - let mut list_data = &mut *(user_data as *mut PulseDevListData); - - let device_id = pa_xstrdup(info.name); - - let group_id = { - let prop = pa_proplist_gets(info.proplist, b"sysfs.path\0".as_ptr() as *const c_char); - if !prop.is_null() { - pa_xstrdup(prop) - } else { - ptr::null_mut() - } - }; - - let vendor_name = { - let prop = pa_proplist_gets(info.proplist, - b"device.vendor.name\0".as_ptr() as *const c_char); - if !prop.is_null() { - pa_xstrdup(prop) - } else { - ptr::null_mut() - } - }; - - let preferred = if strcmp(info.name, list_data.default_sink_name) == 0 { - cubeb::DEVICE_PREF_ALL - } else { - cubeb::DevicePref::empty() - }; - - let ctx = &(*list_data.context); - - let devinfo = cubeb::DeviceInfo { - device_id: device_id, - devid: device_id as cubeb::DeviceId, - friendly_name: pa_xstrdup(info.description), - group_id: group_id, - vendor_name: vendor_name, - devtype: cubeb::DEVICE_TYPE_OUTPUT, - state: ctx.state_from_sink_port(info.active_port), - preferred: preferred, - format: cubeb::DeviceFmt::all(), - default_format: pulse_format_to_cubeb_format(info.sample_spec.format), - max_channels: info.channel_map.channels as u32, - min_rate: 1, - max_rate: PA_RATE_MAX, - default_rate: info.sample_spec.rate, - latency_lo: 0, - latency_hi: 0, - }; - list_data.devinfo.push(devinfo); - - pa_threaded_mainloop_signal(ctx.mainloop, 0); -} - -unsafe extern "C" fn pulse_source_info_cb(_context: *mut pa_context, - i: *const pa_source_info, - eol: i32, - user_data: *mut c_void) { - if eol != 0 || i.is_null() { - return; - } - - debug_assert!(!user_data.is_null()); - - let info = *i; - let mut list_data = &mut *(user_data as *mut PulseDevListData); - - let device_id = pa_xstrdup(info.name); - - let group_id = { - let prop = pa_proplist_gets(info.proplist, b"sysfs.path\0".as_ptr() as *mut c_char); - if !prop.is_null() { - pa_xstrdup(prop) - } else { - ptr::null_mut() - } - }; - - let vendor_name = { - let prop = pa_proplist_gets(info.proplist, - b"device.vendor.name\0".as_ptr() as *mut c_char); - if !prop.is_null() { - pa_xstrdup(prop) - } else { - ptr::null_mut() - } - }; - - let preferred = if strcmp(info.name, list_data.default_source_name) == 0 { - cubeb::DEVICE_PREF_ALL - } else { - cubeb::DevicePref::empty() - }; - - let ctx = &(*list_data.context); - - let devinfo = cubeb::DeviceInfo { - device_id: device_id, - devid: device_id as cubeb::DeviceId, - friendly_name: pa_xstrdup(info.description), - group_id: group_id, - vendor_name: vendor_name, - devtype: cubeb::DEVICE_TYPE_INPUT, - state: ctx.state_from_source_port(info.active_port), - preferred: preferred, - format: cubeb::DeviceFmt::all(), - default_format: pulse_format_to_cubeb_format(info.sample_spec.format), - max_channels: info.channel_map.channels as u32, - min_rate: 1, - max_rate: PA_RATE_MAX, - default_rate: info.sample_spec.rate, - latency_lo: 0, - latency_hi: 0, - }; - - list_data.devinfo.push(devinfo); - - pa_threaded_mainloop_signal(ctx.mainloop, 0); -} - -unsafe extern "C" fn pulse_server_info_cb(_context: *mut pa_context, - i: *const pa_server_info, - user_data: *mut c_void) { - assert!(!i.is_null()); - let info = *i; - let list_data = &mut *(user_data as *mut PulseDevListData); - - dup_str!(list_data.default_sink_name, info.default_sink_name); - dup_str!(list_data.default_source_name, info.default_source_name); - - pa_threaded_mainloop_signal((*list_data.context).mainloop, 0); -} - -unsafe extern "C" fn pulse_subscribe_callback(_ctx: *mut pa_context, - t: pa_subscription_event_type_t, - index: u32, - user_data: *mut c_void) { - let mut ctx = &mut *(user_data as *mut Context); - - match t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK { - PA_SUBSCRIPTION_EVENT_SOURCE | - PA_SUBSCRIPTION_EVENT_SINK => { - - if cubeb::log_enabled() { - if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE { - log!("Removing sink index %d", index); - } else if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW { - log!("Adding sink index %d", index); - } - if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE { - log!("Removing source index %d", index); - } else if (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW { - log!("Adding source index %d", index); - } - } - - if (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE || - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW { - ctx.collection_changed_callback.unwrap()(ctx as *mut _ as *mut _, ctx.collection_changed_user_ptr); - } - }, - _ => {}, - } -} - -extern "C" { - pub fn strcmp(cs: *const c_char, ct: *const c_char) -> c_int; -} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/mod.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/mod.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/mod.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/mod.rs 2017-08-04 13:37:46.386821731 +0200 @@ -7,8 +7,16 @@ mod context; mod cork_state; mod stream; +use std::os::raw::c_char; +use std::ffi::CStr; + pub type Result = ::std::result::Result; pub use self::context::Context; pub use self::stream::Device; pub use self::stream::Stream; + +// helper to convert *const c_char to Option +fn try_cstr_from<'str>(s: *const c_char) -> Option<&'str CStr> { + if s.is_null() { None } else { Some(unsafe { CStr::from_ptr(s) }) } +} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs 2017-08-04 13:37:46.387821728 +0200 @@ -6,8 +6,10 @@ use backend::*; use backend::cork_state::CorkState; use cubeb; +use pulse::{self, CVolumeExt, ChannelMapExt, SampleSpecExt, USecExt}; use pulse_ffi::*; -use std::os::raw::{c_char, c_long, c_void}; +use std::ffi::{CStr, CString}; +use std::os::raw::{c_long, c_void}; use std::ptr; const PULSE_NO_GAIN: f32 = -1.0; @@ -36,15 +38,12 @@ fn cubeb_channel_to_pa_channel(channel: MAP[idx as usize] } -fn layout_to_channel_map(layout: cubeb::ChannelLayout) -> pa_channel_map { +fn layout_to_channel_map(layout: cubeb::ChannelLayout) -> pulse::ChannelMap { assert_ne!(layout, cubeb::LAYOUT_UNDEFINED); let order = cubeb::mixer::channel_index_to_order(layout); - let mut cm: pa_channel_map = Default::default(); - unsafe { - pa_channel_map_init(&mut cm); - } + let mut cm = pulse::ChannelMap::init(); cm.channels = order.len() as u8; for (s, d) in order.iter().zip(cm.map.iter_mut()) { *d = cubeb_channel_to_pa_channel(*s); @@ -57,24 +56,27 @@ pub struct Device(cubeb::Device); impl Drop for Device { fn drop(&mut self) { unsafe { - pa_xfree(self.0.input_name as *mut _); - pa_xfree(self.0.output_name as *mut _); + if !self.0.input_name.is_null() { + let _ = CString::from_raw(self.0.input_name); + } + if !self.0.output_name.is_null() { + let _ = CString::from_raw(self.0.output_name); + } } } } - #[derive(Debug)] pub struct Stream<'ctx> { context: &'ctx Context, - output_stream: *mut pa_stream, - input_stream: *mut pa_stream, + output_stream: Option, + input_stream: Option, data_callback: cubeb::DataCallback, state_callback: cubeb::StateCallback, user_ptr: *mut c_void, drain_timer: *mut pa_time_event, - output_sample_spec: pa_sample_spec, - input_sample_spec: pa_sample_spec, + output_sample_spec: pulse::SampleSpec, + input_sample_spec: pulse::SampleSpec, shutdown: bool, volume: f32, state: cubeb::State, @@ -88,7 +90,7 @@ impl<'ctx> Drop for Stream<'ctx> { impl<'ctx> Stream<'ctx> { pub fn new(context: &'ctx Context, - stream_name: *const c_char, + stream_name: &CStr, input_device: cubeb::DeviceId, input_stream_params: Option, output_device: cubeb::DeviceId, @@ -99,83 +101,167 @@ impl<'ctx> Stream<'ctx> { user_ptr: *mut c_void) -> Result>> { + fn check_error(s: &pulse::Stream, u: *mut c_void) { + let stm = unsafe { &mut *(u as *mut Stream) }; + if !s.get_state().is_good() { + stm.state_change_callback(cubeb::STATE_ERROR); + } + stm.context.mainloop.signal(); + } + + fn read_data(s: &pulse::Stream, nbytes: usize, u: *mut c_void) { + fn read_from_input(s: &pulse::Stream, buffer: *mut *const c_void, size: *mut usize) -> i32 { + let readable_size: i32 = s.readable_size() + .and_then(|s| Ok(s as i32)) + .unwrap_or(-1); + if readable_size > 0 { + if unsafe { s.peek(buffer, size).is_err() } { + return -1; + } + } + readable_size + } + + logv!("Input callback buffer size {}", nbytes); + let mut stm = unsafe { &mut *(u as *mut Stream) }; + if stm.shutdown { + return; + } + + let mut read_data: *const c_void = ptr::null(); + let mut read_size: usize = 0; + while read_from_input(s, &mut read_data, &mut read_size) > 0 { + /* read_data can be NULL in case of a hole. */ + if !read_data.is_null() { + let in_frame_size = stm.input_sample_spec.frame_size(); + let read_frames = read_size / in_frame_size; + + if stm.output_stream.is_some() { + // input/capture + output/playback operation + let out_frame_size = stm.output_sample_spec.frame_size(); + let write_size = read_frames * out_frame_size; + // Offer full duplex data for writing + stm.trigger_user_callback(read_data, write_size); + } else { + // input/capture only operation. Call callback directly + let got = unsafe { + stm.data_callback.unwrap()(stm as *mut _ as *mut _, + stm.user_ptr, + read_data, + ptr::null_mut(), + read_frames as c_long) + }; + + if got < 0 || got as usize != read_frames { + let _ = s.cancel_write(); + stm.shutdown = true; + break; + } + } + } + + if read_size > 0 { + let _ = s.drop(); + } + + if stm.shutdown { + return; + } + } + } + + fn write_data(_: &pulse::Stream, nbytes: usize, u: *mut c_void) { + logv!("Output callback to be written buffer size {}", nbytes); + let mut stm = unsafe { &mut *(u as *mut Stream) }; + if stm.shutdown || stm.state != cubeb::STATE_STARTED { + return; + } + + if stm.input_stream.is_none() { + // Output/playback only operation. + // Write directly to output + debug_assert!(stm.output_stream.is_some()); + stm.trigger_user_callback(ptr::null(), nbytes); + } + } + let mut stm = Box::new(Stream { context: context, - output_stream: ptr::null_mut(), - input_stream: ptr::null_mut(), + output_stream: None, + input_stream: None, data_callback: data_callback, state_callback: state_callback, user_ptr: user_ptr, drain_timer: ptr::null_mut(), - output_sample_spec: pa_sample_spec::default(), - input_sample_spec: pa_sample_spec::default(), + output_sample_spec: pulse::SampleSpec::default(), + input_sample_spec: pulse::SampleSpec::default(), shutdown: false, volume: PULSE_NO_GAIN, state: cubeb::STATE_ERROR, }); - unsafe { - pa_threaded_mainloop_lock(stm.context.mainloop); + if let Some(ref context) = stm.context.context { + stm.context.mainloop.lock(); + + // Setup output stream if let Some(ref stream_params) = output_stream_params { - match stm.pulse_stream_init(stream_params, stream_name) { - Ok(s) => stm.output_stream = s, + match Stream::stream_init(context, stream_params, stream_name) { + Ok(s) => { + stm.output_sample_spec = *s.get_sample_spec(); + + s.set_state_callback(check_error, stm.as_mut() as *mut _ as *mut _); + s.set_write_callback(write_data, stm.as_mut() as *mut _ as *mut _); + + let battr = set_buffering_attribute(latency_frames, &stm.output_sample_spec); + let device_name = super::try_cstr_from(output_device as *const _); + let _ = s.connect_playback(device_name, + &battr, + pulse::STREAM_AUTO_TIMING_UPDATE | pulse::STREAM_INTERPOLATE_TIMING | + pulse::STREAM_START_CORKED | + pulse::STREAM_ADJUST_LATENCY, + None, + None); + + stm.output_stream = Some(s); + }, Err(e) => { - pa_threaded_mainloop_unlock(stm.context.mainloop); + stm.context.mainloop.unlock(); stm.destroy(); return Err(e); }, } - stm.output_sample_spec = *pa_stream_get_sample_spec(stm.output_stream); - - pa_stream_set_state_callback(stm.output_stream, - Some(stream_state_callback), - stm.as_mut() as *mut _ as *mut _); - pa_stream_set_write_callback(stm.output_stream, - Some(stream_write_callback), - stm.as_mut() as *mut _ as *mut _); - - let battr = set_buffering_attribute(latency_frames, &stm.output_sample_spec); - pa_stream_connect_playback(stm.output_stream, - output_device as *mut c_char, - &battr, - PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | - PA_STREAM_START_CORKED | - PA_STREAM_ADJUST_LATENCY, - ptr::null(), - ptr::null_mut()); } // Set up input stream if let Some(ref stream_params) = input_stream_params { - match stm.pulse_stream_init(stream_params, stream_name) { - Ok(s) => stm.input_stream = s, + match Stream::stream_init(context, stream_params, stream_name) { + Ok(s) => { + stm.input_sample_spec = *s.get_sample_spec(); + + s.set_state_callback(check_error, stm.as_mut() as *mut _ as *mut _); + s.set_read_callback(read_data, stm.as_mut() as *mut _ as *mut _); + + let battr = set_buffering_attribute(latency_frames, &stm.input_sample_spec); + let device_name = super::try_cstr_from(input_device as *const _); + let _ = s.connect_record(device_name, + &battr, + pulse::STREAM_AUTO_TIMING_UPDATE | pulse::STREAM_INTERPOLATE_TIMING | + pulse::STREAM_START_CORKED | + pulse::STREAM_ADJUST_LATENCY); + + stm.input_stream = Some(s); + }, Err(e) => { - pa_threaded_mainloop_unlock(stm.context.mainloop); + stm.context.mainloop.unlock(); stm.destroy(); return Err(e); }, } - stm.input_sample_spec = *(pa_stream_get_sample_spec(stm.input_stream)); - - pa_stream_set_state_callback(stm.input_stream, - Some(stream_state_callback), - stm.as_mut() as *mut _ as *mut _); - pa_stream_set_read_callback(stm.input_stream, - Some(stream_read_callback), - stm.as_mut() as *mut _ as *mut _); - - let battr = set_buffering_attribute(latency_frames, &stm.input_sample_spec); - pa_stream_connect_record(stm.input_stream, - input_device as *mut c_char, - &battr, - PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | - PA_STREAM_START_CORKED | - PA_STREAM_ADJUST_LATENCY); } - let r = if stm.wait_until_stream_ready() { + let r = if stm.wait_until_ready() { /* force a timing update now, otherwise timing info does not become valid until some point after initialization has completed. */ stm.update_timing_info() @@ -183,7 +269,7 @@ impl<'ctx> Stream<'ctx> { false }; - pa_threaded_mainloop_unlock(stm.context.mainloop); + stm.context.mainloop.unlock(); if !r { stm.destroy(); @@ -191,10 +277,10 @@ impl<'ctx> Stream<'ctx> { } if cubeb::log_enabled() { - if output_stream_params.is_some() { - let output_att = *pa_stream_get_buffer_attr(stm.output_stream); - log!("Output buffer attributes maxlength %u, tlength %u, \ - prebuf %u, minreq %u, fragsize %u", + if let Some(ref output_stream) = stm.output_stream { + let output_att = output_stream.get_buffer_attr(); + log!("Output buffer attributes maxlength {}, tlength {}, \ + prebuf {}, minreq {}, fragsize {}", output_att.maxlength, output_att.tlength, output_att.prebuf, @@ -202,10 +288,10 @@ impl<'ctx> Stream<'ctx> { output_att.fragsize); } - if input_stream_params.is_some() { - let input_att = *pa_stream_get_buffer_attr(stm.input_stream); - log!("Input buffer attributes maxlength %u, tlength %u, \ - prebuf %u, minreq %u, fragsize %u", + if let Some(ref input_stream) = stm.input_stream { + let input_att = input_stream.get_buffer_attr(); + log!("Input buffer attributes maxlength {}, tlength {}, \ + prebuf {}, minreq {}, fragsize {}", input_att.maxlength, input_att.tlength, input_att.prebuf, @@ -219,224 +305,250 @@ impl<'ctx> Stream<'ctx> { } fn destroy(&mut self) { - self.stream_cork(CorkState::cork()); + self.cork(CorkState::cork()); - unsafe { - pa_threaded_mainloop_lock(self.context.mainloop); - if !self.output_stream.is_null() { - if !self.drain_timer.is_null() { - /* there's no pa_rttime_free, so use this instead. */ - let ma = pa_threaded_mainloop_get_api(self.context.mainloop); - if !ma.is_null() { - (*ma).time_free.unwrap()(self.drain_timer); + self.context.mainloop.lock(); + { + match self.output_stream.take() { + Some(stm) => { + if !self.drain_timer.is_null() { + /* there's no pa_rttime_free, so use this instead. */ + self.context + .mainloop + .get_api() + .time_free(self.drain_timer); } - } - - pa_stream_set_state_callback(self.output_stream, None, ptr::null_mut()); - pa_stream_set_write_callback(self.output_stream, None, ptr::null_mut()); - pa_stream_disconnect(self.output_stream); - pa_stream_unref(self.output_stream); + stm.clear_state_callback(); + stm.clear_write_callback(); + let _ = stm.disconnect(); + stm.unref(); + }, + _ => {}, + } + + match self.input_stream.take() { + Some(stm) => { + stm.clear_state_callback(); + stm.clear_read_callback(); + let _ = stm.disconnect(); + stm.unref(); + }, + _ => {}, } - - if !self.input_stream.is_null() { - pa_stream_set_state_callback(self.input_stream, None, ptr::null_mut()); - pa_stream_set_read_callback(self.input_stream, None, ptr::null_mut()); - pa_stream_disconnect(self.input_stream); - pa_stream_unref(self.input_stream); - } - pa_threaded_mainloop_unlock(self.context.mainloop); } + self.context.mainloop.unlock(); } pub fn start(&mut self) -> i32 { + fn output_preroll(_: &pulse::MainloopApi, u: *mut c_void) { + let mut stm = unsafe { &mut *(u as *mut Stream) }; + if !stm.shutdown { + let size = stm.output_stream + .as_ref() + .map_or(0, |s| s.writable_size().unwrap_or(0)); + stm.trigger_user_callback(ptr::null_mut(), size); + } + } + self.shutdown = false; - self.stream_cork(CorkState::uncork() | CorkState::notify()); + self.cork(CorkState::uncork() | CorkState::notify()); - if !self.output_stream.is_null() && self.input_stream.is_null() { - unsafe { - /* On output only case need to manually call user cb once in order to make - * things roll. This is done via a defer event in order to execute it - * from PA server thread. */ - pa_threaded_mainloop_lock(self.context.mainloop); - pa_mainloop_api_once(pa_threaded_mainloop_get_api(self.context.mainloop), - Some(pulse_defer_event_cb), - self as *mut _ as *mut _); - pa_threaded_mainloop_unlock(self.context.mainloop); - } + if self.output_stream.is_some() && self.input_stream.is_none() { + /* On output only case need to manually call user cb once in order to make + * things roll. This is done via a defer event in order to execute it + * from PA server thread. */ + self.context.mainloop.lock(); + self.context + .mainloop + .get_api() + .once(output_preroll, self as *mut _ as *mut _); + self.context.mainloop.unlock(); } cubeb::OK } pub fn stop(&mut self) -> i32 { - unsafe { - pa_threaded_mainloop_lock(self.context.mainloop); + { + self.context.mainloop.lock(); self.shutdown = true; // If draining is taking place wait to finish while !self.drain_timer.is_null() { - pa_threaded_mainloop_wait(self.context.mainloop); + self.context.mainloop.wait(); } - pa_threaded_mainloop_unlock(self.context.mainloop); + self.context.mainloop.unlock(); } - self.stream_cork(CorkState::cork() | CorkState::notify()); + self.cork(CorkState::cork() | CorkState::notify()); cubeb::OK } pub fn position(&self) -> Result { - if self.output_stream.is_null() { - return Err(cubeb::ERROR); - } + let in_thread = self.context.mainloop.in_thread(); - let position = unsafe { - let in_thread = pa_threaded_mainloop_in_thread(self.context.mainloop); - - if in_thread == 0 { - pa_threaded_mainloop_lock(self.context.mainloop); - } - - let mut r_usec: pa_usec_t = Default::default(); - let r = pa_stream_get_time(self.output_stream, &mut r_usec); - if in_thread == 0 { - pa_threaded_mainloop_unlock(self.context.mainloop); - } - - if r != 0 { - return Err(cubeb::ERROR); - } + if !in_thread { + self.context.mainloop.lock(); + } - let bytes = pa_usec_to_bytes(r_usec, &self.output_sample_spec); - (bytes / pa_frame_size(&self.output_sample_spec)) as u64 + let r = match self.output_stream { + None => Err(cubeb::ERROR), + Some(ref stm) => { + match stm.get_time() { + Ok(r_usec) => { + let bytes = r_usec.to_bytes(&self.output_sample_spec); + Ok((bytes / self.output_sample_spec.frame_size()) as u64) + }, + Err(_) => Err(cubeb::ERROR), + } + }, }; - Ok(position) - } - pub fn latency(&self) -> Result { - if self.output_stream.is_null() { - return Err(cubeb::ERROR); + if !in_thread { + self.context.mainloop.unlock(); } - let mut r_usec: pa_usec_t = 0; - let mut negative: i32 = 0; - let r = unsafe { pa_stream_get_latency(self.output_stream, &mut r_usec, &mut negative) }; + r + } - if r != 0 { - return Err(cubeb::ERROR); + pub fn latency(&self) -> Result { + match self.output_stream { + None => Err(cubeb::ERROR), + Some(ref stm) => { + match stm.get_latency() { + Ok((r_usec, negative)) => { + debug_assert!(negative); + let latency = (r_usec * self.output_sample_spec.rate as pa_usec_t / PA_USEC_PER_SEC) as u32; + Ok(latency) + }, + Err(_) => Err(cubeb::ERROR), + } + }, } - - debug_assert_eq!(negative, 0); - let latency = (r_usec * self.output_sample_spec.rate as pa_usec_t / PA_USEC_PER_SEC) as u32; - - Ok(latency) } pub fn set_volume(&mut self, volume: f32) -> i32 { - if self.output_stream.is_null() { - return cubeb::ERROR; - } - - unsafe { - pa_threaded_mainloop_lock(self.context.mainloop); - - while self.context.default_sink_info.is_none() { - pa_threaded_mainloop_wait(self.context.mainloop); - } - - let mut cvol: pa_cvolume = Default::default(); - - /* if the pulse daemon is configured to use flat volumes, - * apply our own gain instead of changing the input volume on the sink. */ - let flags = { - match self.context.default_sink_info { - Some(ref info) => info.flags, - _ => 0, - } - }; - - if (flags & PA_SINK_FLAT_VOLUME) != 0 { - self.volume = volume; - } else { - let ss = pa_stream_get_sample_spec(self.output_stream); - let vol = pa_sw_volume_from_linear(volume as f64); - pa_cvolume_set(&mut cvol, (*ss).channels as u32, vol); - - let index = pa_stream_get_index(self.output_stream); + match self.output_stream { + None => cubeb::ERROR, + Some(ref stm) => { + if let Some(ref context) = self.context.context { + self.context.mainloop.lock(); + + let mut cvol: pa_cvolume = Default::default(); + + /* if the pulse daemon is configured to use flat + * volumes, apply our own gain instead of changing + * the input volume on the sink. */ + let flags = { + match self.context.default_sink_info { + Some(ref info) => info.flags, + _ => pulse::SinkFlags::empty(), + } + }; + + if flags.contains(pulse::SINK_FLAT_VOLUME) { + self.volume = volume; + } else { + let channels = stm.get_sample_spec().channels; + let vol = pulse::sw_volume_from_linear(volume as f64); + cvol.set(channels as u32, vol); + + let index = stm.get_index(); + + let context_ptr = self.context as *const _ as *mut _; + if let Ok(o) = context.set_sink_input_volume(index, &cvol, context_success, context_ptr) { + self.context.operation_wait(stm, &o); + } + } - let op = pa_context_set_sink_input_volume(self.context.context, - index, - &cvol, - Some(volume_success), - self as *mut _ as *mut _); - if !op.is_null() { - self.context.operation_wait(self.output_stream, op); - pa_operation_unref(op); + self.context.mainloop.unlock(); + cubeb::OK + } else { + cubeb::ERROR } - } - - pa_threaded_mainloop_unlock(self.context.mainloop); + }, } - cubeb::OK } pub fn set_panning(&mut self, panning: f32) -> i32 { - if self.output_stream.is_null() { - return cubeb::ERROR; - } + #[repr(C)] + struct SinkInputInfoResult<'a> { + pub cvol: pulse::CVolume, + pub mainloop: &'a pulse::ThreadedMainloop, + } + + fn get_input_volume(_: &pulse::Context, info: *const pulse::SinkInputInfo, eol: i32, u: *mut c_void) { + let mut r = unsafe { &mut *(u as *mut SinkInputInfoResult) }; + if eol == 0 { + let info = unsafe { *info }; + r.cvol = info.volume; + } + r.mainloop.signal(); + } + + match self.output_stream { + None => cubeb::ERROR, + Some(ref stm) => { + if let Some(ref context) = self.context.context { + self.context.mainloop.lock(); + + let map = stm.get_channel_map(); + if !map.can_balance() { + self.context.mainloop.unlock(); + return cubeb::ERROR; + } - unsafe { - pa_threaded_mainloop_lock(self.context.mainloop); + let index = stm.get_index(); - let map = pa_stream_get_channel_map(self.output_stream); - if pa_channel_map_can_balance(map) == 0 { - pa_threaded_mainloop_unlock(self.context.mainloop); - return cubeb::ERROR; - } + let mut r = SinkInputInfoResult { + cvol: pulse::CVolume::default(), + mainloop: &self.context.mainloop, + }; - let index = pa_stream_get_index(self.output_stream); + if let Ok(o) = context.get_sink_input_info(index, get_input_volume, &mut r as *mut _ as *mut _) { + self.context.operation_wait(stm, &o); + } - let mut cvol: pa_cvolume = Default::default(); - let mut r = SinkInputInfoResult { - cvol: &mut cvol, - mainloop: self.context.mainloop, - }; + r.cvol.set_balance(map, panning); - let op = pa_context_get_sink_input_info(self.context.context, - index, - Some(sink_input_info_cb), - &mut r as *mut _ as *mut _); - if !op.is_null() { - self.context.operation_wait(self.output_stream, op); - pa_operation_unref(op); - } - - pa_cvolume_set_balance(&mut cvol, map, panning); - - let op = pa_context_set_sink_input_volume(self.context.context, - index, - &cvol, - Some(volume_success), - self as *mut _ as *mut _); - if !op.is_null() { - self.context.operation_wait(self.output_stream, op); - pa_operation_unref(op); - } + let context_ptr = self.context as *const _ as *mut _; + if let Ok(o) = context.set_sink_input_volume(index, &r.cvol, context_success, context_ptr) { + self.context.operation_wait(stm, &o); + } - pa_threaded_mainloop_unlock(self.context.mainloop); - } + self.context.mainloop.unlock(); - cubeb::OK + cubeb::OK + } else { + cubeb::ERROR + } + }, + } } pub fn current_device(&self) -> Result> { if self.context.version_0_9_8 { let mut dev = Box::new(cubeb::Device::default()); - if !self.input_stream.is_null() { - dev.input_name = unsafe { pa_xstrdup(pa_stream_get_device_name(self.input_stream)) }; + if self.input_stream.is_some() { + if let Some(ref stm) = self.input_stream { + dev.input_name = match stm.get_device_name() { + Ok(name) => name.to_owned().into_raw(), + Err(_) => { + return Err(cubeb::ERROR); + }, + } + } } - if !self.output_stream.is_null() { - dev.output_name = unsafe { pa_xstrdup(pa_stream_get_device_name(self.output_stream)) }; + if !self.output_stream.is_some() { + if let Some(ref stm) = self.output_stream { + dev.output_name = match stm.get_device_name() { + Ok(name) => name.to_owned().into_raw(), + Err(_) => { + return Err(cubeb::ERROR); + }, + } + } } Ok(dev) @@ -445,51 +557,62 @@ impl<'ctx> Stream<'ctx> { } } - fn pulse_stream_init(&mut self, - stream_params: &cubeb::StreamParams, - stream_name: *const c_char) - -> Result<*mut pa_stream> { + fn stream_init(context: &pulse::Context, + stream_params: &cubeb::StreamParams, + stream_name: &CStr) + -> Result { - fn to_pulse_format(format: cubeb::SampleFormat) -> pa_sample_format_t { + fn to_pulse_format(format: cubeb::SampleFormat) -> pulse::SampleFormat { match format { - cubeb::SAMPLE_S16LE => PA_SAMPLE_S16LE, - cubeb::SAMPLE_S16BE => PA_SAMPLE_S16BE, - cubeb::SAMPLE_FLOAT32LE => PA_SAMPLE_FLOAT32LE, - cubeb::SAMPLE_FLOAT32BE => PA_SAMPLE_FLOAT32BE, - _ => panic!("Invalid format: {:?}", format), + cubeb::SAMPLE_S16LE => pulse::SampleFormat::Signed16LE, + cubeb::SAMPLE_S16BE => pulse::SampleFormat::Signed16BE, + cubeb::SAMPLE_FLOAT32LE => pulse::SampleFormat::Float32LE, + cubeb::SAMPLE_FLOAT32BE => pulse::SampleFormat::Float32BE, + _ => pulse::SampleFormat::Invalid, } } let fmt = to_pulse_format(stream_params.format); - if fmt == PA_SAMPLE_INVALID { + if fmt == pulse::SampleFormat::Invalid { return Err(cubeb::ERROR_INVALID_FORMAT); } - let ss = pa_sample_spec { + let ss = pulse::SampleSpec { channels: stream_params.channels as u8, - format: fmt, + format: fmt.into(), rate: stream_params.rate, }; - let stream = if stream_params.layout == cubeb::LAYOUT_UNDEFINED { - unsafe { pa_stream_new(self.context.context, stream_name, &ss, ptr::null_mut()) } - } else { - let cm = layout_to_channel_map(stream_params.layout); - unsafe { pa_stream_new(self.context.context, stream_name, &ss, &cm) } + let cm: Option = match stream_params.layout { + cubeb::LAYOUT_UNDEFINED => None, + _ => Some(layout_to_channel_map(stream_params.layout)), }; - if !stream.is_null() { - Ok(stream) - } else { - Err(cubeb::ERROR) + let stream = pulse::Stream::new(context, stream_name, &ss, cm.as_ref()); + + match stream { + None => Err(cubeb::ERROR), + Some(stm) => Ok(stm), + } + } + + pub fn cork_stream(&self, stream: Option<&pulse::Stream>, state: CorkState) { + if let Some(stm) = stream { + if let Ok(o) = stm.cork(state.is_cork() as i32, + stream_success, + self as *const _ as *mut _) { + self.context.operation_wait(stream, &o); + } } } - fn stream_cork(&mut self, state: CorkState) { - unsafe { pa_threaded_mainloop_lock(self.context.mainloop) }; - self.context.pulse_stream_cork(self.output_stream, state); - self.context.pulse_stream_cork(self.input_stream, state); - unsafe { pa_threaded_mainloop_unlock(self.context.mainloop) }; + fn cork(&mut self, state: CorkState) { + { + self.context.mainloop.lock(); + self.cork_stream(self.output_stream.as_ref(), state); + self.cork_stream(self.input_stream.as_ref(), state); + self.context.mainloop.unlock() + } if state.is_notify() { self.state_change_callback(if state.is_cork() { @@ -503,18 +626,9 @@ impl<'ctx> Stream<'ctx> { fn update_timing_info(&self) -> bool { let mut r = false; - if !self.output_stream.is_null() { - let o = unsafe { - pa_stream_update_timing_info(self.output_stream, - Some(stream_success_callback), - self as *const _ as *mut _) - }; - - if !o.is_null() { - r = self.context.operation_wait(self.output_stream, o); - unsafe { - pa_operation_unref(o); - } + if let Some(ref stm) = self.output_stream { + if let Ok(o) = stm.update_timing_info(stream_success, self as *const _ as *mut _) { + r = self.context.operation_wait(stm, &o); } if !r { @@ -522,18 +636,9 @@ impl<'ctx> Stream<'ctx> { } } - if !self.input_stream.is_null() { - let o = unsafe { - pa_stream_update_timing_info(self.input_stream, - Some(stream_success_callback), - self as *const _ as *mut _) - }; - - if !o.is_null() { - r = self.context.operation_wait(self.input_stream, o); - unsafe { - pa_operation_unref(o); - } + if let Some(ref stm) = self.input_stream { + if let Ok(o) = stm.update_timing_info(stream_success, self as *const _ as *mut _) { + r = self.context.operation_wait(stm, &o); } } @@ -547,232 +652,162 @@ impl<'ctx> Stream<'ctx> { } } - fn wait_until_stream_ready(&self) -> bool { - if !self.output_stream.is_null() && !wait_until_io_stream_ready(self.output_stream, self.context.mainloop) { - return false; - } - - if !self.input_stream.is_null() && !wait_until_io_stream_ready(self.input_stream, self.context.mainloop) { - return false; - } - - true - } - - fn trigger_user_callback(&mut self, s: *mut pa_stream, input_data: *const c_void, nbytes: usize) { - let frame_size = unsafe { pa_frame_size(&self.output_sample_spec) }; - debug_assert_eq!(nbytes % frame_size, 0); - - let mut buffer: *mut c_void = ptr::null_mut(); - let mut r: i32; - - let mut towrite = nbytes; - let mut read_offset = 0usize; - while towrite > 0 { - let mut size = towrite; - r = unsafe { pa_stream_begin_write(s, &mut buffer, &mut size) }; - // Note: this has failed running under rr on occassion - needs investigation. - debug_assert_eq!(r, 0); - debug_assert!(size > 0); - debug_assert_eq!(size % frame_size, 0); - - logv!("Trigger user callback with output buffer size={}, read_offset={}", - size, - read_offset); - let read_ptr = unsafe { (input_data as *const u8).offset(read_offset as isize) }; - let got = unsafe { - self.data_callback.unwrap()(self as *const _ as *mut _, - self.user_ptr, - read_ptr as *const _ as *mut _, - buffer, - (size / frame_size) as c_long) - }; - if got < 0 { - unsafe { - pa_stream_cancel_write(s); - } - self.shutdown = true; - return; - } - // If more iterations move offset of read buffer - if !input_data.is_null() { - let in_frame_size = unsafe { pa_frame_size(&self.input_sample_spec) }; - read_offset += (size / frame_size) * in_frame_size; + fn wait_until_ready(&self) -> bool { + fn wait_until_io_stream_ready(stm: &pulse::Stream, mainloop: &pulse::ThreadedMainloop) -> bool { + if mainloop.is_null() { + return false; } - if self.volume != PULSE_NO_GAIN { - let samples = (self.output_sample_spec.channels as usize * size / frame_size) as isize; - - if self.output_sample_spec.format == PA_SAMPLE_S16BE || - self.output_sample_spec.format == PA_SAMPLE_S16LE { - let b = buffer as *mut i16; - for i in 0..samples { - unsafe { *b.offset(i) *= self.volume as i16 }; - } - } else { - let b = buffer as *mut f32; - for i in 0..samples { - unsafe { *b.offset(i) *= self.volume }; - } + loop { + let state = stm.get_state(); + if !state.is_good() { + return false; + } + if state == pulse::StreamState::Ready { + break; } + mainloop.wait(); } - r = unsafe { - pa_stream_write(s, - buffer, - got as usize * frame_size, - None, - 0, - PA_SEEK_RELATIVE) - }; - debug_assert_eq!(r, 0); + true + } - if (got as usize) < size / frame_size { - let mut latency: pa_usec_t = 0; - let rr: i32 = unsafe { pa_stream_get_latency(s, &mut latency, ptr::null_mut()) }; - if rr == -(PA_ERR_NODATA as i32) { - /* this needs a better guess. */ - latency = 100 * PA_USEC_PER_MSEC; - } - debug_assert!(r == 0 || r == -(PA_ERR_NODATA as i32)); - /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */ - /* arbitrary safety margin: double the current latency. */ - debug_assert!(self.drain_timer.is_null()); - self.drain_timer = unsafe { - pa_context_rttime_new(self.context.context, - pa_rtclock_now() + 2 * latency, - Some(stream_drain_callback), - self as *const _ as *mut _) - }; - self.shutdown = true; - return; + if let Some(ref stm) = self.output_stream { + if !wait_until_io_stream_ready(stm, &self.context.mainloop) { + return false; } - - towrite -= size; } - debug_assert_eq!(towrite, 0); - } -} - -unsafe extern "C" fn stream_success_callback(_s: *mut pa_stream, _success: i32, u: *mut c_void) { - let stm = &*(u as *mut Stream); - pa_threaded_mainloop_signal(stm.context.mainloop, 0); -} - -unsafe extern "C" fn stream_drain_callback(a: *mut pa_mainloop_api, - e: *mut pa_time_event, - _tv: *const timeval, - u: *mut c_void) { - let mut stm = &mut *(u as *mut Stream); - debug_assert_eq!(stm.drain_timer, e); - stm.state_change_callback(cubeb::STATE_DRAINED); - /* there's no pa_rttime_free, so use this instead. */ - (*a).time_free.unwrap()(stm.drain_timer); - stm.drain_timer = ptr::null_mut(); - pa_threaded_mainloop_signal(stm.context.mainloop, 0); -} - -unsafe extern "C" fn stream_state_callback(s: *mut pa_stream, u: *mut c_void) { - let stm = &mut *(u as *mut Stream); - if !PA_STREAM_IS_GOOD(pa_stream_get_state(s)) { - stm.state_change_callback(cubeb::STATE_ERROR); - } - pa_threaded_mainloop_signal(stm.context.mainloop, 0); -} - -fn read_from_input(s: *mut pa_stream, buffer: *mut *const c_void, size: *mut usize) -> i32 { - let readable_size = unsafe { pa_stream_readable_size(s) }; - if readable_size > 0 && unsafe { pa_stream_peek(s, buffer, size) } < 0 { - return -1; - } - - readable_size as i32 -} + if let Some(ref stm) = self.input_stream { + if !wait_until_io_stream_ready(stm, &self.context.mainloop) { + return false; + } + } -unsafe extern "C" fn stream_write_callback(s: *mut pa_stream, nbytes: usize, u: *mut c_void) { - logv!("Output callback to be written buffer size {}", nbytes); - let mut stm = &mut *(u as *mut Stream); - if stm.shutdown || stm.state != cubeb::STATE_STARTED { - return; + true } - if stm.input_stream.is_null() { - // Output/playback only operation. - // Write directly to output - debug_assert!(!stm.output_stream.is_null()); - stm.trigger_user_callback(s, ptr::null(), nbytes); - } -} + fn trigger_user_callback(&mut self, input_data: *const c_void, nbytes: usize) { + fn drained_cb(a: &pulse::MainloopApi, e: *mut pa_time_event, _tv: &pulse::TimeVal, u: *mut c_void) { + let mut stm = unsafe { &mut *(u as *mut Stream) }; + debug_assert_eq!(stm.drain_timer, e); + stm.state_change_callback(cubeb::STATE_DRAINED); + /* there's no pa_rttime_free, so use this instead. */ + a.time_free(stm.drain_timer); + stm.drain_timer = ptr::null_mut(); + stm.context.mainloop.signal(); + } + + if let Some(ref stm) = self.output_stream { + + let frame_size = self.output_sample_spec.frame_size(); + debug_assert_eq!(nbytes % frame_size, 0); + + let mut towrite = nbytes; + let mut read_offset = 0usize; + while towrite > 0 { + match stm.begin_write(towrite) { + Err(e) => { + panic!("Failed to write data: {}", e); + }, + Ok((buffer, size)) => { + debug_assert!(size > 0); + debug_assert_eq!(size % frame_size, 0); + + logv!("Trigger user callback with output buffer size={}, read_offset={}", + size, + read_offset); + let read_ptr = unsafe { (input_data as *const u8).offset(read_offset as isize) }; + let got = unsafe { + self.data_callback.unwrap()(self as *const _ as *mut _, + self.user_ptr, + read_ptr as *const _ as *mut _, + buffer, + (size / frame_size) as c_long) + }; + if got < 0 { + let _ = stm.cancel_write(); + self.shutdown = true; + return; + } + + // If more iterations move offset of read buffer + if !input_data.is_null() { + let in_frame_size = self.input_sample_spec.frame_size(); + read_offset += (size / frame_size) * in_frame_size; + } + + if self.volume != PULSE_NO_GAIN { + let samples = (self.output_sample_spec.channels as usize * size / frame_size) as isize; + + if self.output_sample_spec.format == PA_SAMPLE_S16BE || + self.output_sample_spec.format == PA_SAMPLE_S16LE { + let b = buffer as *mut i16; + for i in 0..samples { + unsafe { *b.offset(i) *= self.volume as i16 }; + } + } else { + let b = buffer as *mut f32; + for i in 0..samples { + unsafe { *b.offset(i) *= self.volume }; + } + } + } + + let r = stm.write(buffer, + got as usize * frame_size, + 0, + pulse::SeekMode::Relative); + debug_assert!(r.is_ok()); + + if (got as usize) < size / frame_size { + let latency = match stm.get_latency() { + Ok((l, negative)) => { + assert_ne!(negative, true); + l + }, + Err(e) => { + debug_assert_eq!(e, pulse::ErrorCode::from_error_code(PA_ERR_NODATA)); + /* this needs a better guess. */ + 100 * PA_USEC_PER_MSEC + }, + }; + + /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */ + /* arbitrary safety margin: double the current latency. */ + debug_assert!(self.drain_timer.is_null()); + let stream_ptr = self as *const _ as *mut _; + if let Some(ref context) = self.context.context { + self.drain_timer = + context.rttime_new(pulse::rtclock_now() + 2 * latency, drained_cb, stream_ptr); + } + self.shutdown = true; + return; + } -unsafe extern "C" fn stream_read_callback(s: *mut pa_stream, nbytes: usize, u: *mut c_void) { - logv!("Input callback buffer size {}", nbytes); - let mut stm = &mut *(u as *mut Stream); - if stm.shutdown { - return; - } - - let mut read_data: *const c_void = ptr::null(); - let mut read_size: usize = 0; - while read_from_input(s, &mut read_data, &mut read_size) > 0 { - /* read_data can be NULL in case of a hole. */ - if !read_data.is_null() { - let in_frame_size = pa_frame_size(&stm.input_sample_spec); - let read_frames = read_size / in_frame_size; - - if !stm.output_stream.is_null() { - // input/capture + output/playback operation - let out_frame_size = pa_frame_size(&stm.output_sample_spec); - let write_size = read_frames * out_frame_size; - // Offer full duplex data for writing - let stream = stm.output_stream; - stm.trigger_user_callback(stream, read_data, write_size); - } else { - // input/capture only operation. Call callback directly - let got = stm.data_callback.unwrap()(stm as *mut _ as *mut _, - stm.user_ptr, - read_data, - ptr::null_mut(), - read_frames as c_long); - if got < 0 || got as usize != read_frames { - pa_stream_cancel_write(s); - stm.shutdown = true; - break; + towrite -= size; + }, } } - } - - if read_size > 0 { - pa_stream_drop(s); - } - - if stm.shutdown { - return; + debug_assert_eq!(towrite, 0); } } } -fn wait_until_io_stream_ready(stream: *mut pa_stream, mainloop: *mut pa_threaded_mainloop) -> bool { - if stream.is_null() || mainloop.is_null() { - return false; - } - - loop { - let state = unsafe { pa_stream_get_state(stream) }; - if !PA_STREAM_IS_GOOD(state) { - return false; - } - if state == PA_STREAM_READY { - break; - } - unsafe { pa_threaded_mainloop_wait(mainloop) }; - } +fn stream_success(_: &pulse::Stream, success: i32, u: *mut c_void) { + let stm = unsafe { &*(u as *mut Stream) }; + debug_assert_ne!(success, 0); + stm.context.mainloop.signal(); +} - true +fn context_success(_: &pulse::Context, success: i32, u: *mut c_void) { + let ctx = unsafe { &*(u as *mut Context) }; + debug_assert_ne!(success, 0); + ctx.mainloop.signal(); } fn set_buffering_attribute(latency_frames: u32, sample_spec: &pa_sample_spec) -> pa_buffer_attr { - let tlength = latency_frames * unsafe { pa_frame_size(sample_spec) } as u32; + let tlength = latency_frames * sample_spec.frame_size() as u32; let minreq = tlength / 4; let battr = pa_buffer_attr { maxlength: u32::max_value(), @@ -791,34 +826,3 @@ fn set_buffering_attribute(latency_frame battr } - -unsafe extern "C" fn pulse_defer_event_cb(_a: *mut pa_mainloop_api, u: *mut c_void) { - let mut stm = &mut *(u as *mut Stream); - if stm.shutdown { - return; - } - let writable_size = pa_stream_writable_size(stm.output_stream); - let stream = stm.output_stream; - stm.trigger_user_callback(stream, ptr::null_mut(), writable_size); -} - -#[repr(C)] -struct SinkInputInfoResult { - pub cvol: *mut pa_cvolume, - pub mainloop: *mut pa_threaded_mainloop, -} - -unsafe extern "C" fn sink_input_info_cb(_c: *mut pa_context, i: *const pa_sink_input_info, eol: i32, u: *mut c_void) { - let info = &*i; - let mut r = &mut *(u as *mut SinkInputInfoResult); - if eol == 0 { - *r.cvol = info.volume; - } - pa_threaded_mainloop_signal(r.mainloop, 0); -} - -unsafe extern "C" fn volume_success(_c: *mut pa_context, success: i32, u: *mut c_void) { - let stm = &*(u as *mut Stream); - debug_assert_ne!(success, 0); - pa_threaded_mainloop_signal(stm.context.mainloop, 0); -} diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/capi.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/capi.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/capi.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/capi.rs 2017-08-04 13:37:46.387821728 +0200 @@ -5,6 +5,7 @@ use backend; use cubeb; +use std::ffi::CStr; use std::os::raw::{c_char, c_void}; unsafe extern "C" fn capi_init(c: *mut *mut cubeb::Context, context_name: *const c_char) -> i32 { @@ -114,21 +115,18 @@ unsafe extern "C" fn capi_stream_init(c: state_callback: cubeb::StateCallback, user_ptr: *mut c_void) -> i32 { + fn try_stream_params_from(sp: *mut cubeb::StreamParams) -> Option { + if sp.is_null() { None } else { Some(unsafe { *sp }) } + } + let mut ctx = &mut *(c as *mut backend::Context); + let stream_name = CStr::from_ptr(stream_name); match ctx.new_stream(stream_name, input_device, - if input_stream_params.is_null() { - None - } else { - Some(*input_stream_params) - }, + try_stream_params_from(input_stream_params), output_device, - if output_stream_params.is_null() { - None - } else { - Some(*output_stream_params) - }, + try_stream_params_from(output_stream_params), latency_frames, data_callback, state_callback, diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/lib.rs.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/lib.rs --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/lib.rs.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/src/lib.rs 2017-08-04 13:37:46.387821728 +0200 @@ -8,6 +8,7 @@ #[macro_use] extern crate cubeb_ffi as cubeb; extern crate pulse_ffi; +extern crate pulse; extern crate semver; mod capi; diff -up firefox-55.0/media/libcubeb/cubeb-pulse-rs/update.sh.cubeb-pulse-arm firefox-55.0/media/libcubeb/cubeb-pulse-rs/update.sh --- firefox-55.0/media/libcubeb/cubeb-pulse-rs/update.sh.cubeb-pulse-arm 2017-07-31 18:20:49.000000000 +0200 +++ firefox-55.0/media/libcubeb/cubeb-pulse-rs/update.sh 2017-08-04 13:37:46.383821740 +0200 @@ -13,6 +13,9 @@ cp -pr $1/cubeb-ffi/src/* cubeb-ffi/src/ test -d pulse-ffi/src || mkdir -p pulse-ffi/src cp -pr $1/pulse-ffi/Cargo.toml pulse-ffi/ cp -pr $1/pulse-ffi/src/* pulse-ffi/src/ +test -d pulse-rs/src || mkdir -p pulse-rs/src +cp -pr $1/pulse-rs/Cargo.toml pulse-rs/ +cp -pr $1/pulse-rs/src/* pulse-rs/src/ if [ -d $1/.git ]; then rev=$(cd $1 && git rev-parse --verify HEAD) diff -up firefox-55.0/toolkit/library/gtest/rust/Cargo.lock.cubeb-pulse-arm firefox-55.0/toolkit/library/gtest/rust/Cargo.lock --- firefox-55.0/toolkit/library/gtest/rust/Cargo.lock.cubeb-pulse-arm 2017-08-04 13:37:46.388821725 +0200 +++ firefox-55.0/toolkit/library/gtest/rust/Cargo.lock 2017-08-04 13:59:15.592940994 +0200 @@ -252,6 +252,7 @@ name = "cubeb-pulse" version = "0.0.1" dependencies = [ "cubeb-ffi 0.0.1", + "pulse 0.1.0", "pulse-ffi 0.1.0", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -660,6 +661,14 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "pulse" +version = "0.1.0" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pulse-ffi 0.1.0", +] + +[[package]] name = "pulse-ffi" version = "0.1.0" dependencies = [ diff -up firefox-55.0/toolkit/library/rust/Cargo.lock.cubeb-pulse-arm firefox-55.0/toolkit/library/rust/Cargo.lock --- firefox-55.0/toolkit/library/rust/Cargo.lock.cubeb-pulse-arm 2017-08-04 13:37:46.388821725 +0200 +++ firefox-55.0/toolkit/library/rust/Cargo.lock 2017-08-04 13:52:24.551163669 +0200 @@ -250,6 +250,7 @@ name = "cubeb-pulse" version = "0.0.1" dependencies = [ "cubeb-ffi 0.0.1", + "pulse 0.1.0", "pulse-ffi 0.1.0", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -647,6 +648,14 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "pulse" +version = "0.1.0" +dependencies = [ + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pulse-ffi 0.1.0", +] + +[[package]] name = "pulse-ffi" version = "0.1.0" dependencies = [