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