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