1use crate::bit_reader::BitReader;
29use crate::BitstreamError;
30
31pub const NAL_TYPE_IDR: u8 = 5;
35pub const NAL_TYPE_SPS: u8 = 7;
37pub const NAL_TYPE_PPS: u8 = 8;
39pub const NAL_TYPE_NON_IDR_SLICE: u8 = 1;
41pub const NAL_TYPE_AUD: u8 = 9;
43
44pub fn split_annex_b(buf: &[u8]) -> Vec<&[u8]> {
54 let mut out = Vec::new();
55 let mut i = 0;
56 let n = buf.len();
57 let mut last_payload_start: Option<usize> = None;
58 while i < n {
59 let four =
60 i + 3 < n && buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 0 && buf[i + 3] == 1;
61 let three = !four && i + 2 < n && buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1;
62 if four || three {
63 if let Some(start) = last_payload_start.take() {
64 out.push(&buf[start..i]);
65 }
66 i += if four { 4 } else { 3 };
67 last_payload_start = Some(i);
68 continue;
69 }
70 i += 1;
71 }
72 if let Some(start) = last_payload_start.take() {
73 out.push(&buf[start..n]);
74 }
75 out
76}
77
78pub fn ebsp_to_rbsp(ebsp: &[u8]) -> Vec<u8> {
82 let mut out = Vec::with_capacity(ebsp.len());
83 let mut i = 0;
84 let n = ebsp.len();
85 while i < n {
86 if i + 2 < n && ebsp[i] == 0 && ebsp[i + 1] == 0 && ebsp[i + 2] == 3 {
87 out.push(0);
88 out.push(0);
89 i += 3;
90 } else {
91 out.push(ebsp[i]);
92 i += 1;
93 }
94 }
95 out
96}
97
98pub fn nal_header(byte: u8) -> (u8, u8, u8) {
101 let forbidden = (byte >> 7) & 1;
102 let nal_ref_idc = (byte >> 5) & 0x3;
103 let nal_type = byte & 0x1f;
104 (forbidden, nal_ref_idc, nal_type)
105}
106
107#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
114pub struct H264FrameCropping {
115 pub left: u32,
116 pub right: u32,
117 pub top: u32,
118 pub bottom: u32,
119}
120
121#[derive(Debug, Clone, Default, PartialEq, Eq)]
126pub struct H264Sps {
127 pub profile_idc: u8,
128 pub constraint_set_flags: u8,
131 pub level_idc: u8,
132 pub seq_parameter_set_id: u8,
133
134 pub chroma_format_idc: u8,
135 pub separate_colour_plane_flag: bool,
136 pub bit_depth_luma_minus8: u8,
137 pub bit_depth_chroma_minus8: u8,
138
139 pub log2_max_frame_num_minus4: u8,
140 pub pic_order_cnt_type: u8,
141 pub log2_max_pic_order_cnt_lsb_minus4: u8,
142 pub delta_pic_order_always_zero_flag: bool,
143 pub max_num_ref_frames: u32,
144 pub gaps_in_frame_num_value_allowed_flag: bool,
145
146 pub pic_width_in_mbs_minus1: u32,
147 pub pic_height_in_map_units_minus1: u32,
148 pub frame_mbs_only_flag: bool,
149 pub mb_adaptive_frame_field_flag: bool,
150 pub direct_8x8_inference_flag: bool,
151
152 pub frame_cropping: Option<H264FrameCropping>,
153}
154
155impl H264Sps {
156 pub fn has_high_profile_extension(profile_idc: u8) -> bool {
159 matches!(
160 profile_idc,
161 100 | 110 | 122 | 244 | 44 | 83 | 86 | 118 | 128 | 138 | 139 | 134 | 135
162 )
163 }
164
165 pub fn coded_width(&self) -> u32 {
167 (self.pic_width_in_mbs_minus1 + 1) * 16
168 }
169
170 pub fn coded_height(&self) -> u32 {
173 let mb_only = if self.frame_mbs_only_flag { 1 } else { 0 };
174 (self.pic_height_in_map_units_minus1 + 1) * 16 * (2 - mb_only)
175 }
176
177 fn sub_width_c(&self) -> u32 {
181 match self.chroma_format_idc {
182 1 | 2 => 2, 3 => 1, _ => 1,
185 }
186 }
187
188 fn sub_height_c(&self) -> u32 {
189 match self.chroma_format_idc {
190 1 => 2, 2 | 3 => 1, _ => 1,
193 }
194 }
195
196 pub fn display_width(&self) -> u32 {
201 let coded = self.coded_width();
202 let Some(c) = self.frame_cropping else {
203 return coded;
204 };
205 let crop_x = if self.chroma_format_idc == 0 || self.separate_colour_plane_flag {
206 1
207 } else {
208 self.sub_width_c()
209 };
210 coded.saturating_sub((c.left + c.right) * crop_x)
211 }
212
213 pub fn display_height(&self) -> u32 {
215 let coded = self.coded_height();
216 let Some(c) = self.frame_cropping else {
217 return coded;
218 };
219 let crop_y = if self.chroma_format_idc == 0 || self.separate_colour_plane_flag {
220 2 - if self.frame_mbs_only_flag { 1 } else { 0 }
221 } else {
222 self.sub_height_c() * (2 - if self.frame_mbs_only_flag { 1 } else { 0 })
223 };
224 coded.saturating_sub((c.top + c.bottom) * crop_y)
225 }
226}
227
228#[derive(Debug, Clone, Default, PartialEq, Eq)]
230pub struct H264Pps {
231 pub pic_parameter_set_id: u8,
232 pub seq_parameter_set_id: u8,
233 pub entropy_coding_mode_flag: bool,
234 pub bottom_field_pic_order_in_frame_present_flag: bool,
235 pub num_slice_groups_minus1: u32,
236 pub num_ref_idx_l0_default_active_minus1: u8,
237 pub num_ref_idx_l1_default_active_minus1: u8,
238 pub weighted_pred_flag: bool,
239 pub weighted_bipred_idc: u8,
240 pub pic_init_qp_minus26: i32,
241 pub pic_init_qs_minus26: i32,
242 pub chroma_qp_index_offset: i32,
243 pub deblocking_filter_control_present_flag: bool,
244 pub constrained_intra_pred_flag: bool,
245 pub redundant_pic_cnt_present_flag: bool,
246 pub transform_8x8_mode_flag: bool,
249 pub second_chroma_qp_index_offset: i32,
252}
253
254#[derive(Debug, Clone, Default, PartialEq, Eq)]
258pub struct H264SliceHeader {
259 pub first_mb_in_slice: u32,
260 pub slice_type: u8,
262 pub pic_parameter_set_id: u8,
263 pub frame_num: u32,
264 pub field_pic_flag: bool,
265 pub bottom_field_flag: bool,
266 pub idr_pic_id: Option<u32>,
268 pub pic_order_cnt_lsb: u32,
269}
270
271impl H264SliceHeader {
272 pub fn is_i_slice(&self) -> bool {
275 self.slice_type % 5 == 2
276 }
277}
278
279#[derive(Debug)]
281pub struct H264IdrParse<'a> {
282 pub sps: H264Sps,
283 pub pps: H264Pps,
284 pub slice_header: H264SliceHeader,
285 pub idr_access_unit: &'a [u8],
289}
290
291pub fn parse_sps(rbsp: &[u8]) -> Result<H264Sps, BitstreamError> {
299 if rbsp.len() < 3 {
300 return Err(BitstreamError::unexpected_end("SPS shorter than 3 bytes"));
301 }
302 let mut r = BitReader::new(rbsp);
303
304 let profile_idc = r.u(8) as u8;
305 let constraint_set_flags = r.u(8) as u8;
306 let level_idc = r.u(8) as u8;
307 let mut sps = H264Sps {
308 profile_idc,
309 constraint_set_flags,
310 level_idc,
311 ..H264Sps::default()
312 };
313 sps.seq_parameter_set_id = r.ue()? as u8;
314
315 if H264Sps::has_high_profile_extension(profile_idc) {
316 sps.chroma_format_idc = r.ue()? as u8;
317 if sps.chroma_format_idc == 3 {
318 sps.separate_colour_plane_flag = r.u(1) != 0;
319 }
320 sps.bit_depth_luma_minus8 = r.ue()? as u8;
321 sps.bit_depth_chroma_minus8 = r.ue()? as u8;
322 let _qpprime_y_zero_transform_bypass = r.u(1);
323 let seq_scaling_matrix_present = r.u(1);
324 if seq_scaling_matrix_present != 0 {
325 return Err(BitstreamError::unsupported(
326 "SPS seq_scaling_matrix_present_flag=1 not supported by minimal parser",
327 ));
328 }
329 } else {
330 sps.chroma_format_idc = 1; }
332
333 sps.log2_max_frame_num_minus4 = r.ue()? as u8;
334 sps.pic_order_cnt_type = r.ue()? as u8;
335 match sps.pic_order_cnt_type {
336 0 => {
337 sps.log2_max_pic_order_cnt_lsb_minus4 = r.ue()? as u8;
338 }
339 1 => {
340 sps.delta_pic_order_always_zero_flag = r.u(1) != 0;
341 let _offset_for_non_ref_pic = r.se()?;
342 let _offset_for_top_to_bottom_field = r.se()?;
343 let num_ref_frames_in_pic_order_cnt_cycle = r.ue()?;
344 for _ in 0..num_ref_frames_in_pic_order_cnt_cycle {
345 let _offset_for_ref_frame = r.se()?;
346 }
347 }
348 2 => { }
349 other => {
350 return Err(BitstreamError::invalid(format!(
351 "SPS pic_order_cnt_type={other} (must be 0..2)"
352 )));
353 }
354 }
355 sps.max_num_ref_frames = r.ue()?;
356 sps.gaps_in_frame_num_value_allowed_flag = r.u(1) != 0;
357 sps.pic_width_in_mbs_minus1 = r.ue()?;
358 sps.pic_height_in_map_units_minus1 = r.ue()?;
359 sps.frame_mbs_only_flag = r.u(1) != 0;
360 if !sps.frame_mbs_only_flag {
361 sps.mb_adaptive_frame_field_flag = r.u(1) != 0;
362 }
363 sps.direct_8x8_inference_flag = r.u(1) != 0;
364 let frame_cropping_flag = r.u(1);
365 if frame_cropping_flag != 0 {
366 let left = r.ue()?;
367 let right = r.ue()?;
368 let top = r.ue()?;
369 let bottom = r.ue()?;
370 sps.frame_cropping = Some(H264FrameCropping {
371 left,
372 right,
373 top,
374 bottom,
375 });
376 }
377 Ok(sps)
379}
380
381pub fn parse_sps_nal(nal: &[u8]) -> Result<H264Sps, BitstreamError> {
384 if nal.is_empty() {
385 return Err(BitstreamError::unexpected_end("empty SPS NAL"));
386 }
387 let (_, _, nal_type) = nal_header(nal[0]);
388 if nal_type != NAL_TYPE_SPS {
389 return Err(BitstreamError::invalid(format!(
390 "expected SPS NAL (type=7), got type={nal_type}"
391 )));
392 }
393 let rbsp = ebsp_to_rbsp(&nal[1..]);
394 parse_sps(&rbsp)
395}
396
397pub fn parse_pps(rbsp: &[u8]) -> Result<H264Pps, BitstreamError> {
400 if rbsp.is_empty() {
401 return Err(BitstreamError::unexpected_end("empty PPS"));
402 }
403 let mut r = BitReader::new(rbsp);
404 let mut pps = H264Pps {
405 pic_parameter_set_id: r.ue()? as u8,
406 seq_parameter_set_id: r.ue()? as u8,
407 entropy_coding_mode_flag: r.u(1) != 0,
408 bottom_field_pic_order_in_frame_present_flag: r.u(1) != 0,
409 num_slice_groups_minus1: r.ue()?,
410 ..H264Pps::default()
411 };
412 if pps.num_slice_groups_minus1 != 0 {
413 return Err(BitstreamError::unsupported(
414 "PPS num_slice_groups_minus1>0 (FMO/ASO) not supported by minimal parser",
415 ));
416 }
417 pps.num_ref_idx_l0_default_active_minus1 = r.ue()? as u8;
418 pps.num_ref_idx_l1_default_active_minus1 = r.ue()? as u8;
419 pps.weighted_pred_flag = r.u(1) != 0;
420 pps.weighted_bipred_idc = r.u(2) as u8;
421 pps.pic_init_qp_minus26 = r.se()?;
422 pps.pic_init_qs_minus26 = r.se()?;
423 pps.chroma_qp_index_offset = r.se()?;
424 pps.deblocking_filter_control_present_flag = r.u(1) != 0;
425 pps.constrained_intra_pred_flag = r.u(1) != 0;
426 pps.redundant_pic_cnt_present_flag = r.u(1) != 0;
427
428 if r.more_rbsp_data() {
429 pps.transform_8x8_mode_flag = r.u(1) != 0;
430 let pic_scaling_matrix_present = r.u(1);
431 if pic_scaling_matrix_present != 0 {
432 return Err(BitstreamError::unsupported(
433 "PPS pic_scaling_matrix_present_flag=1 not supported by minimal parser",
434 ));
435 }
436 pps.second_chroma_qp_index_offset = r.se()?;
437 } else {
438 pps.second_chroma_qp_index_offset = pps.chroma_qp_index_offset;
439 }
440 Ok(pps)
441}
442
443pub fn parse_pps_nal(nal: &[u8]) -> Result<H264Pps, BitstreamError> {
445 if nal.is_empty() {
446 return Err(BitstreamError::unexpected_end("empty PPS NAL"));
447 }
448 let (_, _, nal_type) = nal_header(nal[0]);
449 if nal_type != NAL_TYPE_PPS {
450 return Err(BitstreamError::invalid(format!(
451 "expected PPS NAL (type=8), got type={nal_type}"
452 )));
453 }
454 let rbsp = ebsp_to_rbsp(&nal[1..]);
455 parse_pps(&rbsp)
456}
457
458pub fn parse_slice_header_minimal(
467 rbsp: &[u8],
468 nal_unit_type: u8,
469 sps: &H264Sps,
470 pps: &H264Pps,
471) -> Result<H264SliceHeader, BitstreamError> {
472 let _ = pps; let mut r = BitReader::new(rbsp);
474 let mut sh = H264SliceHeader {
475 first_mb_in_slice: r.ue()?,
476 slice_type: r.ue()? as u8,
477 pic_parameter_set_id: r.ue()? as u8,
478 ..H264SliceHeader::default()
479 };
480 if sps.separate_colour_plane_flag {
481 let _colour_plane_id = r.u(2);
482 }
483 sh.frame_num = r.u(sps.log2_max_frame_num_minus4 as u32 + 4);
484 if !sps.frame_mbs_only_flag {
485 sh.field_pic_flag = r.u(1) != 0;
486 if sh.field_pic_flag {
487 sh.bottom_field_flag = r.u(1) != 0;
488 }
489 }
490 if nal_unit_type == NAL_TYPE_IDR {
491 sh.idr_pic_id = Some(r.ue()?);
492 }
493 if sps.pic_order_cnt_type == 0 {
494 sh.pic_order_cnt_lsb = r.u(sps.log2_max_pic_order_cnt_lsb_minus4 as u32 + 4);
495 }
496 Ok(sh)
501}
502
503pub fn parse_idr_only(stream: &[u8]) -> Result<H264IdrParse<'_>, BitstreamError> {
510 let nals = locate_annex_b(stream);
514 if nals.is_empty() {
515 return Err(BitstreamError::invalid("no NAL units found in stream"));
516 }
517
518 let mut sps: Option<H264Sps> = None;
519 let mut pps: Option<H264Pps> = None;
520 let mut idr_idx: Option<usize> = None;
521 for (idx, n) in nals.iter().enumerate() {
522 let body = &stream[n.body_start..n.body_end];
523 if body.is_empty() {
524 continue;
525 }
526 let (_, _, nal_type) = nal_header(body[0]);
527 match nal_type {
528 NAL_TYPE_SPS if sps.is_none() => {
529 sps = Some(parse_sps_nal(body)?);
530 }
531 NAL_TYPE_PPS if pps.is_none() => {
532 pps = Some(parse_pps_nal(body)?);
533 }
534 NAL_TYPE_IDR if idr_idx.is_none() => {
535 idr_idx = Some(idx);
536 }
537 _ => {}
538 }
539 }
540 let sps = sps.ok_or_else(|| BitstreamError::invalid("stream has no SPS NAL"))?;
541 let pps = pps.ok_or_else(|| BitstreamError::invalid("stream has no PPS NAL"))?;
542 let idr_idx = idr_idx.ok_or_else(|| BitstreamError::invalid("stream has no IDR NAL"))?;
543
544 let idr_nal = &nals[idr_idx];
545 let body = &stream[idr_nal.body_start..idr_nal.body_end];
546 let rbsp = ebsp_to_rbsp(&body[1..]);
547 let slice_header = parse_slice_header_minimal(&rbsp, NAL_TYPE_IDR, &sps, &pps)?;
548
549 let access_unit = &stream[idr_nal.start_code_start..];
550 Ok(H264IdrParse {
551 sps,
552 pps,
553 slice_header,
554 idr_access_unit: access_unit,
555 })
556}
557
558#[derive(Debug, Clone, Copy)]
561struct NalLoc {
562 start_code_start: usize,
564 body_start: usize,
565 body_end: usize,
566}
567
568fn locate_annex_b(buf: &[u8]) -> Vec<NalLoc> {
569 let mut out = Vec::new();
570 let mut i = 0;
571 let n = buf.len();
572 let mut current: Option<(usize, usize)> = None; while i < n {
574 let four =
575 i + 3 < n && buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 0 && buf[i + 3] == 1;
576 let three = !four && i + 2 < n && buf[i] == 0 && buf[i + 1] == 0 && buf[i + 2] == 1;
577 if four || three {
578 if let Some((sc, body_start)) = current.take() {
579 out.push(NalLoc {
580 start_code_start: sc,
581 body_start,
582 body_end: i,
583 });
584 }
585 let sc = i;
586 i += if four { 4 } else { 3 };
587 current = Some((sc, i));
588 continue;
589 }
590 i += 1;
591 }
592 if let Some((sc, body_start)) = current.take() {
593 out.push(NalLoc {
594 start_code_start: sc,
595 body_start,
596 body_end: n,
597 });
598 }
599 out
600}
601
602#[cfg(test)]
605mod tests {
606 use super::*;
607
608 #[test]
609 fn split_finds_three_nal_starts_in_synthetic_buffer() {
610 let buf = [
611 0, 0, 0, 1, 0x67, 0xaa, 0, 0, 1, 0x68, 0xbb, 0, 0, 0, 1, 0x65, 0xcc, 0xdd, ];
615 let nals = split_annex_b(&buf);
616 assert_eq!(nals.len(), 3);
617 assert_eq!(nals[0], &[0x67, 0xaa]);
618 assert_eq!(nals[1], &[0x68, 0xbb]);
619 assert_eq!(nals[2], &[0x65, 0xcc, 0xdd]);
620 }
621
622 #[test]
623 fn ebsp_to_rbsp_strips_03_in_zero_zero_run() {
624 let ebsp = [0x00, 0x00, 0x03, 0x01, 0x02, 0x03];
625 let rbsp = ebsp_to_rbsp(&ebsp);
626 assert_eq!(rbsp, &[0x00, 0x00, 0x01, 0x02, 0x03]);
627 }
628
629 #[test]
630 fn nal_header_decodes_idr_byte() {
631 let (f, nri, t) = nal_header(0x65);
633 assert_eq!(f, 0);
634 assert_eq!(nri, 3);
635 assert_eq!(t, NAL_TYPE_IDR);
636 }
637}