Converter vídeos: Não é elegante, mas funciona!

Eu tenho um problema que, acho, já falei por aqui… Há alguns meses eu comprei uma Smart TV de 40″, Sony Bravia, mas ela tem um problema: Suporta alguns poucos codecs! Assim, para assistir algum vídeo baixado, como o Jovem Nerd diz, da Locadora do Paulo Coelho, tenho que convertê-lo para o formato MPEG-4, usando os codecs h264 e ac3. O script que eu vinha usando até agora era esse (muito simples):

#!/bin/bash
ffmpeg -y -i "$1" -c:v libx264 -c:a ac3 -sameq "$(echo $1 | sed 's/\.[^.]*$/.mp4/')"

Mas ele têm alguns problemas óbvios:

  1. Se o script não receber parâmetros, ele falha;
  2. Se o primeiro parâmetro for o nome de um arquivo com extensão .mp4, ele falha;
  3. Se o arquivo já estiver codificado em h264 e ac3, ele reconverte, gastando tempo precioso e diminuindo a qualidade final

Eis uma possível solução:

#!/bin/bash
if [ "$1x" == "x" ]; then
  echo "vid2mp4 \<inputfile\>"
  exit
fi

params=""
ffmpeg -i "$1" 2>&1 | grep "Video:" | grep "h264" > /dev/null
if [ $? -eq 0 ]; then
  params="$params -c:v copy"
else
  params="$params -c:v libx264"
fi

ffmpeg -i "$1" 2>&1 | grep "Audio:" | grep "ac3" > /dev/null
if [ $? -eq 0 ]; then
  params="$params -c:a copy"
else
  params="$params -c:a ac3"
fi

tmpfn="$(echo $1 | sed 's/\.[^.]*$/.$$$.mp4/')"
outfn="$(echo $1 | sed 's/\.[^.]*$/.mp4/')"

ffmpeg -y -i "$1" $params -sameq -ac 2 "$tmpfn" && rm "$1" && mv "$tmpfn" "$outfn"

Tá… eu acho que poderia ficar melhor… Está feio, mas é eficiente!

OBS: Notem que eu converto o áudio para stereo porque não tenho, por aqui, um som Dolby ou com múltiplos canais. Eu também poderia forçar a barra e colocar um bitrate para audio fixo em 128 kbps (que é mais que suficiente para um som stereo!), mas a versão 0.10.4-6 do ffmpeg já faz isso pra mim e, deixo esse bitrate de fora na esperança que o bitrate original seja copiado…

Anúncios

