scuffle_ffmpeg/
rational.rs1use std::num::NonZero;
2
3use rusty_ffmpeg::ffi::AVRational;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct Rational {
8 pub numerator: i32,
10 pub denominator: NonZero<i32>,
12}
13
14impl Default for Rational {
15 fn default() -> Self {
16 Self::ZERO
17 }
18}
19
20impl Rational {
21 pub const ONE: Rational = Rational::static_new::<1, 1>();
23 pub const ZERO: Rational = Rational::static_new::<0, 1>();
25
26 pub const fn new(numerator: i32, denominator: NonZero<i32>) -> Self {
28 Self { numerator, denominator }
29 }
30
31 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 pub fn as_f64(&self) -> f64 {
46 self.numerator as f64 / self.denominator.get() as f64
47 }
48
49 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}