經(jīng)過一次不痛快的和他人合作之后,我寫了這段shell腳本,主要用于視頻文件的轉(zhuǎn)換隊(duì)列處理,和視頻大文件的截取.涉及到諸如3gp,asf,wmv,avi,mp4,mpeg,mov,vob,rm,rmvb等格式的轉(zhuǎn)換成flv,同時(shí)分段截取各種格式的大文件.主要采用的工具無(wú)非就是開源的
ffmpeg,
mencoder,
MediaInfo,前兩者轉(zhuǎn)換和截取,后者獲取視頻元數(shù)據(jù).多謝
sleetdrop推薦.
對(duì)于視頻文件的元數(shù)據(jù)而言,是否都存在都柏林元數(shù)據(jù),特征元數(shù)據(jù)是否值得信賴是一個(gè)問題,因此在腳本中做了一些信任機(jī)制的處理,腳本寫的很清晰,應(yīng)該不是太難懂,而且里面都做了撇腳的英文注釋.已經(jīng)通過功能測(cè)試,至于壓力測(cè)試,正在進(jìn)行中.不過對(duì)于腳本運(yùn)行環(huán)境,是在ffmpeg+mencoder環(huán)境下運(yùn)行的,對(duì)于
怎樣搭建ffmpeg+mencoder,可以參考
這篇文章.
可能會(huì)要應(yīng)用于實(shí)際比較惡劣的情況中,因此還需要對(duì)代碼進(jìn)行改進(jìn),甚至可能要用到
shc,日志功能和錯(cuò)誤處理功能還要加強(qiáng),還有就是根據(jù)不同格式和不同質(zhì)量的視頻文件,采用不同的方法進(jìn)行轉(zhuǎn)換也是在下一步策劃之中.如果大家有什么更好的方法,或者對(duì)此進(jìn)行了改進(jìn),請(qǐng)共享出來(lái)吧.
引用
#!/bin/bash
#
#file: convert_videos.sh
#usage:
#desc: convert many other formats of videos to flv format
#author: Spark Zheng
#version: V1.0
#date: 2007-05-31
declare -a video_format #video formats
declare -a video_size #video sizes
declare -a video_metadata #array of the metadata geted by MediaInfo
declare -i flag #delay flag, wait for queue
declare -i file_size #the video file size, we trust it!
VIDEO_IN_DIR=/home/zhengyu/video_again #video queue dir
VIDEO_OUT_DIR=/home/zhengyu/flv #the flv videos moved to
VIDEO_BAK_DIR=/home/zhengyu/video_bak #video which has handled moved to, need?
VIDEO_FORMAT_FILE=/home/zhengyu/code/shell/convert_formats.list #formats, just define it
VIDEO_SIZE_FILE=/home/zhengyu/code/shell/convert_sizes.list #sizes, frame sizes, just define it
DEBUG=1 #debug flag: 1 to debug, 0 to cancel
MediaInfo_dir=/usr/local/bin #MediaInfo tool‘s dir
ffmpeg_dir=/usr/local/bin #ffmpeg tool‘s dir
mencoder_dir=/usr/local/bin #mencoder tool‘s dir
file_limit=10485760 #file size to check: 10M(10*1024*1024)
diff_time_max=600 #diff between caculate time and get time: 10 minute(600 second)
diff_time_min=-600 #same as diff_time_max
bit_rate_max=41943040 #trust when below the max bit rate: 40Mbps(40*1024*1024)
bit_rate_min=1024 #trust when over the min bit rate: 1Kbps(1*1024)
time_to_trucate=1800 #trucate the video when playtime is over: 30 minute(1800 second)
video_format=($(cat $VIDEO_FORMAT_FILE))
video_size=($(cat $VIDEO_SIZE_FILE))
flag=0 #first init flag with 0
pid=$$
#####################functions start#############################
#function: get_metadata
#usage: fill the video_metadata array with useful arguments
#return: 0
function get_metadata()
{
# video_metadata[0]=$(cat tmp|grep "PlayTime"|awk -F: ‘{if(NR==1) print $2}‘)
# video_metadata[1]=$(cat tmp|grep "Bit rate"|awk -F: ‘{if(NR==1) print $2}‘)
# video_metadata[2]=$(cat tmp|grep "File size"|awk -F: ‘{if(NR==1) print $2}‘)
# video_metadata[3]=$(cat tmp|grep "Format"|awk -F: ‘{if(NR==1) print $2}‘)
# video_metadata[4]=$(cat tmp|grep "Width"|awk -F: ‘{if(NR==1) print $2}‘)
# video_metadata[5]=$(cat tmp|grep "Height"|awk -F: ‘{if(NR==1) print $2}‘)
# video_metadata[6]=$(cat tmp|grep "Frame rate"|awk -F: ‘{if(NR==1) print $2}‘)
#you may add it like above, as an example.
video_metadata=($(awk -F: ‘BEGIN{PT=1;pt=0;BR=1;br=0;F=1;f=0;FMT=1;fmt="";W=1;w=0;H=1;h=0;FR=1;fr=0;}
{if(($0 ~ "PlayTime")&&(PT==1))
{PT=0; split($2,array," "); if(array[1]~"h"){pt+=3600*int(array[1]);} else if(array[1]~"mn") {pt+=60*int(array[1]);} else if(array[1]~"s") {pt+=int(array[1]);} if(array[2]~"mn") {pt+=60*int(array[2]);} else if(array[2]~"s") {pt+=int(array[2]);} }
if(($0 ~ "Bit rate")&&(BR==1))
{BR=0; split($2,array," "); if(array[2]~"Mbps") {br=1024*1024*int(array[1]);} else if(array[2]~"Kbps") {br=1024*int(array[1]);} else {br=int(array[1]);}}
if(($0 ~ "File size")&&(F==1))
{F=0; split($2,array," "); if(array[2]~"MiB") {f=1024*1024*int(array[1]);} else if(array[2]~"KiB") {f=1024*int(array[1]);} else {f=int(array[1]);}}
if(($0 ~ "Codec")&&(FMT==1))
{FMT=0; split($2,array," "); fmt=array[1];}
if(($0 ~ "Width")&&(W==1))
{W=0; split($2,array," "); w=int(array[1]);}
if(($0 ~ "Height")&&(H==1))
{H=0; split($2,array," "); h=int(array[1]);}
if(($0 ~ "Frame rate")&&(FR==1))
{FR=0; split($2,array," "); fr=int(array[1]);}}
END{print pt" "br" "f" "fmt" "w" "h" "fr;}‘ /tmp/$0.tmp))
return 0
}
#function: check_metadata
#usage: to decide the metadata we get by get_metadata() weather were trusted
#return: >0 not trusted, =0 trusted
function check_metadata()
{
if [ $DEBUG -gt 0 ]
then
echo ${video_metadata[0]}
echo ${video_metadata[1]}
echo ${video_metadata[2]}
echo ${video_metadata[3]}
echo ${video_metadata[4]}
echo ${video_metadata[5]}
echo ${video_metadata[6]}
echo ""
fi
#not trust bit rates
if [ ${video_metadata[1]} -gt $bit_rate_max -o ${video_metadata[1]} -lt $bit_rate_min ]
then
return 15
fi
diff_cacul=$(expr 8 \* $file_size / ${video_metadata[1]} - ${video_metadata[0]})
#not trust play times
if [ $diff_cacul -gt $diff_time_max -o $diff_cacul -lt $diff_time_min ]
then
[ $DEBUG -gt 0 ] && echo "\$diff_cacul is $diff_cacul"
return 14
fi
#need to check the format?
return 0
}
#function: convert_video
#usage: use ffmpeg/mencoder to convert the videos, trucate videos when necessary
#return: 0
function convert_video()
{
if [ -z "$1" ]
then
return 13
fi
if [ -z "$2" ]
then
trucate_flag=0 #0 means not to trucate the video file
else
trucate_flag=$2
fi
#any other smart method to get the flv_file? another method is directly assigned with $1.flv,maybe better
# flv_file=$(echo $1 | awk ‘{split($0,array,".");for(i in array) {;} array[i]=""; for(j in array) {if(j
flv_file=$1 #for some convenience
#need trucate?
case "$trucate_flag" in
0 )
if [ "X${video_metadata[3]}" == "XRealVideo" ] #rv format like rm/rmvb use mencoder
then
: #mencoder args, need more opts? finish it by yourself like " $mencoder_dir/mencoder $1 -o ${flv_file}.flv ** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "mencoder failed to convert video $1"
fi
[ $DEBUG -gt 0 ] && echo "mencoder invoked for arg2:$trucate_flag"
else #oth format use ffmpeg for convert speed
: #ffmpeg args, need to change the ffmpeg‘s args? you may finish by yourself, like " $ffmpeg_dir/ffmpeg -y -i $1 ${flv_file}.flv ** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "ffmpeg failed to convert video $1"
fi
[ $DEBUG -gt 0 ] && echo "ffmpeg invoked for arg2:$trucate_flag"
fi
;;
* ) #we could take care
loop_count=$(expr $trucate_flag + 1)
dura_time=$(expr ${video_metadata[0]} / $loop_count)
for trucate_count in $(seq $loop_count)
do
if [ "X${video_metadata[3]}" == "XRealVideo" ] #rv format like rm/rmvb use mencoder
then
case "$trucate_count" in
1)
: #mencoder args, need more opts? finish it by yourself, like "$mencoder_dir/mencoder $1 -o ${flv_file}.${trucate_count}.flv -ss $((($trucate_count-1)*$dura_time)) -endpos $(($dura_time+2)) ** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "mencoder failed to convert video $1"
fi
;;
$loop_count)
: #mencoder args, need more opts? finish it by yourself, like " $mencoder_dir/mencoder $1 -o ${flv_file}.${trucate_count}.flv -ss $((($trucate_count-1)*$dura_time-2)) ** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "mencoder failed to convert video $1"
fi
;;
*)
: #mencoder args, need more opts? finish it by yourself, like " $mencoder_dir/mencoder $1 -o ${flv_file}.${trucate_count}.flv -ss $((($trucate_count-1)*$dura_time-2)) -endpos $(($dura_time+2)) ** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "mencoder failed to convert video $1"
fi
;;
esac
[ $DEBUG -gt 0 ] && echo "mencoder invoked for arg2:$trucate_flag"
else #oth format use ffmpeg for convert speed
case "$trucate_count" in
1)
: #ffmpeg args, need more opts? finish it by yourself, like "$ffmpeg_dir/ffmpeg -y -i $1 -ss $((($trucate_count-1)*$dura_time)) -t $(($dura_time+2)) ${flv_file}.${trucate_count}.flv *** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "ffmpeg failed to convert video $1"
fi
;;
$loop_count)
: #ffmpeg args, need more opts? finish it by yourself, like " $ffmpeg_dir/ffmpeg -y -i $1 -ss $((($trucate_count-1)*$dura_time-2)) ${flv_file}.${trucate_count}.flv ** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "ffmpeg failed to convert video $1"
fi
;;
*)
: #ffmpeg args, need more opts? finish it by yourself, like " $ffmpeg_dir/ffmpeg -y -i $1 -ss $((($trucate_count-1)*$dura_time-2)) -t $(($dura_time+2)) ${flv_file}.${trucate_count}.flv ** "
if [ $? -ne 0 ]
then
[ $DEBUG -gt 0 ] && echo "ffmpeg failed to convert video $1"
fi
;;
esac
[ $DEBUG -gt 0 ] && echo "ffmpeg invoked for arg2:$trucate_flag"
fi
done #trucate times end.
;;
esac #need trucate end.
return 0
}
#function: mv_video
#usage: mv the source video and converted videos to VIDEO_OUT_DIR for the loop needed
#return: 0
function mv_video()
{
if [ -z "$1" ]
then
return 12
fi
flvs=$(find . -name \*.flv -type f)
if [ -n "$flvs" ]
then
mv -f $flvs $VIDEO_OUT_DIR
fi
if [ -e "$1" ]
then
mv -f $1 $VIDEO_BAK_DIR
fi
return 0
}
#function: write_pid
#usage: write the script pid to /tmp/$0.pid
#return: 0
function write_pid()
{
echo "$pid" > /tmp/$0.pid
return 0
}
#function: write_log
#usage: write logs to file? to syslogd?
#return: 0
function write_log()
{
case "$1" in
0)
logger -t $0 -i "$2"
;;
*)
log_time=$(date +"%F %T")
echo "${log_time}\tlocalhost\t${0}[${pid}]:\t${2}" >> $3
;;
esac
return 0
}
#function: sig_init_kill
#usage: when in loops, we need to handle the SIGINT and SIGKILL
#return: 1(exit)
function sig_int_kill()
{
if [ -e "/tmp/$0.pid" ]
then
rm -rf /tmp/$0.pid
fi
if [ -e "/tmp/$0.tmp" ]
then
rm -rf /tmp/$0.tmp
fi
if [ -e "/tmp/$0.sig" ]
then
rm -rf /tmp/$0.sig
fi
echo "when out:" >> /tmp/$0.sig
echo "video_file is $video_file" >> /tmp/$0.sig
echo "trucate_flag is $trucate_flag" >> /tmp/$0.sig
echo "...... enjoy your finishing ......" >> /tmp/$0.sig
exit 1
}
######################functions end##############################
write_pid
trap ‘sig_int_kill‘ SIGINT
trap ‘sig_int_kill‘ SIGKILL
pushd $VIDEO_IN_DIR
while : #yes, the SIG is handled
do
video_file=$(find . -type f |tail -n 1) #get a file? many, you complete
[ $DEBUG -gt 0 ] && echo "\$video_file is $video_file"
if [ -z "$video_file" ] #there is no file,waiting for a coming file
then
sleep $((1<<>
((flag++))
if [ $flag -gt 10 ]
then
flag=1
fi
else
flag=0
file_size=$(ls -l $video_file |awk ‘{print $5}‘) #we trust this file_size
if [ $file_size -gt $file_limit ] #too small,the vidoe doesn‘t need to trucate
then
${MediaInfo_dir}/MediaInfo $video_file >/tmp/$0.tmp 2>&1
get_metadata #get the video‘s metadata
check_metadata #check it whether it‘s trusted to
if [ $? -ne 0 ] #not trusted?
then
convert_video $video_file 0
mv_video $video_file
[ $DEBUG -gt 0 ] && echo "not trust not trucated, converted and continued"
continue; #just continue; we finished the untrustable video
fi
if [ ${video_metadata[0]} -gt $time_to_trucate ] #need trucate
then
full_time=$(expr ${video_metadata[0]} / $time_to_trucate)
convert_video $video_file $full_time
mv_video $video_file
[ $DEBUG -gt 0 ] && echo "trucated and converted and continued"
continue; #trucated and finished
fi
#the video is pretty simple
convert_video $video_file 0
mv_video $video_file
[ $DEBUG -gt 0 ] && echo "play time not too long not trucated, converted and continued"
else #file size is too small to trucate
convert_video $video_file 0
mv_video $video_file
[ $DEBUG -gt 0 ] && echo "file to small not trucated, converted and continued"
fi
fi
done
popd
exit 0