use std::io::{ Read, Seek, Result, SeekFrom };
use byteorder::{ ReadBytesExt, BigEndian };
use crate::{ fourcc, read_box, typ_to_str };
#[derive(Default, Clone, Debug)]
pub struct TrackDesc {
pub tkhd_duration: u64,
pub elst_segment_duration: u64,
pub mdhd_timescale: u32,
pub mdhd_duration: u64,
pub stts: Vec<(u32, u32)>,
pub stsz: Vec<u32>,
pub stco: Vec<u64>,
pub stss: Vec<u32>,
pub sdtp: Vec<u8>,
pub sample_offset: u32,
pub chunk_offset: u32,
pub stsz_sample_size: u32,
pub stsz_count: u32,
pub stsc: Vec<(u32, u32, u32)>, pub co64_final_position: u64,
pub skip: bool,
}
#[derive(Default, Clone, Debug)]
pub struct Desc {
pub mdat_position: Vec<(Option<usize>, u64, u64)>, pub mvhd_timescale_per_file: Vec<u32>,
pub moov_mvhd_timescale: u32,
pub moov_mvhd_duration: u64,
pub moov_tracks: Vec<TrackDesc>,
pub mdat_offset: u64,
pub mdat_final_position: u64,
}
pub fn read_desc<R: Read + Seek>(d: &mut R, desc: &mut Desc, track: usize, max_read: u64, file_index: usize) -> Result<()> {
let mut tl_track = track;
let start_offs = d.stream_position()?;
desc.mvhd_timescale_per_file.push(0);
while let Ok((typ, offs, size, header_size)) = read_box(d) {
if size == 0 || typ == 0 { continue; }
if crate::has_children(typ, true) {
read_desc(d, desc, tl_track, size - header_size as u64, file_index)?;
if typ == fourcc("trak") {
tl_track += 1;
}
} else {
log::debug!("Reading {}, offset: {}, size: {size}, header_size: {header_size}", typ_to_str(typ), offs);
let org_pos = d.stream_position()?;
if typ == fourcc("mvhd") || typ == fourcc("tkhd") || typ == fourcc("mdhd") {
let (v, _flags) = (d.read_u8()?, d.read_u24::<BigEndian>()?);
if typ == fourcc("mvhd") {
let timescale = if v == 1 { d.seek(SeekFrom::Current(8+8))?; d.read_u32::<BigEndian>()? }
else { d.seek(SeekFrom::Current(4+4))?; d.read_u32::<BigEndian>()? };
let duration = if v == 1 { d.read_u64::<BigEndian>()? }
else { d.read_u32::<BigEndian>()? as u64 };
if desc.moov_mvhd_timescale == 0 {
desc.moov_mvhd_timescale = timescale;
}
desc.mvhd_timescale_per_file[file_index] = timescale;
desc.moov_mvhd_duration += ((duration as f64 / timescale as f64) * desc.moov_mvhd_timescale as f64).ceil() as u64;
}
if let Some(track_desc) = desc.moov_tracks.get_mut(tl_track) {
if typ == fourcc("tkhd") {
let duration = if v == 1 { d.seek(SeekFrom::Current(8+8+4+4))?; d.read_u64::<BigEndian>()? }
else { d.seek(SeekFrom::Current(4+4+4+4))?; d.read_u32::<BigEndian>()? as u64 };
track_desc.tkhd_duration += ((duration as f64 / *desc.mvhd_timescale_per_file.get(file_index).ok_or(std::io::Error::other("Invalid index"))? as f64) * desc.moov_mvhd_timescale as f64).ceil() as u64;
}
if typ == fourcc("mdhd") {
let timescale = if v == 1 { d.seek(SeekFrom::Current(8+8))?; d.read_u32::<BigEndian>()? }
else { d.seek(SeekFrom::Current(4+4))?; d.read_u32::<BigEndian>()? };
let duration = if v == 1 { d.read_u64::<BigEndian>()? }
else { d.read_u32::<BigEndian>()? as u64 };
if track_desc.mdhd_timescale == 0 {
track_desc.mdhd_timescale = timescale;
}
let add_duration = ((duration as f64 / timescale as f64) * track_desc.mdhd_timescale as f64).ceil() as u64;
track_desc.mdhd_duration += add_duration;
}
}
}
if typ == fourcc("elst") || typ == fourcc("stts") || typ == fourcc("stsz") || typ == fourcc("stss") ||
typ == fourcc("stco") || typ == fourcc("co64") || typ == fourcc("sdtp") || typ == fourcc("stsc") {
let track_desc = desc.moov_tracks.get_mut(tl_track).unwrap();
if !(track_desc.skip && file_index > 0) {
let (v, _flags) = (d.read_u8()?, d.read_u24::<BigEndian>()?);
if typ == fourcc("elst") {
let entry_count = d.read_u32::<BigEndian>()?;
for _ in 0..entry_count {
let segment_duration = if v == 1 { d.read_u64::<BigEndian>()? } else { d.read_u32::<BigEndian>()? as u64 };
let media_time = if v == 1 { d.read_i64::<BigEndian>()? } else { d.read_i32::<BigEndian>()? as i64 };
d.seek(SeekFrom::Current(4))?; if media_time != -1 {
track_desc.elst_segment_duration += segment_duration;
}
}
}
if typ == fourcc("stsz") {
track_desc.stsz_sample_size = d.read_u32::<BigEndian>()?;
let count = d.read_u32::<BigEndian>()?;
if track_desc.stsz_sample_size == 0 {
for _ in 0..count { track_desc.stsz.push(d.read_u32::<BigEndian>()?); }
}
track_desc.stsz_count += count;
}
if typ == fourcc("sdtp") {
let count = size - header_size as u64 - 4;
for _ in 0..count { track_desc.sdtp.push(d.read_u8()?); }
}
if typ == fourcc("stss") || typ == fourcc("stco") || typ == fourcc("co64") || typ == fourcc("stts") || typ == fourcc("stsc") {
let count = d.read_u32::<BigEndian>()?;
let current_file_mdat_position = desc.mdat_position.last().unwrap().1;
let mdat_offset = desc.mdat_offset as i64 - current_file_mdat_position as i64;
for _ in 0..count {
if typ == fourcc("stss") { track_desc.stss.push(d.read_u32::<BigEndian>()? + track_desc.sample_offset); }
if typ == fourcc("stco") { track_desc.stco.push((d.read_u32::<BigEndian>()? as i64 + mdat_offset) as u64); }
if typ == fourcc("co64") { track_desc.stco.push((d.read_u64::<BigEndian>()? as i64 + mdat_offset) as u64); }
if typ == fourcc("stts") { track_desc.stts.push((d.read_u32::<BigEndian>()?, d.read_u32::<BigEndian>()?)); }
if typ == fourcc("stsc") { track_desc.stsc.push((
d.read_u32::<BigEndian>()? + track_desc.chunk_offset,
d.read_u32::<BigEndian>()?,
d.read_u32::<BigEndian>()?
)); }
}
}
}
}
if typ == fourcc("tmcd") {
let track_desc = desc.moov_tracks.get_mut(tl_track).unwrap();
track_desc.skip = true;
}
d.seek(SeekFrom::Start(org_pos + size - header_size as u64))?;
}
if d.stream_position()? - start_offs >= max_read {
break;
}
}
Ok(())
}