gstreamer_video/
caps.rs

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    // 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    // rustdoc-stripper-ignore-next
47    /// Constructs an `VideoCapsBuilder` for the specified encoding.
48    ///
49    /// The resulting `Caps` will use the `encoding` argument as name
50    /// and will not contain any additional fields unless explicitly added.
51    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    // rustdoc-stripper-ignore-next
59    /// Constructs an `VideoCapsBuilder` for the specified encoding.
60    ///
61    /// The resulting `Caps` will use the `encoding` argument as name
62    /// and will not contain any additional fields unless explicitly added.
63    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    // rustdoc-stripper-ignore-next
565    /// Sets field `name` to the given value `value`.
566    ///
567    /// Overrides any default or previously defined value for `name`.
568    #[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    // rustdoc-stripper-ignore-next
576    /// Sets field `name` to the given value `value`.
577    ///
578    /// Overrides any default or previously defined value for `name`.
579    #[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    // rustdoc-stripper-ignore-next
591    /// Sets field `name` to the given value `value`.
592    ///
593    /// Overrides any default or previously defined value for `name`.
594    #[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
628// https://math.stackexchange.com/questions/39582/how-to-compute-next-previous-representable-rational-number/3798608#3798608
629
630/* Extended Euclidean Algorithm: computes (g, x, y),
631 * such that a*x + b*y = g = gcd(a, b) >= 0. */
632fn 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
651/* Computes the neighbours of p/q in the Farey sequence of order n. */
652fn 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        //25.000000011641532
734        assert_eq!(next.numer(), 2147483626);
735        assert_eq!(next.denom(), 85899345);
736        let prev = previous_fraction(twentyfive);
737        //24.999999988358468
738        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        //0.040000000018626
747        assert_eq!(next.numer(), 85899345);
748        assert_eq!(next.denom(), 2147483624);
749        let prev = previous_fraction(twentyfive);
750        //0.039999999981374
751        assert_eq!(prev.numer(), 85899345);
752        assert_eq!(prev.denom(), 2147483626);
753    }
754}