Bug 1360060 - P3: device_collection_destroy for cubeb-pulse-rs. r=kinetik
authorDan Glastonbury <dglastonbury@mozilla.com>
Wed, 24 May 2017 19:33:19 +1000
changeset 411000 1ba8f05e12f31500cee3795649b63bf7abfe3b37
parent 410999 372f1eece4d4b0c019ba70b720933a6c9a5c37ae
child 411001 496a533b6037dbd0a87ae6bf51fa1d919c0dd455
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskinetik
bugs1360060
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1360060 - P3: device_collection_destroy for cubeb-pulse-rs. r=kinetik MozReview-Commit-ID: Hvn12h4O4FE
media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs
media/libcubeb/cubeb-pulse-rs/src/backend/context.rs
media/libcubeb/cubeb-pulse-rs/src/backend/destroy.rs
media/libcubeb/cubeb-pulse-rs/src/backend/mod.rs
media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs
media/libcubeb/cubeb-pulse-rs/src/backend/var_array.rs
media/libcubeb/cubeb-pulse-rs/src/capi.rs
--- a/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs
+++ b/media/libcubeb/cubeb-pulse-rs/cubeb-ffi/src/ffi.rs
@@ -1,67 +1,60 @@
 // Copyright © 2017 Mozilla Foundation
 //
 // This program is made available under an ISC-style license.  See the
 // accompanying file LICENSE for details.
 
 use std::default::Default;
-use std::os::raw::{c_char, c_long, c_void};
+use std::os::raw::{c_char, c_int, c_long, c_uint, c_void};
 use std::ptr;
 
 pub enum Context {}
 pub enum Stream {}
 
-// TODO endian check
-#[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct SampleFormat(i32);
-
 // These need to match cubeb_sample_format
-pub const SAMPLE_S16LE: SampleFormat = SampleFormat(0);
-pub const SAMPLE_S16BE: SampleFormat = SampleFormat(1);
-pub const SAMPLE_FLOAT32LE: SampleFormat = SampleFormat(2);
-pub const SAMPLE_FLOAT32BE: SampleFormat = SampleFormat(3);
+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;
 
 #[cfg(target_endian = "little")]
-pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16LE;
+pub const SAMPLE_S16NE: c_int = SAMPLE_S16LE;
 #[cfg(target_endian = "little")]
-pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32LE;
+pub const SAMPLE_FLOAT32NE: c_int = SAMPLE_FLOAT32LE;
 #[cfg(target_endian = "big")]
-pub const SAMPLE_S16NE: SampleFormat = SAMPLE_S16BE;
+pub const SAMPLE_S16NE: c_int = SAMPLE_S16BE;
 #[cfg(target_endian = "big")]
-pub const SAMPLE_FLOAT32NE: SampleFormat = SAMPLE_FLOAT32BE;
+pub const SAMPLE_FLOAT32NE: c_int = SAMPLE_FLOAT32BE;
 
 pub type DeviceId = *const c_void;
 
-#[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct ChannelLayout(i32);
-
 // These need to match cubeb_channel_layout
