无需存储帧到文件夹,用多段MP4视频生成单个视频的实现方案
问题描述
我有一组名为file1.mp4、file2.mp4……filen.mp4的MP4视频片段,希望编写代码依次遍历这些文件,每10秒提取一帧,再将提取的帧合成为帧率6fps的单个输出视频。我已有实现该功能的代码,但该代码会先将提取的帧存储到子文件夹中再合成视频,我认为这种方式效率较低。请问是否可以直接提取视频片段的帧并生成最终视频,无需将帧存储到文件夹中?
现有代码
#!/bin/bash # Step 1: Extract frames every 10 seconds input_folder="/home/aj/Videos/merge" output_folder="/home/aj/Videos/merge" subfolder="frames" # Create subfolder if it doesn't exist mkdir -p "$output_folder/$subfolder" # Get the total number of input files total_files=$(find "$input_folder" -maxdepth 1 -type f -name "*.mp4" | wc -l) processed_files=0 global_frame_counter=0 # Iterate through mp4 files in the input folder for file in "$input_folder"/*.mp4; do filename=$(basename "$file") filename_no_ext="${filename%.mp4}" # Extract frames every 10 seconds ffmpeg -i "$file" -vf fps=1/10 "$output_folder/$subfolder/output_${global_frame_counter}%02d.png" # Increment the global frame counter ((global_frame_counter ++)) # Increment the processed files count ((processed_files++)) # Calculate and echo the percentage of files processed percentage=$((processed_files * 100 / total_files)) echo -e "\033[1;32mProcessing: $percentage% complete\033[0m" done # Step 2: Combine frames into a single video cd "$output_folder/$subfolder" || exit ffmpeg -r 6 -f image2 -s 1920x1080 -i "output_%*.png" -vcodec libx264 -crf 25 -pix_fmt yuv420p "$output_folder/result.mp4" echo "Frames extracted and video created successfully."
解决方案
可以通过ffmpeg的管道(pipe)和滤镜链实现直接处理,跳过中间帧的磁盘存储,大幅提升效率。下面是优化后的脚本:
#!/bin/bash input_folder="/home/aj/Videos/merge" output_video="/home/aj/Videos/merge/result.mp4" total_files=$(find "$input_folder" -maxdepth 1 -type f -name "*.mp4" | wc -l) processed_files=0 # 构建滤镜链:为每个输入文件添加抽帧滤镜,再合并所有流 filter_complex="" map_args="" for file in "$input_folder"/*.mp4; do # 为当前文件添加fps滤镜(每10秒1帧) filter_complex+="[$processed_files:v]fps=fps=1/10[v$processed_files];" map_args+=" -map [v$processed_files]" ((processed_files++)) percentage=$((processed_files * 100 / total_files)) echo -e "\033[1;32mProcessing: $percentage% complete\033[0m" done # 添加concat滤镜合并所有抽帧后的视频流 filter_complex+=$(printf "[v%d]" $(seq 0 $((processed_files-1)))) filter_complex+="concat=n=$processed_files:v=1:a=0[vout]" # 执行ffmpeg命令,直接输出最终视频 ffmpeg -y $(printf "-i %s " "$input_folder"/*.mp4) -filter_complex "$filter_complex" \ -map "[vout]" -r 6 -vcodec libx264 -crf 25 -pix_fmt yuv420p "$output_video" echo "视频生成成功,已保存至 $output_video"
优化说明
- 无中间文件:全程在内存中处理帧数据,无需创建临时文件夹存储PNG帧,避免磁盘IO开销
- 单进程处理:用一个ffmpeg进程完成抽帧、合并、编码全流程,减少多进程启动的额外开销
- 逻辑简洁:通过滤镜链串联所有操作,流程更清晰,维护成本更低
内容的提问来源于stack exchange,提问作者Athekul
相关产品推荐
相关产品推荐

