libpulse_binding/
format.rs

1// Copyright 2017 Lyndon Brown
2//
3// This file is part of the PulseAudio Rust language binding.
4//
5// Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
6// copy, modify, or distribute this file except in compliance with said license. You can find copies
7// of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
8// <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
9// respectively.
10//
11// Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a
12// fair-use basis, as discussed in the overall project readme (available in the git repository).
13
14//! Utility functions for handling a stream or sink format.
15//!
16//! # Note
17//!
18//! Clients using an [`Info`] structure must remember to at least set the encoding attribute, which
19//! can be done through the [`Info::set_encoding()`] method.
20
21use std::os::raw::{c_char, c_void};
22use std::ffi::{CStr, CString};
23use std::ptr::{null, null_mut};
24use std::borrow::Cow;
25use num_derive::{FromPrimitive, ToPrimitive};
26use crate::{sample, channelmap};
27use crate::error::PAErr;
28use crate::proplist::{Proplist, ProplistInternal};
29
30pub use capi::pa_prop_type_t as PropType;
31
32/// Represents the type of encoding used in a stream or accepted by a sink.
33#[repr(C)]
34#[non_exhaustive]
35#[derive(Debug, Copy, Clone, PartialEq, Eq)]
36#[derive(FromPrimitive, ToPrimitive)]
37#[allow(non_camel_case_types)]
38pub enum Encoding {
39    /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate
40       (C API) equivalent */
41    /// Any encoding format, PCM or compressed.
42    Any,
43    /// Any PCM format.
44    PCM,
45    /// AC3 data encapsulated in IEC 61937 header/padding.
46    AC3_IEC61937,
47    /// EAC3 data encapsulated in IEC 61937 header/padding.
48    EAC3_IEC61937,
49    /// MPEG-1 or MPEG-2 (Part 3, not AAC) data encapsulated in IEC 61937 header/padding.
50    MPEG_IEC61937,
51    /// DTS data encapsulated in IEC 61937 header/padding.
52    DTS_IEC61937,
53    /// MPEG-2 AAC data encapsulated in IEC 61937 header/padding.
54    MPEG2_AAC_IEC61937,
55    /// Dolby TrueHD data encapsulated in IEC 61937 header/padding.
56    #[cfg(any(doc, feature = "pa_v13"))]
57    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
58    TRUEHD_IEC61937,
59    /// DTS-HD Master Audio encapsulated in IEC 61937 header/padding.
60    #[cfg(any(doc, feature = "pa_v13"))]
61    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
62    DTSHD_IEC61937,
63
64    /// Represents an invalid encoding.
65    Invalid = -1,
66}
67
68/// Check is equal to `sys` equivalent
69#[test]
70fn enc_compare_capi() {
71    assert_eq!(std::mem::size_of::<Encoding>(), std::mem::size_of::<capi::pa_encoding_t>());
72    assert_eq!(std::mem::align_of::<Encoding>(), std::mem::align_of::<capi::pa_encoding_t>());
73
74    // Check order and value of variants match
75    // No point checking conversions in both directions since both are a transmute
76    assert_eq!(Encoding::Any,                Encoding::from(capi::pa_encoding_t::Any));
77    assert_eq!(Encoding::PCM,                Encoding::from(capi::pa_encoding_t::PCM));
78    assert_eq!(Encoding::AC3_IEC61937,       Encoding::from(capi::pa_encoding_t::AC3_IEC61937));
79    assert_eq!(Encoding::EAC3_IEC61937,      Encoding::from(capi::pa_encoding_t::EAC3_IEC61937));
80    assert_eq!(Encoding::MPEG_IEC61937,      Encoding::from(capi::pa_encoding_t::MPEG_IEC61937));
81    assert_eq!(Encoding::DTS_IEC61937,       Encoding::from(capi::pa_encoding_t::DTS_IEC61937));
82    assert_eq!(Encoding::MPEG2_AAC_IEC61937, Encoding::from(capi::pa_encoding_t::MPEG2_AAC_IEC61937));
83    #[cfg(any(doc, feature = "pa_v13"))]
84    assert_eq!(Encoding::TRUEHD_IEC61937,    Encoding::from(capi::pa_encoding_t::TRUEHD_IEC61937));
85    #[cfg(any(doc, feature = "pa_v13"))]
86    assert_eq!(Encoding::DTSHD_IEC61937,     Encoding::from(capi::pa_encoding_t::DTSHD_IEC61937));
87    assert_eq!(Encoding::Invalid,            Encoding::from(capi::pa_encoding_t::Invalid));
88}
89
90impl From<Encoding> for capi::pa_encoding_t {
91    #[inline]
92    fn from(e: Encoding) -> Self {
93        unsafe { std::mem::transmute(e) }
94    }
95}
96impl From<capi::pa_encoding_t> for Encoding {
97    #[inline]
98    fn from(e: capi::pa_encoding_t) -> Self {
99        unsafe { std::mem::transmute(e) }
100    }
101}
102
103impl Default for Encoding {
104    #[inline(always)]
105    fn default() -> Self {
106        Encoding::Invalid
107    }
108}
109
110/// Represents the format of data provided in a stream or processed by a sink.
111pub struct Info {
112    /// The actual C object.
113    pub(crate) ptr: *mut InfoInternal,
114    /// Wrapped property list pointer.
115    properties: Proplist,
116    /// Used to avoid freeing the internal object when used as a weak wrapper in callbacks.
117    weak: bool,
118}
119
120unsafe impl Send for Info {}
121unsafe impl Sync for Info {}
122
123/// The raw C structure (with documentation).
124#[repr(C)]
125pub(crate) struct InfoInternal {
126    /* NOTE: This struct must be directly usable by the C API, thus same attributes/layout/etc */
127    /// The encoding used for the format.
128    pub encoding: Encoding,
129    /// Additional encoding-specific properties such as sample rate, bitrate, etc.
130    pub list: *mut ProplistInternal,
131}
132
133/// Test size is equal to `sys` equivalent (duplicated here for different documentation)
134#[test]
135fn info_compare_capi() {
136    assert_eq!(std::mem::size_of::<InfoInternal>(), std::mem::size_of::<capi::pa_format_info>());
137    assert_eq!(std::mem::align_of::<InfoInternal>(), std::mem::align_of::<capi::pa_format_info>());
138}
139
140impl std::fmt::Debug for Info {
141    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
142        write!(f, "Info {{ encoding: {:?}, properties: {:?} }}", self.get_encoding(),
143            *self.get_properties())
144    }
145}
146
147impl Encoding {
148    /// Returns a printable string representing the given encoding type.
149    pub fn to_string(e: Self) -> Option<Cow<'static, str>> {
150        let ptr = unsafe { capi::pa_encoding_to_string(e.into()) };
151        match ptr.is_null() {
152            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy() }),
153            true => None,
154        }
155    }
156
157    /// Converts a string of the form returned by [`to_string()`](Self::to_string) back to an
158    /// `Encoding`.
159    #[cfg(any(doc, feature = "pa_v12"))]
160    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v12")))]
161    pub fn from_string(encoding: &str) -> Self {
162        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
163        // as_ptr() giving dangling pointers!
164        let c_enc = CString::new(encoding).unwrap();
165        unsafe { capi::pa_encoding_from_string(c_enc.as_ptr()).into() }
166    }
167}
168
169impl Info {
170    /// Allocates a new `Info` structure.
171    ///
172    /// Clients must initialise at least the encoding field themselves. Returns `None` on failure.
173    pub fn new() -> Option<Self> {
174        let ptr = unsafe { capi::pa_format_info_new() };
175        match ptr.is_null() {
176            false => Some(Self::from_raw(ptr as *mut InfoInternal)),
177            true => None,
178        }
179    }
180
181    /// Parses a human-readable string of the form generated by [`print()`](Self::print) into an
182    /// `Info` structure.
183    ///
184    /// Returns `None` on failure.
185    pub fn new_from_string(s: &str) -> Option<Self> {
186        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
187        // as_ptr() giving dangling pointers!
188        let c_str = CString::new(s).unwrap();
189        let ptr = unsafe { capi::pa_format_info_from_string(c_str.as_ptr()) };
190        match ptr.is_null() {
191            false => Some(Self::from_raw(ptr as *mut InfoInternal)),
192            true => None,
193        }
194    }
195
196    /// Utility function to take a [`Spec`] and generate the corresponding `Info`.
197    ///
198    /// Note that if you want the server to choose some of the stream parameters, for example the
199    /// sample rate, so that they match the device parameters, then you shouldn’t use this function.
200    /// In order to allow the server to choose a parameter value, that parameter must be left
201    /// unspecified in the `Info` object, and this function always specifies all parameters. An
202    /// exception is the channel map: if you pass `None` for the channel map, then the channel map
203    /// will be left unspecified, allowing the server to choose it.
204    ///
205    /// Returns `None` on failure.
206    ///
207    /// [`Spec`]: crate::sample::Spec
208    pub fn new_from_sample_spec(ss: &sample::Spec, map: Option<&channelmap::Map>) -> Option<Self> {
209        let p_map = map.map_or(null::<capi::pa_channel_map>(), |m| m.as_ref());
210        let ptr = unsafe { capi::pa_format_info_from_sample_spec(ss.as_ref(), p_map) };
211        match ptr.is_null() {
212            false => Some(Self::from_raw(ptr as *mut InfoInternal)),
213            true => None,
214        }
215    }
216
217    /// Creates a new `Info` from an existing [`InfoInternal`] pointer.
218    pub(crate) fn from_raw(ptr: *mut InfoInternal) -> Self {
219        assert_eq!(false, ptr.is_null());
220        // Note, yes, this should be using `from_raw_weak()`, the ‘free’ function for a format info
221        // object free’s its own proplist!
222        unsafe { Self { ptr: ptr, properties: Proplist::from_raw_weak((*ptr).list), weak: false } }
223    }
224
225    /// Creates a new `Info` from an existing [`InfoInternal`] pointer.
226    ///
227    /// This is the ‘weak’ version, which avoids destroying the internal object when dropped.
228    pub(crate) fn from_raw_weak(ptr: *mut InfoInternal) -> Self {
229        assert_eq!(false, ptr.is_null());
230        unsafe { Self { ptr: ptr, properties: Proplist::from_raw_weak((*ptr).list), weak: true } }
231    }
232
233    /// Checks whether the `Info` structure is valid.
234    #[inline]
235    pub fn is_valid(&self) -> bool {
236        unsafe { capi::pa_format_info_valid(self.ptr as *const capi::pa_format_info) != 0 }
237    }
238
239    /// Checks whether the `Info` structure represents a PCM (i.e. uncompressed data) format.
240    #[inline]
241    pub fn is_pcm(&self) -> bool {
242        unsafe { capi::pa_format_info_is_pcm(self.ptr as *const capi::pa_format_info) != 0 }
243    }
244
245    /// Checks whether the format represented by self is a subset of the format represented by
246    /// `with`.
247    ///
248    /// This means that `with` must have all the fields that self does, but the reverse need not be
249    /// true. This is typically expected to be used to check if a stream’s format is compatible with
250    /// a given sink. In such a case, self would be the sink’s format and `with` would be the
251    /// streams.
252    #[inline]
253    pub fn is_compatible_with(&self, with: &Self) -> bool {
254        unsafe { capi::pa_format_info_is_compatible(self.ptr as *const capi::pa_format_info,
255            with.ptr as *const capi::pa_format_info) != 0 }
256    }
257
258    /// Gets a human-readable string representing the given format.
259    pub fn print(&self) -> String {
260        const PRINT_MAX: usize = capi::PA_FORMAT_INFO_SNPRINT_MAX;
261        let mut tmp = Vec::with_capacity(PRINT_MAX);
262        unsafe {
263            capi::pa_format_info_snprint(tmp.as_mut_ptr(), PRINT_MAX,
264                self.ptr as *const capi::pa_format_info);
265            CStr::from_ptr(tmp.as_mut_ptr()).to_string_lossy().into_owned()
266        }
267    }
268
269    /// Utility function to generate a [`Spec`] and [`Map`] corresponding to a given `Info`.
270    ///
271    /// The conversion for PCM formats is straight-forward. For non-PCM formats, if there is a fixed
272    /// size-time conversion (i.e. all IEC61937-encapsulated formats), a “fake” sample spec whose
273    /// size-time conversion corresponds to this format is provided and the channel map argument is
274    /// ignored. For formats with variable size-time conversion, this function will fail.
275    ///
276    /// [`Spec`]: crate::sample::Spec
277    /// [`Map`]: crate::channelmap::Map
278    pub fn to_sample_spec(&self, ss: &mut sample::Spec, map: &mut channelmap::Map)
279        -> Result<(), PAErr>
280    {
281        match unsafe { capi::pa_format_info_to_sample_spec(
282            self.ptr as *const capi::pa_format_info, ss.as_mut(), map.as_mut()) }
283        {
284            0 => Ok(()),
285            e => Err(PAErr(e)),
286        }
287    }
288
289    /// Gets the encoding.
290    #[inline]
291    pub fn get_encoding(&self) -> Encoding {
292        unsafe { (*self.ptr).encoding }
293    }
294
295    /// Sets the encoding attribute.
296    #[inline]
297    pub fn set_encoding(&mut self, encoding: Encoding) {
298        unsafe { (*self.ptr).encoding = encoding };
299    }
300
301    /// Gets an immutable reference to the property list.
302    #[inline]
303    pub fn get_properties(&self) -> &Proplist {
304        &self.properties
305    }
306
307    /// Gets a mutable reference to the property list.
308    #[inline]
309    pub fn get_properties_mut(&mut self) -> &mut Proplist {
310        &mut self.properties
311    }
312
313    /// Gets the type of property key.
314    pub fn get_prop_type(&self, key: &str) -> PropType {
315        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
316        // as_ptr() giving dangling pointers!
317        let c_key = CString::new(key).unwrap();
318        unsafe { capi::pa_format_info_get_prop_type(self.ptr as *const capi::pa_format_info,
319            c_key.as_ptr()) }
320    }
321
322    /// Gets an integer property.
323    pub fn get_prop_int(&self, key: &str) -> Result<i32, PAErr> {
324        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
325        // as_ptr() giving dangling pointers!
326        let mut i: i32 = 0;
327        let c_key = CString::new(key).unwrap();
328        match unsafe { capi::pa_format_info_get_prop_int(self.ptr as *const capi::pa_format_info,
329            c_key.as_ptr(), &mut i) }
330        {
331            0 => Ok(i),
332            e => Err(PAErr(e)),
333        }
334    }
335
336    /// Gets an integer range property. On success, returns min-max tuple.
337    pub fn get_prop_int_range(&self, key: &str) -> Result<(i32, i32), PAErr> {
338        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
339        // as_ptr() giving dangling pointers!
340        let mut min: i32 = 0;
341        let mut max: i32 = 0;
342        let c_key = CString::new(key).unwrap();
343        match unsafe { capi::pa_format_info_get_prop_int_range(
344            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut min, &mut max) }
345        {
346            0 => Ok((min, max)),
347            e => Err(PAErr(e)),
348        }
349    }
350
351    /// Gets an integer array property.
352    ///
353    /// Returns `None` on error.
354    pub fn get_prop_int_array(&self, key: &str) -> Option<Vec<i32>> {
355        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
356        // as_ptr() giving dangling pointers!
357        let c_key = CString::new(key).unwrap();
358        let mut count: i32 = 0;
359        let mut p_ints = null_mut::<i32>();
360        let result = unsafe { capi::pa_format_info_get_prop_int_array(
361            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_ints, &mut count) };
362        if result != 0 {
363            return None;
364        }
365        // Clone each int in the array
366        let mut values: Vec<i32> = Vec::with_capacity(count as usize);
367        for i in 0..count {
368            values.push(unsafe { *p_ints.offset(i as isize) });
369        }
370        // Free the PA allocated array
371        unsafe { capi::pa_xfree(p_ints as *mut c_void) };
372        // Return vector of ints
373        Some(values)
374    }
375
376    /// Gets a string property.
377    pub fn get_prop_string(&self, key: &str) -> Option<String> {
378        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
379        // as_ptr() giving dangling pointers!
380        let c_key = CString::new(key).unwrap();
381        let mut p_str = null_mut::<c_char>();
382        let result = unsafe { capi::pa_format_info_get_prop_string(
383            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut p_str) };
384        if result != 0 || p_str.is_null() {
385            return None;
386        }
387        unsafe {
388            let ret = Some(CStr::from_ptr(p_str).to_string_lossy().into_owned());
389            capi::pa_xfree(p_str as *mut c_void);
390            ret
391        }
392    }
393
394    /// Gets a string array property.
395    pub fn get_prop_string_array(&self, key: &str) -> Option<Vec<String>> {
396        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
397        // as_ptr() giving dangling pointers!
398        let c_key = CString::new(key).unwrap();
399        let mut count: i32 = 0;
400        let mut pp_str = null_mut::<*mut c_char>();
401        let result = unsafe { capi::pa_format_info_get_prop_string_array(
402            self.ptr as *const capi::pa_format_info, c_key.as_ptr(), &mut pp_str, &mut count) };
403        if result != 0 || pp_str.is_null() {
404            return None;
405        }
406        // Clone each string in the array to owned String
407        let mut values: Vec<String> = Vec::with_capacity(count as usize);
408        for i in 0..count {
409            let p_str = unsafe { *pp_str.offset(i as isize) };
410            if !p_str.is_null() {
411                values.push(unsafe { CStr::from_ptr(p_str).to_string_lossy().into_owned() });
412            }
413        }
414        // Free all PA internally allocated strings
415        unsafe { capi::pa_format_info_free_string_array(pp_str, count) };
416        // Return vector of Strings
417        Some(values)
418    }
419
420    /// Gets the sample format stored in the format info.
421    ///
422    /// Returns `Err` if the sample format property is not set at all, or is invalid.
423    #[cfg(any(doc, feature = "pa_v13"))]
424    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
425    pub fn get_sample_format(&self) -> Result<crate::sample::Format, PAErr> {
426        let mut sf: capi::pa_sample_format_t = capi::PA_SAMPLE_INVALID;
427        match unsafe { capi::pa_format_info_get_sample_format(
428            self.ptr as *const capi::pa_format_info, &mut sf) }
429        {
430            0 => Ok(crate::sample::Format::from(sf)),
431            e => Err(PAErr(e)),
432        }
433    }
434
435    /// Gets the sample rate stored in the format info.
436    ///
437    /// Returns `Err` if the sample rate property is not set at all, or is invalid.
438    #[cfg(any(doc, feature = "pa_v13"))]
439    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
440    pub fn get_rate(&self) -> Result<u32, PAErr> {
441        let mut rate: u32 = 0;
442        match unsafe { capi::pa_format_info_get_rate(self.ptr as *const capi::pa_format_info,
443            &mut rate) }
444        {
445            0 => Ok(rate),
446            e => Err(PAErr(e)),
447        }
448    }
449
450    /// Gets the channel count stored in the format info.
451    ///
452    /// Returns `Err` if the channels property is not set at all, or is invalid.
453    #[cfg(any(doc, feature = "pa_v13"))]
454    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
455    pub fn get_channel_count(&self) -> Result<u8, PAErr> {
456        let mut channels: u8 = 0;
457        match unsafe { capi::pa_format_info_get_channels(self.ptr as *const capi::pa_format_info,
458            &mut channels) }
459        {
460            0 => Ok(channels),
461            e => Err(PAErr(e)),
462        }
463    }
464
465    /// Gets the channel map stored in the format info.
466    ///
467    /// Returns `Err` if the channel map property is not set at all, or if the string form it is
468    /// stored in within the property set fails to parse successfully.
469    #[cfg(any(doc, feature = "pa_v13"))]
470    #[cfg_attr(docsrs, doc(cfg(feature = "pa_v13")))]
471    pub fn get_channel_map(&self) -> Result<crate::channelmap::Map, PAErr> {
472        // Returning the entire struct written to here may be a little less efficient than taking a
473        // pointer like the C API, but we avoid the possibility of leaving the user with an
474        // incomplete struct; parsing from string form is inefficient anyway, and we thus should not
475        // expect this to be done frequently anyway.
476        let mut map: capi::pa_channel_map = capi::pa_channel_map::default();
477        match unsafe { capi::pa_format_info_get_channel_map(
478            self.ptr as *const capi::pa_format_info, &mut map) }
479        {
480            0 => Ok(crate::channelmap::Map::from(map)),
481            e => Err(PAErr(e)),
482        }
483    }
484
485    /// Sets an integer property.
486    pub fn set_prop_int(&mut self, key: &str, value: i32) {
487        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
488        // as_ptr() giving dangling pointers!
489        let c_key = CString::new(key).unwrap();
490        unsafe { capi::pa_format_info_set_prop_int(self.ptr as *mut capi::pa_format_info,
491            c_key.as_ptr(), value); }
492    }
493
494    /// Sets a property with a list of integer values.
495    pub fn set_prop_int_array(&mut self, key: &str, values: &[i32]) {
496        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
497        // as_ptr() giving dangling pointers!
498        let c_key = CString::new(key).unwrap();
499        unsafe { capi::pa_format_info_set_prop_int_array(self.ptr as *mut capi::pa_format_info,
500            c_key.as_ptr(), values.as_ptr(), values.len() as i32); }
501    }
502
503    /// Sets a property which can have any value in a given integer range.
504    pub fn set_prop_int_range(&mut self, key: &str, min: i32, max: i32) {
505        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
506        // as_ptr() giving dangling pointers!
507        let c_key = CString::new(key).unwrap();
508        unsafe { capi::pa_format_info_set_prop_int_range(self.ptr as *mut capi::pa_format_info,
509            c_key.as_ptr(), min, max); }
510    }
511
512    /// Sets a string property.
513    pub fn set_prop_string(&mut self, key: &str, value: &str) {
514        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
515        // as_ptr() giving dangling pointers!
516        let c_key = CString::new(key).unwrap();
517        let c_value = CString::new(value).unwrap();
518        unsafe { capi::pa_format_info_set_prop_string(self.ptr as *mut capi::pa_format_info,
519            c_key.as_ptr(), c_value.as_ptr()); }
520    }
521
522    /// Sets a property with a list of string values.
523    pub fn set_prop_string_array(&mut self, key: &str, values: &[&str]) {
524        // Warning: New CStrings will be immediately freed if not bound to a variable, leading to
525        // as_ptr() giving dangling pointers!
526        let c_key = CString::new(key).unwrap();
527        let mut c_values: Vec<CString> = Vec::with_capacity(values.len());
528        for v in values {
529            c_values.push(CString::new(*v).unwrap());
530        }
531
532        // Capture array of pointers to the above CString values
533        let mut c_value_ptrs: Vec<*const c_char> = Vec::with_capacity(c_values.len());
534        for v in &c_values {
535            c_value_ptrs.push(v.as_ptr());
536        }
537        unsafe {
538            capi::pa_format_info_set_prop_string_array(self.ptr as *mut capi::pa_format_info,
539                c_key.as_ptr(), c_value_ptrs.as_ptr(), c_value_ptrs.len() as i32);
540        }
541    }
542
543    /// Convenience method to set the sample format as a property.
544    ///
545    /// Note for PCM: If the sample format is left unspecified in the `Info` object, then the server
546    /// will select the stream sample format. In that case the stream sample format will most likely
547    /// match the device sample format, meaning that sample format conversion will be avoided.
548    #[inline]
549    pub fn set_sample_format(&mut self, sf: sample::Format) {
550        unsafe { capi::pa_format_info_set_sample_format(self.ptr as *mut capi::pa_format_info,
551            sf.into()); }
552    }
553
554    /// Convenience method to set the sampling rate as a property.
555    ///
556    /// Note for PCM: If the sample rate is left unspecified in the `Info` object, then the server
557    /// will select the stream sample rate. In that case the stream sample rate will most likely
558    /// match the device sample rate, meaning that sample rate conversion will be avoided.
559    #[inline]
560    pub fn set_rate(&mut self, rate: i32) {
561        unsafe { capi::pa_format_info_set_rate(self.ptr as *mut capi::pa_format_info, rate) }
562    }
563
564    /// Convenience method to set the number of channels as a property.
565    ///
566    /// Note for PCM: If the channel count is left unspecified in the `Info` object, then the server
567    /// will select the stream channel count. In that case the stream channel count will most likely
568    /// match the device channel count, meaning that up/downmixing will be avoided.
569    #[inline]
570    pub fn set_channels(&mut self, channels: u32) {
571        debug_assert!(channels <= std::i32::MAX as u32);
572        unsafe { capi::pa_format_info_set_channels(self.ptr as *mut capi::pa_format_info,
573            channels as i32) }
574    }
575
576    /// Convenience method to set the channel map as a property.
577    ///
578    /// Note for PCM: If the channel map is left unspecified in the `Info` object, then the server
579    /// will select the stream channel map. In that case the stream channel map will most likely
580    /// match the device channel map, meaning that remixing will be avoided.
581    #[inline]
582    pub fn set_channel_map(&mut self, map: &channelmap::Map) {
583        unsafe { capi::pa_format_info_set_channel_map(self.ptr as *mut capi::pa_format_info,
584            map.as_ref()) }
585    }
586}
587
588impl Drop for Info {
589    fn drop(&mut self) {
590        if !self.weak {
591            unsafe { capi::pa_format_info_free(self.ptr as *mut capi::pa_format_info) };
592        }
593    }
594}
595
596impl Clone for Info {
597    /// Returns a new `Info` struct and representing the same format. If this is called on a ‘weak’
598    /// instance, a non-weak object is returned.
599    fn clone(&self) -> Self {
600        let ptr = unsafe { capi::pa_format_info_copy(self.ptr as *const capi::pa_format_info) };
601        assert_eq!(false, ptr.is_null());
602        Self::from_raw(ptr as *mut InfoInternal)
603    }
604}