#![forbid(unsafe_code)]
#![allow(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
reason = "residual coefficient/index arithmetic below is bounded by CAVLC's own \
spec-defined ranges (TotalCoeff <= 16, 4x4/2x2 block indices), guarded by \
explicit range checks or checked_* arithmetic at each cast site"
)]
use super::bitreader::BitReader;
use super::cavlc_tables;
use super::error::H264Error;
const MAX_VLC_BITS: u8 = 20;
const fn parse_bits(s: &str) -> (u32, u8) {
let bytes = s.as_bytes();
let mut value: u32 = 0;
let mut i = 0;
while i < bytes.len() {
value = (value << 1) | if bytes[i] == b'1' { 1 } else { 0 };
i += 1;
}
(value, bytes.len() as u8)
}
fn decode_vlc(
reader: &mut BitReader<'_>,
table: &[(u8, u8, &str)],
filter_second: Option<u8>,
) -> Result<(u8, u8), H264Error> {
let mut acc: u32 = 0;
let mut len: u8 = 0;
loop {
if len >= MAX_VLC_BITS {
return Err(H264Error::InvalidCavlcCode);
}
acc = (acc << 1) | reader.read_bit()?;
len += 1;
for &(first, second, code_str) in table {
if let Some(want) = filter_second {
if second != want {
continue;
}
}
let (code, code_len) = parse_bits(code_str);
if code_len == len && code == acc {
return Ok((first, second));
}
}
}
}
fn decode_coeff_token(reader: &mut BitReader<'_>, nc: i32) -> Result<(u8, u8), H264Error> {
if nc >= 8 {
let code = reader.read_bits(6)?;
return Ok(if code == 3 {
(0, 0)
} else {
(((code >> 2) + 1) as u8, (code & 3) as u8)
});
}
let table: &[(u8, u8, &str)] = match nc {
-1 => cavlc_tables::COEFF_TOKEN_CHROMA_DC,
0..=1 => cavlc_tables::COEFF_TOKEN_VLC0,
2..=3 => cavlc_tables::COEFF_TOKEN_VLC1,
4..=7 => cavlc_tables::COEFF_TOKEN_VLC2,
_ => return Err(H264Error::InvalidCavlcCode),
};
decode_vlc(reader, table, None)
}
fn read_level_prefix(reader: &mut BitReader<'_>) -> Result<u32, H264Error> {
let mut count = 0u32;
while reader.read_bit()? == 0 {
count = count.checked_add(1).ok_or(H264Error::InvalidCavlcCode)?;
}
Ok(count)
}
fn decode_levels(
reader: &mut BitReader<'_>,
total_coeff: u8,
trailing_ones: u8,
) -> Result<[i32; 16], H264Error> {
let mut levels = [0i32; 16];
for level in levels.iter_mut().take(usize::from(trailing_ones)) {
*level = if reader.read_bit()? == 0 { 1 } else { -1 };
}
let mut suffix_length: u32 = u32::from(total_coeff > 10 && trailing_ones < 3);
let mut is_first_level = true;
let remaining = usize::from(total_coeff) - usize::from(trailing_ones);
for level_slot in levels
.iter_mut()
.skip(usize::from(trailing_ones))
.take(remaining)
{
let level_prefix = read_level_prefix(reader)?;
let level_suffix_size: u32 = if level_prefix == 14 && suffix_length == 0 {
4
} else if level_prefix >= 15 {
level_prefix
.checked_sub(3)
.ok_or(H264Error::FieldOverflow)?
} else {
suffix_length
};
let mut level_code = i64::from(level_prefix.min(15))
.checked_shl(suffix_length)
.ok_or(H264Error::FieldOverflow)?;
if level_suffix_size > 0 {
let level_suffix = reader.read_bits(level_suffix_size)?;
level_code = level_code
.checked_add(i64::from(level_suffix))
.ok_or(H264Error::FieldOverflow)?;
}
if level_prefix >= 15 && suffix_length == 0 {
level_code = level_code.checked_add(15).ok_or(H264Error::FieldOverflow)?;
}
if level_prefix >= 16 {
let bias = 1i64
.checked_shl(
level_prefix
.checked_sub(3)
.ok_or(H264Error::FieldOverflow)?,
)
.ok_or(H264Error::FieldOverflow)?
.checked_sub(4096)
.ok_or(H264Error::FieldOverflow)?;
level_code = level_code
.checked_add(bias)
.ok_or(H264Error::FieldOverflow)?;
}
if is_first_level && trailing_ones < 3 {
level_code = level_code.checked_add(2).ok_or(H264Error::FieldOverflow)?;
}
is_first_level = false;
let level = if level_code % 2 == 0 {
(level_code + 2) >> 1
} else {
(-level_code - 1) >> 1
};
let level = i32::try_from(level).map_err(|_err| H264Error::FieldOverflow)?;
*level_slot = level;
if suffix_length == 0 {
suffix_length = 1;
}
let threshold = 3u32
.checked_shl(suffix_length - 1)
.ok_or(H264Error::FieldOverflow)?;
if level.unsigned_abs() > threshold && suffix_length < 6 {
suffix_length += 1;
}
}
Ok(levels)
}
fn decode_run_before(reader: &mut BitReader<'_>, zeros_left: u8) -> Result<u8, H264Error> {
let column = zeros_left.min(7);
Ok(decode_vlc(reader, cavlc_tables::RUN_BEFORE, Some(column))?.0)
}
fn reconstruct_scan_positions(
reader: &mut BitReader<'_>,
levels: &[i32; 16],
total_coeff: u8,
total_zeros: u8,
max_num_coeff: u8,
) -> Result<[i32; 16], H264Error> {
let mut scan = [0i32; 16];
if total_coeff == 0 {
return Ok(scan);
}
let tc = usize::from(total_coeff);
let mut runs = [0u8; 16];
let mut zeros_left = total_zeros;
for run in runs.iter_mut().take(tc - 1) {
if zeros_left > 0 {
let decoded = decode_run_before(reader, zeros_left)?;
*run = decoded;
zeros_left = zeros_left
.checked_sub(decoded)
.ok_or(H264Error::InvalidCavlcCode)?;
}
}
runs[tc - 1] = zeros_left;
let mut pos: i32 = -1;
for i in (0..tc).rev() {
pos = pos
.checked_add(i32::from(runs[i]) + 1)
.ok_or(H264Error::InvalidCavlcCode)?;
if pos < 0 || pos >= i32::from(max_num_coeff) {
return Err(H264Error::InvalidCavlcCode);
}
scan[pos as usize] = levels[i];
}
Ok(scan)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct Residual4x4 {
pub(super) raster: [i32; 16],
pub(super) total_coeff: u8,
}
pub(super) fn decode_4x4_residual(
reader: &mut BitReader<'_>,
nc: i32,
ac_only: bool,
) -> Result<Residual4x4, H264Error> {
let max_num_coeff: u8 = if ac_only { 15 } else { 16 };
let (total_coeff, trailing_ones) = decode_coeff_token(reader, nc)?;
if total_coeff > max_num_coeff {
return Err(H264Error::InvalidCavlcCode);
}
if total_coeff == 0 {
return Ok(Residual4x4 {
raster: [0; 16],
total_coeff: 0,
});
}
let levels = decode_levels(reader, total_coeff, trailing_ones)?;
let total_zeros = if total_coeff < max_num_coeff {
decode_vlc(reader, cavlc_tables::TOTAL_ZEROS_4X4, Some(total_coeff))?.0
} else {
0
};
let scan =
reconstruct_scan_positions(reader, &levels, total_coeff, total_zeros, max_num_coeff)?;
let scan_offset = usize::from(ac_only);
let mut raster = [0i32; 16];
for (k, &level) in scan.iter().take(usize::from(max_num_coeff)).enumerate() {
if level != 0 {
raster[usize::from(cavlc_tables::ZIGZAG_4X4[k + scan_offset])] = level;
}
}
Ok(Residual4x4 {
raster,
total_coeff,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct ResidualChromaDc {
pub(super) c: [i32; 4],
pub(super) total_coeff: u8,
}
pub(super) fn decode_chroma_dc_residual(
reader: &mut BitReader<'_>,
) -> Result<ResidualChromaDc, H264Error> {
const MAX_NUM_COEFF: u8 = 4;
let (total_coeff, trailing_ones) =
decode_vlc(reader, cavlc_tables::COEFF_TOKEN_CHROMA_DC, None)?;
if total_coeff > MAX_NUM_COEFF {
return Err(H264Error::InvalidCavlcCode);
}
if total_coeff == 0 {
return Ok(ResidualChromaDc {
c: [0; 4],
total_coeff: 0,
});
}
let levels = decode_levels(reader, total_coeff, trailing_ones)?;
let total_zeros = if total_coeff < MAX_NUM_COEFF {
decode_vlc(
reader,
cavlc_tables::TOTAL_ZEROS_CHROMA_DC,
Some(total_coeff),
)?
.0
} else {
0
};
let scan =
reconstruct_scan_positions(reader, &levels, total_coeff, total_zeros, MAX_NUM_COEFF)?;
let mut c = [0i32; 4];
c.copy_from_slice(&scan[..4]);
Ok(ResidualChromaDc { c, total_coeff })
}
#[cfg(test)]
#[path = "cavlc_tests.rs"]
mod tests;