Update to bitstream-io 3

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/2211>
This commit is contained in:
Sebastian Dröge 2025-04-21 13:42:27 +03:00 committed by GStreamer Marge Bot
parent 6f7a1e21d5
commit 1ea767ac2a
24 changed files with 158 additions and 278 deletions

30
Cargo.lock generated
View file

@ -1015,6 +1015,15 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2"
[[package]]
name = "bitstream-io"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "680575de65ce8b916b82a447458b94a48776707d9c2681a9d8da351c06886a1f"
dependencies = [
"core2",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -1428,6 +1437,15 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
dependencies = [
"memchr",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
@ -2668,7 +2686,7 @@ version = "0.14.0-alpha.1"
dependencies = [
"anyhow",
"atomic_refcell",
"bitstream-io",
"bitstream-io 3.2.0",
"byteorder",
"cairo-rs",
"cdp-types",
@ -2779,7 +2797,7 @@ name = "gst-plugin-fmp4"
version = "0.14.0-alpha.1"
dependencies = [
"anyhow",
"bitstream-io",
"bitstream-io 3.2.0",
"chrono",
"dash-mpd",
"gst-plugin-version-helper",
@ -2959,7 +2977,7 @@ name = "gst-plugin-mp4"
version = "0.14.0-alpha.1"
dependencies = [
"anyhow",
"bitstream-io",
"bitstream-io 3.2.0",
"gst-plugin-version-helper",
"gstreamer",
"gstreamer-audio",
@ -2977,7 +2995,7 @@ name = "gst-plugin-mpegtslive"
version = "0.14.0-alpha.1"
dependencies = [
"anyhow",
"bitstream-io",
"bitstream-io 3.2.0",
"gst-plugin-version-helper",
"gstreamer",
"smallvec",
@ -3148,7 +3166,7 @@ version = "0.14.0-alpha.1"
dependencies = [
"anyhow",
"atomic_refcell",
"bitstream-io",
"bitstream-io 3.2.0",
"byte-slice-cast",
"chrono",
"futures",
@ -6402,7 +6420,7 @@ dependencies = [
"arg_enum_proc_macro",
"arrayvec",
"av1-grain",
"bitstream-io",
"bitstream-io 2.6.0",
"built",
"cc",
"cfg-if",

View file

@ -16,7 +16,7 @@ gst-audio = { workspace = true, features = ["v1_18"] }
gst-video = { workspace = true, features = ["v1_18"] }
gst-pbutils = { workspace = true, features = ["v1_20"] }
gst-tag = { workspace = true, features = ["v1_20"] }
bitstream-io = "2.3"
bitstream-io = "3"
[lib]
name = "gstfmp4"

View file

@ -20,7 +20,7 @@ where
let mut num_bytes = 0;
for i in 0..8 {
let byte = reader.read::<u32>(8)?;
let byte = reader.read::<8, u32>()?;
value |= (byte & 0x7f) << (i * 7);
num_bytes += 1;
if byte & 0x80 == 0 {
@ -91,7 +91,7 @@ impl SizedObu {
));
}
let obu_type = reader.read::<u8>(4)?.into();
let obu_type = reader.read::<4, u8>()?.into();
let has_extension = reader.read_bit()?;
// require a size field
@ -106,7 +106,7 @@ impl SizedObu {
let _ = reader.read_bit()?;
let (temporal_id, spatial_id) = if has_extension {
(reader.read::<u8>(3)?, reader.read::<u8>(2)?)
(reader.read::<3, u8>()?, reader.read::<2, u8>()?)
} else {
(0, 0)
};

View file

@ -16,7 +16,7 @@ gst-audio = { workspace = true, features = ["v1_18"] }
gst-video = { workspace = true, features = ["v1_20"] }
gst-pbutils = { workspace = true, features = ["v1_18"] }
gst-tag = { workspace = true, features = ["v1_18"] }
bitstream-io = "2.3"
bitstream-io = "3"
num-integer = { version = "0.1", default-features = false, features = [] }
[lib]

View file

@ -20,7 +20,7 @@ where
let mut num_bytes = 0;
for i in 0..8 {
let byte = reader.read::<u32>(8)?;
let byte = reader.read::<8, u32>()?;
value |= (byte & 0x7f) << (i * 7);
num_bytes += 1;
if byte & 0x80 == 0 {
@ -91,7 +91,7 @@ impl SizedObu {
));
}
let obu_type = reader.read::<u8>(4)?.into();
let obu_type = reader.read::<4, u8>()?.into();
let has_extension = reader.read_bit()?;
// require a size field
@ -106,7 +106,7 @@ impl SizedObu {
let _ = reader.read_bit()?;
let (temporal_id, spatial_id) = if has_extension {
(reader.read::<u8>(3)?, reader.read::<u8>(2)?)
(reader.read::<3, u8>()?, reader.read::<2, u8>()?)
} else {
(0, 0)
};

View file

@ -10,7 +10,7 @@ rust-version.workspace = true
[dependencies]
gst.workspace = true
bitstream-io = "2.3"
bitstream-io = "3"
anyhow = "1"
smallvec = "1"

View file

@ -252,7 +252,7 @@ impl FromBitStream for TableHeader {
let table_id = r.read_to::<u8>().context("table_id")?;
let section_syntax_indicator = r.read_bit().context("table_syntax_indicator")?;
r.skip(5).context("reserved")?;
let section_length = r.read::<u16>(10).context("section_length")?;
let section_length = r.read::<10, u16>().context("section_length")?;
Ok(TableHeader {
table_id,
@ -280,7 +280,7 @@ impl FromBitStream for TableSyntaxSection {
{
let table_id_extension = r.read_to::<u16>().context("table_id_extension")?;
r.skip(2).context("reserved")?;
let version_number = r.read::<u8>(5).context("version_number")?;
let version_number = r.read::<5, u8>().context("version_number")?;
let current_next_indicator = r.read_bit().context("current_next_indicator")?;
let section_number = r.read_to::<u8>().context("section_number")?;
let last_section_number = r.read_to::<u8>().context("last_section_number")?;
@ -310,7 +310,7 @@ impl FromBitStream for ProgramAccessTable {
{
let program_num = r.read_to::<u16>().context("program_num")?;
r.skip(3).context("reserved")?;
let program_map_pid = r.read::<u16>(13).context("program_map_pid")?;
let program_map_pid = r.read::<13, u16>().context("program_map_pid")?;
Ok(ProgramAccessTable {
program_num,
@ -334,11 +334,11 @@ impl FromBitStream for ProgramMappingTable {
Self: Sized,
{
r.skip(3).context("reserved")?;
let pcr_pid = r.read::<u16>(13).context("pcr_pid")?;
let pcr_pid = r.read::<13, u16>().context("pcr_pid")?;
r.skip(4).context("reserved")?;
r.skip(2).context("program_info_length_unused")?;
let program_info_length = r.read::<u16>(10).context("program_info_length")?;
let program_info_length = r.read::<10, u16>().context("program_info_length")?;
r.skip(8 * program_info_length as u32)
.context("program_descriptors")?;
@ -364,7 +364,7 @@ impl FromBitStream for ProgramMappingTable {
};
let Some(elementary_pid) =
try_read(r, |r| r.read::<u16>(13)).context("elementary_pid")?
try_read(r, |r| r.read::<13, u16>()).context("elementary_pid")?
else {
break;
};
@ -378,7 +378,7 @@ impl FromBitStream for ProgramMappingTable {
};
let Some(es_info_length) =
try_read(r, |r| r.read::<u16>(10)).context("es_info_length")?
try_read(r, |r| r.read::<10, u16>()).context("es_info_length")?
else {
break;
};
@ -632,11 +632,11 @@ impl FromBitStream for PacketHeader {
let tei = r.read_bit().context("tei")?;
let pusi = r.read_bit().context("pusi")?;
let tp = r.read_bit().context("tp")?;
let pid = r.read::<u16>(13).context("pid")?;
let pid = r.read::<13, u16>().context("pid")?;
let tsc = r.read::<u8>(2).context("tsc")?;
let afc = r.read::<u8>(2).context("afc")?;
let cc = r.read::<u8>(4).context("cc")?;
let tsc = r.read::<2, u8>().context("tsc")?;
let afc = r.read::<2, u8>().context("afc")?;
let cc = r.read::<4, u8>().context("cc")?;
Ok(PacketHeader {
tei,
@ -671,9 +671,9 @@ impl FromBitStream for AdaptionField {
// PCR present
let pcr = if pcr_present {
let pcr = r.read::<u64>(33).context("pcr_base")? * 300;
let pcr = r.read::<33, u64>().context("pcr_base")? * 300;
r.skip(6).context("pcr_reserved")?;
let pcr = pcr + r.read::<u64>(9).context("pcr_extension")? % 300;
let pcr = pcr + r.read::<9, u64>().context("pcr_extension")? % 300;
Some(pcr)
} else {
None

View file

@ -11,7 +11,7 @@ rust-version.workspace = true
[dependencies]
anyhow = "1"
atomic_refcell = "0.1"
bitstream-io = "2.4"
bitstream-io = "3"
byte-slice-cast = "1.2"
chrono = { version = "0.4", default-features = false }
gst = { workspace = true, features = ["v1_20"] }

View file

@ -279,7 +279,7 @@ impl RtpBaseDepay2Impl for RtpAmrDepay {
}
if frame_size > 0 {
match r.read::<u8>(frame_size) {
match r.read_var::<u8>(frame_size) {
Ok(b) => {
out_data.push(b << (8 - frame_size));
}

View file

@ -681,7 +681,7 @@ impl RtpAmrPay {
}
if num_bits > 0 {
if let Err(err) = w.write(num_bits as u32, data[0] >> (8 - num_bits)) {
if let Err(err) = w.write_var(num_bits as u32, data[0] >> (8 - num_bits)) {
gst::error!(CAT, imp = self, "Failed writing payload: {err}");
return Err(gst::FlowError::Error);
}

View file

@ -102,7 +102,7 @@ impl FromBitStreamWith<'_> for PayloadHeader {
bail!("CRC not allowed in bandwidth-efficient mode");
}
let cmr = r.read::<u8>(4).context("cmr")?;
let cmr = r.read::<4, u8>().context("cmr")?;
let mut toc_entries = SmallVec::<[TocEntry; 16]>::new();
loop {
@ -184,7 +184,7 @@ impl ToBitStreamWith<'_> for PayloadHeader {
bail!("No TOC entries");
}
w.write::<u8>(4, self.cmr).context("cmr")?;
w.write::<4, u8>(self.cmr).context("cmr")?;
for (i, entry) in self.toc_entries.iter().enumerate() {
let mut entry = entry.clone();
@ -256,7 +256,7 @@ impl FromBitStreamWith<'_> for TocEntry {
Self: Sized,
{
let last = !r.read_bit().context("last")?;
let frame_type = r.read::<u8>(4).context("frame_type")?;
let frame_type = r.read::<4, u8>().context("frame_type")?;
let frame_quality_indicator = r.read_bit().context("q")?;
if !cfg.wide_band && (9..=14).contains(&frame_type) {
@ -331,7 +331,7 @@ impl ToBitStreamWith<'_> for TocEntry {
}
w.write_bit(!self.last).context("last")?;
w.write::<u8>(4, self.frame_type).context("frame_type")?;
w.write::<4, u8>(self.frame_type).context("frame_type")?;
w.write_bit(self.frame_quality_indicator)
.context("frame_quality_indicator")?;

View file

@ -21,7 +21,7 @@ where
let mut num_bytes = 0;
for i in 0..8 {
let byte = reader.read::<u32>(8)?;
let byte = reader.read::<8, u32>()?;
value |= (byte & 0x7f) << (i * 7);
num_bytes += 1;
if byte & 0x80 == 0 {
@ -40,7 +40,7 @@ where
{
loop {
writer.write_bit(value > 0x7f)?;
writer.write(7, value & 0x7f)?;
writer.write::<7, u32>(value & 0x7f)?;
value >>= 7;
if value == 0 {
writer.byte_align()?;

View file

@ -80,7 +80,7 @@ impl UnsizedObu {
));
}
let obu_type = reader.read::<u8>(4)?.into();
let obu_type = reader.read::<4, u8>()?.into();
let has_extension = reader.read_bit()?;
let has_size_field = reader.read_bit()?;
@ -89,7 +89,7 @@ impl UnsizedObu {
let _ = reader.read_bit()?;
let (temporal_id, spatial_id) = if has_extension {
(reader.read::<u8>(3)?, reader.read::<u8>(2)?)
(reader.read::<3, u8>()?, reader.read::<2, u8>()?)
} else {
(0, 0)
};
@ -146,7 +146,7 @@ impl SizedObu {
));
}
let obu_type = reader.read::<u8>(4)?.into();
let obu_type = reader.read::<4, u8>()?.into();
let has_extension = reader.read_bit()?;
// require a size field
@ -161,7 +161,7 @@ impl SizedObu {
let _ = reader.read_bit()?;
let (temporal_id, spatial_id) = if has_extension {
(reader.read::<u8>(3)?, reader.read::<u8>(2)?)
(reader.read::<3, u8>()?, reader.read::<2, u8>()?)
} else {
(0, 0)
};

View file

@ -77,15 +77,15 @@ impl FromBitStream for StreamMuxConfig {
// numProgram == 0 means 1 program (4 bits)
// numLayer == 0 means 1 layer (3 bits)
if r.read::<u8>(1).context("audioMuxVersion")? != 0 {
if r.read::<1, u8>().context("audioMuxVersion")? != 0 {
Err(UnknownVersion)?;
}
let _ = r.read_bit().context("allStreamsSameTimeFraming")?;
let num_sub_frames = r.read::<u8>(6).context("numSubFrames")? + 1;
let num_sub_frames = r.read::<6, u8>().context("numSubFrames")? + 1;
let num_progs = r.read::<u8>(4).context("numProgram")? + 1;
let num_layers = r.read::<u8>(3).context("numLayer")? + 1;
let num_progs = r.read::<4, u8>().context("numProgram")? + 1;
let num_layers = r.read::<3, u8>().context("numLayer")? + 1;
if !(num_progs == 1 && num_layers == 1) {
// Same as for rtpmp4adepay
Err(UnsupportedProgsLayer {
@ -127,24 +127,24 @@ impl FromBitStream for AudioSpecificConfig {
fn from_reader<R: BitRead + ?Sized>(r: &mut R) -> anyhow::Result<Self> {
use MPEG4AudioParserError::*;
let audio_object_type = r.read(5).context("audioObjectType")?;
let audio_object_type = r.read::<5, _>().context("audioObjectType")?;
if audio_object_type == 0 {
Err(InvalidAudioObjectType0)?;
}
let sampling_freq_idx = r.read::<u8>(4).context("samplingFrequencyIndex")?;
let sampling_freq_idx = r.read::<4, u8>().context("samplingFrequencyIndex")?;
if sampling_freq_idx as usize >= ACC_SAMPLING_FREQS.len() && sampling_freq_idx != 0xf {
Err(InvalidSamplingFreqIdx(sampling_freq_idx))?;
}
// RTP rate depends on sampling freq of the audio
let sampling_freq = if sampling_freq_idx == 0xf {
r.read(24).context("samplingFrequency")?
r.read::<24, _>().context("samplingFrequency")?
} else {
ACC_SAMPLING_FREQS[sampling_freq_idx as usize]
};
let channel_conf = r.read(4).context("channelConfiguration")?;
let channel_conf = r.read::<4, _>().context("channelConfiguration")?;
if channel_conf > 7 {
Err(InvalidChannels(channel_conf))?;
}

View file

@ -146,18 +146,18 @@ impl ConfigWithCodecData {
// numProgram == 0 means 1 program (4 bits)
// numLayer == 0 means 1 layer (3 bits)
w.write(1, 0).unwrap();
w.write::<1, _>(0).unwrap();
w.write_bit(true).unwrap();
w.write(13, 0).unwrap();
w.write::<13, _>(0).unwrap();
// 1 bit missing for byte alignment
// Append AudioSpecificConfig for prog 1 layer 1 (from codec_data)
for byte in codec_data_ref.as_slice() {
w.write(8, *byte).context("appending codec_data")?
w.write::<8, _>(*byte).context("appending codec_data")?
}
// Padding
w.write(7, 0).unwrap();
w.write::<7, _>(0).unwrap();
Ok(ConfigWithCodecData {
audio_config,

View file

@ -3,7 +3,6 @@
use bitstream_io::{BitRead, BitWrite, FromBitStreamWith, ToBitStreamWith};
use crate::mp4g::{AccessUnitIndex, ModeConfig};
use crate::utils::{mask_valid_2_comp, raw_2_comp_to_i32};
#[derive(thiserror::Error, Debug, PartialEq, Eq)]
pub enum AuHeaderError {
@ -21,18 +20,6 @@ pub enum AuHeaderError {
#[error("Unexpected CTS flag set for the first AU header {}", .0)]
CtsFlagSetInFirstAuHeader(AccessUnitIndex),
#[error("Out of range CTS-delta {cts_delta} for AU {index}")]
OutOfRangeSizeCtsDelta {
cts_delta: i32,
index: AccessUnitIndex,
},
#[error("Out of range DTS-delta {dts_delta} for AU {index}")]
OutOfRangeSizeDtsDelta {
dts_delta: i32,
index: AccessUnitIndex,
},
}
#[derive(Debug)]
@ -76,7 +63,7 @@ impl<'a> FromBitStreamWith<'a> for AuHeader {
if ctx.config.size_len > 0 {
let val = r
.read::<u32>(ctx.config.size_len as u32)
.read_var::<u32>(ctx.config.size_len as u32)
.context("AU-size")?;
// Will ensure the size is non-zero after we get the index
@ -85,12 +72,12 @@ impl<'a> FromBitStreamWith<'a> for AuHeader {
this.index = match ctx.prev_index {
None => r
.read::<u32>(ctx.config.index_len as u32)
.read_var::<u32>(ctx.config.index_len as u32)
.context("AU-Index")?
.into(),
Some(prev_index) => {
let delta = r
.read::<u32>(ctx.config.index_delta_len as u32)
.read_var::<u32>(ctx.config.index_delta_len as u32)
.context("AU-Index-delta")?;
if delta > 0 {
this.is_interleaved = true;
@ -112,17 +99,15 @@ impl<'a> FromBitStreamWith<'a> for AuHeader {
}
let delta = r
.read::<u32>(ctx.config.cts_delta_len as u32)
.read_var::<i32>(ctx.config.cts_delta_len as u32)
.context("CTS-delta")?;
let delta = raw_2_comp_to_i32(delta, ctx.config.cts_delta_len);
this.cts_delta = Some(delta);
}
if ctx.config.dts_delta_len > 0 && r.read_bit().context("DTS-flag")? {
let delta = r
.read::<u32>(ctx.config.dts_delta_len as u32)
.read_var::<i32>(ctx.config.dts_delta_len as u32)
.context("DTS-delta")?;
let delta = raw_2_comp_to_i32(delta, ctx.config.dts_delta_len);
this.dts_delta = Some(delta);
}
@ -161,13 +146,13 @@ impl<'a> ToBitStreamWith<'a> for AuHeader {
Err(ZeroSizedAu(self.index))?;
}
w.write(ctx.config.size_len as u32, size)
w.write_var(ctx.config.size_len as u32, size)
.context("AU-size")?;
}
match ctx.prev_index {
None => w
.write(ctx.config.index_len as u32, *self.index)
.write_var(ctx.config.index_len as u32, *self.index)
.context("AU-Index")?,
Some(prev_index) => {
let index_delta = self
@ -179,7 +164,7 @@ impl<'a> ToBitStreamWith<'a> for AuHeader {
prev_index,
})
.context("AU-Index-delta")?;
w.write(ctx.config.index_delta_len as u32, index_delta)
w.write_var(ctx.config.index_delta_len as u32, index_delta)
.context("AU-Index-delta")?;
}
}
@ -191,16 +176,8 @@ impl<'a> ToBitStreamWith<'a> for AuHeader {
if ctx.prev_index.is_none() {
w.write_bit(false).context("CTS-flag")?;
} else if let Some(cts_delta) = self.cts_delta {
let Some(cts_delta) = mask_valid_2_comp(cts_delta, ctx.config.cts_delta_len) else {
return Err(OutOfRangeSizeCtsDelta {
cts_delta,
index: self.index,
}
.into());
};
w.write_bit(true).context("CTS-flag")?;
w.write(ctx.config.cts_delta_len as u32, cts_delta)
w.write_var(ctx.config.cts_delta_len as u32, cts_delta)
.context("CTS-delta")?;
} else {
w.write_bit(false).context("CTS-flag")?;
@ -209,16 +186,8 @@ impl<'a> ToBitStreamWith<'a> for AuHeader {
if ctx.config.dts_delta_len > 0 {
if let Some(dts_delta) = self.dts_delta {
let Some(dts_delta) = mask_valid_2_comp(dts_delta, ctx.config.dts_delta_len) else {
return Err(OutOfRangeSizeDtsDelta {
dts_delta,
index: self.index,
}
.into());
};
w.write_bit(true).context("DTS-flag")?;
w.write(ctx.config.dts_delta_len as u32, dts_delta)
w.write_var(ctx.config.dts_delta_len as u32, dts_delta)
.context("DTS-delta")?;
} else {
w.write_bit(false).context("DTS-flag")?;

View file

@ -722,7 +722,7 @@ impl RtpMpeg4GenericPay {
let mut res = w.build_with(&header, &ctx);
if res.is_ok() {
// add final padding
res = w.write(7, 0).map_err(Into::into);
res = w.write::<7, u8>(0).map_err(Into::into);
}
if let Err(err) = res {
gst::error!(
@ -913,7 +913,7 @@ impl RtpMpeg4GenericPay {
}
// add final padding
if let Err(err) = w.write(7, 0) {
if let Err(err) = w.write::<7, u8>(0) {
gst::error!(
CAT,
imp = self,

View file

@ -10,54 +10,6 @@ pub fn seqnum_distance(seqnum1: u16, seqnum2: u16) -> i16 {
seqnum1.wrapping_sub(seqnum2)
}
/// Converts a raw two's complement value of len `bit_len` into an i32.
///
/// # Panic
///
/// Panics if `bit_len` > 32.
#[inline]
pub fn raw_2_comp_to_i32(val: u32, bit_len: u8) -> i32 {
assert!(bit_len <= 32);
if val < 1u32 << (bit_len - 1) as u32 {
// val is positive
val as i32
} else {
((0x1_0000_0000 - (1u64 << bit_len)) as u32 + val) as i32
}
}
/// Masks the provided `i32` value to be used as a two's complement of len `bit_len`,
/// so the resulting value can be passed to APIs which check the bit range.
///
/// Returns `None` the `i32` value exceeds the range of a two's complement
/// of len `bit_len`.
///
/// # Panic
///
/// Panics if `bit_len` > 32.
#[inline]
pub fn mask_valid_2_comp(val: i32, bit_len: u8) -> Option<i32> {
let bit_len = bit_len as u32;
if bit_len == i32::BITS {
return Some(val);
}
assert!(bit_len < i32::BITS);
let overhead = i32::BITS - bit_len;
let leading_zeros = val.leading_zeros();
if leading_zeros > 0 && leading_zeros < overhead
|| leading_zeros == 0 && val.leading_ones() < overhead
{
return None;
}
Some(((1 << bit_len) - 1) & val)
}
/// Defines a comparable new type `$typ` on a `[std::num::Wrapping]::<u32>`.
///
/// The new type will wrap-around on additions and substractions and it comparison
@ -431,70 +383,6 @@ mod tests {
assert_eq!(seqnum_distance(0, 0x8000), -0x8000);
}
#[test]
fn raw_2_comp_12bits_to_i32() {
const BITS: u8 = 12;
assert_eq!(raw_2_comp_to_i32(0, BITS), 0);
assert_eq!(raw_2_comp_to_i32(1, BITS), 1);
assert_eq!(raw_2_comp_to_i32(2, BITS), 2);
assert_eq!(raw_2_comp_to_i32(0xfff, BITS), -1i16 as i32);
assert_eq!(raw_2_comp_to_i32(0xffe, BITS), -2i16 as i32);
assert_eq!(raw_2_comp_to_i32(0x7ff, BITS), (1 << (BITS - 1)) - 1);
assert_eq!(raw_2_comp_to_i32(0x800, BITS), -(1 << (BITS - 1)));
}
#[test]
fn raw_2_comp_16bits_to_i32() {
const BITS: u8 = i16::BITS as u8;
assert_eq!(raw_2_comp_to_i32(0, BITS), 0);
assert_eq!(raw_2_comp_to_i32(1, BITS), 1);
assert_eq!(raw_2_comp_to_i32(2, BITS), 2);
assert_eq!(raw_2_comp_to_i32(0xffff, BITS), -1i16 as i32);
assert_eq!(raw_2_comp_to_i32(0xfffe, BITS), -2i16 as i32);
assert_eq!(raw_2_comp_to_i32(0x7fff, BITS), i16::MAX as i32);
assert_eq!(raw_2_comp_to_i32(0x8000, BITS), i16::MIN as i32);
}
#[test]
fn raw_2_comp_32bits_to_i32() {
const BITS: u8 = i32::BITS as u8;
assert_eq!(raw_2_comp_to_i32(0, BITS), 0);
assert_eq!(raw_2_comp_to_i32(1, BITS), 1);
assert_eq!(raw_2_comp_to_i32(2, BITS), 2);
assert_eq!(raw_2_comp_to_i32(0xffff_ffff, BITS), -1i16 as i32);
assert_eq!(raw_2_comp_to_i32(0xffff_fffe, BITS), -2i16 as i32);
assert_eq!(raw_2_comp_to_i32(0x7fff_ffff, BITS), i32::MAX);
assert_eq!(raw_2_comp_to_i32(0x8000_0000, BITS), i32::MIN);
}
#[test]
fn mask_valid_2_comp_ok() {
const BITS: u8 = i32::BITS as u8;
assert_eq!(mask_valid_2_comp(0, BITS), Some(0));
assert_eq!(mask_valid_2_comp(-1, BITS), Some(-1));
assert_eq!(mask_valid_2_comp(i32::MIN, BITS), Some(i32::MIN));
assert_eq!(mask_valid_2_comp(i32::MAX, BITS), Some(i32::MAX));
assert_eq!(mask_valid_2_comp(0, 6), Some(0));
assert_eq!(mask_valid_2_comp(0x2f, 6), Some(0x2f)); // -1i6
assert_eq!(mask_valid_2_comp(0x20, 6), Some(0x20)); // i6::MIN
assert_eq!(mask_valid_2_comp(0x1f, 6), Some(0x1f)); // i6::MAX
assert_eq!(mask_valid_2_comp(0x1f, 5), Some(0x1f)); // i6::MAX => -1i5
}
#[test]
fn mask_valid_2_comp_ko() {
const BITS: u8 = i32::BITS as u8;
assert_eq!(mask_valid_2_comp(0, BITS), Some(0));
assert_eq!(mask_valid_2_comp(-1, BITS), Some(-1));
assert_eq!(mask_valid_2_comp(i32::MIN, BITS), Some(i32::MIN));
assert_eq!(mask_valid_2_comp(i32::MAX, BITS), Some(i32::MAX));
assert_eq!(mask_valid_2_comp(0, 5), Some(0));
assert!(mask_valid_2_comp(0x2f, 5).is_none()); // -1i6
assert!(mask_valid_2_comp(0x20, 5).is_none()); // i6::MIN
}
#[test]
fn wrapping_u32_basics() {
let zero = MyWrapper::ZERO;

View file

@ -7,8 +7,8 @@
//
// SPDX-License-Identifier: MPL-2.0
use bitstream_io::{BigEndian, BitQueue, BitRead, Endianness};
use std::io;
use bitstream_io::{BigEndian, BitRead, Endianness, UnsignedInteger as _};
use std::{io, mem};
/// Implementation of the bool decoder from RFC 6386:
/// https://datatracker.ietf.org/doc/html/rfc6386#section-7.3
@ -112,66 +112,69 @@ impl<R: io::Read> BitRead for BoolDecoder<R> {
self.next_bit()
}
fn read<U>(&mut self, mut bits: u32) -> std::io::Result<U>
fn read_unsigned_counted<const MAX: u32, U>(
&mut self,
bits: bitstream_io::BitCount<MAX>,
) -> io::Result<U>
where
U: bitstream_io::Numeric,
U: bitstream_io::UnsignedInteger,
{
if bits > U::BITS_SIZE {
let mut bits = u32::from(bits);
if bits > MAX || bits > mem::size_of::<U>() as u32 * 8 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"excessive bits for type read",
));
}
let mut queue = BitQueue::<BigEndian, U>::new();
let mut v = U::ZERO;
while bits > 0 {
queue.push(1, U::from_u8(self.next_bit()? as u8));
v = v << 1 | U::from_u8(self.next_bit()? as u8);
bits -= 1;
}
Ok(queue.value())
Ok(v)
}
fn read_signed<S>(&mut self, bits: u32) -> std::io::Result<S>
fn read_signed_counted<const MAX: u32, S>(
&mut self,
bits: bitstream_io::BitCount<MAX>,
) -> io::Result<S>
where
S: bitstream_io::SignedNumeric,
S: bitstream_io::SignedInteger,
{
BigEndian::read_signed(self, bits)
let sign = self.next_bit()?;
let v = self.read_unsigned_counted::<MAX, S::Unsigned>(
bits.checked_sub::<MAX>(1).ok_or(io::Error::new(
io::ErrorKind::InvalidInput,
"signed reads need at least 1 bit for sign",
))?,
)?;
Ok(if sign {
v.as_negative(bits.into())
} else {
v.as_non_negative()
})
}
fn read_signed_in<const BITS: u32, S>(&mut self) -> std::io::Result<S>
where
S: bitstream_io::SignedNumeric,
{
BigEndian::read_signed(self, BITS)
}
fn read_to<V>(&mut self) -> std::io::Result<V>
fn read_to<V>(&mut self) -> io::Result<V>
where
V: bitstream_io::Primitive,
{
BigEndian::read_primitive(self)
}
fn read_as_to<F, V>(&mut self) -> std::io::Result<V>
fn read_as_to<F, V>(&mut self) -> io::Result<V>
where
F: bitstream_io::Endianness,
F: Endianness,
V: bitstream_io::Primitive,
{
F::read_primitive(self)
}
fn skip(&mut self, mut bits: u32) -> std::io::Result<()> {
while bits > 0 {
self.next_bit()?;
bits -= 1;
}
Ok(())
}
fn byte_aligned(&self) -> bool {
false
unimplemented!()
}
fn byte_align(&mut self) {

View file

@ -104,7 +104,7 @@ trait BitReadExt: BitRead {
// `as_negative()` that works with absolute value + sign instead of two's complement.
fn read_with_sign(&mut self, bits: u32) -> Result<i8, io::Error> {
assert!(bits > 0 && bits <= 7);
let value = self.read::<u8>(bits)?;
let value = self.read_var::<u8>(bits)?;
let sign = self.read_bit()?;
if sign {
@ -133,8 +133,8 @@ impl FromBitStreamWith<'_> for FrameHeader {
// that is read here is 50:50 so it's equivalent to no encoding at all.
let (color_space, clamping_type) = if *keyframe {
(
Some(r.read::<u8>(1).context("color_space")?),
Some(r.read::<u8>(1).context("clamping_type")?),
Some(r.read::<1, u8>().context("color_space")?),
Some(r.read::<1, u8>().context("clamping_type")?),
)
} else {
(None, None)
@ -150,9 +150,9 @@ impl FromBitStreamWith<'_> for FrameHeader {
None
};
let filter_type = r.read::<u8>(1).context("filter_type")?;
let loop_filter_level = r.read::<u8>(6).context("loop_filter_level")?;
let sharpness_level = r.read::<u8>(3).context("sharpness_level")?;
let filter_type = r.read::<1, u8>().context("filter_type")?;
let loop_filter_level = r.read::<6, u8>().context("loop_filter_level")?;
let sharpness_level = r.read::<3, u8>().context("sharpness_level")?;
let loop_filter_adj_enable = r.read_bit().context("loop_filter_adj_enable")?;
@ -162,7 +162,7 @@ impl FromBitStreamWith<'_> for FrameHeader {
None
};
let nbr_of_dct_partitions = 1 << r.read::<u8>(2).context("nbr_of_dct_partitions")?;
let nbr_of_dct_partitions = 1 << r.read::<2, u8>().context("nbr_of_dct_partitions")?;
Ok(FrameHeader {
color_space,
@ -234,7 +234,7 @@ impl FromBitStream for SegmentFeatureData {
where
Self: Sized,
{
let segment_feature_mode = r.read::<u8>(1).context("segment_feature_mode")?;
let segment_feature_mode = r.read::<1, u8>().context("segment_feature_mode")?;
let mut quantizer_update = SmallVec::new();
let mut loop_filter_update = SmallVec::new();
@ -283,7 +283,7 @@ impl FromBitStream for MbSegmentationMap {
let segment_prob_update = r.read_bit().context("segment_prob_update")?;
if segment_prob_update {
let segment_prob = r.read::<u8>(8).context("segment_prob")?;
let segment_prob = r.read::<8, u8>().context("segment_prob")?;
segment_probs.push(Some(segment_prob));
} else {

View file

@ -31,13 +31,13 @@ impl FromBitStream for FrameHeader {
where
Self: Sized,
{
let marker = r.read::<u8>(2).context("frame_marker")?;
let marker = r.read::<2, u8>().context("frame_marker")?;
if marker != 2 {
bail!("Wrong frame marker");
}
let profile_low_bit = r.read::<u8>(1).context("profile_low_bit")?;
let profile_high_bit = r.read::<u8>(1).context("profile_high_bit")?;
let profile_low_bit = r.read::<1, u8>().context("profile_low_bit")?;
let profile_high_bit = r.read::<1, u8>().context("profile_high_bit")?;
let profile = (profile_high_bit << 1) | profile_low_bit;
if profile == 3 {
@ -131,8 +131,10 @@ impl FromBitStreamWith<'_> for KeyframeInfo {
frame_height_minus_1 as u32 + 1,
);
let render_and_frame_size_different =
r.read::<u8>(1).context("render_and_frame_size_different")? == 1;
let render_and_frame_size_different = r
.read::<1, u8>()
.context("render_and_frame_size_different")?
== 1;
let render_size = if render_and_frame_size_different {
let render_width_minus_1 = r.read_to::<u16>().context("render_width_minus_1")?;
@ -199,13 +201,13 @@ impl FromBitStreamWith<'_> for ColorConfig {
8
};
let color_space = r.read::<u8>(3).context("color_space")?;
let color_space = r.read::<3, u8>().context("color_space")?;
let (color_range, sub_sampling_x, sub_sampling_y) = if color_space != CS_RGB {
let color_range = r.read::<u8>(1).context("color_range")?;
let color_range = r.read::<1, u8>().context("color_range")?;
let (sub_sampling_x, sub_sampling_y) = if *profile == 1 || *profile == 3 {
let sub_sampling_x = r.read::<u8>(1).context("sub_sampling_x")?;
let sub_sampling_y = r.read::<u8>(1).context("sub_sampling_y")?;
let sub_sampling_x = r.read::<1, u8>().context("sub_sampling_x")?;
let sub_sampling_y = r.read::<1, u8>().context("sub_sampling_y")?;
r.skip(1).context("reserved_zero")?;
(sub_sampling_x, sub_sampling_y)

View file

@ -28,7 +28,7 @@ gst-base = { workspace = true, features = ["v1_22"]}
gst-video = { workspace = true, features = ["v1_16"]}
winnow = "0.7"
smallvec = "1"
bitstream-io = "2.3"
bitstream-io = "3"
itertools = "0.14"
[dev-dependencies]

View file

@ -29,24 +29,24 @@ impl AncDataHeader {
let mut r = BitReader::endian(Cursor::new(slice), BigEndian);
let zeroes = r.read::<u8>(6).context("zero bits")?;
let zeroes = r.read::<6, u8>().context("zero bits")?;
if zeroes != 0 {
anyhow::bail!("Zero bits not zero!");
}
let c_not_y_channel_flag = r.read_bit().context("c_not_y_channel_flag")?;
let line_number = r.read::<u16>(11).context("line number")?;
let horizontal_offset = r.read::<u16>(12).context("horizontal offset")?;
let line_number = r.read::<11, u16>().context("line number")?;
let horizontal_offset = r.read::<12, u16>().context("horizontal offset")?;
// Top two bits are parity bits and can be stripped off
let did = (r.read::<u16>(10).context("DID")? & 0xff) as u8;
let sdid = (r.read::<u16>(10).context("SDID")? & 0xff) as u8;
let data_count = (r.read::<u16>(10).context("data count")? & 0xff) as u8;
let did = (r.read::<10, u16>().context("DID")? & 0xff) as u8;
let sdid = (r.read::<10, u16>().context("SDID")? & 0xff) as u8;
let data_count = (r.read::<10, u16>().context("data count")? & 0xff) as u8;
r.skip(data_count as u32 * 10).context("data")?;
let checksum = r.read::<u16>(10).context("checksum")?;
let checksum = r.read::<10, u16>().context("checksum")?;
while !r.byte_aligned() {
let one = r.read::<u8>(1).context("alignment")?;
let one = r.read::<1, u8>().context("alignment")?;
if one != 1 {
anyhow::bail!("Alignment bits are not ones!");
}
@ -104,33 +104,33 @@ pub(crate) fn convert_to_st2038_buffer(
let mut w = BitWriter::endian(&mut output, BigEndian);
w.write::<u8>(6, 0b00_0000).context("zero bits")?;
w.write::<6, u8>(0b00_0000).context("zero bits")?;
w.write_bit(c_not_y_channel).context("c_not_y_channel")?;
w.write::<u16>(11, line_number).context("line number")?;
w.write::<u16>(12, horizontal_offset)
w.write::<11, u16>(line_number).context("line number")?;
w.write::<12, u16>(horizontal_offset)
.context("horizontal offset")?;
let mut checksum = 0u16;
w.write::<u16>(10, extend_with_even_odd_parity(did, &mut checksum))
w.write::<10, u16>(extend_with_even_odd_parity(did, &mut checksum))
.context("DID")?;
w.write::<u16>(10, extend_with_even_odd_parity(sdid, &mut checksum))
w.write::<10, u16>(extend_with_even_odd_parity(sdid, &mut checksum))
.context("SDID")?;
w.write::<u16>(
10,
extend_with_even_odd_parity(payload.len() as u8, &mut checksum),
)
w.write::<10, u16>(extend_with_even_odd_parity(
payload.len() as u8,
&mut checksum,
))
.context("data count")?;
for &b in payload {
w.write::<u16>(10, extend_with_even_odd_parity(b, &mut checksum))
w.write::<10, u16>(extend_with_even_odd_parity(b, &mut checksum))
.context("payload")?;
}
checksum &= 0x1_ff;
checksum |= ((!(checksum >> 8)) & 0x0_01) << 9;
w.write::<u16>(10, checksum).context("checksum")?;
w.write::<10, u16>(checksum).context("checksum")?;
while !w.byte_aligned() {
w.write_bit(true).context("padding")?;

View file

@ -179,7 +179,7 @@ impl St2038AncToCc {
// Convert data from 10 bits to 8 bits
for _ in 0..header.data_count {
let b = (r.read::<u16>(10).unwrap() & 0xff) as u8;
let b = (r.read::<10, u16>().unwrap() & 0xff) as u8;
sub_slice[0] = b;
sub_slice = &mut sub_slice[1..];
}