1use std::ops::{Bound::*, RangeBounds};
2
3use gst::Caps;
4
5use glib::IntoGStr;
6
7use crate::{AudioFormat, AudioLayout};
8
9pub struct AudioCapsBuilder<T> {
10 builder: gst::caps::Builder<T>,
11}
12
13impl AudioCapsBuilder<gst::caps::NoFeature> {
14 pub fn new() -> Self {
26 assert_initialized_main_thread!();
27 let builder = Caps::builder(glib::gstr!("audio/x-raw"));
28 let builder = AudioCapsBuilder { builder };
29 builder
30 .rate_range(..)
31 .channels_range(..)
32 .layout_list([AudioLayout::Interleaved, AudioLayout::NonInterleaved])
33 .format_list(AudioFormat::iter_raw())
34 }
35
36 pub fn new_interleaved() -> Self {
49 AudioCapsBuilder::new().layout(AudioLayout::Interleaved)
50 }
51
52 pub fn for_encoding(encoding: impl IntoGStr) -> Self {
58 assert_initialized_main_thread!();
59 AudioCapsBuilder {
60 builder: Caps::builder(encoding),
61 }
62 }
63
64 pub fn any_features(self) -> AudioCapsBuilder<gst::caps::HasFeatures> {
65 AudioCapsBuilder {
66 builder: self.builder.any_features(),
67 }
68 }
69
70 pub fn features(
71 self,
72 features: impl IntoIterator<Item = impl IntoGStr>,
73 ) -> AudioCapsBuilder<gst::caps::HasFeatures> {
74 AudioCapsBuilder {
75 builder: self.builder.features(features),
76 }
77 }
78}
79
80impl Default for AudioCapsBuilder<gst::caps::NoFeature> {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86impl<T> AudioCapsBuilder<T> {
87 pub fn format(self, format: AudioFormat) -> Self {
88 Self {
89 builder: self.builder.field(glib::gstr!("format"), format.to_str()),
90 }
91 }
92
93 pub fn format_if(self, format: AudioFormat, predicate: bool) -> Self {
94 if predicate {
95 self.format(format)
96 } else {
97 self
98 }
99 }
100
101 pub fn format_if_some(self, format: Option<AudioFormat>) -> Self {
102 if let Some(format) = format {
103 self.format(format)
104 } else {
105 self
106 }
107 }
108
109 pub fn format_list(self, formats: impl IntoIterator<Item = AudioFormat>) -> Self {
110 Self {
111 builder: self.builder.field(
112 glib::gstr!("format"),
113 gst::List::new(formats.into_iter().map(|f| f.to_str())),
114 ),
115 }
116 }
117
118 pub fn format_list_if(
119 self,
120 formats: impl IntoIterator<Item = AudioFormat>,
121 predicate: bool,
122 ) -> Self {
123 if predicate {
124 self.format_list(formats)
125 } else {
126 self
127 }
128 }
129
130 pub fn format_list_if_some(
131 self,
132 formats: Option<impl IntoIterator<Item = AudioFormat>>,
133 ) -> Self {
134 if let Some(formats) = formats {
135 self.format_list(formats)
136 } else {
137 self
138 }
139 }
140
141 pub fn format_list_if_not_empty(self, formats: impl IntoIterator<Item = AudioFormat>) -> Self {
142 let mut formats = formats.into_iter().peekable();
143 if formats.peek().is_some() {
144 self.format_list(formats)
145 } else {
146 self
147 }
148 }
149
150 pub fn rate(self, rate: i32) -> Self {
151 Self {
152 builder: self.builder.field(glib::gstr!("rate"), rate),
153 }
154 }
155
156 pub fn rate_if(self, rate: i32, predicate: bool) -> Self {
157 if predicate {
158 self.rate(rate)
159 } else {
160 self
161 }
162 }
163
164 pub fn rate_if_some(self, rate: Option<i32>) -> Self {
165 if let Some(rate) = rate {
166 self.rate(rate)
167 } else {
168 self
169 }
170 }
171
172 pub fn rate_range(self, rates: impl RangeBounds<i32>) -> Self {
173 let (start, end) = range_bounds_i32_start_end(rates);
174 let gst_rates = gst::IntRange::<i32>::new(start, end);
175 Self {
176 builder: self.builder.field(glib::gstr!("rate"), gst_rates),
177 }
178 }
179
180 pub fn rate_range_if(self, rates: impl RangeBounds<i32>, predicate: bool) -> Self {
181 if predicate {
182 self.rate_range(rates)
183 } else {
184 self
185 }
186 }
187
188 pub fn rate_range_if_some(self, rates: Option<impl RangeBounds<i32>>) -> Self {
189 if let Some(rates) = rates {
190 self.rate_range(rates)
191 } else {
192 self
193 }
194 }
195
196 pub fn rate_list(self, rates: impl IntoIterator<Item = i32>) -> Self {
197 Self {
198 builder: self
199 .builder
200 .field(glib::gstr!("rate"), gst::List::new(rates)),
201 }
202 }
203
204 pub fn rate_list_if(self, rates: impl IntoIterator<Item = i32>, predicate: bool) -> Self {
205 if predicate {
206 self.rate_list(rates)
207 } else {
208 self
209 }
210 }
211
212 pub fn rate_list_if_some(self, rates: Option<impl IntoIterator<Item = i32>>) -> Self {
213 if let Some(rates) = rates {
214 self.rate_list(rates)
215 } else {
216 self
217 }
218 }
219
220 pub fn rate_list_if_not_empty(self, rates: impl IntoIterator<Item = i32>) -> Self {
221 let mut rates = rates.into_iter().peekable();
222 if rates.peek().is_some() {
223 self.rate_list(rates)
224 } else {
225 self
226 }
227 }
228
229 pub fn channels(self, channels: i32) -> Self {
230 Self {
231 builder: self.builder.field(glib::gstr!("channels"), channels),
232 }
233 }
234
235 pub fn channels_if(self, channels: i32, predicate: bool) -> Self {
236 if predicate {
237 self.channels(channels)
238 } else {
239 self
240 }
241 }
242
243 pub fn channels_if_some(self, channels: Option<i32>) -> Self {
244 if let Some(channels) = channels {
245 self.channels(channels)
246 } else {
247 self
248 }
249 }
250
251 pub fn channels_range(self, channels: impl RangeBounds<i32>) -> Self {
252 let (start, end) = range_bounds_i32_start_end(channels);
253 let gst_channels: gst::IntRange<i32> = gst::IntRange::new(start, end);
254 Self {
255 builder: self.builder.field(glib::gstr!("channels"), gst_channels),
256 }
257 }
258
259 pub fn channels_range_if(self, channels: impl RangeBounds<i32>, predicate: bool) -> Self {
260 if predicate {
261 self.channels_range(channels)
262 } else {
263 self
264 }
265 }
266
267 pub fn channels_range_if_some(self, channels: Option<impl RangeBounds<i32>>) -> Self {
268 if let Some(channels) = channels {
269 self.channels_range(channels)
270 } else {
271 self
272 }
273 }
274
275 pub fn channels_list(self, channels: impl IntoIterator<Item = i32>) -> Self {
276 Self {
277 builder: self
278 .builder
279 .field(glib::gstr!("channels"), gst::List::new(channels)),
280 }
281 }
282
283 pub fn channels_list_if(
284 self,
285 channels: impl IntoIterator<Item = i32>,
286 predicate: bool,
287 ) -> Self {
288 if predicate {
289 self.channels_list(channels)
290 } else {
291 self
292 }
293 }
294
295 pub fn channels_list_if_some(self, channels: Option<impl IntoIterator<Item = i32>>) -> Self {
296 if let Some(channels) = channels {
297 self.channels_list(channels)
298 } else {
299 self
300 }
301 }
302
303 pub fn channels_list_if_not_empty(self, channels: impl IntoIterator<Item = i32>) -> Self {
304 let mut channels = channels.into_iter().peekable();
305 if channels.peek().is_some() {
306 self.channels_list(channels)
307 } else {
308 self
309 }
310 }
311
312 pub fn layout(self, layout: AudioLayout) -> Self {
313 Self {
314 builder: self
315 .builder
316 .field(glib::gstr!("layout"), layout_str(layout)),
317 }
318 }
319
320 pub fn layout_if(self, layout: AudioLayout, predicate: bool) -> Self {
321 if predicate {
322 self.layout(layout)
323 } else {
324 self
325 }
326 }
327
328 pub fn layout_if_some(self, layout: Option<AudioLayout>) -> Self {
329 if let Some(layout) = layout {
330 self.layout(layout)
331 } else {
332 self
333 }
334 }
335
336 pub fn layout_list(self, layouts: impl IntoIterator<Item = AudioLayout>) -> Self {
337 Self {
338 builder: self.builder.field(
339 glib::gstr!("layout"),
340 gst::List::new(layouts.into_iter().map(layout_str)),
341 ),
342 }
343 }
344
345 pub fn layout_list_if(
346 self,
347 layouts: impl IntoIterator<Item = AudioLayout>,
348 predicate: bool,
349 ) -> Self {
350 if predicate {
351 self.layout_list(layouts)
352 } else {
353 self
354 }
355 }
356
357 pub fn layout_list_if_some(
358 self,
359 layouts: Option<impl IntoIterator<Item = AudioLayout>>,
360 ) -> Self {
361 if let Some(layouts) = layouts {
362 self.layout_list(layouts)
363 } else {
364 self
365 }
366 }
367
368 pub fn layout_list_if_not_empty(self, layouts: impl IntoIterator<Item = AudioLayout>) -> Self {
369 let mut layouts = layouts.into_iter().peekable();
370 if layouts.peek().is_some() {
371 self.layout_list(layouts)
372 } else {
373 self
374 }
375 }
376
377 pub fn channel_mask(self, channel_mask: u64) -> Self {
378 Self {
379 builder: self
380 .builder
381 .field("channel-mask", gst::Bitmask::new(channel_mask)),
382 }
383 }
384
385 pub fn channel_mask_if(self, channel_mask: u64, predicate: bool) -> Self {
386 if predicate {
387 self.channel_mask(channel_mask)
388 } else {
389 self
390 }
391 }
392
393 pub fn channel_mask_if_some(self, channel_mask: Option<u64>) -> Self {
394 if let Some(channel_mask) = channel_mask {
395 self.channel_mask(channel_mask)
396 } else {
397 self
398 }
399 }
400
401 pub fn fallback_channel_mask(self) -> Self {
402 let channels = self.builder.structure().get::<i32>(glib::gstr!("channels"));
403 match channels {
404 Ok(channels) => Self {
405 builder: self.builder.field(
406 glib::gstr!("channel-mask"),
407 gst::Bitmask::new(crate::AudioChannelPosition::fallback_mask(channels as u32)),
408 ),
409 },
410 Err(e) => panic!("{e:?}"),
411 }
412 }
413
414 #[inline]
419 pub fn field(self, name: impl IntoGStr, value: impl Into<glib::Value> + Send) -> Self {
420 Self {
421 builder: self.builder.field(name, value),
422 }
423 }
424
425 gst::impl_builder_gvalue_extra_setters!(field);
426
427 #[must_use]
428 pub fn build(self) -> gst::Caps {
429 self.builder.build()
430 }
431}
432
433fn range_bounds_i32_start_end(range: impl RangeBounds<i32>) -> (i32, i32) {
434 skip_assert_initialized!();
435 let start = match range.start_bound() {
436 Unbounded => 1,
437 Excluded(n) => n + 1,
438 Included(n) => *n,
439 };
440 let end = match range.end_bound() {
441 Unbounded => i32::MAX,
442 Excluded(n) => n - 1,
443 Included(n) => *n,
444 };
445 (start, end)
446}
447
448fn layout_str(layout: AudioLayout) -> &'static glib::GStr {
449 skip_assert_initialized!();
450 match layout {
451 crate::AudioLayout::Interleaved => glib::gstr!("interleaved"),
452 crate::AudioLayout::NonInterleaved => glib::gstr!("non-interleaved"),
453 crate::AudioLayout::__Unknown(_) => glib::gstr!("unknown"),
454 }
455}
456
457#[cfg(test)]
458mod tests {
459 use super::{AudioCapsBuilder, AudioFormat};
460
461 #[test]
462 fn default_encoding() {
463 gst::init().unwrap();
464 let caps = AudioCapsBuilder::new().build();
465 assert_eq!(caps.structure(0).unwrap().name(), "audio/x-raw");
466 }
467
468 #[test]
469 fn explicit_encoding() {
470 gst::init().unwrap();
471 let caps = AudioCapsBuilder::for_encoding("audio/mpeg").build();
472 assert_eq!(caps.structure(0).unwrap().name(), "audio/mpeg");
473 }
474
475 #[test]
476 fn format_if() {
477 gst::init().unwrap();
478
479 let formats = [AudioFormat::S24be, AudioFormat::S16be, AudioFormat::U8];
480 let caps_with_format = AudioCapsBuilder::for_encoding("audio/x-raw")
481 .format_list(formats)
482 .build();
483 assert!(caps_with_format
484 .structure(0)
485 .unwrap()
486 .get::<gst::List>("format")
487 .unwrap()
488 .iter()
489 .map(|f| f.get::<String>().unwrap())
490 .eq(formats.iter().map(|f| f.to_string())));
491
492 let caps = AudioCapsBuilder::for_encoding("audio/x-raw")
493 .format_list_if_some(Some(formats))
494 .build();
495 assert_eq!(caps, caps_with_format);
496
497 let caps = AudioCapsBuilder::for_encoding("audio/x-raw")
498 .format_list_if_some(Option::<Vec<AudioFormat>>::None)
499 .build();
500 assert!(!caps.structure(0).unwrap().has_field("format"));
501
502 let caps = AudioCapsBuilder::for_encoding("audio/x-raw")
503 .format_list_if_not_empty(formats)
504 .build();
505 assert_eq!(caps, caps_with_format);
506
507 let caps = AudioCapsBuilder::for_encoding("audio/x-raw")
508 .format_list_if_not_empty(Vec::<AudioFormat>::new())
509 .build();
510 assert!(!caps.structure(0).unwrap().has_field("format"));
511 }
512}