use alpha2_time::FrameRate;
use serde::{Deserialize, Serialize};
use crate::MediaKind;
pub const MAX_SEQUENCE_FRAMES: usize = 100_000;
pub const MAX_PROVIDER_DOCUMENT_BYTES: usize = 256 * 1024 * 1024;
pub const MAX_LOTTIE_DOCUMENT_BYTES: usize = 16 * 1024 * 1024;
pub const MAX_SVG_DOCUMENT_BYTES: usize = 32 * 1024 * 1024;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(tag = "format", rename_all = "snake_case", from = "ProviderSourceWire")]
#[non_exhaustive]
pub enum ProviderSource {
Svg,
AnimatedImage,
Lottie {
#[serde(default, skip_serializing_if = "Option::is_none")]
animation: Option<String>,
},
ImageSequence {
frames: Vec<Option<String>>,
frame_rate: FrameRate,
#[serde(default)]
start_number: u64,
#[serde(default)]
missing: MissingFrames,
#[serde(default, skip_serializing_if = "Option::is_none")]
primaries: Option<LinearPrimaries>,
},
Exr {
#[serde(default, skip_serializing_if = "Option::is_none")]
primaries: Option<LinearPrimaries>,
},
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[cfg_attr(feature = "schema", schemars(rename = "ProviderSource"))]
#[serde(tag = "format", rename_all = "snake_case", deny_unknown_fields)]
pub(crate) enum ProviderSourceWire {
Svg {},
AnimatedImage {},
Lottie {
#[serde(default, skip_serializing_if = "Option::is_none")]
animation: Option<String>,
},
ImageSequence {
frames: Vec<Option<String>>,
frame_rate: FrameRate,
#[serde(default)]
start_number: u64,
#[serde(default)]
missing: MissingFrames,
#[serde(default, skip_serializing_if = "Option::is_none")]
primaries: Option<LinearPrimaries>,
},
Exr {
#[serde(default, skip_serializing_if = "Option::is_none")]
primaries: Option<LinearPrimaries>,
},
}
impl From<ProviderSourceWire> for ProviderSource {
fn from(wire: ProviderSourceWire) -> Self {
match wire {
ProviderSourceWire::Svg {} => Self::Svg,
ProviderSourceWire::AnimatedImage {} => Self::AnimatedImage,
ProviderSourceWire::Lottie { animation } => Self::Lottie { animation },
ProviderSourceWire::ImageSequence {
frames,
frame_rate,
start_number,
missing,
primaries,
} => Self::ImageSequence {
frames,
frame_rate,
start_number,
missing,
primaries,
},
ProviderSourceWire::Exr { primaries } => Self::Exr { primaries },
}
}
}
#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum LinearPrimaries {
Bt709,
Bt2020,
P3D65,
AcesAp0,
AcesAp1,
}
#[derive(Copy, Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum MissingFrames {
#[default]
Reject,
Hold,
Blank,
}
impl ProviderSource {
#[must_use]
pub const fn kind(&self) -> MediaKind {
match self {
Self::Svg | Self::Exr { .. } => MediaKind::Image,
Self::AnimatedImage | Self::Lottie { .. } | Self::ImageSequence { .. } => {
MediaKind::Video
}
}
}
#[must_use]
pub fn is_valid(&self) -> bool {
if let Self::Lottie {
animation: Some(animation),
} = self
{
return !animation.is_empty();
}
let Self::ImageSequence {
frames, missing, ..
} = self
else {
return true;
};
let present = frames.iter().flatten().collect::<Vec<_>>();
let unique = present
.iter()
.collect::<std::collections::HashSet<_>>()
.len()
== present.len();
!frames.is_empty()
&& frames.len() <= MAX_SEQUENCE_FRAMES
&& frames[0].is_some()
&& unique
&& present.iter().all(|locator| !locator.is_empty())
&& (present.len() == frames.len() || *missing != MissingFrames::Reject)
}
}