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