-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathbench_video_decode.rs
More file actions
122 lines (113 loc) · 3.88 KB
/
Copy pathbench_video_decode.rs
File metadata and controls
122 lines (113 loc) · 3.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//! Benchmark: decode H.264/HEVC MP4 video with yscv Mp4VideoReader.
//!
//! Usage: cargo run --release --example bench_video_decode -- <video.mp4> [--luma-only] [--hw]
//!
//! `--luma-only` skips YUV-to-RGB for fair comparison with ffmpeg `-f null`.
//! `--hw` uses hardware decode (VideoToolbox/VAAPI/NVDEC/MediaFoundation).
use std::path::Path;
use std::time::Instant;
fn main() {
let args: Vec<String> = std::env::args().collect();
let path_str = args
.get(1)
.map(|s| s.as_str())
.unwrap_or("examples/src/CENSUSWITHOUTLOGO.mp4");
let luma_only = args.iter().any(|a| a == "--luma-only");
let hw_mode = args.iter().any(|a| a == "--hw");
let path = Path::new(path_str);
if !path.exists() {
eprintln!("File not found: {}", path.display());
std::process::exit(1);
}
println!("=== yscv Mp4VideoReader benchmark ===");
println!("File: {}", path.display());
if hw_mode {
println!("Mode: HARDWARE DECODE");
} else if luma_only {
println!("Mode: LUMA-ONLY (skip YUV→RGB, fair vs ffmpeg -f null)");
}
let t0 = Instant::now();
let mut reader = if hw_mode {
match yscv_video::Mp4VideoReader::open_hw(path) {
Ok(r) => r,
Err(e) => {
eprintln!("Failed to open (HW): {e}");
std::process::exit(1);
}
}
} else {
match yscv_video::Mp4VideoReader::open(path) {
Ok(r) => r,
Err(e) => {
eprintln!("Failed to open: {e}");
std::process::exit(1);
}
}
};
let open_time = t0.elapsed();
println!("Open + parse: {:.0}ms", open_time.as_secs_f64() * 1000.0);
println!("NAL count: {}", reader.nal_count());
println!("Codec: {:?}", reader.codec());
if let Some(audio) = reader.audio_info() {
println!(
"Audio: {:?} {}Hz {}ch",
audio.codec, audio.sample_rate, audio.channels
);
}
if hw_mode {
println!("HW Backend: {}", reader.hw_backend());
}
let t1 = Instant::now();
let mut decoded = 0u32;
let mut errors = 0u32;
let mut first_frame_time = None;
loop {
let result = if luma_only {
reader.next_frame_luma_only()
} else {
reader.next_frame()
};
match result {
Ok(Some(frame)) => {
if decoded == 0 {
first_frame_time = Some(t1.elapsed());
let min = frame.rgb8_data.iter().copied().min().unwrap_or(0);
let max = frame.rgb8_data.iter().copied().max().unwrap_or(0);
println!(
"Frame 0: {}x{} keyframe={} pixel_range=[{}, {}]",
frame.width, frame.height, frame.keyframe, min, max
);
if min == max {
println!("WARNING: frame is uniform color — possible decode issue");
}
}
decoded += 1;
}
Ok(None) => break,
Err(e) => {
if errors == 0 {
eprintln!("First error at frame {decoded}: {e}");
}
errors += 1;
if errors > 200 {
break;
}
}
}
}
let total_time = t1.elapsed();
println!("\n--- Results ---");
println!("Decoded: {decoded} frames");
println!("Errors: {errors}");
if let Some(ft) = first_frame_time {
println!("First frame: {:.1}ms", ft.as_secs_f64() * 1000.0);
}
println!("Total decode: {:.0}ms", total_time.as_secs_f64() * 1000.0);
if decoded > 0 {
println!(
"Per frame: {:.2}ms ({:.1} FPS)",
total_time.as_secs_f64() * 1000.0 / decoded as f64,
decoded as f64 / total_time.as_secs_f64()
);
}
}