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