mod gif;
pub use gif::GifEncoder;
use crate::core::{PlottingError, Result};
use std::path::Path;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Quality {
Low,
#[default]
Medium,
High,
Lossless,
}
impl Quality {
#[cfg(feature = "animation-video")]
pub fn to_rav1e_speed(self) -> u8 {
match self {
Quality::Low => 10,
Quality::Medium => 6,
Quality::High => 2,
Quality::Lossless => 0,
}
}
pub fn to_gif_speed(self) -> i32 {
match self {
Quality::Low => 30,
Quality::Medium => 10,
Quality::High => 1,
Quality::Lossless => 1,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum Codec {
Gif,
Av1,
#[default]
Auto,
}
impl Codec {
pub fn from_extension(ext: &str) -> Option<Self> {
match ext.to_lowercase().as_str() {
"gif" => Some(Codec::Gif),
"mp4" | "webm" | "mkv" => Some(Codec::Av1),
_ => None,
}
}
pub fn default_extension(&self) -> &'static str {
match self {
Codec::Gif => "gif",
Codec::Av1 => "mp4",
Codec::Auto => "gif",
}
}
}
pub trait Encoder: Send {
fn init(&mut self, width: u32, height: u32) -> Result<()>;
fn encode_frame(&mut self, rgb_data: &[u8], timestamp_ms: u64) -> Result<()>;
fn finalize(self: Box<Self>) -> Result<()>;
fn extensions(&self) -> &[&str];
fn supports_extension(&self, ext: &str) -> bool {
let ext_lower = ext.to_lowercase();
self.extensions()
.iter()
.any(|e| e.to_lowercase() == ext_lower)
}
}
pub fn create_encoder(path: &Path, quality: Quality) -> Result<Box<dyn Encoder>> {
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("gif");
match Codec::from_extension(ext) {
Some(Codec::Gif) | None => Ok(Box::new(GifEncoder::new(path, quality)?)),
Some(Codec::Av1) => {
#[cfg(feature = "animation-video")]
{
Err(PlottingError::RenderError(
"AV1 encoder not yet implemented".into(),
))
}
#[cfg(not(feature = "animation-video"))]
{
Err(PlottingError::RenderError(
"AV1 encoding requires 'animation-video' feature".into(),
))
}
}
Some(Codec::Auto) => {
Ok(Box::new(GifEncoder::new(path, quality)?))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_codec_from_extension() {
assert_eq!(Codec::from_extension("gif"), Some(Codec::Gif));
assert_eq!(Codec::from_extension("GIF"), Some(Codec::Gif));
assert_eq!(Codec::from_extension("mp4"), Some(Codec::Av1));
assert_eq!(Codec::from_extension("webm"), Some(Codec::Av1));
assert_eq!(Codec::from_extension("unknown"), None);
}
#[test]
fn test_quality_gif_speed() {
assert_eq!(Quality::Low.to_gif_speed(), 30);
assert_eq!(Quality::Medium.to_gif_speed(), 10);
assert_eq!(Quality::High.to_gif_speed(), 1);
}
#[test]
fn test_codec_default_extension() {
assert_eq!(Codec::Gif.default_extension(), "gif");
assert_eq!(Codec::Av1.default_extension(), "mp4");
}
}