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)]
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}