arcly-stream 0.1.0

A high-performance live-media streaming kernel: lock-free zero-copy frame fan-out, instant-start GOP cache, pluggable HLS/recording, and trait-driven protocol/storage/auth/observer extension points — runtime, config, and metrics free.
Documentation
//! H.265 (HEVC) bitstream helpers: NAL classification and SPS resolution
//! extraction. Shares the Annex-B scanner and conformance-crop math with H.264.

use super::bitreader::BitReader;
use super::nal::{conformance_dims, iter_nals, unescape_rbsp};
use super::parser::{CodecParser, VideoParams};
use crate::CodecId;

/// NAL unit type for a video parameter set.
pub const NAL_VPS: u8 = 32;
/// NAL unit type for a sequence parameter set.
pub const NAL_SPS: u8 = 33;
/// NAL unit type for a picture parameter set.
pub const NAL_PPS: u8 = 34;

/// HEVC NAL unit type, read from the 2-byte NAL header.
fn nal_type(nal: &[u8]) -> Option<u8> {
    nal.first().map(|b| (b >> 1) & 0x3F)
}

/// HEVC IRAP (random-access) VCL NAL types are `16..=23`
/// (BLA/IDR/CRA). A frame containing any such VCL NAL is a random-access point.
fn is_irap(t: u8) -> bool {
    (16..=23).contains(&t)
}

/// Skip the HEVC `profile_tier_level`, capturing `(profile_idc, tier, level_idc)`.
fn read_profile_tier_level(r: &mut BitReader, max_sub_layers_minus1: u32) -> Option<(u8, u8, u8)> {
    // general profile portion is 88 bits, then 8-bit general_level_idc.
    let _general_profile_space = r.read_bits(2)?;
    let general_tier_flag = r.read_bit()? as u8;
    let general_profile_idc = r.read_bits(5)? as u8;
    let _compat = r.read_bits(32)?; // 32 compatibility flags
    let _constraint_hi = r.read_bits(32)?; // 48 constraint/reserved/inbld bits …
    let _constraint_lo = r.read_bits(16)?; // … = 48 total
    let general_level_idc = r.read_bits(8)? as u8;

    // Sub-layer present flags.
    let mut sub_profile = [false; 8];
    let mut sub_level = [false; 8];
    for i in 0..max_sub_layers_minus1 as usize {
        sub_profile[i] = r.read_bit()? == 1;
        sub_level[i] = r.read_bit()? == 1;
    }
    if max_sub_layers_minus1 > 0 {
        for _ in max_sub_layers_minus1..8 {
            let _reserved = r.read_bits(2)?;
        }
    }
    for i in 0..max_sub_layers_minus1 as usize {
        if sub_profile[i] {
            // 88-bit sub-layer profile block.
            r.read_bits(32)?;
            r.read_bits(32)?;
            r.read_bits(24)?;
        }
        if sub_level[i] {
            r.read_bits(8)?;
        }
    }
    Some((general_profile_idc, general_tier_flag, general_level_idc))
}

/// Parse an HEVC SPS NAL (including its 2-byte header) into [`VideoParams`].
fn parse_sps(nal: &[u8]) -> Option<VideoParams> {
    if nal.len() < 3 || nal_type(nal)? != NAL_SPS {
        return None;
    }
    let rbsp = unescape_rbsp(&nal[2..]);
    let mut r = BitReader::new(&rbsp);

    let _sps_video_parameter_set_id = r.read_bits(4)?;
    let max_sub_layers_minus1 = r.read_bits(3)?;
    let _sps_temporal_id_nesting = r.read_bit()?;

    let (profile, tier, level) = read_profile_tier_level(&mut r, max_sub_layers_minus1)?;

    let _sps_seq_parameter_set_id = r.read_ue()?;
    let chroma_format_idc = r.read_ue()?;
    if chroma_format_idc == 3 {
        let _separate_colour_plane = r.read_bit()?;
    }
    let width_luma = r.read_ue()?;
    let height_luma = r.read_ue()?;

    let (mut l, mut rr, mut t, mut b) = (0, 0, 0, 0);
    if r.read_bit()? == 1 {
        l = r.read_ue()?;
        rr = r.read_ue()?;
        t = r.read_ue()?;
        b = r.read_ue()?;
    }
    let (width, height) = conformance_dims(width_luma, height_luma, chroma_format_idc, l, rr, t, b);

    Some(VideoParams {
        width,
        height,
        profile,
        level,
        tier,
        bit_depth: 8,
    })
}

