gstreamer_audio/
audio_info.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{fmt, marker::PhantomData, mem, ptr, slice};
4
5use crate::ffi;
6use glib::translate::{
7    from_glib, from_glib_full, from_glib_none, IntoGlib, ToGlibPtr, ToGlibPtrMut,
8};
9use gst::prelude::*;
10
11/// Information describing audio properties. This information can be filled
12/// in from GstCaps with [`from_caps()`][Self::from_caps()].
13///
14/// Use the provided macros to access the info in this structure.
15#[doc(alias = "GstAudioInfo")]
16#[derive(Clone)]
17#[repr(transparent)]
18pub struct AudioInfo(ffi::GstAudioInfo);
19
20impl fmt::Debug for AudioInfo {
21    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22        f.debug_struct("AudioInfo")
23            .field("format-info", &self.format_info())
24            .field("rate", &self.rate())
25            .field("channels", &self.channels())
26            .field("positions", &self.positions())
27            .field("flags", &self.flags())
28            .field("layout", &self.layout())
29            .finish()
30    }
31}
32
33#[derive(Debug)]
34#[must_use = "The builder must be built to be used"]
35pub struct AudioInfoBuilder<'a> {
36    format: crate::AudioFormat,
37    rate: u32,
38    channels: u32,
39    positions: Option<&'a [crate::AudioChannelPosition]>,
40    flags: Option<crate::AudioFlags>,
41    layout: Option<crate::AudioLayout>,
42}
43
44impl<'a> AudioInfoBuilder<'a> {
45    #[must_use = "The built AudioInfo must be used"]
46    pub fn build(self) -> Result<AudioInfo, glib::error::BoolError> {
47        unsafe {
48            let mut info = mem::MaybeUninit::uninit();
49
50            if let Some(p) = self.positions {
51                if p.len() != self.channels as usize || p.len() > 64 {
52                    return Err(glib::bool_error!("Invalid positions length"));
53                }
54
55                let valid: bool = from_glib(ffi::gst_audio_check_valid_channel_positions(
56                    p.as_ptr() as *mut _,
57                    self.channels as i32,
58                    true.into_glib(),
59                ));
60                if !valid {
61                    return Err(glib::bool_error!("channel positions are invalid"));
62                }
63            }
64
65            let positions_ptr = self
66                .positions
67                .as_ref()
68                .map(|p| p.as_ptr())
69                .unwrap_or(ptr::null());
70
71            ffi::gst_audio_info_set_format(
72                info.as_mut_ptr(),
73                self.format.into_glib(),
74                self.rate as i32,
75                self.channels as i32,
76                positions_ptr as *mut _,
77            );
78
79            let mut info = info.assume_init();
80
81            if info.finfo.is_null() || info.rate <= 0 || info.channels <= 0 {
82                return Err(glib::bool_error!("Failed to build AudioInfo"));
83            }
84
85            if let Some(flags) = self.flags {
86                info.flags = flags.into_glib();
87            }
88
89            if let Some(layout) = self.layout {
90                info.layout = layout.into_glib();
91            }
92
93            Ok(AudioInfo(info))
94        }
95    }
96
97    pub fn positions(self, positions: &'a [crate::AudioChannelPosition]) -> AudioInfoBuilder<'a> {
98        Self {
99            positions: Some(positions),
100            ..self
101        }
102    }
103
104    pub fn positions_if(
105        self,
106        positions: &'a [crate::AudioChannelPosition],
107        predicate: bool,
108    ) -> AudioInfoBuilder<'a> {
109        if predicate {
110            self.positions(positions)
111        } else {
112            self
113        }
114    }
115
116    pub fn positions_if_some(
117        self,
118        positions: Option<&'a [crate::AudioChannelPosition]>,
119    ) -> AudioInfoBuilder<'a> {
120        if let Some(positions) = positions {
121            self.positions(positions)
122        } else {
123            self
124        }
125    }
126
127    pub fn flags(self, flags: crate::AudioFlags) -> Self {
128        Self {
129            flags: Some(flags),
130            ..self
131        }
132    }
133
134    pub fn flags_if(self, flags: crate::AudioFlags, predicate: bool) -> Self {
135        if predicate {
136            self.flags(flags)
137        } else {
138            self
139        }
140    }
141
142    pub fn flags_if_some(self, flags: Option<crate::AudioFlags>) -> Self {
143        if let Some(flags) = flags {
144            self.flags(flags)
145        } else {
146            self
147        }
148    }
149
150    pub fn layout(self, layout: crate::AudioLayout) -> Self {
151        Self {
152            layout: Some(layout),
153            ..self
154        }
155    }
156
157    pub fn layout_if(self, layout: crate::AudioLayout, predicate: bool) -> Self {
158        if predicate {
159            self.layout(layout)
160        } else {
161            self
162        }
163    }
164
165    pub fn layout_if_some(self, layout: Option<crate::AudioLayout>) -> Self {
166        if let Some(layout) = layout {
167            self.layout(layout)
168        } else {
169            self
170        }
171    }
172}
173
174impl AudioInfo {
175    pub fn builder<'a>(
176        format: crate::AudioFormat,
177        rate: u32,
178        channels: u32,
179    ) -> AudioInfoBuilder<'a> {
180        assert_initialized_main_thread!();
181
182        AudioInfoBuilder {
183            format,
184            rate,
185            channels,
186            positions: None,
187            flags: None,
188            layout: None,
189        }
190    }
191
192    pub fn builder_from_info(info: &AudioInfo) -> AudioInfoBuilder<'_> {
193        assert_initialized_main_thread!();
194
195        AudioInfoBuilder {
196            format: info.format(),
197            rate: info.rate(),
198            channels: info.channels(),
199            positions: info.positions(),
200            flags: Some(info.flags()),
201            layout: Some(info.layout()),
202        }
203    }
204
205    #[inline]
206    pub fn is_valid(&self) -> bool {
207        !self.0.finfo.is_null() && self.0.channels > 0 && self.0.rate > 0 && self.0.bpf > 0
208    }
209
210    /// Parse `caps` to generate a [`AudioInfo`][crate::AudioInfo].
211    /// ## `caps`
212    /// a [`gst::Caps`][crate::gst::Caps]
213    ///
214    /// # Returns
215    ///
216    /// A [`AudioInfo`][crate::AudioInfo], or [`None`] if `caps` couldn't be parsed
217    #[doc(alias = "gst_audio_info_from_caps")]
218    pub fn from_caps(caps: &gst::CapsRef) -> Result<Self, glib::error::BoolError> {
219        skip_assert_initialized!();
220
221        unsafe {
222            let mut info = mem::MaybeUninit::uninit();
223            if from_glib(ffi::gst_audio_info_from_caps(
224                info.as_mut_ptr(),
225                caps.as_ptr(),
226            )) {
227                Ok(Self(info.assume_init()))
228            } else {
229                Err(glib::bool_error!("Failed to create AudioInfo from caps"))
230            }
231        }
232    }
233
234    /// Convert the values of `self` into a [`gst::Caps`][crate::gst::Caps].
235    ///
236    /// # Returns
237    ///
238    /// the new [`gst::Caps`][crate::gst::Caps] containing the
239    ///  info of `self`.
240    #[doc(alias = "gst_audio_info_to_caps")]
241    pub fn to_caps(&self) -> Result<gst::Caps, glib::error::BoolError> {
242        unsafe {
243            let result = from_glib_full(ffi::gst_audio_info_to_caps(&self.0));
244            match result {
245                Some(c) => Ok(c),
246                None => Err(glib::bool_error!("Failed to create caps from AudioInfo")),
247            }
248        }
249    }
250
251    /// Converts among various [`gst::Format`][crate::gst::Format] types. This function handles
252    /// GST_FORMAT_BYTES, GST_FORMAT_TIME, and GST_FORMAT_DEFAULT. For
253    /// raw audio, GST_FORMAT_DEFAULT corresponds to audio frames. This
254    /// function can be used to handle pad queries of the type GST_QUERY_CONVERT.
255    /// ## `src_fmt`
256    /// [`gst::Format`][crate::gst::Format] of the `src_val`
257    /// ## `src_val`
258    /// value to convert
259    /// ## `dest_fmt`
260    /// [`gst::Format`][crate::gst::Format] of the `dest_val`
261    ///
262    /// # Returns
263    ///
264    /// TRUE if the conversion was successful.
265    ///
266    /// ## `dest_val`
267    /// pointer to destination value
268    #[doc(alias = "gst_audio_info_convert")]
269    pub fn convert<U: gst::format::SpecificFormattedValueFullRange>(
270        &self,
271        src_val: impl gst::format::FormattedValue,
272    ) -> Option<U> {
273        assert_initialized_main_thread!();
274        unsafe {
275            let mut dest_val = mem::MaybeUninit::uninit();
276            if from_glib(ffi::gst_audio_info_convert(
277                &self.0,
278                src_val.format().into_glib(),
279                src_val.into_raw_value(),
280                U::default_format().into_glib(),
281                dest_val.as_mut_ptr(),
282            )) {
283                Some(U::from_raw(U::default_format(), dest_val.assume_init()))
284            } else {
285                None
286            }
287        }
288    }
289
290    pub fn convert_generic(
291        &self,
292        src_val: impl gst::format::FormattedValue,
293        dest_fmt: gst::Format,
294    ) -> Option<gst::GenericFormattedValue> {
295        assert_initialized_main_thread!();
296        unsafe {
297            let mut dest_val = mem::MaybeUninit::uninit();
298            if from_glib(ffi::gst_audio_info_convert(
299                &self.0,
300                src_val.format().into_glib(),
301                src_val.into_raw_value(),
302                dest_fmt.into_glib(),
303                dest_val.as_mut_ptr(),
304            )) {
305                Some(gst::GenericFormattedValue::new(
306                    dest_fmt,
307                    dest_val.assume_init(),
308                ))
309            } else {
310                None
311            }
312        }
313    }
314
315    #[inline]
316    pub fn format(&self) -> crate::AudioFormat {
317        if self.0.finfo.is_null() {
318            return crate::AudioFormat::Unknown;
319        }
320
321        unsafe { from_glib((*self.0.finfo).format) }
322    }
323
324    #[inline]
325    pub fn format_info(&self) -> crate::AudioFormatInfo {
326        crate::AudioFormatInfo::from_format(self.format())
327    }
328
329    #[inline]
330    pub fn layout(&self) -> crate::AudioLayout {
331        unsafe { from_glib(self.0.layout) }
332    }
333
334    #[inline]
335    pub fn flags(&self) -> crate::AudioFlags {
336        unsafe { from_glib(self.0.flags) }
337    }
338
339    #[inline]
340    pub fn rate(&self) -> u32 {
341        self.0.rate as u32
342    }
343
344    #[inline]
345    pub fn channels(&self) -> u32 {
346        self.0.channels as u32
347    }
348
349    #[inline]
350    pub fn bpf(&self) -> u32 {
351        self.0.bpf as u32
352    }
353
354    #[inline]
355    pub fn bps(&self) -> u32 {
356        self.format_info().depth() >> 3
357    }
358
359    #[inline]
360    pub fn depth(&self) -> u32 {
361        self.format_info().depth()
362    }
363
364    #[inline]
365    pub fn width(&self) -> u32 {
366        self.format_info().width()
367    }
368
369    #[inline]
370    pub fn endianness(&self) -> crate::AudioEndianness {
371        self.format_info().endianness()
372    }
373
374    #[inline]
375    pub fn is_big_endian(&self) -> bool {
376        self.format_info().is_big_endian()
377    }
378
379    #[inline]
380    pub fn is_little_endian(&self) -> bool {
381        self.format_info().is_little_endian()
382    }
383
384    #[inline]
385    pub fn is_float(&self) -> bool {
386        self.format_info().is_float()
387    }
388
389    #[inline]
390    pub fn is_integer(&self) -> bool {
391        self.format_info().is_integer()
392    }
393
394    #[inline]
395    pub fn is_signed(&self) -> bool {
396        self.format_info().is_signed()
397    }
398
399    #[inline]
400    pub fn positions(&self) -> Option<&[crate::AudioChannelPosition]> {
401        if self.0.channels > 64 || self.is_unpositioned() {
402            return None;
403        }
404
405        Some(unsafe {
406            slice::from_raw_parts(
407                &self.0.position as *const i32 as *const crate::AudioChannelPosition,
408                self.0.channels as usize,
409            )
410        })
411    }
412
413    #[inline]
414    pub fn is_unpositioned(&self) -> bool {
415        self.flags().contains(crate::AudioFlags::UNPOSITIONED)
416    }
417}
418
419impl PartialEq for AudioInfo {
420    #[doc(alias = "gst_audio_info_is_equal")]
421    #[inline]
422    fn eq(&self, other: &Self) -> bool {
423        unsafe { from_glib(ffi::gst_audio_info_is_equal(&self.0, &other.0)) }
424    }
425}
426
427impl Eq for AudioInfo {}
428
429unsafe impl Send for AudioInfo {}
430unsafe impl Sync for AudioInfo {}
431
432impl glib::types::StaticType for AudioInfo {
433    #[inline]
434    fn static_type() -> glib::types::Type {
435        unsafe { glib::translate::from_glib(ffi::gst_audio_info_get_type()) }
436    }
437}
438
439impl glib::value::ValueType for AudioInfo {
440    type Type = Self;
441}
442
443#[doc(hidden)]
444unsafe impl<'a> glib::value::FromValue<'a> for AudioInfo {
445    type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
446
447    unsafe fn from_value(value: &'a glib::Value) -> Self {
448        skip_assert_initialized!();
449        from_glib_none(
450            glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut ffi::GstAudioInfo
451        )
452    }
453}
454
455#[doc(hidden)]
456impl glib::value::ToValue for AudioInfo {
457    fn to_value(&self) -> glib::Value {
458        let mut value = glib::Value::for_value_type::<Self>();
459        unsafe {
460            glib::gobject_ffi::g_value_set_boxed(
461                value.to_glib_none_mut().0,
462                self.to_glib_none().0 as *mut _,
463            )
464        }
465        value
466    }
467
468    fn value_type(&self) -> glib::Type {
469        Self::static_type()
470    }
471}
472
473#[doc(hidden)]
474impl From<AudioInfo> for glib::Value {
475    fn from(v: AudioInfo) -> glib::Value {
476        skip_assert_initialized!();
477        glib::value::ToValue::to_value(&v)
478    }
479}
480
481#[doc(hidden)]
482impl glib::value::ToValueOptional for AudioInfo {
483    fn to_value_optional(s: Option<&Self>) -> glib::Value {
484        skip_assert_initialized!();
485        let mut value = glib::Value::for_value_type::<Self>();
486        unsafe {
487            glib::gobject_ffi::g_value_set_boxed(
488                value.to_glib_none_mut().0,
489                s.to_glib_none().0 as *mut _,
490            )
491        }
492        value
493    }
494}
495
496#[doc(hidden)]
497impl glib::translate::Uninitialized for AudioInfo {
498    #[inline]
499    unsafe fn uninitialized() -> Self {
500        mem::zeroed()
501    }
502}
503
504#[doc(hidden)]
505impl glib::translate::GlibPtrDefault for AudioInfo {
506    type GlibType = *mut ffi::GstAudioInfo;
507}
508
509#[doc(hidden)]
510impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstAudioInfo> for AudioInfo {
511    type Storage = PhantomData<&'a Self>;
512
513    #[inline]
514    fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstAudioInfo, Self> {
515        glib::translate::Stash(&self.0, PhantomData)
516    }
517
518    fn to_glib_full(&self) -> *const ffi::GstAudioInfo {
519        unimplemented!()
520    }
521}
522
523#[doc(hidden)]
524impl glib::translate::FromGlibPtrNone<*const ffi::GstAudioInfo> for AudioInfo {
525    #[inline]
526    unsafe fn from_glib_none(ptr: *const ffi::GstAudioInfo) -> Self {
527        Self(ptr::read(ptr))
528    }
529}
530
531#[doc(hidden)]
532impl glib::translate::FromGlibPtrNone<*mut ffi::GstAudioInfo> for AudioInfo {
533    #[inline]
534    unsafe fn from_glib_none(ptr: *mut ffi::GstAudioInfo) -> Self {
535        Self(ptr::read(ptr))
536    }
537}
538
539#[doc(hidden)]
540impl glib::translate::FromGlibPtrFull<*mut ffi::GstAudioInfo> for AudioInfo {
541    #[inline]
542    unsafe fn from_glib_full(ptr: *mut ffi::GstAudioInfo) -> Self {
543        let info = from_glib_none(ptr);
544        glib::ffi::g_free(ptr as *mut _);
545        info
546    }
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552
553    #[test]
554    fn test_new() {
555        gst::init().unwrap();
556
557        let info = AudioInfo::builder(crate::AudioFormat::S16le, 48000, 2)
558            .build()
559            .unwrap();
560        assert_eq!(info.format(), crate::AudioFormat::S16le);
561        assert_eq!(info.rate(), 48000);
562        assert_eq!(info.channels(), 2);
563        assert_eq!(
564            &info.positions().unwrap(),
565            &[
566                crate::AudioChannelPosition::FrontLeft,
567                crate::AudioChannelPosition::FrontRight,
568            ]
569        );
570
571        let positions = [
572            crate::AudioChannelPosition::RearLeft,
573            crate::AudioChannelPosition::RearRight,
574        ];
575        let info = AudioInfo::builder(crate::AudioFormat::S16le, 48000, 2)
576            .positions(&positions)
577            .build()
578            .unwrap();
579        assert_eq!(info.format(), crate::AudioFormat::S16le);
580        assert_eq!(info.rate(), 48000);
581        assert_eq!(info.channels(), 2);
582        assert_eq!(
583            &info.positions().unwrap(),
584            &[
585                crate::AudioChannelPosition::RearLeft,
586                crate::AudioChannelPosition::RearRight,
587            ]
588        );
589    }
590
591    #[test]
592    fn test_from_to_caps() {
593        gst::init().unwrap();
594
595        let caps = crate::AudioCapsBuilder::new_interleaved()
596            .format(crate::AudioFormat::S16le)
597            .rate(48000)
598            .channels(2)
599            .fallback_channel_mask()
600            .build();
601        let info = AudioInfo::from_caps(&caps).unwrap();
602        assert_eq!(info.format(), crate::AudioFormat::S16le);
603        assert_eq!(info.rate(), 48000);
604        assert_eq!(info.channels(), 2);
605        assert_eq!(
606            &info.positions().unwrap(),
607            &[
608                crate::AudioChannelPosition::FrontLeft,
609                crate::AudioChannelPosition::FrontRight,
610            ]
611        );
612
613        let caps2 = info.to_caps().unwrap();
614        assert_eq!(caps, caps2);
615
616        let info2 = AudioInfo::from_caps(&caps2).unwrap();
617        assert!(info == info2);
618    }
619}