mirror of
https://github.com/storytold/craft-libs.git
synced 2026-11-12 08:52:32 +00:00
Three-way merge of the changes EffectCraft made to its av1enc and hevcenc crates after craft-video took them: - hevc: EncoderConfig carries colour_primaries, transfer_characteristics and matrix_coeffs (H.273 code points, BT.709 by default) and the VUI writes them. - av1: EncoderConfig carries color_primaries, transfer_characteristics and matrix_coefficients and color_config writes them. - av1: motion search keeps a visited set and stops a SAD once it cannot win, SATD has fixed-size kernels, and a decision snapshot saves only the block's BlockDecoded flags. Output is unchanged. ffprobe tests check the colour description of both streams.
44 lines
2.2 KiB
Rust
44 lines
2.2 KiB
Rust
//! ffprobe as the external oracle for what the HEVC parameter sets say.
|
|
|
|
use std::process::Command;
|
|
|
|
use craft_video::hevc::{Encoder, EncoderConfig, Frame};
|
|
|
|
/// The VUI carries the configured colour description, and BT.709 when none is set.
|
|
#[test]
|
|
fn colour_description_reaches_the_vui() {
|
|
let Some(ffprobe) = craft_media_testkit::ffprobe() else { return };
|
|
let (w, h) = (64usize, 64usize);
|
|
let (y, c) = (vec![100u16; w * h], vec![128u16; w * h / 4]);
|
|
for (codes, names) in [(None, ["bt709", "bt709", "bt709"]), (Some([9u8, 16, 9]), ["bt2020", "smpte2084", "bt2020nc"])] {
|
|
let mut cfg = EncoderConfig::new(w as u32, h as u32, 30, 1);
|
|
if let Some([p, t, m]) = codes {
|
|
(cfg.colour_primaries, cfg.transfer_characteristics, cfg.matrix_coeffs) = (p, t, m);
|
|
}
|
|
let mut enc = Encoder::new(cfg).unwrap();
|
|
// Annex B: the parameter sets, then the picture's length-prefixed NAL units.
|
|
let mut stream = Vec::new();
|
|
for set in enc.parameter_sets() {
|
|
stream.extend_from_slice(&[0, 0, 0, 1]);
|
|
stream.extend_from_slice(&set);
|
|
}
|
|
let packet = enc.encode(&Frame { y: &y, u: &c, v: &c, y_stride: w, uv_stride: w / 2 }).unwrap();
|
|
let mut rest = packet.data.as_slice();
|
|
while let Some((len, tail)) = rest.split_first_chunk::<4>() {
|
|
let (nal, tail) = tail.split_at(u32::from_be_bytes(*len) as usize);
|
|
stream.extend_from_slice(&[0, 0, 0, 1]);
|
|
stream.extend_from_slice(nal);
|
|
rest = tail;
|
|
}
|
|
let path = craft_media_testkit::fixture_subdir("hevc").join(format!("colour-{}-{}.hevc", std::process::id(), codes.is_some()));
|
|
std::fs::write(&path, stream).unwrap();
|
|
let out = Command::new(&ffprobe).args(["-v", "error", "-show_streams", "-of", "flat"]).arg(&path).output().unwrap();
|
|
let _ = std::fs::remove_file(&path);
|
|
let s = String::from_utf8_lossy(&out.stdout);
|
|
let [p, t, m] = names;
|
|
for want in [format!("color_primaries=\"{p}\""), format!("color_transfer=\"{t}\""), format!("color_space=\"{m}\"")] {
|
|
assert!(s.contains(&want), "ffprobe output lacks {want}:\n{s}");
|
|
}
|
|
}
|
|
}
|