-pub const LAYOUT_UNDEFINED: ChannelLayout = ChannelLayout(0);
-pub const LAYOUT_DUAL_MONO: ChannelLayout = ChannelLayout(1);
-pub const LAYOUT_DUAL_MONO_LFE: ChannelLayout = ChannelLayout(2);
-pub const LAYOUT_MONO: ChannelLayout = ChannelLayout(3);
-pub const LAYOUT_MONO_LFE: ChannelLayout = ChannelLayout(4);
-pub const LAYOUT_STEREO: ChannelLayout = ChannelLayout(5);
-pub const LAYOUT_STEREO_LFE: ChannelLayout = ChannelLayout(6);
-pub const LAYOUT_3F: ChannelLayout = ChannelLayout(7);
-pub const LAYOUT_3F_LFE: ChannelLayout = ChannelLayout(8);
-pub const LAYOUT_2F1: ChannelLayout = ChannelLayout(9);
-pub const LAYOUT_2F1_LFE: ChannelLayout = ChannelLayout(10);
-pub const LAYOUT_3F1: ChannelLayout = ChannelLayout(11);
-pub const LAYOUT_3F1_LFE: ChannelLayout = ChannelLayout(12);
-pub const LAYOUT_2F2: ChannelLayout = ChannelLayout(13);
-pub const LAYOUT_2F2_LFE: ChannelLayout = ChannelLayout(14);
-pub const LAYOUT_3F2: ChannelLayout = ChannelLayout(15);
-pub const LAYOUT_3F2_LFE: ChannelLayout = ChannelLayout(16);
-pub const LAYOUT_3F3R_LFE: ChannelLayout = ChannelLayout(17);
-pub const LAYOUT_3F4_LFE: ChannelLayout = ChannelLayout(18);
-pub const LAYOUT_MAX: ChannelLayout = ChannelLayout(19);
+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;
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug)]
 pub struct StreamParams {
     pub format: SampleFormat,
     pub rate: u32,
     pub channels: u32,
     pub layout: ChannelLayout,
@@ -78,25 +71,22 @@ impl Default for Device {
     fn default() -> Self {
         Device {
             output_name: ptr::null_mut(),
             input_name: ptr::null_mut(),
         }
     }
 }
 
-#[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct State(i32);
-
 // These need to match cubeb_state
-pub const STATE_STARTED: State = State(0);
-pub const STATE_STOPPED: State = State(1);
-pub const STATE_DRAINED: State = State(2);
-pub const STATE_ERROR: State = State(3);
+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 OK: i32 = 0;
 pub const ERROR: i32 = -1;
 pub const ERROR_INVALID_FORMAT: i32 = -2;
 pub const ERROR_INVALID_PARAMETER: i32 = -3;
 pub const ERROR_NOT_SUPPORTED: i32 = -4;
 pub const ERROR_DEVICE_UNAVAILABLE: i32 = -5;
 
@@ -174,20 +164,20 @@ pub struct DeviceInfo {
     pub min_rate: u32,
     pub latency_lo: u32,
     pub latency_hi: u32,
 }
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug)]
 pub struct DeviceCollection {
+    /// Array of device info.
+    pub device: *const DeviceInfo,
     /// Device count in collection.
-    pub count: u32,
-    /// Array of pointers to device info.
-    pub device: [*const DeviceInfo; 0],
+    pub count: usize,
 }
 
 pub type DataCallback = Option<unsafe extern "C" fn(stream: *mut Stream,
                                                     user_ptr: *mut c_void,
                                                     input_buffer: *const c_void,
                                                     output_buffer: *mut c_void,
                                                     nframes: c_long)
                                                     -> c_long>;
@@ -223,18 +213,20 @@ pub struct Ops {
                                                      params: StreamParams,
                                                      latency_ms: *mut u32)
                                                      -> i32>,
     pub get_preferred_sample_rate: Option<unsafe extern "C" fn(context: *mut Context, rate: *mut u32) -> i32>,
     pub get_preferred_channel_layout:
         Option<unsafe extern "C" fn(context: *mut Context, layout: *mut ChannelLayout) -> i32>,
     pub enumerate_devices: Option<unsafe extern "C" fn(context: *mut Context,
                                                        devtype: DeviceType,
-                                                       collection: *mut *mut DeviceCollection)
+                                                       collection: *mut DeviceCollection)
                                                        -> i32>,
+    pub device_collection_destroy:
+        Option<unsafe extern "C" fn(context: *mut Context, collection: *mut DeviceCollection) -> i32>,
     pub destroy: Option<unsafe extern "C" fn(context: *mut Context)>,
     pub stream_init: StreamInitFn,
     pub stream_destroy: Option<unsafe extern "C" fn(stream: *mut Stream)>,
     pub stream_start: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
     pub stream_stop: Option<unsafe extern "C" fn(stream: *mut Stream) -> i32>,
     pub stream_get_position: Option<unsafe extern "C" fn(stream: *mut Stream, position: *mut u64) -> i32>,
     pub stream_get_latency: Option<unsafe extern "C" fn(stream: *mut Stream, latency: *mut u32) -> i32>,
     pub stream_set_volume: Option<unsafe extern "C" fn(stream: *mut Stream, volumes: f32) -> i32>,
