diff --git a/build-cubeb-pulse-arm.patch b/build-cubeb-pulse-arm.patch new file mode 100644 index 0000000..008208c --- /dev/null +++ b/build-cubeb-pulse-arm.patch @@ -0,0 +1,4946 @@ +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 = [ diff --git a/firefox.spec b/firefox.spec index 78ee592..da83481 100644 --- a/firefox.spec +++ b/firefox.spec @@ -1,5 +1,5 @@ -#Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=1387010 -ExcludeArch: aarch64 armv7hl +#Disabled due to build failures +ExcludeArch: ppc ppc64 ppc64le # Use ALSA backend? %define alsa_backend 0 @@ -130,6 +130,7 @@ Patch30: fedora-build.patch Patch31: build-ppc64-s390x-curl.patch Patch32: build-rust-ppc64le.patch Patch33: build-ppc-s390-dom.patch +Patch34: build-cubeb-pulse-arm.patch # Fedora specific patches # Unable to install addons from https pages @@ -304,6 +305,7 @@ This package contains results of tests executed during build. %patch31 -p1 -b .ppc64-s390x-curl %patch32 -p1 -b .rust-ppc64le %patch33 -p1 -b .ppc-s390-dom +%patch34 -p1 -b .cubeb-pulse-arm %patch3 -p1 -b .arm