Skip to content

Commit

Permalink
touch-ups to rbsp interface
Browse files Browse the repository at this point in the history
These improve suitability for parsing H.265 as well as H.264.

The changes to `ByteReader` construction are because H.265 has
two-byte NAL headers, so automatically skipping one byte is super
confusing. Arguably it was confusing even with H.264. Make the skip
explicit.

Additionally, `BitRead::read_to` is nice for loading stuff directly
into primitives such as `[u8; 11]`. That's the representation that
makes the most sense for the H.265 profile (as defined in section 7.3.3,
`profile_tier_level`, `if( profilePresentFlag )` block). When
constructing a `HEVCDecoderConfigurationRecord`, we need that full
11-byte span; when constructing the RFC 6381 codec string, we need
a 6-byte span. So parsing the whole thing into individual fields means
reconstructing these later.

And finally, I renamed methods to more closely match `bitstream-io`,
which is what `h264-reader` is using internally now and is
overwhelmingly the commonly used crate for bitstream parsing in general.
  • Loading branch information
scottlamb committed Aug 12, 2024
1 parent 7ca37a5 commit 659eade
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 78 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@

* BREAKING CHANGE: The `ParamSetId` type has been removed and replaced with separate `PicParamSetId` and
`SeqParamSetId` types, since the allowed range of values needs to be different in these two usages.
* BREAKING CHANGE: The `rbsp::ByteReader::new` constructor has been removed in favor of more explicit
`ByteReader::skipping_h264_header`, alongside the new `ByteReader::without_skip` and `ByteReader::skipping_bytes`
that are suitable for other situations or parsing H.265 streams with two-byte NAL headers.
* BREAKING CHANGE: the `rbsp::BitReaderError::ReadError` has been removed; methods consistently return
the variant `rbsp::BitReaderError::ReadErrorFor` which additionally supplies the field name.
* BREAKING CHANGE: some methods in `rbsp::BitRead` have been renamed to match the `bitstream-io` conventions.

## 0.7.0 - 2023-05-30

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "h264-reader"
version = "0.7.1-dev"
version = "0.8.0-dev"
authors = ["David Holroyd <[email protected]>"]
license = "MIT/Apache-2.0"
description = "Reader for H264 bitstream syntax"
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

use std::fmt::Debug;

pub use bitstream_io;

