mp4-track 0.1.0

Track-level MP4 reader and progressive writer with bounded parsing
Documentation
//! Streaming and allocation bounds for the large valid fixture.

#![allow(unsafe_code)]

use mp4_track::{Mp4Reader, TrackId};
use std::alloc::{GlobalAlloc, Layout, System};
use std::fs::File;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};

struct CountingAllocator;

static CURRENT: AtomicUsize = AtomicUsize::new(0);
static PEAK: AtomicUsize = AtomicUsize::new(0);

fn add(size: usize) {
    let current = CURRENT
        .fetch_add(size, Ordering::Relaxed)
        .saturating_add(size);
    PEAK.fetch_max(current, Ordering::Relaxed);
}

unsafe impl GlobalAlloc for CountingAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        let pointer = unsafe { System.alloc(layout) };
        if !pointer.is_null() {
            add(layout.size());
        }
        pointer
    }

    unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) {
        CURRENT.fetch_sub(layout.size(), Ordering::Relaxed);
        unsafe { System.dealloc(pointer, layout) };
    }

    unsafe fn realloc(&self, pointer: *mut u8, old: Layout, new_size: usize) -> *mut u8 {
        let resized = unsafe { System.realloc(pointer, old, new_size) };
        if !resized.is_null() {
            if new_size >= old.size() {
                add(new_size - old.size());
            } else {
                CURRENT.fetch_sub(old.size() - new_size, Ordering::Relaxed);
            }
        }
        resized
    }
}

#[global_allocator]
static ALLOCATOR: CountingAllocator = CountingAllocator;

struct CountedReader {
    file: File,
    bytes_read: usize,
}

impl Read for CountedReader {
    fn read(&mut self, buffer: &mut [u8]) -> std::io::Result<usize> {
        let count = self.file.read(buffer)?;
        self.bytes_read = self.bytes_read.saturating_add(count);
        Ok(count)
    }
}

impl Seek for CountedReader {
    fn seek(&mut self, position: SeekFrom) -> std::io::Result<u64> {
        self.file.seek(position)
    }
}

#[test]
fn open_many_samples_stays_streaming_and_bounded() {
    // Local release baseline on 2026-08-30: open 1.116 ms, sample count 33.917 us,
    // 290,044 bytes read, and 6,052,757 bytes of peak live allocation.
    let path =
        Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/v09_perf_many_samples.mp4");
    let file = File::open(path).expect("fixture");
    let counted = CountedReader {
        file,
        bytes_read: 0,
    };
    let baseline = CURRENT.load(Ordering::Relaxed);
    PEAK.store(baseline, Ordering::Relaxed);
    let started = Instant::now();
    let reader = Mp4Reader::open(counted).expect("large fixture must parse");
    let open_elapsed = started.elapsed();
    let peak = PEAK.load(Ordering::Relaxed).saturating_sub(baseline);
    let sample_started = Instant::now();
    assert_eq!(reader.samples(TrackId(1)).expect("samples").count(), 72_000);
    let sample_elapsed = sample_started.elapsed();
    let counted = reader.into_inner();

    eprintln!(
        "open={open_elapsed:?} sample_count={sample_elapsed:?} bytes_read={} peak_bytes={peak}",
        counted.bytes_read
    );

    assert!(
        counted.bytes_read <= 289_996 + 4_096,
        "open read {} bytes",
        counted.bytes_read
    );
    assert!(
        open_elapsed < Duration::from_millis(200),
        "open took {open_elapsed:?}"
    );
    assert!(
        sample_elapsed < Duration::from_millis(10),
        "sample count took {sample_elapsed:?}"
    );
    assert!(
        peak < 16 * 1024 * 1024,
        "open allocated {peak} bytes at peak"
    );
}