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