1use super::bitreader::BitReader;
5use super::nal::{conformance_dims, iter_nals, unescape_rbsp};
6use super::parser::{CodecParser, VideoParams};
7use crate::CodecId;
8
9pub const NAL_VPS: u8 = 32;
11pub const NAL_SPS: u8 = 33;
13pub const NAL_PPS: u8 = 34;
15
16fn nal_type(nal: &[u8]) -> Option<u8> {
18 nal.first().map(|b| (b >> 1) & 0x3F)
19}
20
21fn is_irap(t: u8) -> bool {
24 (16..=23).contains(&t)
25}
26
27fn read_profile_tier_level(r: &mut BitReader, max_sub_layers_minus1: u32) -> Option<(u8, u8, u8)> {
29 let _general_profile_space = r.read_bits(2)?;
31 let general_tier_flag = r.read_bit()? as u8;
32 let general_profile_idc = r.read_bits(5)? as u8;
33 let _compat = r.read_bits(32)?; let _constraint_hi = r.read_bits(32)?; let _constraint_lo = r.read_bits(16)?; let general_level_idc = r.read_bits(8)? as u8;
37
38 let mut sub_profile = [false; 8];
40 let mut sub_level = [false; 8];
41 for i in 0..max_sub_layers_minus1 as usize {
42 sub_profile[i] = r.read_bit()? == 1;
43 sub_level[i] = r.read_bit()? == 1;
44 }
45 if max_sub_layers_minus1 > 0 {
46 for _ in max_sub_layers_minus1..8 {
47 let _reserved = r.read_bits(2)?;
48 }
49 }
50 for i in 0..max_sub_layers_minus1 as usize {
51 if sub_profile[i] {
52 r.read_bits(32)?;
54 r.read_bits(32)?;
55 r.read_bits(24)?;
56 }
57 if sub_level[i] {
58 r.read_bits(8)?;
59 }
60 }
61 Some((general_profile_idc, general_tier_flag, general_level_idc))
62}
63
64fn parse_sps(nal: &[u8]) -> Option<VideoParams> {
66 if nal.len() < 3 || nal_type(nal)? != NAL_SPS {
67 return None;
68 }
69 let rbsp = unescape_rbsp(&nal[2..]);
70 let mut r = BitReader::new(&rbsp);
71
72 let _sps_video_parameter_set_id = r.read_bits(4)?;
73 let max_sub_layers_minus1 = r.read_bits(3)?;
74 let _sps_temporal_id_nesting = r.read_bit()?;
75
76 let (profile, tier, level) = read_profile_tier_level(&mut r, max_sub_layers_minus1)?;
77
78 let _sps_seq_parameter_set_id = r.read_ue()?;
79 let chroma_format_idc = r.read_ue()?;
80 if chroma_format_idc == 3 {
81 let _separate_colour_plane = r.read_bit()?;
82 }
83 let width_luma = r.read_ue()?;
84 let height_luma = r.read_ue()?;
85
86 let (mut l, mut rr, mut t, mut b) = (0, 0, 0, 0);
87 if r.read_bit()? == 1 {
88 l = r.read_ue()?;
89 rr = r.read_ue()?;
90 t = r.read_ue()?;
91 b = r.read_ue()?;
92 }
93 let (width, height) = conformance_dims(width_luma, height_luma, chroma_format_idc, l, rr, t, b);
94
95 Some(VideoParams {
96 width,
97 height,
98 profile,
99 level,
100 tier,
101 bit_depth: 8,
102 })
103}
104
105pub struct H265;
107
108impl CodecParser for H265 {
109 const CODEC: CodecId = CodecId::H265;
110
111 fn parse_config(data: &[u8]) -> Option<VideoParams> {
112 iter_nals(data).find_map(parse_sps)
113 }
114
115 fn is_random_access_point(data: &[u8]) -> bool {
116 iter_nals(data).any(|n| nal_type(n).is_some_and(|t| t <= 31 && is_irap(t)))
117 }
118
119 fn carries_config(data: &[u8]) -> bool {
120 iter_nals(data)
121 .any(|n| nal_type(n).is_some_and(|t| matches!(t, NAL_VPS | NAL_SPS | NAL_PPS)))
122 }
123
124 fn hls_codec_string(p: &VideoParams) -> String {
125 let tier = if p.tier == 1 { 'H' } else { 'L' };
127 format!("hvc1.{}.6.{}{}.B0", p.profile, tier, p.level)
128 }
129}
130
131pub fn hvcc_config_record(data: &[u8]) -> Option<(VideoParams, Vec<u8>)> {
140 let params = H265::parse_config(data)?;
141
142 let mut vps: Vec<&[u8]> = Vec::new();
143 let mut sps: Vec<&[u8]> = Vec::new();
144 let mut pps: Vec<&[u8]> = Vec::new();
145 for nal in iter_nals(data) {
146 match nal_type(nal) {
147 Some(NAL_VPS) => vps.push(nal),
148 Some(NAL_SPS) => sps.push(nal),
149 Some(NAL_PPS) => pps.push(nal),
150 _ => {}
151 }
152 }
153 let first_sps = *sps.first()?;
154
155 let rbsp = unescape_rbsp(first_sps.get(2..)?);
158 let ptl = rbsp.get(1..13)?; let chroma_format_idc = 1u8; let bit_depth_minus8 = params.bit_depth.max(8).saturating_sub(8);
162
163 let mut rec = Vec::with_capacity(32 + first_sps.len());
164 rec.push(1); rec.extend_from_slice(ptl); rec.extend_from_slice(&[0xF0, 0x00]); rec.push(0xFC); rec.push(0xFC | (chroma_format_idc & 0x03)); rec.push(0xF8 | (bit_depth_minus8 & 0x07)); rec.push(0xF8 | (bit_depth_minus8 & 0x07)); rec.extend_from_slice(&0u16.to_be_bytes()); rec.push((1 << 3) | 0x03);
175
176 let groups = [(NAL_VPS, &vps), (NAL_SPS, &sps), (NAL_PPS, &pps)];
178 let present: Vec<_> = groups.iter().filter(|(_, v)| !v.is_empty()).collect();
179 rec.push(present.len() as u8); for (t, nals) in present {
181 rec.push(0x80 | (t & 0x3F)); rec.extend_from_slice(&(nals.len() as u16).to_be_bytes());
183 for nal in nals.iter() {
184 rec.extend_from_slice(&(nal.len() as u16).to_be_bytes());
185 rec.extend_from_slice(nal);
186 }
187 }
188 Some((params, rec))
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use crate::codec::testutil::BitWriter;
195
196 fn sps_1920x1080() -> Vec<u8> {
199 let mut w = BitWriter::default();
200 w.bits(0, 4); w.bits(0, 3); w.bit(0); w.bits(0, 2); w.bit(0); w.bits(1, 5); w.bits(0, 32); w.bits(0, 32); w.bits(0, 16); w.bits(120, 8); w.ue(0); w.ue(1); w.ue(1920); w.ue(1088); w.bit(1); w.ue(0); w.ue(0); w.ue(0); w.ue(2); let mut nal = vec![0x42u8, 0x01]; nal.extend_from_slice(&w.bytes());
222 nal
223 }
224
225 #[test]
226 fn parse_sps_extracts_resolution() {
227 let p = parse_sps(&sps_1920x1080()).expect("parse");
228 assert_eq!((p.width, p.height), (1920, 1084));
230 assert_eq!((p.profile, p.tier, p.level), (1, 0, 120));
231 }
232
233 #[test]
234 fn hvcc_config_record_copies_ptl_and_lists_arrays() {
235 let mut au = vec![0, 0, 0, 1, 0x40, 0x01, 0xAA]; au.extend_from_slice(&[0, 0, 0, 1]);
237 au.extend_from_slice(&sps_1920x1080()); au.extend_from_slice(&[0, 0, 0, 1, 0x44, 0x01, 0xBB]); let (params, rec) = hvcc_config_record(&au).expect("hvcC built");
241 assert_eq!((params.width, params.profile, params.level), (1920, 1, 120));
242
243 assert_eq!(rec[0], 1, "configurationVersion");
244 assert_eq!(rec[1] & 0x1F, 1, "general_profile_idc copied from SPS");
245 assert_eq!(rec[12], 120, "general_level_idc copied from SPS");
246 assert_eq!(rec[22], 3, "three NAL arrays (VPS/SPS/PPS)");
248 }
249
250 #[test]
251 fn hvcc_config_record_requires_an_sps() {
252 assert!(hvcc_config_record(&[0, 0, 0, 1, 0x40, 0x01, 0xAA]).is_none());
254 }
255
256 #[test]
257 fn classifies_irap_and_config() {
258 let mut au = vec![0, 0, 0, 1];
259 au.extend_from_slice(&sps_1920x1080());
260 au.extend_from_slice(&[0, 0, 0, 1, 0x26, 0x01, 0xAA]);
262
263 assert!(H265::is_random_access_point(&au));
264 assert!(H265::carries_config(&au));
265 let p = H265::parse_config(&au).expect("params");
266 assert_eq!(p.width, 1920);
267 assert_eq!(H265::hls_codec_string(&p), "hvc1.1.6.L120.B0");
268
269 assert!(!H265::is_random_access_point(&[
271 0, 0, 0, 1, 0x02, 0x01, 0xAA
272 ]));
273 }
274}