vsd-mp4 0.2.0

MP4 parser ported from shaka-player with decryption and subtitle extraction support.
Documentation
/*
    REFERENCES
    ----------

    1. https://github.com/shaka-project/shaka-player/blob/a4e926772e1b754fe968ee6f97490f08a40fe535/lib/text/mp4_ttml_parser.js

*/

use crate::{
    Result, bail, data, parser,
    parser::Mp4Parser,
    sub::{Subtitles, ttml},
};

/// A parser for extracting TTML subtitles from MP4 files.
pub struct StppSubsParser;

impl StppSubsParser {
    /// Creates a new `Mp4TtmlParser` from the given initialization segment.
    pub fn from_init(data: &[u8]) -> Result<Self> {
        let saw_stpp = data!(false);
        let saw_stpp_c = saw_stpp.clone();

        Mp4Parser::new()
            .base_box("moov", parser::children)
            .base_box("trak", parser::children)
            .base_box("mdia", parser::children)
            .base_box("minf", parser::children)
            .base_box("stbl", parser::children)
            .full_box("stsd", parser::sample_description)
            .base_box("stpp", move |box_| {
                *saw_stpp_c.borrow_mut() = true;
                box_.parser.stop();
                Ok(())
            })
            .parse(data, false, false)?;

        if !saw_stpp.take() {
            bail!("STPP box not found.");
        }

        Ok(Self)
    }

    /// Parses the given media segment data to extract subtitles.
    pub fn parse(&self, data: &[u8]) -> Result<Subtitles> {
        let saw_mdat = data!(false);
        let subtitles = data!(Subtitles::new());

        let saw_mdat_c = saw_mdat.clone();
        let subtitles_c = subtitles.clone();

        Mp4Parser::new()
            .base_box(
                "mdat",
                parser::alldata(move |data| {
                    *saw_mdat_c.borrow_mut() = true;
                    // Join this to any previous payload, in case the mp4 has multiple
                    // mdats.
                    let xml = String::from_utf8(data)?;
                    subtitles_c
                        .borrow_mut()
                        .extend_cues(ttml::parse(&xml)?.into_cues());
                    Ok(())
                }),
            )
            .parse(data, false, false)?;

        if !saw_mdat.take() {
            bail!("MDAT box not found.");
        }

        Ok(subtitles.take())
    }
}