use crate::dictionary::Auid;
use crate::structured_storage::StorageReader;
use crate::{EssenceData, Result};
use serde::{Deserialize, Serialize};
use std::io::{Read, Seek};
use std::path::PathBuf;
use uuid::Uuid;
#[derive(Debug, Clone)]
pub enum EssenceDescriptor {
File(FileDescriptor),
Physical(PhysicalDescriptor),
DigitalImage(DigitalImageDescriptor),
Sound(SoundDescriptor),
Data(DataEssenceDescriptor),
Multiple(MultipleDescriptor),
}
impl EssenceDescriptor {
#[must_use]
pub fn sample_rate(&self) -> Option<(u32, u32)> {
match self {
EssenceDescriptor::DigitalImage(d) => Some(d.sample_rate),
EssenceDescriptor::Sound(d) => Some(d.audio_sample_rate),
_ => None,
}
}
#[must_use]
pub fn essence_container(&self) -> Option<Auid> {
match self {
EssenceDescriptor::File(d) => Some(d.essence_container),
EssenceDescriptor::DigitalImage(d) => Some(d.file.essence_container),
EssenceDescriptor::Sound(d) => Some(d.file.essence_container),
_ => None,
}
}
#[must_use]
pub fn codec(&self) -> Option<Auid> {
match self {
EssenceDescriptor::File(d) => d.codec,
EssenceDescriptor::DigitalImage(d) => d.file.codec,
EssenceDescriptor::Sound(d) => d.file.codec,
_ => None,
}
}
#[must_use]
pub fn locators(&self) -> Vec<&Locator> {
match self {
EssenceDescriptor::File(d) => &d.locators,
EssenceDescriptor::DigitalImage(d) => &d.file.locators,
EssenceDescriptor::Sound(d) => &d.file.locators,
_ => return Vec::new(),
}
.iter()
.collect()
}
}
#[derive(Debug, Clone)]
pub struct FileDescriptor {
pub linked_track_id: Option<u32>,
pub sample_rate: (u32, u32),
pub length: Option<i64>,
pub essence_container: Auid,
pub codec: Option<Auid>,
pub locators: Vec<Locator>,
}
impl FileDescriptor {
#[must_use]
pub fn new(sample_rate: (u32, u32), essence_container: Auid) -> Self {
Self {
linked_track_id: None,
sample_rate,
length: None,
essence_container,
codec: None,
locators: Vec::new(),
}
}
pub fn add_locator(&mut self, locator: Locator) {
self.locators.push(locator);
}
}
#[derive(Debug, Clone)]
pub struct PhysicalDescriptor {
pub file: FileDescriptor,
}
#[derive(Debug, Clone)]
pub struct DigitalImageDescriptor {
pub file: FileDescriptor,
pub sample_rate: (u32, u32),
pub stored_width: u32,
pub stored_height: u32,
pub sampled_width: Option<u32>,
pub sampled_height: Option<u32>,
pub sampled_x_offset: Option<u32>,
pub sampled_y_offset: Option<u32>,
pub display_width: Option<u32>,
pub display_height: Option<u32>,
pub display_x_offset: Option<u32>,
pub display_y_offset: Option<u32>,
pub aspect_ratio: Option<(u32, u32)>,
pub frame_layout: FrameLayout,
pub video_line_map: Option<Vec<u32>>,
pub alpha_transparency: Option<AlphaTransparency>,
pub component_depth: Option<u32>,
pub horizontal_subsampling: Option<u32>,
pub vertical_subsampling: Option<u32>,
pub color_siting: Option<ColorSiting>,
pub image_alignment_factor: Option<u32>,
pub compression: Option<Auid>,
}
impl DigitalImageDescriptor {
#[must_use]
pub fn new(
sample_rate: (u32, u32),
stored_width: u32,
stored_height: u32,
essence_container: Auid,
) -> Self {
Self {
file: FileDescriptor::new(sample_rate, essence_container),
sample_rate,
stored_width,
stored_height,
sampled_width: None,
sampled_height: None,
sampled_x_offset: None,
sampled_y_offset: None,
display_width: None,
display_height: None,
display_x_offset: None,
display_y_offset: None,
aspect_ratio: None,
frame_layout: FrameLayout::FullFrame,
video_line_map: None,
alpha_transparency: None,
component_depth: None,
horizontal_subsampling: None,
vertical_subsampling: None,
color_siting: None,
image_alignment_factor: None,
compression: None,
}
}
#[must_use]
pub fn with_aspect_ratio(mut self, horizontal: u32, vertical: u32) -> Self {
self.aspect_ratio = Some((horizontal, vertical));
self
}
#[must_use]
pub fn with_frame_layout(mut self, layout: FrameLayout) -> Self {
self.frame_layout = layout;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum FrameLayout {
FullFrame,
SeparateFields,
SingleField,
MixedFields,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AlphaTransparency {
None,
MinValueTransparent,
MaxValueTransparent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ColorSiting {
CoSiting,
Averaging,
ThreeTap,
Quincunx,
Rec601,
LineAlternating,
VerticalMidpoint,
Unknown,
}
#[derive(Debug, Clone)]
pub struct SoundDescriptor {
pub file: FileDescriptor,
pub audio_sample_rate: (u32, u32),
pub locked: bool,
pub audio_ref_level: Option<i8>,
pub electrospatial_formulation: Option<ElectrospatialFormulation>,
pub channel_count: u32,
pub quantization_bits: u32,
pub dial_norm: Option<i8>,
pub compression: Option<Auid>,
}
impl SoundDescriptor {
#[must_use]
pub fn new(
audio_sample_rate: (u32, u32),
channel_count: u32,
quantization_bits: u32,
essence_container: Auid,
) -> Self {
Self {
file: FileDescriptor::new(audio_sample_rate, essence_container),
audio_sample_rate,
locked: false,
audio_ref_level: None,
electrospatial_formulation: None,
channel_count,
quantization_bits,
dial_norm: None,
compression: None,
}
}
#[must_use]
pub fn with_locked(mut self, locked: bool) -> Self {
self.locked = locked;
self
}
#[must_use]
pub fn with_audio_ref_level(mut self, level: i8) -> Self {
self.audio_ref_level = Some(level);
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ElectrospatialFormulation {
Default,
TwoChannelMode,
SingleChannelMode,
PrimarySecondaryMode,
StereophoniccMode,
SingleChannelDoubleSampling,
StereoLeftChannelDoubleSampling,
StereoRightChannelDoubleSampling,
MultiChannelMode,
}
#[derive(Debug, Clone)]
pub struct DataEssenceDescriptor {
pub file: FileDescriptor,
pub data_essence_coding: Option<Auid>,
}
#[derive(Debug, Clone)]
pub struct MultipleDescriptor {
pub file: FileDescriptor,
pub sub_descriptors: Vec<EssenceDescriptor>,
}
impl MultipleDescriptor {
#[must_use]
pub fn new(essence_container: Auid) -> Self {
Self {
file: FileDescriptor::new((1, 1), essence_container),
sub_descriptors: Vec::new(),
}
}
pub fn add_sub_descriptor(&mut self, descriptor: EssenceDescriptor) {
self.sub_descriptors.push(descriptor);
}
}
#[derive(Debug, Clone)]
pub enum Locator {
File(FileLocator),
Network(NetworkLocator),
Text(TextLocator),
}
#[derive(Debug, Clone)]
pub struct FileLocator {
pub path: PathBuf,
}
impl FileLocator {
pub fn new(path: impl Into<PathBuf>) -> Self {
Self { path: path.into() }
}
}
#[derive(Debug, Clone)]
pub struct NetworkLocator {
pub url: String,
}
impl NetworkLocator {
pub fn new(url: impl Into<String>) -> Self {
Self { url: url.into() }
}
}
#[derive(Debug, Clone)]
pub struct TextLocator {
pub text: String,
}
impl TextLocator {
pub fn new(text: impl Into<String>) -> Self {
Self { text: text.into() }
}
}
#[derive(Debug, Clone)]
pub enum EssenceReference {
Embedded {
mob_id: Uuid,
slot_id: u32,
},
External {
path: PathBuf,
mob_id: Option<Uuid>,
},
Network {
url: String,
mob_id: Option<Uuid>,
},
}
impl EssenceReference {
#[must_use]
pub fn is_embedded(&self) -> bool {
matches!(self, EssenceReference::Embedded { .. })
}
#[must_use]
pub fn is_external(&self) -> bool {
matches!(
self,
EssenceReference::External { .. } | EssenceReference::Network { .. }
)
}
#[must_use]
pub fn mob_id(&self) -> Option<Uuid> {
match self {
EssenceReference::Embedded { mob_id, .. } => Some(*mob_id),
EssenceReference::External { mob_id, .. } => *mob_id,
EssenceReference::Network { mob_id, .. } => *mob_id,
}
}
}
pub struct EssenceAccess {
descriptor: EssenceDescriptor,
reference: EssenceReference,
}
impl EssenceAccess {
#[must_use]
pub fn new(descriptor: EssenceDescriptor, reference: EssenceReference) -> Self {
Self {
descriptor,
reference,
}
}
#[must_use]
pub fn descriptor(&self) -> &EssenceDescriptor {
&self.descriptor
}
#[must_use]
pub fn reference(&self) -> &EssenceReference {
&self.reference
}
#[must_use]
pub fn is_embedded(&self) -> bool {
self.reference.is_embedded()
}
#[must_use]
pub fn is_external(&self) -> bool {
self.reference.is_external()
}
}
pub fn read_essence_data<R: Read + Seek>(
_storage: &mut StorageReader<R>,
) -> Result<Vec<EssenceData>> {
Ok(Vec::new())
}
pub struct CodecRegistry {
codecs: std::collections::HashMap<Auid, CodecInfo>,
}
impl CodecRegistry {
#[must_use]
pub fn new() -> Self {
let mut registry = Self {
codecs: std::collections::HashMap::new(),
};
registry.add_standard_codecs();
registry
}
pub fn add_codec(&mut self, auid: Auid, info: CodecInfo) {
self.codecs.insert(auid, info);
}
#[must_use]
pub fn get_codec(&self, auid: &Auid) -> Option<&CodecInfo> {
self.codecs.get(auid)
}
fn add_standard_codecs(&mut self) {
let codecs = vec![
(
Auid::null(),
CodecInfo::new("Uncompressed", CodecType::Video),
),
];
for (auid, info) in codecs {
self.add_codec(auid, info);
}
}
}
impl Default for CodecRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct CodecInfo {
pub name: String,
pub codec_type: CodecType,
pub description: Option<String>,
}
impl CodecInfo {
pub fn new(name: impl Into<String>, codec_type: CodecType) -> Self {
Self {
name: name.into(),
codec_type,
description: None,
}
}
pub fn with_description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CodecType {
Video,
Audio,
Data,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_descriptor() {
let mut desc = FileDescriptor::new((25, 1), Auid::null());
assert_eq!(desc.sample_rate, (25, 1));
desc.add_locator(Locator::File(FileLocator::new("/path/to/file.mov")));
assert_eq!(desc.locators.len(), 1);
}
#[test]
fn test_digital_image_descriptor() {
let desc = DigitalImageDescriptor::new((25, 1), 1920, 1080, Auid::null())
.with_aspect_ratio(16, 9)
.with_frame_layout(FrameLayout::FullFrame);
assert_eq!(desc.stored_width, 1920);
assert_eq!(desc.stored_height, 1080);
assert_eq!(desc.aspect_ratio, Some((16, 9)));
assert_eq!(desc.frame_layout, FrameLayout::FullFrame);
}
#[test]
fn test_sound_descriptor() {
let desc = SoundDescriptor::new((48000, 1), 2, 24, Auid::null())
.with_locked(true)
.with_audio_ref_level(-20);
assert_eq!(desc.audio_sample_rate, (48000, 1));
assert_eq!(desc.channel_count, 2);
assert_eq!(desc.quantization_bits, 24);
assert!(desc.locked);
assert_eq!(desc.audio_ref_level, Some(-20));
}
#[test]
fn test_multiple_descriptor() {
let mut multi = MultipleDescriptor::new(Auid::null());
let video = EssenceDescriptor::DigitalImage(DigitalImageDescriptor::new(
(25, 1),
1920,
1080,
Auid::null(),
));
let audio = EssenceDescriptor::Sound(SoundDescriptor::new((48000, 1), 2, 24, Auid::null()));
multi.add_sub_descriptor(video);
multi.add_sub_descriptor(audio);
assert_eq!(multi.sub_descriptors.len(), 2);
}
#[test]
fn test_file_locator() {
let locator = FileLocator::new("/media/video.mov");
assert_eq!(
locator.path.to_str().expect("to_str should succeed"),
"/media/video.mov"
);
}
#[test]
fn test_network_locator() {
let locator = NetworkLocator::new("http://server.com/video.mov");
assert_eq!(locator.url, "http://server.com/video.mov");
}
#[test]
fn test_essence_reference() {
let embedded = EssenceReference::Embedded {
mob_id: Uuid::new_v4(),
slot_id: 1,
};
assert!(embedded.is_embedded());
assert!(!embedded.is_external());
let external = EssenceReference::External {
path: PathBuf::from("/path/to/file.mov"),
mob_id: None,
};
assert!(external.is_external());
assert!(!external.is_embedded());
}
#[test]
fn test_essence_access() {
let desc = EssenceDescriptor::File(FileDescriptor::new((25, 1), Auid::null()));
let reference = EssenceReference::External {
path: PathBuf::from("/path/to/file.mov"),
mob_id: None,
};
let access = EssenceAccess::new(desc, reference);
assert!(access.is_external());
}
#[test]
fn test_codec_registry() {
let mut registry = CodecRegistry::new();
let codec_info = CodecInfo::new("TestCodec", CodecType::Video);
let auid = Auid::null();
registry.add_codec(auid, codec_info);
assert!(registry.get_codec(&auid).is_some());
}
}