Erros estranhos ao usar ffmpeg em um loop

23

Eu tenho um script bash percorrendo os resultados de uma localização e executando uma codificação ffmpeg de alguns arquivos FLV. Enquanto o script está sendo executado, a saída do ffmpeg parece estar interrompida e está gerando alguns erros de aparência estranha, como o abaixo. Não tenho ideia do que está acontecendo aqui. Alguém pode me apontar na direção certa?

É como se o loop ainda estivesse em execução quando não deveria estar e interrompendo o processo ffmpeg.

O erro específico é:

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'

Mais alguns detalhes da saída 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'

O script é o seguinte

#!/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
Mark Williams
fonte
Eu não sou muito scripter, mas uma sugestão óbvia - faça com que seu script imprima as linhas que ele está executando. Eles podem não ser o que você pensa que são.
Faheem Mitha 12/04
Como uma questão de lado: mp4filename=$(basename "$f" mp4)pode ser útil (ver man basenamee man dirnamepara mais informações)
Peter.O
Diga bash -x myscriptpara obter um rastreamento linha por linha da execução do script, com todas as variáveis ​​expandidas. Ah, e aliás, você reinventou a basenameroda na FILENAME=linha. :)
Warren Young
1
Eu encontrei a solução. O script bash parece produzir entrada (ou seja, a tecla 'c') que interfere no processo ffmpeg. Piping "</ dev / null" no ffmpeg da seguinte forma: 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 Corrige o problema. via [ linuxquestions.org/questions/programming-9/… [1]: linuxquestions.org/questions/programming-9/…
Mark Williams

Respostas:

56

Sua pergunta é realmente Bash FAQ # 89 : basta adicionar </dev/nullpara impedir a ffmpegleitura de sua entrada padrão.


Tomei a liberdade de corrigir seu script para você, pois ele contém muitos erros em potencial. Alguns dos pontos importantes:

  • Os nomes de arquivos são difíceis de manipular, porque a maioria dos sistemas de arquivos permite que eles contenham todos os tipos de caracteres não imprimíveis que as pessoas comuns veriam como lixo. Fazer suposições simplificadoras como "nomes de arquivos contêm apenas caracteres 'normais'" tende a resultar em scripts de shell frágeis que aparecempara trabalhar em nomes de arquivos "normais" e depois quebrar o dia em que eles se deparam com um nome de arquivo particularmente desagradável que não segue as suposições do script. Por outro lado, o manuseio correto dos nomes dos arquivos pode ser tão incômodo que você pode achar que não vale a pena se a chance de encontrar um nome de arquivo estranho estiver próxima de zero (ou seja, você só usa o script em seus próprios arquivos e você atribui aos seus próprios arquivos nomes "simples"). Às vezes, é possível evitar completamente essa decisão não analisando os nomes dos arquivos. Felizmente, isso é possível com find(1)a -execopção. Basta colocar {}o argumento em -exece você não precisa se preocupar em analisar a findsaída.

  • Usar sedou outros processos externos para executar operações simples de cadeia, como remover extensões e prefixos, é ineficiente. Em vez disso, use expansões de parâmetros que fazem parte do shell (nenhum processo externo significa que será mais rápido). Alguns artigos úteis sobre o assunto estão listados abaixo:

  • Use $( )e não use ``mais: Bash FAQ 82 .

  • Evite usar nomes de variáveis ​​UPPERCASE. Esse espaço para nome é geralmente reservado pelo shell para fins especiais (como PATH), portanto, usá-lo para suas próprias variáveis ​​é uma má idéia.

E agora, sem mais delongas, aqui está um script limpo para você:

#!/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
' _ {} +

Nota: Eu usei o POSIX shporque você não usava ou não precisava de nenhum bashrecurso específico no original.

jw013
fonte
3
É uma resposta brilhante! Obrigado pelo esforço em escrever o script corrigido. Apenas imaginando, existe um similar ao guia de Greg para zsh? Obrigado!
Art
1
@ Art Desculpe, eu não sei muito sobre zsh. Talvez algumas pessoas do zsh no site saibam.
Jw013
O problema é que eu preciso verificar se o ffmpeg está produzindo um erro para posteriormente descartar o script e decidir se deve ou não apagar a versão anterior do arquivo convertido. Estou convertendo mkv para mp4 para um Plex Media Server. Eu gaguejo com grandes arquivos mkv, então decidi converter todos os mkv para mp4. Outro problema é que eu preciso verificar a falha na conversão do fluxo de legendas em busca de formatos baseados em imagem; nesse caso, uso outro processo para extrair os subs. Então, como executo o ffmpeg, obtenho sua saída e não encontro esse problema?
dacabdi
15

Eu encontrei a solução . O script bash parece produzir entrada (a chave 'c') que interfere no ffmpegprocesso.

Adicionando < /dev/nullà ffmpeglinha de comando, assim:

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

corrige o problema.

Mark Williams
fonte