use super::error::FrameExportError;
use super::frame::VideoFrame;
use crate::core::scheduler::ffmpeg_scheduler::{FfmpegScheduler, Running};
use crate::error::Error;
use crossbeam_channel::Receiver;
fn map_terminal_error(e: Error) -> Error {
match e {
Error::FrameFilterProcess(boxed) => match boxed.downcast::<FrameExportError>() {
Ok(typed) => Error::FrameExport(*typed),
Err(other) => Error::FrameFilterProcess(other),
},
other => other,
}
}
pub struct FrameIter {
rx: Option<Receiver<VideoFrame>>,
scheduler: Option<FfmpegScheduler<Running>>,
terminated: bool,
}
impl FrameIter {
pub(crate) fn new(rx: Receiver<VideoFrame>, scheduler: FfmpegScheduler<Running>) -> Self {
Self {
rx: Some(rx),
scheduler: Some(scheduler),
terminated: false,
}
}
fn finish(&mut self) -> Option<Result<VideoFrame, Error>> {
self.terminated = true;
self.rx = None;
match self.scheduler.take() {
Some(scheduler) => match scheduler.wait() {
Ok(()) => None,
Err(e) => Some(Err(map_terminal_error(e))),
},
None => None,
}
}
}
impl Iterator for FrameIter {
type Item = Result<VideoFrame, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.terminated {
return None;
}
let recv = match self.rx.as_ref() {
Some(rx) => rx.recv(),
None => return self.finish(),
};
match recv {
Ok(frame) => Some(Ok(frame)),
Err(_) => self.finish(),
}
}
}
impl std::iter::FusedIterator for FrameIter {}
impl Drop for FrameIter {
fn drop(&mut self) {
self.rx = None;
if let Some(scheduler) = self.scheduler.take() {
scheduler.abort();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn typed_export_error_is_unwrapped_from_filter_process() {
let boxed: Box<dyn std::error::Error + Send + Sync> =
Box::new(FrameExportError::HdrRequiresToneMapping);
let mapped = map_terminal_error(Error::FrameFilterProcess(boxed));
assert!(matches!(
mapped,
Error::FrameExport(FrameExportError::HdrRequiresToneMapping)
));
}
#[test]
fn foreign_filter_process_error_passes_through() {
let boxed: Box<dyn std::error::Error + Send + Sync> = "some other failure".into();
let mapped = map_terminal_error(Error::FrameFilterProcess(boxed));
assert!(matches!(mapped, Error::FrameFilterProcess(_)));
}
#[test]
fn non_filter_errors_pass_through() {
let mapped = map_terminal_error(Error::EOF);
assert!(matches!(mapped, Error::EOF));
}
}