Я все еще новичок в ffmpeg. Я могу сделать основы с 1-проходной кодировкой crf, но, рискуя использовать 2-проходную кодировку, я сталкиваюсь с ошибкой, которую не могу устранить. Любая помощь приветствуется.
Короче говоря, когда я запускаю следующий скрипт в 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 более эффективно / читабельно (без потери предполагаемой функциональности).
Спасибо!
PPS
Я могу без проблем запустить следующий, похожий, однопроходный скрипт '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;
ОБНОВЛЕНИЕ 3/3/2018
В моем оригинальном сценарии были проблемы, помимо исходной, которую я опубликовал. С тех пор я исправил другие ошибки и выкладываю свое окончательное решение, чтобы помочь другим, которые могут столкнуться с этим в будущем при попытке создать свой собственный двухпроходный скрипт.
ВНИМАНИЕ: Используйте на свой страх и риск. Я не даю никаких гарантий, что этот скрипт будет работать для ваших конкретных потребностей в вашей вычислительной среде. Обязательно сделайте резервную копию файлов, которые вы кодируете, и выполните это в безопасной среде, чтобы убедиться, что это работает правильно для вас, прежде чем адаптировать его к своим производственным потребностям.
примечание: следующее будет легче читать, если его скопировать и вставить в предпочитаемый вами редактор 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;