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