use crate::{
audio::utils::has_audio,
common::{errors::*, utils::*},
downloader::youtube,
};
use either::Either;
use gif;
use image::{io::Reader as ImageReader, DynamicImage};
use opencv::{prelude::*, videoio::VideoCapture};
use std::{fs::File, path::Path};
use tempfile::TempPath;
use url::Url;
pub enum FrameIterator {
Image(Option<DynamicImage>),
Video(VideoCapture),
AnimatedGif {
frames: Vec<DynamicImage>,
current_frame: usize,
},
}
pub struct MediaData {
pub frame_iter: FrameIterator,
pub fps: Option<f64>,
pub audio_path: Option<Either<TempPath, String>>,
}
impl Iterator for FrameIterator {
type Item = DynamicImage;
fn next(&mut self) -> Option<Self::Item> {
match self {
FrameIterator::Image(ref mut img) => img.take(),
FrameIterator::Video(ref mut video) => capture_video_frame(video),
FrameIterator::AnimatedGif {
ref frames,
ref mut current_frame,
} => {
let frame = frames.get(*current_frame).cloned();
*current_frame = (*current_frame + 1) % frames.len();
frame
}
}
}
}
impl FrameIterator {
pub fn skip_frames(&mut self, n: usize) {
match self {
FrameIterator::Image(_) => {
}
FrameIterator::Video(ref mut video) => {
for _ in 0..n {
let mut frame = Mat::default();
if !video.read(&mut frame).unwrap_or(false) || frame.empty() {
break;
}
}
}
FrameIterator::AnimatedGif {
ref mut current_frame,
frames,
} => {
*current_frame = (*current_frame + n) % frames.len();
}
}
}
}
pub fn open_media(path: String) -> Result<MediaData, MyError> {
let p = Path::new(&path);
let x = Path::new(p).to_owned();
let path = x.as_path(); let ext = path.extension().and_then(std::ffi::OsStr::to_str);
if let Ok(url) = Url::parse(path.to_str().unwrap_or("")) {
if let Some(domain) = url.domain() {
if domain.ends_with("youtube.com") || domain.ends_with("youtu.be") {
let video = youtube::download_video(path.to_str().unwrap_or(""))?;
let fps = extract_fps(video.as_os_str().to_str().unwrap_or(""));
let video_open = open_video(&video)?;
return Ok(MediaData {
frame_iter: video_open,
fps,
audio_path: Some(Either::Left(video)),
});
}
}
}
let fps = extract_fps(path.as_os_str().to_str().unwrap_or(""));
let audio = has_audio(path.as_os_str().to_str().unwrap_or(""))?;
let audio_track = if audio {
Some(Either::Right(path.to_str().unwrap_or("").to_string()))
} else {
None
};
match ext {
Some("png") | Some("bmp") | Some("ico") | Some("tif") | Some("tiff") | Some("jpg")
| Some("jpeg") => Ok(MediaData {
frame_iter: open_image(path)?,
fps: None,
audio_path: None,
}),
Some("mp4") | Some("avi") | Some("webm") | Some("mkv") | Some("mov") | Some("flv")
| Some("ogg") => Ok(MediaData {
frame_iter: open_video(path)?,
fps,
audio_path: audio_track,
}),
Some("gif") => Ok(MediaData {
frame_iter: open_gif(path)?,
fps: None,
audio_path: None,
}),
_ => Ok(MediaData {
frame_iter: open_video(path)?,
fps,
audio_path: audio_track,
}),
}
}
fn capture_video_frame(video: &mut VideoCapture) -> Option<DynamicImage> {
let mut frame = Mat::default();
if video.read(&mut frame).unwrap_or(false) && !frame.empty() {
mat_to_dynamic_image(&frame)
} else {
None
}
}
fn open_image(path: &Path) -> Result<FrameIterator, MyError> {
let img = ImageReader::open(path)?.decode().map_err(|e| {
MyError::Application(format!("{error}: {e:?}", error = ERROR_DECODING_IMAGE))
})?;
Ok(FrameIterator::Image(Some(img)))
}
fn open_video(path: &Path) -> Result<FrameIterator, MyError> {
let video = VideoCapture::from_file(
path.to_str().expect(ERROR_OPENING_VIDEO),
opencv::videoio::CAP_ANY,
)?;
if video.is_opened()? {
Ok(FrameIterator::Video(video))
} else {
Err(MyError::Application(ERROR_OPENING_VIDEO.to_string()))
}
}
fn open_gif(path: &Path) -> Result<FrameIterator, MyError> {
let file = File::open(path)
.map_err(|e| MyError::Application(format!("{error}: {e:?}", error = ERROR_OPENING_GIF)))?;
let mut options = gif::DecodeOptions::new();
options.set_color_output(gif::ColorOutput::RGBA);
let mut decoder = options.read_info(file).map_err(|e| {
MyError::Application(format!("{error}: {e:?}", error = ERROR_READING_GIF_HEADER))
})?;
let mut frames = Vec::new();
while let Ok(Some(frame)) = decoder.read_next_frame() {
let buffer = frame.buffer.clone();
if let Some(image) = image::RgbaImage::from_raw(
decoder.width() as u32,
decoder.height() as u32,
buffer.to_vec(),
) {
frames.push(DynamicImage::ImageRgba8(image));
} else {
}
}
Ok(FrameIterator::AnimatedGif {
frames,
current_frame: 0,
})
}