1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::fmt; |
4 | #[cfg (feature = "v1_16" )] |
5 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
6 | use std::ptr; |
7 | #[cfg (feature = "v1_16" )] |
8 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
9 | use std::slice; |
10 | |
11 | use glib::translate::{from_glib, IntoGlib}; |
12 | #[cfg (feature = "v1_16" )] |
13 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
14 | use glib::translate::{from_glib_none, ToGlibPtr}; |
15 | use gst::prelude::*; |
16 | |
17 | #[repr (transparent)] |
18 | #[doc (alias = "GstAudioClippingMeta" )] |
19 | pub struct AudioClippingMeta(ffi::GstAudioClippingMeta); |
20 | |
21 | unsafe impl Send for AudioClippingMeta {} |
22 | unsafe impl Sync for AudioClippingMeta {} |
23 | |
24 | impl AudioClippingMeta { |
25 | #[doc (alias = "gst_buffer_add_audio_clipping_meta" )] |
26 | pub fn add<V: gst::format::FormattedValue>( |
27 | buffer: &mut gst::BufferRef, |
28 | start: V, |
29 | end: V, |
30 | ) -> gst::MetaRefMut<Self, gst::meta::Standalone> { |
31 | skip_assert_initialized!(); |
32 | assert_eq!(start.format(), end.format()); |
33 | unsafe { |
34 | let meta = ffi::gst_buffer_add_audio_clipping_meta( |
35 | buffer.as_mut_ptr(), |
36 | start.format().into_glib(), |
37 | start.into_raw_value() as u64, |
38 | end.into_raw_value() as u64, |
39 | ); |
40 | |
41 | Self::from_mut_ptr(buffer, meta) |
42 | } |
43 | } |
44 | |
45 | #[doc (alias = "get_start" )] |
46 | #[inline ] |
47 | pub fn start(&self) -> gst::GenericFormattedValue { |
48 | unsafe { gst::GenericFormattedValue::new(from_glib(self.0.format), self.0.start as i64) } |
49 | } |
50 | |
51 | #[doc (alias = "get_end" )] |
52 | #[inline ] |
53 | pub fn end(&self) -> gst::GenericFormattedValue { |
54 | unsafe { gst::GenericFormattedValue::new(from_glib(self.0.format), self.0.end as i64) } |
55 | } |
56 | } |
57 | |
58 | unsafe impl MetaAPI for AudioClippingMeta { |
59 | type GstType = ffi::GstAudioClippingMeta; |
60 | |
61 | #[doc (alias = "gst_audio_clipping_meta_api_get_type" )] |
62 | #[inline ] |
63 | fn meta_api() -> glib::Type { |
64 | unsafe { from_glib(val:ffi::gst_audio_clipping_meta_api_get_type()) } |
65 | } |
66 | } |
67 | |
68 | impl fmt::Debug for AudioClippingMeta { |
69 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
70 | f&mut DebugStruct<'_, '_>.debug_struct("AudioClippingMeta" ) |
71 | .field("start" , &self.start()) |
72 | .field(name:"end" , &self.end()) |
73 | .finish() |
74 | } |
75 | } |
76 | |
77 | #[cfg (feature = "v1_16" )] |
78 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
79 | #[repr (transparent)] |
80 | #[doc (alias = "GstAudioMeta" )] |
81 | pub struct AudioMeta(ffi::GstAudioMeta); |
82 | |
83 | #[cfg (feature = "v1_16" )] |
84 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
85 | unsafe impl Send for AudioMeta {} |
86 | #[cfg (feature = "v1_16" )] |
87 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
88 | unsafe impl Sync for AudioMeta {} |
89 | |
90 | #[cfg (feature = "v1_16" )] |
91 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
92 | impl AudioMeta { |
93 | #[doc (alias = "gst_buffer_add_audio_meta" )] |
94 | pub fn add<'a>( |
95 | buffer: &'a mut gst::BufferRef, |
96 | info: &crate::AudioInfo, |
97 | samples: usize, |
98 | offsets: &[usize], |
99 | ) -> Result<gst::MetaRefMut<'a, Self, gst::meta::Standalone>, glib::BoolError> { |
100 | skip_assert_initialized!(); |
101 | |
102 | if !info.is_valid() { |
103 | return Err(glib::bool_error!("Invalid audio info" )); |
104 | } |
105 | |
106 | if info.rate() == 0 |
107 | || info.channels() == 0 |
108 | || info.format() == crate::AudioFormat::Unknown |
109 | || info.format() == crate::AudioFormat::Encoded |
110 | { |
111 | return Err(glib::bool_error!("Unsupported audio format {:?}" , info)); |
112 | } |
113 | |
114 | if !offsets.is_empty() && info.layout() != crate::AudioLayout::NonInterleaved { |
115 | return Err(glib::bool_error!( |
116 | "Channel offsets only supported for non-interleaved audio" |
117 | )); |
118 | } |
119 | |
120 | if !offsets.is_empty() && offsets.len() != info.channels() as usize { |
121 | return Err(glib::bool_error!( |
122 | "Number of channel offsets different than number of channels ( {} != {})" , |
123 | offsets.len(), |
124 | info.channels() |
125 | )); |
126 | } |
127 | |
128 | if info.layout() == crate::AudioLayout::NonInterleaved { |
129 | let plane_size = samples * (info.width() / 8) as usize; |
130 | let max_offset = if offsets.is_empty() { |
131 | plane_size * (info.channels() - 1) as usize |
132 | } else { |
133 | let mut max_offset = None; |
134 | |
135 | for (i, offset) in offsets.iter().copied().enumerate() { |
136 | if let Some(current_max_offset) = max_offset { |
137 | max_offset = Some(std::cmp::max(current_max_offset, offset)); |
138 | } else { |
139 | max_offset = Some(offset); |
140 | } |
141 | |
142 | for (j, other_offset) in offsets.iter().copied().enumerate() { |
143 | if i != j |
144 | && !(other_offset + plane_size <= offset |
145 | || offset + plane_size <= other_offset) |
146 | { |
147 | return Err(glib::bool_error!("Overlapping audio channel offsets: offset {} for channel {} and offset {} for channel {} with a plane size of {}" , offset, i, other_offset, j, plane_size)); |
148 | } |
149 | } |
150 | } |
151 | |
152 | max_offset.unwrap() |
153 | }; |
154 | |
155 | if max_offset + plane_size > buffer.size() { |
156 | return Err(glib::bool_error!("Audio channel offsets out of bounds: max offset {} with plane size {} and buffer size {}" , max_offset, plane_size, buffer.size())); |
157 | } |
158 | } |
159 | |
160 | unsafe { |
161 | let meta = ffi::gst_buffer_add_audio_meta( |
162 | buffer.as_mut_ptr(), |
163 | info.to_glib_none().0, |
164 | samples, |
165 | if offsets.is_empty() { |
166 | ptr::null_mut() |
167 | } else { |
168 | offsets.as_ptr() as *mut _ |
169 | }, |
170 | ); |
171 | |
172 | if meta.is_null() { |
173 | return Err(glib::bool_error!("Failed to add audio meta" )); |
174 | } |
175 | |
176 | Ok(Self::from_mut_ptr(buffer, meta)) |
177 | } |
178 | } |
179 | |
180 | #[doc (alias = "get_info" )] |
181 | #[inline ] |
182 | pub fn info(&self) -> crate::AudioInfo { |
183 | unsafe { from_glib_none(&self.0.info as *const _ as *mut ffi::GstAudioInfo) } |
184 | } |
185 | |
186 | #[doc (alias = "get_samples" )] |
187 | #[inline ] |
188 | pub fn samples(&self) -> usize { |
189 | self.0.samples |
190 | } |
191 | |
192 | #[doc (alias = "get_offsets" )] |
193 | #[inline ] |
194 | pub fn offsets(&self) -> &[usize] { |
195 | if self.0.offsets.is_null() || self.0.info.channels < 1 { |
196 | return &[]; |
197 | } |
198 | |
199 | unsafe { slice::from_raw_parts(self.0.offsets, self.0.info.channels as usize) } |
200 | } |
201 | } |
202 | |
203 | #[cfg (feature = "v1_16" )] |
204 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
205 | unsafe impl MetaAPI for AudioMeta { |
206 | type GstType = ffi::GstAudioMeta; |
207 | |
208 | #[doc (alias = "gst_audio_meta_api_get_type" )] |
209 | #[inline ] |
210 | fn meta_api() -> glib::Type { |
211 | unsafe { from_glib(ffi::gst_audio_meta_api_get_type()) } |
212 | } |
213 | } |
214 | |
215 | #[cfg (feature = "v1_16" )] |
216 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_16" )))] |
217 | impl fmt::Debug for AudioMeta { |
218 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
219 | f.debug_struct("AudioMeta" ) |
220 | .field("info" , &self.info()) |
221 | .field("samples" , &self.samples()) |
222 | .field("offsets" , &self.offsets()) |
223 | .finish() |
224 | } |
225 | } |
226 | |
227 | #[cfg (feature = "v1_20" )] |
228 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_20" )))] |
229 | #[repr (transparent)] |
230 | #[doc (alias = "GstAudioLevelMeta" )] |
231 | pub struct AudioLevelMeta(ffi::GstAudioLevelMeta); |
232 | |
233 | #[cfg (feature = "v1_20" )] |
234 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_20" )))] |
235 | unsafe impl Send for AudioLevelMeta {} |
236 | #[cfg (feature = "v1_20" )] |
237 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_20" )))] |
238 | unsafe impl Sync for AudioLevelMeta {} |
239 | |
240 | #[cfg (feature = "v1_20" )] |
241 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_20" )))] |
242 | impl AudioLevelMeta { |
243 | #[doc (alias = "gst_buffer_add_audio_level_meta" )] |
244 | pub fn add( |
245 | buffer: &mut gst::BufferRef, |
246 | level: u8, |
247 | voice_activity: bool, |
248 | ) -> gst::MetaRefMut<Self, gst::meta::Standalone> { |
249 | skip_assert_initialized!(); |
250 | unsafe { |
251 | let meta = ffi::gst_buffer_add_audio_level_meta( |
252 | buffer.as_mut_ptr(), |
253 | level, |
254 | voice_activity.into_glib(), |
255 | ); |
256 | |
257 | Self::from_mut_ptr(buffer, meta) |
258 | } |
259 | } |
260 | |
261 | #[doc (alias = "get_level" )] |
262 | #[inline ] |
263 | pub fn level(&self) -> u8 { |
264 | self.0.level |
265 | } |
266 | |
267 | #[doc (alias = "get_voice_activity" )] |
268 | #[inline ] |
269 | pub fn voice_activity(&self) -> bool { |
270 | unsafe { from_glib(self.0.voice_activity) } |
271 | } |
272 | } |
273 | |
274 | #[cfg (feature = "v1_20" )] |
275 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_20" )))] |
276 | unsafe impl MetaAPI for AudioLevelMeta { |
277 | type GstType = ffi::GstAudioLevelMeta; |
278 | |
279 | #[doc (alias = "gst_audio_level_meta_api_get_type" )] |
280 | #[inline ] |
281 | fn meta_api() -> glib::Type { |
282 | unsafe { from_glib(ffi::gst_audio_level_meta_api_get_type()) } |
283 | } |
284 | } |
285 | |
286 | #[cfg (feature = "v1_20" )] |
287 | #[cfg_attr (docsrs, doc(cfg(feature = "v1_20" )))] |
288 | impl fmt::Debug for AudioLevelMeta { |
289 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
290 | f.debug_struct("AudioLevelMeta" ) |
291 | .field("level" , &self.level()) |
292 | .field("voice_activity" , &self.voice_activity()) |
293 | .finish() |
294 | } |
295 | } |
296 | |
297 | #[cfg (test)] |
298 | mod tests { |
299 | use super::*; |
300 | |
301 | #[test ] |
302 | fn test_add_get_audio_clipping_meta() { |
303 | use gst::prelude::*; |
304 | |
305 | gst::init().unwrap(); |
306 | |
307 | let mut buffer = gst::Buffer::with_size(1024).unwrap(); |
308 | |
309 | let start = 1.default_format(); |
310 | let stop = 2.default_format(); |
311 | |
312 | { |
313 | let cmeta = AudioClippingMeta::add(buffer.get_mut().unwrap(), start, stop); |
314 | assert_eq!(cmeta.start().try_into(), Ok(Some(start))); |
315 | assert_eq!(cmeta.end().try_into(), Ok(Some(stop))); |
316 | } |
317 | |
318 | { |
319 | let cmeta = buffer.meta::<AudioClippingMeta>().unwrap(); |
320 | assert_eq!(cmeta.start().try_into(), Ok(Some(start))); |
321 | assert_eq!(cmeta.end().try_into(), Ok(Some(stop))); |
322 | } |
323 | } |
324 | |
325 | #[cfg (feature = "v1_20" )] |
326 | #[test ] |
327 | fn test_add_get_audio_level_meta() { |
328 | gst::init().unwrap(); |
329 | |
330 | let mut buffer = gst::Buffer::with_size(1024).unwrap(); |
331 | |
332 | { |
333 | let cmeta = AudioLevelMeta::add(buffer.get_mut().unwrap(), 10, true); |
334 | assert_eq!(cmeta.level(), 10); |
335 | assert!(cmeta.voice_activity()); |
336 | } |
337 | |
338 | { |
339 | let cmeta = buffer.meta::<AudioLevelMeta>().unwrap(); |
340 | assert_eq!(cmeta.level(), 10); |
341 | assert!(cmeta.voice_activity()); |
342 | } |
343 | } |
344 | } |
345 | |