使用rust操作command,调用ffprobe获取video duration,然后使用ffmpeg filter 获取需要的帧拼接成Nx1的雪碧图
use dialoguer::{theme::ColorfulTheme, Input, Select}; use anyhow::Result; use std::env; use std::fs::File; use std::io::prelude::*; use std::process::{Command, Stdio}; pub fn main_process()-> Result<String> { let file_path: String = get_video_file()?; handle_ffmpeg(&file_path)?; return Ok(file_path); } fn get_video_file() -> Result<String> { //输入要处理的video文件地址 let file_path: String = Input::new() .with_prompt("请输入要处理的文件地址") .with_initial_text("") .default("".into()) .interact_text()?; return Ok(file_path); } fn handle_ffmpeg( file_path: &String) ->Result<()>{ let mut frames_output = Command::new("ffprobe") .args(["-v","error", "-show_entries", "format=duration", "-of", "default=noprint_wrappers=1:nokey=1" ,file_path]) .output() .expect("failed to execute process"); let mut duration = String::from_utf8_lossy(&frames_output.stdout).trim().to_string(); //方法一 通过时间间隔取帧,可能有刚好遇到间隔取不到帧的问题 // let duration = duration.parse::<f64>().expect("failed to parse duration") / 25.0; // let interval = ["select=(isnan(prev_selected_t)+gte(t-prev_selected_t\\,", duration.to_string().as_str() , ")),scale=120:120,crop=120:120,tile=1x25,format=pix_fmts=rgb24"].join(""); // let res = Command::new("ffmpeg") // .args(["-y", "-i", file_path, "-f", "image2", "-vframes", "1", "-vf", interval.as_str() ,"output.png"]) // .output() // .expect("failed to execute process"); // 方法二 算出间隔多少帧取一帧,比较准确 let duration = (duration.parse::<f64>().expect("failed to parse duration") * 25.0 / 25.0).floor(); //先乘以帧率,再除以要取的帧个数,再向下取整 let interval = ["select=not(mod(n\\,", duration.to_string().as_str() , ")),scale=120:120,crop=120:120,tile=1x25,format=pix_fmts=rgb24"].join(""); Command::new("ffmpeg") .args(["-y","-i", file_path, "-vf", interval.as_str() ,"-frames","1","output.png"]) .output() .expect("failed to execute process"); Ok(()) }
网页端
<div className="App"> <div className="sprite-wp"> <div className="sprite"/> </div> </div>
.sprite-wp { width: 40px; height: 40px; overflow: hidden; } .sprite { width: 40px; height: 1000px; will-change: transform; background: url("../public/output.png") no-repeat center; animation: frame 4000ms steps(25) both infinite; } @keyframes frame { 0% {transform: translate3d(0,0,0);} 100% {transform: translate3d(0,-1000px,0);} }
How to use:
- brew install pkg-config ffmpeg
- run binary file “keyframe” : ./keyframe