gstreamer_audio/
audio_format_info.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{cmp::Ordering, fmt, marker::PhantomData, str};
4
5use crate::ffi;
6use glib::{
7    prelude::*,
8    translate::{from_glib, from_glib_none, FromGlib, IntoGlib, ToGlibPtr, ToGlibPtrMut},
9};
10
11#[derive(PartialEq, Eq, Copy, Clone, Debug, Hash)]
12pub enum AudioEndianness {
13    Unknown,
14    LittleEndian = 1234,
15    BigEndian = 4321,
16}
17
18impl FromGlib<i32> for AudioEndianness {
19    #[allow(unused_unsafe)]
20    #[inline]
21    unsafe fn from_glib(value: i32) -> Self {
22        skip_assert_initialized!();
23
24        match value {
25            1234 => Self::LittleEndian,
26            4321 => Self::BigEndian,
27            _ => Self::Unknown,
28        }
29    }
30}
31
32impl IntoGlib for AudioEndianness {
33    type GlibType = i32;
34
35    #[inline]
36    fn into_glib(self) -> i32 {
37        match self {
38            Self::LittleEndian => 1234,
39            Self::BigEndian => 4321,
40            _ => 0,
41        }
42    }
43}
44
45/// Information for an audio format.
46#[doc(alias = "GstAudioFormatInfo")]
47#[derive(Copy, Clone)]
48pub struct AudioFormatInfo(&'static ffi::GstAudioFormatInfo);
49
50impl AudioFormatInfo {
51    #[inline]
52    pub fn from_format(format: crate::AudioFormat) -> Self {
53        assert_initialized_main_thread!();
54
55        unsafe {
56            let info = ffi::gst_audio_format_get_info(format.into_glib());
57            debug_assert!(!info.is_null());
58
59            Self(&*info)
60        }
61    }
62
63    #[inline]
64    pub fn format(&self) -> crate::AudioFormat {
65        unsafe { from_glib(self.0.format) }
66    }
67
68    #[inline]
69    pub fn name<'a>(&self) -> &'a glib::GStr {
70        unsafe { glib::GStr::from_ptr(self.0.name) }
71    }
72
73    #[inline]
74    pub fn description<'a>(&self) -> &'a glib::GStr {
75        unsafe { glib::GStr::from_ptr(self.0.description) }
76    }
77
78    #[inline]
79    pub fn flags(&self) -> crate::AudioFormatFlags {
80        unsafe { from_glib(self.0.flags) }
81    }
82
83    #[inline]
84    pub fn endianness(&self) -> AudioEndianness {
85        unsafe { from_glib(self.0.endianness) }
86    }
87
88    #[inline]
89    pub fn width(&self) -> u32 {
90        self.0.width as u32
91    }
92
93    #[inline]
94    pub fn depth(&self) -> u32 {
95        self.0.depth as u32
96    }
97
98    #[inline]
99    pub fn unpack_format(&self) -> crate::AudioFormat {
100        unsafe { from_glib(self.0.unpack_format) }
101    }
102
103    #[inline]
104    pub fn silence<'a>(&self) -> &'a [u8] {
105        &self.0.silence
106    }
107
108    pub fn unpack(&self, flags: crate::AudioPackFlags, dest: &mut [u8], src: &[u8]) {
109        let unpack_format = Self::from_format(self.unpack_format());
110        let unpack_width = unpack_format.width() as usize;
111
112        if unpack_width == 0 || self.0.unpack_func.is_none() {
113            panic!("No unpack format for {self:?}");
114        }
115
116        let self_width = self.width() as usize;
117        if self_width == 0 {
118            panic!("No width for {self:?}");
119        }
120
121        if src.len() % (self_width / 8) != 0 {
122            panic!("Incomplete number of samples in src");
123        }
124
125        let nsamples = src.len() / (self_width / 8);
126
127        if dest.len() != nsamples * (unpack_width / 8) {
128            panic!("Invalid dest length");
129        }
130
131        unsafe {
132            (self.0.unpack_func.as_ref().unwrap())(
133                self.0,
134                flags.into_glib(),
135                dest.as_mut_ptr() as *mut _,
136                src.as_ptr() as *const _,
137                nsamples as i32,
138            );
139        }
140    }
141
142    pub fn pack(&self, flags: crate::AudioPackFlags, dest: &mut [u8], src: &[u8]) {
143        let unpack_format = Self::from_format(self.unpack_format());
144        let unpack_width = unpack_format.width() as usize;
145
146        if unpack_width == 0 || self.0.pack_func.is_none() {
147            panic!("No unpack format for {self:?}");
148        }
149
150        let self_width = self.width() as usize;
151        if self_width == 0 {
152            panic!("No width for {self:?}");
153        }
154
155        if src.len() % (unpack_width / 8) != 0 {
156            panic!("Incomplete number of samples in src");
157        }
158
159        let nsamples = src.len() / (unpack_width / 8);
160
161        if dest.len() != nsamples * (self_width / 8) {
162            panic!("Invalid dest length");
163        }
164
165        unsafe {
166            (self.0.pack_func.as_ref().unwrap())(
167                self.0,
168                flags.into_glib(),
169                src.as_ptr() as *const _,
170                dest.as_mut_ptr() as *mut _,
171                nsamples as i32,
172            );
173        }
174    }
175
176    /// Fill `length` bytes in `dest` with silence samples for `self`.
177    /// ## `dest`
178    /// a destination
179    ///  to fill
180    #[doc(alias = "gst_audio_format_info_fill_silence")]
181    #[doc(alias = "gst_audio_format_fill_silence")]
182    pub fn fill_silence(&self, dest: &mut [u8]) {
183        let self_width = self.width() as usize;
184
185        if self_width == 0 {
186            panic!("Filling with silence unsupported");
187        }
188
189        if dest.len() % (self_width / 8) != 0 {
190            panic!("Incomplete number of samples in dest");
191        }
192
193        unsafe {
194            cfg_if::cfg_if! {
195                if #[cfg(feature = "v1_20")] {
196                    ffi::gst_audio_format_info_fill_silence(self.0, dest.as_mut_ptr() as *mut _, dest.len())
197                } else {
198                    ffi::gst_audio_format_fill_silence(self.0, dest.as_mut_ptr() as *mut _, dest.len())
199                }
200            }
201        }
202    }
203
204    #[inline]
205    pub fn is_float(&self) -> bool {
206        self.flags().contains(crate::AudioFormatFlags::FLOAT)
207    }
208
209    #[inline]
210    pub fn is_integer(&self) -> bool {
211        self.flags().contains(crate::AudioFormatFlags::INTEGER)
212    }
213
214    #[inline]
215    pub fn is_signed(&self) -> bool {
216        self.flags().contains(crate::AudioFormatFlags::SIGNED)
217    }
218
219    #[inline]
220    pub fn is_little_endian(&self) -> bool {
221        self.endianness() == AudioEndianness::LittleEndian
222    }
223
224    #[inline]
225    pub fn is_big_endian(&self) -> bool {
226        self.endianness() == AudioEndianness::BigEndian
227    }
228}
229
230unsafe impl Sync for AudioFormatInfo {}
231unsafe impl Send for AudioFormatInfo {}
232
233impl PartialEq for AudioFormatInfo {
234    #[inline]
235    fn eq(&self, other: &Self) -> bool {
236        self.format() == other.format()
237    }
238}
239
240impl Eq for AudioFormatInfo {}
241
242impl PartialOrd for AudioFormatInfo {
243    #[inline]
244    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
245        Some(self.cmp(other))
246    }
247}
248
249impl Ord for AudioFormatInfo {
250    // See GST_AUDIO_FORMATS_ALL for the sorting algorithm
251    fn cmp(&self, other: &Self) -> Ordering {
252        self.depth()
253            .cmp(&other.depth())
254            .then_with(|| self.width().cmp(&other.width()))
255            .then_with(|| {
256                match (
257                    self.flags().contains(crate::AudioFormatFlags::FLOAT),
258                    other.flags().contains(crate::AudioFormatFlags::FLOAT),
259                ) {
260                    (true, false) => Ordering::Greater,
261                    (false, true) => Ordering::Less,
262                    _ => Ordering::Equal,
263                }
264            })
265            .then_with(|| {
266                match (
267                    self.flags().contains(crate::AudioFormatFlags::SIGNED),
268                    other.flags().contains(crate::AudioFormatFlags::SIGNED),
269                ) {
270                    (true, false) => Ordering::Greater,
271                    (false, true) => Ordering::Less,
272                    _ => Ordering::Equal,
273                }
274            })
275            .then_with(|| match (self.endianness(), other.endianness()) {
276                (crate::AudioEndianness::LittleEndian, crate::AudioEndianness::BigEndian) => {
277                    #[cfg(target_endian = "little")]
278                    {
279                        Ordering::Greater
280                    }
281                    #[cfg(target_endian = "big")]
282                    {
283                        Ordering::Less
284                    }
285                }
286                (crate::AudioEndianness::BigEndian, crate::AudioEndianness::LittleEndian) => {
287                    #[cfg(target_endian = "little")]
288                    {
289                        Ordering::Less
290                    }
291                    #[cfg(target_endian = "big")]
292                    {
293                        Ordering::Greater
294                    }
295                }
296                _ => Ordering::Equal,
297            })
298    }
299}
300
301impl fmt::Debug for AudioFormatInfo {
302    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
303        f.debug_struct("AudioFormatInfo")
304            .field("format", &self.format())
305            .field("name", &self.name())
306            .field("description", &self.description())
307            .field("flags", &self.flags())
308            .field("endianness", &self.endianness())
309            .field("width", &self.width())
310            .field("depth", &self.depth())
311            .field("silence", &self.silence())
312            .finish()
313    }
314}
315
316impl fmt::Display for AudioFormatInfo {
317    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
318        f.write_str(self.name())
319    }
320}
321
322impl str::FromStr for crate::AudioFormatInfo {
323    type Err = glib::BoolError;
324
325    fn from_str(s: &str) -> Result<Self, Self::Err> {
326        skip_assert_initialized!();
327        let format = s.parse()?;
328        Ok(Self::from_format(format))
329    }
330}
331
332impl From<crate::AudioFormat> for AudioFormatInfo {
333    #[inline]
334    fn from(f: crate::AudioFormat) -> Self {
335        skip_assert_initialized!();
336        Self::from_format(f)
337    }
338}
339
340impl glib::types::StaticType for AudioFormatInfo {
341    #[inline]
342    fn static_type() -> glib::types::Type {
343        unsafe { glib::translate::from_glib(ffi::gst_audio_format_info_get_type()) }
344    }
345}
346
347impl glib::value::ValueType for AudioFormatInfo {
348    type Type = Self;
349}
350
351#[doc(hidden)]
352unsafe impl<'a> glib::value::FromValue<'a> for AudioFormatInfo {
353    type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
354
355    unsafe fn from_value(value: &'a glib::Value) -> Self {
356        skip_assert_initialized!();
357        from_glib_none(glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0)
358            as *mut ffi::GstAudioFormatInfo)
359    }
360}
361
362#[doc(hidden)]
363impl glib::value::ToValue for AudioFormatInfo {
364    fn to_value(&self) -> glib::Value {
365        let mut value = glib::Value::for_value_type::<Self>();
366        unsafe {
367            glib::gobject_ffi::g_value_set_boxed(
368                value.to_glib_none_mut().0,
369                self.to_glib_none().0 as *mut _,
370            )
371        }
372        value
373    }
374
375    fn value_type(&self) -> glib::Type {
376        Self::static_type()
377    }
378}
379
380#[doc(hidden)]
381impl glib::value::ToValueOptional for AudioFormatInfo {
382    fn to_value_optional(s: Option<&Self>) -> glib::Value {
383        skip_assert_initialized!();
384        let mut value = glib::Value::for_value_type::<Self>();
385        unsafe {
386            glib::gobject_ffi::g_value_set_boxed(
387                value.to_glib_none_mut().0,
388                s.to_glib_none().0 as *mut _,
389            )
390        }
391        value
392    }
393}
394
395#[doc(hidden)]
396impl From<AudioFormatInfo> for glib::Value {
397    fn from(v: AudioFormatInfo) -> glib::Value {
398        skip_assert_initialized!();
399        glib::value::ToValue::to_value(&v)
400    }
401}
402
403#[doc(hidden)]
404impl glib::translate::GlibPtrDefault for AudioFormatInfo {
405    type GlibType = *mut ffi::GstAudioFormatInfo;
406}
407
408#[doc(hidden)]
409unsafe impl glib::translate::TransparentPtrType for AudioFormatInfo {}
410
411#[doc(hidden)]
412impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstAudioFormatInfo> for AudioFormatInfo {
413    type Storage = PhantomData<&'a Self>;
414
415    #[inline]
416    fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstAudioFormatInfo, Self> {
417        glib::translate::Stash(self.0, PhantomData)
418    }
419
420    fn to_glib_full(&self) -> *const ffi::GstAudioFormatInfo {
421        unimplemented!()
422    }
423}
424
425#[doc(hidden)]
426impl glib::translate::FromGlibPtrNone<*mut ffi::GstAudioFormatInfo> for AudioFormatInfo {
427    #[inline]
428    unsafe fn from_glib_none(ptr: *mut ffi::GstAudioFormatInfo) -> Self {
429        Self(&*ptr)
430    }
431}
432
433#[doc(hidden)]
434impl glib::translate::FromGlibPtrNone<*const ffi::GstAudioFormatInfo> for AudioFormatInfo {
435    #[inline]
436    unsafe fn from_glib_none(ptr: *const ffi::GstAudioFormatInfo) -> Self {
437        Self(&*ptr)
438    }
439}
440
441#[cfg(test)]
442mod tests {
443    use super::*;
444
445    #[test]
446    fn test_get() {
447        gst::init().unwrap();
448
449        let info = AudioFormatInfo::from_format(crate::AudioFormat::S16le);
450        assert_eq!(info.name(), "S16LE");
451
452        let other_info = "S16LE".parse().unwrap();
453        assert_eq!(info, other_info);
454    }
455
456    #[test]
457    fn pack_unpack() {
458        gst::init().unwrap();
459
460        let info = AudioFormatInfo::from_format(crate::AudioFormat::S16le);
461        let unpack_info = AudioFormatInfo::from_format(info.unpack_format());
462
463        assert!(unpack_info.width() > 0);
464
465        let input = [0, 0, 255, 255, 128, 128, 64, 64];
466        let mut unpacked = [0; 16];
467        let mut output = [0; 8];
468
469        info.unpack(crate::AudioPackFlags::empty(), &mut unpacked, &input);
470        info.pack(crate::AudioPackFlags::empty(), &mut output, &unpacked);
471
472        assert_eq!(input, output);
473    }
474}