lesavka/client/src/sync_probe/analyze/test_support.rs

119 lines
3.9 KiB
Rust

use std::env;
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::Path;
use temp_env::with_var;
use tempfile::tempdir;
pub(super) fn with_fake_media_tools<T>(
ffprobe_output: &[u8],
ffmpeg_video_output: &[u8],
ffmpeg_audio_output: &[u8],
test: impl FnOnce(&Path) -> T,
) -> T {
let temp_dir = tempdir().expect("tempdir");
fs::write(temp_dir.path().join("ffprobe.out"), ffprobe_output).expect("write ffprobe");
fs::write(
temp_dir.path().join("ffmpeg-video.out"),
ffmpeg_video_output,
)
.expect("write ffmpeg video");
fs::write(
temp_dir.path().join("ffmpeg-audio.out"),
ffmpeg_audio_output,
)
.expect("write ffmpeg audio");
write_executable(
temp_dir.path(),
"ffprobe",
"#!/bin/sh\ncat \"$(dirname \"$0\")/ffprobe.out\"\n",
);
write_executable(
temp_dir.path(),
"ffmpeg",
"#!/bin/sh\ncase \" $* \" in\n *\" -map 0:v:0 \"*) cat \"$(dirname \"$0\")/ffmpeg-video.out\" ;;\n *\" -map 0:a:0 \"*) cat \"$(dirname \"$0\")/ffmpeg-audio.out\" ;;\n *) printf 'unexpected ffmpeg args: %s\\n' \"$*\" >&2; exit 64 ;;\nesac\n",
);
let prior_path = env::var("PATH").unwrap_or_default();
let merged_path = if prior_path.is_empty() {
temp_dir.path().display().to_string()
} else {
format!("{}:{prior_path}", temp_dir.path().display())
};
let capture_path = temp_dir.path().join("capture.mkv");
fs::write(&capture_path, b"fake-capture").expect("write capture");
with_var("PATH", Some(merged_path.as_str()), || test(&capture_path))
}
pub(super) fn frame_json(timestamps: &[f64]) -> Vec<u8> {
let frames = timestamps
.iter()
.map(|timestamp| {
serde_json::json!({
"best_effort_timestamp_time": format!("{timestamp:.3}")
})
})
.collect::<Vec<_>>();
serde_json::to_vec(&serde_json::json!({ "frames": frames })).expect("frame json")
}
pub(super) fn click_track_samples(click_times_s: &[f64], total_samples: usize) -> Vec<i16> {
let mut samples = vec![0i16; total_samples];
for click_time_s in click_times_s {
let start = (*click_time_s * 48_000.0).round() as usize;
for sample in samples.iter_mut().skip(start).take(300) {
*sample = 18_000;
}
}
samples
}
pub(super) fn thumbnail_video_bytes(brightness_values: &[u8]) -> Vec<u8> {
const SIDE: usize = 64;
let mut bytes = Vec::with_capacity(brightness_values.len() * SIDE * SIDE);
for brightness in brightness_values {
let mut frame = vec![20u8; SIDE * SIDE];
for y in SIDE / 4..SIDE - SIDE / 4 {
for x in SIDE / 4..SIDE - SIDE / 4 {
frame[y * SIDE + x] = *brightness;
}
}
bytes.extend_from_slice(&frame);
}
bytes
}
pub(super) fn thumbnail_rgb_video_bytes(colors: &[(u8, u8, u8)]) -> Vec<u8> {
const SIDE: usize = 64;
let mut bytes = Vec::with_capacity(colors.len() * SIDE * SIDE * 3);
for (r, g, b) in colors {
let mut frame = vec![0u8; SIDE * SIDE * 3];
for y in SIDE / 4..SIDE - SIDE / 4 {
for x in SIDE / 4..SIDE - SIDE / 4 {
let offset = (y * SIDE + x) * 3;
frame[offset] = *r;
frame[offset + 1] = *g;
frame[offset + 2] = *b;
}
}
bytes.extend_from_slice(&frame);
}
bytes
}
pub(super) fn audio_samples_to_bytes(samples: &[i16]) -> Vec<u8> {
samples
.iter()
.flat_map(|sample| sample.to_le_bytes())
.collect()
}
fn write_executable(dir: &Path, name: &str, contents: &str) {
let path = dir.join(name);
fs::write(&path, contents).expect("write script");
let mut permissions = fs::metadata(&path).expect("script metadata").permissions();
permissions.set_mode(0o755);
fs::set_permissions(&path, permissions).expect("script permissions");
}