16 comentários sobre “Converter vídeos: Não é elegante, mas funciona!

  1. Ia deixar pra lá… Mas aqui vão meus 2c.

    Primeiro, não gostei da mistura de estilos… Portanto IMHO ficaria melhor isto:

    #!/bin/bash
    if [ “$1x” == “x” ]; then
    echo “vid2mp4 \”
    exit
    fi

    params=””
    ffmpeg -i “$1” 2>&1 | grep “Video:” | grep “h264″ > /dev/null
    if [ $? -eq 0 ]; then
    params=”$params -c:v copy”
    else
    params=”$params -c:v libx264″
    fi

    ffmpeg -i “$1” 2>&1 | grep “Audio:” | grep “ac3″ > /dev/null
    if [ $? -eq 0 ]; then
    params=”$params -c:a copy”
    else
    params=”$params -c:a ac3″
    fi

    tmpfn=”$(echo $1 | sed ‘s/\.[^.]*$/.$$$.mp4/’)”
    outfn=”$(echo $1 | sed ‘s/\.[^.]*$/.mp4/’)”

    ffmpeg -y -i “$1” $params -sameq -ac 2 “$tmpfn” && rm “$1” && mv “$tmpfn” “$outfn”

    Sacou … que retirei os ifs e coloquei no mesmo estilo do último comando, mas mesmo deste jeito, ainda está muito chinfrin para um “soul programmer”.

    Eu “compilaria” isto assim:

    .SUFFIXES: .vid .mp4
    OPTIONS := -c:v libx264 -c:a ac3
    TMPDIR := /tmp

    .vid.mp4:
    echo ffmpeg -y -i $< ${OPTIONS} -sameq -ac 2 ${TMPDIR}/$@
    echo touch ${TMPDIR}/$@
    echo rm $<
    echo mv ${TMPDIR}/$@ $@

    $make foo.vid

    Corte e cole isto num makefile, mas para e rode (como está aí) "com água", depois retire os echos que deve funcionar.

    Aliás sempre uso o make para estas atividades (meus backups sõa assim !)

    {}'s
    MaRZ

    PS:não gostei desta historia de criar um arquivo no tmp depos trazer pra cá… Cumbersome! Eu faria no mesmo diretório, ou seja, colocaria o .vid no /tmp direto eRobert Mecklenburg deixaria por lá, afinal apagar o /tmp é função do sistema no reboot, por isto criei uma partição enorme e separada. Tá no tmp, tá morto !

    Leituras recomendadas: info make e Magaging Projects with Make, O’Reilly, Robert Mecklenburg

    1. Quanto às modificações no script, não consegui ver as diferenças…

      Quanto ao makefile, note que o ffmpeg seleciona o container do arquivo final a partir de sua extensão. Renomear o arquivo para extensão .vid causa um erro pq o ffmpeg não saberá qual é o container. Ainda, o script só muda o codec se este não for o mesmo no arquivo original. Assim, se o cídeo já estiver codificado em h264 (e por isso o ‘if’) ele só recodificará o áudio… Se ambos os codecs forem h264 e ac3, para o vídeo e o áudio, respectivamente, então só o container mudará (não verifico o container).

      O makefile não faz a mesma coisa que o script…

      1. Srry… cortei e colei errado !#!/bin/bash

        [ “$1x” == “x” ] && echo `basename $0` ‘ ‘ && exit

        params=”$params -c:v libx264″
        ffmpeg -i “$1” 2>&1 | grep “Video:” | grep “h264″ > /dev/null
        [ $? -eq 0 ] && params=”$params -c:v copy”

        params=”$params -c:a ac3″
        ffmpeg -i “$1” 2’>’&1 | grep “Audio:” | grep “ac3″ ‘>’ /dev/null
        [ $? -eq 0 ] && params=”$params -c:a copy”

        tmpfn=”$(echo $1 | sed ‘s/\.[^.]*$/.$$$.mp4/’)”
        outfn=”$(echo $1 | sed ‘s/\.[^.]*$/.mp4/’)”

        ffmpeg -y -i “$1” $params -sameq -ac 2 “$tmpfn” && rm “$1” && mv “$tmpfn” “$outfn”

        Quanto ao make…. Eu quis mostrar que usar o make para a atividade fica mais clean… Não analisei estas consisderações sobre o que vc fez. O ponto crucial, e que entendi no seu script, é que vc está usando muito codigo para mexer no nome do arquivo. de qq jeito, vc consegue fazer isto com o makefile mais fácil do que com o bash, usando as substituições e functions prontas.

        Pelo jeito vc nem testou – basta tirar os echos que eu acho que funcionaria sim. Eu não tenho um .vid aqui para testar, mas suspeito, pois não entendo muito de conversão de audio/video, que funciona pois o echo do comando ffmpeg eu cortei e colei do seu script e só parametrizei – sem os horríveis ifs que vc colocou.

    2. Rodando aqui, só que no lugar do ffmpeg, usei um echo e usei um touch para simular a criação do .mp4 que seria feito pelo ffmpeg.

      marz@pistache:~$ make foo.mp4
      echo ffmpeg -y -i foo.vid -c:v libx264 -c:a ac3 -sameq -ac 2 /tmp/foo.mp4
      ffmpeg -y -i foo.vid -c:v libx264 -c:a ac3 -sameq -ac 2 /tmp/foo.mp4
      touch /tmp/foo.mp4
      rm foo.vid
      mv /tmp/foo.mp4 foo.mp4

      1. E os horríveis ifs estão lá justamente para testar se os codecs já estão ou não corretos… o makefile ficará bem mais complexo que o script para testar isso…

  2. Algumas considerações adicionais sobre o uso do ‘make’:

    – O manual da FSF sobre o ‘GNU make’ diz, claramente, que o uso da regra .X.Y: provavelmente vai ser depreciada;
    – Você usou uma extensão hipotética de vídeo (.vid) que não será reconhecida pelo ffmpeg;
    – O script aceita qualquer container de video (.avi, .mp4, .divx, .asf, .rmvb, etc) – o seu make só aceita o .vid (hipotético);
    – Para verificar os codecs, terá que copiar os condicionais do script dentro do Makefile, colocando um ‘\’ no final para que as linhas executadas o sejam no mesmo shell – dai, pra que makefile?

    [[]]ão
    Fred

    1. Não fui muito “eficiente” pois queria montar somente uma prova conceitual que o uso do make pode (e IMHO) deve ser usado pois agrega muito valor (e elegância) às soluções. Sem falar que (brincando com ele ainda mais) percebi que é um filho legítimo do LISP !!!! (hehehe). Por isto, e somente por isto ;), resolvi futucar no seu bash… Se vai terminar no futuro esta ou aquela característica, não sei, mas a regra .X.Y é muito boa para quando trabalhamos com extensões não configuradas em .SUFFIXES e, funciona bem. Mas dá para fazer com outros recursos, regex, afinal quando se anda pelo “lisp” se pode tudo ! (:-))))

      Mas vamos lá, perdi mais um tempinho, até por que sempre quis usar as funções (tipos lisp) do make, mas como eu não sabia lisp, ficava evitando. Permita-me mais uma incursão no seu bash… Não se sinta “espancado(*)” por isto, e veja que eu apenas testei “com água” aqui e pediria que desta vez, vc testasse aí e, se possível corrigisse alguma falha “catilográfica” que porventura encontre. Note que eu tentei ficar fiel ao seu “espancamento”(*) digo, bash, mas dá para usar outros recursos que poderia explorar melhor se fosse modificar. (use com make foo.mp4)

      tmppfn = $(patsubst %.mp4,%.$$$$.mp4,$@)
      outfn = $(subst %.vid,.mp4,$@)

      params = $(shell \
      ffmpeg -i $@ 2>&1 | grep $(1) | grep $(2) | /dev/null; \
      [ $$? -eq 0 ] && echo ” -c:v copy” || echo ” -c:v libx264″ )

      .SUFFIXES: .vid .mp4

      .vid.mp4:
      $(call params, “Video:”, “h264″, ” -c:v copy”, ” -c:v libx264″ )
      $(call params, “Audio:”, “ac3″, ” -c:a copy”, ” -c:a ac3″ )
      @ffmpeg -y -i $@ $(params) -sameq -ac 2 $(tmpfn) && rm $@ && mv $(tmpfn) $(outfn)

      (*) N.T.: to bash [bashed|bashed] {v.} espancar {v.}

  3. Faltou substituir os params 3 e 4 em shell … Srrry

    tmpfn = $(patsubst %.mp4,%.$$$$.mp4,$@)
    outfn = $(subst %.vid,.mp4,$@)

    params = $(shell \
    ffmpeg -i $@ 2>&1 | grep $(1) | grep $(2) | /dev/null; \
    [ $$? -eq 0 ] && echo $(3) || echo $(4) )

    .SUFFIXES: .vid .mp4

    .vid.mp4:
    $(call params, “Video:”, “h264″, ” -c:v copy”, ” -c:v libx264″ )
    $(call params, “Audio:”, “ac3″, ” -c:a copy”, ” -c:a ac3″ )
    @ffmpeg -y -i $@ $(params) -sameq -ac 2 $(tmpfn) && rm $@ && mv $(tmpfn) $(outfn)

    1. Srry again…. o param deve ser acumulativo.

      tmpfn = $(patsubst %.mp4,%.$$$$.mp4,$@)
      outfn = $(subst %.vid,.mp4,$@)

      params = $(shell \
      ffmpeg -i $@ 2>&1 | grep $(1) | grep $(2) | /dev/null; \
      [ $$? -eq 0 ] && echo $(3) || echo $(4) )

      .SUFFIXES: .vid .mp4

      .vid.mp4:
      @ffmpeg -y -i $@ \
      $(call params, “Video:”, “h264″, ” -c:v copy”, ” -c:v libx264″ ) \
      $(call params, “Audio:”, “ac3″, ” -c:a copy”, ” -c:a ac3″ ) \
      -sameq -ac 2 $(tmpfn) && rm $@ && mv $(tmpfn) $(outfn)

      1. Trabalhar com makefiles é tão intuitivo, né?

        Só precisou de algumas tentativas para fazer o treco funcionar com uma única extensão de vídeo hipotética!

      2. Com certeza cê entendeu o ponto, e este eu demonstrei no primeiro reply. Os demais foram exercícios com o make, que para mim são muito úteis na prática. Mas, ao velho estilo Frediano, melhor polemizar, certo?! Com certeza o seu “lindo” grupo de ifs com repetecos e uso de sed e uso dois estilos de if x && ficou um primor e vc fez de prima sem testes e erros! O que é o meu make merdinha, com comandos a beira da obsolecência (: OO) diante do esplendor do seu lindo shell? Nada além de imitação.

        Mas fico feliz pq funcionou, nunca antes eu havia usado uma função no make, (parece muito com uma cons). I <3 Lisp and C.

      3. Apropos: apesar da sua franca ironia, considero o uso do makefile intuitivo sim. Muito mais do que o case, por exemplo, ou os quotes bizarros do bash. Mas gosto muito do bash, afinal é com o motor dele que executamos os comandos de dentro do make (após os targets). Quanto a questão de usar apenas uma terminação hipotética (sic) de arquivo , sei lá o que vc quis dizer com isto. Vc usou isto no nome do seu arquivo… dai eu pensei que .vid era de vid 2 mp4. Pesquisei depois e vi que existe esta coisa sim: http://www.filesuffix.com/extension/vid.html. Boiei nesta de “hipotética”!

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s