Skip to main content

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!(
147                                "Overlapping audio channel offsets: offset {} for channel {} and offset {} for channel {} with a plane size of {}",
148                                offset,
149                                i,
150                                other_offset,
151                                j,
152                                plane_size
153                            ));
154                        }
155                    }
156                }
157
158                max_offset.unwrap()
159            };
160
161            if max_offset + plane_size > buffer.size() {
162                return Err(glib::bool_error!(
163                    "Audio channel offsets out of bounds: max offset {} with plane size {} and buffer size {}",
164                    max_offset,
165                    plane_size,
166                    buffer.size()
167                ));
168            }
169        }
170
171        unsafe {
172            let meta = ffi::gst_buffer_add_audio_meta(
173                buffer.as_mut_ptr(),
174                info.to_glib_none().0,
175                samples,
176                if offsets.is_empty() {
177                    ptr::null_mut()
178                } else {
179                    offsets.as_ptr() as *mut _
180                },
181            );
182
183            if meta.is_null() {
184                return Err(glib::bool_error!("Failed to add audio meta"));
185            }
186
187            Ok(Self::from_mut_ptr(buffer, meta))
188        }
189    }
190
191    #[doc(alias = "get_info")]
192    #[inline]
193    pub fn info(&self) -> &crate::AudioInfo {
194        unsafe { &*(&self.0.info as *const ffi::GstAudioInfo as *const crate::AudioInfo) }
195    }
196
197    #[doc(alias = "get_samples")]
198    #[inline]
199    pub fn samples(&self) -> usize {
200        self.0.samples
201    }
202
203    #[doc(alias = "get_offsets")]
204    #[inline]
205    pub fn offsets(&self) -> &[usize] {
206        if self.0.offsets.is_null() || self.0.info.channels < 1 {
207            return &[];
208        }
209
210        unsafe { slice::from_raw_parts(self.0.offsets, self.0.info.channels as usize) }
211    }
212}
213
214#[cfg(feature = "v1_16")]
215#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
216unsafe impl MetaAPI for AudioMeta {
217    type GstType = ffi::GstAudioMeta;
218
219    #[doc(alias = "gst_audio_meta_api_get_type")]
220    #[inline]
221    fn meta_api() -> glib::Type {
222        unsafe { from_glib(ffi::gst_audio_meta_api_get_type()) }
223    }
224}
225
226#[cfg(feature = "v1_16")]
227#[cfg_attr(docsrs, doc(cfg(feature = "v1_16")))]
228impl fmt::Debug for AudioMeta {
229    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230        f.debug_struct("AudioMeta")
231            .field("info", &self.info())
232            .field("samples", &self.samples())
233            .field("offsets", &self.offsets())
234            .finish()
235    }
236}
237
238#[cfg(feature = "v1_20")]
239#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
240#[repr(transparent)]
241#[doc(alias = "GstAudioLevelMeta")]
242pub struct AudioLevelMeta(ffi::GstAudioLevelMeta);
243
244#[cfg(feature = "v1_20")]
245#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
246unsafe impl Send for AudioLevelMeta {}
247#[cfg(feature = "v1_20")]
248#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
249unsafe impl Sync for AudioLevelMeta {}
250
251#[cfg(feature = "v1_20")]
252#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
253impl AudioLevelMeta {
254    #[doc(alias = "gst_buffer_add_audio_level_meta")]
255    pub fn add(
256        buffer: &mut gst::BufferRef,
257        level: u8,
258        voice_activity: bool,
259    ) -> gst::MetaRefMut<'_, Self, gst::meta::Standalone> {
260        skip_assert_initialized!();
261        unsafe {
262            let meta = ffi::gst_buffer_add_audio_level_meta(
263                buffer.as_mut_ptr(),
264                level,
265                voice_activity.into_glib(),
266            );
267
268            Self::from_mut_ptr(buffer, meta)
269        }
270    }
271
272    #[doc(alias = "get_level")]
273    #[inline]
274    pub fn level(&self) -> u8 {
275        self.0.level
276    }
277
278    #[doc(alias = "get_voice_activity")]
279    #[inline]
280    pub fn voice_activity(&self) -> bool {
281        unsafe { from_glib(self.0.voice_activity) }
282    }
283}
284
285#[cfg(feature = "v1_20")]
286#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
287unsafe impl MetaAPI for AudioLevelMeta {
288    type GstType = ffi::GstAudioLevelMeta;
289
290    #[doc(alias = "gst_audio_level_meta_api_get_type")]
291    #[inline]
292    fn meta_api() -> glib::Type {
293        unsafe { from_glib(ffi::gst_audio_level_meta_api_get_type()) }
294    }
295}
296
297#[cfg(feature = "v1_20")]
298#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
299impl fmt::Debug for AudioLevelMeta {
300    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
301        f.debug_struct("AudioLevelMeta")
302            .field("level", &self.level())
303            .field("voice_activity", &self.voice_activity())
304            .finish()
305    }
306}
307
308pub mod tags {
309    gst::impl_meta_tag!(Audio, crate::ffi::GST_META_TAG_AUDIO_STR);
310    gst::impl_meta_tag!(Channels, crate::ffi::GST_META_TAG_AUDIO_CHANNELS_STR);
311    gst::impl_meta_tag!(Rate, crate::ffi::GST_META_TAG_AUDIO_RATE_STR);
312    #[cfg(feature = "v1_24")]
313    gst::impl_meta_tag!(
314        DSDPlaneOffsets,
315        crate::ffi::GST_META_TAG_DSD_PLANE_OFFSETS_STR
316    );
317}
318
319#[cfg(test)]
320mod tests {
321    use super::*;
322
323    #[test]
324    fn test_add_get_audio_clipping_meta() {
325        use gst::prelude::*;
326
327        gst::init().unwrap();
328
329        let mut buffer = gst::Buffer::with_size(1024).unwrap();
330
331        let start = 1.default_format();
332        let stop = 2.default_format();
333
334        {
335            let cmeta = AudioClippingMeta::add(buffer.get_mut().unwrap(), start, stop);
336            assert_eq!(cmeta.start().try_into(), Ok(Some(start)));
337            assert_eq!(cmeta.end().try_into(), Ok(Some(stop)));
338        }
339
340        {
341            let cmeta = buffer.meta::<AudioClippingMeta>().unwrap();
342            assert_eq!(cmeta.start().try_into(), Ok(Some(start)));
343            assert_eq!(cmeta.end().try_into(), Ok(Some(stop)));
344
345            assert!(cmeta.has_tag::<tags::Audio>());
346        }
347    }
348
349    #[cfg(feature = "v1_20")]
350    #[test]
351    fn test_add_get_audio_level_meta() {
352        gst::init().unwrap();
353
354        let mut buffer = gst::Buffer::with_size(1024).unwrap();
355
356        {
357            let cmeta = AudioLevelMeta::add(buffer.get_mut().unwrap(), 10, true);
358            assert_eq!(cmeta.level(), 10);
359            assert!(cmeta.voice_activity());
360        }
361
362        {
363            let cmeta = buffer.meta::<AudioLevelMeta>().unwrap();
364            assert_eq!(cmeta.level(), 10);
365            assert!(cmeta.voice_activity());
366        }
367    }
368}