Skip to content

Commit

Permalink
parse chapter start and duration
Browse files Browse the repository at this point in the history
  • Loading branch information
saecki committed Jul 25, 2021
1 parent 99845fb commit 061653e
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 18 deletions.
10 changes: 5 additions & 5 deletions src/atom/head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,25 +107,25 @@ pub fn parse_head(reader: &mut impl Read) -> crate::Result<Head> {
return Err(crate::Error::new(ErrorKind::Io(e), "Error reading atom length"));
}
};
let mut ident = Fourcc([0u8; 4]);
if let Err(e) = reader.read_exact(&mut *ident) {
let mut fourcc = Fourcc::default();
if let Err(e) = reader.read_exact(&mut *fourcc) {
return Err(crate::Error::new(ErrorKind::Io(e), "Error reading atom identifier"));
}

if len == 1 {
match reader.read_be_u64() {
Ok(l) => Ok(Head::new(true, l, ident)),
Ok(l) => Ok(Head::new(true, l, fourcc)),
Err(e) => {
Err(crate::Error::new(ErrorKind::Io(e), "Error reading extended atom length"))
}
}
} else if len < 8 {
Err(crate::Error::new(
crate::ErrorKind::Parsing,
format!("Read length of '{}' which is less than 8 bytes: {}", ident, len),
format!("Read length of '{}' which is less than 8 bytes: {}", fourcc, len),
))
} else {
Ok(Head::new(false, len, ident))
Ok(Head::new(false, len, fourcc))
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/atom/ident.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ pub(crate) const SAMPLE_TABLE: Fourcc = Fourcc(*b"stbl");
pub(crate) const SAMPLE_TABLE_CHUNK_OFFSET: Fourcc = Fourcc(*b"stco");
/// (`co64`)
pub(crate) const SAMPLE_TABLE_CHUNK_OFFSET_64: Fourcc = Fourcc(*b"co64");
/// (`stts`)
pub(crate) const SAMPLE_TABLE_TIME_TO_SAMPLE: Fourcc = Fourcc(*b"stts");
/// (`stsd`)
pub(crate) const SAMPLE_TABLE_SAMPLE_DESCRIPTION: Fourcc = Fourcc(*b"stsd");
/// (`mp4a`)
Expand Down
57 changes: 46 additions & 11 deletions src/atom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ use mvhd::*;
use stbl::*;
use stco::*;
use stsd::*;
use stts::*;
use tkhd::*;
use trak::*;
use tref::*;
Expand Down Expand Up @@ -85,6 +86,7 @@ mod mvhd;
mod stbl;
mod stco;
mod stsd;
mod stts;
mod tkhd;
mod trak;
mod tref;
Expand Down Expand Up @@ -197,6 +199,7 @@ pub(crate) fn read_tag(reader: &mut (impl Read + Seek)) -> crate::Result<Tag> {
parsed_bytes += head.len();
};

let timescale = moov.mvhd.as_ref().map(|a| a.timescale);
let mut chapters = Vec::new();
for chap in moov.trak.iter().filter_map(|a| a.tref.as_ref().and_then(|a| a.chap.as_ref())) {
for c_id in chap.chapter_ids.iter() {
Expand All @@ -213,22 +216,30 @@ pub(crate) fn read_tag(reader: &mut (impl Read + Seek)) -> crate::Result<Tag> {
.as_ref()
.and_then(|a| a.minf.as_ref())
.and_then(|a| a.stbl.as_ref());
let stts = stbl.and_then(|a| a.stts.as_ref());

if let Some(stco) = stbl.and_then(|a| a.stco.as_ref()) {
chapters.reserve(stco.offsets.len());
for o in stco.offsets.iter() {
chapters.push(read_chapter(reader, *o as u64)?);
}
read_chapters(
reader,
&mut chapters,
timescale.unwrap_or(1000),
stco.offsets.iter().map(|o| *o as u64),
stts.map_or([].iter(), |a| a.items.iter()),
)?;
} else if let Some(co64) = stbl.and_then(|a| a.co64.as_ref()) {
chapters.reserve(co64.offsets.len());
for o in co64.offsets.iter() {
chapters.push(read_chapter(reader, *o)?);
}
read_chapters(
reader,
&mut chapters,
timescale.unwrap_or(1000),
co64.offsets.iter().copied(),
stts.map_or([].iter(), |a| a.items.iter()),
)?;
}
}
}

let mvhd = moov.mvhd;
let mp4a = moov.trak.into_iter().find_map(|trak| {
trak.mdia
.and_then(|a| a.minf)
Expand All @@ -244,8 +255,8 @@ pub(crate) fn read_tag(reader: &mut (impl Read + Seek)) -> crate::Result<Tag> {
.unwrap_or_default();

let mut info = AudioInfo::default();
if let Some(i) = mvhd {
info.duration = Some(scaled_duration(i.timescale, i.duration));
if let Some(a) = moov.mvhd {
info.duration = Some(scaled_duration(a.timescale, a.duration));
}
if let Some(i) = mp4a {
info.channel_config = i.channel_config;
Expand All @@ -257,7 +268,31 @@ pub(crate) fn read_tag(reader: &mut (impl Read + Seek)) -> crate::Result<Tag> {
Ok(Tag::new(ftyp, info, ilst, chapters))
}

fn read_chapter(reader: &mut (impl Read + Seek), offset: u64) -> crate::Result<Chapter> {
fn read_chapters<'a>(
reader: &mut (impl Read + Seek),
chapters: &mut Vec<Chapter>,
timescale: u32,
offsets: impl Iterator<Item = u64>,
mut durations: impl Iterator<Item = &'a SttsItem>,
) -> crate::Result<()> {
let mut start = 0;

for o in offsets {
let duration = durations.next().map_or(0, |i| i.sample_duration) as u64;
let title = read_chapter_title(reader, o)?;
chapters.push(Chapter {
start: scaled_duration(timescale, start),
duration: scaled_duration(timescale, duration),
title,
});

start += duration;
}

Ok(())
}

fn read_chapter_title(reader: &mut (impl Read + Seek), offset: u64) -> crate::Result<String> {
reader.seek(SeekFrom::Start(offset))?;
let len = reader.read_be_u16()?;
let bom = reader.read_be_u16()?;
Expand All @@ -272,7 +307,7 @@ fn read_chapter(reader: &mut (impl Read + Seek), offset: u64) -> crate::Result<C
}
};

Ok(Chapter { title })
Ok(title)
}

/// Attempts to write the metadata atoms to the file inside the item list atom.
Expand Down
2 changes: 2 additions & 0 deletions src/atom/stbl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub struct Stbl {
pub stsd: Option<Stsd>,
pub stco: Option<Stco>,
pub co64: Option<Co64>,
pub stts: Option<Stts>,
}

impl Atom for Stbl {
Expand All @@ -25,6 +26,7 @@ impl ParseAtom for Stbl {
}
SAMPLE_TABLE_CHUNK_OFFSET => stbl.stco = Some(Stco::parse(reader, head.size())?),
SAMPLE_TABLE_CHUNK_OFFSET_64 => stbl.co64 = Some(Co64::parse(reader, head.size())?),
SAMPLE_TABLE_TIME_TO_SAMPLE => stbl.stts = Some(Stts::parse(reader, head.size())?),
_ => {
reader.seek(SeekFrom::Current(head.content_len() as i64))?;
}
Expand Down
49 changes: 49 additions & 0 deletions src/atom/stts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use super::*;

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Stts {
pub table_pos: u64,
pub items: Vec<SttsItem>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct SttsItem {
pub sample_count: u32,
pub sample_duration: u32,
}

impl Atom for Stts {
const FOURCC: Fourcc = SAMPLE_TABLE_TIME_TO_SAMPLE;
}

impl ParseAtom for Stts {
fn parse_atom(reader: &mut (impl Read + Seek), size: Size) -> crate::Result<Self> {
let (version, _) = parse_full_head(reader)?;

if version != 0 {
return Err(crate::Error::new(
crate::ErrorKind::UnknownVersion(version),
"Unknown sample table time to sample (stts) version",
));
}

let entries = reader.read_be_u32()?;
if 8 + 8 * entries as u64 != size.content_len() {
return Err(crate::Error::new(
crate::ErrorKind::Parsing,
"Sample table time to sample (stts) table size doesn't match atom length",
));
}

let table_pos = reader.seek(SeekFrom::Current(0))?;
let mut items = Vec::with_capacity(entries as usize);
for _ in 0..entries {
items.push(SttsItem {
sample_count: reader.read_be_u32()?,
sample_duration: reader.read_be_u32()?,
});
}

Ok(Self { table_pos, items })
}
}
4 changes: 2 additions & 2 deletions src/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ pub struct Tag {
ftyp: String,
/// Readonly audio information
info: AudioInfo,
/// A vector containing metadata item atoms
/// A list of Metadata item atoms.
atoms: Vec<MetaItem>,
/// A vector containing chapters
/// A list of chapters.
chapters: Vec<Chapter>,
}

Expand Down
11 changes: 11 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,17 @@ impl ImgFmt {
/// A struct representing a chapter.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Chapter {
/// The start of the chapter.
pub start: Duration,
/// The duration of the chapter.
pub duration: Duration,
/// The title of the chapter.
pub title: String,
}

impl Chapter {
/// Returns the end of the chapter
pub fn end(&self) -> Duration {
self.start + self.duration
}
}

0 comments on commit 061653e

Please sign in to comment.