mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-25 19:17:06 -06:00
- 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>
124 lines
3.7 KiB
Rust
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);
|
|
}
|
|
}
|