gstreamer_audio/
audio_meta.rs

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