mp4forge 0.2.0

Rust library and CLI for inspecting, probing, extracting, and rewriting MP4 box structures
Documentation
use std::env;
use std::error::Error;
use std::fs::File;
use std::io::Cursor;

use mp4forge::FourCc;
use mp4forge::boxes::iso14496_12::Emsg;
use mp4forge::rewrite::rewrite_box_as;
use mp4forge::walk::BoxPath;

fn main() {
    if let Err(error) = run() {
        eprintln!("{error}");
        std::process::exit(1);
    }
}

fn run() -> Result<(), Box<dyn Error>> {
    let Some(output_path) = env::args().nth(1) else {
        return Err("usage: cargo run --example rewrite_emsg -- <output.mp4>".into());
    };

    let input = sample_emsg_file();
    let mut reader = Cursor::new(input);
    let output = File::create(output_path)?;
    rewrite_box_as::<_, _, Emsg, _>(
        &mut reader,
        output,
        BoxPath::from([FourCc::from_bytes(*b"emsg")]),
        |emsg| {
            emsg.message_data = b"hello world".to_vec();
        },
    )?;

    Ok(())
}

fn sample_emsg_file() -> Vec<u8> {
    let mut emsg_payload = vec![0x00, 0x00, 0x00, 0x00];
    append_null_terminated_string(&mut emsg_payload, "urn:test");
    append_null_terminated_string(&mut emsg_payload, "demo");
    append_u32(&mut emsg_payload, 1000);
    append_u32(&mut emsg_payload, 0);
    append_u32(&mut emsg_payload, 5);
    append_u32(&mut emsg_payload, 1);
    emsg_payload.extend_from_slice(b"hello");

    let mut file = Vec::new();
    file.extend_from_slice(&box_bytes("free", &[0x01, 0x02, 0x03]));
    file.extend_from_slice(&box_bytes("emsg", &emsg_payload));
    file.extend_from_slice(&box_bytes("free", &[0x04, 0x05]));
    file
}

fn append_null_terminated_string(dst: &mut Vec<u8>, value: &str) {
    dst.extend_from_slice(value.as_bytes());
    dst.push(0x00);
}

fn append_u32(dst: &mut Vec<u8>, value: u32) {
    dst.extend_from_slice(&value.to_be_bytes());
}

fn box_bytes(box_type: &str, payload: &[u8]) -> Vec<u8> {
    let mut box_bytes = Vec::with_capacity(8 + payload.len());
    box_bytes.extend_from_slice(&((payload.len() + 8) as u32).to_be_bytes());
    box_bytes.extend_from_slice(box_type.as_bytes());
    box_bytes.extend_from_slice(payload);
    box_bytes
}