124 lines
3.5 KiB
Rust
124 lines
3.5 KiB
Rust
use spielab::{
|
|
media::{VideoCapture, VideoEncoder},
|
|
settings::{VideoConfig, VideoQuality},
|
|
};
|
|
use std::{process::Stdio, time::Duration};
|
|
use tokio::{io::AsyncWriteExt, process::Command};
|
|
|
|
/// Exercises the actual encoder, Annex-B reader and an independent H.264 decoder.
|
|
#[tokio::test]
|
|
#[ignore = "requires FFmpeg with libx264 installed"]
|
|
async fn encoded_access_units_decode_to_three_video_frames() {
|
|
decode_frames(VideoConfig {
|
|
width: 320,
|
|
height: 240,
|
|
..VideoQuality::Efficient.config()
|
|
})
|
|
.await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "requires FFmpeg with libx264 installed"]
|
|
async fn every_quality_preset_encodes_the_requested_resolution_and_frame_rate() {
|
|
for quality in VideoQuality::ALL {
|
|
decode_frames(quality.config()).await;
|
|
}
|
|
}
|
|
|
|
async fn decode_frames(config: VideoConfig) {
|
|
decode_with_encoder(config, &VideoEncoder::Software, 3).await;
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "requires a working VA-API H.264 encoder on /dev/dri/renderD128"]
|
|
async fn hardware_frames_decode_at_every_quality_and_auto_detection_selects_gpu() {
|
|
let encoder = VideoEncoder::Vaapi("/dev/dri/renderD128".into());
|
|
for quality in VideoQuality::ALL {
|
|
encoder.probe(quality.config()).await.unwrap();
|
|
decode_with_encoder(
|
|
quality.config(),
|
|
&encoder,
|
|
quality.config().fps as usize + 2,
|
|
)
|
|
.await;
|
|
}
|
|
assert!(matches!(
|
|
VideoEncoder::detect(VideoQuality::High.config()).await,
|
|
VideoEncoder::Vaapi(_)
|
|
));
|
|
}
|
|
|
|
async fn decode_with_encoder(config: VideoConfig, encoder: &VideoEncoder, count: usize) {
|
|
let mut capture = VideoCapture::start_with_encoder(None, config, encoder).unwrap();
|
|
let mut stream = vec![];
|
|
for index in 0..count {
|
|
let nals = tokio::time::timeout(Duration::from_secs(5), capture.frame())
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
if index == 0 {
|
|
assert!(nals.iter().any(|n| n[0] & 31 == 7));
|
|
assert!(nals.iter().any(|n| n[0] & 31 == 8));
|
|
}
|
|
for nal in nals {
|
|
stream.extend_from_slice(&[0, 0, 0, 1]);
|
|
stream.extend_from_slice(&nal);
|
|
}
|
|
}
|
|
drop(capture);
|
|
let mut decoder = Command::new("ffmpeg")
|
|
.args([
|
|
"-hide_banner",
|
|
"-loglevel",
|
|
"error",
|
|
"-f",
|
|
"h264",
|
|
"-i",
|
|
"pipe:0",
|
|
"-f",
|
|
"framehash",
|
|
"pipe:1",
|
|
])
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.kill_on_drop(true)
|
|
.spawn()
|
|
.unwrap();
|
|
decoder
|
|
.stdin
|
|
.take()
|
|
.unwrap()
|
|
.write_all(&stream)
|
|
.await
|
|
.unwrap();
|
|
let output = tokio::time::timeout(Duration::from_secs(5), decoder.wait_with_output())
|
|
.await
|
|
.unwrap()
|
|
.unwrap();
|
|
assert!(
|
|
output.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
let decoded = String::from_utf8(output.stdout).unwrap();
|
|
assert!(
|
|
decoded.contains(&format!(
|
|
"#dimensions 0: {}x{}",
|
|
config.width, config.height
|
|
)),
|
|
"{decoded}"
|
|
);
|
|
assert!(
|
|
decoded.contains(&format!("#tb 0: 1/{}", config.fps)),
|
|
"{decoded}"
|
|
);
|
|
assert_eq!(
|
|
decoded
|
|
.lines()
|
|
.filter(|line| !line.starts_with('#') && !line.is_empty())
|
|
.count(),
|
|
count
|
|
);
|
|
}
|