@@ -252,59 +244,42 @@ pub struct Ops {
 #[derive(Clone, Copy, Debug)]
 pub struct LayoutMap {
     pub name: *const c_char,
     pub channels: u32,
     pub layout: ChannelLayout,
 }
 
 // cubeb_mixer.h
-#[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct Channel(i32);
-impl Into<i32> for Channel {
-    fn into(self) -> i32 {
-        self.0
-    }
-}
 
 // These need to match cubeb_channel
-pub const CHANNEL_INVALID: Channel = Channel(-1);
-pub const CHANNEL_MONO: Channel = Channel(0);
-pub const CHANNEL_LEFT: Channel = Channel(1);
-pub const CHANNEL_RIGHT: Channel = Channel(2);
-pub const CHANNEL_CENTER: Channel = Channel(3);
-pub const CHANNEL_LS: Channel = Channel(4);
-pub const CHANNEL_RS: Channel = Channel(5);
-pub const CHANNEL_RLS: Channel = Channel(6);
-pub const CHANNEL_RCENTER: Channel = Channel(7);
-pub const CHANNEL_RRS: Channel = Channel(8);
-pub const CHANNEL_LFE: Channel = Channel(9);
-pub const CHANNEL_MAX: Channel = Channel(10);
+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;
 
 #[repr(C)]
-#[derive(Clone, Copy, Debug)]
 pub struct ChannelMap {
-    pub channels: u32,
-    pub map: [Channel; 10],
+    pub channels: c_uint,
+    pub map: [Channel; 256],
 }
 impl ::std::default::Default for ChannelMap {
     fn default() -> Self {
         ChannelMap {
             channels: 0,
-            map: [CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID,
-                  CHANNEL_INVALID],
+            map: unsafe { ::std::mem::zeroed() },
         }
     }
 }
 
 extern "C" {
     pub fn cubeb_channel_map_to_layout(channel_map: *const ChannelMap) -> ChannelLayout;
     pub fn cubeb_should_upmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
     pub fn cubeb_should_downmix(stream: *const StreamParams, mixer: *const StreamParams) -> bool;
--- a/media/libcubeb/cubeb-pulse-rs/src/backend/context.rs
+++ b/media/libcubeb/cubeb-pulse-rs/src/backend/context.rs
@@ -1,22 +1,22 @@
 // Copyright © 2017 Mozilla Foundation
 //
 // This program is made available under an ISC-style license.  See the
 // accompanying file LICENSE for details.
 
 use backend::*;
 use backend::cork_state::CorkState;
-use backend::var_array::VarArray;
 use capi::PULSE_OPS;
 use cubeb;
 use pulse_ffi::*;
 use semver;
 use std::default::Default;
 use std::ffi::CStr;
+use std::mem;
 use std::os::raw::{c_char, c_int, c_void};
 use std::ptr;
 
 macro_rules! dup_str {
     ($Dst: expr, $Src: expr) => {
         if !$Dst.is_null() {
             pa_xfree($Dst as *mut _);
         }
@@ -47,45 +47,58 @@ fn channel_map_to_layout(cm: &pa_channel
     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]);
     }
     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,
+}
+
+#[derive(Debug)]
 pub struct Context {
     pub ops: *const cubeb::Ops,
     pub mainloop: *mut pa_threaded_mainloop,
     pub context: *mut pa_context,
-    pub default_sink_info: *mut pa_sink_info,
+    pub default_sink_info: Option<DefaultInfo>,
     pub context_name: *const c_char,
     pub collection_changed_callback: cubeb::DeviceCollectionChangedCallback,
     pub collection_changed_user_ptr: *mut c_void,
     pub error: bool,
     pub version_2_0_0: bool,
     pub version_0_9_8: bool,
     #[cfg(feature = "pulse-dlopen")]
     pub libpulse: LibLoader,
 }
 
+impl Drop for Context {
+    fn drop(&mut self) {
+        self.destroy();
+    }
+}
+
 impl Context {
     #[cfg(feature = "pulse-dlopen")]
     fn _new(name: *const i8) -> Result<Box<Self>> {
         let libpulse = unsafe { open() };
         if libpulse.is_none() {
             return Err(cubeb::ERROR);
         }
 
         let ctx = Box::new(Context {
                                ops: &PULSE_OPS,
                                libpulse: libpulse.unwrap(),
                                mainloop: unsafe { pa_threaded_mainloop_new() },
                                context: 0 as *mut _,
-                               default_sink_info: 0 as *mut _,
+                               default_sink_info: None,
                                context_name: name,
                                collection_changed_callback: None,
                                collection_changed_user_ptr: 0 as *mut _,
                                error: true,
                                version_0_9_8: false,
                                version_2_0_0: false,
                            });
 
@@ -93,17 +106,17 @@ impl Context {
     }
 
     #[cfg(not(feature = "pulse-dlopen"))]
     fn _new(name: *const i8) -> Result<Box<Self>> {
         Ok(Box::new(Context {
                         ops: &PULSE_OPS,
                         mainloop: unsafe { pa_threaded_mainloop_new() },
                         context: 0 as *mut _,
-                        default_sink_info: 0 as *mut _,
+                        default_sink_info: None,
                         context_name: name,
                         collection_changed_callback: None,
                         collection_changed_user_ptr: 0 as *mut _,
                         error: true,
                         version_0_9_8: false,
                         version_2_0_0: false,
                     }))
     }
@@ -114,32 +127,36 @@ impl Context {
         unsafe { pa_threaded_mainloop_start(ctx.mainloop) };
 
         if ctx.pulse_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);
-            pa_context_get_server_info(ctx.context,
-                                       Some(server_info_callback),
-                                       ctx.as_mut() as *mut Context as *mut _);
+            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);
+            }
             pa_threaded_mainloop_unlock(ctx.mainloop);
+            assert!(ctx.default_sink_info.is_some());
         }
 
         // Return the result.
         Ok(ctx)
     }
 
     pub fn destroy(&mut self) {
-        if !self.default_sink_info.is_null() {
-            let _ = unsafe { Box::from_raw(self.default_sink_info) };
-        }
-
         if !self.context.is_null() {
             unsafe { self.pulse_context_destroy() };
         }
 
         if !self.mainloop.is_null() {
             unsafe {
                 pa_threaded_mainloop_stop(self.mainloop);
                 pa_threaded_mainloop_free(self.mainloop);
@@ -170,57 +187,42 @@ impl Context {
                     output_stream_params,
                     latency_frames,
                     data_callback,
                     state_callback,
                     user_ptr)
     }
 
     pub fn max_channel_count(&self) -> Result<u32> {
-        unsafe {
-            pa_threaded_mainloop_lock(self.mainloop);
-            while self.default_sink_info.is_null() {
-                pa_threaded_mainloop_wait(self.mainloop);
-            }
-            pa_threaded_mainloop_unlock(self.mainloop);
-
-            Ok((*self.default_sink_info).channel_map.channels as u32)
+        match self.default_sink_info {
+            Some(ref info) => Ok(info.channel_map.channels as u32),
+            None => Err(cubeb::ERROR),
         }
     }
 
     pub fn preferred_sample_rate(&self) -> Result<u32> {
-        unsafe {
-            pa_threaded_mainloop_lock(self.mainloop);
-            while self.default_sink_info.is_null() {
-                pa_threaded_mainloop_wait(self.mainloop);
-            }
-            pa_threaded_mainloop_unlock(self.mainloop);
-
-            Ok((*self.default_sink_info).sample_spec.rate)
+        match self.default_sink_info {
+            Some(ref info) => Ok(info.sample_spec.rate),
+            None => Err(cubeb::ERROR),
         }
     }
 
     pub fn min_latency(&self, params: &cubeb::StreamParams) -> Result<u32> {
         // According to PulseAudio developers, this is a safe minimum.
         Ok(25 * params.rate / 1000)
     }
 
     pub fn preferred_channel_layout(&self) -> Result<cubeb::ChannelLayout> {
-        unsafe {
-            pa_threaded_mainloop_lock(self.mainloop);
-            while self.default_sink_info.is_null() {
-                pa_threaded_mainloop_wait(self.mainloop);
-            }
-            pa_threaded_mainloop_unlock(self.mainloop);
-
-            Ok(channel_map_to_layout(&(*self.default_sink_info).channel_map))
+        match self.default_sink_info {
+            Some(ref info) => Ok(channel_map_to_layout(&info.channel_map)),
+            None => Err(cubeb::ERROR),
         }
     }
 
-    pub fn enumerate_devices(&self, devtype: cubeb::DeviceType) -> Result<*mut cubeb::DeviceCollection> {
+    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 _;
 
         unsafe {
             pa_threaded_mainloop_lock(self.mainloop);
 
             let o = pa_context_get_server_info(self.context,
                                                Some(pulse_server_info_cb),
@@ -248,28 +250,53 @@ impl Context {
                     self.operation_wait(ptr::null_mut(), o);
                     pa_operation_unref(o);
                 }
             }
 
             pa_threaded_mainloop_unlock(self.mainloop);
         }
 
-        // TODO: This is dodgy - Need to account for padding between count
-        // and device array in C code on 64-bit platforms. Using an extra
-        // pointer instead of the header size to achieve this.
-        let mut coll: Box<VarArray<*const cubeb::DeviceInfo>> = VarArray::with_length(user_data.devinfo.len());
-        for (e1, e2) in user_data
-                .devinfo
-                .drain(..)
-                .zip(coll.as_mut_slice().iter_mut()) {
-            *e2 = e1;
+        // Extract the array of cubeb_device_info from
+        // PulseDevListData and convert it into C representation.
+        let mut tmp = Vec::new();
+        mem::swap(&mut user_data.devinfo, &mut tmp);
+        let devices = tmp.into_boxed_slice();
+        let coll = cubeb::DeviceCollection {
+            device: devices.as_ptr(),
+            count: devices.len(),
+        };
+
+        // Giving away the memory owned by devices.  Don't free it!
+        mem::forget(devices);
+        Ok(coll)
+    }
+
+    pub fn device_collection_destroy(&self, collection: *mut cubeb::DeviceCollection) {
+        debug_assert!(!collection.is_null());
+        unsafe {
+            let coll = *collection;
+            let mut devices = Vec::from_raw_parts(coll.device as *mut cubeb::DeviceInfo,
+                                                  coll.count,
+                                                  coll.count);
+            for dev in devices.iter_mut() {
+                if !dev.device_id.is_null() {
+                    pa_xfree(dev.device_id as *mut _);
+                }
+                if !dev.group_id.is_null() {
+                    pa_xfree(dev.group_id as *mut _);
+                }
+                if !dev.vendor_name.is_null() {
+                    pa_xfree(dev.vendor_name as *mut _);
+                }
+                if !dev.friendly_name.is_null() {
+                    pa_xfree(dev.friendly_name as *mut _);
+                }
+            }
         }
-
-        Ok(Box::into_raw(coll) as *mut cubeb::DeviceCollection)
     }
 
     pub fn register_device_collection_changed(&mut self,
                                               devtype: cubeb::DeviceType,
                                               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) {
@@ -464,51 +491,52 @@ impl Context {
                 cubeb::DeviceState::Enabled
             }
         } else {
             cubeb::DeviceState::Enabled
         }
     }
 }
 
-
 // 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 {
-            if !ctx.default_sink_info.is_null() {
-                let _ = Box::from_raw(ctx.default_sink_info);
-            }
-            ctx.default_sink_info = Box::into_raw(Box::new(*info));
+            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);
     }
 
-    pa_context_get_sink_info_by_name(context,
-                                     (*info).default_sink_name,
-                                     Some(sink_info_callback),
-                                     u);
+    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,
-    devinfo: Vec<*const cubeb::DeviceInfo>,
+    devinfo: Vec<cubeb::DeviceInfo>,
     context: *mut Context,
 }
 
 impl Drop for PulseDevListData {
     fn drop(&mut self) {
-        for elem in &mut self.devinfo {
-            let _ = unsafe { Box::from_raw(elem) };
-        }
         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 _);
@@ -595,17 +623,17 @@ unsafe extern "C" fn pulse_sink_info_cb(
         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(Box::into_raw(Box::new(devinfo)));
+    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) {
@@ -661,17 +689,17 @@ unsafe extern "C" fn pulse_source_info_c
         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(Box::into_raw(Box::new(devinfo)));
+    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());
deleted file mode 100644
--- a/media/libcubeb/cubeb-pulse-rs/src/backend/mod.rs
+++ b/media/libcubeb/cubeb-pulse-rs/src/backend/mod.rs
@@ -1,15 +1,14 @@
 // Copyright © 2017 Mozilla Foundation
 //
 // This program is made available under an ISC-style license.  See the
 // accompanying file LICENSE for details.
 
 mod context;
 mod cork_state;
 mod stream;
-mod var_array;
 
 pub type Result<T> = ::std::result::Result<T, i32>;
 
 pub use self::context::Context;
 pub use self::stream::Device;
 pub use self::stream::Stream;
--- a/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs
+++ b/media/libcubeb/cubeb-pulse-rs/src/backend/stream.rs
@@ -333,25 +333,32 @@ impl<'ctx> Stream<'ctx> {
     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_null() {
+            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. */
-            if ((*self.context.default_sink_info).flags & PA_SINK_FLAT_VOLUME) != 0 {
+            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);
 
@@ -459,19 +466,22 @@ impl<'ctx> Stream<'ctx> {
         }
 
         let ss = pa_sample_spec {
             channels: stream_params.channels as u8,
             format: fmt,
             rate: stream_params.rate,
         };
 
-        let cm = layout_to_channel_map(stream_params.layout);
-
-        let stream = unsafe { pa_stream_new(self.context.context, stream_name, &ss, &cm) };
+        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) }
+        };
 
         if !stream.is_null() {
             Ok(stream)
         } else {
             Err(cubeb::ERROR)
         }
     }
 
