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 one rational number.
22    pub const ONE: Rational = Rational::static_new::<1, 1>();
23    /// The zero rational number.
24    pub const ZERO: Rational = Rational::static_new::<0, 1>();
25
26    /// Create a new rational number.
27    pub const fn new(numerator: i32, denominator: NonZero<i32>) -> Self {
28        Self { numerator, denominator }
29    }
30
31    /// Construct a new rational number at compile time.
32    ///
33    /// # Panics
34    ///
35    /// This will panic if the denominator is 0.
36    pub const fn static_new<const N: i32, const D: i32>() -> Self {
37        const {
38            assert!(D != 0, "denominator is 0");
39        }
40
41        Self::new(N, NonZero::new(D).expect("denominator is 0"))
42    }
43
44    /// Get the rational number as a floating point number.
45    pub fn as_f64(&self) -> f64 {
46        self.numerator as f64 / self.denominator.get() as f64
47    }
48
49    /// Create a new rational number from a floating point number.
50    /// The number might be truncated.
51    pub fn from_f64_rounded(value: f64) -> Self {
52        let denominator = value.abs().recip();
53        let numerator = (value * denominator).round() as i32;
54        Self {
55            numerator,
56            denominator: NonZero::new(denominator as i32).expect("denominator is 0"),
57        }
58    }
59}
60
61impl From<AVRational> for Rational {
62    fn from(rational: AVRational) -> Self {
63        if rational.den == 0 {
64            return Self::ZERO;
65        }
66
67        Self {
68            numerator: rational.num,
69            denominator: NonZero::new(rational.den).expect("denominator is 0"),
70        }
71    }
72}
73
74impl From<Rational> for AVRational {
75    fn from(rational: Rational) -> Self {
76        Self {
77            num: rational.numerator,
78            den: rational.denominator.get(),
79        }
80    }
81}
82
83impl From<i32> for Rational {
84    fn from(value: i32) -> Self {
85        Self {
86            numerator: value,
87            denominator: NonZero::new(1).expect("1 is not 0"),
88        }
89    }
90}
91
92impl From<Rational> for f32 {
93    fn from(rational: Rational) -> Self {
94        rational.numerator as f32 / rational.denominator.get() as f32
95    }
96}
97
98impl From<Rational> for f64 {
99    fn from(rational: Rational) -> Self {
100        rational.numerator as f64 / rational.denominator.get() as f64
101    }
102}