Tomp4, convertir une vidéo en MP4

Menu principal

tomp4 est un script Shell fonctionnant sous Linux et qui permet de convertir n’importe quelle vidéo en une vidéo au format MP4.

Les vidéos ainsi générées sont utilisables par un lecteur Flash. Elles pourront être jouées sans nécessiter qu’elles soient complètement chargées.

Ce format est également compatible avec les équipements Apple.

Description

ffmpeg, x264 et faac sont utilisés afin de contourner des limitations imposées par les paquets fournis sur les différents Linux. Par exemple, ffmpeg peut très bien savoir décoder du H264/AVC mais ne pas savoir l’encoder ; x264 peut savoir encoder du H264/AVC mais ne pas savoir décoder autre chose que du YUV4MPEG car il n’aura pas été compilé avec le support lavf.

Pour simplifier, ffmpeg décode et multiplexe, x264/faac encodent.

Pré-requis

Ce script utilise les programmes suivants :

x264 0.106.1741
paquet x264 sous Ubuntu
faac 1.26.1
paquet faac sous Ubuntu
ffmpeg 0.6.6
paquet ffmpeg sous Ubuntu
ffprobe 0.6.6
paquet ffmpeg sous Ubuntu
qt-faststart 0.6.6
paquet ffmpeg sous Ubuntu
mplayer 1.0rc4-4.5.2
paquet mplayer sous Ubuntu

Les numéros de version de chaque programme indiquent sur quelles versions tomp4 a été testé. Il se peut que des versions récentes posent problème (notamment pour ffmpeg).

tomp4 utilise également pod2text pour l’affichage de l’aide en ligne.

Utilisation

shell
tomp4 [-a <bitrate>] [-b <bitrate>] [-d] [-o <destination>] <source>

tomp4 -h

Options disponibles :

-a <bitrate>
bitrate pour l’audio en kbps. Si le bitrate pour l’audio n’est pas spécifié, c’est le bitrate par défaut de faac qui est utilisé.
-b <bitrate>
bitrate pour la vidéo en kbps. Si le bitrate pour la vidéo n’est pas spécifié, c’est le bitrate par défaut de x264 qui est utilisé.
-d
active le désentrelacement de la source. Le désentrelacement utilise l’algorithme yadif avec les réglages par défaut.
-h
affiche l’aide en ligne
-o <destination>
fichier de destination. Si le fichier de destination est omis, tomp4reprend le nom du fichier source et remplace son extension par mp4.

Code source

Télécharger tomp4.xz Taille du fichier : 4,3 Ko

shell
#!/usr/bin/env bash

# ----------------------------------------------------------------------------
# Définition des fonctions
# ----------------------------------------------------------------------------

# Nettoyage des fichiers temporaires
# Retour: le code de retour de la commande rm utilisée
# Sortie: -
function nettoyage {
  rm "$tmpvid" "$tmpaud" "$tmpaudvid" \
     "$tmpfifovideo" "$tmpfifoaudio" \
     2> /dev/null
}

# Teste le résultat de la dernière commande et arrête le script s’il y a eu
# un problème. Les fichiers temporaires sont également nettoyés
# Retour: 0 (le code de retour de la commande echo)
# Sortie: "OK" ou "Erreur !!!"
function finsiprobleme {
  test $? -ne 0 && { echo "Erreur !!!"; nettoyage; exit 1; }
  echo "OK"
}

# Détermine les disponibilités des utilitaires requis par tomp4
# Retour: 0 si tous les utilitaires sont présents, 1 si au moins l’un d’entre
#         eux est absent.
# Sortie: messages de disponibilité
function prerequis {
  rc=0
  for utilitaire in x264 faac ffmpeg ffprobe qt-faststart mplayer
  do
    echo -n "  - Disponibilité de $utilitaire... "
    which $utilitaire > /dev/null && echo "OK" || { rc=1; echo "introuvable"; }
  done

  return $rc
}

# Détermine le PAR (Pixel Aspect Ratio) d’une vidéo
# Paramètre 1: fichier vidéo
# Retour: le code de retour de la commande FFprobe utilisée
# Sortie: le PAR sous la forme num:den
function par {
  # Pattern de recherche du PAR et du DAR dans la sortie de FFprobe. Une
  # sous-expression permet de cibler la valeur du PAR. La valeur donnée par
  # FFprobe est directement compatible avec x264 qui parle de SAR (Sample
  # Aspect Ratio)
  pattern='^.*\[PAR \([1-9][0-9]*:[1-9][0-9]*\) DAR [0-9]*:[0-9]*\].*$'

  # Récupère uniquement le premier PAR trouvé et l’envoie sur la sortie
  # standard
  ffprobe "$1" 2>&1 | sed -n "0,/$pattern/s/$pattern/\1/p"
  #echo "5:4"
}

