summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Stransky <stransky@redhat.com>2017-08-04 14:34:59 +0200
committerMartin Stransky <stransky@redhat.com>2017-08-04 14:34:59 +0200
commit54cf05b5a37859e8565a679783d65651265ad7a0 (patch)
treea766a34dff5017835bacf161537a73d8e2423889
parentMerge branch 'master' into f26 (diff)
parentAdded arm rust build fix (diff)
downloadlibrewolf-fedora-ff-54cf05b5a37859e8565a679783d65651265ad7a0.tar.gz
librewolf-fedora-ff-54cf05b5a37859e8565a679783d65651265ad7a0.tar.bz2
librewolf-fedora-ff-54cf05b5a37859e8565a679783d65651265ad7a0.zip
Merge branch 'master' into f26
-rw-r--r--build-cubeb-pulse-arm.patch4946
-rw-r--r--firefox.spec6
2 files changed, 4950 insertions, 2 deletions
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 <dglastonbury@mozilla.com>"]
++
++[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<T>(...)` 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<CB>(&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::<F>() == 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::<F>()(&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<Self>
++ where OPT: Into<Option<&'a CStr>>
++ {
++ 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<CB>(&self, _: CB, userdata: *mut c_void)
++ where CB: Fn(&Context, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ unsafe {
++ ffi::pa_context_set_state_callback(self.raw_mut(), Some(wrapped::<CB>), 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<Option<&'a CStr>>
++ {
++ 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<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Context, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ op_or_err!(self,
++ ffi::pa_context_drain(self.raw_mut(), Some(wrapped::<CB>), userdata))
++ }
++
++ pub fn rttime_new<CB>(&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::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&api, e, timeval, userdata);
++ forget(api);
++
++ result
++ }
++
++ unsafe { ffi::pa_context_rttime_new(self.raw_mut(), usec, Some(wrapped::<CB>), userdata) }
++ }
++
++ pub fn get_server_info<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Context, &ServerInfo, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, info, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ op_or_err!(self,
++ ffi::pa_context_get_server_info(self.raw_mut(), Some(wrapped::<CB>), userdata))
++ }
++
++ pub fn get_sink_info_by_name<CB>(&self, name: &CStr, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Context, *const SinkInfo, i32, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&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::<CB>), userdata))
++ }
++
++ pub fn get_sink_info_list<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Context, *const SinkInfo, i32, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, info, eol, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ op_or_err!(self,
++ ffi::pa_context_get_sink_info_list(self.raw_mut(), Some(wrapped::<CB>), userdata))
++ }
++
++ pub fn get_sink_input_info<CB>(&self, idx: u32, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Context, *const SinkInputInfo, i32, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, info, eol, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ op_or_err!(self,
++ ffi::pa_context_get_sink_input_info(self.raw_mut(), idx, Some(wrapped::<CB>), userdata))
++ }
++
++ pub fn get_source_info_list<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Context, *const SourceInfo, i32, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, info, eol, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ op_or_err!(self,
++ ffi::pa_context_get_source_info_list(self.raw_mut(), Some(wrapped::<CB>), userdata))
++ }
++
++ pub fn set_sink_input_volume<CB>(&self,
++ idx: u32,
++ volume: &CVolume,
++ _: CB,
++ userdata: *mut c_void)
++ -> Result<Operation>
++ where CB: Fn(&Context, i32, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, success, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ op_or_err!(self,
++ ffi::pa_context_set_sink_input_volume(self.raw_mut(), idx, volume, Some(wrapped::<CB>), userdata))
++ }
++
++ pub fn subscribe<CB>(&self, m: SubscriptionMask, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Context, i32, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, success, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ op_or_err!(self,
++ ffi::pa_context_subscribe(self.raw_mut(), m.into(), Some(wrapped::<CB>), 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<CB>(&self, _: CB, userdata: *mut c_void)
++ where CB: Fn(&Context, SubscriptionEvent, u32, *mut c_void)
++ {
++ debug_assert_eq!(::std::mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&ctx, event, idx, userdata);
++ forget(ctx);
++
++ result
++ }
++
++ unsafe {
++ ffi::pa_context_set_subscribe_callback(self.raw_mut(), Some(wrapped::<CB>), 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<ffi::pa_sample_format_t> 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<Self> {
++ 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<ffi::pa_context_state_t> 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<Self> {
++ 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<ffi::pa_stream_state_t> 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<Self> {
++ if x >= ffi::PA_OPERATION_RUNNING && x <= ffi::PA_OPERATION_CANCELLED {
++ Some(unsafe { ::std::mem::transmute(x) })
++ } else {
++ None
++ }
++ }
++}
++
++impl Into<ffi::pa_operation_state_t> 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<ffi::pa_context_flags_t> 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<Self> {
++ if x >= ffi::PA_DEVICE_TYPE_SINK && x <= ffi::PA_DEVICE_TYPE_SOURCE {
++ Some(unsafe { ::std::mem::transmute(x) })
++ } else {
++ None
++ }
++ }
++}
++
++impl Into<ffi::pa_device_type_t> 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<Self> {
++ if x >= ffi::PA_STREAM_NODIRECTION && x <= ffi::PA_STREAM_UPLOAD {
++ Some(unsafe { ::std::mem::transmute(x) })
++ } else {
++ None
++ }
++ }
++}
++
++impl Into<ffi::pa_stream_direction_t> 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<Self> {
++ 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<ffi::pa_stream_flags_t> 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<Self> {
++ if (x & !ffi::PA_SUBSCRIPTION_MASK_ALL) == 0 {
++ Some(unsafe { ::std::mem::transmute(x) })
++ } else {
++ None
++ }
++ }
++}
++
++impl Into<ffi::pa_subscription_mask_t> 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<Self> {
++ 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<Self> {
++ if x >= ffi::PA_SEEK_RELATIVE && x <= ffi::PA_SEEK_RELATIVE_END {
++ Some(unsafe { ::std::mem::transmute(x) })
++ } else {
++ None
++ }
++ }
++}
++
++impl Into<ffi::pa_seek_mode_t> 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<SinkFlags> {
++ 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<ffi::pa_source_flags_t> 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<Self> {
++ if x >= ffi::PA_PORT_AVAILABLE_UNKNOWN && x <= ffi::PA_PORT_AVAILABLE_YES {
++ Some(unsafe { ::std::mem::transmute(x) })
++ } else {
++ None
++ }
++ }
++}
++
++impl Into<ffi::pa_port_available_t> 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<Self> {
++ 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<ffi::pa_channel_position_t> for ChannelPosition {
++ fn into(self) -> ffi::pa_channel_position_t {
++ self as ffi::pa_channel_position_t
++ }
++}
++pub type Result<T> = ::std::result::Result<T, error::ErrorCode>;
++
++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<unsafe extern "C" fn(m: *mut ffi::pa_mainloop_api,
++ userdata: *mut c_void)>;
++fn wrap_once_cb<F>(_: F) -> pa_once_cb_t
++ where F: Fn(&MainloopApi, *mut c_void)
++{
++ assert!(mem::size_of::<F>() == 0);
++
++ unsafe extern "C" fn wrapped<F>(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::<F>)
++}
++
++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<CB>(&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<T>(&self, key: T) -> Option<&CStr>
++ where T: Into<Vec<u8>>
++ {
++ 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<Self>
++ where CM: Into<Option<&'a ChannelMap>>
++ {
++ 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<Context> {
++ 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<bool> {
++ let r = unsafe { ffi::pa_stream_is_suspended(self.raw_mut()) };
++ error_result!(r != 0, r)
++ }
++
++ pub fn is_corked(&self) -> Result<bool> {
++ 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<Option<&'a CStr>>,
++ A: Into<Option<&'a BufferAttr>>,
++ V: Into<Option<&'a CVolume>>,
++ S: Into<Option<&'a mut Stream>>
++ {
++ 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<Option<&'a CStr>>,
++ A: Into<Option<&'a BufferAttr>>
++ {
++ 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<usize> {
++ 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<usize> {
++ 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<CB>(&self, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Stream, i32, *mut c_void)
++ {
++ debug_assert_eq!(mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&mut stm, success, userdata);
++ forget(stm);
++
++ result
++ }
++
++ let r = unsafe { ffi::pa_stream_update_timing_info(self.raw_mut(), Some(wrapped::<CB>), 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<CB>(&self, _: CB, userdata: *mut c_void)
++ where CB: Fn(&Stream, *mut c_void)
++ {
++ debug_assert_eq!(mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&mut stm, userdata);
++ forget(stm);
++
++ result
++ }
++
++ unsafe {
++ ffi::pa_stream_set_state_callback(self.raw_mut(), Some(wrapped::<CB>), 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<CB>(&self, _: CB, userdata: *mut c_void)
++ where CB: Fn(&Stream, usize, *mut c_void)
++ {
++ debug_assert_eq!(mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&mut stm, nbytes, userdata);
++ forget(stm);
++
++ result
++ }
++
++ unsafe {
++ ffi::pa_stream_set_write_callback(self.raw_mut(), Some(wrapped::<CB>), 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<CB>(&self, _: CB, userdata: *mut c_void)
++ where CB: Fn(&Stream, usize, *mut c_void)
++ {
++ debug_assert_eq!(mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&mut stm, nbytes, userdata);
++ forget(stm);
++
++ result
++ }
++
++ unsafe {
++ ffi::pa_stream_set_read_callback(self.raw_mut(), Some(wrapped::<CB>), userdata);
++ }
++ }
++
++ pub fn cork<CB>(&self, b: i32, _: CB, userdata: *mut c_void) -> Result<Operation>
++ where CB: Fn(&Stream, i32, *mut c_void)
++ {
++ debug_assert_eq!(mem::size_of::<CB>(), 0);
++
++ // See: A note about `wrapped` functions
++ unsafe extern "C" fn wrapped<F>(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::<F>()(&mut stm, success, userdata);
++ forget(stm);
++
++ result
++ }
++
++ let r = unsafe { ffi::pa_stream_cork(self.raw_mut(), b, Some(wrapped::<CB>), 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<Option<&'a CStr>>
++{
++ 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<T, U, F: FnOnce(&T) -> *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>(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<pulse::Context>,
+ pub default_sink_info: Option<DefaultInfo>,
+- pub context_name: *const c_char,
++ pub context_name: Option<CString>,
+ 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<Box<Self>> {
++ fn _new(name: Option<CString>) -> Result<Box<Self>> {
+ 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<Box<Self>> {
++ fn _new(name: Option<CString>) -> Result<Box<Self>> {
+ 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<Box<Self>> {
++ 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<cubeb::StreamParams>,
+ output_device: cubeb::DeviceId,
+@@ -178,7 +184,7 @@ impl Context {
+ state_callback: cubeb::StateCallback,
+ user_ptr: *mut c_void)
+ -> Result<Box<Stream>> {
+- 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<cubeb::DeviceCollection> {
+- 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<Option<&'a pulse::Stream>>
++ {
++ 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<cubeb::DeviceInfo>,
+- 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<T> = ::std::result::Result<T, i32>;
+
+ pub use self::context::Context;
+ pub use self::stream::Device;
+ pub use self::stream::Stream;
++
++// helper to convert *const c_char to Option<CStr>
++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<pulse::Stream>,
++ input_stream: Option<pulse::Stream>,
+ 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<cubeb::StreamParams>,
+ output_device: cubeb::DeviceId,
+@@ -99,83 +101,167 @@ impl<'ctx> Stream<'ctx> {
+ user_ptr: *mut c_void)
+ -> Result<Box<Stream<'ctx>>> {
+
++ 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<u64> {
+- 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<u32> {
+- 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<u32> {
++ 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<Box<cubeb::Device>> {
+ 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<pulse::Stream> {
+
+- 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<pa_channel_map> = 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<cubeb::StreamParams> {
++ 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
bgstack15