web-dev-qa-db-fra.com

Comment concaténer deux fichiers MP4 avec FFmpeg?

J'essaye de concaténer deux fichiers MP4 en utilisant ffmpeg. J'ai besoin que ce soit un processus automatique, c'est pourquoi j'ai choisi ffmpeg. Je convertis les deux fichiers en fichiers .ts, puis je les concatène et j'essaie ensuite de coder ce fichier .ts concaténé. Les fichiers sont encodés en h264 et aac et j'espère pouvoir conserver la même qualité ou être aussi proche que possible de l'original.

ffmpeg -i part1.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy part1.ts
ffmpeg -i part2.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy part2.ts
cat part1.ts part2.ts > parts.ts
ffmpeg -y -i parts.ts -acodec copy -ar 44100 -ab 96k -coder ac -vbsf h264_mp4toannexb parts.mp4

Malheureusement, je reçois le message d'erreur suivant renvoyé par ffmpeg lors de l'encodage:

[h264 @ 0x1012600]sps_id out of range
[h264 @ 0x1012600]non-existing SPS 0 referenced in buffering period
[h264 @ 0x1012600]sps_id out of range
[h264 @ 0x1012600]non-existing SPS 0 referenced in buffering period
[NULL @ 0x101d600]error, non monotone timestamps 13779431 >= 13779431kbits/s    
av_interleaved_write_frame(): Error while opening file

Cela se produit à peu près à mi-chemin de l'encodage, ce qui me fait penser que vous ne pouvez pas concaténer deux fichiers .ts ensemble et le faire fonctionner.

233
Mark L

J'ai fini par utiliser le format intermédiaire mpg et cela a fonctionné (NOTE: ceci est un exemple dangereux, -qscale 0 réencodera la vidéo ...)

ffmpeg -i 1.mp4 -qscale 0 1.mpg
ffmpeg -i 2.mp4 -qscale 0 2.mpg
cat 1.mpg 2.mpg | ffmpeg -f mpeg -i - -qscale 0 -vcodec mpeg4 output.mp4
30
Mark L

FFmpeg a trois méthodes de concaténation.

1. filtre vidéo concat

ffmpeg -i opening.mkv -i episode.mkv -i ending.mkv \
  -filter_complex "[0:v] [0:a] [1:v] [1:a] [2:v] [2:a] concat=n=3:v=1:a=1 [v] [a]" \
  -map "[v]" -map "[a]" output.mkv

Notez que cette méthode effectue un ré-encodage.

2. concat demuxer

$ cat mylist.txt
file '/path/to/file1'
file '/path/to/file2'
file '/path/to/file3'

$ ffmpeg -f concat -i mylist.txt -c copy output

Pour les fenêtres:

(echo file 'first file.mp4' & echo file 'second file.mp4' )>list.txt
ffmpeg -safe 0 -f concat -i list.txt -c copy output.mp4

3. protocole concat

ffmpeg -i "concat:input1|input2" -codec copy output

Cette méthode ne fonctionne pas pour de nombreux formats, y compris MP4, en raison de la nature de ces formats et de la concaténation simpliste effectuée par cette méthode.

Lequel utiliser

  • concat filter: utilisez-le si vos entrées n'ont pas les mêmes paramètres (largeur, hauteur, etc.), ou ne sont pas les mêmes formats/codecs, ou si vous souhaitez effectuer un filtrage. (Vous pouvez uniquement ré-encoder les entrées qui ne correspondent pas afin de partager le même codec et d'autres paramètres, puis utilisez le démultiplexeur concat pour éviter de ré-encoder les autres entrées).

  • concat demuxer: à utiliser lorsque vous souhaitez éviter un réencodage et que votre format ne prend pas en charge la concaténation au niveau fichier (la plupart des fichiers utilisés par les utilisateurs généraux ne prennent pas en charge la concaténation au niveau fichier).

  • concat protocol: à utiliser avec les formats qui prennent en charge la concaténation de fichiers .__ (MPEG-1, MPEG-2 PS, DV). Ne pas utiliser avec MP4.

En cas de doute, essayez le démultiplicateur concat.

