Skip to content

Commit 3f8618a

Browse files
committed
Add support for seq_parameter_set_extension_rbsp() and subset_seq_parameter_set_rbsp()
1 parent 5dd682d commit 3f8618a

File tree

8 files changed

+598
-8
lines changed

8 files changed

+598
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
* Add `Profile::CavlcIntra444` (profile_idc 44) and `Profile::MFCHigh` (profile_idc 134) enum variants.
1414
* Parse AvcC extension fields (`chroma_format`, `bit_depth_luma_minus8`, `bit_depth_chroma_minus8`, SPS extension NAL units) for High profile and above.
1515
* Add `slice_group_change_cycle`, `slice_alpha_c0_offset_div2` and `slice_beta_offset_div2 to `SliceHeader`
16+
* Parse `seq_parameter_set_extension_rbsp()` (NAL type 13) via new `sps_extension::SeqParameterSetExtension`.
17+
* Parse `subset_seq_parameter_set_rbsp()` (NAL type 15) via new `subset_sps::SubsetSps`, including SVC extension (profiles 83/86) and MVC extension (profiles 118/128/134).
18+
* Add `Context::put_subset_seq_param_set()`, `Context::subset_sps_by_id()`, and `Context::subset_sps()` for managing subset SPS state.
19+
* Add `ChromaInfo::chroma_array_type()` helper.
1620

1721
### Fixed
1822

examples/dump.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ use h264_reader::nal::sei::user_data_registered_itu_t_t35::ItuTT35;
66
use h264_reader::nal::sei::HeaderType;
77
use h264_reader::nal::slice::SliceHeader;
88
use h264_reader::nal::sps::SeqParameterSet;
9+
use h264_reader::nal::sps_extension::SeqParameterSetExtension;
10+
use h264_reader::nal::subset_sps::SubsetSps;
911
use h264_reader::nal::{sei, Nal, RefNal, UnitType};
1012
use h264_reader::push::NalInterest;
1113
use h264_reader::Context;
@@ -126,6 +128,23 @@ fn main() {
126128
}
127129
}
128130
}
131+
UnitType::SeqParameterSetExtension => {
132+
hex_dump(&nal);
133+
match SeqParameterSetExtension::from_bits(nal.rbsp_bits()) {
134+
Ok(ext) => println!("{:#?}", ext),
135+
Err(e) => println!("SPS extension error: {:?}", e),
136+
}
137+
}
138+
UnitType::SubsetSeqParameterSet => {
139+
hex_dump(&nal);
140+
match SubsetSps::from_bits(nal.rbsp_bits()) {
141+
Ok(subset) => {
142+
println!("{:#?}", subset);
143+
ctx.put_subset_seq_param_set(subset);
144+
}
145+
Err(e) => println!("Subset SPS error: {:?}", e),
146+
}
147+
}
129148
_ => {
130149
println!("Unhandled: {:?}", nal_unit_type);
131150
}

fuzz/fuzz_targets/fuzz_target_1.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
use libfuzzer_sys::fuzz_target;
33
use h264_reader::annexb::AnnexBReader;
44
use h264_reader::Context;
5-
use h264_reader::nal::{Nal, RefNal, UnitType, pps, slice, sei, sps};
5+
use h264_reader::nal::{Nal, RefNal, UnitType, pps, slice, sei, sps, sps_extension, subset_sps};
66
use h264_reader::push::NalInterest;
77

88
fuzz_target!(|data: &[u8]| {
@@ -45,6 +45,14 @@ fuzz_target!(|data: &[u8]| {
4545
UnitType::SliceLayerWithoutPartitioningIdr | UnitType::SliceLayerWithoutPartitioningNonIdr => {
4646
let _ = slice::SliceHeader::from_bits(&ctx, &mut nal.rbsp_bits(), hdr);
4747
},
48+
UnitType::SeqParameterSetExtension => {
49+
let _ = sps_extension::SeqParameterSetExtension::from_bits(nal.rbsp_bits());
50+
},
51+
UnitType::SubsetSeqParameterSet => {
52+
if let Ok(subset) = subset_sps::SubsetSps::from_bits(nal.rbsp_bits()) {
53+
ctx.put_subset_seq_param_set(subset);
54+
}
55+
},
4856
_ => {},
4957
}
5058
NalInterest::Buffer

src/lib.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub mod rbsp;
1919
pub struct Context {
2020
seq_param_sets: ParamSetMap<nal::sps::SeqParameterSet>,
2121
pic_param_sets: ParamSetMap<nal::pps::PicParameterSet>,
22+
subset_seq_param_sets: ParamSetMap<nal::subset_sps::SubsetSps>,
2223
}
2324
impl Context {
2425
#[inline]
@@ -51,6 +52,25 @@ impl Context {
5152
let i = usize::from(pps.pic_parameter_set_id.id());
5253
self.pic_param_sets.put(i, pps);
5354
}
55+
#[inline]
56+
pub fn subset_sps_by_id(
57+
&self,
58+
id: nal::sps::SeqParamSetId,
59+
) -> Option<&nal::subset_sps::SubsetSps> {
60+
self.subset_seq_param_sets.get(usize::from(id.id()))
61+
}
62+
#[inline]
63+
pub fn subset_sps(&self) -> impl Iterator<Item = &nal::subset_sps::SubsetSps> {
64+
self.subset_seq_param_sets.iter()
65+
}
66+
/// Stores a subset SPS and also registers its base SPS in the regular SPS map
67+
/// so that PPS and slice header parsing can find it via `sps_by_id()`.
68+
#[inline]
69+
pub fn put_subset_seq_param_set(&mut self, subset: nal::subset_sps::SubsetSps) {
70+
let i = usize::from(subset.sps.seq_parameter_set_id.id());
71+
self.seq_param_sets.put(i, subset.sps.clone());
72+
self.subset_seq_param_sets.put(i, subset);
73+
}
5474
}
5575

5676
/// A map for very small indexes; SPS/PPS IDs must be in `[0, 32)`, and typically only 0 is used.

src/nal/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ pub mod pps;
88
pub mod sei;
99
pub mod slice;
1010
pub mod sps;
11+
pub mod sps_extension;
12+
pub mod subset_sps;
1113

1214
use crate::rbsp;
1315
use hex_slice::AsHex;

src/nal/sps.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,16 @@ pub struct ChromaInfo {
440440
pub scaling_matrix: Option<SeqScalingMatrix>,
441441
}
442442
impl ChromaInfo {
443+
/// Returns `ChromaArrayType` as defined by the spec: 0 if `separate_colour_plane_flag` is
444+
/// true, otherwise equal to `chroma_format_idc`.
445+
pub fn chroma_array_type(&self) -> u8 {
446+
if self.separate_colour_plane_flag {
447+
0
448+
} else {
449+
self.chroma_format.to_u32() as u8
450+
}
451+
}
452+
443453
pub fn read<R: BitRead>(r: &mut R, profile_idc: ProfileIdc) -> Result<ChromaInfo, SpsError> {
444454
if profile_idc.has_chroma_info() {
445455
let chroma_format_idc = r.read_ue("chroma_format_idc")?;
@@ -1121,7 +1131,11 @@ pub struct SeqParameterSet {
11211131
pub vui_parameters: Option<VuiParameters>,
11221132
}
11231133
impl SeqParameterSet {
1124-
pub fn from_bits<R: BitRead>(mut r: R) -> Result<SeqParameterSet, SpsError> {
1134+
/// Parses `seq_parameter_set_data()` (spec 7.3.2.1.1) without consuming the RBSP trailing
1135+
/// bits. This is used by both `from_bits()` and `subset_seq_parameter_set_rbsp()` parsing.
1136+
pub(crate) fn read_seq_parameter_set_data<R: BitRead>(
1137+
r: &mut R,
1138+
) -> Result<SeqParameterSet, SpsError> {
11251139
let profile_idc = r.read::<u8>(8, "profile_idc")?.into();
11261140
let constraint_flags = r.read::<u8>(8, "constraint_flags")?.into();
11271141
let level_idc = r.read::<u8>(8, "level_idc")?;
@@ -1131,24 +1145,29 @@ impl SeqParameterSet {
11311145
level_idc,
11321146
seq_parameter_set_id: SeqParamSetId::from_u32(r.read_ue("seq_parameter_set_id")?)
11331147
.map_err(SpsError::BadSeqParamSetId)?,
1134-
chroma_info: ChromaInfo::read(&mut r, profile_idc)?,
1135-
log2_max_frame_num_minus4: Self::read_log2_max_frame_num_minus4(&mut r)?,
1136-
pic_order_cnt: PicOrderCntType::read(&mut r).map_err(SpsError::PicOrderCnt)?,
1148+
chroma_info: ChromaInfo::read(r, profile_idc)?,
1149+
log2_max_frame_num_minus4: Self::read_log2_max_frame_num_minus4(r)?,
1150+
pic_order_cnt: PicOrderCntType::read(r).map_err(SpsError::PicOrderCnt)?,
11371151
max_num_ref_frames: r.read_ue("max_num_ref_frames")?,
11381152
gaps_in_frame_num_value_allowed_flag: r
11391153
.read_bool("gaps_in_frame_num_value_allowed_flag")?,
11401154
pic_width_in_mbs_minus1: r.read_ue("pic_width_in_mbs_minus1")?,
11411155
pic_height_in_map_units_minus1: r.read_ue("pic_height_in_map_units_minus1")?,
1142-
frame_mbs_flags: FrameMbsFlags::read(&mut r)?,
1156+
frame_mbs_flags: FrameMbsFlags::read(r)?,
11431157
direct_8x8_inference_flag: r.read_bool("direct_8x8_inference_flag")?,
1144-
frame_cropping: FrameCropping::read(&mut r)?,
1158+
frame_cropping: FrameCropping::read(r)?,
11451159
// read the basic SPS data without reading VUI parameters yet, since checks of the
11461160
// bitstream restriction fields within the VUI parameters will need access to the
11471161
// initial SPS data
11481162
vui_parameters: None,
11491163
};
1150-
let vui_parameters = VuiParameters::read(&mut r, &sps)?;
1164+
let vui_parameters = VuiParameters::read(r, &sps)?;
11511165
sps.vui_parameters = vui_parameters;
1166+
Ok(sps)
1167+
}
1168+
1169+
pub fn from_bits<R: BitRead>(mut r: R) -> Result<SeqParameterSet, SpsError> {
1170+
let sps = Self::read_seq_parameter_set_data(&mut r)?;
11521171
r.finish_rbsp()?;
11531172
Ok(sps)
11541173
}

src/nal/sps_extension.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//! Parser for `seq_parameter_set_extension_rbsp()` (NAL type 13, spec 7.3.2.1.2).
2+
3+
use crate::nal::sps::{SeqParamSetId, SpsError};
4+
use crate::rbsp::BitRead;
5+
6+
/// Auxiliary format information, present when `aux_format_idc != 0`.
7+
#[derive(Clone, Debug, PartialEq, Eq)]
8+
pub struct AuxFormatInfo {
9+
pub bit_depth_aux_minus8: u8,
10+
pub alpha_incr_flag: bool,
11+
pub alpha_opaque_value: u32,
12+
pub alpha_transparent_value: u32,
13+
}
14+
15+
/// Parsed `seq_parameter_set_extension_rbsp()` (NAL unit type 13).
16+
#[derive(Clone, Debug, PartialEq, Eq)]
17+
pub struct SeqParameterSetExtension {
18+
pub seq_parameter_set_id: SeqParamSetId,
19+
pub aux_format_idc: u32,
20+
pub aux_format_info: Option<AuxFormatInfo>,
21+
pub additional_extension_flag: bool,
22+
}
23+
24+
impl SeqParameterSetExtension {
25+
pub fn from_bits<R: BitRead>(mut r: R) -> Result<SeqParameterSetExtension, SpsError> {
26+
let seq_parameter_set_id =
27+
SeqParamSetId::from_u32(r.read_ue("seq_parameter_set_id")?)
28+
.map_err(SpsError::BadSeqParamSetId)?;
29+
let aux_format_idc = r.read_ue("aux_format_idc")?;
30+
let aux_format_info = if aux_format_idc != 0 {
31+
let bit_depth_aux_minus8 = r.read_ue("bit_depth_aux_minus8")?;
32+
if bit_depth_aux_minus8 > 4 {
33+
return Err(SpsError::BitDepthOutOfRange(bit_depth_aux_minus8));
34+
}
35+
let bit_depth_aux_minus8 = bit_depth_aux_minus8 as u8;
36+
let alpha_incr_flag = r.read_bool("alpha_incr_flag")?;
37+
let v = bit_depth_aux_minus8 as u32 + 9;
38+
let alpha_opaque_value = r.read(v, "alpha_opaque_value")?;
39+
let alpha_transparent_value = r.read(v, "alpha_transparent_value")?;
40+
Some(AuxFormatInfo {
41+
bit_depth_aux_minus8,
42+
alpha_incr_flag,
43+
alpha_opaque_value,
44+
alpha_transparent_value,
45+
})
46+
} else {
47+
None
48+
};
49+
let additional_extension_flag = r.read_bool("additional_extension_flag")?;
50+
r.finish_rbsp()?;
51+
Ok(SeqParameterSetExtension {
52+
seq_parameter_set_id,
53+
aux_format_idc,
54+
aux_format_info,
55+
additional_extension_flag,
56+
})
57+
}
58+
}
59+
60+
#[cfg(test)]
61+
mod test {
62+
use super::*;
63+
use crate::rbsp::BitReader;
64+
65+
#[test]
66+
fn parse_minimal() {
67+
// seq_parameter_set_id=0 (ue: 1 bit '1')
68+
// aux_format_idc=0 (ue: 1 bit '1')
69+
// additional_extension_flag=0 (1 bit)
70+
// rbsp_stop_one_bit=1 + alignment padding
71+
// bits: 1 1 0 1 0000 = 0xD0
72+
let data = [0xD0u8];
73+
let ext = SeqParameterSetExtension::from_bits(BitReader::new(&data[..])).unwrap();
74+
assert_eq!(ext.seq_parameter_set_id, SeqParamSetId::from_u32(0).unwrap());
75+
assert_eq!(ext.aux_format_idc, 0);
76+
assert!(ext.aux_format_info.is_none());
77+
assert!(!ext.additional_extension_flag);
78+
}
79+
80+
#[test]
81+
fn parse_with_aux_format() {
82+
// seq_parameter_set_id=0 (ue: '1') 1 bit
83+
// aux_format_idc=1 (ue: '010') 3 bits
84+
// bit_depth_aux_minus8=0 (ue: '1') 1 bit
85+
// alpha_incr_flag=0 1 bit
86+
// alpha_opaque_value: u(9) = 0x1FF 9 bits
87+
// alpha_transparent_value: u(9) = 0x000 9 bits
88+
// additional_extension_flag=0 1 bit
89+
// rbsp_stop_one_bit=1 + padding 1 bit + 6 pad
90+
//
91+
// bits: 1 010 1 0 111111111 000000000 0 1 000000
92+
// byte 0: 1010_1011 = 0xAB
93+
// byte 1: 1111_1110 = 0xFE
94+
// byte 2: 0000_0000 = 0x00
95+
// byte 3: 0100_0000 = 0x40
96+
let data = [0xABu8, 0xFE, 0x00, 0x40];
97+
let ext = SeqParameterSetExtension::from_bits(BitReader::new(&data[..])).unwrap();
98+
assert_eq!(ext.seq_parameter_set_id, SeqParamSetId::from_u32(0).unwrap());
99+
assert_eq!(ext.aux_format_idc, 1);
100+
let info = ext.aux_format_info.as_ref().unwrap();
101+
assert_eq!(info.bit_depth_aux_minus8, 0);
102+
assert!(!info.alpha_incr_flag);
103+
assert_eq!(info.alpha_opaque_value, 0x1FF);
104+
assert_eq!(info.alpha_transparent_value, 0x000);
105+
assert!(!ext.additional_extension_flag);
106+
}
107+
}

0 commit comments

Comments
 (0)