1use std::ops::{Bound::*, RangeBounds};
2
3use glib::translate::*;
4use gst::{Caps, IdStr};
5
6use crate::VideoFormat;
7
8pub struct VideoCapsBuilder<T> {
9 builder: gst::caps::Builder<T>,
10}
11
12impl VideoCapsBuilder<gst::caps::NoFeature> {
13 pub fn new() -> Self {
24 assert_initialized_main_thread!();
25 let builder = Caps::builder(glib::gstr!("video/x-raw"));
26 let builder = VideoCapsBuilder { builder };
27 builder
28 .format_list(VideoFormat::iter_raw())
29 .width_range(..)
30 .height_range(..)
31 .framerate_range(..)
32 }
33
34 pub fn for_encoding(encoding: impl IntoGStr) -> Self {
40 assert_initialized_main_thread!();
41 VideoCapsBuilder {
42 builder: Caps::builder(encoding),
43 }
44 }
45
46 pub fn for_encoding_from_static(encoding: impl AsRef<glib::GStr> + 'static) -> Self {
52 assert_initialized_main_thread!();
53 VideoCapsBuilder {
54 builder: Caps::builder_from_static(encoding),
55 }
56 }
57
58 pub fn for_encoding_from_id(encoding: impl AsRef<IdStr>) -> Self {
64 assert_initialized_main_thread!();
65 VideoCapsBuilder {
66 builder: Caps::builder_from_id(encoding),
67 }
68 }
69
70 pub fn any_features(self) -> VideoCapsBuilder<gst::caps::HasFeatures> {
71 VideoCapsBuilder {
72 builder: self.builder.any_features(),
73 }
74 }
75
76 pub fn features<S: IntoGStr>(
77 self,
78 features: impl IntoIterator<Item = S>,
79 ) -> VideoCapsBuilder<gst::caps::HasFeatures> {
80 VideoCapsBuilder {
81 builder: self.builder.features(features),
82 }
83 }
84
85 pub fn features_from_statics<S: AsRef<glib::GStr> + 'static>(
86 self,
87 features: impl IntoIterator<Item = S>,
88 ) -> VideoCapsBuilder<gst::caps::HasFeatures> {
89 VideoCapsBuilder {
90 builder: self.builder.features_from_statics(features),
91 }
92 }
93
94 pub fn features_from_ids<S: AsRef<IdStr>>(
95 self,
96 features: impl IntoIterator<Item = S>,
97 ) -> VideoCapsBuilder<gst::caps::HasFeatures> {
98 VideoCapsBuilder {
99 builder: self.builder.features_from_ids(features),
100 }
101 }
102}
103
104impl Default for VideoCapsBuilder<gst::caps::NoFeature> {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl<T> VideoCapsBuilder<T> {
111 pub fn format(self, format: VideoFormat) -> Self {
112 Self {
113 builder: self.builder.field(glib::gstr!("format"), format.to_str()),
114 }
115 }
116
117 pub fn format_if(self, format: VideoFormat, predicate: bool) -> Self {
118 if predicate {
119 self.format(format)
120 } else {
121 self
122 }
123 }
124
125 pub fn format_if_some(self, format: Option<VideoFormat>) -> Self {
126 if let Some(format) = format {
127 self.format(format)
128 } else {
129 self
130 }
131 }
132
133 pub fn format_list(self, formats: impl IntoIterator<Item = VideoFormat>) -> Self {
134 Self {
135 builder: self.builder.field(
136 glib::gstr!("format"),
137 gst::List::new(formats.into_iter().map(|f| f.to_str())),
138 ),
139 }
140 }
141
142 pub fn format_list_if(
143 self,
144 formats: impl IntoIterator<Item = VideoFormat>,
145 predicate: bool,
146 ) -> Self {
147 if predicate {
148 self.format_list(formats)
149 } else {
150 self
151 }
152 }
153
154 pub fn format_list_if_some(
155 self,
156 formats: Option<impl IntoIterator<Item = VideoFormat>>,
157 ) -> Self {
158 if let Some(formats) = formats {
159 self.format_list(formats)
160 } else {
161 self
162 }
163 }
164
165 pub fn format_list_if_not_empty(self, formats: impl IntoIterator<Item = VideoFormat>) -> Self {
166 let mut formats = formats.into_iter().peekable();
167 if formats.peek().is_some() {
168 self.format_list(formats)
169 } else {
170 self
171 }
172 }
173
174 pub fn width(self, width: i32) -> Self {
175 Self {
176 builder: self.builder.field(glib::gstr!("width"), width),
177 }
178 }
179
180 pub fn width_if(self, width: i32, predicate: bool) -> Self {
181 if predicate {
182 self.width(width)
183 } else {
184 self
185 }
186 }
187
188 pub fn width_if_some(self, width: Option<i32>) -> Self {
189 if let Some(width) = width {
190 self.width(width)
191 } else {
192 self
193 }
194 }
195
196 pub fn width_range(self, widths: impl RangeBounds<i32>) -> Self {
197 let (start, end) = range_bounds_i32_start_end(widths);
198 let gst_widths: gst::IntRange<i32> = gst::IntRange::new(start, end);
199 Self {
200 builder: self.builder.field(glib::gstr!("width"), gst_widths),
201 }
202 }
203
204 pub fn width_range_if_some(self, widths: Option<impl RangeBounds<i32>>) -> Self {
205 if let Some(widths) = widths {
206 self.width_range(widths)
207 } else {
208 self
209 }
210 }
211
212 pub fn width_range_if(self, widths: impl RangeBounds<i32>, predicate: bool) -> Self {
213 if predicate {
214 self.width_range(widths)
215 } else {
216 self
217 }
218 }
219
220 pub fn width_list(self, widths: impl IntoIterator<Item = i32>) -> Self {
221 Self {
222 builder: self
223 .builder
224 .field(glib::gstr!("width"), gst::List::new(widths)),
225 }
226 }
227
228 pub fn width_list_if(self, widths: impl IntoIterator<Item = i32>, predicate: bool) -> Self {
229 if predicate {
230 self.width_list(widths)
231 } else {
232 self
233 }
234 }
235
236 pub fn width_list_if_some(self, widths: Option<impl IntoIterator<Item = i32>>) -> Self {
237 if let Some(widths) = widths {
238 self.width_list(widths)
239 } else {
240 self
241 }
242 }
243
244 pub fn width_list_if_not_empty(self, widths: impl IntoIterator<Item = i32>) -> Self {
245 let mut widths = widths.into_iter().peekable();
246 if widths.peek().is_some() {
247 self.width_list(widths)
248 } else {
249 self
250 }
251 }
252
253 pub fn height(self, height: i32) -> Self {
254 Self {
255 builder: self.builder.field(glib::gstr!("height"), height),
256 }
257 }
258
259 pub fn height_if(self, height: i32, predicate: bool) -> Self {
260 if predicate {
261 self.height(height)
262 } else {
263 self
264 }
265 }
266
267 pub fn height_if_some(self, height: Option<i32>) -> Self {
268 if let Some(height) = height {
269 self.height(height)
270 } else {
271 self
272 }
273 }
274
275 pub fn height_range(self, heights: impl RangeBounds<i32>) -> Self {
276 let (start, end) = range_bounds_i32_start_end(heights);
277 let gst_heights: gst::IntRange<i32> = gst::IntRange::new(start, end);
278 Self {
279 builder: self.builder.field(glib::gstr!("height"), gst_heights),
280 }
281 }
282
283 pub fn height_range_if(self, heights: impl RangeBounds<i32>, predicate: bool) -> Self {
284 if predicate {
285 self.height_range(heights)
286 } else {
287 self
288 }
289 }
290
291 pub fn height_range_if_some(self, heights: Option<impl RangeBounds<i32>>) -> Self {
292 if let Some(heights) = heights {
293 self.height_range(heights)
294 } else {
295 self
296 }
297 }
298
299 pub fn height_list(self, heights: impl IntoIterator<Item = i32>) -> Self {
300 Self {
301 builder: self
302 .builder
303 .field(glib::gstr!("height"), gst::List::new(heights)),
304 }
305 }
306
307 pub fn height_list_if(self, heights: impl IntoIterator<Item = i32>, predicate: bool) -> Self {
308 if predicate {
309 self.height_list(heights)
310 } else {
311 self
312 }
313 }
314
315 pub fn height_list_if_some(self, heights: Option<impl IntoIterator<Item = i32>>) -> Self {
316 if let Some(heights) = heights {
317 self.height_list(heights)
318 } else {
319 self
320 }
321 }
322
323 pub fn height_list_if_not_empty(self, heights: impl IntoIterator<Item = i32>) -> Self {
324 let mut heights = heights.into_iter().peekable();
325 if heights.peek().is_some() {
326 self.height_list(heights)
327 } else {
328 self
329 }
330 }
331
332 pub fn framerate(self, framerate: gst::Fraction) -> Self {
333 Self {
334 builder: self.builder.field(glib::gstr!("framerate"), framerate),
335 }
336 }
337
338 pub fn framerate_if(self, framerate: gst::Fraction, predicate: bool) -> Self {
339 if predicate {
340 self.framerate(framerate)
341 } else {
342 self
343 }
344 }
345
346 pub fn framerate_if_some(self, framerate: Option<gst::Fraction>) -> Self {
347 if let Some(framerate) = framerate {
348 self.framerate(framerate)
349 } else {
350 self
351 }
352 }
353
354 pub fn framerate_range(self, framerates: impl RangeBounds<gst::Fraction>) -> Self {
355 let start = match framerates.start_bound() {
356 Unbounded => gst::Fraction::new(0, 1),
357 Excluded(n) => next_fraction(*n),
358 Included(n) => {
359 assert!(n.numer() >= 0);
360 *n
361 }
362 };
363 let end = match framerates.end_bound() {
364 Unbounded => gst::Fraction::new(i32::MAX, 1),
365 Excluded(n) => previous_fraction(*n),
366 Included(n) => {
367 assert!(n.numer() >= 0);
368 *n
369 }
370 };
371 assert!(start <= end);
372 let framerates: gst::FractionRange = gst::FractionRange::new(start, end);
373 Self {
374 builder: self.builder.field(glib::gstr!("framerate"), framerates),
375 }
376 }
377
378 pub fn framerate_range_if(
379 self,
380 framerates: impl RangeBounds<gst::Fraction>,
381 predicate: bool,
382 ) -> Self {
383 if predicate {
384 self.framerate_range(framerates)
385 } else {
386 self
387 }
388 }
389
390 pub fn framerate_range_if_some(
391 self,
392 framerates: Option<impl RangeBounds<gst::Fraction>>,
393 ) -> Self {
394 if let Some(framerates) = framerates {
395 self.framerate_range(framerates)
396 } else {
397 self
398 }
399 }
400
401 pub fn framerate_list(self, framerates: impl IntoIterator<Item = gst::Fraction>) -> Self {
402 Self {
403 builder: self
404 .builder
405 .field(glib::gstr!("framerate"), gst::List::new(framerates)),
406 }
407 }
408
409 pub fn framerate_list_if(
410 self,
411 framerates: impl IntoIterator<Item = gst::Fraction>,
412 predicate: bool,
413 ) -> Self {
414 if predicate {
415 self.framerate_list(framerates)
416 } else {
417 self
418 }
419 }
420
421 pub fn framerate_list_if_some(
422 self,
423 framerates: Option<impl IntoIterator<Item = gst::Fraction>>,
424 ) -> Self {
425 if let Some(framerates) = framerates {
426 self.framerate_list(framerates)
427 } else {
428 self
429 }
430 }
431
432 pub fn framerate_list_if_not_empty(
433 self,
434 framerates: impl IntoIterator<Item = gst::Fraction>,
435 ) -> Self {
436 let mut framerates = framerates.into_iter().peekable();
437 if framerates.peek().is_some() {
438 self.framerate_list(framerates)
439 } else {
440 self
441 }
442 }
443
444 pub fn pixel_aspect_ratio(self, pixel_aspect_ratio: gst::Fraction) -> Self {
445 Self {
446 builder: self.builder.field("pixel-aspect-ratio", pixel_aspect_ratio),
447 }
448 }
449
450 pub fn pixel_aspect_ratio_if(self, pixel_aspect_ratio: gst::Fraction, predicate: bool) -> Self {
451 if predicate {
452 self.pixel_aspect_ratio(pixel_aspect_ratio)
453 } else {
454 self
455 }
456 }
457
458 pub fn pixel_aspect_ratio_if_some(self, pixel_aspect_ratio: Option<gst::Fraction>) -> Self {
459 if let Some(pixel_aspect_ratio) = pixel_aspect_ratio {
460 self.pixel_aspect_ratio(pixel_aspect_ratio)
461 } else {
462 self
463 }
464 }
465
466 pub fn pixel_aspect_ratio_range(
467 self,
468 pixel_aspect_ratios: impl RangeBounds<gst::Fraction>,
469 ) -> Self {
470 let start = match pixel_aspect_ratios.start_bound() {
471 Unbounded => gst::Fraction::new(1, i32::MAX),
472 Excluded(n) => next_fraction(*n),
473 Included(n) => {
474 assert!(n.numer() >= 0);
475 *n
476 }
477 };
478 let end = match pixel_aspect_ratios.end_bound() {
479 Unbounded => gst::Fraction::new(i32::MAX, 1),
480 Excluded(n) => previous_fraction(*n),
481 Included(n) => {
482 assert!(n.numer() >= 0);
483 *n
484 }
485 };
486 assert!(start <= end);
487 let pixel_aspect_ratios: gst::FractionRange = gst::FractionRange::new(start, end);
488 Self {
489 builder: self
490 .builder
491 .field("pixel-aspect-ratio", pixel_aspect_ratios),
492 }
493 }
494
495 pub fn pixel_aspect_ratio_range_if(
496 self,
497 pixel_aspect_ratios: impl RangeBounds<gst::Fraction>,
498 predicate: bool,
499 ) -> Self {
500 if predicate {
501 self.pixel_aspect_ratio_range(pixel_aspect_ratios)
502 } else {
503 self
504 }
505 }
506
507 pub fn pixel_aspect_ratio_range_if_some(
508 self,
509 pixel_aspect_ratios: Option<impl RangeBounds<gst::Fraction>>,
510 ) -> Self {
511 if let Some(pixel_aspect_ratios) = pixel_aspect_ratios {
512 self.pixel_aspect_ratio_range(pixel_aspect_ratios)
513 } else {
514 self
515 }
516 }
517
518 pub fn pixel_aspect_ratio_list(
519 self,
520 pixel_aspect_ratios: impl IntoIterator<Item = gst::Fraction>,
521 ) -> Self {
522 Self {
523 builder: self
524 .builder
525 .field("pixel-aspect-ratio", gst::List::new(pixel_aspect_ratios)),
526 }
527 }
528
529 pub fn pixel_aspect_ratio_list_if(
530 self,
531 pixel_aspect_ratios: impl IntoIterator<Item = gst::Fraction>,
532 predicate: bool,
533 ) -> Self {
534 if predicate {
535 self.pixel_aspect_ratio_list(pixel_aspect_ratios)
536 } else {
537 self
538 }
539 }
540
541 pub fn pixel_aspect_ratio_list_if_some(
542 self,
543 pixel_aspect_ratios: Option<impl IntoIterator<Item = gst::Fraction>>,
544 ) -> Self {
545 if let Some(pixel_aspect_ratios) = pixel_aspect_ratios {
546 self.pixel_aspect_ratio_list(pixel_aspect_ratios)
547 } else {
548 self
549 }
550 }
551
552 pub fn pixel_aspect_ratio_list_if_not_empty(
553 self,
554 pixel_aspect_ratios: impl IntoIterator<Item = gst::Fraction>,
555 ) -> Self {
556 let mut pixel_aspect_ratios = pixel_aspect_ratios.into_iter().peekable();
557 if pixel_aspect_ratios.peek().is_some() {
558 self.pixel_aspect_ratio_list(pixel_aspect_ratios)
559 } else {
560 self
561 }
562 }
563
564 #[inline]
569 pub fn field(self, name: impl IntoGStr, value: impl Into<glib::Value> + Send) -> Self {
570 Self {
571 builder: self.builder.field(name, value),
572 }
573 }
574
575 #[inline]
580 pub fn field_with_static(
581 self,
582 name: impl AsRef<glib::GStr> + 'static,
583 value: impl Into<glib::Value> + Send,
584 ) -> Self {
585 Self {
586 builder: self.builder.field_with_static(name, value),
587 }
588 }
589
590 #[inline]
595 pub fn field_with_id(
596 self,
597 name: impl AsRef<IdStr>,
598 value: impl Into<glib::Value> + Send,
599 ) -> Self {
600 Self {
601 builder: self.builder.field_with_id(name, value),
602 }
603 }
604
605 gst::impl_builder_gvalue_extra_setters!(field);
606
607 #[must_use]
608 pub fn build(self) -> gst::Caps {
609 self.builder.build()
610 }
611}
612
613fn range_bounds_i32_start_end(range: impl RangeBounds<i32>) -> (i32, i32) {
614 skip_assert_initialized!();
615 let start = match range.start_bound() {
616 Unbounded => 1,
617 Excluded(n) => n + 1,
618 Included(n) => *n,
619 };
620 let end = match range.end_bound() {
621 Unbounded => i32::MAX,
622 Excluded(n) => n - 1,
623 Included(n) => *n,
624 };
625 (start, end)
626}
627
628fn xgcd(mut a: i64, mut b: i64) -> (i64, i64, i64) {
633 skip_assert_initialized!();
634 let mut x0 = 0i64;
635 let mut x1 = 1i64;
636 let mut y0 = 1i64;
637 let mut y1 = 0i64;
638 while a != 0 {
639 let q;
640 (q, a, b) = (b / a, b % a, a);
641 (y0, y1) = (y1, y0 - q * y1);
642 (x0, x1) = (x1, x0 - q * x1);
643 }
644 if b >= 0 {
645 (b, x0, y0)
646 } else {
647 (-b, -x0, -y0)
648 }
649}
650
651fn farey_neighbours(p: i32, q: i32) -> (i32, i32, i32, i32) {
653 skip_assert_initialized!();
654 let n = i32::MAX as i64;
655 assert!(q != 0);
656 let mut p = p as i64;
657 let mut q = q as i64;
658 if q < 0 {
659 p = -p;
660 q = -q;
661 }
662 let (g, r, _) = xgcd(p, q);
663 p /= g;
664 q /= g;
665 let b = ((n - r) / q) * q + r;
666 let a = (b * p - 1) / q;
667 let d = ((n + r) / q) * q - r;
668 let c = (d * p + 1) / q;
669 (a as i32, b as i32, c as i32, d as i32)
670}
671
672fn previous_fraction(fraction: gst::Fraction) -> gst::Fraction {
673 skip_assert_initialized!();
674 let num = fraction.numer();
675 let den = fraction.denom();
676 let (new_num, new_den);
677 if num < den {
678 (new_num, new_den, _, _) = farey_neighbours(num, den);
679 } else {
680 (_, _, new_den, new_num) = farey_neighbours(den, num);
681 }
682 gst::Fraction::new(new_num, new_den)
683}
684
685fn next_fraction(fraction: gst::Fraction) -> gst::Fraction {
686 skip_assert_initialized!();
687 let num = fraction.numer();
688 let den = fraction.denom();
689 let (new_num, new_den);
690 if num < den {
691 (_, _, new_num, new_den) = farey_neighbours(num, den);
692 } else {
693 (new_den, new_num, _, _) = farey_neighbours(den, num);
694 }
695 gst::Fraction::new(new_num, new_den)
696}
697
698#[cfg(test)]
699mod tests {
700 use super::{next_fraction, previous_fraction, VideoCapsBuilder};
701
702 #[test]
703 fn default_encoding() {
704 gst::init().unwrap();
705 let caps = VideoCapsBuilder::new().build();
706 assert_eq!(caps.structure(0).unwrap().name(), "video/x-raw");
707 }
708
709 #[test]
710 fn explicit_encoding() {
711 gst::init().unwrap();
712 let caps = VideoCapsBuilder::for_encoding("video/mpeg").build();
713 assert_eq!(caps.structure(0).unwrap().name(), "video/mpeg");
714 }
715
716 #[test]
717 fn test_0_1_fraction() {
718 gst::init().unwrap();
719 let zero_over_one = gst::Fraction::new(0, 1);
720 let prev = previous_fraction(zero_over_one);
721 assert_eq!(prev.numer(), -1);
722 assert_eq!(prev.denom(), i32::MAX);
723 let next = next_fraction(zero_over_one);
724 assert_eq!(next.numer(), 1);
725 assert_eq!(next.denom(), i32::MAX);
726 }
727
728 #[test]
729 fn test_25_1() {
730 gst::init().unwrap();
731 let twentyfive = gst::Fraction::new(25, 1);
732 let next = next_fraction(twentyfive);
733 assert_eq!(next.numer(), 2147483626);
735 assert_eq!(next.denom(), 85899345);
736 let prev = previous_fraction(twentyfive);
737 assert_eq!(prev.numer(), 2147483624);
739 assert_eq!(prev.denom(), 85899345);
740 }
741 #[test]
742 fn test_1_25() {
743 gst::init().unwrap();
744 let twentyfive = gst::Fraction::new(1, 25);
745 let next = next_fraction(twentyfive);
746 assert_eq!(next.numer(), 85899345);
748 assert_eq!(next.denom(), 2147483624);
749 let prev = previous_fraction(twentyfive);
750 assert_eq!(prev.numer(), 85899345);
752 assert_eq!(prev.denom(), 2147483626);
753 }
754}