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