deleted file mode 100644
--- a/media/libcubeb/cubeb-pulse-rs/src/backend/var_array.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-/// A C-style variable length array implemented as one allocation with
-/// a prepended header.
-
-// Copyright © 2017 Mozilla Foundation
-//
-// This program is made available under an ISC-style license.  See the
-// accompanying file LICENSE for details.
-
-use pulse_ffi::pa_xrealloc;
-use std::ptr;
-
-#[repr(C)]
-#[derive(Debug)]
-pub struct VarArray<T> {
-    len: u32,
-    data: [T; 0],
-}
-
-impl<T> VarArray<T> {
-    pub fn len(&self) -> usize {
-        self.len as usize
-    }
-
-    unsafe fn _realloc(ptr: Option<Box<Self>>, count: usize) -> Box<Self> {
-        use std::mem::{size_of, transmute};
-
-        let size = size_of::<Self>() + count * size_of::<T>();
-        let raw_ptr = match ptr {
-            Some(box_ptr) => Box::into_raw(box_ptr) as *mut u8,
-            None => ptr::null_mut(),
-        };
-        let mem = pa_xrealloc(raw_ptr as *mut _, size);
-        let mut result: Box<Self> = transmute(mem);
-        result.len = count as u32;
-        result
-    }
-
-    pub fn with_length(len: usize) -> Box<VarArray<T>> {
-        unsafe { Self::_realloc(None, len) }
-    }
-
-    pub fn as_mut_slice(&mut self) -> &mut [T] {
-        use std::slice::from_raw_parts_mut;
-
-        unsafe { from_raw_parts_mut(&self.data as *const _ as *mut _, self.len()) }
-    }
-}
-
-impl<T> Drop for VarArray<T> {
-    fn drop(&mut self) {
-        let ptr = self as *mut Self;
-        unsafe {
-            Self::_realloc(Some(Box::from_raw(ptr)), 0);
-        }
-    }
-}
--- a/media/libcubeb/cubeb-pulse-rs/src/capi.rs
+++ b/media/libcubeb/cubeb-pulse-rs/src/capi.rs
@@ -71,29 +71,38 @@ unsafe extern "C" fn capi_get_preferred_
             cubeb::OK
         },
         Err(e) => e,
     }
 }
 
 unsafe extern "C" fn capi_enumerate_devices(c: *mut cubeb::Context,
                                             devtype: cubeb::DeviceType,
-                                            collection: *mut *mut cubeb::DeviceCollection)
+                                            collection: *mut cubeb::DeviceCollection)
                                             -> i32 {
     let ctx = &*(c as *mut backend::Context);
 
     match ctx.enumerate_devices(devtype) {
         Ok(dc) => {
             *collection = dc;
             cubeb::OK
         },
         Err(e) => e,
     }
 }
 
