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 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 #[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 #[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 #[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}