Bug 1360060 - P3: device_collection_destroy for cubeb-pulse-rs. r?kinetik draft
authorDan Glastonbury <dglastonbury@mozilla.com>
Wed, 24 May 2017 19:33:19 +1000
changeset 590146 8166bcf9ab704bccc329fc4dc22cfa762a51bb99
parent 590145 55ca24589b69240037f79fb5ba391cc98cb4393a
child 632099 463347273a2d6f072628713600d8db4a3f96287f
push id62606
push userbmo:dglastonbury@mozilla.com
push dateWed, 07 Jun 2017 07:13:29 +0000
reviewerskinetik
bugs1360060
milestone55.0a1
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),