pub mod annexb;
pub mod avcc;
pub mod nal;
Expand Down
8 changes: 4 additions & 4 deletions src/nal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,9 @@ impl fmt::Debug for NalHeader {
///
/// // Reading RBSP as a bit sequence:
/// let mut r = nal.rbsp_bits();
/// assert_eq!(r.read_u8(4, "first nibble").unwrap(), 0x1);
/// assert_eq!(r.read_u8(4, "second nibble").unwrap(), 0x2);
/// assert_eq!(r.read_u32(23, "23 bits at a time").unwrap(), 0x1a_00_00);
/// assert_eq!(r.read::<u8>(4, "first nibble").unwrap(), 0x1);
/// assert_eq!(r.read::<u8>(4, "second nibble").unwrap(), 0x2);
/// assert_eq!(r.read::<u32>(23, "23 bits at a time").unwrap(), 0x1a_00_00);
/// assert!(r.has_more_rbsp_data("more left").unwrap());
/// ```
pub trait Nal {
Expand All @@ -217,7 +217,7 @@ pub trait Nal {
/// emulation-prevention-three-bytes).
#[inline]
fn rbsp_bytes(&self) -> rbsp::ByteReader<Self::BufRead> {
rbsp::ByteReader::new(self.reader())
rbsp::ByteReader::skipping_h264_header(self.reader())
}

/// Reads bits within the RBSP form.
Expand Down
4 changes: 2 additions & 2 deletions src/nal/pps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl SliceGroup {
let size = (1f64 + f64::from(num_slice_groups_minus1)).log2().ceil() as u32;
let mut run_length_minus1 = Vec::with_capacity(num_slice_groups_minus1 as usize + 1);
for _ in 0..pic_size_in_map_units_minus1 + 1 {
run_length_minus1.push(r.read_u32(size, "slice_group_id")?);
run_length_minus1.push(r.read(size, "slice_group_id")?);
}
Ok(run_length_minus1)
}
Expand Down Expand Up @@ -271,7 +271,7 @@ impl PicParameterSet {
"num_ref_idx_l1_default_active_minus1",
)?,
weighted_pred_flag: r.read_bool("weighted_pred_flag")?,
weighted_bipred_idc: r.read_u8(2, "weighted_bipred_idc")?,
weighted_bipred_idc: r.read(2, "weighted_bipred_idc")?,
pic_init_qp_minus26: r.read_se("pic_init_qp_minus26")?,
pic_init_qs_minus26: r.read_se("pic_init_qs_minus26")?,
chroma_qp_index_offset: r.read_se("chroma_qp_index_offset")?,
Expand Down
5 changes: 2 additions & 3 deletions src/nal/sei/buffering_period.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@ fn read_cpb_removal_delay_list<R: BitRead>(
let mut res = vec![];
for _ in 0..count {
res.push(InitialCpbRemoval {
initial_cpb_removal_delay: r.read_u32(length, "initial_cpb_removal_delay")?,
initial_cpb_removal_delay_offset: r
.read_u32(length, "initial_cpb_removal_delay_offset")?,
initial_cpb_removal_delay: r.read(length, "initial_cpb_removal_delay")?,
initial_cpb_removal_delay_offset: r.read(length, "initial_cpb_removal_delay_offset")?,
});
}
Ok(res)
Expand Down
26 changes: 13 additions & 13 deletions src/nal/sei/pic_timing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,25 +171,25 @@ impl ClockTimestamp {
r: &mut R,
sps: &sps::SeqParameterSet,
) -> Result<ClockTimestamp, PicTimingError> {
let ct_type = CtType::from_id(r.read_u8(2, "ct_type")?);
let ct_type = CtType::from_id(r.read(2, "ct_type")?);
let nuit_field_based_flag = r.read_bool("nuit_field_based_flag")?;
let counting_type = CountingType::from_id(r.read_u8(5, "counting_type")?);
let counting_type = CountingType::from_id(r.read(5, "counting_type")?);
let full_timestamp_flag = r.read_bool("full_timestamp_flag")?;
let discontinuity_flag = r.read_bool("discontinuity_flag")?;
let cnt_dropped_flag = r.read_bool("cnt_dropped_flag")?;
let n_frames = r.read_u8(8, "n_frames")?;
let n_frames = r.read(8, "n_frames")?;
let smh = if full_timestamp_flag {
SecMinHour::SMH(
r.read_u8(6, "seconds_value")?,
r.read_u8(6, "minutes_value")?,
r.read_u8(5, "hours_value")?,
r.read(6, "seconds_value")?,
r.read(6, "minutes_value")?,
r.read(5, "hours_value")?,
)
} else if r.read_bool("seconds_flag")? {
let seconds = r.read_u8(6, "seconds_value")?;
let seconds = r.read(6, "seconds_value")?;
if r.read_bool("minutes_flag")? {
let minutes = r.read_u8(6, "minutes_value")?;
let minutes = r.read(6, "minutes_value")?;
if r.read_bool("hours_flag")? {
let hours = r.read_u8(5, "hours_value")?;
let hours = r.read(5, "hours_value")?;
SecMinHour::SMH(seconds, minutes, hours)
} else {
SecMinHour::SM(seconds, minutes)
Expand All @@ -214,7 +214,7 @@ impl ClockTimestamp {
let time_offset = if time_offset_length == 0 {
None
} else {
Some(r.read_i32(u32::from(time_offset_length), "time_offset_length")?)
Some(r.read(u32::from(time_offset_length), "time_offset_length")?)
};
Ok(ClockTimestamp {
ct_type,
Expand Down Expand Up @@ -269,11 +269,11 @@ impl PicTiming {
.or_else(|| vui_params.nal_hrd_parameters.as_ref())
{
Some(Delays {
cpb_removal_delay: r.read_u32(
cpb_removal_delay: r.read(
u32::from(hrd.cpb_removal_delay_length_minus1) + 1,
"cpb_removal_delay",
)?,
dpb_output_delay: r.read_u32(
dpb_output_delay: r.read(
u32::from(hrd.dpb_output_delay_length_minus1) + 1,
"dpb_output_delay",
)?,
Expand All @@ -292,7 +292,7 @@ impl PicTiming {
) -> Result<Option<PicStruct>, PicTimingError> {
Ok(if let Some(ref vui_params) = sps.vui_parameters {
if vui_params.pic_struct_present_flag {
let pic_struct = PicStructType::from_id(r.read_u8(4, "pic_struct")?)?;
let pic_struct = PicStructType::from_id(r.read(4, "pic_struct")?)?;
let clock_timestamps = Self::read_clock_timestamps(r, &pic_struct, sps)?;

Some(PicStruct {
Expand Down
6 changes: 3 additions & 3 deletions src/nal/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,11 +453,11 @@ impl SliceHeader {
SliceHeaderError::UndefinedSeqParamSetId(pps.seq_parameter_set_id),
)?;
let colour_plane = if sps.chroma_info.separate_colour_plane_flag {
Some(ColourPlane::from_id(r.read_u8(2, "colour_plane_id")?)?)
Some(ColourPlane::from_id(r.read(2, "colour_plane_id")?)?)
} else {
None
};
let frame_num = r.read_u16(u32::from(sps.log2_max_frame_num()), "frame_num")?;
let frame_num = r.read(u32::from(sps.log2_max_frame_num()), "frame_num")?;
let field_pic = if let sps::FrameMbsFlags::Fields { .. } = sps.frame_mbs_flags {
if r.read_bool("field_pic_flag")? {
if r.read_bool("bottom_field_flag")? {
Expand All @@ -481,7 +481,7 @@ impl SliceHeader {
sps::PicOrderCntType::TypeZero {
log2_max_pic_order_cnt_lsb_minus4,
} => {
let pic_order_cnt_lsb = r.read_u32(
let pic_order_cnt_lsb = r.read(
u32::from(log2_max_pic_order_cnt_lsb_minus4) + 4,
"pic_order_cnt_lsb",
)?;
Expand Down
36 changes: 18 additions & 18 deletions src/nal/sps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -545,7 +545,7 @@ impl AspectRatioInfo {
fn read<R: BitRead>(r: &mut R) -> Result<Option<AspectRatioInfo>, BitReaderError> {
let aspect_ratio_info_present_flag = r.read_bool("aspect_ratio_info_present_flag")?;
Ok(if aspect_ratio_info_present_flag {
let aspect_ratio_idc = r.read_u8(8, "aspect_ratio_idc")?;
let aspect_ratio_idc = r.read(8, "aspect_ratio_idc")?;
Some(match aspect_ratio_idc {
0 => AspectRatioInfo::Unspecified,
1 => AspectRatioInfo::Ratio1_1,
Expand All @@ -565,8 +565,8 @@ impl AspectRatioInfo {
15 => AspectRatioInfo::Ratio3_2,
16 => AspectRatioInfo::Ratio2_1,
255 => AspectRatioInfo::Extended(
r.read_u16(16, "sar_width")?,
r.read_u16(16, "sar_height")?,
r.read(16, "sar_width")?,
r.read(16, "sar_height")?,
),
_ => AspectRatioInfo::Reserved(aspect_ratio_idc),
})
Expand Down Expand Up @@ -670,9 +670,9 @@ impl ColourDescription {
let colour_description_present_flag = r.read_bool("colour_description_present_flag")?;
Ok(if colour_description_present_flag {
Some(ColourDescription {
colour_primaries: r.read_u8(8, "colour_primaries")?,
transfer_characteristics: r.read_u8(8, "transfer_characteristics")?,
matrix_coefficients: r.read_u8(8, "matrix_coefficients")?,
colour_primaries: r.read(8, "colour_primaries")?,
transfer_characteristics: r.read(8, "transfer_characteristics")?,
matrix_coefficients: r.read(8, "matrix_coefficients")?,
})
} else {
None
Expand All @@ -691,7 +691,7 @@ impl VideoSignalType {
let video_signal_type_present_flag = r.read_bool("video_signal_type_present_flag")?;
Ok(if video_signal_type_present_flag {
Some(VideoSignalType {
video_format: VideoFormat::from(r.read_u8(3, "video_format")?),
video_format: VideoFormat::from(r.read(3, "video_format")?),
video_full_range_flag: r.read_bool("video_full_range_flag")?,
colour_description: ColourDescription::read(r)?,
})
Expand Down Expand Up @@ -732,8 +732,8 @@ impl TimingInfo {
let timing_info_present_flag = r.read_bool("timing_info_present_flag")?;
Ok(if timing_info_present_flag {
Some(TimingInfo {
num_units_in_tick: r.read_u32(32, "num_units_in_tick")?,
time_scale: r.read_u32(32, "time_scale")?,
num_units_in_tick: r.read(32, "num_units_in_tick")?,
time_scale: r.read(32, "time_scale")?,
fixed_frame_rate_flag: r.read_bool("fixed_frame_rate_flag")?,
})
} else {
Expand Down Expand Up @@ -782,14 +782,14 @@ impl HrdParameters {
}
let cpb_cnt = cpb_cnt_minus1 + 1;
Some(HrdParameters {
bit_rate_scale: r.read_u8(4, "bit_rate_scale")?,
cpb_size_scale: r.read_u8(4, "cpb_size_scale")?,
bit_rate_scale: r.read(4, "bit_rate_scale")?,
cpb_size_scale: r.read(4, "cpb_size_scale")?,
cpb_specs: Self::read_cpb_specs(r, cpb_cnt)?,
initial_cpb_removal_delay_length_minus1: r
.read_u8(5, "initial_cpb_removal_delay_length_minus1")?,
cpb_removal_delay_length_minus1: r.read_u8(5, "cpb_removal_delay_length_minus1")?,
dpb_output_delay_length_minus1: r.read_u8(5, "dpb_output_delay_length_minus1")?,
time_offset_length: r.read_u8(5, "time_offset_length")?,
.read(5, "initial_cpb_removal_delay_length_minus1")?,
cpb_removal_delay_length_minus1: r.read(5, "cpb_removal_delay_length_minus1")?,
dpb_output_delay_length_minus1: r.read(5, "dpb_output_delay_length_minus1")?,
time_offset_length: r.read(5, "time_offset_length")?,
})
} else {
None
Expand Down Expand Up @@ -894,11 +894,11 @@ pub struct SeqParameterSet {
}
impl SeqParameterSet {
pub fn from_bits<R: BitRead>(mut r: R) -> Result<SeqParameterSet, SpsError> {
let profile_idc = r.read_u8(8, "profile_idc")?.into();
let profile_idc = r.read::<u8>(8, "profile_idc")?.into();
let sps = SeqParameterSet {
profile_idc,
constraint_flags: r.read_u8(8, "constraint_flags")?.into(),
level_idc: r.read_u8(8, "level_idc")?,
constraint_flags: r.read::<u8>(8, "constraint_flags")?.into(),
level_idc: r.read(8, "level_idc")?,
seq_parameter_set_id: SeqParamSetId::from_u32(r.read_ue("seq_parameter_set_id")?)
.map_err(SpsError::BadSeqParamSetId)?,
chroma_info: ChromaInfo::read(&mut r, profile_idc)?,
Expand Down
Loading

0 comments on commit 659eade

Please sign in to comment.