+unsafe extern "C" fn capi_device_collection_destroy(c: *mut cubeb::Context,
+                                                    collection: *mut cubeb::DeviceCollection)
+                                                    -> i32 {
+    let ctx = &*(c as *mut backend::Context);
+
+    ctx.device_collection_destroy(collection);
+    cubeb::OK
+}
+
 unsafe extern "C" fn capi_destroy(c: *mut cubeb::Context) {
     let _: Box<backend::Context> = Box::from_raw(c as *mut _);
 }
 
 unsafe extern "C" fn capi_stream_init(c: *mut cubeb::Context,
                                       s: *mut *mut cubeb::Stream,
                                       stream_name: *const c_char,
                                       input_device: cubeb::DeviceId,
@@ -214,16 +223,17 @@ unsafe extern "C" fn capi_register_devic
 pub const PULSE_OPS: cubeb::Ops = cubeb::Ops {
     init: Some(capi_init),
     get_backend_id: Some(capi_get_backend_id),
     get_max_channel_count: Some(capi_get_max_channel_count),
     get_min_latency: Some(capi_get_min_latency),
     get_preferred_sample_rate: Some(capi_get_preferred_sample_rate),
     get_preferred_channel_layout: Some(capi_get_preferred_channel_layout),
     enumerate_devices: Some(capi_enumerate_devices),
+    device_collection_destroy: Some(capi_device_collection_destroy),
     destroy: Some(capi_destroy),
     stream_init: Some(capi_stream_init),
     stream_destroy: Some(capi_stream_destroy),
     stream_start: Some(capi_stream_start),
     stream_stop: Some(capi_stream_stop),
     stream_get_position: Some(capi_stream_get_position),
     stream_get_latency: Some(capi_stream_get_latency),
     stream_set_volume: Some(capi_stream_set_volume),