# Récupère le numéro du premier flux audio. La piste audio sélectionnée par
# défaut par MPlayer n’est pas toujours la piste principale. MPlayer
# sélectionne la piste dont l’identifiant est le plus petit tandis que FFmpeg
# sélectionne la première piste qui se présente. Par exemple, si les pistes
# 0x81 et 0x80 se présentent dans cet ordre, MPlayer choisira 0x80 comme piste
# principale quand FFmpeg choisira 0x81. L’algorithme de FFmpeg semble être la
# norme. Cette fonction permet de déterminer la première piste selon FFmpeg et
# de retrouver son numéro correspondant sous MPlayer.
# Paramètre 1: fichier vidéo
# Retour: 0 (le code de la dernière commande echo)
# Sortie: le numéro du premier flux audio utilisable avec MPlayer
function audiostream {
  # Pattern de recherche des flux audio dans la sortie de FFprobe. Une
  # sous-expression permet de cibler son numéro hexadécimal.
  pattern='^.*Stream #[0-9]\.[0-9]\[\(0x[a-fA-F0-9]*\)\]: Audio:.*$'

  # Récupère le premier flux audio selon FFmpeg (sous la forme 0xFF)
  ffmpegstream=$(ffprobe "$1" 2>&1 | sed -n "0,/$pattern/s/$pattern/\1/p")

  # Récupère l’équivalent MPlayer du flux trouvé par FFprobe (ex 0x81). Pour
  # cela, cette fois, on récupère tous les flux audio (ex. 0x81\n0x80), on les
  # trie (0x80\n0x81), on les numérote (1 0x80\n2 0x81), on ne garde que la
  # ligne correspondant au premier flux trouvé par FFprobe (2 0x81) et on
  # récupère au final uniquement le numéro de ligne (2).
  mplayerstream=$( \
    ffprobe "$1" 2>&1 < /dev/null \
    | sed -n "s/$pattern/\1/p" \
    | sort \
    | nl --number-width=1 \
    | grep "$ffmpegstream\$" \
    | cut --field=1 \
  )

  # nl numérote les lignes à partir de 1, Mplayer travaille à partir de 0.
  # Envoie le numéro ainsi trouvé sur la sortie standard
  echo $((mplayerstream-1))
}

# Récupère le numéro du premier flux vidéo. La piste vidéo sélectionnée par
# défaut par MPlayer n’est pas toujours la piste principale. MPlayer
# sélectionne la piste dont l’identifiant est le plus petit tandis que FFmpeg
# sélectionne la première piste qui se présente. Par exemple, si les pistes
# 0x81 et 0x80 se présentent dans cet ordre, MPlayer choisira 0x80 comme piste
# principale quand FFmpeg choisira 0x81. L’algorithme de FFmpeg semble être la
# norme. Cette fonction permet de déterminer la première piste selon FFmpeg et
# de retrouver son numéro correspondant sous MPlayer.
# Paramètre 1: fichier vidéo
# Retour: 0 (le code de la dernière commande echo)
# Sortie: le numéro du premier flux vidéo utilisable avec MPlayer
function videostream {
  # Pattern de recherche des flux vidéo dans la sortie de FFprobe. Une
  # sous-expression permet de cibler son numéro hexadécimal.
  pattern='^.*Stream #[0-9]\.[0-9]\[\(0x[a-fA-F0-9]*\)\]: Video:.*$'

  # Récupère le premier flux vidéo selon FFmpeg (sous la forme 0xFFFF)
  ffmpegstream=$(ffprobe "$1" 2>&1 | sed -n "0,/$pattern/s/$pattern/\1/p")
  
  # Récupère l’équivalent MPlayer du flux trouvé par FFprobe (ex 0x81). Pour
  # cela, cette fois, on récupère tous les flux audio (ex. 0x81\n0x80), on les
  # trie (0x80\n0x81), on les numérote (1 0x80\n2 0x81), on ne garde que la
  # ligne correspondant au premier flux trouvé par FFprobe (2 0x81) et on
  # récupère au final uniquement le numéro de ligne (2).
  mplayerstream=$( \
    ffprobe "$1" 2>&1 < /dev/null \
    | sed -n "s/$pattern/\1/p" \
    | sort \
    | nl --number-width=1 \
    | grep "$ffmpegstream\$" \
    | cut --field=1 \
  )

  # nl numérote les lignes à partir de 1, Mplayer travaille à partir de 0
  # Envoie le numéro ainsi trouvé sur la sortie standard
  echo $((mplayerstream-1))
}

