gstreamer_video/
caps.rs

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    // rustdoc-stripper-ignore-next
14    /// Constructs an `VideoCapsBuilder` for the "video/x-raw" encoding.
15    ///
16    /// If left unchanged, the resulting `Caps` will be initialized with:
17    /// - "video/x-raw" encoding.
18    /// - all available formats.
19    /// - maximum width range.
20    /// - maximum height range.
21    ///
22    /// Use [`VideoCapsBuilder::for_encoding`] to specify another encoding.
23    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    // rustdoc-stripper-ignore-next
35    /// Constructs an `VideoCapsBuilder` for the specified encoding.
36    ///
37    /// The resulting `Caps` will use the `encoding` argument as name
38    /// and will not contain any additional fields unless explicitly added.
39    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    // rustdoc-stripper-ignore-next
523    /// Sets field `name` to the given value `value`.
524    ///
525    /// Overrides any default or previously defined value for `name`.
526    #[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
556// https://math.stackexchange.com/questions/39582/how-to-compute-next-previous-representable-rational-number/3798608#3798608
557
558/* Extended Euclidean Algorithm: computes (g, x, y),
559 * such that a*x + b*y = g = gcd(a, b) >= 0. */
560fn 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
579/* Computes the neighbours of p/q in the Farey sequence of order n. */
580fn 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        //25.000000011641532
662        assert_eq!(next.numer(), 2147483626);
663        assert_eq!(next.denom(), 85899345);
664        let prev = previous_fraction(twentyfive);
665        //24.999999988358468
666        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        //0.040000000018626
675        assert_eq!(next.numer(), 85899345);
676        assert_eq!(next.denom(), 2147483624);
677        let prev = previous_fraction(twentyfive);
678        //0.039999999981374
679        assert_eq!(prev.numer(), 85899345);
680        assert_eq!(prev.denom(), 2147483626);
681    }
682}