arcly-stream 0.1.6

An open-extensible live-media streaming kernel: lock-free zero-copy frame fan-out, instant-start GOP cache, a pluggable multi-protocol ingestion layer (RTMP, RTSP, SRT, WHIP/WHEP shipped), and a feature-gated pure-Rust media plane (MPEG-TS/HLS/fMP4) — runtime, config, and metrics free.
Documentation
//! Shared Annex-B NAL utilities for the NAL-based codecs (H.264, H.265, VVC).
//!
//! All three frame their bitstreams identically — `00 00 01` / `00 00 00 01`
//! start codes with `00 00 03` emulation prevention — and differ only in NAL
//! header layout and parameter-set field order. Start-code scanning delegates to
//! the crate-canonical `memchr`-accelerated scanner; this module owns NAL
//! iteration, RBSP de-emulation, and conformance cropping.

/// Iterate the NAL units of an Annex-B bytestream, yielding each NAL payload
/// (including its NAL header) **without** the start code.
pub fn iter_nals(data: &[u8]) -> NalIter<'_> {
    NalIter { data, pos: 0 }
}

/// Iterator over Annex-B NAL units. See [`iter_nals`].
pub struct NalIter<'a> {
    data: &'a [u8],
    pos: usize,
}

impl<'a> Iterator for NalIter<'a> {
    type Item = &'a [u8];

    fn next(&mut self) -> Option<&'a [u8]> {
        let start = next_start_code(self.data, self.pos)?;
        let nal_begin = start.0 + start.1;
        let nal_end = match next_start_code(self.data, nal_begin) {
            Some((off, _)) => off,
            None => self.data.len(),
        };
        self.pos = nal_end;
        if nal_begin >= nal_end {
            return self.next();
        }
        Some(&self.data[nal_begin..nal_end])
    }
}

/// Find the next start code at/after `from`, returning `(offset, len)` where
/// `len` is 3 (`00 00 01`) or 4 (`00 00 00 01`).
///
/// The start code must *begin* at or after `from` (its leading zeros are not
/// sought before the origin) — the contract [`NalIter`] relies on as it resumes
/// past each consumed NAL.
pub fn next_start_code(data: &[u8], from: usize) -> Option<(usize, usize)> {
    crate::bytescan::scan_start_code(data, from, from)
}

/// Strip emulation-prevention bytes (`00 00 03` → `00 00`) from an RBSP.
pub fn unescape_rbsp(nal: &[u8]) -> Vec<u8> {
    let mut out = Vec::with_capacity(nal.len());
    let mut zeros = 0;
    for &b in nal {
        if zeros >= 2 && b == 0x03 {
            zeros = 0;
            continue;
        }
        if b == 0 {
            zeros += 1;
        } else {
            zeros = 0;
        }
        out.push(b);
    }
    out
}

/// Apply HEVC/VVC conformance-window cropping to coded luma dimensions,
/// returning the displayed `(width, height)`. `chroma_format_idc` selects the
/// crop unit (4:2:0 → 2×2, 4:2:2 → 2×1, 4:4:4 / mono → 1×1).
pub fn conformance_dims(
    width_luma: u32,
    height_luma: u32,
    chroma_format_idc: u32,
    crop_left: u32,
    crop_right: u32,
    crop_top: u32,
    crop_bottom: u32,
) -> (u32, u32) {
    let (sub_w, sub_h) = match chroma_format_idc {
        1 => (2, 2), // 4:2:0
        2 => (2, 1), // 4:2:2
        _ => (1, 1), // 4:4:4 or monochrome
    };
    let width = width_luma.saturating_sub((crop_left + crop_right) * sub_w);
    let height = height_luma.saturating_sub((crop_top + crop_bottom) * sub_h);
    (width, height)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn iterates_three_and_four_byte_start_codes() {
        let data = [0, 0, 0, 1, 9, 0xF0, 0, 0, 1, 7, 0x42];
        let nals: Vec<&[u8]> = iter_nals(&data).collect();
        assert_eq!(nals, vec![&[9u8, 0xF0][..], &[7u8, 0x42][..]]);
    }

    #[test]
    fn unescape_removes_emulation_bytes() {
        assert_eq!(unescape_rbsp(&[0, 0, 3, 1]), vec![0, 0, 1]);
        assert_eq!(unescape_rbsp(&[0, 0, 3, 0, 0, 3, 2]), vec![0, 0, 0, 0, 2]);
    }

    #[test]
    fn conformance_crop_420() {
        // 1920x1088 coded, crop 4 rows off the bottom in 4:2:0 → 1920x1080.
        assert_eq!(conformance_dims(1920, 1088, 1, 0, 0, 0, 4), (1920, 1080));
    }
}