Regarde aussi

478
rogerdpack

Voici un moyen rapide (prend moins de 1 minute) et sans perte de le faire sans avoir besoin de fichiers intermédiaires:

ls Movie_Part_1.mp4 Movie_Part_2.mp4 | Perl -ne 'print "file $_"' | ffmpeg -f concat -i - -c copy Movie_Joined.mp4

Le "ls" contient les fichiers à joindre Le "Perl" crée le fichier de concaténation à la volée dans un tube La partie "-i -" indique à ffmpeg de lire le tube

(Remarque: mes fichiers ne contenaient ni espaces ni éléments étranges. Vous aurez besoin de la possibilité d’échapper à Shell si vous souhaitez utiliser cette idée avec des fichiers "durs").

45
cnd

POUR LES FICHIERS MP4

Pour les fichiers .mp4 (que j’ai obtenus de DailyMotion.com: un épisode télévisé de 50 minutes téléchargeable en trois parties, sous forme de trois fichiers vidéo .mp4), voici une solution efficace pour Windows 7, et est-ce que NOT implique le réencodage des fichiers.

J'ai renommé les fichiers (file1.mp4, file2.mp4, file3.mp4) de sorte que les parties soient dans le bon ordre pour visionner l'épisode complet de tv.

Ensuite, j'ai créé un fichier batch simple (concat.bat), avec le contenu suivant:

:: Create File List
echo file file1.mp4 >  mylist.txt 
echo file file2.mp4 >> mylist.txt
echo file file3.mp4 >> mylist.txt

:: Concatenate Files
ffmpeg -f concat -i mylist.txt -c copy output.mp4

Le fichier de commandes et ffmpeg.exe doivent tous deux être placés dans le même dossier que les fichiers .mp4 à joindre. Ensuite, exécutez le fichier de commandes. Cela prend généralement moins de dix secondes pour s'exécuter.

Addendum (2018/10/21) -

