Add h264 codec detection and orphaned playlist cleanup job
Implement `is_h264_encoded` to detect existing h264 videos and optimize processing by using stream copy when possible. Introduce a background job for cleaning up orphaned playlists and segments based on missing source videos. Improve checks for playlist generation necessity.
This commit is contained in:
@@ -100,6 +100,44 @@ pub fn generate_video_thumbnail(path: &Path, destination: &Path) {
|
||||
.expect("Failure to create video frame");
|
||||
}
|
||||
|
||||
/// Check if a video is already encoded with h264 codec
|
||||
/// Returns true if the video uses h264, false otherwise or if detection fails
|
||||
async fn is_h264_encoded(video_path: &str) -> bool {
|
||||
let output = tokio::process::Command::new("ffprobe")
|
||||
.arg("-v")
|
||||
.arg("error")
|
||||
.arg("-select_streams")
|
||||
.arg("v:0")
|
||||
.arg("-show_entries")
|
||||
.arg("stream=codec_name")
|
||||
.arg("-of")
|
||||
.arg("default=noprint_wrappers=1:nokey=1")
|
||||
.arg(video_path)
|
||||
.output()
|
||||
.await;
|
||||
|
||||
match output {
|
||||
Ok(output) if output.status.success() => {
|
||||
let codec = String::from_utf8_lossy(&output.stdout);
|
||||
let codec = codec.trim();
|
||||
debug!("Detected codec for {}: {}", video_path, codec);
|
||||
codec == "h264"
|
||||
}
|
||||
Ok(output) => {
|
||||
warn!(
|
||||
"ffprobe failed for {}: {}",
|
||||
video_path,
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
);
|
||||
false
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to run ffprobe for {}: {}", video_path, e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VideoPlaylistManager {
|
||||
playlist_dir: PathBuf,
|
||||
playlist_generator: Addr<PlaylistGenerator>,
|
||||
@@ -306,25 +344,43 @@ impl Handler<GeneratePlaylistMessage> for PlaylistGenerator {
|
||||
return Err(std::io::Error::from(std::io::ErrorKind::AlreadyExists));
|
||||
}
|
||||
|
||||
// Check if video is already h264 encoded
|
||||
let is_h264 = is_h264_encoded(&video_file).await;
|
||||
let use_copy = is_h264;
|
||||
|
||||
if use_copy {
|
||||
info!("Video {} is already h264, using stream copy", video_file);
|
||||
span.add_event("Using stream copy (h264 detected)", vec![]);
|
||||
} else {
|
||||
info!("Video {} needs transcoding to h264", video_file);
|
||||
span.add_event("Transcoding to h264", vec![]);
|
||||
}
|
||||
|
||||
tokio::spawn(async move {
|
||||
let ffmpeg_result = tokio::process::Command::new("ffmpeg")
|
||||
.arg("-i")
|
||||
.arg(&video_file)
|
||||
.arg("-c:v")
|
||||
.arg("h264")
|
||||
.arg("-crf")
|
||||
.arg("21")
|
||||
.arg("-preset")
|
||||
.arg("veryfast")
|
||||
.arg("-hls_time")
|
||||
.arg("3")
|
||||
.arg("-hls_list_size")
|
||||
.arg("100")
|
||||
.arg("-vf")
|
||||
.arg("scale=1080:-2,setsar=1:1")
|
||||
.arg(playlist_file)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::piped())
|
||||
let mut cmd = tokio::process::Command::new("ffmpeg");
|
||||
cmd.arg("-i").arg(&video_file);
|
||||
|
||||
if use_copy {
|
||||
// Video is already h264, just copy the stream
|
||||
cmd.arg("-c:v").arg("copy");
|
||||
cmd.arg("-c:a").arg("aac"); // Still need to ensure audio is compatible
|
||||
} else {
|
||||
// Need to transcode
|
||||
cmd.arg("-c:v").arg("h264");
|
||||
cmd.arg("-crf").arg("21");
|
||||
cmd.arg("-preset").arg("veryfast");
|
||||
cmd.arg("-vf").arg("scale=1080:-2,setsar=1:1");
|
||||
cmd.arg("-c:a").arg("aac");
|
||||
}
|
||||
|
||||
// Common HLS settings
|
||||
cmd.arg("-hls_time").arg("3");
|
||||
cmd.arg("-hls_list_size").arg("100");
|
||||
cmd.arg(&playlist_file);
|
||||
cmd.stdout(Stdio::null());
|
||||
cmd.stderr(Stdio::piped());
|
||||
|
||||
let ffmpeg_result = cmd
|
||||
.output()
|
||||
.inspect_err(|e| error!("Failed to run ffmpeg on child process: {}", e))
|
||||
.map_err(|e| std::io::Error::other(e.to_string()))
|
||||
|
||||
Reference in New Issue
Block a user