我对 ffmpeg 还只是半新手。我可以用 1-pass crf 编码完成基本操作,但当我尝试 2-pass 编码时,我遇到了一个无法解决的错误。任何帮助都非常感谢。
简而言之,当我在 Ubuntu 中运行以下脚本时,
IFS=$'\n'
for i in `ls *.{ts,mkv,mpeg,mpg,mov,avi,mp4,m2t,mts}`
do
#Output naming scheme
extension=`echo "$i" | egrep -o '\.(ts|mkv|mpeg|mpg|mov|avi|mp4|mts)$'`
extensionFreeFileName=$(echo "$i" | awk -F `echo ${extension}` '{print $1}')
newFileName="$extensionFreeFileName"
#Extract proper frame rate from source video
testFrameFormat='/'
testFramsString=`mediainfo "$i" | grep "Frame rate "`
if [[ "$testFramsString" =~ "$testFrameFormat" ]]; then
frameRate=`mediainfo "$i" | grep "Frame rate " | awk -F '[()]' '{print $2}' | head -1`
else
frameRate=`mediainfo "$i" | grep "Frame rate " | awk -F "\: " '{print $2}' | awk -F " " '{print $1}' | awk -F " " '{print $1}' | head -1`
fi
#Video bit rate lookup and calculations
videoBitRate=`mediainfo "$i" | grep "Bit rate " | head -1 | tail -1 | awk -F' : ' '{print $2}' | awk -F' kb/s' '{print $1}' | sed 's| ||g'`
videoBitRate=$((videoBitRate * 1024))
avc35BitRate=$((videoBitRate * 35 / 100))
avc25BitRate=$((videoBitRate * 25 / 100))
hevc30BitRate=$((videoBitRate * 30 / 100))
hevc27BitRate=$((videoBitRate * 27 / 100))
hevc24BitRate=$((videoBitRate * 24 / 100))
hevc21BitRate=$((videoBitRate * 21 / 100))
hevc17BitRate=$((videoBitRate * 17 / 100))
#check to ensure calculations are being performed as expected
echo -e "\n\n\n\n"
echo -e "$i"
echo -e "$frameRate"
echo -e "$videoBitRate"
echo -e "$avc35BitRate"
echo -e "$avc25BitRate"
echo -e "$hevc30BitRate"
echo -e "$hevc27BitRate"
echo -e "$hevc24BitRate"
echo -e "$hevc21BitRate"
echo -e "$hevc17BitRate"
echo -e "\n\n\n"
#Encoding command (two-pass, multiple outputs)
~/bin/ffmpeg -analyzeduration 500000000 -probesize 500000000 -i "$i" -preset veryslow \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc30BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName hevc30" -an -f matroska /dev/null \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc27BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName hevc27" -an -f matroska /dev/null \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc24BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName hevc24" -an -f matroska /dev/null \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc21BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName hevc21" -an -f matroska /dev/null \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc17BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName hevc17" -an -f matroska /dev/null \
-vf yadif -r "$frameRate" -map 0:v -c:v libx265 -b:V "$avc35BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName avc35" -an -f matroska /dev/null \
-vf yadif -r "$frameRate" -map 0:v -c:v libx265 -b:V "$avc25BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName avc25" -an -f matroska /dev/null &&
~/bin/ffmpeg -analyzeduration 500000000 -probesize 500000000 -i "$i" -preset veryslow \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc30BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName hevc30" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 30pct).mkv" \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc27BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName hevc27" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 27pct).mkv" \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc24BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName hevc24" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 24pct).mkv" \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc21BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName hevc21" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 21pct).mkv" \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc17BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName hevc17" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 17pct).mkv" \
-vf yadif -r "$frameRate" -map 0:v -c:v libx265 -b:V "$avc35BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName avc35" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (AVC 35pct).mkv" \
-vf yadif -r "$frameRate" -map 0:v -c:v libx265 -b:V "$avc25BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName avc25" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (AVC 25pct).mkv"
done;
我收到以下错误:
TwoPassConvert_A-Z.sh: line 50: -vf: command not found
TwoPassConvert_A-Z.sh: line 58: -vf: command not found
分别引用脚本中的以下几行:
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc24BitRate" -x265-params pass=1 -passlogfile "$extensionFreeFileName hevc24" -an -f matroska /dev/null \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -b:V "$hevc24BitRate" -x265-params pass=2 -passlogfile "$extensionFreeFileName hevc24" -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 24pct).mkv" \
如您能提供任何帮助以纠正此错误,我们将不胜感激。
此外,当脚本运行时,我收到以下提示:
File '/dev/null' already exists. Overwrite ? [y/N]
有没有一种方法可以让我在运行脚本时始终回答“是”?
PS 任何关于如何更高效/易读(不丢失预期功能)编写脚本的 ffmpeg 部分的建议也都将受到欢迎。
谢谢!
聚苯硫醚
我可以成功运行以下类似的一次性“crf”脚本,没有任何问题:
IFS=$'\n'
for i in `ls *.{ts,mkv,mpeg,mpg,mov,avi,mp4,m2t,mts}`
do
extension=`echo "$i" | egrep -o '\.(ts|mkv|mpeg|mpg|mov|avi|mp4|mts)$'`
extensionFreeFileName=$(echo "$i" | awk -F `echo ${extension}` '{print $1}')
newFileName="$extensionFreeFileName"
testFrameFormat='/'
testFramsString=`mediainfo "$i" | grep "Frame rate "`
if [[ "$testFramsString" =~ "$testFrameFormat" ]]; then
frameRate=`mediainfo "$i" | grep "Frame rate " | awk -F '[()]' '{print $2}' | head -1`
else
frameRate=`mediainfo "$i" | grep "Frame rate " | awk -F "\: " '{print $2}' | awk -F " " '{print $1}' | awk -F " " '{print $1}' | head -1`
fi
echo -e "\n\n\n\n\n$frameRate\n\n\n\n\n\n"
~/bin/ffmpeg -analyzeduration 500000000 -probesize 500000000 -i "$i" -preset veryslow \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -crf 24 -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 24).mkv" \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -crf 26 -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 26).mkv" \
-vf bwdif=0:0:0 -r "$frameRate" -map 0:v -c:v libx265 -crf 30 -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (HEVC 30).mkv" \
-vf yadif -r "$frameRate" -map 0:v -c:v libx264 -crf 28 -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (AVC 28).mkv" \
-vf yadif -r "$frameRate" -map 0:v -c:v libx264 -crf 26 -map 0:a -c:a copy -map 0:s? -c:s copy "./$newFileName (AVC 26).mkv"
done;
更新日期:2018 年 11 月 3 日
除了我最初发布的问题之外,我原来的脚本中还存在其他问题。我已经纠正了其他错误,并发布了我的最终解决方案,以帮助将来在尝试构建自己的双通道脚本时可能遇到此问题的其他人。
注意:使用时风险自负。我不保证或担保此脚本将满足您计算环境中的特定需求。在将其调整到适合您的生产需求之前,请务必备份您正在编码的文件并在安全的环境中执行此操作,以确保它能正常工作
注意:如果将以下内容复制并粘贴到您喜欢的 bash 脚本编辑器中,会更容易阅读(我个人喜欢 gedit,如果使用 .sh 扩展名保存,它会提供漂亮的颜色编码)
IFS=$'\n'
#Script is designed to accept zero or one argument when executing.
#The argument is the path that contains the files you want to encode; syntax: user#bash script.sh [argument-path]
#The following commands ensure that if an argument is entered, it has a trailing '/'
#If no argument is present, it executes from the path your are currently in.
path=$1
pathLength=`echo $path | wc -c`
if [[ "$path" == "" ]]; then
path="./"
elif [[ "${path:$((pathLength - 2)):1}" == "/" ]]; then
path="$path"
else
path="$path/"
fi
pathLength=`echo $path | wc -c`
#for i in `ls *.{ts,mkv,mpeg,mpg,mov,avi,mp4,m2t,mts}` #alternative to loop through multiple file extensions
for i in `ls "$path"*.mts`
do
#move file to local machine (useful if original file) exists on network)
cp "$i" /tmp && echo "success"
#format $i variable to work correctly with commands below
i="${i:$((pathLength - 1))}"
#assign $j variable to work with the file cp to /tmp to avoid unwanted actions on original file
j="/tmp/$i"
#determines file extension, base file name (without extenion), and creates newFileName variable as base file name
extension=`echo "$i" | egrep -o '\.(ts|mkv|mpeg|mpg|mov|avi|mp4|mts)$'`
extensionFreeFileName=$(echo "$i" | awk -F `echo ${extension}` '{print $1}')
newFileName="$extensionFreeFileName"
#get frame rate of original video in order to match frame rate when encoding (designed to work with only one video stream))
#requires 'mediainfo' to be installed
testFrameFormat='/'
testFramsString=`mediainfo "$i" | grep "Frame rate "`
if [[ "$testFramsString" =~ "$testFrameFormat" ]]; then
frameRate=`mediainfo "$i" | grep "Frame rate " | awk -F '[()]' '{print $2}' | head -1`
else
frameRate=`mediainfo "$i" | grep "Frame rate " | awk -F "\: " '{print $2}' | awk -F " " '{print $1}' | awk -F " " '{print $1}' | head -1`
fi
#Visual confirmation that frame rate extracted from original video makes sense (either fractional 30000/1001 or decimal 29.970 format)
echo -e "\n\n\n\n\n$frameRate\n\n\n\n\n\n"
#Video bit rate lookup and calculations for desire output bitrate (5 percent in this instance)
videoBitRate=`mediainfo "$i" | grep "Bit rate " | head -1 | tail -1 | awk -F' : ' '{print $2}' | awk -F' kb/s' '{print $1}' | awk -F' Mb/s' '{print $1}' | sed 's| ||g'`
videoBitRate=`echo "$videoBitRate *1024 * 1024" | bc | awk -F'.' '{print $1}'`
#Note: 5 percent of original video bit rate won't work in most situations
#My video is highly static (virtually no movement) and does not require a high bit rate for quality output
#For encoding MPEG-2 original with moderate level of movement, I recomment a minimum of 50% for AVC and 25% for HEVC
#For enciding AVC original with moderate movement to HEVC, I recommend a minimum of 50%
#Ideally, you should experiment with a smaller extracted portion of video with highest motion using:
# user# ffmpeg -ss [start time in 00:00:00.000 format] -i source -t [duration in seconds] \
# [video codecs like those shown below]
#After determining what is acceptable to you, modify the target bitrates in the next two lines
hevc05BitRate=`echo "$videoBitRate * 5 / 100" | bc | awk -F'.' '{print $1}'`
avc05BitRate=`echo "$videoBitRate * 5 / 100" | bc | awk -F'.' '{print $1}'`
# ffmpeg command and options
~/bin/ffmpeg -y -analyzeduration 500000000 -probesize 500000000 -i "$j" -preset slow \
-map 0:v -c:v libx265 -b:v "$hevc05BitRate" -x265-params pass=1:stats=/tmp/passA -an -f matroska /dev/null \
-map 0:v -c:v libx264 -b:v "$avc05BitRate" -x264-params pass=1:stats=/tmp/passB -an -f matroska /dev/null && \
~/bin/ffmpeg -y -analyzeduration 500000000 -probesize 500000000 -i "$j" -preset slow \
-vf bwdif=0:0:0 -map 0:v -c:v libx265 -b:v "$hevc05BitRate" -x265-params pass=2:stats=/tmp/passA -map 0:a -c:a copy -map 0:s? -c:s copy "/tmp/$newFileName (HEVC 05pct).mkv" \
-vf yadif -map 0:v -c:v libx264 -b:v "$avc05BitRate" -x264-params pass=2:stats=/tmp/passB -map 0:a -c:a copy -map 0:s? -c:s copy "/tmp/$newFileName (AVC 05pct).mkv"
# When two-pass is complete, move the newly encoded files to their target destination
mv "/tmp/*AVC*" "/media/WinE/School Videos/"
mv "/tmp/*HEVC*" "/media/WinE/School Videos/"
# Remove temporary source file (NOTE: original file should still be untouched)
rm "$j"
done;
答案1
\
删除第 49 行和第 57 行之后的尾随空格。
反斜杠是为了转义后面的换行符(而不是多余的空格)。
要覆盖文件,请假设用“是”覆盖-y
(例如ffmpeg -y ...
)。