use std::os::raw::{c_char, c_void};
use std::ffi::{CStr, CString};
use std::ptr::{null, null_mut};
use std::borrow::Cow;
use num_derive::{FromPrimitive, ToPrimitive};
use crate::{sample, channelmap};
use crate::error::PAErr;
use crate::proplist::{Proplist, ProplistInternal};
pub use capi::pa_prop_type_t as PropType;
#[repr(C)]
#[non_exhaustive]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[derive(FromPrimitive, ToPrimitive)]
#[allow(non_camel_case_types)]
pub enum Encoding {
Any,
PCM,
AC3_IEC61937,
EAC3_IEC61937,
MPEG_IEC61937,
DTS_IEC61937,
MPEG2_AAC_IEC61937,
#[cfg(any(doc, feature = "pa_v13"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
TRUEHD_IEC61937,
#[cfg(any(doc, feature = "pa_v13"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
DTSHD_IEC61937,
Invalid = -1,
}
#[test]
fn enc_compare_capi() {
assert_eq!(std::mem::size_of::<Encoding>(), std::mem::size_of::<capi::pa_encoding_t>());
assert_eq!(std::mem::align_of::<Encoding>(), std::mem::align_of::<capi::pa_encoding_t>());
assert_eq!(Encoding::Any, Encoding::from(capi::pa_encoding_t::Any));
assert_eq!(Encoding::PCM, Encoding::from(capi::pa_encoding_t::PCM));
assert_eq!(Encoding::AC3_IEC61937, Encoding::from(capi::pa_encoding_t::AC3_IEC61937));
assert_eq!(Encoding::EAC3_IEC61937, Encoding::from(capi::pa_encoding_t::EAC3_IEC61937));
assert_eq!(Encoding::MPEG_IEC61937, Encoding::from(capi::pa_encoding_t::MPEG_IEC61937));
assert_eq!(Encoding::DTS_IEC61937, Encoding::from(capi::pa_encoding_t::DTS_IEC61937));
assert_eq!(Encoding::MPEG2_AAC_IEC61937, Encoding::from(capi::pa_encoding_t::MPEG2_AAC_IEC61937));
#[cfg(any(doc, feature = "pa_v13"))]
assert_eq!(Encoding::TRUEHD_IEC61937, Encoding::from(capi::pa_encoding_t::TRUEHD_IEC61937));
#[cfg(any(doc, feature = "pa_v13"))]
assert_eq!(Encoding::DTSHD_IEC61937, Encoding::from(capi::pa_encoding_t::DTSHD_IEC61937));
assert_eq!(Encoding::Invalid, Encoding::from(capi::pa_encoding_t::Invalid));
}
impl From<Encoding> for capi::pa_encoding_t {
#[inline]
fn from(e: Encoding) -> Self {
unsafe { std::mem::transmute(e) }
}
}
impl From<capi::pa_encoding_t> for Encoding {
#[inline]
fn from(e: capi::pa_encoding_t) -> Self {
unsafe { std::mem::transmute(e) }
}
}
impl Default for Encoding {
#[inline(always)]
fn default() -> Self {
Encoding::Invalid
}
}
pub struct Info {
pub(crate) ptr: *mut InfoInternal,
properties: Proplist,
weak: bool,
}
unsafe impl Send for Info {}
unsafe impl Sync for Info {}
#[repr(C)]
pub(crate) struct InfoInternal {
pub encoding: Encoding,
pub list: *mut ProplistInternal,
}
#[test]
fn info_compare_capi() {
assert_eq!(std::mem::size_of::<InfoInternal>(), std::mem::size_of::<capi::pa_format_info>());
assert_eq!(std::mem::align_of::<InfoInternal>(), std::mem::align_of::<capi::pa_format_info>());
}
impl std::fmt::Debug for Info {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Info {{ encoding: {:?}, properties: {:?} }}", self.get_encoding(),
*self.get_properties())
}
}
impl Encoding {
pub fn to_string(e: Self) -> Option<Cow<'static, str>> {
let ptr = unsafe { capi::pa_encoding_to_string(e.into()) };
match ptr.is_null() {
false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
true => None,
}
}
#[cfg(any(doc, feature = "pa_v12"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v12")))]
pub fn from_string(encoding: &str) -> Self {
let c_enc = CString::new(encoding.clone()).unwrap();
unsafe { capi::pa_encoding_from_string(c_enc.as_ptr()).into() }
}
}
impl Info {
pub fn new() -> Option<Self> {
let ptr = unsafe { capi::pa_format_info_new() };
match ptr.is_null() {
false => Some(Self::from_raw(ptr as *mut InfoInternal)),
true => None,
}
}
pub fn new_from_string(s: &str) -> Option<Self> {
let c_str = CString::new(s.clone()).unwrap();
let ptr = unsafe { capi::pa_format_info_from_string(c_str.as_ptr()) };
match ptr.is_null() {
false => Some(Self::from_raw(ptr as *mut InfoInternal)),
true => None,
}
}
pub fn new_from_sample_spec(ss: &sample::Spec, map: Option<&channelmap::Map>)
-> Option<Self>
{
let p_map = map.map_or(null::<capi::pa_channel_map>(), |m| m.as_ref());
let ptr = unsafe { capi::pa_format_info_from_sample_spec(ss.as_ref(), p_map) };
match ptr.is_null() {
false => Some(Self::from_raw(ptr as *mut InfoInternal)),
true => None,
}
}
pub(crate) fn from_raw(ptr: *mut InfoInternal) -> Self {
assert_eq!(false, ptr.is_null());
unsafe {
Self {
ptr: ptr,
properties: Proplist::from_raw_weak((*ptr).list),
weak: false,
}
}
}
pub(crate) fn from_raw_weak(ptr: *mut InfoInternal) -> Self {
assert_eq!(false, ptr.is_null());
unsafe {
Self {
ptr: ptr,
properties: Proplist::from_raw_weak((*ptr).list),
weak: true,
}
}
}
#[inline]
pub fn is_valid(&self) -> bool {
unsafe { capi::pa_format_info_valid(self.ptr as *const capi::pa_format_info) != 0 }
}
#[inline]
pub fn is_pcm(&self) -> bool {
unsafe { capi::pa_format_info_is_pcm(self.ptr as *const capi::pa_format_info) != 0 }
}
#[inline]
pub fn is_compatible_with(&self, with: &Self) -> bool {
unsafe { capi::pa_format_info_is_compatible(self.ptr as *const capi::pa_format_info,
with.ptr as *const capi::pa_format_info) != 0 }
}
pub fn print(&self) -> String {
const PRINT_MAX: usize = capi::PA_FORMAT_INFO_SNPRINT_MAX;
let mut tmp = Vec::with_capacity(PRINT_MAX);
unsafe {
capi::pa_format_info_snprint(tmp.as_mut_ptr(), PRINT_MAX,
self.ptr as *const capi::pa_format_info);
CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
}
}
pub fn to_sample_spec(&self, ss: &mut sample::Spec, map: &mut channelmap::Map)
-> Result<(), PAErr>
{
match unsafe { capi::pa_format_info_to_sample_spec(
self.ptr as *const capi::pa_format_info, ss.as_mut(), map.as_mut()) }
{
0 => Ok(()),
e => Err(PAErr(e)),
}
}
#[inline]
pub fn get_encoding(&self) -> Encoding {
unsafe { (*self.ptr).encoding }
}
#[inline]
pub fn set_encoding(&mut self, encoding: Encoding) {
unsafe { (*self.ptr).encoding = encoding };
}
#[inline]
pub fn get_properties(&self) -> &Proplist {
&self.properties
}
#[inline]
pub fn get_properties_mut(&mut self) -> &mut Proplist {
&mut self.properties
}
pub fn get_prop_type(&self, key: &str) -> PropType {
let c_key = CString::new(key.clone()).unwrap();
unsafe { capi::pa_format_info_get_prop_type(self.ptr as *const capi::pa_format_info,
c_key.as_ptr()) }
}
pub fn get_prop_int(&self, key: &str) -> Result<i32, PAErr> {
let mut i: i32 = 0;
let c_key = CString::new(key.clone()).unwrap();
match unsafe { capi::pa_format_info_get_prop_int(self.ptr as *const capi::pa_format_info,
c_key.as_ptr(), &mut i) }
{
0 => Ok(i),
e => Err(PAErr(e)),
}
}
pub fn get_prop_int_range(&self, key: &str) -> Result<(i32, i32), PAErr> {
let mut min: i32 = 0;
let mut max: i32 = 0;
let c_key = CString::new(key.clone()).unwrap();
match unsafe { capi::pa_format_info_get_prop_int_range(
self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut min, &mut max) }
{
0 => Ok((min, max)),
e => Err(PAErr(e)),
}
}
pub fn get_prop_int_array(&self, key: &str) -> Option<Vec<i32>> {
let c_key = CString::new(key.clone()).unwrap();
let mut count: i32 = 0;
let mut p_ints = null_mut::<i32>();
let result = unsafe { capi::pa_format_info_get_prop_int_array(
self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_ints, &mut count) };
if result != 0 {
return None;
}
let mut values: Vec<i32> = Vec::with_capacity(count as usize);
for i in 0..count {
values.push(unsafe { *p_ints.offset(i as isize) });
}
unsafe { capi::pa_xfree(p_ints as *mut c_void) };
Some(values)
}
pub fn get_prop_string(&self, key: &str) -> Option<String> {
let c_key = CString::new(key.clone()).unwrap();
let mut p_str = null_mut::<c_char>();
let result = unsafe { capi::pa_format_info_get_prop_string(
self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_str) };
if result != 0 || p_str.is_null() {
return None;
}
unsafe {
let ret = Some(CStr::from_ptr(p_str).to_string_lossy().into_owned());
capi::pa_xfree(p_str as *mut c_void);
ret
}
}
pub fn get_prop_string_array(&self, key: &str) -> Option<Vec<String>> {
let c_key = CString::new(key.clone()).unwrap();
let mut count: i32 = 0;
let mut pp_str = null_mut::<*mut c_char>();
let result = unsafe { capi::pa_format_info_get_prop_string_array(
self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut pp_str, &mut count) };
if result != 0 || pp_str.is_null() {
return None;
}
let mut values: Vec<String> = Vec::with_capacity(count as usize);
for i in 0..count {
let p_str = unsafe { *pp_str.offset(i as isize) };
if !p_str.is_null() {
values.push(unsafe { CStr::from_ptr(p_str).to_string_lossy().into_owned() });
}
}
unsafe { capi::pa_format_info_free_string_array(pp_str, count) };
Some(values)
}
#[cfg(any(doc, feature = "pa_v13"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
pub fn get_sample_format(&self) -> Result<crate::sample::Format, PAErr> {
let mut sf: capi::pa_sample_format_t = capi::PA_SAMPLE_INVALID;
match unsafe { capi::pa_format_info_get_sample_format(
self.ptr as *const capi::pa_format_info, &mut sf) }
{
0 => Ok(crate::sample::Format::from(sf)),
e => Err(PAErr(e)),
}
}
#[cfg(any(doc, feature = "pa_v13"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
pub fn get_rate(&self) -> Result<u32, PAErr> {
let mut rate: u32 = 0;
match unsafe { capi::pa_format_info_get_rate(self.ptr as *const capi::pa_format_info,
&mut rate) }
{
0 => Ok(rate),
e => Err(PAErr(e)),
}
}
#[cfg(any(doc, feature = "pa_v13"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
pub fn get_channel_count(&self) -> Result<u8, PAErr> {
let mut channels: u8 = 0;
match unsafe { capi::pa_format_info_get_channels(self.ptr as *const capi::pa_format_info,
&mut channels) }
{
0 => Ok(channels),
e => Err(PAErr(e)),
}
}
#[cfg(any(doc, feature = "pa_v13"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
pub fn get_channel_map(&self) -> Result<crate::channelmap::Map, PAErr> {
let mut map: capi::pa_channel_map = capi::pa_channel_map::default();
match unsafe { capi::pa_format_info_get_channel_map(
self.ptr as *const capi::pa_format_info, &mut map) }
{
0 => Ok(crate::channelmap::Map::from(map)),
e => Err(PAErr(e)),
}
}
pub fn set_prop_int(&mut self, key: &str, value: i32) {
let c_key = CString::new(key.clone()).unwrap();
unsafe { capi::pa_format_info_set_prop_int(self.ptr as *mut capi::pa_format_info,
c_key.as_ptr(), value); }
}
pub fn set_prop_int_array(&mut self, key: &str, values: &[i32]) {
let c_key = CString::new(key.clone()).unwrap();
unsafe { capi::pa_format_info_set_prop_int_array(self.ptr as *mut capi::pa_format_info,
c_key.as_ptr(), values.as_ptr(), values.len() as i32); }
}
pub fn set_prop_int_range(&mut self, key: &str, min: i32, max: i32) {
let c_key = CString::new(key.clone()).unwrap();
unsafe { capi::pa_format_info_set_prop_int_range(self.ptr as *mut capi::pa_format_info,
c_key.as_ptr(), min, max); }
}
pub fn set_prop_string(&mut self, key: &str, value: &str) {
let c_key = CString::new(key.clone()).unwrap();
let c_value = CString::new(value.clone()).unwrap();
unsafe { capi::pa_format_info_set_prop_string(self.ptr as *mut capi::pa_format_info,
c_key.as_ptr(), c_value.as_ptr()); }
}
pub fn set_prop_string_array(&mut self, key: &str, values: &[&str]) {
let c_key = CString::new(key.clone()).unwrap();
let mut c_values: Vec<CString> = Vec::with_capacity(values.len());
for v in values {
c_values.push(CString::new(v.clone()).unwrap());
}
let mut c_value_ptrs: Vec<*const c_char> = Vec::with_capacity(c_values.len());
for v in &c_values {
c_value_ptrs.push(v.as_ptr());
}
unsafe {
capi::pa_format_info_set_prop_string_array(self.ptr as *mut capi::pa_format_info,
c_key.as_ptr(), c_value_ptrs.as_ptr(), c_value_ptrs.len() as i32);
}
}
#[inline]
pub fn set_sample_format(&mut self, sf: sample::Format) {
unsafe { capi::pa_format_info_set_sample_format(self.ptr as *mut capi::pa_format_info,
sf.into()); }
}
#[inline]
pub fn set_rate(&mut self, rate: i32) {
unsafe { capi::pa_format_info_set_rate(self.ptr as *mut capi::pa_format_info, rate) }
}
#[inline]
pub fn set_channels(&mut self, channels: u32) {
debug_assert!(channels <= std::i32::MAX as u32);
unsafe { capi::pa_format_info_set_channels(self.ptr as *mut capi::pa_format_info,
channels as i32) }
}
#[inline]
pub fn set_channel_map(&mut self, map: &channelmap::Map) {
unsafe { capi::pa_format_info_set_channel_map(self.ptr as *mut capi::pa_format_info,
map.as_ref()) }
}
}
impl Drop for Info {
fn drop(&mut self) {
if !self.weak {
unsafe { capi::pa_format_info_free(self.ptr as *mut capi::pa_format_info) };
}
}
}
impl Clone for Info {
fn clone(&self) -> Self {
let ptr = unsafe { capi::pa_format_info_copy(self.ptr as *const capi::pa_format_info) };
assert_eq!(false, ptr.is_null());
Self::from_raw(ptr as *mut InfoInternal)
}
}