From d9749fbcc5c3ab6f87db96911770485cc836d39b Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Wed, 28 Oct 2026 06:02:27 +0000 Subject: [PATCH] Refactor h264 for strict Clippy checks --- crates/h264/src/deblock.rs | 139 +++++++++++++----------- crates/h264/src/intra.rs | 210 ++++++++++++++++--------------------- crates/h264/src/slice.rs | 206 +++++++++++++++++++----------------- 3 files changed, 279 insertions(+), 276 deletions(-) diff --git a/crates/h264/src/deblock.rs b/crates/h264/src/deblock.rs index 0d815aff..3781cab9 100644 --- a/crates/h264/src/deblock.rs +++ b/crates/h264/src/deblock.rs @@ -302,71 +302,12 @@ pub fn deblock_mb(pic: &mut PicState, addr: usize, mb_w: usize) { let top = usable(if my > 0 { Some(addr - mb_w) } else { None }); let alpha_off = sq.alpha_offset; let beta_off = sq.beta_offset; - let t8 = q.transform_8x8; - // bS[dir][edge][segment]; dir 0 = vertical edges (x), 1 = horizontal edges (y) - let mut bs = [[[0u8; 4]; 4]; 2]; - if q.kind.is_intra() { - for dir in 0..2 { - if (if dir == 0 { left } else { top }).is_some() { - bs[dir][0] = [4; 4]; - } - for e in 1..4 { - if !(t8 && e % 2 == 1) { - bs[dir][e] = [3; 4]; - } - } - } - } else { - let qids = ref_ids8(q, sq); - let q_uniform = (0..2).all(|l| { - let r = q.ref_idx[l]; - r[1] == r[0] && r[2] == r[0] && r[3] == r[0] && q.mv[l].iter().all(|m| *m == q.mv[l][0]) - }); - let qm: [Motion; 16] = std::array::from_fn(|r| block_motion(q, &qids, r)); - for dir in 0..2 { - let neighbor = if dir == 0 { left } else { top }; - if let Some(n) = neighbor { - let p = &pic.mbs[n]; - if p.kind.is_intra() { - bs[dir][0] = [4; 4]; - } else { - let pids = ref_ids8(p, &pic.slices[p.slice_num as usize]); - for k in 0..4 { - let (rq, rp) = if dir == 0 { (k * 4, k * 4 + 3) } else { (k, 12 + k) }; - bs[dir][0][k] = if (p.nz_mask >> rp) & 1 != 0 || (q.nz_mask >> rq) & 1 != 0 { - 2 - } else { - motion_bs(&block_motion(p, &pids, rp), &qm[rq]) - }; - } - } - } - if q_uniform && q.nz_mask == 0 { - // one motion for the whole MB and no coefficients: all internal edges have bS 0 - continue; - } - for e in 1..4 { - if t8 && e % 2 == 1 { - continue; - } - for k in 0..4 { - let (rq, rp) = if dir == 0 { (k * 4 + e, k * 4 + e - 1) } else { (e * 4 + k, e * 4 + k - 4) }; - bs[dir][e][k] = if (q.nz_mask >> rp) & 1 != 0 || (q.nz_mask >> rq) & 1 != 0 { 2 } else { motion_bs(&qm[rp], &qm[rq]) }; - } - } - } - } + let bs = boundary_strengths(pic, q, sq, [left, top]); let qp_of = |st: &MbState| if st.kind == MbKind::IPcm { 0 } else { st.qp as i32 }; let q_qp = qp_of(q); let q_qpc = q.qpc; let p_qp = [left.map(|n| (qp_of(&pic.mbs[n]), pic.mbs[n].qpc)), top.map(|n| (qp_of(&pic.mbs[n]), pic.mbs[n].qpc))]; - let params = |qpp: i32, qpq: i32, b: [u8; 4]| { - let qpav = (qpp + qpq + 1) >> 1; - let index_a = (qpav + alpha_off).clamp(0, 51) as usize; - let index_b = (qpav + beta_off).clamp(0, 51) as usize; - let tc = |b: u8| if (1..4).contains(&b) { TC0[index_a][b as usize - 1] as i32 } else { 0 }; - EdgeParams { bs: b, tc0: [tc(b[0]), tc(b[1]), tc(b[2]), tc(b[3])], alpha: ALPHA[index_a] as i32, beta: BETA[index_b] as i32 } - }; + let offsets = (alpha_off, beta_off); let width = pic.planes.width; let cwidth = pic.planes.cwidth; // luma @@ -376,7 +317,7 @@ pub fn deblock_mb(pic: &mut PicState, addr: usize, mb_w: usize) { continue; } let qpp = if e == 0 { p_qp[dir].map(|p| p.0).unwrap_or(q_qp) } else { q_qp }; - let ep = params(qpp, q_qp, bs[dir][e]); + let ep = edge_params(offsets, qpp, q_qp, bs[dir][e]); if ep.alpha == 0 || ep.beta == 0 { // filterSamplesFlag needs |p0 - q0| < alpha and |p1 - p0| < beta (8.7.2.2): no // sample of the edge changes (low QPs) @@ -398,7 +339,7 @@ pub fn deblock_mb(pic: &mut PicState, addr: usize, mb_w: usize) { continue; } let qpp = if e == 0 { p_qp[dir].map(|p| p.1[c]).unwrap_or(q_qpc[c]) } else { q_qpc[c] } as i32; - let ep = params(qpp, q_qpc[c] as i32, bs[dir][e]); + let ep = edge_params(offsets, qpp, q_qpc[c] as i32, bs[dir][e]); if ep.alpha == 0 || ep.beta == 0 { continue; } @@ -413,6 +354,78 @@ pub fn deblock_mb(pic: &mut PicState, addr: usize, mb_w: usize) { } } +/// Filter parameters of one edge from the neighbouring QPs and the slice's alpha/beta offsets. +fn edge_params((alpha_off, beta_off): (i32, i32), qpp: i32, qpq: i32, b: [u8; 4]) -> EdgeParams { + let qpav = (qpp + qpq + 1) >> 1; + let index_a = (qpav + alpha_off).clamp(0, 51) as usize; + let index_b = (qpav + beta_off).clamp(0, 51) as usize; + let tc = |b: u8| if (1..4).contains(&b) { TC0[index_a][b as usize - 1] as i32 } else { 0 }; + EdgeParams { bs: b, tc0: [tc(b[0]), tc(b[1]), tc(b[2]), tc(b[3])], alpha: ALPHA[index_a] as i32, beta: BETA[index_b] as i32 } +} + +/// bS[dir][edge][segment]; dir 0 = vertical edges (x), 1 = horizontal edges (y). `neighbors` is +/// `[left, top]`. +fn boundary_strengths(pic: &PicState, q: &MbState, sq: &SliceInfo, neighbors: [Option; 2]) -> [[[u8; 4]; 4]; 2] { + if q.kind.is_intra() { intra_strengths(q.transform_8x8, neighbors) } else { inter_strengths(pic, q, sq, neighbors) } +} + +fn intra_strengths(t8: bool, neighbors: [Option; 2]) -> [[[u8; 4]; 4]; 2] { + let mut bs = [[[0u8; 4]; 4]; 2]; + for dir in 0..2 { + if neighbors[dir].is_some() { + bs[dir][0] = [4; 4]; + } + for e in 1..4 { + if !(t8 && e % 2 == 1) { + bs[dir][e] = [3; 4]; + } + } + } + bs +} + +fn inter_strengths(pic: &PicState, q: &MbState, sq: &SliceInfo, neighbors: [Option; 2]) -> [[[u8; 4]; 4]; 2] { + let mut bs = [[[0u8; 4]; 4]; 2]; + let t8 = q.transform_8x8; + let qids = ref_ids8(q, sq); + let q_uniform = (0..2).all(|l| { + let r = q.ref_idx[l]; + r[1] == r[0] && r[2] == r[0] && r[3] == r[0] && q.mv[l].iter().all(|m| *m == q.mv[l][0]) + }); + let qm: [Motion; 16] = std::array::from_fn(|r| block_motion(q, &qids, r)); + for dir in 0..2 { + if let Some(n) = neighbors[dir] { + bs[dir][0] = edge_strengths_to_neighbor(&pic.mbs[n], &pic.slices[pic.mbs[n].slice_num as usize], q, &qm, dir); + } + if q_uniform && q.nz_mask == 0 { + // one motion for the whole MB and no coefficients: all internal edges have bS 0 + continue; + } + for e in 1..4 { + if t8 && e % 2 == 1 { + continue; + } + for k in 0..4 { + let (rq, rp) = if dir == 0 { (k * 4 + e, k * 4 + e - 1) } else { (e * 4 + k, e * 4 + k - 4) }; + bs[dir][e][k] = if (q.nz_mask >> rp) & 1 != 0 || (q.nz_mask >> rq) & 1 != 0 { 2 } else { motion_bs(&qm[rp], &qm[rq]) }; + } + } + } + bs +} + +/// bS of the macroblock edge shared with neighbour `p` (left for `dir` 0, top for `dir` 1). +fn edge_strengths_to_neighbor(p: &MbState, p_slice: &SliceInfo, q: &MbState, qm: &[Motion; 16], dir: usize) -> [u8; 4] { + if p.kind.is_intra() { + return [4; 4]; + } + let pids = ref_ids8(p, p_slice); + std::array::from_fn(|k| { + let (rq, rp) = if dir == 0 { (k * 4, k * 4 + 3) } else { (k, 12 + k) }; + if (p.nz_mask >> rp) & 1 != 0 || (q.nz_mask >> rq) & 1 != 0 { 2 } else { motion_bs(&block_motion(p, &pids, rp), &qm[rq]) } + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/h264/src/intra.rs b/crates/h264/src/intra.rs index decf2965..c2cd8fba 100644 --- a/crates/h264/src/intra.rs +++ b/crates/h264/src/intra.rs @@ -61,126 +61,102 @@ fn load_edge(plane: &[u8], stride: usize, x: usize, y: usize, n: usize, a: Avail /// Directional / DC prediction shared by Intra_4x4 and Intra_8x8 (8.3.1.2.x, 8.3.2.2.x). fn pred_nxn(mode: u8, e: &Edge, n: i32, a: Avail, out: &mut [u8], stride: usize) { - let mut put = |x: i32, y: i32, v: i32| out[y as usize * stride + x as usize] = v as u8; + let mut fill = |value: &dyn Fn(i32, i32) -> i32| { + for y in 0..n { + for x in 0..n { + out[y as usize * stride + x as usize] = value(x, y) as u8; + } + } + }; match mode { - 0 => { - for y in 0..n { - for x in 0..n { - put(x, y, e.t(x)); - } - } - } - 1 => { - for y in 0..n { - for x in 0..n { - put(x, y, e.l(y)); - } - } - } + 0 => fill(&|x, _| e.t(x)), + 1 => fill(&|_, y| e.l(y)), 2 => { - let shift = if n == 4 { 2 } else { 3 }; - let st: i32 = (0..n).map(|x| e.t(x)).sum(); - let sl: i32 = (0..n).map(|y| e.l(y)).sum(); - let dc = match (a.top, a.left) { - (true, true) => (st + sl + n) >> (shift + 1), - (false, true) => (sl + (n >> 1)) >> shift, - (true, false) => (st + (n >> 1)) >> shift, - _ => 128, - }; - for y in 0..n { - for x in 0..n { - put(x, y, dc); - } - } - } - 3 => { - for y in 0..n { - for x in 0..n { - let v = if x == n - 1 && y == n - 1 { - (e.t(2 * n - 2) + 3 * e.t(2 * n - 1) + 2) >> 2 - } else { - (e.t(x + y) + 2 * e.t(x + y + 1) + e.t(x + y + 2) + 2) >> 2 - }; - put(x, y, v); - } - } - } - 4 => { - for y in 0..n { - for x in 0..n { - let v = if x > y { - (e.t(x - y - 2) + 2 * e.t(x - y - 1) + e.t(x - y) + 2) >> 2 - } else if x < y { - (e.l(y - x - 2) + 2 * e.l(y - x - 1) + e.l(y - x) + 2) >> 2 - } else { - (e.t(0) + 2 * e.tl + e.l(0) + 2) >> 2 - }; - put(x, y, v); - } - } - } - 5 => { - for y in 0..n { - for x in 0..n { - let z = 2 * x - y; - let v = if z >= 0 && z % 2 == 0 { - (e.t(x - (y >> 1) - 1) + e.t(x - (y >> 1)) + 1) >> 1 - } else if z > 0 { - (e.t(x - (y >> 1) - 2) + 2 * e.t(x - (y >> 1) - 1) + e.t(x - (y >> 1)) + 2) >> 2 - } else if z == -1 { - (e.l(0) + 2 * e.tl + e.t(0) + 2) >> 2 - } else { - (e.l(y - 2 * x - 1) + 2 * e.l(y - 2 * x - 2) + e.l(y - 2 * x - 3) + 2) >> 2 - }; - put(x, y, v); - } - } - } - 6 => { - for y in 0..n { - for x in 0..n { - let z = 2 * y - x; - let v = if z >= 0 && z % 2 == 0 { - (e.l(y - (x >> 1) - 1) + e.l(y - (x >> 1)) + 1) >> 1 - } else if z > 0 { - (e.l(y - (x >> 1) - 2) + 2 * e.l(y - (x >> 1) - 1) + e.l(y - (x >> 1)) + 2) >> 2 - } else if z == -1 { - (e.l(0) + 2 * e.tl + e.t(0) + 2) >> 2 - } else { - (e.t(x - 2 * y - 1) + 2 * e.t(x - 2 * y - 2) + e.t(x - 2 * y - 3) + 2) >> 2 - }; - put(x, y, v); - } - } - } - 7 => { - for y in 0..n { - for x in 0..n { - let i = x + (y >> 1); - let v = if y % 2 == 0 { (e.t(i) + e.t(i + 1) + 1) >> 1 } else { (e.t(i) + 2 * e.t(i + 1) + e.t(i + 2) + 2) >> 2 }; - put(x, y, v); - } - } - } - _ => { - let zmax = 2 * n - 3; - for y in 0..n { - for x in 0..n { - let z = x + 2 * y; - let i = y + (x >> 1); - let v = if z < zmax && z % 2 == 0 { - (e.l(i) + e.l(i + 1) + 1) >> 1 - } else if z < zmax { - (e.l(i) + 2 * e.l(i + 1) + e.l(i + 2) + 2) >> 2 - } else if z == zmax { - (e.l(n - 2) + 3 * e.l(n - 1) + 2) >> 2 - } else { - e.l(n - 1) - }; - put(x, y, v); - } - } + let dc = dc_value(e, n, a); + fill(&|_, _| dc); } + 3 => fill(&|x, y| diagonal_down_left(e, n, x, y)), + 4 => fill(&|x, y| diagonal_down_right(e, x, y)), + 5 => fill(&|x, y| vertical_right(e, x, y)), + 6 => fill(&|x, y| horizontal_down(e, x, y)), + 7 => fill(&|x, y| vertical_left(e, x, y)), + _ => fill(&|x, y| horizontal_up(e, n, x, y)), + } +} + +fn dc_value(e: &Edge, n: i32, a: Avail) -> i32 { + let shift = if n == 4 { 2 } else { 3 }; + let st: i32 = (0..n).map(|x| e.t(x)).sum(); + let sl: i32 = (0..n).map(|y| e.l(y)).sum(); + match (a.top, a.left) { + (true, true) => (st + sl + n) >> (shift + 1), + (false, true) => (sl + (n >> 1)) >> shift, + (true, false) => (st + (n >> 1)) >> shift, + _ => 128, + } +} + +fn diagonal_down_left(e: &Edge, n: i32, x: i32, y: i32) -> i32 { + if x == n - 1 && y == n - 1 { + (e.t(2 * n - 2) + 3 * e.t(2 * n - 1) + 2) >> 2 + } else { + (e.t(x + y) + 2 * e.t(x + y + 1) + e.t(x + y + 2) + 2) >> 2 + } +} + +fn diagonal_down_right(e: &Edge, x: i32, y: i32) -> i32 { + if x > y { + (e.t(x - y - 2) + 2 * e.t(x - y - 1) + e.t(x - y) + 2) >> 2 + } else if x < y { + (e.l(y - x - 2) + 2 * e.l(y - x - 1) + e.l(y - x) + 2) >> 2 + } else { + (e.t(0) + 2 * e.tl + e.l(0) + 2) >> 2 + } +} + +fn vertical_right(e: &Edge, x: i32, y: i32) -> i32 { + let z = 2 * x - y; + if z >= 0 && z % 2 == 0 { + (e.t(x - (y >> 1) - 1) + e.t(x - (y >> 1)) + 1) >> 1 + } else if z > 0 { + (e.t(x - (y >> 1) - 2) + 2 * e.t(x - (y >> 1) - 1) + e.t(x - (y >> 1)) + 2) >> 2 + } else if z == -1 { + (e.l(0) + 2 * e.tl + e.t(0) + 2) >> 2 + } else { + (e.l(y - 2 * x - 1) + 2 * e.l(y - 2 * x - 2) + e.l(y - 2 * x - 3) + 2) >> 2 + } +} + +fn horizontal_down(e: &Edge, x: i32, y: i32) -> i32 { + let z = 2 * y - x; + if z >= 0 && z % 2 == 0 { + (e.l(y - (x >> 1) - 1) + e.l(y - (x >> 1)) + 1) >> 1 + } else if z > 0 { + (e.l(y - (x >> 1) - 2) + 2 * e.l(y - (x >> 1) - 1) + e.l(y - (x >> 1)) + 2) >> 2 + } else if z == -1 { + (e.l(0) + 2 * e.tl + e.t(0) + 2) >> 2 + } else { + (e.t(x - 2 * y - 1) + 2 * e.t(x - 2 * y - 2) + e.t(x - 2 * y - 3) + 2) >> 2 + } +} + +fn vertical_left(e: &Edge, x: i32, y: i32) -> i32 { + let i = x + (y >> 1); + if y % 2 == 0 { (e.t(i) + e.t(i + 1) + 1) >> 1 } else { (e.t(i) + 2 * e.t(i + 1) + e.t(i + 2) + 2) >> 2 } +} + +fn horizontal_up(e: &Edge, n: i32, x: i32, y: i32) -> i32 { + let zmax = 2 * n - 3; + let z = x + 2 * y; + let i = y + (x >> 1); + if z < zmax && z % 2 == 0 { + (e.l(i) + e.l(i + 1) + 1) >> 1 + } else if z < zmax { + (e.l(i) + 2 * e.l(i + 1) + e.l(i + 2) + 2) >> 2 + } else if z == zmax { + (e.l(n - 2) + 3 * e.l(n - 1) + 2) >> 2 + } else { + e.l(n - 1) } } diff --git a/crates/h264/src/slice.rs b/crates/h264/src/slice.rs index e1cad290..23ae971e 100644 --- a/crates/h264/src/slice.rs +++ b/crates/h264/src/slice.rs @@ -142,6 +142,107 @@ pub struct SliceHeader { pub header_bits: usize, } +/// ref_pic_list_modification() for list 0 and list 1. +fn read_ref_pic_list_mods(r: &mut BitReader, slice_type: SliceType) -> Result<[Vec; 2]> { + let mut ref_pic_list_mod = [Vec::new(), Vec::new()]; + for (list, mods) in ref_pic_list_mod.iter_mut().enumerate() { + let present = if list == 0 { !slice_type.is_intra() } else { slice_type.is_b() }; + if present && r.read_flag()? { + loop { + let idc = r.read_ue()?; + ensure!(idc <= 5, "modification_of_pic_nums_idc out of range"); + if idc == 3 { + break; + } + let value = r.read_ue()?; + mods.push(RefPicListMod { idc, value }); + ensure!(mods.len() <= 66, "too many ref_pic_list_modification entries"); + } + } + } + Ok(ref_pic_list_mod) +} + +/// pred_weight_table(). +fn read_pred_weight_table(r: &mut BitReader, sps: &Sps, slice_type: SliceType, num_ref_idx_active: [u32; 2]) -> Result { + let luma_log2_denom = r.read_ue()?; + ensure!(luma_log2_denom <= 7, "luma_log2_weight_denom out of range"); + let has_chroma = sps.chroma_array_type() != 0; + let mut chroma_log2_denom = 0; + if has_chroma { + chroma_log2_denom = r.read_ue()?; + ensure!(chroma_log2_denom <= 7, "chroma_log2_weight_denom out of range"); + } + let mut lists = [Vec::new(), Vec::new()]; + let nlists = if slice_type.is_b() { 2 } else { 1 }; + for (l, entries) in lists.iter_mut().enumerate().take(nlists) { + for _ in 0..num_ref_idx_active[l] { + entries.push(read_weight_entry(r, has_chroma, luma_log2_denom, chroma_log2_denom)?); + } + } + let [l0, l1] = lists; + Ok(PredWeightTable { luma_log2_denom, chroma_log2_denom, l0, l1 }) +} + +fn read_weight_entry(r: &mut BitReader, has_chroma: bool, luma_log2_denom: u32, chroma_log2_denom: u32) -> Result { + let mut e = WeightEntry { luma_weight: 1 << luma_log2_denom, chroma_weight: [1 << chroma_log2_denom; 2], ..Default::default() }; + if r.read_flag()? { + e.luma_flag = true; + e.luma_weight = r.read_se()?; + e.luma_offset = r.read_se()?; + ensure!((-128..=127).contains(&e.luma_weight) && (-128..=127).contains(&e.luma_offset), "luma weight out of range"); + } + if has_chroma && r.read_flag()? { + e.chroma_flag = true; + for j in 0..2 { + e.chroma_weight[j] = r.read_se()?; + e.chroma_offset[j] = r.read_se()?; + ensure!((-128..=127).contains(&e.chroma_weight[j]) && (-128..=127).contains(&e.chroma_offset[j]), "chroma weight out of range"); + } + } + Ok(e) +} + +/// The memory_management_control_operation list of dec_ref_pic_marking(). +fn read_mmcos(r: &mut BitReader) -> Result> { + let mut mmcos = Vec::new(); + loop { + let op = r.read_ue()?; + ensure!(op <= 6, "memory_management_control_operation out of range"); + if op == 0 { + return Ok(mmcos); + } + let mut m = Mmco { op, difference_of_pic_nums_minus1: 0, long_term_pic_num: 0, long_term_frame_idx: 0, max_long_term_frame_idx_plus1: 0 }; + if op == 1 || op == 3 { + m.difference_of_pic_nums_minus1 = r.read_ue()?; + } + if op == 2 { + m.long_term_pic_num = r.read_ue()?; + } + if op == 3 || op == 6 { + m.long_term_frame_idx = r.read_ue()?; + } + if op == 4 { + m.max_long_term_frame_idx_plus1 = r.read_ue()?; + } + mmcos.push(m); + ensure!(mmcos.len() <= 66, "too many MMCOs"); + } +} + +/// Deblocking filter control: (disable_deblocking_filter_idc, alpha_c0_offset_div2, beta_offset_div2). +fn read_deblocking_params(r: &mut BitReader) -> Result<(u32, i32, i32)> { + let disable_deblocking_filter_idc = r.read_ue()?; + ensure!(disable_deblocking_filter_idc <= 2, "disable_deblocking_filter_idc out of range"); + if disable_deblocking_filter_idc == 1 { + return Ok((disable_deblocking_filter_idc, 0, 0)); + } + let alpha = r.read_se()?; + let beta = r.read_se()?; + ensure!((-6..=6).contains(&alpha) && (-6..=6).contains(&beta), "deblocking offsets out of range"); + Ok((disable_deblocking_filter_idc, alpha, beta)) +} + impl SliceHeader { pub fn qp(&self, pps: &Pps) -> i32 { pps.pic_init_qp.saturating_add(self.slice_qp_delta) @@ -214,59 +315,12 @@ impl SliceHeader { num_ref_idx_active[0] = 0; } ensure!(nal.nal_unit_type != 20 && nal.nal_unit_type != 21, "MVC slices are not supported"); - let mut ref_pic_list_mod = [Vec::new(), Vec::new()]; - for (list, mods) in ref_pic_list_mod.iter_mut().enumerate() { - let present = if list == 0 { !slice_type.is_intra() } else { slice_type.is_b() }; - if present && r.read_flag()? { - loop { - let idc = r.read_ue()?; - ensure!(idc <= 5, "modification_of_pic_nums_idc out of range"); - if idc == 3 { - break; - } - let value = r.read_ue()?; - mods.push(RefPicListMod { idc, value }); - ensure!(mods.len() <= 66, "too many ref_pic_list_modification entries"); - } - } - } - let mut pred_weight_table = None; - if (pps.weighted_pred && slice_type.is_p()) || (pps.weighted_bipred_idc == 1 && slice_type.is_b()) { - let luma_log2_denom = r.read_ue()?; - ensure!(luma_log2_denom <= 7, "luma_log2_weight_denom out of range"); - let mut chroma_log2_denom = 0; - if sps.chroma_array_type() != 0 { - chroma_log2_denom = r.read_ue()?; - ensure!(chroma_log2_denom <= 7, "chroma_log2_weight_denom out of range"); - } - let mut lists = [Vec::new(), Vec::new()]; - let nlists = if slice_type.is_b() { 2 } else { 1 }; - for (l, entries) in lists.iter_mut().enumerate().take(nlists) { - for _ in 0..num_ref_idx_active[l] { - let mut e = WeightEntry { luma_weight: 1 << luma_log2_denom, chroma_weight: [1 << chroma_log2_denom; 2], ..Default::default() }; - if r.read_flag()? { - e.luma_flag = true; - e.luma_weight = r.read_se()?; - e.luma_offset = r.read_se()?; - ensure!((-128..=127).contains(&e.luma_weight) && (-128..=127).contains(&e.luma_offset), "luma weight out of range"); - } - if sps.chroma_array_type() != 0 && r.read_flag()? { - e.chroma_flag = true; - for j in 0..2 { - e.chroma_weight[j] = r.read_se()?; - e.chroma_offset[j] = r.read_se()?; - ensure!( - (-128..=127).contains(&e.chroma_weight[j]) && (-128..=127).contains(&e.chroma_offset[j]), - "chroma weight out of range" - ); - } - } - entries.push(e); - } - } - let [l0, l1] = lists; - pred_weight_table = Some(PredWeightTable { luma_log2_denom, chroma_log2_denom, l0, l1 }); - } + let ref_pic_list_mod = read_ref_pic_list_mods(&mut r, slice_type)?; + let pred_weight_table = if (pps.weighted_pred && slice_type.is_p()) || (pps.weighted_bipred_idc == 1 && slice_type.is_b()) { + Some(read_pred_weight_table(&mut r, sps, slice_type, num_ref_idx_active)?) + } else { + None + }; let mut no_output_of_prior_pics = false; let mut long_term_reference = false; let mut adaptive_ref_pic_marking = false; @@ -278,34 +332,7 @@ impl SliceHeader { } else { adaptive_ref_pic_marking = r.read_flag()?; if adaptive_ref_pic_marking { - loop { - let op = r.read_ue()?; - ensure!(op <= 6, "memory_management_control_operation out of range"); - if op == 0 { - break; - } - let mut m = Mmco { - op, - difference_of_pic_nums_minus1: 0, - long_term_pic_num: 0, - long_term_frame_idx: 0, - max_long_term_frame_idx_plus1: 0, - }; - if op == 1 || op == 3 { - m.difference_of_pic_nums_minus1 = r.read_ue()?; - } - if op == 2 { - m.long_term_pic_num = r.read_ue()?; - } - if op == 3 || op == 6 { - m.long_term_frame_idx = r.read_ue()?; - } - if op == 4 { - m.max_long_term_frame_idx_plus1 = r.read_ue()?; - } - mmcos.push(m); - ensure!(mmcos.len() <= 66, "too many MMCOs"); - } + mmcos = read_mmcos(&mut r)?; } } } @@ -325,21 +352,8 @@ impl SliceHeader { } slice_qs_delta = r.read_se()?; } - let mut disable_deblocking_filter_idc = 0; - let mut slice_alpha_c0_offset_div2 = 0; - let mut slice_beta_offset_div2 = 0; - if pps.deblocking_filter_control_present { - disable_deblocking_filter_idc = r.read_ue()?; - ensure!(disable_deblocking_filter_idc <= 2, "disable_deblocking_filter_idc out of range"); - if disable_deblocking_filter_idc != 1 { - slice_alpha_c0_offset_div2 = r.read_se()?; - slice_beta_offset_div2 = r.read_se()?; - ensure!( - (-6..=6).contains(&slice_alpha_c0_offset_div2) && (-6..=6).contains(&slice_beta_offset_div2), - "deblocking offsets out of range" - ); - } - } + let (disable_deblocking_filter_idc, slice_alpha_c0_offset_div2, slice_beta_offset_div2) = + if pps.deblocking_filter_control_present { read_deblocking_params(&mut r)? } else { (0, 0, 0) }; let mut slice_group_change_cycle = 0; if pps.num_slice_groups > 1 && (3..=5).contains(&pps.slice_group_map_type) { // Ceil(Log2(PicSizeInMapUnits / SliceGroupChangeRate + 1)); rate is not retained, so read