extern crate ffmpeg_next as ffmpeg;
use std::time::Duration;
use ffmpeg::{
Rational as AvRational,
util::mathematics::rescale::Rescale,
};
#[derive(Clone, Debug)]
pub struct Time {
time: Option<i64>,
time_base: AvRational,
}
impl Time {
pub fn zero() -> Self {
Time {
time: Some(0),
time_base: (1, 90000).into(),
}
}
pub(crate) fn new(
time: Option<i64>,
time_base: AvRational,
) -> Time {
Self {
time,
time_base,
}
}
pub fn has_value(&self) -> bool {
self.time.is_some()
}
pub fn aligned_with(&self, rhs: &Time) -> Aligned {
Aligned {
lhs: self.time.clone(),
rhs: rhs
.time
.map(|rhs_time| rhs_time.rescale(rhs.time_base, self.time_base)),
time_base: self.time_base,
}
}
pub(crate) fn aligned(&self, time_base: AvRational) -> Time {
Time {
time: self
.time
.map(|time| time.rescale(self.time_base, time_base)),
time_base,
}
}
pub(crate) fn into_value(self) -> Option<i64> {
self.time
}
}
impl From<Duration> for Time {
fn from(duration: Duration) -> Self {
const TIMEBASE: (i32, i32) = (1, 90000);
let time = duration.as_secs_f64() * (TIMEBASE.1 as f64);
let time = time.round() as i64;
Self {
time: Some(time),
time_base: TIMEBASE.into(),
}
}
}
impl From<Time> for Duration {
fn from(timestamp: Time) -> Self {
if let Some(offset) = timestamp.time {
let micros = offset
.rescale(timestamp.time_base, AvRational::new(1, 1_000_000))
.max(0) as u64;
Duration::from_micros(micros)
} else {
Duration::ZERO
}
}
}
pub struct Aligned {
lhs: Option<i64>,
rhs: Option<i64>,
time_base: AvRational,
}
impl Aligned {
pub fn add(self) -> Time {
self.apply(|lhs, rhs| lhs + rhs)
}
pub fn subtract(self) -> Time {
self.apply(|lhs, rhs| lhs - rhs)
}
fn apply<F>(self, f: F) -> Time
where
F: FnOnce(i64, i64) -> i64
{
match (self.lhs, self.rhs) {
(Some(lhs_time), Some(rhs_time)) => {
Time {
time: Some(f(lhs_time, rhs_time)),
time_base: self.time_base,
}
},
_ => {
Time {
time: None,
time_base: self.time_base,
}
}
}
}
}