Bug 1360060 - P3: device_collection_destroy for cubeb-pulse-rs. r=kinetik

MozReview-Commit-ID: Hvn12h4O4FE

--HG--
extra : rebase_source : 9a689bcc516c1a3d363c2996bce67a083970aad9
This commit is contained in:
Dan Glastonbury 2017-05-24 19:33:19 +10:00
Родитель d8f7d93bff
Коммит 839fad3c98
7 изменённых файлов: 174 добавлений и 208 удалений

Просмотреть файл

@ -4,59 +4,52 @@
// 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)]
@ -83,15 +76,12 @@ impl Default for Device {
}
}
#[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;
@ -179,10 +169,10 @@ pub struct DeviceInfo {
#[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,
@ -228,8 +218,10 @@ pub struct Ops {
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)>,
@ -257,49 +249,32 @@ pub struct LayoutMap {
}
// 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() },
}
}
}

Просмотреть файл

@ -5,13 +5,13 @@
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;
@ -51,12 +51,19 @@ fn channel_map_to_layout(cm: &pa_channel_map) -> cubeb::ChannelLayout {
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,
@ -67,6 +74,12 @@ pub struct Context {
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>> {
@ -80,7 +93,7 @@ impl Context {
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 _,
@ -98,7 +111,7 @@ impl 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 _,
@ -119,11 +132,19 @@ impl Context {
}
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.
@ -131,10 +152,6 @@ impl Context {
}
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() };
}
@ -175,26 +192,16 @@ impl Context {
}
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),
}
}
@ -204,18 +211,13 @@ impl Context {
}
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 _;
@ -253,18 +255,43 @@ impl Context {
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(),
};
Ok(Box::into_raw(coll) as *mut cubeb::DeviceCollection)
// 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 _);
}
}
}
}
pub fn register_device_collection_changed(&mut self,
@ -469,7 +496,6 @@ 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,
@ -478,32 +504,34 @@ unsafe extern "C" fn server_info_callback(context: *mut pa_context, info: *const
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 _);
@ -600,7 +628,7 @@ unsafe extern "C" fn pulse_sink_info_cb(_context: *mut pa_context,
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);
}
@ -666,7 +694,7 @@ unsafe extern "C" fn pulse_source_info_cb(_context: *mut pa_context,
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);
}

Просмотреть файл

Просмотреть файл

@ -6,7 +6,6 @@
mod context;
mod cork_state;
mod stream;
mod var_array;
pub type Result<T> = ::std::result::Result<T, i32>;

Просмотреть файл

@ -338,7 +338,7 @@ impl<'ctx> Stream<'ctx> {
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);
}
@ -346,7 +346,14 @@ impl<'ctx> Stream<'ctx> {
/* 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);
@ -464,9 +471,12 @@ impl<'ctx> Stream<'ctx> {
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)

Просмотреть файл

@ -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);
}
}
}

Просмотреть файл

@ -76,7 +76,7 @@ unsafe extern "C" fn capi_get_preferred_channel_layout(c: *mut cubeb::Context,
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);
@ -89,6 +89,15 @@ unsafe extern "C" fn capi_enumerate_devices(c: *mut cubeb::Context,
}
}
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 _);
}
@ -219,6 +228,7 @@ pub const PULSE_OPS: cubeb::Ops = cubeb::Ops {
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),