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