# Affiche l’aide de l’utilitaire
function afficheaide {
  # Utilise POD (Plain Old Documentation) de Perl pour la génération de l’aide
  # en ligne. La documentation se trouve à la fin de ce script
  pod2text --color $(which "$0")

  echo
  echo "Test des prérequis:"
  echo
  prerequis
  echo
}

# ----------------------------------------------------------------------------
# Valeurs par défaut
# ----------------------------------------------------------------------------
audbitrate=""
vidbitrate=""
tmpvid="/tmp/$$-tmpvid.mp4"
tmpaud="/tmp/$$-tmpaud.m4a"
tmpaudvid="/tmp/$$-tmpaudvid.mp4"
tmpfifovideo="/tmp/$$-tmpfifovideo.mp4"
tmpfifoaudio="/tmp/$$-tmpfifoaudio.wav"

# ----------------------------------------------------------------------------
# Analyse des arguments
# ----------------------------------------------------------------------------
while getopts ":ho:a:b:dr:" option
do
  case "$option" in
    h) # Aide du programme
      afficheaide; exit 0;;

    o) # Fichier de destination
      destination=$OPTARG;;

    a) # Bitrate pour l’audio
      audbitrate="-b $OPTARG";;

    b) # Bitrate pour la vidéo
      vidbitrate="--bitrate $OPTARG";;

    d) # Désentrelacement de la source
      desentrelace="-vf yadif";;

    \?) # Erreur option invalide
      afficheaide
      echo "Option invalide" >&2
      echo
      exit 1
      ;;

    :) # Erreur argument requis introuvable
      afficheaide
      echo "Argument requis absent" >&2
      echo
      exit 1
      ;;
  esac
done

# Retire tous les arguments traités par getopts
shift $((OPTIND-1))

# Validation des arguments (nombre d’arguments restants > 0)
echo -n "→ Validation des arguments... "
test $# -ne 0
finsiprobleme

# Définition des variables
source="$1"

# Si la destination n’a pas été spécifiée, on la génère à partir de la source
test -z "$destination" && destination="${source%.*}.mp4"

# ----------------------------------------------------------------------------
# Vérifie que tous les outils nécessaires sont disponibles avant de faire
# quoi que ce soit
# ----------------------------------------------------------------------------
echo "→ Test des prérequis:"
prerequis
test $? -ne 0 && exit 1

# ----------------------------------------------------------------------------
# Identifie la vidéo source
# ----------------------------------------------------------------------------

# Détermine le PAR (Pixel Aspect Ratio) de la vidéo
echo -n "→ Détermination du PAR (Pixel Aspect Ratio)... "
aspect=$(par "$source")
test -n $aspect
finsiprobleme

# Détermine la première piste vidéo
echo -n "→ Recherche de la piste vidéo... "
vstream=$(videostream "$source")
test -n $vstream
finsiprobleme

# Détermine la première piste audio
echo -n "→ Recherche de la piste audio... "
astream=$(audiostream "$source")
test -n $astream
finsiprobleme

# ----------------------------------------------------------------------------
# Encodage de la vidéo en MPEG-4
# ----------------------------------------------------------------------------

# Encode en MPEG-4 le flux vidéo de la source
echo -n "→ Encodage en MPEG-4 du flux vidéo de la source... "

# Crée une pile fifo car mplayer ne peut pas utiliser stdout
mkfifo "$tmpfifovideo"

# Lance le processus d’encodage vidéo en tâche de fond
x264 $vidbitrate \
     --preset slow \
     --sar $aspect \
     --crf 18 \
     --demuxer y4m \
     -o "$tmpvid" "$tmpfifovideo" \
     > /dev/null &

# Récupère l’identifiant du processus d’encodage vidéo
x264pid=$!

# L’option -noconsolecontrols est nécessaire car sinon mplayer va chercher à
# lire tout ce qui peut se présenter sur l’entrée standard, semant le trouble
# si vous utilisez tomp4 à l’intérieur d’une boucle lisant elle-même sur
# l’entrée standard. De plus, sans cette option, tout appui sur une touche
# pendant le traitement pourrait avoir des conséquences non souhaitées sur le
# résultat final.
mplayer -noconsolecontrols \
        -really-quiet \
        -benchmark \
        "$source" \
        -nosound \
        -ao null \
        -nosub \
        $desentrelace \
        -vo "yuv4mpeg:file=$tmpfifovideo" \
        > /dev/null

