Unir arquivos mp4 no linux

26

Eu quero juntar dois arquivos mp4 para criar um único. Os fluxos de vídeo são codificados em h264 e o áudio em aac. Eu não posso re-codificar os vídeos para outro formato devido a razões computacionais. Além disso, não posso usar nenhum programa GUI, todo o processamento deve ser executado com os utilitários de linha de comando do Linux. O FFmpeg não pode fazer isso para arquivos mpeg4, então eu usei o MP4Box:

MP4Box -add video1.mp4 -cat video2.mp4 newvideo.mp4

Infelizmente, o áudio fica todo misturado. Eu pensei que o problema era que o áudio estava em aac então eu o transcodifiquei em mp3 e usei novamente o MP4Box. Nesse caso, o áudio é bom para a primeira metade de newvideo.mp4 (correspondendo a video1.mp4 ), mas depois não há áudio e não consigo navegar no vídeo também.

Meu próximo pensamento foi que os fluxos de áudio e vídeo tiveram algumas pequenas discrepâncias em seus comprimentos que eu deveria consertar. Então, para cada vídeo de entrada, dividai os streams de vídeo e áudio e juntei-os à opção -shortest no FFmpeg.

Assim, para o primeiro vídeo eu corri:

 avconv -y -i video1.mp4 -c copy -map 0:0 videostream1.mp4
 avconv -y -i video1.mp4 -c copy -map 0:1 audiostream1.m4a
 avconv -y -i videostream1.mp4 -i audiostream1.m4a  -c copy -shortest  video1_aligned.mp4

Da mesma forma para o segundo vídeo e, em seguida, usado MP4Box como anteriormente. Infelizmente isso também não funcionou. O único sucesso que tive foi quando entrei nos streams de vídeo separadamente (ou seja, videostream1.mp4 e videostream2.mp4) e nos streams de áudio (ou seja, audiostream1.m4a e audiostream2.m4a) e depois juntei o vídeo e o áudio em um arquivo final. No entanto, a sincronização é perdida para a segunda metade do vídeo. Concretamente, há um atraso de 1 segundo de áudio e vídeo. Qualquer sugestão é muito bem vinda.

    
por Jose Armando 18.12.2012 / 14:12

6 respostas

24

A melhor maneira de fazer isso atualmente é com o demuxer de concat . Primeiro, crie um arquivo chamado inputs.txt formatado da seguinte forma:

file '/path/to/input1.mp4'
file '/path/to/input2.mp4'
file '/path/to/input3.mp4'

Então, simplesmente execute este comando ffmpeg:

ffmpeg -f concat -i inputs.txt -c copy output.mp4

Veja também concatenação em ffmpeg FAQ .

Estou mantendo o seguinte aqui para o benefício de qualquer pessoa que use versões mais antigas do ffmpeg.

As versões mais recentes do ffmpeg podem fazer isso: você terá que remontar os arquivos em fluxos de transporte mpeg primeiro (razoavelmente leve ao processador, já que ele está apenas alterando o formato do contêiner):

ffmpeg -i input.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb middle.ts

Se isso gerar um erro sobre o h264, talvez seja necessário usar:

ffmpeg -i input.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb middle.ts

Você terá que fazer isso separadamente com cada arquivo de entrada. Para concatenar os arquivos juntos, use:

ffmpeg -i "concat:middle1.ts|middle2.ts|middle3.ts" -c copy output.mp4

Se isso gerar um erro sobre o aac, talvez seja necessário usar

ffmpeg -i "concat:middle1.ts|middle2.ts|middle3.ts" -c copy -absf aac_adtstoasc output.mp4

Se o seu sistema suporta pipes nomeados, você pode fazer isso sem criar arquivos intermediários.

mkfifo temp0 temp1

Você terá que fazer o seguinte em três terminais virtuais separados:

