1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::fmt;
4#[cfg(feature = "v1_16")]
5#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
6use std::ptr;
7#[cfg(feature = "v1_16")]
8#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
9use std::slice;
10
11use glib::translate::{from_glib, IntoGlib};
12#[cfg(feature = "v1_16")]
13#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
14use glib::translate::{from_glib_none, ToGlibPtr};
15use gst::prelude::*;
16
17#[repr(transparent)]
18#[doc(alias = "GstAudioClippingMeta")]
19pub struct AudioClippingMeta(ffi::GstAudioClippingMeta);
20
21unsafe impl Send for AudioClippingMeta {}
22unsafe impl Sync for AudioClippingMeta {}
23
24impl 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
58unsafe 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
68impl 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")]
81pub struct AudioMeta(ffi::GstAudioMeta);
82
83#[cfg(feature = "v1_16")]
84#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
85unsafe impl Send for AudioMeta {}
86#[cfg(feature = "v1_16")]
87#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
88unsafe impl Sync for AudioMeta {}
89
90#[cfg(feature = "v1_16")]
91#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
92impl 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")))]
205unsafe 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")))]
217impl 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")]
231pub struct AudioLevelMeta(ffi::GstAudioLevelMeta);
232
233#[cfg(feature = "v1_20")]
234#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
235unsafe impl Send for AudioLevelMeta {}
236#[cfg(feature = "v1_20")]
237#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
238unsafe impl Sync for AudioLevelMeta {}
239
240#[cfg(feature = "v1_20")]
241#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
242impl 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")))]
276unsafe 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")))]
288impl 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)]
298mod 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