Summary
While evaluating yscv-video 0.1.10 as the decode backend for a pure-Rust GUI video player,
we found that Mp4VideoReader::next_frame() returns Ok(Some(frame)) with near-uniform
gray pixel data (mean ≈ 128, std ≈ 0–8) for every real-world MP4 we tested — including the
very first IDR frame. No error is ever surfaced. A self-contained encoder→decoder roundtrip
(no external files) also fails to reconstruct the picture.
Container parsing itself appears fine: dimensions, frame counts and sample extraction are all
correct. The failure seems isolated to pixel reconstruction, and it fails silently.
Environment
- yscv-video 0.1.10 (crates.io, default features, software decode path)
- rustc 1.95.0, Windows 11 x86_64 (AVX2)
cargo build --release
Repro 1 — self-contained roundtrip (no downloads needed)
Encode 60 frames of a gradient background with a white square moving 8 px/frame,
feed the Annex-B output straight back into H264Decoder via parse_annex_b:
use yscv_video::{H264Decoder, H264Encoder, parse_annex_b, rgb8_to_yuv420};
fn main() {
let (w, h) = (640usize, 352usize); // NOTE: h=360 panics, see "Bonus" below
let mut enc = H264Encoder::new(w as u32, h as u32, 28);
let mut dec = H264Decoder::new();
let (mut total, mut found) = (0usize, 0usize);
for f in 0..60usize {
let mut rgb = vec![0u8; w * h * 3];
for y in 0..h {
for x in 0..w {
let i = (y * w + x) * 3;
rgb[i] = (x * 255 / w) as u8;
rgb[i + 1] = (y * 255 / h) as u8;
rgb[i + 2] = 64;
}
}
let sq = 20 + f * 8;
for y in 100..180 {
for x in sq..(sq + 80).min(w) {
let i = (y * w + x) * 3;
rgb[i] = 255; rgb[i + 1] = 255; rgb[i + 2] = 255;
}
}
let au = enc.encode_frame(&rgb8_to_yuv420(&rgb, w, h));
for nal in parse_annex_b(&au) {
if let Ok(Some(fr)) = dec.process_nal(&nal) {
total += 1;
let row = 140 * fr.width * 3;
let hit = (0..fr.width).any(|x| {
let i = row + x * 3;
fr.rgb8_data[i] > 200 && fr.rgb8_data[i + 1] > 200 && fr.rgb8_data[i + 2] > 200
});
if hit { found += 1; }
}
}
}
println!("decoded {total}/60, white square visible in {found}/{total} frames");
}
Expected: square visible in ~60/60 frames, at monotonically increasing x.
Actual: decoded 60/60, white square visible in 2/60 frames — and per-frame mean
luminance drifts wildly (69→98) even though the source scene has constant brightness.
Repro 2 — real-world files decode to gray
use yscv_video::Mp4VideoReader;
fn main() {
let mut r = Mp4VideoReader::open(std::path::Path::new("input.mp4")).unwrap();
let mut idx = 0usize;
while let Ok(Some(f)) = r.next_frame() {
if idx % 20 == 0 {
let n = f.rgb8_data.len() as f64;
let mean = f.rgb8_data.iter().map(|&v| v as f64).sum::<f64>() / n;
let var = f.rgb8_data.iter().map(|&v| (v as f64 - mean).powi(2)).sum::<f64>() / n;
println!("frame {idx}: {}x{} mean={mean:.2} std={:.2}", f.width, f.height, var.sqrt());
}
idx += 1;
}
println!("total decoded: {idx}");
}
Results across 4 independent public files (natural video should have std ≈ 30–70):
Notes:
- The very first IDR of a Constrained Baseline stream decodes to a literally uniform
128-gray frame (std=0.00), so the failure is already in the intra path — before any
inter/B-frame machinery is involved. CAVLC-only content fails too, so it is not
CABAC-specific.
- Dimensions, frame counts, bit_depth and keyframe flags are all correct, and consecutive
frames differ by tiny amounts (MAD < 0.25), i.e. the decoder emits initialized-but-mostly-
unreconstructed buffers rather than erroring out.
- Decode throughput is excellent (3.3 ms/frame @1080p H.264 on our machine), which makes the
silent-garbage failure mode easy to miss in benchmarks.
Bonus bugs found along the way
H264Encoder::encode_frame panics (index out of bounds, h264_encoder.rs:1424) when
height is not a multiple of 16 (e.g. 640×360).
- When reconstruction fails, no
Err/log is produced — a debug assertion or an error path
(like the "decoder probably failed silently" check in your own hevc_integration.rs)
promoted to the library itself would make this much easier to catch.
Question
Is real-world H.264/HEVC content (x264/x265-encoded MP4s) expected to work in 0.1.x, or is
that part of the v0.2.0 milestone tracked in #4 / #5? We'd genuinely like to adopt yscv as a
pure-Rust decode backend and are happy to provide more repro files, logs, or bisect help.
Summary
While evaluating
yscv-video0.1.10 as the decode backend for a pure-Rust GUI video player,we found that
Mp4VideoReader::next_frame()returnsOk(Some(frame))with near-uniformgray pixel data (mean ≈ 128, std ≈ 0–8) for every real-world MP4 we tested — including the
very first IDR frame. No error is ever surfaced. A self-contained encoder→decoder roundtrip
(no external files) also fails to reconstruct the picture.
Container parsing itself appears fine: dimensions, frame counts and sample extraction are all
correct. The failure seems isolated to pixel reconstruction, and it fails silently.
Environment
cargo build --releaseRepro 1 — self-contained roundtrip (no downloads needed)
Encode 60 frames of a gradient background with a white square moving 8 px/frame,
feed the Annex-B output straight back into
H264Decoderviaparse_annex_b:Expected: square visible in ~60/60 frames, at monotonically increasing x.
Actual:
decoded 60/60, white square visible in 2/60 frames— and per-frame meanluminance drifts wildly (69→98) even though the source scene has constant brightness.
Repro 2 — real-world files decode to gray
Results across 4 independent public files (natural video should have std ≈ 30–70):
Notes:
128-gray frame (
std=0.00), so the failure is already in the intra path — before anyinter/B-frame machinery is involved. CAVLC-only content fails too, so it is not
CABAC-specific.
frames differ by tiny amounts (MAD < 0.25), i.e. the decoder emits initialized-but-mostly-
unreconstructed buffers rather than erroring out.
silent-garbage failure mode easy to miss in benchmarks.
Bonus bugs found along the way
H264Encoder::encode_framepanics (index out of bounds,h264_encoder.rs:1424) whenheight is not a multiple of 16 (e.g. 640×360).
Err/log is produced — a debug assertion or an error path(like the
"decoder probably failed silently"check in your ownhevc_integration.rs)promoted to the library itself would make this much easier to catch.
Question
Is real-world H.264/HEVC content (x264/x265-encoded MP4s) expected to work in 0.1.x, or is
that part of the v0.2.0 milestone tracked in #4 / #5? We'd genuinely like to adopt yscv as a
pure-Rust decode backend and are happy to provide more repro files, logs, or bisect help.