Si ce que vous recherchiez est une méthode pour spécifier tous les fichiers mp4 du dossier actuel sans beaucoup de retaper, essayez ceci dans votre fichier de commandes Windows batch (DOIT inclure l'option -safe 0):

:: Create File List
for %%i in (*.mp4) do echo file '%%i'>> mylist.txt

:: Concatenate Files
ffmpeg -f concat -safe 0 -i mylist.txt -c copy output.mp4

Cela fonctionne sur Windows 7, dans un fichier de commandes. Ne pas essayez de l'utiliser sur la ligne de commande, car cela ne fonctionne que dans un fichier batch!

40
Ed999

pour les fichiers MP4:

S'ils ne sont pas exactement les mêmes (fichiers codés à 100%, même résolution, même type), vous devez d'abord les transcoder en flux intermédiaires:

ffmpeg -i myfile1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp1.ts
ffmpeg -i myfile2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts temp2.ts
// now join
ffmpeg -i "concat:temp1.ts|temp2.ts" -c copy -bsf:a aac_adtstoasc output.mp4

REMARQUE! : Le résultat sera comme le premier fichier (et non le second)

33
T.Todua

J'essayais de concaténer trois fichiers audio .mp3 dans un fichier .m4a et cette commande ffmpeg fonctionne. 

Commande d'entrée:

ffmpeg -i input1.mp3 -i input2.mp3 -i input3.mp3 -filter_complex concat=n=3:v=0:a=1 -f MOV -vn -y input.m4a

Signification de: "-filter_complex concat = n = 3: v = 0: a = 1"

concat signifie que la fonction de jonction (Media) est utilisée.
n signifie que le nombre total de fichiers en entrée est confirmé.
v signifie a la vidéo? utilisez 0 = pas de vidéo, 1 = contient de la vidéo.
a signifie a l'audio? utilisez 0 = pas d'audio, 1 = contient de l'audio.
-f signifie que le format de fichier est forcé (pour voir tous les formats supportés, utilisez ffmpeg -formats)
-vn signifie que la vidéo est désactivée (et que -an désactive le son si non voulu)
-y signifie écraser les fichiers de sortie (si le fichier de sortie existe déjà).

Pour plus d'informations: utilisez ffmpeg -h full Affichez toutes les options (y compris toutes les options spécifiques au format et au codec, très long)

30
Ellie Tam

sur la base des réponses de rogerdpack et Ed999, j'ai créé ma version .sh

#!/bin/bash

[ -e list.txt ] && rm list.txt
for f in *.mp4
do
   echo "file $f" >> list.txt
done

ffmpeg -f concat -i list.txt -c copy joined-out.mp4 && rm list.txt

il joint tous les fichiers *.mp4 du dossier en cours à joined-out.mp4

testé sur mac.

la taille du fichier résultant est la somme exacte de mes 60 fichiers testés. Ne devrait pas être une perte. Juste ce dont j'avais besoin

3
Alexandr Perfilov

Voici un script que j'ai créé pour concaténer plusieurs MP4 GoPro en un MP4 720p. J'espère que c'est utile.

#!/bin/sh
cmd="( "
for i; do
    cmd="${cmd}ffmpeg -i $i -ab 256000 -vb 10000000 -mbd rd -trellis 2 -cmp 2 -subcmp 2 -g 100 -f mpeg -; "
done
cmd="${cmd} ) | ffmpeg -i - -vb 10000000 -ab 256000 -s 1280x720 -y out-`date +%F-%H%M.%S`.mp4"
echo "${cmd}"
eval ${cmd}
3
Reid Ellis

De la documentation ici: https://trac.ffmpeg.org/wiki/Concatenate

Si vous avez des fichiers MP4, vous pouvez les concaténer sans perte en les transcodant d’abord en flux de transport MPEG-2. Avec la vidéo H.264 et le son AAC, vous pouvez utiliser les éléments suivants:

ffmpeg -i input1.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate1.ts
ffmpeg -i input2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts intermediate2.ts
ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy -bsf:a aac_adtstoasc output.mp4

Cette approche fonctionne sur toutes les plateformes.

J'avais besoin de la capacité d'encapsuler cela dans un script multi-plateforme, alors j'ai utilisé fluent-ffmpeg et j'ai mis au point la solution suivante:

const unlink = path =>
  new Promise((resolve, reject) =>
    fs.unlink(path, err => (err ? reject(err) : resolve()))
  )

const createIntermediate = file =>
  new Promise((resolve, reject) => {
    const out = `${Math.random()
      .toString(13)
      .slice(2)}.ts`

    ffmpeg(file)
      .outputOptions('-c', 'copy', '-bsf:v', 'h264_mp4toannexb', '-f', 'mpegts')
      .output(out)
      .on('end', () => resolve(out))
      .on('error', reject)
      .run()
  })

const concat = async (files, output) => {
  const names = await Promise.all(files.map(createIntermediate))
  const namesString = names.join('|')

  await new Promise((resolve, reject) =>
    ffmpeg(`concat:${namesString}`)
      .outputOptions('-c', 'copy', '-bsf:a', 'aac_adtstoasc')
      .output(output)
      .on('end', resolve)
      .on('error', reject)
      .run()
  )

  names.map(unlink)
}

concat(['file1.mp4', 'file2.mp4', 'file3.mp4'], 'output.mp4').then(() =>
  console.log('done!')
)
3
itslittlejohn

Fusion de tous les fichiers mp4 du répertoire en cours

Personnellement, j'aime bien ne pas créer de fichier externe que je dois supprimer par la suite; ma solution a donc été la suivante: la liste de numérotation des fichiers (comme file_1_name, file_2_name, file_10_name, file_20_name, file_100_name, ...)

#!/bin/bash
filesList=""
for file in $(ls -1v *.mp4);do #lists even numbered file
    filesList="${filesList}${file}|"
done
filesList=${filesList%?} # removes trailing pipe
ffmpeg -i "concat:$filesList" -c copy $(date +%Y%m%d_%H%M%S)_merged.mp4
1
Pipo

Cela a fonctionné pour moi (sur Windows)

ffmpeg -i "concat:input1|input2" -codec copy output

en particulier

ffmpeg -i "concat:01.mp4|02.mp4" -codec copy output.mp4

Python

Utiliser du code python pour le faire avec autant de MP4 qu'il y a dans un dossier Import glob Import os

stringa = ""
for f in glob.glob("*.mp4"):
    stringa += f + "|"

os.system("ffmpeg -i \"concat:" + stringa + "\" -codec copy output.mp4")
1
Giovanni Gianni
ffmpeg \
  -i input_1.mp4 \
  -i input_2.mp4 \
  -filter_complex '[0:v]pad=iw*2:ih[int];[int][1:v]overlay=W/2:0[vid]' \
  -map [vid] \
  -c:v libx264 \
  -crf 23 \
  -preset veryfast \
  output.mp4
1
Deepak Kumar

Le protocole de concat décrit ici; https://trac.ffmpeg.org/wiki/Concatenate#protocol

Lorsque implémenté en utilisant des canaux nommés pour éviter les fichiers intermédiaires

Est très rapide (lu: instantané), n’a pas de cadres perdus et fonctionne bien.

N'oubliez pas de supprimer les fichiers de canal nommés et de vérifier si la vidéo est H264 et AAC, ce que vous pouvez faire uniquement avec ffmpeg -i filename.mp4 (recherchez les mentions h264 et aac).

0
Roel Van de Paar

Voici ma méthode pour rejoindre un répertoire plein de fichiers MP4 en utilisant la substitution de commande et le filtre vidéo concat (ceci ré-encodera) - imaginé que quelqu'un d'autre puisse utiliser quelque chose de ce one-liner, surtout si vous avez beaucoup de fichiers rejoint 17 dossiers en un seul coup):

