1use 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#[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 #[inline]
193 pub fn is_valid(&self) -> bool {
194 !self.0.finfo.is_null() && self.0.channels > 0 && self.0.rate > 0 && self.0.bpf > 0
195 }
196
197 #[doc(alias = "gst_audio_info_from_caps")]
205 pub fn from_caps(caps: &gst::CapsRef) -> Result<Self, glib::error::BoolError> {
206 skip_assert_initialized!();
207
208 unsafe {
209 let mut info = mem::MaybeUninit::uninit();
210 if from_glib(ffi::gst_audio_info_from_caps(
211 info.as_mut_ptr(),
212 caps.as_ptr(),
213 )) {
214 Ok(Self(info.assume_init()))
215 } else {
216 Err(glib::bool_error!("Failed to create AudioInfo from caps"))
217 }
218 }
219 }
220
221 #[doc(alias = "gst_audio_info_to_caps")]
228 pub fn to_caps(&self) -> Result<gst::Caps, glib::error::BoolError> {
229 unsafe {
230 let result = from_glib_full(ffi::gst_audio_info_to_caps(&self.0));
231 match result {
232 Some(c) => Ok(c),
233 None => Err(glib::bool_error!("Failed to create caps from AudioInfo")),
234 }
235 }
236 }
237
238 #[doc(alias = "gst_audio_info_convert")]
256 pub fn convert<U: gst::format::SpecificFormattedValueFullRange>(
257 &self,
258 src_val: impl gst::format::FormattedValue,
259 ) -> Option<U> {
260 assert_initialized_main_thread!();
261 unsafe {
262 let mut dest_val = mem::MaybeUninit::uninit();
263 if from_glib(ffi::gst_audio_info_convert(
264 &self.0,
265 src_val.format().into_glib(),
266 src_val.into_raw_value(),
267 U::default_format().into_glib(),
268 dest_val.as_mut_ptr(),
269 )) {
270 Some(U::from_raw(U::default_format(), dest_val.assume_init()))
271 } else {
272 None
273 }
274 }
275 }
276
277 pub fn convert_generic(
278 &self,
279 src_val: impl gst::format::FormattedValue,
280 dest_fmt: gst::Format,
281 ) -> Option<gst::GenericFormattedValue> {
282 assert_initialized_main_thread!();
283 unsafe {
284 let mut dest_val = mem::MaybeUninit::uninit();
285 if from_glib(ffi::gst_audio_info_convert(
286 &self.0,
287 src_val.format().into_glib(),
288 src_val.into_raw_value(),
289 dest_fmt.into_glib(),
290 dest_val.as_mut_ptr(),
291 )) {
292 Some(gst::GenericFormattedValue::new(
293 dest_fmt,
294 dest_val.assume_init(),
295 ))
296 } else {
297 None
298 }
299 }
300 }
301
302 #[inline]
303 pub fn format(&self) -> crate::AudioFormat {
304 if self.0.finfo.is_null() {
305 return crate::AudioFormat::Unknown;
306 }
307
308 unsafe { from_glib((*self.0.finfo).format) }
309 }
310
311 #[inline]
312 pub fn format_info(&self) -> crate::AudioFormatInfo {
313 crate::AudioFormatInfo::from_format(self.format())
314 }
315
316 #[inline]
317 pub fn layout(&self) -> crate::AudioLayout {
318 unsafe { from_glib(self.0.layout) }
319 }
320
321 #[inline]
322 pub fn flags(&self) -> crate::AudioFlags {
323 unsafe { from_glib(self.0.flags) }
324 }
325
326 #[inline]
327 pub fn rate(&self) -> u32 {
328 self.0.rate as u32
329 }
330
331 #[inline]
332 pub fn channels(&self) -> u32 {
333 self.0.channels as u32
334 }
335
336 #[inline]
337 pub fn bpf(&self) -> u32 {
338 self.0.bpf as u32
339 }
340
341 #[inline]
342 pub fn bps(&self) -> u32 {
343 self.format_info().depth() >> 3
344 }
345
346 #[inline]
347 pub fn depth(&self) -> u32 {
348 self.format_info().depth()
349 }
350
351 #[inline]
352 pub fn width(&self) -> u32 {
353 self.format_info().width()
354 }
355
356 #[inline]
357 pub fn endianness(&self) -> crate::AudioEndianness {
358 self.format_info().endianness()
359 }
360
361 #[inline]
362 pub fn is_big_endian(&self) -> bool {
363 self.format_info().is_big_endian()
364 }
365
366 #[inline]
367 pub fn is_little_endian(&self) -> bool {
368 self.format_info().is_little_endian()
369 }
370
371 #[inline]
372 pub fn is_float(&self) -> bool {
373 self.format_info().is_float()
374 }
375
376 #[inline]
377 pub fn is_integer(&self) -> bool {
378 self.format_info().is_integer()
379 }
380
381 #[inline]
382 pub fn is_signed(&self) -> bool {
383 self.format_info().is_signed()
384 }
385
386 #[inline]
387 pub fn positions(&self) -> Option<&[crate::AudioChannelPosition]> {
388 if self.0.channels > 64 || self.is_unpositioned() {
389 return None;
390 }
391
392 Some(unsafe {
393 slice::from_raw_parts(
394 &self.0.position as *const i32 as *const crate::AudioChannelPosition,
395 self.0.channels as usize,
396 )
397 })
398 }
399
400 #[inline]
401 pub fn is_unpositioned(&self) -> bool {
402 self.flags().contains(crate::AudioFlags::UNPOSITIONED)
403 }
404}
405
406impl PartialEq for AudioInfo {
407 #[doc(alias = "gst_audio_info_is_equal")]
408 #[inline]
409 fn eq(&self, other: &Self) -> bool {
410 unsafe { from_glib(ffi::gst_audio_info_is_equal(&self.0, &other.0)) }
411 }
412}
413
414impl Eq for AudioInfo {}
415
416unsafe impl Send for AudioInfo {}
417unsafe impl Sync for AudioInfo {}
418
419impl glib::types::StaticType for AudioInfo {
420 #[inline]
421 fn static_type() -> glib::types::Type {
422 unsafe { glib::translate::from_glib(ffi::gst_audio_info_get_type()) }
423 }
424}
425
426impl glib::value::ValueType for AudioInfo {
427 type Type = Self;
428}
429
430#[doc(hidden)]
431unsafe impl<'a> glib::value::FromValue<'a> for AudioInfo {
432 type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>;
433
434 unsafe fn from_value(value: &'a glib::Value) -> Self {
435 skip_assert_initialized!();
436 from_glib_none(
437 glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0) as *mut ffi::GstAudioInfo
438 )
439 }
440}
441
442#[doc(hidden)]
443impl glib::value::ToValue for AudioInfo {
444 fn to_value(&self) -> glib::Value {
445 let mut value = glib::Value::for_value_type::<Self>();
446 unsafe {
447 glib::gobject_ffi::g_value_set_boxed(
448 value.to_glib_none_mut().0,
449 self.to_glib_none().0 as *mut _,
450 )
451 }
452 value
453 }
454
455 fn value_type(&self) -> glib::Type {
456 Self::static_type()
457 }
458}
459
460#[doc(hidden)]
461impl From<AudioInfo> for glib::Value {
462 fn from(v: AudioInfo) -> glib::Value {
463 skip_assert_initialized!();
464 glib::value::ToValue::to_value(&v)
465 }
466}
467
468#[doc(hidden)]
469impl glib::value::ToValueOptional for AudioInfo {
470 fn to_value_optional(s: Option<&Self>) -> glib::Value {
471 skip_assert_initialized!();
472 let mut value = glib::Value::for_value_type::<Self>();
473 unsafe {
474 glib::gobject_ffi::g_value_set_boxed(
475 value.to_glib_none_mut().0,
476 s.to_glib_none().0 as *mut _,
477 )
478 }
479 value
480 }
481}
482
483#[doc(hidden)]
484impl glib::translate::Uninitialized for AudioInfo {
485 #[inline]
486 unsafe fn uninitialized() -> Self {
487 mem::zeroed()
488 }
489}
490
491#[doc(hidden)]
492impl glib::translate::GlibPtrDefault for AudioInfo {
493 type GlibType = *mut ffi::GstAudioInfo;
494}
495
496#[doc(hidden)]
497impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstAudioInfo> for AudioInfo {
498 type Storage = PhantomData<&'a Self>;
499
500 #[inline]
501 fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstAudioInfo, Self> {
502 glib::translate::Stash(&self.0, PhantomData)
503 }
504
505 fn to_glib_full(&self) -> *const ffi::GstAudioInfo {
506 unimplemented!()
507 }
508}
509
510#[doc(hidden)]
511impl glib::translate::FromGlibPtrNone<*const ffi::GstAudioInfo> for AudioInfo {
512 #[inline]
513 unsafe fn from_glib_none(ptr: *const ffi::GstAudioInfo) -> Self {
514 Self(ptr::read(ptr))
515 }
516}
517
518#[doc(hidden)]
519impl glib::translate::FromGlibPtrNone<*mut ffi::GstAudioInfo> for AudioInfo {
520 #[inline]
521 unsafe fn from_glib_none(ptr: *mut ffi::GstAudioInfo) -> Self {
522 Self(ptr::read(ptr))
523 }
524}
525
526#[doc(hidden)]
527impl glib::translate::FromGlibPtrFull<*mut ffi::GstAudioInfo> for AudioInfo {
528 #[inline]
529 unsafe fn from_glib_full(ptr: *mut ffi::GstAudioInfo) -> Self {
530 let info = from_glib_none(ptr);
531 glib::ffi::g_free(ptr as *mut _);
532 info
533 }
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539
540 #[test]
541 fn test_new() {
542 gst::init().unwrap();
543
544 let info = AudioInfo::builder(crate::AudioFormat::S16le, 48000, 2)
545 .build()
546 .unwrap();
547 assert_eq!(info.format(), crate::AudioFormat::S16le);
548 assert_eq!(info.rate(), 48000);
549 assert_eq!(info.channels(), 2);
550 assert_eq!(
551 &info.positions().unwrap(),
552 &[
553 crate::AudioChannelPosition::FrontLeft,
554 crate::AudioChannelPosition::FrontRight,
555 ]
556 );
557
558 let positions = [
559 crate::AudioChannelPosition::RearLeft,
560 crate::AudioChannelPosition::RearRight,
561 ];
562 let info = AudioInfo::builder(crate::AudioFormat::S16le, 48000, 2)
563 .positions(&positions)
564 .build()
565 .unwrap();
566 assert_eq!(info.format(), crate::AudioFormat::S16le);
567 assert_eq!(info.rate(), 48000);
568 assert_eq!(info.channels(), 2);
569 assert_eq!(
570 &info.positions().unwrap(),
571 &[
572 crate::AudioChannelPosition::RearLeft,
573 crate::AudioChannelPosition::RearRight,
574 ]
575 );
576 }
577
578 #[test]
579 fn test_from_to_caps() {
580 gst::init().unwrap();
581
582 let caps = crate::AudioCapsBuilder::new_interleaved()
583 .format(crate::AudioFormat::S16le)
584 .rate(48000)
585 .channels(2)
586 .fallback_channel_mask()
587 .build();
588 let info = AudioInfo::from_caps(&caps).unwrap();
589 assert_eq!(info.format(), crate::AudioFormat::S16le);
590 assert_eq!(info.rate(), 48000);
591 assert_eq!(info.channels(), 2);
592 assert_eq!(
593 &info.positions().unwrap(),
594 &[
595 crate::AudioChannelPosition::FrontLeft,
596 crate::AudioChannelPosition::FrontRight,
597 ]
598 );
599
600 let caps2 = info.to_caps().unwrap();
601 assert_eq!(caps, caps2);
602
603 let info2 = AudioInfo::from_caps(&caps2).unwrap();
604 assert!(info == info2);
605 }
606}