web-dev-qa-db-fra.com

Rejoignez les fichiers mp4 sous linux

Je veux joindre deux fichiers MP4 pour en créer un seul. Les flux vidéo sont encodés en h264 et l'audio en aac. Je ne peux pas ré-encoder les vidéos dans un autre format pour des raisons de calcul. De plus, je ne peux utiliser aucun programme graphique, tous les traitements doivent être effectués avec des utilitaires de ligne de commande Linux. FFmpeg ne peut pas faire cela pour les fichiers mpeg4 alors j'ai plutôt utilisé MP4Box:

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

Malheureusement, l'audio est tout mélangé. Je pensais que le problème était que l'audio était en aac alors je l'ai transcodé en mp3 et utilisé à nouveau MP4Box. Dans ce cas, le son est correct pour la première moitié de newvideo.mp4 (correspondant à video1.mp4), mais il n’ya pas d’audio et je ne peux pas naviguer dans la vidéo également.

Ma pensée suivante a été que les flux audio et vidéo présentaient quelques différences minimes que je devrais corriger. Ainsi, pour chaque vidéo d'entrée, j'ai scindé les flux vidéo et audio, puis les ai associés à l'option -shortest dans FFmpeg.

Ainsi pour la première vidéo que j'ai courue:

 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

De même pour la deuxième vidéo et ensuite utilisé MP4Box comme précédemment. Malheureusement, cela n'a pas fonctionné non plus. Mon seul succès a été lorsque j'ai rejoint les flux vidéo séparément (vidéostream1.mp4 et videostream2.mp4) et les flux audio (audiostream1.m4a et audiostream2.m4a), puis jeté la vidéo et l'audio dans un fichier final. Cependant, la synchronisation est perdue pour la seconde moitié de la vidéo. Concrètement, il y a un délai d'une seconde pour l'audio et la vidéo. Toutes les suggestions sont vraiment les bienvenues.

30
Jose Armando

Le meilleur moyen de le faire est actuellement avec le démultiplexeur concat . Commencez par créer un fichier appelé inputs.txt formaté comme suit:

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

Ensuite, exécutez simplement cette commande ffmpeg:

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

Voir également la concaténation dans ffmpeg FAQ .


Je garde ce qui suit ici pour le bénéfice de toute personne utilisant les anciennes versions de ffmpeg.

Les dernières versions de ffmpeg peuvent le faire: vous devrez d’abord remuxter les fichiers dans les flux de transport mpeg (assez léger, car il ne s'agit que de changer le format du conteneur):

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

Si cela génère une erreur à propos de h264, vous devrez peut-être utiliser:

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

Vous devrez le faire séparément avec chaque fichier d'entrée. Pour concaténer les fichiers ensemble, utilisez:

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

Si cela génère une erreur à propos de aac, vous devrez peut-être utiliser

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

Si votre système prend en charge les canaux nommés, vous pouvez le faire sans créer de fichiers intermédiaires.

mkfifo temp0 temp1

Vous devrez effectuer les opérations suivantes dans trois terminaux virtuels distincts:

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

Si output.mp4 existe déjà, le troisième ffmpeg vous demandera si vous voulez l’écraser, mais il le fera après avoir accédé aux FIFO , ce qui fera le premier à ffmpegs fermer. Veillez donc à choisir un nom non utilisé pour votre fichier de sortie.

Cela risque de ne pas fonctionner si vos fichiers d’entrée sont différents. Je pense que les différences de débit binaire sont acceptables, mais que la taille, le débit, etc. doivent correspondre.

27
evilsoup

Pour mp4, la seule solution de travail que j'ai trouvée était avec MP4Box du paquet 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 la commande est

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

avec mencoder et avconv, je ne pouvais pas le faire fonctionner :-(

7
Philippe Gachoud

Voici un one-liner qui facilite grandement les choses. Il fait tout ce qui était nécessaire pour vous fournir un fichier vidéo. À votre santé!

find *.mp4 | sed 's:\ :\\\ :g'| sed 's/^/file /' > list.txt; ffmpeg -f concat -i list.txt -c copy output.mp4; rm list.txts
2
Anonymous

Sur un Mac (macOS 10.13 High Sierra), j’ai utilisé les éléments suivants avec succès:

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

Réorganisez comme ci-dessus de la même manière si nécessaire (notez que le fichier ./ a été supprimé - le chemin entraînait l'erreur ffmpeg de générer une erreur de nom de fichier non sécurisé).

Ensuite, j'ai dû supprimer le codec et le débit spécifiés pour le ffmpeg par défaut de MacPorts:

ffmpeg -f concat -i mylist.txt  output.m4a
2
Robert Cole

Merci pour le script dvo. C'était un excellent point de départ pour moi. Je n'ai eu aucun problème à utiliser des pipes nommées, j'ai donc adapté le script pour les utiliser à la place. De plus, je l'ai corrigé pour qu'il fonctionne avec les noms de fichiers contenant des espaces. La sortie vers les canaux nommés ne "va" pas jusqu'à ce que vous commenciez à les lire, d'où la nécessité d'arrière-plan des tâches de remux initiales avec un & avant le dernier appel à avconv. De plus, n'oubliez pas d'utiliser -c copy, sinon vous finirez par ré-encoder le tout.

#!/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
2
Mark

Merci evilsoup; Voici un petit script pour automatiser le processus, en utilisant la plus récente avconv.
BTW, les pipes nommées ne fonctionnaient pas pour moi (la sortie leur était bloquée).

   #!/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
1
dvo

Ont connu un grand succès avec

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

vous devrez peut-être réorganiser légèrement mylist.txt

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

ffmpeg version 2.2.3

0
Benjamin