Merge pull request #16 from storytold/pr/never-crash-codecs-small
Codecs: never crash (ac3, audio-dsp, opus, mpeg2v, prores, dnx)
This commit is contained in:
commit
28563e8030
|
|
@ -13,6 +13,8 @@
|
|||
//!
|
||||
//! E-AC-3 (bsid 11-16) is recognised and refused.
|
||||
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))]
|
||||
|
||||
mod tables;
|
||||
|
||||
use std::sync::OnceLock;
|
||||
|
|
@ -1029,7 +1031,7 @@ impl Decoder {
|
|||
/// Coded channel order (Table 5.8) → WAV / SMPTE order: fronts L R C, LFE, surrounds.
|
||||
fn wav_order(mut pcm: Vec<Vec<f32>>, acmod: usize, lfeon: bool) -> Vec<Vec<f32>> {
|
||||
let nf = NFCHANS[acmod];
|
||||
let lfe = lfeon.then(|| pcm.pop().expect("lfe"));
|
||||
let lfe = if lfeon { pcm.pop() } else { None };
|
||||
let mut fbw = pcm;
|
||||
// acmod 3, 5, 7 code L C R …: move C after R
|
||||
if acmod & 1 == 1 && acmod != 1 {
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ pub fn matrix(from: Layout, to: Layout, mixdown: Mixdown) -> Vec<Vec<f32>> {
|
|||
let (ni, no) = (from.channels(), to.channels());
|
||||
let mut m = vec![vec![0.0f32; ni]; no];
|
||||
match (from, to) {
|
||||
(a, b) if a == b => {
|
||||
(Layout::Mono, Layout::Mono) | (Layout::Stereo, Layout::Stereo) | (Layout::Surround51, Layout::Surround51) => {
|
||||
for (i, row) in m.iter_mut().enumerate() {
|
||||
row[i] = 1.0;
|
||||
}
|
||||
|
|
@ -159,7 +159,6 @@ pub fn matrix(from: Layout, to: Layout, mixdown: Mixdown) -> Vec<Vec<f32>> {
|
|||
m[1][LFE] = K;
|
||||
}
|
||||
}
|
||||
_ => unreachable!("all layout pairs are covered"),
|
||||
}
|
||||
m
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
//! denormals never appear (no FTZ/DAZ CPU flags needed, which keeps the crate `unsafe`-free and
|
||||
//! portable to wasm).
|
||||
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))]
|
||||
|
||||
pub mod biquad;
|
||||
pub mod channels;
|
||||
pub mod design;
|
||||
|
|
@ -169,12 +171,11 @@ impl ParamValues {
|
|||
pub(crate) fn get(&self, id: &str) -> Option<f32> {
|
||||
self.index_of(id).map(|i| self.values[i])
|
||||
}
|
||||
/// Value of a parameter that is known to exist (panics otherwise — programming error).
|
||||
/// Value of a parameter that is known to exist (0 otherwise — a programming error caught by
|
||||
/// debug builds).
|
||||
pub(crate) fn v(&self, id: &str) -> f32 {
|
||||
match self.get(id) {
|
||||
Some(v) => v,
|
||||
None => panic!("unknown parameter {id}"),
|
||||
}
|
||||
debug_assert!(self.get(id).is_some(), "unknown parameter {id}");
|
||||
self.get(id).unwrap_or(0.0)
|
||||
}
|
||||
pub(crate) fn on(&self, id: &str) -> bool {
|
||||
self.v(id) >= 0.5
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ let f = decode_frame_with(&sample, &DecodeOptions { threads: false, ..Default::d
|
|||
let hdr: FrameHeader = probe(&sample)?; // cid, raster, depth, chroma…
|
||||
let name = cid_name(hdr.cid); // "DNxHR HQX", "DNxHD 1080i 8-bit (CID 1242)"
|
||||
|
||||
let mut enc = Encoder::new(Profile::Hq, 1920, 1080); // or Encoder::with_config(EncoderConfig { .. })
|
||||
let mut enc = Encoder::new(Profile::Hq, 1920, 1080)?; // or Encoder::with_config(EncoderConfig { .. })
|
||||
let sample: Vec<u8> = enc.encode(&frame)?; // exactly enc.frame_size() bytes (CBR)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ pub(crate) fn idct8x8(block: &mut [f32; 64]) {
|
|||
let mut nz = [false; 8];
|
||||
let mut dc_only = true;
|
||||
// Horizontal pass: t[r][x] = Σ_u F[r][u] · B[u][x]
|
||||
for r in 0..8 {
|
||||
let c: &[f32; 8] = block[r * 8..r * 8 + 8].try_into().unwrap();
|
||||
for (r, c) in block.as_chunks::<8>().0.iter().enumerate() {
|
||||
if c[1..].iter().all(|&v| v == 0.0) {
|
||||
if c[0] != 0.0 {
|
||||
t[r] = [c[0] * BASIS[0][0]; 8];
|
||||
|
|
|
|||
|
|
@ -61,8 +61,9 @@ struct Prepared {
|
|||
}
|
||||
|
||||
impl Encoder {
|
||||
pub fn new(profile: Profile, width: u32, height: u32) -> Encoder {
|
||||
Encoder::with_config(EncoderConfig::new(profile, width, height)).expect("valid default configuration")
|
||||
/// An encoder with the default configuration; fails on rasters the profile can't code.
|
||||
pub fn new(profile: Profile, width: u32, height: u32) -> Result<Encoder> {
|
||||
Encoder::with_config(EncoderConfig::new(profile, width, height))
|
||||
}
|
||||
|
||||
pub fn with_config(cfg: EncoderConfig) -> Result<Encoder> {
|
||||
|
|
@ -79,7 +80,7 @@ impl Encoder {
|
|||
if !ok_depth {
|
||||
return Err(Error::Input(format!("{} does not support {}-bit", cfg.profile.name(), cfg.bit_depth)));
|
||||
}
|
||||
let info = cid_info(cfg.profile.cid()).expect("RI CID");
|
||||
let info = cid_info(cfg.profile.cid()).ok_or_else(|| Error::Input(format!("no compression table for {}", cfg.profile.name())))?;
|
||||
let frame_size = cfg.profile.frame_size(cfg.width, cfg.height);
|
||||
let header_size = header::header_size_for(cfg.height);
|
||||
Ok(Encoder { cfg, info, frame_size, header_size, last_q: 8 })
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ pub fn parse(d: &[u8]) -> Result<FrameHeader> {
|
|||
2 => ColorVolume::Bt2020Cl,
|
||||
_ => ColorVolume::OutOfBand,
|
||||
};
|
||||
let timecode = (d[0x30] & 0x80 != 0).then(|| d[0x31..0x39].try_into().unwrap());
|
||||
let timecode = if d[0x30] & 0x80 != 0 { d.get(0x31..).and_then(|t| t.first_chunk::<8>()).copied() } else { None };
|
||||
let mb_rows = be16(d, 0x16C);
|
||||
if width == 0 || lines == 0 || width > 16384 || lines > 16384 {
|
||||
return Err(Error::Invalid("raster size"));
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
//!
|
||||
//! See the crate README for accuracy, performance and limitations.
|
||||
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))]
|
||||
|
||||
mod bits;
|
||||
mod dct;
|
||||
mod decode;
|
||||
|
|
|
|||
|
|
@ -68,17 +68,20 @@ fn roundtrip_all_profiles() {
|
|||
|
||||
#[test]
|
||||
fn rejects_bad_input() {
|
||||
let mut enc = Encoder::new(Profile::Hq, 64, 64);
|
||||
let mut enc = Encoder::new(Profile::Hq, 64, 64).unwrap();
|
||||
assert!(enc.encode(&Frame::new(32, 64, ChromaFormat::Yuv422, 8, false)).is_err());
|
||||
assert!(enc.encode(&Frame::new(64, 64, ChromaFormat::Yuv444, 8, false)).is_err());
|
||||
assert!(Encoder::with_config(EncoderConfig { bit_depth: 10, ..EncoderConfig::new(Profile::Hq, 64, 64) }).is_err());
|
||||
assert!(decode_frame(&[0u8; 100]).is_err());
|
||||
assert!(decode_frame(&[0u8; 1000]).is_err());
|
||||
// An unsupported raster is an error, not a panic.
|
||||
assert!(Encoder::new(Profile::Hq, 0, 0).is_err());
|
||||
assert!(Encoder::new(Profile::Hq, 20_000, 64).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn corrupt_streams_never_panic() {
|
||||
let mut enc = Encoder::new(Profile::Hq, 256, 128);
|
||||
let mut enc = Encoder::new(Profile::Hq, 256, 128).unwrap();
|
||||
let good = enc.encode(&picture(256, 128, ChromaFormat::Yuv422, 8, 1)).unwrap();
|
||||
let mut seed = 0x1234_5678u32;
|
||||
let mut rnd = || {
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ impl<'a> Bits<'a> {
|
|||
#[inline(always)]
|
||||
pub fn peek32(&self) -> u32 {
|
||||
let byte = self.pos >> 3;
|
||||
let w = if byte + 8 <= self.data.len() {
|
||||
u64::from_be_bytes(self.data[byte..byte + 8].try_into().expect("8 bytes"))
|
||||
let w = if let Some(&b) = self.data.get(byte..).and_then(|d| d.first_chunk::<8>()) {
|
||||
u64::from_be_bytes(b)
|
||||
} else {
|
||||
let mut b = [0u8; 8];
|
||||
if byte < self.data.len() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@
|
|||
//! # Ok::<(), filmcraft_mpeg2v::Error>(())
|
||||
//! ```
|
||||
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))]
|
||||
|
||||
mod bits;
|
||||
pub mod headers;
|
||||
mod idct;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ fn parse_code(s: &str) -> (u32, u32) {
|
|||
n += 1;
|
||||
}
|
||||
' ' => {}
|
||||
_ => panic!("bad code {s}"),
|
||||
// Codes are literals in this crate; the table tests cover them.
|
||||
_ => debug_assert!(false, "bad code {s}"),
|
||||
}
|
||||
}
|
||||
(v, n)
|
||||
|
|
|
|||
|
|
@ -783,8 +783,8 @@ pub fn quant_all_bands(
|
|||
}
|
||||
let out_off = xo - norm_offset;
|
||||
let x = &mut x_buf[xo..xo + n];
|
||||
if dual_stereo {
|
||||
let y = &mut y_buf.as_deref_mut().expect("stereo")[xo..xo + n];
|
||||
if dual_stereo && let Some(y_buf) = y_buf.as_deref_mut() {
|
||||
let y = &mut y_buf[xo..xo + n];
|
||||
let lb = effective_lowband.map(|e| {
|
||||
scratch[..n].copy_from_slice(&norm[e..e + n]);
|
||||
&mut scratch[..n]
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
//!
|
||||
//! Layer L0: no dependencies beyond `std`; no `unsafe`; builds for `wasm32-unknown-unknown`.
|
||||
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))]
|
||||
// The fixed-point parts mirror the normative integer arithmetic; keep C-like shift expressions.
|
||||
#![allow(clippy::precedence, clippy::int_plus_one)]
|
||||
|
||||
|
|
|
|||
|
|
@ -211,8 +211,7 @@ impl Channel {
|
|||
(LostFlag::Lbrr, Some(_)) => self.lbrr_flags[self.n_frames_decoded],
|
||||
_ => false,
|
||||
};
|
||||
if decode {
|
||||
let dec = dec.expect("range decoder");
|
||||
if decode && let Some(dec) = dec {
|
||||
self.decode_indices(dec, self.n_frames_decoded, lost == LostFlag::Lbrr, cond);
|
||||
let mut pulses = [0i32; MAX_FRAME + 16];
|
||||
decode_pulses(dec, &mut pulses, self.indices.signal_type, self.indices.quant_offset_type, l);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,7 @@ pub(crate) fn idct8x8(block: &mut [f32; 64]) {
|
|||
let mut nz = [false; 8];
|
||||
let mut dc_only = true;
|
||||
// Horizontal pass: t[r][x] = Σ_u F[r][u] · B[u][x]
|
||||
for r in 0..8 {
|
||||
let c: &[f32; 8] = block[r * 8..r * 8 + 8].try_into().unwrap();
|
||||
for (r, c) in block.as_chunks::<8>().0.iter().enumerate() {
|
||||
if c[1..].iter().all(|&v| v == 0.0) {
|
||||
if c[0] != 0.0 {
|
||||
t[r] = [c[0] * BASIS[0][0]; 8];
|
||||
|
|
|
|||
|
|
@ -476,8 +476,9 @@ fn put_block(f: &[f32; 64], plane: &mut [u16], stride: usize, x0: usize, y0: usi
|
|||
if xn == 8 && (y0 + 7) * step + parity < rows {
|
||||
for yy in 0..8 {
|
||||
let o = ((y0 + yy) * step + parity) * stride + x0;
|
||||
let row: &mut [u16; 8] = (&mut plane[o..o + 8]).try_into().unwrap();
|
||||
let src: &[f32; 8] = f[yy * 8..yy * 8 + 8].try_into().unwrap();
|
||||
let (Some(row), Some(src)) = (plane.get_mut(o..).and_then(|p| p.first_chunk_mut::<8>()), f.as_chunks::<8>().0.get(yy)) else {
|
||||
return;
|
||||
};
|
||||
for x in 0..8 {
|
||||
row[x] = conv.conv(src[x]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -429,7 +429,7 @@ fn write_slice(sd: &SliceData, q: u8) -> Vec<u8> {
|
|||
|
||||
/// Encode the slice's alpha samples (16-bit coding) in raster order over its 16 lines.
|
||||
fn encode_alpha_slice(src: &Source, x0: usize, y0: usize, mbs: usize) -> Vec<u8> {
|
||||
let a = src.frame.alpha.as_ref().expect("alpha checked");
|
||||
let Some(a) = src.frame.alpha.as_ref() else { return Vec::new() };
|
||||
let d = src.frame.bit_depth as u32;
|
||||
let to16 = |v: u16| -> u32 {
|
||||
let v = v as u32 & ((1 << d) - 1);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
//!
|
||||
//! See the crate README for bitstream details, accuracy and limitations.
|
||||
|
||||
#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable))]
|
||||
|
||||
mod bits;
|
||||
mod dct;
|
||||
mod decode;
|
||||
|
|
|
|||
Loading…
Reference in New Issue