1use std::ops::{Bound::*, RangeBounds};
2
3use glib::IntoGStr;
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_list(self, formats: impl IntoIterator<Item = VideoFormat>) -> Self {
76 Self {
77 builder: self.builder.field(
78 glib::gstr!("format"),
79 gst::List::new(formats.into_iter().map(|f| f.to_str())),
80 ),
81 }
82 }
83
84 pub fn width(self, width: i32) -> Self {
85 Self {
86 builder: self.builder.field(glib::gstr!("width"), width),
87 }
88 }
89
90 pub fn width_range(self, widths: impl RangeBounds<i32>) -> Self {
91 let (start, end) = range_bounds_i32_start_end(widths);
92 let gst_widths: gst::IntRange<i32> = gst::IntRange::new(start, end);
93 Self {
94 builder: self.builder.field(glib::gstr!("width"), gst_widths),
95 }
96 }
97
98 pub fn width_list(self, widths: impl IntoIterator<Item = i32>) -> Self {
99 Self {
100 builder: self
101 .builder
102 .field(glib::gstr!("width"), gst::List::new(widths)),
103 }
104 }
105
106 pub fn height(self, height: i32) -> Self {
107 Self {
108 builder: self.builder.field(glib::gstr!("height"), height),
109 }
110 }
111
112 pub fn height_range(self, heights: impl RangeBounds<i32>) -> Self {
113 let (start, end) = range_bounds_i32_start_end(heights);
114 let gst_heights: gst::IntRange<i32> = gst::IntRange::new(start, end);
115 Self {
116 builder: self.builder.field(glib::gstr!("height"), gst_heights),
117 }
118 }
119
120 pub fn height_list(self, heights: impl IntoIterator<Item = i32>) -> Self {
121 Self {
122 builder: self
123 .builder
124 .field(glib::gstr!("height"), gst::List::new(heights)),
125 }
126 }
127
128 pub fn framerate(self, framerate: gst::Fraction) -> Self {
129 Self {
130 builder: self.builder.field(glib::gstr!("framerate"), framerate),
131 }
132 }
133
134 pub fn framerate_range(self, framerates: impl RangeBounds<gst::Fraction>) -> Self {
135 let start = match framerates.start_bound() {
136 Unbounded => gst::Fraction::new(0, 1),
137 Excluded(n) => next_fraction(*n),
138 Included(n) => {
139 assert!(n.numer() >= 0);
140 *n
141 }
142 };
143 let end = match framerates.end_bound() {
144 Unbounded => gst::Fraction::new(i32::MAX, 1),
145 Excluded(n) => previous_fraction(*n),
146 Included(n) => {
147 assert!(n.numer() >= 0);
148 *n
149 }
150 };
151 assert!(start <= end);
152 let framerates: gst::FractionRange = gst::FractionRange::new(start, end);
153 Self {
154 builder: self.builder.field(glib::gstr!("framerate"), framerates),
155 }
156 }
157
158 pub fn framerate_list(self, framerates: impl IntoIterator<Item = gst::Fraction>) -> Self {
159 Self {
160 builder: self
161 .builder
162 .field(glib::gstr!("framerate"), gst::List::new(framerates)),
163 }
164 }
165
166 pub fn pixel_aspect_ratio(self, pixel_aspect_ratio: gst::Fraction) -> Self {
167 Self {
168 builder: self.builder.field("pixel-aspect-ratio", pixel_aspect_ratio),
169 }
170 }
171
172 pub fn pixel_aspect_ratio_range(
173 self,
174 pixel_aspect_ratios: impl RangeBounds<gst::Fraction>,
175 ) -> Self {
176 let start = match pixel_aspect_ratios.start_bound() {
177 Unbounded => gst::Fraction::new(1, i32::MAX),
178 Excluded(n) => next_fraction(*n),
179 Included(n) => {
180 assert!(n.numer() >= 0);
181 *n
182 }
183 };
184 let end = match pixel_aspect_ratios.end_bound() {
185 Unbounded => gst::Fraction::new(i32::MAX, 1),
186 Excluded(n) => previous_fraction(*n),
187 Included(n) => {
188 assert!(n.numer() >= 0);
189 *n
190 }
191 };
192 assert!(start <= end);
193 let pixel_aspect_ratios: gst::FractionRange = gst::FractionRange::new(start, end);
194 Self {
195 builder: self
196 .builder
197 .field("pixel-aspect-ratio", pixel_aspect_ratios),
198 }
199 }
200
201 pub fn pixel_aspect_ratio_list(
202 self,
203 pixel_aspect_ratios: impl IntoIterator<Item = gst::Fraction>,
204 ) -> Self {
205 Self {
206 builder: self
207 .builder
208 .field("pixel-aspect-ratio", gst::List::new(pixel_aspect_ratios)),
209 }
210 }
211
212 pub fn field(self, name: &str, value: impl Into<glib::Value> + Send) -> Self {
213 Self {
214 builder: self.builder.field(name, value),
215 }
216 }
217
218 #[must_use]
219 pub fn build(self) -> gst::Caps {
220 self.builder.build()
221 }
222}
223
224fn range_bounds_i32_start_end(range: impl RangeBounds<i32>) -> (i32, i32) {
225 skip_assert_initialized!();
226 let start: i32 = match range.start_bound() {
227 Unbounded => 1,
228 Excluded(n: &i32) => n + 1,
229 Included(n: &i32) => *n,
230 };
231 let end: i32 = match range.end_bound() {
232 Unbounded => i32::MAX,
233 Excluded(n: &i32) => n - 1,
234 Included(n: &i32) => *n,
235 };
236 (start, end)
237}
238
239// https://math.stackexchange.com/questions/39582/how-to-compute-next-previous-representable-rational-number/3798608#3798608
240
241/* Extended Euclidean Algorithm: computes (g, x, y),
242 * such that a*x + b*y = g = gcd(a, b) >= 0. */
243fn xgcd(mut a: i64, mut b: i64) -> (i64, i64, i64) {
244 skip_assert_initialized!();
245 let mut x0: i64 = 0i64;
246 let mut x1: i64 = 1i64;
247 let mut y0: i64 = 1i64;
248 let mut y1: i64 = 0i64;
249 while a != 0 {
250 let q: i64;
251 (q, a, b) = (b / a, b % a, a);
252 (y0, y1) = (y1, y0 - q * y1);
253 (x0, x1) = (x1, x0 - q * x1);
254 }
255 if b >= 0 {
256 (b, x0, y0)
257 } else {
258 (-b, -x0, -y0)
259 }
260}
261
262/* Computes the neighbours of p/q in the Farey sequence of order n. */
263fn farey_neighbours(p: i32, q: i32) -> (i32, i32, i32, i32) {
264 skip_assert_initialized!();
265 let n: i64 = i32::MAX as i64;
266 assert!(q != 0);
267 let mut p: i64 = p as i64;
268 let mut q: i64 = q as i64;
269 if q < 0 {
270 p = -p;
271 q = -q;
272 }
273 let (g: i64, r: i64, _) = xgcd(a:p, b:q);
274 p /= g;
275 q /= g;
276 let b: i64 = ((n - r) / q) * q + r;
277 let a: i64 = (b * p - 1) / q;
278 let d: i64 = ((n + r) / q) * q - r;
279 let c: i64 = (d * p + 1) / q;
280 (a as i32, b as i32, c as i32, d as i32)
281}
282
283fn previous_fraction(fraction: gst::Fraction) -> gst::Fraction {
284 skip_assert_initialized!();
285 let num: i32 = fraction.numer();
286 let den: i32 = fraction.denom();
287 let (new_num: i32, new_den: i32);
288 if num < den {
289 (new_num, new_den, _, _) = farey_neighbours(p:num, q:den);
290 } else {
291 (_, _, new_den, new_num) = farey_neighbours(p:den, q:num);
292 }
293 gst::Fraction::new(new_num, new_den)
294}
295
296fn next_fraction(fraction: gst::Fraction) -> gst::Fraction {
297 skip_assert_initialized!();
298 let num: i32 = fraction.numer();
299 let den: i32 = fraction.denom();
300 let (new_num: i32, new_den: i32);
301 if num < den {
302 (_, _, new_num, new_den) = farey_neighbours(p:num, q:den);
303 } else {
304 (new_den, new_num, _, _) = farey_neighbours(p:den, q:num);
305 }
306 gst::Fraction::new(new_num, new_den)
307}
308
309#[cfg(test)]
310mod tests {
311 use super::{next_fraction, previous_fraction, VideoCapsBuilder};
312
313 #[test]
314 fn default_encoding() {
315 gst::init().unwrap();
316 let caps = VideoCapsBuilder::new().build();
317 assert_eq!(caps.structure(0).unwrap().name(), "video/x-raw");
318 }
319
320 #[test]
321 fn explicit_encoding() {
322 gst::init().unwrap();
323 let caps = VideoCapsBuilder::for_encoding("video/mpeg").build();
324 assert_eq!(caps.structure(0).unwrap().name(), "video/mpeg");
325 }
326
327 #[test]
328 fn test_0_1_fraction() {
329 gst::init().unwrap();
330 let zero_over_one = gst::Fraction::new(0, 1);
331 let prev = previous_fraction(zero_over_one);
332 assert_eq!(prev.numer(), -1);
333 assert_eq!(prev.denom(), i32::MAX);
334 let next = next_fraction(zero_over_one);
335 assert_eq!(next.numer(), 1);
336 assert_eq!(next.denom(), i32::MAX);
337 }
338
339 #[test]
340 fn test_25_1() {
341 gst::init().unwrap();
342 let twentyfive = gst::Fraction::new(25, 1);
343 let next = next_fraction(twentyfive);
344 //25.000000011641532
345 assert_eq!(next.numer(), 2147483626);
346 assert_eq!(next.denom(), 85899345);
347 let prev = previous_fraction(twentyfive);
348 //24.999999988358468
349 assert_eq!(prev.numer(), 2147483624);
350 assert_eq!(prev.denom(), 85899345);
351 }
352 #[test]
353 fn test_1_25() {
354 gst::init().unwrap();
355 let twentyfive = gst::Fraction::new(1, 25);
356 let next = next_fraction(twentyfive);
357 //0.040000000018626
358 assert_eq!(next.numer(), 85899345);
359 assert_eq!(next.denom(), 2147483624);
360 let prev = previous_fraction(twentyfive);
361 //0.039999999981374
362 assert_eq!(prev.numer(), 85899345);
363 assert_eq!(prev.denom(), 2147483626);
364 }
365}
366