需求:因为时不时会在B站上分享一些游戏BGM合集,拿到多个音频文件之后要先选一张好看的图片,然后逐个音频地与这张图片合成一个静态的视频,这个过程用视频编辑软件来操作的话非常地重复、费事,故考虑使用FFmpeg来转换合成并实现批量操作。
方案一
ffmpeg -i BGM01.mp3 -i cover.png -b:v 2400k -b:a 192k out.mp4
-i 参数添加输入的音频和图片, -b:v 和 -b:a 分别用于设置视频和音频的比特率。
这样就把音频和图片合成在一起了。视频在本地播放的时候是没有问题的,声音正常,图片会连续显示,但是仔细看看播放信息可发现一些东西:帧率和位率是0,时长只有40ms。
上传的时候也会提示“转码失败” (因为时长太短):
方案二
ffmpeg -loop 1 -y -i cover.png -i BGM01.MP3 -shortest -r 24 -b:a 2400k -b:a 192k out.mp4
我们要做的是让图片不止是有一帧的时长,而是延伸到整个音频的长度,于是这里使用 -loop 来循环播放某个输入的元素,后面跟的1代表使用第一个输入进行循环,同时需要加 -shortest 参数来限制循环的次数,指定视频的时长以输入的中最短的时长为准(也就是音频的长度),否则会无限循环。-r 24则指定帧率。
OK,这回帧率显示正常了,这样生成的视频就可以上传了,可以根据实际需要调整一下码率。
最后就是写一个批量处理的脚本了,可以用任意脚本语言来写,这里用PHP。
注:-hwaccel qsv 是硬件加速选项,可以去掉,具体加速选项参考这里 HWAccelIntro – FFmpeg
<?php
// 请根据操作系统选择一行执行,删除另外一行
system("dir /b *.mp3 > list.txt"); // windows
system("ls *.mp3 > list.txt"); // linux
$list = file_get_contents("list.txt");
$files = explode("\n", $list);
unset($files[count($files) - 1]); // 清除末行空行
foreach($files as $file) {
$filename = substr($file, 0, strlen($file) - 4); //去除文件格式.mp3后缀
system("ffmpeg -loop 1 -y -hwaccel auto -i cover.png -i \"{$file}\" -shortest -r 24 -b:a 192k \"{$filename}.mp4\"");
}
CMD
forfiles /M *.mp3 /C "ffmpeg -loop 1 -y -hwaccel auto -i cover2.png -i \"@FILE\" -shortest -r 24 -b:a 192k \"@FNAME.mp4\""
Shell
#!/bin/bash
ls *.mp3 | while read file
do
filename=`echo "$file" | sed s/\.mp3//i`
ffmpeg -loop 1 -y -hwaccel auto -i cover2.png -i "${file}" -shortest -r 24 -b:a 192k "${filename}.mp4"
done