Skip to main content

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 { 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    // rustdoc-stripper-ignore-next
553    /// Sets field `name` to the given value `value`.
554    ///
555    /// Overrides any default or previously defined value for `name`.
556    #[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    // rustdoc-stripper-ignore-next
564    /// Sets field `name` to the given value `value`.
565    ///
566    /// Overrides any default or previously defined value for `name`.
567    #[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    // rustdoc-stripper-ignore-next
579    /// Sets field `name` to the given value `value`.
580    ///
581    /// Overrides any default or previously defined value for `name`.
582    #[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
616// https://math.stackexchange.com/questions/39582/how-to-compute-next-previous-representable-rational-number/3798608#3798608
617
618/* Extended Euclidean Algorithm: computes (g, x, y),
619 * such that a*x + b*y = g = gcd(a, b) >= 0. */
620fn 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
635/* Computes the neighbours of p/q in the Farey sequence of order n. */
636fn 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        //25.000000011641532
718        assert_eq!(next.numer(), 2147483626);
719        assert_eq!(next.denom(), 85899345);
720        let prev = previous_fraction(twentyfive);
721        //24.999999988358468
722        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        //0.040000000018626
731        assert_eq!(next.numer(), 85899345);
732        assert_eq!(next.denom(), 2147483624);
733        let prev = previous_fraction(twentyfive);
734        //0.039999999981374
735        assert_eq!(prev.numer(), 85899345);
736        assert_eq!(prev.denom(), 2147483626);
737    }
738}