FFMPEG多进程转码
最近做项目,需要对linux服务器上的视频做批量转码,所以研究了下linux shell编程,通过各种途径搜寻资料,结合项目实际情况,完成了以下脚本,功能比较简单,希望对其他有相似需求的人有所帮助。
代码的主要功能为配置进程上限,并行对指定文件夹内文件进行递归,将所有非mp4格式的视频进行转码,统一转为mp4格式,同时生成缩略图
知识点归纳
- 文件递归
- 并发进程数量控制
- ffmpeg命令使用
#!/bin/bash
#未解决的问题:输入的第二个参数无法获取;ok
#qsv格式视频无法转码,需要检查ffmpeg;
#转码成功的视频,放入到一个专门的文件中记录;
#转码失败的视频,放入到一个专门的文件中记录;
#转码中的视频,跳过;
function scandir() {
local cur_dir parent_dir workdir isTransFailed
workdir=$1
isTransFailed=$2
if [[ $isTransFailed = "" ]]
then
isTransFailed="n"
else
isTransFailed="y"
fi
cd $workdir
if [ $workdir = "/" ]
then
cur_dir=""
else
cur_dir=$(pwd)
fi
for dirlist in $(ls $cur_dir)
do
if test -d $dirlist
then
cd $dirlist
scandir $cur_dir/$dirlist $isTransFailed
cd ..
else #cat /proc/sys/kernel/random/uuid
#跳过非法格式的文件
if [ "${dirlist##*.}" = "out" ] || [ "${dirlist##*.}" = "jpg" ] || [ "${dirlist##*.}" = "mp4" ] || [ "${dirlist##*.}" = "" ]
then
continue
fi
#如果没有对应的out文件,直接转码
if [ ! -f "$cur_dir/${dirlist%%.*}.mp4.out" ]
then
echo ${cur_dir}/${dirlist}/"开始转码》》》"
read -u6 # 从文件描述符6中读取行(实际指向fifo管道)
{
/usr/local/bin/ffmpeg -y -i ${cur_dir}/${dirlist} -vcodec libx264 -vb 450k -acodec libvo_aacenc ${cur_dir}/${dirlist%%.*}.mp4 > ${cur_dir}/${dirlist%%.*}.mp4.out 2>&1
echo >&6 # 再次往fifo管道文件中写入一个空行。
} &
echo "查看进度,请执行命令:tail -f "${cur_dir}/"\"${dirlist%%.*}.mp4.out\""
fi
#如果已经转过码,那么会有out文件,根据out文件判断是否需要再次转码
local outfile="$cur_dir/${dirlist%%.*}.mp4.out"
#转码失败,或者out文件被清空,则打印原视频文件名
if [ $(cat $outfile|grep failed)x = "Conversion failed!"x ] || [ ! -s $outfile ]
then
#echo ${outfile}
echo "文件转换失败:"$cur_dir/$dirlist
local transErr=$(cat $outfile | awk -F ':' '/.*input.*/ {print $2}')
echo "失败原因:"$transErr
if [[ $isTransFailed = "y" ]]
then
echo ${cur_dir}/${dirlist}/"重新执行转码》》》"
read -u6 # 从文件描述符6中读取行(实际指向fifo管道)
{
/usr/local/bin/ffmpeg -y -i ${cur_dir}/${dirlist} -vcodec libx264 -vb 450k -acodec libvo_aacenc ${cur_dir}/${dirlist%%.*}.mp4 > ${cur_dir}/${dirlist%%.*}.mp4.out 2>&1
echo >&6 # 再次往fifo管道文件中写入一个空行。
} &
echo "查看进进度,请执行命令:tail -f "${cur_dir}/"\"${dirlist%%.*}.mp4.out\""
fi
else
#tmpt=$(cat ${cur_dir}/${dirlist} | awk -F ',' '/Input.*/ {print $3}')
echo "文件转换成功,或者在转换中:"$cur_dir/$dirlist > /dev/null
fi
fi
done
}
oldIFS=$IFS
IFS=$(echo -en "\n\b")
if test -d $1
then
#############进程控制管道########################################################################
SEND_THREAD_NUM=200 #设置线程数,在这里所谓的线程,其实就是几乎同时放入后台(使用&)执行的进程。
tmp_fifofile="/tmp/$$.fifo" # 脚本运行的当前进程ID号作为文件名
mkfifo "$tmp_fifofile" # 新建一个随机fifo管道文件
exec 6<>"$tmp_fifofile" # 定义文件描述符6指向这个fifo管道文件
for ((i=0;i<$SEND_THREAD_NUM;i++));do
echo >&6 # for循环 往 fifo管道文件中写入SEND_THREAD_NUM个空行
done
###########开始执行转码
scandir $1 $2
###########等待转换结束
wait
rm "$tmp_fifofile"
elif test -f $1
then
echo "you input a file but not a directory,pls reinput and try again"
exit 1
else
echo "the Directory isn't exist which you input,pls input a new one!!"
exit 1
fi
IFS=$oldIFS