ffmpeg -i input0.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb -y temp0
ffmpeg -i input1.mp4 -map 0 -c copy -f mpegts -bsf h264_mp4toannexb -y temp1
ffmpeg -f mpegts -i "concat:temp0|temp1" -c copy -absf aac_adtstoasc output.mp4

Se o output.mp4 já existir, o terceiro ffmpeg perguntará se você deseja sobrescrevê-lo, mas ele fará isso após ter acessado os FIFOs , e isso fará com que o primeiro ffmpegs perto. Portanto, certifique-se de escolher um nome não usado para o seu arquivo de saída.

Isso pode não funcionar se seus arquivos de entrada forem diferentes - acredito que as diferenças na taxa de bits sejam aceitáveis, mas o tamanho do quadro, a taxa de quadros, etc., precisam corresponder.

    
por 20.12.2012 / 05:26
7

Para o mp4, a única solução de trabalho que encontrei foi com o MP4Box do pacote gpac

#!/bin/bash
filesList=""
for file in $(ls *.mp4|sort -n);do
    filesList="$filesList -cat $file"
done
MP4Box $filesList -new merged_files_$(date +%Y%m%d_%H%M%S).mp4

ou o comando é

MP4Box -cat file1.mp4 -cat file2.mp4 -new mergedFile.mp4

com o mencoder e o avconv Eu não poderia fazer isso funcionar: - (

    
por 22.03.2014 / 22:18
2

Obrigado pelo script dvo. Foi um ótimo ponto de partida para mim. Eu não tive nenhum problema com o uso de pipes nomeados, então adaptei o script para usá-los. Além disso, eu consertei para trabalhar com nomes de arquivos com espaços neles. A saída para pipes nomeados não "vai" até você começar a ler a partir deles, daí a necessidade de fazer o background das tarefas iniciais de remux com um & antes da chamada final para avconv. Além disso, não se esqueça de usar -c copy, ou você acabará re-codificando todo o lote.

#!/bin/bash
for FILE in "$@"; do
   TAG=$(echo "$FILE" | sed -e s/[^A-Za-z0-9.]/_/g)
   mkfifo $TAG.mp4concatpipe
   avconv -loglevel "quiet" -i "$FILE"  -f mpegts -c copy -bsf h264_mp4toannexb -y $TAG.mp4concatpipe &
   IN=$IN\|$TAG.fifo
done

IN=${IN#\|}
avconv -i concat:$IN -c copy out.mp4
rm *.mp4concatpipe
    
por 16.10.2013 / 14:00
2

Em um Mac (macOS 10.13 High Sierra) usei o seguinte com sucesso:

for f in *.m4a; do echo "file '$f'" >> mylist.txt; done

Reordene como acima da mesma forma, se necessário (observe que o ./ foi removido - o caminho estava fazendo com que o ffmpeg emitisse o erro de nome de arquivo não seguro).

Depois tive que remover o codec e a taxa de bits especificados para o MacPorts ffmpeg padrão para que ele fosse:

ffmpeg -f concat -i mylist.txt  output.m4a
    
por 14.11.2017 / 01:42
1

Obrigado evilsoup; aqui está um pequeno script para automatizar o processo, usando o mais recente avconv .
BTW, pipes nomeados não funcionou para mim (a saída para eles ficou presa).

   #!/bin/bash

    for FILE in $@; do
      avconv -i $FILE -map 0 -f mpegts -bsf h264_mp4toannexb -c:v copy -y $FILE.tmp
      IN=$IN\|$FILE.tmp
    done

    IN=${IN#\|}
    avconv -i concat:"$IN" out.mp4

    for FILE in $@; do
      rm $FILE.tmp
    done
    
por 26.03.2013 / 12:11
0

Tive muito sucesso com

for f in ./*.m4a; do echo "file '$f'" >> mylist.txt; done

você pode precisar reordenar mylist.txt ligeiramente

ffmpeg -f concat -i mylist.txt -c:a libfdk_aac -b:a 128k output.m4a

ffmpeg versão 2.2.3

    
por 02.09.2014 / 19:45