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!(
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}