Outils pour utilisateurs


ffmpeg

ffmpeg permet de modifier des flux audio / vidéo / sous-titre d'un fichier vidéo mp4 mkv avi.

J'ai 2 fichiers vidéo, fichier0.mp4 et fichier1.avi.
Le fichier0 est de bonne qualité vidéo, et la bande son en anglais.
Le fichier1 est de moyenne qualité vidéo, et la bande son est en français.
Je désire une vidéo de bonne qualité avec le son en français avec un second son en anglais. J'ai aussi les sous-titres en français et en anglais que je veux mettre dans le fichier vidéo.
Je dois tout d'abord regarder comment sont constitués les fichiers vidéo grâce à la commande ffprobe.

>ffprobe fichier0.mp4
...
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709/bt709/unknown), 1280x536 [SAR 1:1 DAR 160:67], 905 kb/s, 23.98 fps, 23.98 tbr, 24k tbn, 47.95 tbc (default)
Metadata:
  creation_time   : 2014-09-17T11:05:33.000000Z
  handler_name    : video.264#trackID=1:fps=23.976 - Imported with GPAC 0.5.0-rev4065
Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 93 kb/s (default)
Metadata:
  creation_time   : 2014-09-17T11:05:42.000000Z
  handler_name    : GPAC ISO Audio Handler

>ffprobe fichier1.avi
...
Stream #0:0: Video: mpeg4 (Simple Profile) (XVID / 0x44495658), yuv420p, 720x300 [SAR 1:1 DAR 12:5], 720 kb/s, 23.98 fps, 23.98 tbr, 23.98 tbn, 23.98 tbc
Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 48000 Hz, stereo, s16p, 128 kb/s

les flux sont souvent 0 pour la vidéo, 1 pour l'audio principale, 2 pour l'audio secondaire…, on trouve ensuite les sous-titres.
ffmpeg prend en entrée des fichiers, et ensuite on lui dit quoi faire des flux.

ffmpeg -i fichier0.mp4 -i fichier1.avi -i sous-titres.fr.srt -i sous-titres.en.srt \
 -map 0:v \
 -map 1:1 -metadata:s:a:0 language=fre -disposition:a:0 default \
 -map 0:1 -metadata:s:a:1 language=eng -disposition:a:1 none \
 -map 2 -metadata:s:s:0 language=fre \
 -map 3 -metadata:s:s:1 language=eng \
 -c copy video.mkv

Explication de la commande ci-dessus :
Sur la première ligne, on met tous les fichiers en entrée
ensuite :
- sur le flux 0 (le premier fichier), on prend la vidéo seulement
- sur le flux 1:1 (le second fichier), on ne prend que l'audio (piste son french), on lui dit que la médadonnée stream audio 0 sera de langage french et que l'audio 0 sera la piste audio par défaut
- sur le flux 0:1 (la piste son en anglais), on lui dit que c'est une piste en anglais, et qu'il n'y a aucune priorité. J'ai été obligé de lui dire car cette piste est par défaut dans le flux d'origine.
- sur le flux 2 (sous-titre français), on dit dans les métadonnée du flux sous-titre 0 que le langage est french
- sur le flux 3 (sous-titre anglais), on dit dans les métadonnée du flux sous-titre 1 que le langage est english
- on dit à ffmpeg qu'on ne fera que des copies, donc il n'y aura aucun encodage à faire et on lui donne le fichier de sortie.
De l'ordre des fichiers d'entrée va dépendre les commandes map et donc tout ça modifie l'ordre des flux dans le fichier de sortie. Ici, j'ai fait un exemple ordonné, mais vous pouvez ordonner les fichiers dans l'ordre qui vous plaît, du moment que vous modifiez les numéros de flux dans les commandes map. Par contre, l'ordre des map va donner l'ordre des flux dans le fichier de sortie.
Dans cet exemple, chaque flux traité est sur une ligne pour faciliter la lecture. La commande que je viens d'expliquer donne un fichier video.mkv avec le détail suivant :

>ffprobe video.mkv
...
Input #0, matroska,webm, from 'video.mkv':
  Metadata:
    COMPATIBLE_BRANDS: isomavc1
    MAJOR_BRAND     : isom
    MINOR_VERSION   : 1
    ENCODER         : Lavf57.83.100
  Duration: 01:53:35.88, start: 0.000000, bitrate: 1133 kb/s
    Stream #0:0: Video: h264 (High), yuv420p(tv, bt709/bt709/unknown, progressive), 1280x536 [SAR 1:1 DAR 160:67], 23.98 fps, 23.98 tbr, 1k tbn, 47.95 tbc (default)
    Metadata:
      HANDLER_NAME    : video.264#trackID=1:fps=23.976 - Imported with GPAC 0.5.0-rev4065
      DURATION        : 01:53:29.481000000
    Stream #0:1(fre): Audio: mp3, 48000 Hz, stereo, s16p, 128 kb/s (default)
    Metadata:
      ENCODER         : Lavf
      DURATION        : 01:53:28.488000000
    Stream #0:2(eng): Audio: aac (LC), 48000 Hz, stereo, fltp
    Metadata:
      HANDLER_NAME    : GPAC ISO Audio Handler
      DURATION        : 01:53:29.547000000
    Stream #0:3(fre): Subtitle: subrip
    Metadata:
      DURATION        : 01:53:27.229000000
    Stream #0:4(eng): Subtitle: subrip
    Metadata:
      DURATION        : 01:53:35.880000000

