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