//! MPEG-4 Generic mode. use gst::caps::NoFeature; use std::str::FromStr; #[derive(thiserror::Error, Debug, PartialEq, Eq)] pub enum ModeError { #[error("sizelength & constantsize can't be both defined")] BothAuSizeLenAndConstantSize, #[error("Neither sizelength nor constantsize are defined, need at least one of them")] NeitherAuSizeLenNorConstantSize, #[error("indexlength > 0 but indexdeltalength not defined")] MandatoryIndexDeltaLength, } #[derive(Debug, Default)] pub struct ModeConfig { pub(crate) size_len: u8, pub(crate) index_len: u8, pub(crate) index_delta_len: u8, pub(crate) cts_delta_len: u8, pub(crate) dts_delta_len: u8, pub(crate) random_access_indication: bool, pub(crate) stream_state_indication: u8, pub(crate) auxiliary_data_size_len: u8, pub(crate) constant_size: u32, pub(crate) constant_duration: u32, pub(crate) max_displacement: u32, } impl ModeConfig { #[inline] pub fn has_header_section(&self) -> bool { self.size_len > 0 || self.index_len > 0 || self.index_delta_len > 0 || self.cts_delta_len > 0 || self.dts_delta_len > 0 || self.random_access_indication || self.stream_state_indication > 0 } #[inline] pub fn has_auxiliary_section(&self) -> bool { self.auxiliary_data_size_len > 0 } #[inline] pub fn constant_duration(&self) -> Option { if self.constant_duration == 0 { return None; } Some(self.constant_duration) } #[inline] pub fn max_displacement(&self) -> Option { if self.max_displacement == 0 { return None; } Some(self.max_displacement) } /// Returns the max length in bits of the AU headers pub fn max_header_bit_len(&self) -> usize { self.size_len as usize + std::cmp::max(self.index_len, self.index_delta_len) as usize + self.cts_delta_len as usize + self.dts_delta_len as usize + if self.random_access_indication { 1 } else { 0 } + self.stream_state_indication as usize } pub fn from_caps(s: &gst::StructureRef) -> anyhow::Result { use ModeError::*; // These values are optional and have a default value of 0 (no header) let size_len = Self::parse_int::(s, "sizelength")?; let constant_size = Self::parse_int::(s, "constantsize")?; if size_len != 0 && constant_size != 0 { Err(BothAuSizeLenAndConstantSize)?; } if size_len == 0 && constant_size == 0 { Err(NeitherAuSizeLenNorConstantSize)?; } // ยง 3.2.1 // > If the AU-Index field is present in the first AU-header in the AU // > Header Section, then the AU-Index-delta field MUST be present in // > any subsequent (non-first) AU-header. let index_len = Self::parse_int::(s, "indexlength")?; let index_delta_len = Self::parse_int::(s, "indexdeltalength")?; if index_len > 0 && index_delta_len == 0 { Err(MandatoryIndexDeltaLength)?; } // TODO check mode & mode_config conformity Ok(ModeConfig { size_len, index_len, index_delta_len, cts_delta_len: Self::parse_int::(s, "ctsdeltalength")?, dts_delta_len: Self::parse_int::(s, "dtsdeltalength")?, random_access_indication: Self::parse_int::(s, "randomaccessindication")? > 0, stream_state_indication: Self::parse_int::(s, "streamstateindication")?, auxiliary_data_size_len: Self::parse_int::(s, "auxiliarydatasizelength")?, constant_size, constant_duration: Self::parse_int::(s, "constantduration")?, max_displacement: Self::parse_int::(s, "maxdisplacement")?, }) } /// Tries to read the `field` from the provided structure as an integer of type `T`. /// /// Returns: /// /// * `Ok(val)` if the field is present and its value could be parsed. /// * `Ok(0)` if the field is not present. /// * `Err(_)` otherwise. fn parse_int<'a, T>(s: &'a gst::StructureRef, field: &'static str) -> anyhow::Result where T: TryFrom + FromStr + gst::glib::value::FromValue<'a>, >::Error: std::error::Error + Send + Sync + 'static, ::Err: std::error::Error + Send + Sync + 'static, { use anyhow::Context; use gst::structure::GetError::*; match s.get::(field) { Ok(val) => Ok(val), Err(FieldNotFound { .. }) => Ok(T::try_from(0i32).unwrap()), Err(ValueGetError { .. }) => match s.get::(field) { Ok(val) => Ok(T::try_from(val).context(field)?), Err(_) => Ok(s .get::<&str>(field) .context(field)? .parse::() .context(field)?), }, } } pub fn add_to_caps( &self, builder: gst::caps::Builder, ) -> Result, ModeError> { use ModeError::*; if self.size_len != 0 && self.constant_size != 0 { Err(BothAuSizeLenAndConstantSize)?; } if self.size_len == 0 && self.constant_size == 0 { Err(NeitherAuSizeLenNorConstantSize)?; } if self.index_len > 0 && self.index_delta_len == 0 { Err(MandatoryIndexDeltaLength)?; } if self.stream_state_indication > 0 { panic!("AU Header Stream State not supported"); } Ok(builder .field("sizelength", self.size_len as i32) .field("indexlength", self.index_len as i32) .field("indexdeltalength", self.index_delta_len as i32) .field("ctsdeltalength", self.cts_delta_len as i32) .field("dtsdeltalength", self.dts_delta_len as i32) .field( "randomaccessindication", if self.random_access_indication { 1u8 } else { 0u8 }, ) .field("streamstateindication", self.stream_state_indication as i32) .field( "auxiliarydatasizelength", self.auxiliary_data_size_len as i32, ) .field("constantsize", self.constant_size as i32) .field("constantduration", self.constant_duration as i32) .field("maxdisplacement", self.max_displacement as i32)) } }