use std::collections::BTreeMap;
use crate::FourCc;
use crate::codec::{CodecBox, DynCodecBox};
pub mod av1;
pub mod avs3;
pub mod dolby;
pub mod dts;
pub mod etsi_ts_102_366;
pub mod etsi_ts_103_190;
pub mod flac;
pub mod iamf;
pub mod isma_cryp;
pub mod iso14496_12;
pub mod iso14496_14;
pub mod iso14496_15;
pub mod iso14496_30;
pub mod iso23001_5;
pub mod iso23001_7;
pub mod marlin;
pub mod metadata;
pub mod mpeg_h;
pub mod oma_dcf;
pub mod opus;
pub mod threegpp;
pub mod vp;
pub trait AnyTypeBox {
fn set_box_type(&mut self, box_type: FourCc);
}
type BoxConstructor = fn(FourCc) -> Box<dyn DynCodecBox>;
type ContextPredicate = fn(BoxLookupContext) -> bool;
type DynamicContextPredicate = fn(FourCc, BoxLookupContext) -> bool;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct BoxLookupContext {
pub(crate) is_quicktime_compatible: bool,
pub(crate) quicktime_keys_meta_entry_count: usize,
pub(crate) ilst_meta_item: Option<FourCc>,
pub(crate) under_audio_sample_entry: bool,
pub(crate) under_wave: bool,
pub(crate) under_ilst: bool,
pub(crate) under_ilst_meta: bool,
pub(crate) under_ilst_free_meta: bool,
pub(crate) under_udta: bool,
}
impl BoxLookupContext {
pub const fn new() -> Self {
Self {
is_quicktime_compatible: false,
quicktime_keys_meta_entry_count: 0,
ilst_meta_item: None,
under_audio_sample_entry: false,
under_wave: false,
under_ilst: false,
under_ilst_meta: false,
under_ilst_free_meta: false,
under_udta: false,
}
}
pub const fn is_quicktime_compatible(&self) -> bool {
self.is_quicktime_compatible
}
pub const fn with_quicktime_compatible(mut self, is_quicktime_compatible: bool) -> Self {
self.is_quicktime_compatible = is_quicktime_compatible;
self
}
pub const fn with_metadata_keys_entry_count(
mut self,
quicktime_keys_meta_entry_count: usize,
) -> Self {
self.quicktime_keys_meta_entry_count = quicktime_keys_meta_entry_count;
self
}
pub const fn metadata_keys_entry_count(&self) -> usize {
self.quicktime_keys_meta_entry_count
}
pub const fn ilst_meta_item(&self) -> Option<FourCc> {
self.ilst_meta_item
}
pub const fn under_audio_sample_entry(&self) -> bool {
self.under_audio_sample_entry
}
pub const fn under_wave(&self) -> bool {
self.under_wave
}
pub const fn under_ilst(&self) -> bool {
self.under_ilst
}
pub const fn under_ilst_meta(&self) -> bool {
self.under_ilst_meta
}
pub const fn under_ilst_free_meta(&self) -> bool {
self.under_ilst_free_meta
}
pub const fn under_udta(&self) -> bool {
self.under_udta
}
pub fn enter(mut self, box_type: FourCc) -> Self {
const WAVE: FourCc = FourCc::from_bytes(*b"wave");
const ILST: FourCc = FourCc::from_bytes(*b"ilst");
const UDTA: FourCc = FourCc::from_bytes(*b"udta");
const FREE_FORM: FourCc = FourCc::from_bytes(*b"----");
const MP4A: FourCc = FourCc::from_bytes(*b"mp4a");
const ENCA: FourCc = FourCc::from_bytes(*b"enca");
const SAMR: FourCc = FourCc::from_bytes(*b"samr");
const SAWB: FourCc = FourCc::from_bytes(*b"sawb");
const SQCP: FourCc = FourCc::from_bytes(*b"sqcp");
const SEVC: FourCc = FourCc::from_bytes(*b"sevc");
const SSMV: FourCc = FourCc::from_bytes(*b"ssmv");
const ALAC: FourCc = FourCc::from_bytes(*b"alac");
const AC_3: FourCc = FourCc::from_bytes(*b"ac-3");
const EC_3: FourCc = FourCc::from_bytes(*b"ec-3");
const AC_4: FourCc = FourCc::from_bytes(*b"ac-4");
const MLPA: FourCc = FourCc::from_bytes(*b"mlpa");
const DTSC: FourCc = FourCc::from_bytes(*b"dtsc");
const DTSE: FourCc = FourCc::from_bytes(*b"dtse");
const DTSH: FourCc = FourCc::from_bytes(*b"dtsh");
const DTSL: FourCc = FourCc::from_bytes(*b"dtsl");
const DTSM: FourCc = FourCc::from_bytes(*b"dtsm");
const DTS_MINUS: FourCc = FourCc::from_bytes(*b"dts-");
const DTSX: FourCc = FourCc::from_bytes(*b"dtsx");
const DTSY: FourCc = FourCc::from_bytes(*b"dtsy");
const FLAC: FourCc = FourCc::from_bytes(*b"fLaC");
const OPUS: FourCc = FourCc::from_bytes(*b"Opus");
const IAMF: FourCc = FourCc::from_bytes(*b"iamf");
const MHA1: FourCc = FourCc::from_bytes(*b"mha1");
const MHM1: FourCc = FourCc::from_bytes(*b"mhm1");
if matches!(
box_type,
MP4A | ENCA
| SAMR
| SAWB
| SQCP
| SEVC
| SSMV
| ALAC
| AC_3
| EC_3
| AC_4
| MLPA
| DTSC
| DTSE
| DTSH
| DTSL
| DTSM
| DTS_MINUS
| DTSX
| DTSY
| FLAC
| OPUS
| IAMF
| MHA1
| MHM1
) {
self.under_audio_sample_entry = true;
}
if box_type == WAVE {
self.under_wave = true;
} else if box_type == ILST {
self.ilst_meta_item = None;
self.under_ilst = true;
} else if self.under_ilst
&& !self.under_ilst_meta
&& metadata::is_ilst_meta_box_type(box_type)
{
self.ilst_meta_item = Some(box_type);
self.under_ilst_meta = true;
if box_type == FREE_FORM {
self.under_ilst_free_meta = true;
}
} else if box_type == UDTA {
self.under_udta = true;
}
self
}
}
#[derive(Clone, Copy)]
pub struct BoxRegistration {
box_type: FourCc,
supported_versions: &'static [u8],
constructor: BoxConstructor,
}
impl BoxRegistration {
fn new(
box_type: FourCc,
supported_versions: &'static [u8],
constructor: BoxConstructor,
) -> Self {
Self {
box_type,
supported_versions,
constructor,
}
}
pub const fn box_type(&self) -> FourCc {
self.box_type
}
pub const fn supported_versions(&self) -> &'static [u8] {
self.supported_versions
}
}
#[derive(Clone, Copy)]
struct ContextualBoxRegistration {
registration: BoxRegistration,
matches: ContextPredicate,
}
impl ContextualBoxRegistration {
const fn new(registration: BoxRegistration, matches: ContextPredicate) -> Self {
Self {
registration,
matches,
}
}
}
#[derive(Clone, Copy)]
struct DynamicBoxRegistration {
supported_versions: &'static [u8],
constructor: BoxConstructor,
matches: DynamicContextPredicate,
}
impl DynamicBoxRegistration {
const fn new(
supported_versions: &'static [u8],
constructor: BoxConstructor,
matches: DynamicContextPredicate,
) -> Self {
Self {
supported_versions,
constructor,
matches,
}
}
}
#[derive(Clone, Copy)]
struct ResolvedRegistration {
supported_versions: &'static [u8],
constructor: BoxConstructor,
}
impl ResolvedRegistration {
const fn new(supported_versions: &'static [u8], constructor: BoxConstructor) -> Self {
Self {
supported_versions,
constructor,
}
}
}
#[derive(Clone, Default)]
pub struct BoxRegistry {
entries: BTreeMap<FourCc, BoxRegistration>,
contextual_entries: BTreeMap<FourCc, Vec<ContextualBoxRegistration>>,
dynamic_entries: Vec<DynamicBoxRegistration>,
}
impl BoxRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn is_registered(&self, box_type: FourCc) -> bool {
self.is_registered_with_context(box_type, BoxLookupContext::new())
}
pub fn is_registered_with_context(&self, box_type: FourCc, context: BoxLookupContext) -> bool {
self.resolve_registration(box_type, context).is_some()
}
pub fn supported_versions(&self, box_type: FourCc) -> Option<&'static [u8]> {
self.supported_versions_with_context(box_type, BoxLookupContext::new())
}
pub fn supported_versions_with_context(
&self,
box_type: FourCc,
context: BoxLookupContext,
) -> Option<&'static [u8]> {
self.resolve_registration(box_type, context)
.map(|registration| registration.supported_versions)
}
pub fn is_supported_version(&self, box_type: FourCc, version: u8) -> bool {
self.is_supported_version_with_context(box_type, version, BoxLookupContext::new())
}
pub fn is_supported_version_with_context(
&self,
box_type: FourCc,
version: u8,
context: BoxLookupContext,
) -> bool {
let Some(supported_versions) = self.supported_versions_with_context(box_type, context)
else {
return false;
};
supported_versions.is_empty() || supported_versions.contains(&version)
}
pub fn register<T>(&mut self, box_type: FourCc) -> Option<BoxRegistration>
where
T: CodecBox + Default + 'static,
{
self.insert(BoxRegistration::new(
box_type,
T::SUPPORTED_VERSIONS,
construct_fixed::<T>,
))
}
pub fn register_any<T>(&mut self, box_type: FourCc) -> Option<BoxRegistration>
where
T: CodecBox + AnyTypeBox + Default + 'static,
{
self.insert(BoxRegistration::new(
box_type,
T::SUPPORTED_VERSIONS,
construct_any::<T>,
))
}
pub fn register_contextual<T>(
&mut self,
box_type: FourCc,
matches: fn(BoxLookupContext) -> bool,
) where
T: CodecBox + Default + 'static,
{
let registration = ContextualBoxRegistration::new(
BoxRegistration::new(box_type, T::SUPPORTED_VERSIONS, construct_fixed::<T>),
matches,
);
self.contextual_entries
.entry(box_type)
.or_default()
.push(registration);
}
pub fn register_contextual_any<T>(
&mut self,
box_type: FourCc,
matches: fn(BoxLookupContext) -> bool,
) where
T: CodecBox + AnyTypeBox + Default + 'static,
{
let registration = ContextualBoxRegistration::new(
BoxRegistration::new(box_type, T::SUPPORTED_VERSIONS, construct_any::<T>),
matches,
);
self.contextual_entries
.entry(box_type)
.or_default()
.push(registration);
}
pub(crate) fn register_dynamic_any<T>(&mut self, matches: DynamicContextPredicate)
where
T: CodecBox + AnyTypeBox + Default + 'static,
{
self.dynamic_entries.push(DynamicBoxRegistration::new(
T::SUPPORTED_VERSIONS,
construct_any::<T>,
matches,
));
}
pub fn new_box(&self, box_type: FourCc) -> Option<Box<dyn DynCodecBox>> {
self.new_box_with_context(box_type, BoxLookupContext::new())
}
pub fn new_box_with_context(
&self,
box_type: FourCc,
context: BoxLookupContext,
) -> Option<Box<dyn DynCodecBox>> {
self.resolve_registration(box_type, context)
.map(|registration| (registration.constructor)(box_type))
}
fn insert(&mut self, registration: BoxRegistration) -> Option<BoxRegistration> {
self.entries.insert(registration.box_type, registration)
}
fn resolve_registration(
&self,
box_type: FourCc,
context: BoxLookupContext,
) -> Option<ResolvedRegistration> {
if let Some(registration) = self.entries.get(&box_type) {
return Some(ResolvedRegistration::new(
registration.supported_versions(),
registration.constructor,
));
}
if let Some(registrations) = self.contextual_entries.get(&box_type)
&& let Some(registration) = registrations
.iter()
.find(|registration| (registration.matches)(context))
{
return Some(ResolvedRegistration::new(
registration.registration.supported_versions(),
registration.registration.constructor,
));
}
self.dynamic_entries
.iter()
.find(|registration| (registration.matches)(box_type, context))
.map(|registration| {
ResolvedRegistration::new(registration.supported_versions, registration.constructor)
})
}
}
pub fn default_registry() -> BoxRegistry {
let mut registry = BoxRegistry::new();
iso14496_12::register_boxes(&mut registry);
iso14496_14::register_boxes(&mut registry);
iso14496_30::register_boxes(&mut registry);
isma_cryp::register_boxes(&mut registry);
marlin::register_boxes(&mut registry);
metadata::register_boxes(&mut registry);
oma_dcf::register_boxes(&mut registry);
threegpp::register_boxes(&mut registry);
av1::register_boxes(&mut registry);
avs3::register_boxes(&mut registry);
dolby::register_boxes(&mut registry);
etsi_ts_102_366::register_boxes(&mut registry);
etsi_ts_103_190::register_boxes(&mut registry);
flac::register_boxes(&mut registry);
mpeg_h::register_boxes(&mut registry);
opus::register_boxes(&mut registry);
vp::register_boxes(&mut registry);
iso14496_15::register_boxes(&mut registry);
iso23001_5::register_boxes(&mut registry);
iso23001_7::register_boxes(&mut registry);
registry
}
fn construct_fixed<T>(_box_type: FourCc) -> Box<dyn DynCodecBox>
where
T: CodecBox + Default + 'static,
{
Box::new(T::default())
}
fn construct_any<T>(box_type: FourCc) -> Box<dyn DynCodecBox>
where
T: CodecBox + AnyTypeBox + Default + 'static,
{
let mut boxed = T::default();
boxed.set_box_type(box_type);
Box::new(boxed)
}