ffmpeg $(for f in *.mp4 ; do echo -n "-i $f "; done) -filter_complex \
"$(i=0 ; for f in *.mp4 ; do echo -n "[$i:v] [$i:a] " ; i=$((i+1)) ; done \
&& echo "concat=n=$i:v=1:a=1 [v] [a]")" -map "[v]" -map "[a]" output.mp4

N.B. cette commande joint vos fichiers dans l’ordre dans lequel ils sont nommés (c’est-à-dire le même ordre dans lequel ils sont présentés si vous exécutez ls *.mp4) - dans mon cas, ils avaient chacun un numéro de piste, ce qui a bien fonctionné.

0
Peter

Pour les fichiers .mp4, j’ai constaté que l’outil de ligne de commande opensource fonctionnait mieux et plus rapidement: mp4box. Ensuite, vous pouvez l'utiliser de cette façon:

mp4box.exe -add video1.mp4 -cat video2.mp4 destvideo.mp4

Téléchargez-le ici pour la plupart des plateformes: https://gpac.wp.imt.fr/mp4box/

0
Mario

Après divers essais, le script ci-dessous a fonctionné pour moi sous Windows 10 PowerShell.

    $files=Get-ChildItem -path e:\ -Filter *.mp4


    $files| ForEach-Object  {"file '$($_.FullName)'"}| Out-File -FilePath e:\temp.txt -Encoding ASCII


    if (-not (test-path "e:\ffmpeg\bin\ffmpeg.exe")) {throw "e:\ffmpeg\bin\ffmpeg.exe needed"}

    E:\ffmpeg\bin\ffmpeg.exe -safe 0 -f concat -i "e:\temp.txt" -c copy -bsf:v hevc_mp4toannexb -an e:\joined.mp4

    # Conversion Cleanup
    Remove-Item e:\temp.txt

Ici les deux premières lignes créent un fichier texte temp.txt qui a le contenu suivant

file 'e:\first.mp4'
file 'e:\second.mp4'

3ème, 4ème lignes vérifie si ffmpeg est disponible sur chemin et crée le "join.mp4"

Les principales différences par rapport aux autres réponses sont les suivantes

usage  of -bsf:v hevc_mp4toannexb -an

pour mon fichier mp4 ci-dessus travaillé, vous devrez peut-être utiliser d'autres alternatives comme ci-dessous en fonction de votre encodage vidéo.

h264_mp4toannexb

Tous ces filtres possibles sont disponibles à l’adresse https://ffmpeg.org/ffmpeg-bitstream-filters.html

0
rahul maindargi