#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum StructuredData {
SampleDescription(StsdData),
DecodingTimeToSample(SttsData),
CompositionTimeToSample(CttsData),
SampleToChunk(StscData),
SampleSize(StszData),
SyncSample(StssData),
ChunkOffset(StcoData),
ChunkOffset64(Co64Data),
MediaHeader(MdhdData),
HandlerReference(HdlrData),
TrackHeader(TkhdData),
MovieHeader(MvhdData),
EditList(ElstData),
SegmentIndex(SidxData),
TrackFragmentRun(TrunData),
TrackFragmentHeader(TfhdData),
TrackFragmentDecodeTime(TfdtData),
TrackExtends(TrexData),
ProtectionSystemHeader(PsshData),
TrackEncryption(TencData),
EventMessage(EmsgData),
ElementaryStream(EsdsData),
ObjectDescriptor(IodsData),
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EsdsData {
pub version: u8,
pub flags: u32,
pub object_type: u8,
pub object_type_name: String,
pub max_bitrate: u32,
pub avg_bitrate: u32,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub audio_config: Option<AudioSpecificConfig>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct IodsData {
pub version: u8,
pub flags: u32,
pub od_id: u16,
pub include_inline_profiles: bool,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub url: Option<String>,
pub od_profile_level: u8,
pub scene_profile_level: u8,
pub audio_profile_level: u8,
pub visual_profile_level: u8,
pub graphics_profile_level: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct AudioSpecificConfig {
pub audio_object_type: u8,
pub profile: String,
pub sample_rate: u32,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub extension_sample_rate: Option<u32>,
pub channel_configuration: u8,
pub sbr: bool,
pub ps: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PsshData {
pub version: u8,
pub flags: u32,
pub system_id: String,
pub system_name: Option<String>,
pub key_ids: Vec<String>,
pub data_size: u32,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub widevine: Option<Box<crate::drm::WidevinePsshData>>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub playready: Option<Box<crate::drm::PlayReadyPsshData>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TencData {
pub version: u8,
pub flags: u32,
pub default_crypt_byte_block: u8,
pub default_skip_byte_block: u8,
pub default_is_protected: bool,
pub default_per_sample_iv_size: u8,
pub default_kid: String,
pub default_constant_iv: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EmsgData {
pub version: u8,
pub flags: u32,
pub scheme_id_uri: String,
pub value: String,
pub timescale: u32,
pub presentation_time: Option<u64>,
pub presentation_time_delta: Option<u32>,
pub event_duration: u32,
pub id: u32,
pub message_size: u64,
}
impl StructuredData {
pub fn summary(&self) -> String {
match self {
StructuredData::MovieHeader(d) => {
format!("timescale={} duration={}", d.timescale, d.duration)
}
StructuredData::TrackHeader(d) => format!(
"track_id={} duration={} width={} height={} flags=0x{:06X}",
d.track_id, d.duration, d.width, d.height, d.flags
),
StructuredData::MediaHeader(d) => format!(
"timescale={} duration={} language={}",
d.timescale, d.duration, d.language
),
StructuredData::HandlerReference(d) => {
format!("handler_type={} name={:?}", d.handler_type, d.name)
}
StructuredData::EditList(d) => match d.entries.first() {
Some(e) => format!(
"version={} entries={} first: duration={} media_time={} rate={}/{}",
d.version,
d.entry_count,
e.segment_duration,
e.media_time,
e.media_rate_integer,
e.media_rate_fraction
),
None => format!("version={} entries=0", d.version),
},
StructuredData::SegmentIndex(d) => format!(
"timescale={} earliest_presentation_time={} first_offset={} references={}",
d.timescale,
d.earliest_presentation_time,
d.first_offset,
d.references.len()
),
StructuredData::SampleDescription(d) => {
let e = d.entries.first();
match e {
Some(e) => {
let mut s = format!("codec={}", e.codec);
if let (Some(w), Some(h)) = (e.width, e.height) {
s.push_str(&format!(" {}x{}", w, h));
}
if let Some(ch) = e.channel_count {
s.push_str(&format!(" channels={}", ch));
}
if let Some(sr) = e.sample_rate {
s.push_str(&format!(" sample_rate={}", sr));
}
if let Some(bits) = e.sample_size {
s.push_str(&format!(" bits={}", bits));
}
s.push_str(&format!(" entries={}", d.entry_count));
s
}
None => "entries=0".to_string(),
}
}
StructuredData::DecodingTimeToSample(d) => {
let summary: Vec<String> = d
.entries
.iter()
.take(4)
.map(|e| format!("{}×{}", e.sample_count, e.sample_delta))
.collect();
let ellipsis = if d.entry_count > 4 { ", …" } else { "" };
format!(
"entries={} [{}{}]",
d.entry_count,
summary.join(", "),
ellipsis
)
}
StructuredData::CompositionTimeToSample(d) => format!("entries={}", d.entry_count),
StructuredData::SampleToChunk(d) => format!("entries={}", d.entry_count),
StructuredData::SampleSize(d) => {
if d.sample_size > 0 {
format!("fixed_size={} count={}", d.sample_size, d.sample_count)
} else {
format!("variable count={}", d.sample_count)
}
}
StructuredData::SyncSample(d) => format!("keyframes={}", d.entry_count),
StructuredData::ChunkOffset(d) => format!("chunks={}", d.entry_count),
StructuredData::ChunkOffset64(d) => format!("chunks={}", d.entry_count),
StructuredData::TrackFragmentRun(d) => {
let mut parts = vec![format!("samples={}", d.sample_count)];
if let Some(off) = d.data_offset {
parts.push(format!("data_offset={}", off));
}
let has_dur = d.flags & 0x100 != 0;
let has_size = d.flags & 0x200 != 0;
if (has_dur || has_size)
&& let Some(first) = d.samples.first()
{
if let Some(dur) = first.duration {
parts.push(format!("first_dur={}", dur));
}
if let Some(sz) = first.size {
parts.push(format!("first_size={}", sz));
}
}
parts.join(" ")
}
StructuredData::TrackFragmentHeader(d) => {
let mut parts = vec![format!("track_id={}", d.track_id)];
if let Some(v) = d.base_data_offset {
parts.push(format!("base_data_offset={}", v));
}
if let Some(v) = d.sample_description_index {
parts.push(format!("sample_description_index={}", v));
}
if let Some(v) = d.default_sample_duration {
parts.push(format!("default_duration={}", v));
}
if let Some(v) = d.default_sample_size {
parts.push(format!("default_size={}", v));
}
if let Some(v) = d.default_sample_flags {
parts.push(format!("default_flags=0x{:08X}", v));
}
if d.default_base_is_moof {
parts.push("default_base_is_moof".into());
}
parts.join(" ")
}
StructuredData::TrackFragmentDecodeTime(d) => {
format!("base_media_decode_time={}", d.base_media_decode_time)
}
StructuredData::TrackExtends(d) => format!(
"track_id={} default_sample_description_index={} default_sample_duration={} default_sample_size={} default_sample_flags=0x{:08X}",
d.track_id,
d.default_sample_description_index,
d.default_sample_duration,
d.default_sample_size,
d.default_sample_flags
),
StructuredData::ProtectionSystemHeader(d) => {
let mut s = match &d.system_name {
Some(name) => format!("system={} ({})", name, d.system_id),
None => format!("system={}", d.system_id),
};
if !d.key_ids.is_empty() {
s.push_str(&format!(" kids={}", d.key_ids.len()));
for kid in d.key_ids.iter().take(2) {
s.push_str(&format!(" {}", kid));
}
if d.key_ids.len() > 2 {
s.push('…');
}
}
s.push_str(&format!(" data_size={}", d.data_size));
if let Some(wv) = &d.widevine {
if let Some(provider) = &wv.provider {
s.push_str(&format!(" provider={provider}"));
}
if let Some(scheme) = &wv.protection_scheme {
s.push_str(&format!(" scheme={scheme}"));
}
if !wv.key_ids.is_empty() && d.key_ids.is_empty() {
s.push_str(&format!(" wv_kids={}", wv.key_ids.len()));
}
}
if let Some(pr) = &d.playready {
if let Some(version) = &pr.wrm_header_version {
s.push_str(&format!(" wrm_version={version}"));
}
if let Some(la_url) = &pr.la_url {
s.push_str(&format!(" la_url=\"{la_url}\""));
}
}
s
}
StructuredData::TrackEncryption(d) => {
let mut s = format!(
"protected={} iv_size={} kid={}",
d.default_is_protected, d.default_per_sample_iv_size, d.default_kid
);
if let Some(iv) = &d.default_constant_iv {
s.push_str(&format!(" constant_iv={}", iv));
}
if d.default_crypt_byte_block != 0 || d.default_skip_byte_block != 0 {
s.push_str(&format!(
" pattern={}:{}",
d.default_crypt_byte_block, d.default_skip_byte_block
));
}
s
}
StructuredData::ElementaryStream(d) => {
let mut s = format!(
"objectType=0x{:02X} ({})",
d.object_type, d.object_type_name
);
if let Some(a) = &d.audio_config {
s.push_str(&format!(
" profile={} sample_rate={}",
a.profile, a.sample_rate
));
if let Some(ext) = a.extension_sample_rate {
s.push_str(&format!(" output_rate={}", ext));
}
s.push_str(&format!(" channels={}", a.channel_configuration));
}
s.push_str(&format!(
" max_bitrate={} avg_bitrate={}",
d.max_bitrate, d.avg_bitrate
));
s
}
StructuredData::EventMessage(d) => {
let time = match (d.presentation_time, d.presentation_time_delta) {
(Some(t), _) => format!("presentation_time={}", t),
(None, Some(dt)) => format!("presentation_time_delta={}", dt),
_ => String::new(),
};
format!(
"scheme={:?} value={:?} timescale={} {} duration={} id={} message_size={}",
d.scheme_id_uri,
d.value,
d.timescale,
time,
d.event_duration,
d.id,
d.message_size
)
}
StructuredData::ObjectDescriptor(d) => {
let mut s = format!("od_id={}", d.od_id);
if let Some(url) = &d.url {
s.push_str(&format!(" url={:?}", url));
} else {
s.push_str(&format!(
" od_profile=0x{:02X} scene=0x{:02X} audio=0x{:02X} visual=0x{:02X} graphics=0x{:02X}",
d.od_profile_level,
d.scene_profile_level,
d.audio_profile_level,
d.visual_profile_level,
d.graphics_profile_level
));
}
s
}
}
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MvhdData {
pub version: u8,
pub flags: u32,
pub creation_time: u64,
pub modification_time: u64,
pub timescale: u32,
pub duration: u64,
pub rate: f32,
pub volume: f32,
pub next_track_id: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ElstData {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub entries: Vec<ElstEntry>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ElstEntry {
pub segment_duration: u64,
pub media_time: i64,
pub media_rate_integer: i16,
pub media_rate_fraction: i16,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SidxData {
pub version: u8,
pub flags: u32,
pub reference_id: u32,
pub timescale: u32,
pub earliest_presentation_time: u64,
pub first_offset: u64,
pub references: Vec<SidxReference>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SidxReference {
pub reference_type: u8,
pub referenced_size: u32,
pub subsegment_duration: u32,
pub starts_with_sap: bool,
pub sap_type: u8,
pub sap_delta_time: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TfhdData {
pub version: u8,
pub flags: u32,
pub track_id: u32,
pub base_data_offset: Option<u64>,
pub sample_description_index: Option<u32>,
pub default_sample_duration: Option<u32>,
pub default_sample_size: Option<u32>,
pub default_sample_flags: Option<u32>,
pub duration_is_empty: bool,
pub default_base_is_moof: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TfdtData {
pub version: u8,
pub flags: u32,
pub base_media_decode_time: u64,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TrexData {
pub track_id: u32,
pub default_sample_description_index: u32,
pub default_sample_duration: u32,
pub default_sample_size: u32,
pub default_sample_flags: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StsdData {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub entries: Vec<SampleEntry>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SampleEntry {
pub size: u32,
pub codec: String,
pub data_reference_index: u16,
pub width: Option<u16>,
pub height: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub channel_count: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub sample_size: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none", default)]
pub sample_rate: Option<u32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SttsData {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub entries: Vec<SttsEntry>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SttsEntry {
pub sample_count: u32,
pub sample_delta: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CttsData {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub entries: Vec<CttsEntry>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CttsEntry {
pub sample_count: u32,
pub sample_offset: i32, }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StscData {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub entries: Vec<StscEntry>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StscEntry {
pub first_chunk: u32,
pub samples_per_chunk: u32,
pub sample_description_index: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StszData {
pub version: u8,
pub flags: u32,
pub sample_size: u32,
pub sample_count: u32,
pub sample_sizes: Vec<u32>, }
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StssData {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub sample_numbers: Vec<u32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StcoData {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub chunk_offsets: Vec<u32>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Co64Data {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub chunk_offsets: Vec<u64>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MdhdData {
pub version: u8,
pub flags: u32,
pub creation_time: u64,
pub modification_time: u64,
pub timescale: u32,
pub duration: u64,
pub language: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct HdlrData {
pub version: u8,
pub flags: u32,
pub handler_type: String,
pub name: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TkhdData {
pub version: u8,
pub flags: u32,
pub track_id: u32,
pub duration: u64,
pub width: f32,
pub height: f32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TrunData {
pub version: u8,
pub flags: u32,
pub sample_count: u32,
pub data_offset: Option<i32>,
pub first_sample_flags: Option<u32>,
pub samples: Vec<TrunSample>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TrunSample {
pub duration: Option<u32>,
pub size: Option<u32>,
pub flags: Option<u32>,
pub composition_time_offset: Option<i32>,
}