use crate::{error, FFramesContext, ResolvedScenesTimeline, ScenesWithAudio, TimeBase};
use std::{
iter::FromIterator,
ops::{Add, Range, Sub},
rc::Rc,
};
#[derive(Debug, Clone)]
pub enum AudioTimestamp<'a> {
Eof,
Frame(usize),
Second(f32),
Time {
minutes: f32,
seconds: f32,
},
DurationOfAudio(&'a str),
__Add(Rc<(AudioTimestamp<'a>, AudioTimestamp<'a>)>),
__Subtract(Rc<(AudioTimestamp<'a>, AudioTimestamp<'a>)>),
}
impl<'a> Add for AudioTimestamp<'a> {
type Output = AudioTimestamp<'a>;
fn add(self, rhs: Self) -> Self::Output {
Self::__Add(Rc::new((self, rhs)))
}
}
impl<'a> Sub for AudioTimestamp<'a> {
type Output = AudioTimestamp<'a>;
fn sub(self, rhs: Self) -> Self::Output {
Self::__Subtract(Rc::new((self, rhs)))
}
}
pub trait AudioTimelineUnit {
fn from_frames(frames: usize, tb: &TimeBase) -> Self;
fn from_usize(val: usize) -> Self;
fn as_usize(&self) -> usize;
fn to_frames(&self, tb: &TimeBase) -> usize;
}
#[derive(PartialEq, PartialOrd, Debug, Copy, Clone)]
pub struct AudioTimelineFrames(usize);
impl AudioTimelineUnit for AudioTimelineFrames {
fn from_frames(frames: usize, _: &TimeBase) -> Self {
AudioTimelineFrames(frames)
}
fn as_usize(&self) -> usize {
self.0
}
fn from_usize(val: usize) -> Self {
AudioTimelineFrames(val)
}
fn to_frames(&self, _: &TimeBase) -> usize {
self.0
}
}
#[derive(PartialEq, PartialOrd, Debug, Clone, Copy)]
pub struct AudioTimelineSamples(pub(crate) usize);
impl AudioTimelineUnit for AudioTimelineSamples {
fn from_frames(frames: usize, tb: &TimeBase) -> Self {
AudioTimelineSamples(frames * tb.sample_rate / tb.fps)
}
fn as_usize(&self) -> usize {
self.0
}
fn from_usize(val: usize) -> Self {
AudioTimelineSamples(val)
}
fn to_frames(&self, tb: &TimeBase) -> usize {
self.0 * tb.fps / tb.sample_rate
}
}
impl AudioTimestamp<'_> {
fn infer_relying_on_dynamic_duration_audio_files<'a>(
&self,
name: &'a str,
) -> Option<Vec<&'a str>> {
match self {
AudioTimestamp::Eof => Some(vec![name]),
AudioTimestamp::DurationOfAudio(_) => Some(vec![name]),
AudioTimestamp::__Subtract(add) | AudioTimestamp::__Add(add) => {
let (a, b) = &**add;
let a = a.infer_relying_on_dynamic_duration_audio_files(name);
let b = b.infer_relying_on_dynamic_duration_audio_files(name);
match (a, b) {
(Some(mut a), Some(mut b)) => {
a.append(&mut b);
Some(a)
}
(Some(a), None) => Some(a),
(None, Some(b)) => Some(b),
(None, None) => None,
}
}
AudioTimestamp::Frame(_) => None,
AudioTimestamp::Second(_) => None,
AudioTimestamp::Time { .. } => None,
}
}
pub(crate) fn to_frames(
&self,
filename: &str,
tb: &TimeBase,
eof_offset: Option<usize>,
resolve_audio_duration_in_frames: &impl Fn(&str) -> error::Result<usize>,
) -> error::Result<usize> {
Ok(match self {
AudioTimestamp::Frame(frame) => *frame,
AudioTimestamp::Second(seconds) => (*seconds * tb.fps as f32) as usize,
AudioTimestamp::Eof => {
resolve_audio_duration_in_frames(filename)? + eof_offset.unwrap_or(0)
}
AudioTimestamp::DurationOfAudio(filename) => {
resolve_audio_duration_in_frames(filename)?
}
AudioTimestamp::Time {
minutes: minute,
seconds: second,
} => ((*minute * 60.0 + *second) * tb.fps as f32) as usize,
AudioTimestamp::__Add(add) => {
let (a, b) = &**add;
a.to_frames(filename, tb, eof_offset, resolve_audio_duration_in_frames)?
+ b.to_frames(filename, tb, eof_offset, resolve_audio_duration_in_frames)?
}
AudioTimestamp::__Subtract(add) => {
let (a, b) = &**add;
a.to_frames(filename, tb, eof_offset, resolve_audio_duration_in_frames)?
- b.to_frames(filename, tb, eof_offset, resolve_audio_duration_in_frames)?
}
})
}
pub(crate) fn to_unit<TUnit: AudioTimelineUnit>(
&self,
filename: &str,
tb: &TimeBase,
eof_offset: Option<usize>,
resolve_audio_duration_in_frames: impl Fn(&str) -> error::Result<usize>,
) -> error::Result<TUnit> {
let frames = self.to_frames(filename, tb, eof_offset, &resolve_audio_duration_in_frames)?;
Ok(TUnit::from_frames(frames, tb))
}
}
type AudioDuration<'a> = Range<AudioTimestamp<'a>>;
#[derive(Debug, Clone)]
pub struct AudioMap<'a>(pub Option<Vec<(&'a str, AudioDuration<'a>)>>);
type AudioTimeline<TUnit> = Vec<(String, Range<TUnit>)>;
#[derive(Debug)]
pub struct ResolvedAudioMap<TUnit: AudioTimelineUnit>(pub(crate) AudioTimeline<TUnit>);
impl<TUnit: AudioTimelineUnit + Copy> ResolvedAudioMap<TUnit> {
pub(crate) fn round_max_duration(&mut self, max_duration: TUnit) {
let max_duration_usize = max_duration.as_usize();
for (_, range) in self.0.iter_mut() {
if range.end.as_usize() > max_duration_usize {
range.end = max_duration;
}
}
}
pub fn calc_stream_duration(&self) -> usize {
self.0
.iter()
.map(|(_, range)| (range.start.as_usize() + range.end.as_usize()))
.max()
.unwrap_or(0)
}
}
impl<'a> AudioMap<'a> {
pub fn none() -> Self {
AudioMap(None)
}
pub fn unstable_flatten_with_scenes(self, scene_audios: &'a ScenesWithAudio) -> Self {
if self.0.is_none() && scene_audios.0.is_none() {
return Self(None);
}
let mut map = self.0.unwrap_or_default();
if let Some(scenes) = scene_audios.0.as_ref() {
for audio_map in scenes.iter() {
if let Some(scene_audio_map) = audio_map.audio_map.0.as_ref() {
map.extend(scene_audio_map.iter().cloned())
}
}
}
Self(Some(map))
}
pub fn used_audio_files<T: FromIterator<&'a str>>(&'a self) -> Option<T> {
self.0.as_ref().map(|map| {
map.iter()
.filter_map(|(filename, range)| {
let start = range
.start
.infer_relying_on_dynamic_duration_audio_files(filename);
let end = range
.end
.infer_relying_on_dynamic_duration_audio_files(filename);
match (start, end) {
(Some(mut start), Some(mut end)) => {
start.append(&mut end);
Some(start)
}
(Some(start), None) => Some(start),
(None, Some(end)) => Some(end),
(None, None) => None,
}
})
.flatten()
.collect::<T>()
})
}
pub(crate) fn resolve<TUnit: AudioTimelineUnit + std::fmt::Debug>(
&self,
offset: TUnit,
tb: &TimeBase,
resolve_audio_duration_in_frames: impl Fn(&str) -> error::Result<usize>,
) -> error::Result<Option<AudioTimeline<TUnit>>> {
let offset = offset.as_usize();
self.0
.as_ref()
.map(|file_durations| {
file_durations
.iter()
.map(|(filename, range)| {
let start_unit = range.start.to_unit::<TUnit>(
filename,
tb,
None,
&resolve_audio_duration_in_frames,
)?;
let end_unit = range.end.to_unit::<TUnit>(
filename,
tb,
Some(start_unit.to_frames(tb)),
&resolve_audio_duration_in_frames,
)?;
Ok((
filename.to_string(),
TUnit::from_usize(start_unit.as_usize() + offset)
..TUnit::from_usize(end_unit.as_usize() + offset),
))
})
.collect::<error::Result<Vec<_>>>()
})
.transpose()
}
pub fn resolve_with_scenes<TUnit: AudioTimelineUnit + std::fmt::Debug>(
&'a self,
scenes: Option<&ResolvedScenesTimeline>,
tb: &TimeBase,
resolve_audio_duration_in_frames: impl Fn(&str) -> error::Result<usize>,
) -> error::Result<Option<ResolvedAudioMap<TUnit>>> {
let global_resolved_map =
self.resolve(TUnit::from_usize(0), tb, &resolve_audio_duration_in_frames)?;
let scenes_resolved_map = scenes
.map(|scenes| -> crate::error::Result<_> {
Ok(scenes
.iter()
.map(|(range, _, scene)| {
scene.audio().resolve(
TUnit::from_frames(range.start, tb),
tb,
&resolve_audio_duration_in_frames,
)
})
.collect::<error::Result<Vec<_>>>()?
.into_iter()
.flatten()
.flatten()
.collect::<Vec<_>>())
})
.transpose()?;
Ok(match (scenes_resolved_map, global_resolved_map) {
(Some(scenes_resolved_map), Some(mut global_resolved_map)) => {
global_resolved_map.extend(scenes_resolved_map);
Some(ResolvedAudioMap(global_resolved_map))
}
(Some(scene_maps), None) => Some(ResolvedAudioMap(scene_maps)),
(None, Some(global_maps)) => Some(ResolvedAudioMap(global_maps)),
(None, None) => None,
})
}
pub fn resolve_with_ctx<'media: 'a, TUnit: AudioTimelineUnit + std::fmt::Debug>(
&'a self,
ctx: &'a FFramesContext<'a, 'media>,
) -> error::Result<Option<ResolvedAudioMap<TUnit>>> {
self.resolve_with_scenes(ctx.scenes, &ctx.time_base, |filename| {
let audio_data = ctx.get_audio(filename).ok_or_else(|| {
crate::error::FFramesError::RequiredAudioNotFound(filename.to_string())
})?;
Ok(audio_data.duration_in_frames(&ctx.time_base))
})
}
}
impl<'a, const N: usize> From<[(&'a str, AudioDuration<'a>); N]> for AudioMap<'a> {
fn from(arr: [(&'a str, AudioDuration<'a>); N]) -> Self {
AudioMap(Some(arr.to_vec()))
}
}
impl<'a> FromIterator<(&'a str, AudioDuration<'a>)> for AudioMap<'a> {
fn from_iter<T: IntoIterator<Item = (&'a str, AudioDuration<'a>)>>(iter: T) -> Self {
AudioMap(Some(iter.into_iter().collect()))
}
}