/// [`CodecParser`] implementation for H.265 / HEVC.
pub struct H265;

impl CodecParser for H265 {
    const CODEC: CodecId = CodecId::H265;

    fn parse_config(data: &[u8]) -> Option<VideoParams> {
        iter_nals(data).find_map(parse_sps)
    }

    fn is_random_access_point(data: &[u8]) -> bool {
        iter_nals(data).any(|n| nal_type(n).is_some_and(|t| t <= 31 && is_irap(t)))
    }

    fn carries_config(data: &[u8]) -> bool {
        iter_nals(data)
            .any(|n| nal_type(n).is_some_and(|t| matches!(t, NAL_VPS | NAL_SPS | NAL_PPS)))
    }

    fn hls_codec_string(p: &VideoParams) -> String {
        // hvc1.{profile_space + profile_idc}.{compat}.{tier}{level}.{constraints}
        let tier = if p.tier == 1 { 'H' } else { 'L' };
        format!("hvc1.{}.6.{}{}.B0", p.profile, tier, p.level)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::codec::testutil::BitWriter;

    /// Synthesize an HEVC SPS NAL for 1920x1080 (Main, level 4.0), 4:2:0 with a
    /// 4-row conformance crop (coded 1088 → displayed 1080).
    fn sps_1920x1080() -> Vec<u8> {
        let mut w = BitWriter::default();
        w.bits(0, 4); // sps_video_parameter_set_id
        w.bits(0, 3); // sps_max_sub_layers_minus1 = 0 (no sub-layer loops)
        w.bit(0); // sps_temporal_id_nesting_flag
                  // profile_tier_level (96 bits): profile_space(2) tier(1) idc(5) …
        w.bits(0, 2); // general_profile_space
        w.bit(0); // general_tier_flag = 0 (Main tier)
        w.bits(1, 5); // general_profile_idc = 1 (Main)
        w.bits(0, 32); // compatibility flags
        w.bits(0, 32); // constraint/reserved (hi)
        w.bits(0, 16); // constraint/reserved (lo) → 48 total
        w.bits(120, 8); // general_level_idc = 120 (level 4.0)
        w.ue(0); // sps_seq_parameter_set_id
        w.ue(1); // chroma_format_idc = 1 (4:2:0)
        w.ue(1920); // pic_width_in_luma_samples
        w.ue(1088); // pic_height_in_luma_samples
        w.bit(1); // conformance_window_flag
        w.ue(0); // conf_win_left_offset
        w.ue(0); // conf_win_right_offset
        w.ue(0); // conf_win_top_offset
        w.ue(2); // conf_win_bottom_offset → 1088 - 2*2 = 1084? see below
        let mut nal = vec![0x42u8, 0x01]; // NAL header: type 33 (SPS), tid+1=1
        nal.extend_from_slice(&w.bytes());
        nal
    }

    #[test]
    fn parse_sps_extracts_resolution() {
        let p = parse_sps(&sps_1920x1080()).expect("parse");
        // 4:2:0 crop unit Y = 2, bottom offset 2 → 1088 - 2*2 = 1084.
        assert_eq!((p.width, p.height), (1920, 1084));
        assert_eq!((p.profile, p.tier, p.level), (1, 0, 120));
    }

    #[test]
    fn classifies_irap_and_config() {
        let mut au = vec![0, 0, 0, 1];
        au.extend_from_slice(&sps_1920x1080());
        // IDR_W_RADL VCL NAL (type 19): byte0 = 19<<1 = 0x26.
        au.extend_from_slice(&[0, 0, 0, 1, 0x26, 0x01, 0xAA]);

        assert!(H265::is_random_access_point(&au));
        assert!(H265::carries_config(&au));
        let p = H265::parse_config(&au).expect("params");
        assert_eq!(p.width, 1920);
        assert_eq!(H265::hls_codec_string(&p), "hvc1.1.6.L120.B0");

        // A TRAIL_R slice (type 1) alone is not a RAP.
        assert!(!H265::is_random_access_point(&[
            0, 0, 0, 1, 0x02, 0x01, 0xAA
        ]));
    }
}