scuffle_ffmpeg/
rational.rs

1use std::num::NonZero;
2
3use rusty_ffmpeg::ffi::AVRational;
4
5/// A rational number.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct Rational {
8    /// Numerator.
9    pub numerator: i32,
10    /// Denominator.
11    pub denominator: NonZero<i32>,
12}
13
14impl Default for Rational {
15    fn default() -> Self {
16        Self::ZERO
17    }
18}
19
20impl Rational {
21    /// The zero rational number.
22    pub const ZERO: Rational = Rational::static_new::<0, 1>();
23
24    /// Create a new rational number.
25    pub const fn new(numerator: i32, denominator: NonZero<i32>) -> Self {
26        Self { numerator, denominator }
27    }
28
29    /// Construct a new rational number at compile time.
30    ///
31    /// # Panics
32    ///
33    /// This will panic if the denominator is 0.
34    pub const fn static_new<const N: i32, const D: i32>() -> Self {
35        const {
36            assert!(D != 0, "denominator is 0");
37        }
38
39        Self::new(N, NonZero::new(D).expect("denominator is 0"))
40    }
41
42    /// Get the rational number as a floating point number.
43    pub fn as_f64(&self) -> f64 {
44        self.numerator as f64 / self.denominator.get() as f64
45    }
46
47    /// Create a new rational number from a floating point number.
48    /// The number might be truncated.
49    pub fn from_f64_rounded(value: f64) -> Self {
50        let denominator = value.abs().recip();
51        let numerator = (value * denominator).round() as i32;
52        Self {
53            numerator,
54            denominator: NonZero::new(denominator as i32).expect("denominator is 0"),
55        }
56    }
57}
58
59impl From<AVRational> for Rational {
60    fn from(rational: AVRational) -> Self {
61        if rational.den == 0 {
62            return Self::ZERO;
63        }
64
65        Self {
66            numerator: rational.num,
67            denominator: NonZero::new(rational.den).expect("denominator is 0"),
68        }
69    }
70}
71
72impl From<Rational> for AVRational {
73    fn from(rational: Rational) -> Self {
74        Self {
75            num: rational.numerator,
76            den: rational.denominator.get(),
77        }
78    }
79}
80
81impl From<i32> for Rational {
82    fn from(value: i32) -> Self {
83        Self {
84            numerator: value,
85            denominator: NonZero::new(1).expect("1 is not 0"),
86        }
87    }
88}
89
90impl From<Rational> for f32 {
91    fn from(rational: Rational) -> Self {
92        rational.numerator as f32 / rational.denominator.get() as f32
93    }
94}
95
96impl From<Rational> for f64 {
97    fn from(rational: Rational) -> Self {
98        rational.numerator as f64 / rational.denominator.get() as f64
99    }
100}