
У меня есть скрипт bash, который циклически проходит по результатам поиска и выполняет кодирование ffmpeg некоторых файлов FLV. Пока скрипт работает, вывод ffmpeg, похоже, прерывается и выводит какие-то странные ошибки, как показано ниже. Я понятия не имею, что здесь происходит. Может ли кто-нибудь указать мне правильное направление?
Как будто цикл все еще выполняется, хотя он не должен выполняться, и прерывает процесс ffmpeg.
Конкретная ошибка:
frame= 68 fps= 67 q=28.0 00000000000000000000000000001000size= 22kB time=00:00:00.50 bitrate= 363.2kbits/s dup=1 drop=0
Enter command: <target> <time> <command>[ <argument>]
Parse error, at least 3 arguments were expected, only 1 given in string 'om/pt_br/nx/R3T4N2_HD3D_demoCheckedOut.flv'
Еще немного подробностей из вывода ffmpeg:
[buffer @ 0xa30e1e0] w:800 h:600 pixfmt:yuv420p tb:1/1000000 sar:0/1 sws_param:flags=2
[libx264 @ 0xa333240] using cpu capabilities: MMX2 SSE2Fast SSSE3 FastShuffle SSE4.1 Cache64
[libx264 @ 0xa333240] profile High, level 3.1
[libx264 @ 0xa333240] 264 - core 122 r2184 5c85e0a - H.264/MPEG-4 AVC codec - Copyleft 2003-2012 - http://www.videolan.org/x264.html - options: cabac=1 ref=5 deblock=1:0:0 analyse=0x3:0x113 me=umh subme=8 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=2 b_bias=0 direct=3 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=50 rc=cbr mbtree=1 bitrate=500 ratetol=1.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 vbv_maxrate=500 vbv_bufsize=1000 nal_hrd=none ip_ratio=1.40 aq=1:1.00
Output #0, mp4, to './mp4s/pt_br/teamcenter/tc8_interactive/videos/8_SRM_EN.mp4':
Metadata:
audiodelay : 0
canSeekToEnd : true
encoder : Lavf54.3.100
Stream #0:0: Video: h264 (![0][0][0] / 0x0021), yuv420p, 800x600, q=-1--1, 500 kb/s, 30k tbn, 29.97 tbc
Stream #0:1: Audio: aac (@[0][0][0] / 0x0040), 44100 Hz, mono, s16, 128 kb/s
Stream mapping:
Stream #0:1 -> #0:0 (vp6f -> libx264)
Stream #0:0 -> #0:1 (mp3 -> libfaac)
Press [q] to stop, [?] for help
error parsing debug value0 00000000000000000000000000000000size= 13kB time=00:00:00.-3 bitrate=-3165.5kbits/s dup=1 drop=0
debug=0
frame= 68 fps= 67 q=28.0 00000000000000000000000000001000size= 22kB time=00:00:00.50 bitrate= 363.2kbits/s dup=1 drop=0
Enter command: <target> <time> <command>[ <argument>]
Parse error, at least 3 arguments were expected, only 1 given in string 'om/pt_br/nx/R3T4N2_HD3D_demoCheckedOut.flv'
Сценарий следующий
#!/bin/bash
LOGFILE=encodemp4ize.log
echo '' > $LOGFILE
STARTTIME=date
echo "Started at `$STARTTIME`" >> $LOGFILE
rsync -avz flvs/ mp4s/ --exclude '*.flv'
#find flvs/ -name "*.flv" > flv-files
# The loop
find flvs/ -name "*.flv" | while read f
do
FILENAME=`echo $f | sed 's#flvs/##'`
MP4FILENAME=`echo $FILENAME | sed 's#.flv#.mp4#'`
ffmpeg -i "$f" -vcodec libx264 -vprofile high -preset slow -b:v 500k -maxrate 500k -bufsize 1000k -threads 0 -acodec libfaac -ab 128k "./mp4s/$MP4FILENAME"
echo "$f MP4 done" >> $LOGFILE
done
решение1
Ваш вопрос на самом делеЧасто задаваемые вопросы по Bash #89: просто добавьте, </dev/null
чтобы запретить ffmpeg
чтение стандартного ввода.
Я взял на себя смелость исправить ваш сценарий, потому что он содержит много потенциальных ошибок. Несколько важных моментов:
С именами файлов сложно работать, поскольку большинство файловых систем позволяют им содержать всевозможные непечатаемые символы, которые обычные люди посчитали бы мусором. Упрощающие предположения, такие как «имена файлов содержат только «нормальные» символы», приводят к появлению хрупких скриптов оболочки, которыепоявлятьсяработать с "нормальными" именами файлов, а затем прерываться в тот день, когда они сталкиваются с особенно неприятным именем файла, которое не соответствует предположениям скрипта. С другой стороны, правильная обработка имен файлов может быть такой хлопотной, что вы можете посчитать это не стоящим усилий, если вероятность столкнуться со странным именем файла, как ожидается, близка к нулю (т. е. вы используете скрипт только для своих собственных файлов и даете своим собственным файлам "простые" имена). Иногда можно вообще избежать этого решения, не анализируя имена файлов вообще. К счастью, это возможно с
find(1)
опцией-exec
. Просто введите{}
аргумент to-exec
и вам не придется беспокоиться о анализеfind
вывода.Использование
sed
или других внешних процессов для выполнения простых строковых операций, таких как удаление расширений и префиксов, неэффективно. Вместо этого используйте расширения параметров, которые являются частью оболочки (отсутствие внешнего процесса означает, что это будет быстрее). Ниже перечислены некоторые полезные статьи по этой теме:- Часто задаваемые вопросы по Башу 73: Расширения параметров
- Часто задаваемые вопросы по Башу 100: Манипуляции со строками
Используйте
$( )
и``
больше не используйте: Часто задаваемые вопросы по Башу 82.Избегайте использования имен переменных в ВЕРХНЕМ РЕГИСТРЕ. Это пространство имен обычно резервируется оболочкой для специальных целей (например,
PATH
), поэтому использование его для собственных переменных — плохая идея.
А теперь, без лишних слов, вот вам очищенный скрипт:
#!/bin/sh
logfile=encodemp4ize.log
echo "Started at $(date)." > "$logfile"
rsync -avz --exclude '*.flv' flvs/ mp4s/
find flvs/ -type f -name '*.flv' -exec sh -c '
for flvsfile; do
file=${flvsfile#flvs/}
< /dev/null ffmpeg -i "$flvsfile" -vcodec libx264 -vprofile high \
-preset slow -b:v 500k -maxrate 500k -bufsize 1000k \
-threads 0 -acodec libfaac -ab 128k \
"mp4s/${file%flv}"mp4
printf %s\\n "$flvsfile MP4 done." >> "$logfile"
done
' _ {} +
Примечание: Я использовал POSIX, поскольку в вашем оригинале sh
не использовались и не требовались какие-либо специфические функции.bash
решение2
Я нашелрешение. Скрипт bash, по-видимому, создает ввод (а именно клавишу «c»), который мешает процессу ffmpeg
.
Добавляем < /dev/null
в ffmpeg
командную строку, например так:
ffmpeg -i "./$f" -vcodec libx264 -vprofile high -preset slow -b:v 500k -maxrate 500k -bufsize 1000k -threads 0 -acodec libfaac -ab 128k "./mp4s/$MP4FILENAME" < /dev/null
решает проблему.
решение3
В качестве альтернативного решения ffmpeg [...] < /dev/null
вы можете использовать:
ffmpeg -nostdin [...]
Подробности отдокументация ffmpeg:
Чтобы явно отключить взаимодействие, необходимо указать -nostdin.
Отключение взаимодействия на стандартном вводе полезно, например, если ffmpeg находится в группе фоновых процессов. Примерно того же результата можно добиться с помощью ffmpeg ... < /dev/null, но для этого требуется оболочка.
решение4
В while
цикле я столкнулся с похожими проблемами. Вы можете использовать for
цикл, а в for
цикле вы можете избежать взятия списка аудио и запуска его в цикле с помощью команды find
.
Я использовал find
и sed
для получения списка аудиофайлов в моем каталоге, потому что я видел ситуации, когда использование подстановочных знаков приводило к "argument list too long"
ошибке.
Теперь, если мы просто выполним поиск, то он выдаст абсолютный путь к файлам, что приведет к ffmpeg
сбою, поэтому мы удалили абсолютный путь с помощью sed
.
Вы можете использовать такую команду (использовал ее много раз, и она работает отлично)
for f1 in `find . -maxdepth 1 -name "*.mkv" | sed 's/^\.\///g'`; do ffmpeg -i "$f1" -q:a 0 -map a ../wav/"${f1%.*}.wav"; done