1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{cmp::Ordering, fmt, marker::PhantomData, str}; |
4 | |
5 | use crate::ffi; |
6 | use glib::{ |
7 | prelude::*, |
8 | translate::{from_glib, from_glib_none, FromGlib, IntoGlib, ToGlibPtr, ToGlibPtrMut}, |
9 | }; |
10 | |
11 | #[derive (PartialEq, Eq, Copy, Clone, Debug, Hash)] |
12 | pub enum AudioEndianness { |
13 | Unknown, |
14 | LittleEndian = 1234, |
15 | BigEndian = 4321, |
16 | } |
17 | |
18 | impl FromGlib<i32> for AudioEndianness { |
19 | #[allow (unused_unsafe)] |
20 | #[inline ] |
21 | unsafe fn from_glib(value: i32) -> Self { |
22 | skip_assert_initialized!(); |
23 | |
24 | match value { |
25 | 1234 => Self::LittleEndian, |
26 | 4321 => Self::BigEndian, |
27 | _ => Self::Unknown, |
28 | } |
29 | } |
30 | } |
31 | |
32 | impl IntoGlib for AudioEndianness { |
33 | type GlibType = i32; |
34 | |
35 | #[inline ] |
36 | fn into_glib(self) -> i32 { |
37 | match self { |
38 | Self::LittleEndian => 1234, |
39 | Self::BigEndian => 4321, |
40 | _ => 0, |
41 | } |
42 | } |
43 | } |
44 | |
45 | #[doc (alias = "GstAudioFormatInfo" )] |
46 | #[derive (Copy, Clone)] |
47 | pub struct AudioFormatInfo(&'static ffi::GstAudioFormatInfo); |
48 | |
49 | impl AudioFormatInfo { |
50 | #[inline ] |
51 | pub fn from_format(format: crate::AudioFormat) -> Self { |
52 | assert_initialized_main_thread!(); |
53 | |
54 | unsafe { |
55 | let info = ffi::gst_audio_format_get_info(format.into_glib()); |
56 | debug_assert!(!info.is_null()); |
57 | |
58 | Self(&*info) |
59 | } |
60 | } |
61 | |
62 | #[inline ] |
63 | pub fn format(&self) -> crate::AudioFormat { |
64 | unsafe { from_glib(self.0.format) } |
65 | } |
66 | |
67 | #[inline ] |
68 | pub fn name<'a>(&self) -> &'a glib::GStr { |
69 | unsafe { glib::GStr::from_ptr(self.0.name) } |
70 | } |
71 | |
72 | #[inline ] |
73 | pub fn description<'a>(&self) -> &'a glib::GStr { |
74 | unsafe { glib::GStr::from_ptr(self.0.description) } |
75 | } |
76 | |
77 | #[inline ] |
78 | pub fn flags(&self) -> crate::AudioFormatFlags { |
79 | unsafe { from_glib(self.0.flags) } |
80 | } |
81 | |
82 | #[inline ] |
83 | pub fn endianness(&self) -> AudioEndianness { |
84 | unsafe { from_glib(self.0.endianness) } |
85 | } |
86 | |
87 | #[inline ] |
88 | pub fn width(&self) -> u32 { |
89 | self.0.width as u32 |
90 | } |
91 | |
92 | #[inline ] |
93 | pub fn depth(&self) -> u32 { |
94 | self.0.depth as u32 |
95 | } |
96 | |
97 | #[inline ] |
98 | pub fn unpack_format(&self) -> crate::AudioFormat { |
99 | unsafe { from_glib(self.0.unpack_format) } |
100 | } |
101 | |
102 | #[inline ] |
103 | pub fn silence<'a>(&self) -> &'a [u8] { |
104 | &self.0.silence |
105 | } |
106 | |
107 | pub fn unpack(&self, flags: crate::AudioPackFlags, dest: &mut [u8], src: &[u8]) { |
108 | let unpack_format = Self::from_format(self.unpack_format()); |
109 | let unpack_width = unpack_format.width() as usize; |
110 | |
111 | if unpack_width == 0 || self.0.unpack_func.is_none() { |
112 | panic!("No unpack format for {self:?}" ); |
113 | } |
114 | |
115 | let self_width = self.width() as usize; |
116 | if self_width == 0 { |
117 | panic!("No width for {self:?}" ); |
118 | } |
119 | |
120 | if src.len() % (self_width / 8) != 0 { |
121 | panic!("Incomplete number of samples in src" ); |
122 | } |
123 | |
124 | let nsamples = src.len() / (self_width / 8); |
125 | |
126 | if dest.len() != nsamples * (unpack_width / 8) { |
127 | panic!("Invalid dest length" ); |
128 | } |
129 | |
130 | unsafe { |
131 | (self.0.unpack_func.as_ref().unwrap())( |
132 | self.0, |
133 | flags.into_glib(), |
134 | dest.as_mut_ptr() as *mut _, |
135 | src.as_ptr() as *const _, |
136 | nsamples as i32, |
137 | ); |
138 | } |
139 | } |
140 | |
141 | pub fn pack(&self, flags: crate::AudioPackFlags, dest: &mut [u8], src: &[u8]) { |
142 | let unpack_format = Self::from_format(self.unpack_format()); |
143 | let unpack_width = unpack_format.width() as usize; |
144 | |
145 | if unpack_width == 0 || self.0.pack_func.is_none() { |
146 | panic!("No unpack format for {self:?}" ); |
147 | } |
148 | |
149 | let self_width = self.width() as usize; |
150 | if self_width == 0 { |
151 | panic!("No width for {self:?}" ); |
152 | } |
153 | |
154 | if src.len() % (unpack_width / 8) != 0 { |
155 | panic!("Incomplete number of samples in src" ); |
156 | } |
157 | |
158 | let nsamples = src.len() / (unpack_width / 8); |
159 | |
160 | if dest.len() != nsamples * (self_width / 8) { |
161 | panic!("Invalid dest length" ); |
162 | } |
163 | |
164 | unsafe { |
165 | (self.0.pack_func.as_ref().unwrap())( |
166 | self.0, |
167 | flags.into_glib(), |
168 | src.as_ptr() as *const _, |
169 | dest.as_mut_ptr() as *mut _, |
170 | nsamples as i32, |
171 | ); |
172 | } |
173 | } |
174 | |
175 | #[doc (alias = "gst_audio_format_info_fill_silence" )] |
176 | #[doc (alias = "gst_audio_format_fill_silence" )] |
177 | pub fn fill_silence(&self, dest: &mut [u8]) { |
178 | let self_width = self.width() as usize; |
179 | |
180 | if self_width == 0 { |
181 | panic!("Filling with silence unsupported" ); |
182 | } |
183 | |
184 | if dest.len() % (self_width / 8) != 0 { |
185 | panic!("Incomplete number of samples in dest" ); |
186 | } |
187 | |
188 | unsafe { |
189 | cfg_if::cfg_if! { |
190 | if #[cfg(feature = "v1_20" )] { |
191 | ffi::gst_audio_format_info_fill_silence(self.0, dest.as_mut_ptr() as *mut _, dest.len()) |
192 | } else { |
193 | ffi::gst_audio_format_fill_silence(self.0, dest.as_mut_ptr() as *mut _, dest.len()) |
194 | } |
195 | } |
196 | } |
197 | } |
198 | |
199 | #[inline ] |
200 | pub fn is_float(&self) -> bool { |
201 | self.flags().contains(crate::AudioFormatFlags::FLOAT) |
202 | } |
203 | |
204 | #[inline ] |
205 | pub fn is_integer(&self) -> bool { |
206 | self.flags().contains(crate::AudioFormatFlags::INTEGER) |
207 | } |
208 | |
209 | #[inline ] |
210 | pub fn is_signed(&self) -> bool { |
211 | self.flags().contains(crate::AudioFormatFlags::SIGNED) |
212 | } |
213 | |
214 | #[inline ] |
215 | pub fn is_little_endian(&self) -> bool { |
216 | self.endianness() == AudioEndianness::LittleEndian |
217 | } |
218 | |
219 | #[inline ] |
220 | pub fn is_big_endian(&self) -> bool { |
221 | self.endianness() == AudioEndianness::BigEndian |
222 | } |
223 | } |
224 | |
225 | unsafe impl Sync for AudioFormatInfo {} |
226 | unsafe impl Send for AudioFormatInfo {} |
227 | |
228 | impl PartialEq for AudioFormatInfo { |
229 | #[inline ] |
230 | fn eq(&self, other: &Self) -> bool { |
231 | self.format() == other.format() |
232 | } |
233 | } |
234 | |
235 | impl Eq for AudioFormatInfo {} |
236 | |
237 | impl PartialOrd for AudioFormatInfo { |
238 | #[inline ] |
239 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { |
240 | Some(self.cmp(other)) |
241 | } |
242 | } |
243 | |
244 | impl Ord for AudioFormatInfo { |
245 | // See GST_AUDIO_FORMATS_ALL for the sorting algorithm |
246 | fn cmp(&self, other: &Self) -> Ordering { |
247 | self.depth() |
248 | .cmp(&other.depth()) |
249 | .then_with(|| self.width().cmp(&other.width())) |
250 | .then_with(|| { |
251 | match ( |
252 | self.flags().contains(crate::AudioFormatFlags::FLOAT), |
253 | other.flags().contains(crate::AudioFormatFlags::FLOAT), |
254 | ) { |
255 | (true, false) => Ordering::Greater, |
256 | (false, true) => Ordering::Less, |
257 | _ => Ordering::Equal, |
258 | } |
259 | }) |
260 | .then_with(|| { |
261 | match ( |
262 | self.flags().contains(crate::AudioFormatFlags::SIGNED), |
263 | other.flags().contains(crate::AudioFormatFlags::SIGNED), |
264 | ) { |
265 | (true, false) => Ordering::Greater, |
266 | (false, true) => Ordering::Less, |
267 | _ => Ordering::Equal, |
268 | } |
269 | }) |
270 | .then_with(|| match (self.endianness(), other.endianness()) { |
271 | (crate::AudioEndianness::LittleEndian, crate::AudioEndianness::BigEndian) => { |
272 | #[cfg (target_endian = "little" )] |
273 | { |
274 | Ordering::Greater |
275 | } |
276 | #[cfg (target_endian = "big" )] |
277 | { |
278 | Ordering::Less |
279 | } |
280 | } |
281 | (crate::AudioEndianness::BigEndian, crate::AudioEndianness::LittleEndian) => { |
282 | #[cfg (target_endian = "little" )] |
283 | { |
284 | Ordering::Less |
285 | } |
286 | #[cfg (target_endian = "big" )] |
287 | { |
288 | Ordering::Greater |
289 | } |
290 | } |
291 | _ => Ordering::Equal, |
292 | }) |
293 | } |
294 | } |
295 | |
296 | impl fmt::Debug for AudioFormatInfo { |
297 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
298 | f&mut DebugStruct<'_, '_>.debug_struct("AudioFormatInfo" ) |
299 | .field("format" , &self.format()) |
300 | .field("name" , &self.name()) |
301 | .field("description" , &self.description()) |
302 | .field("flags" , &self.flags()) |
303 | .field("endianness" , &self.endianness()) |
304 | .field("width" , &self.width()) |
305 | .field("depth" , &self.depth()) |
306 | .field(name:"silence" , &self.silence()) |
307 | .finish() |
308 | } |
309 | } |
310 | |
311 | impl fmt::Display for AudioFormatInfo { |
312 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
313 | f.write_str(self.name()) |
314 | } |
315 | } |
316 | |
317 | impl str::FromStr for crate::AudioFormatInfo { |
318 | type Err = glib::BoolError; |
319 | |
320 | fn from_str(s: &str) -> Result<Self, Self::Err> { |
321 | skip_assert_initialized!(); |
322 | let format: AudioFormat = s.parse()?; |
323 | Ok(Self::from_format(format)) |
324 | } |
325 | } |
326 | |
327 | impl From<crate::AudioFormat> for AudioFormatInfo { |
328 | #[inline ] |
329 | fn from(f: crate::AudioFormat) -> Self { |
330 | skip_assert_initialized!(); |
331 | Self::from_format(f) |
332 | } |
333 | } |
334 | |
335 | impl glib::types::StaticType for AudioFormatInfo { |
336 | #[inline ] |
337 | fn static_type() -> glib::types::Type { |
338 | unsafe { glib::translate::from_glib(val:ffi::gst_audio_format_info_get_type()) } |
339 | } |
340 | } |
341 | |
342 | impl glib::value::ValueType for AudioFormatInfo { |
343 | type Type = Self; |
344 | } |
345 | |
346 | #[doc (hidden)] |
347 | unsafe impl<'a> glib::value::FromValue<'a> for AudioFormatInfo { |
348 | type Checker = glib::value::GenericValueTypeOrNoneChecker<Self>; |
349 | |
350 | unsafe fn from_value(value: &'a glib::Value) -> Self { |
351 | skip_assert_initialized!(); |
352 | from_glib_none(ptr:glib::gobject_ffi::g_value_get_boxed(value.to_glib_none().0) |
353 | as *mut ffi::GstAudioFormatInfo) |
354 | } |
355 | } |
356 | |
357 | #[doc (hidden)] |
358 | impl glib::value::ToValue for AudioFormatInfo { |
359 | fn to_value(&self) -> glib::Value { |
360 | let mut value: Value = glib::Value::for_value_type::<Self>(); |
361 | unsafe { |
362 | glib::gobject_ffi::g_value_set_boxed( |
363 | value.to_glib_none_mut().0, |
364 | self.to_glib_none().0 as *mut _, |
365 | ) |
366 | } |
367 | value |
368 | } |
369 | |
370 | fn value_type(&self) -> glib::Type { |
371 | Self::static_type() |
372 | } |
373 | } |
374 | |
375 | #[doc (hidden)] |
376 | impl glib::value::ToValueOptional for AudioFormatInfo { |
377 | fn to_value_optional(s: Option<&Self>) -> glib::Value { |
378 | skip_assert_initialized!(); |
379 | let mut value: Value = glib::Value::for_value_type::<Self>(); |
380 | unsafe { |
381 | glib::gobject_ffi::g_value_set_boxed( |
382 | value.to_glib_none_mut().0, |
383 | v_boxed:s.to_glib_none().0 as *mut _, |
384 | ) |
385 | } |
386 | value |
387 | } |
388 | } |
389 | |
390 | #[doc (hidden)] |
391 | impl From<AudioFormatInfo> for glib::Value { |
392 | fn from(v: AudioFormatInfo) -> glib::Value { |
393 | skip_assert_initialized!(); |
394 | glib::value::ToValue::to_value(&v) |
395 | } |
396 | } |
397 | |
398 | #[doc (hidden)] |
399 | impl glib::translate::GlibPtrDefault for AudioFormatInfo { |
400 | type GlibType = *mut ffi::GstAudioFormatInfo; |
401 | } |
402 | |
403 | #[doc (hidden)] |
404 | unsafe impl glib::translate::TransparentPtrType for AudioFormatInfo {} |
405 | |
406 | #[doc (hidden)] |
407 | impl<'a> glib::translate::ToGlibPtr<'a, *const ffi::GstAudioFormatInfo> for AudioFormatInfo { |
408 | type Storage = PhantomData<&'a Self>; |
409 | |
410 | #[inline ] |
411 | fn to_glib_none(&'a self) -> glib::translate::Stash<'a, *const ffi::GstAudioFormatInfo, Self> { |
412 | glib::translate::Stash(self.0, PhantomData) |
413 | } |
414 | |
415 | fn to_glib_full(&self) -> *const ffi::GstAudioFormatInfo { |
416 | unimplemented!() |
417 | } |
418 | } |
419 | |
420 | #[doc (hidden)] |
421 | impl glib::translate::FromGlibPtrNone<*mut ffi::GstAudioFormatInfo> for AudioFormatInfo { |
422 | #[inline ] |
423 | unsafe fn from_glib_none(ptr: *mut ffi::GstAudioFormatInfo) -> Self { |
424 | Self(&*ptr) |
425 | } |
426 | } |
427 | |
428 | #[doc (hidden)] |
429 | impl glib::translate::FromGlibPtrNone<*const ffi::GstAudioFormatInfo> for AudioFormatInfo { |
430 | #[inline ] |
431 | unsafe fn from_glib_none(ptr: *const ffi::GstAudioFormatInfo) -> Self { |
432 | Self(&*ptr) |
433 | } |
434 | } |
435 | |
436 | #[cfg (test)] |
437 | mod tests { |
438 | use super::*; |
439 | |
440 | #[test ] |
441 | fn test_get() { |
442 | gst::init().unwrap(); |
443 | |
444 | let info = AudioFormatInfo::from_format(crate::AudioFormat::S16le); |
445 | assert_eq!(info.name(), "S16LE" ); |
446 | |
447 | let other_info = "S16LE" .parse().unwrap(); |
448 | assert_eq!(info, other_info); |
449 | } |
450 | |
451 | #[test ] |
452 | fn pack_unpack() { |
453 | gst::init().unwrap(); |
454 | |
455 | let info = AudioFormatInfo::from_format(crate::AudioFormat::S16le); |
456 | let unpack_info = AudioFormatInfo::from_format(info.unpack_format()); |
457 | |
458 | assert!(unpack_info.width() > 0); |
459 | |
460 | let input = [0, 0, 255, 255, 128, 128, 64, 64]; |
461 | let mut unpacked = [0; 16]; |
462 | let mut output = [0; 8]; |
463 | |
464 | info.unpack(crate::AudioPackFlags::empty(), &mut unpacked, &input); |
465 | info.pack(crate::AudioPackFlags::empty(), &mut output, &unpacked); |
466 | |
467 | assert_eq!(input, output); |
468 | } |
469 | } |
470 | |