finsiprobleme

# Attend que le processus d’encodage vidéo se termine
wait $x264pid

# Encode en FAAC le flux audio de la source
echo -n "→ Encodage en FAAC du flux audio de la source... "

# Crée une pile fifo car mplayer ne peut pas utiliser stdout
mkfifo "$tmpfifoaudio"
faac $audbitrate -o "$tmpaud" "$tmpfifoaudio" > /dev/null 2>&1 &
faacpid=$!

mplayer -noconsolecontrols \
        -really-quiet \
        -benchmark \
        "$source" \
        -novideo \
        -ao "pcm:fast:file=$tmpfifoaudio" \
        -vo null \
        > /dev/null 2>&1
finsiprobleme

# Attend que le processus d’encodage audio se termine
wait $faacpid

# Multiplexe les flux audio et vidéo
echo -n "→ Multiplexage des flux audio et vidéo... "
ffmpeg -i "$tmpvid" \
       -vcodec copy \
       -i "$tmpaud" \
       -acodec copy \
       "$tmpaudvid" \
       2> /dev/null
finsiprobleme

# Ajoute les informations de streaming et produit la vidéo finale
echo -n "→ Ajout des informations pour le streaming... "
qt-faststart "$tmpaudvid" "$destination" > /dev/null 2>&1
finsiprobleme

# ----------------------------------------------------------------------------
# Conversion effectuée, nettoyage
# ----------------------------------------------------------------------------

# Nettoie les fichiers intermédiaires
echo -n "→ Nettoyage des fichiers intermédiaires... "
nettoyage
finsiprobleme

# Fin
echo "La vidéo a été convertie !"

exit 0

# ----------------------------------------------------------------------------
# Documentation de tomp4 au format POD
# ----------------------------------------------------------------------------
: <<=cut

=pod

=encoding utf8

=head1 NAME

tomp4 - Convertit une vidéo au format MP4

=head1 SYNOPSIS

tomp4 [B<-a> I<bitrate>] [B<-b> I<bitrate>] [B<-d>] [B<-o> I<destination>]
I<source>

tomp4 B<-h>

=head1 DESCRIPTION

B<tomp4> permet de convertir n'importe quelle vidéo en une
vidéo au format MP4 utilisable par un lecteur Flash. Une telle vidéo
pourra être jouée sans nécessiter qu'elle soit complètement chargée.
Ce format est également compatible avec les équipements Apple.

ffmpeg, x264 et faac sont utilisés afin de contourner des limitations
imposées par les paquets fournis sur les différents Linux. Par
exemple, ffmpeg peut très bien savoir décoder du H264/AVC mais ne pas
savoir l'encoder ; x264 peut savoir encoder du H264/AVC mais ne pas
savoir décoder autre chose que du YUV4MPEG car il n'aura pas été
compilé avec le support lavf.

Pour simplifier, ffmpeg décode et multiplexe, x264/faac encodent.

Ce script utilise les programmes suivants:

=over 1

=item - x264 (paquet x264 sous Ubuntu)

=item - faac (paquet faac sous Ubuntu)

=item - ffmpeg (paquet ffmpeg sous Ubuntu)

=item - ffprobe (paquet ffmpeg sous Ubuntu)

=item - qt-faststart (paquet ffmpeg sous Ubuntu)

=item - mplayer (paquet mplayer sous Ubuntu)

=back

=head1 OPTIONS

=over

=item B<-a> I<bitrate>

  Bitrate pour l'audio en kbps. Si le bitrate pour l'audio n'est pas spécifié,
  c'est le bitrate par défaut de faac qui est utilisé.
  
=item B<-b> I<bitrate>

  Bitrate pour la vidéo en kbps. Si le bitrate pour la vidéo n'est pas
  spécifié, c'est le bitrate par défaut de x264 qui est utilisé.

=item B<-d>

  Active le désentrelacement de la source. Le désentrelacement utilise
  l'algorithme yadif avec les réglages par défaut.

=item B<-h>

  Affiche cette aide.

=item B<-o> I<destination>

  Fichier de destination. Si le fichier de destination est omis, tomp4 reprend
  le nom du fichier source et remplace son extension par mp4.

=back

=head1 AUTEUR

Frédéric Bisson <zigazou@free.fr>.

=head1 LICENSE

Copyright 2012, 2013 Frédéric Bisson <zigazou@free.fr>.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see L<http://www.gnu.org/licenses/>

=cut

Licence

Copyright 2012, 2013 Frédéric BISSON zigazou@free.fr

This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/