mp4forge 0.8.0

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

use mp4forge::sidx::{
    TopLevelSidxPlanAction, TopLevelSidxPlanOptions, apply_top_level_sidx_plan,
    plan_top_level_sidx_update_bytes,
};

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

fn run() -> Result<(), Box<dyn Error>> {
    let args = env::args().skip(1).collect::<Vec<_>>();
    let (input_path, output_path, non_zero_ept) = match args.as_slice() {
        [input_path, output_path] => (input_path.as_str(), output_path.as_str(), false),
        [input_path, output_path, flag] if flag == "--non-zero-ept" => {
            (input_path.as_str(), output_path.as_str(), true)
        }
        _ => {
            return Err(
                "usage: cargo run --example refresh_top_level_sidx -- <input.mp4> <output.mp4> [--non-zero-ept]"
                    .into(),
            );
        }
    };

    let input = std::fs::read(input_path)?;
    let Some(plan) = plan_top_level_sidx_update_bytes(
        &input,
        TopLevelSidxPlanOptions {
            add_if_not_exists: true,
            non_zero_ept,
        },
    )?
    else {
        return Err("no top-level sidx change was needed".into());
    };

    let action = match &plan.action {
        TopLevelSidxPlanAction::Insert => "inserted",
        TopLevelSidxPlanAction::Replace { .. } => "updated",
    };

    let mut output = Vec::with_capacity(input.len().saturating_add(plan.encoded_box_size as usize));
    let applied = apply_top_level_sidx_plan(&mut Cursor::new(&input), &mut output, &plan)?;
    std::fs::write(output_path, &output)?;

    println!(
        "{action} top-level sidx at offset {} with {} references",
        applied.info.offset(),
        applied.sidx.reference_count
    );

    Ok(())
}