On voit très bien dans le résultat de la commande ffprobe, on a plusieurs flux :
- Stream #0:0: Video: h264
- Stream #0:1(fre): Audio: mp3
- Stream #0:2(eng): Audio: aac
- Stream #0:3(fre): Subtitle: subrip
- Stream #0:4(eng): Subtitle: subrip

Ces flux sont dans l'ordre des map de la commande qui a servi à créer ce fichier.


Autre exemple

J'ai un file0.avi et un file1.srt
le premier fichier se présente ainsi (ffprobe)

Input #0, avi, from 'file0.avi':
  Metadata:
    IAS1            : English
  Duration: 00:30:02.04, start: 0.000000, bitrate: 1824 kb/s
    Stream #0:0: Video: mpeg4 (Advanced Simple Profile) (XVID / 0x44495658), yuv420p, 576x432 [SAR 1:1 DAR 4:3], 1391 kb/s, 25 fps, 25 tbr, 25 tbn, 25 tbc
    Stream #0:1: Audio: mp3 (U[0][0][0] / 0x0055), 48000 Hz, stereo, s16p, 137 kb/s
    Stream #0:2: Audio: mp3 (U[0][0][0] / 0x0055), 48000 Hz, stereo, s16p, 134 kb/s
    Stream #0:3: Audio: mp3 (U[0][0][0] / 0x0055), 48000 Hz, stereo, s16p, 132 kb/s

Je désire récupérer le flux vidéo 0 et la première piste son 1 et inclure le sous-titre français file1.srt

ffmpeg -fflags +genpts -i file0.avi -i file1.srt \
-map 0:v \
-map 0:1 -metadata:s:a:0 language=eng -disposition:a:0 default \
-map 1 -metadata:s:s:0 language=fre \
-c copy video.mkv

En premier lieu, on désigne les fichiers d'entrée
On prend la piste vidéo
On prend la première piste audio (le flux 1 du fichier 0), piste audio par défaut en english
On prend le sous-titre, qui sera french
On ne fera que des copies sans conversion dans le fichier video.mkv Si l'erreur «Can't write packet with unknown timestamp» s'affiche à l'exécution, on peut rajouter l'option -fflags +genpts qui va recréer les timestamps. Cette erreur peut arriver quand on fait un changement de format vers une destination mkv.

>ffprobe video.mkv
...
Input #0, matroska,webm, from 'video.mkv':
...
Stream #0:0: Video: mpeg4 (Advanced Simple Profile), yuv420p, 576x432 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc
...
Stream #0:1(eng): Audio: mp3, 48000 Hz, stereo, s16p, 112 kb/s (default)
...
Stream #0:2(fre): Subtitle: subrip

On voit bien ici les 3 flux dans le fichier video.mkv qu'on a généré précédemment.

astuce

pour les options map, on peut désigner une piste avec son numéro de flux ou avec son type :
-map 0:0 est le flux 0 du fichier0 et est identique à map 0:v qui est la vidéo du fichier0
-map 0:1 est le flux 1 du fichier0 donc souvent le son et est identique à map 0:a:0 qui est le premier flux audio du fichier0. S'il n'y a qu'une seule piste audio, nmap 0:a doit fonctionner

astuce ffprobe

ffprobe a l'air de sortir vers stderr. Si on veut filtrer les résultats avec grep ou sed, ou autre, on doit rediriger le stderr vers stdout grâce à 2>&1
Exemple avec le fichier vidéo précédent où on veut filtrer les flux

>ffprobre video.mkv 2>&1 | grep '#0'
Input #0, matroska,webm, from 'video.mkv':
    Stream #0:0: Video: mpeg4 (Advanced Simple Profile), yuv420p, 576x432 [SAR 1:1 DAR 4:3], 25 fps, 25 tbr, 1k tbn, 25 tbc
    Stream #0:1(eng): Audio: mp3, 48000 Hz, stereo, s16p, 112 kb/s (default)
    Stream #0:2(fre): Subtitle: subrip

Là, on n'est pas débordé par les messages à l'écran.