Files
deckcraft/crates/hevc/src/tables.rs
Brandon ThomasandClaude Opus 5.5 4375fe7b89 Media playback: slidecraft-media (probe, audio decode, H.264/HEVC/VP9/AV1 video, mixer and clock), cpal output, editor control bar, slide show playback, media.* commands
- crates/media: probe (duration, rate/channels, size/fps; WMA/WMV recognised), audio decode to
  f32 PCM (MP3, AAC M4A/ADTS, ALAC, WAV, AIFF, CAF, FLAC, Vorbis via symphonia; Opus with our
  decoder in Ogg/WebM/MKV), video decode (MP4/MOV/WebM/MKV) with YUV->RGBA, poster frames,
  trim/fade/volume, Player mixer + per-voice clock (device-clocked or wall-clocked), VideoFeed.
- Codec crates ported from FilmCraft: bitstream, h264, hevc, vp9, av1, isobmff, matroska, ogg, opus.
- insert.audio/insert.video probe and store duration/size and a poster frame; unplayable media
  is embedded with a warning. New media.play/pause/toggle/stop/seek/info/posterFrame.
- UI: MediaHost (cpal output injected by the desktop app), control bar under media, video frames
  on canvas and in the show; autoplay, click to play, loop, rewind, across slides, hide, full screen.
- Tests with in-code WAV/AIFF and CC0 self-generated fixtures (make.sh, ATTRIBUTION.md rows).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:35:09 -04:00

124 lines
3.7 KiB
Rust

//! Derived tables: scan orders (6.5.3 - 6.5.5), chroma QP mapping and small helper tables.
pub use crate::spec_tables::*;
/// Up-right diagonal scan (6.5.3) of a `N x N` block as (x, y) pairs.
const fn diag<const L: usize>(n: usize) -> [(u8, u8); L] {
let mut out = [(0u8, 0u8); L];
let mut i = 0;
let mut x: i32 = 0;
let mut y: i32 = 0;
while i < L {
while y >= 0 {
if (x as usize) < n && (y as usize) < n {
out[i] = (x as u8, y as u8);
i += 1;
}
y -= 1;
x += 1;
}
y = x;
x = 0;
}
out
}
/// Horizontal (6.5.4, `vertical == false`) or vertical (6.5.5) scan.
const fn hv<const L: usize>(n: usize, vertical: bool) -> [(u8, u8); L] {
let mut out = [(0u8, 0u8); L];
let mut i = 0;
while i < L {
let (a, b) = ((i % n) as u8, (i / n) as u8);
out[i] = if vertical { (b, a) } else { (a, b) };
i += 1;
}
out
}
pub static SCAN_1: [[(u8, u8); 1]; 3] = [[(0, 0)], [(0, 0)], [(0, 0)]];
pub static SCAN_2: [[(u8, u8); 4]; 3] = [diag::<4>(2), hv::<4>(2, false), hv::<4>(2, true)];
pub static SCAN_4: [[(u8, u8); 16]; 3] = [diag::<16>(4), hv::<16>(4, false), hv::<16>(4, true)];
pub static SCAN_8: [[(u8, u8); 64]; 3] = [diag::<64>(8), hv::<64>(8, false), hv::<64>(8, true)];
/// ScanOrder[log2BlockSize][scanIdx] (log2BlockSize 0..=3).
#[inline]
pub fn scan_order(log2: u32, scan_idx: usize) -> &'static [(u8, u8)] {
match log2 {
0 => &SCAN_1[scan_idx],
1 => &SCAN_2[scan_idx],
2 => &SCAN_4[scan_idx],
_ => &SCAN_8[scan_idx],
}
}
/// Up-right diagonal scan for log2 size 2 or 3.
pub fn scan_diag(log2: u32) -> &'static [(u8, u8)] {
scan_order(log2, 0)
}
/// Table 8-10: QpC as a function of qPi (ChromaArrayType 1).
#[inline]
pub fn qpc_420(qpi: i32) -> i32 {
if qpi < 30 {
qpi
} else if qpi > 43 {
qpi - 6
} else {
QPC_30_43[(qpi - 30) as usize] as i32
}
}
/// Table 9-50 ctxIdxMap for 4x4 sig_coeff_flag contexts.
pub static CTX_IDX_MAP: [u8; 16] = [0, 1, 4, 5, 2, 3, 4, 5, 6, 6, 8, 8, 7, 7, 8, 8];
/// levelScale (8-309).
pub static LEVEL_SCALE: [i32; 6] = [40, 45, 51, 57, 64, 72];
/// DST-VII 4x4 matrix (8.6.4.2, trType 1).
pub static DST_MATRIX: [[i8; 4]; 4] = [[29, 55, 74, 84], [74, 74, 0, -74], [84, -29, -74, 55], [55, -84, 74, -29]];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn diagonal_scan_4x4() {
let s = &SCAN_4[0];
assert_eq!(&s[..6], &[(0, 0), (0, 1), (1, 0), (0, 2), (1, 1), (2, 0)]);
assert_eq!(s[15], (3, 3));
assert_eq!(SCAN_2[0], [(0, 0), (0, 1), (1, 0), (1, 1)]);
assert_eq!(SCAN_4[2][1], (0, 1));
assert_eq!(SCAN_4[1][1], (1, 0));
}
#[test]
fn dct_matrix_properties() {
// rows are nearly orthogonal with norm ~ 64^2 * 32
for a in 0..32 {
for b in 0..32 {
let d: i64 = (0..32).map(|k| TRANS_MATRIX[a][k] as i64 * TRANS_MATRIX[b][k] as i64).sum();
if a == b {
assert!((d - 131072).abs() < 1000, "row {a} norm {d}");
} else {
assert!(d.abs() < 1000, "rows {a},{b}: {d}");
}
}
}
// even basis functions are symmetric, odd ones antisymmetric
for n in 0..32 {
for m in 0..16 {
let (a, b) = (TRANS_MATRIX[n][m] as i32, TRANS_MATRIX[n][31 - m] as i32);
assert_eq!(a, if n % 2 == 0 { b } else { -b }, "{n} {m}");
}
}
}
#[test]
fn qpc_table() {
assert_eq!(qpc_420(29), 29);
assert_eq!(qpc_420(30), 29);
assert_eq!(qpc_420(43), 37);
assert_eq!(qpc_420(51), 45);
}
}