gst-plugins-rs/net/rtp/src/mp4g/mode.rs

142 lines
4.6 KiB
Rust

//! MPEG-4 Generic mode.
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<u32> {
if self.constant_duration == 0 {
return None;
}
Some(self.constant_duration)
}
#[inline]
pub fn max_displacement(&self) -> Option<u32> {
if self.max_displacement == 0 {
return None;
}
Some(self.max_displacement)
}
pub fn from_caps(s: &gst::StructureRef) -> anyhow::Result<Self> {
use ModeError::*;
// These values are optional and have a default value of 0 (no header)
let size_len = Self::parse_int::<u8>(s, "sizelength")?;
let constant_size = Self::parse_int::<u32>(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::<u8>(s, "indexlength")?;
let index_delta_len = Self::parse_int::<u8>(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::<u8>(s, "ctsdeltalength")?,
dts_delta_len: Self::parse_int::<u8>(s, "dtsdeltalength")?,
random_access_indication: Self::parse_int::<u8>(s, "randomaccessindication")? > 0,
stream_state_indication: Self::parse_int::<u8>(s, "streamstateindication")?,
auxiliary_data_size_len: Self::parse_int::<u8>(s, "auxiliarydatasizelength")?,
constant_size,
constant_duration: Self::parse_int::<u32>(s, "constantduration")?,
max_displacement: Self::parse_int::<u32>(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<T>
where
T: TryFrom<i32> + FromStr + gst::glib::value::FromValue<'a>,
<T as TryFrom<i32>>::Error: std::error::Error + Send + Sync + 'static,
<T as FromStr>::Err: std::error::Error + Send + Sync + 'static,
{
use anyhow::Context;
use gst::structure::GetError::*;
match s.get::<T>(field) {
Ok(val) => Ok(val),
Err(FieldNotFound { .. }) => Ok(T::try_from(0i32).unwrap()),
Err(ValueGetError { .. }) => match s.get::<i32>(field) {
Ok(val) => Ok(T::try_from(val).context(field)?),
Err(_) => Ok(s
.get::<&str>(field)
.context(field)?
.parse::<T>()
.context(field)?),
},
}
}
}