Transformez les enregistrements vocaux en vidéos à partager avec Python et FFmpeg

October 25, 2021
Rédigé par
Carlos Mucuho
Contributeur
Les opinions exprimées par les contributeurs de Twilio sont les leurs

Transformez les enregistrements vocaux en vidéos à partager avec Python et FFmpeg

Dans ce tutoriel, nous allons apprendre à créer une application avec Python et FFmpeg - qui nous permettra de transformer les enregistrements vocaux en vidéos intéressantes que nous pourrons facilement partager sur les réseaux sociaux.

À la fin du tutoriel, nous aurons transformé un enregistrement vocal en une vidéo comme celle-ci :

Vidéo - Transformation d"un enregistrement vocal en une vidéo

Prérequis pour ce tutoriel

Pour suivre ce tutoriel, vous aurez besoin des composants suivants :

  • Un ou plusieurs enregistrements vocaux que vous souhaitez convertir en vidéos. Les enregistrements Programmable Voice conservés sur votre compte Twilio sont parfaits pour ce tutoriel.
  • Python 3.6+ installé.
  • Version 4.3.1 FFmpeg  ou ultérieure installée.

Créer la structure du projet

Dans cette section, nous allons créer notre répertoire de projet, puis des sous-répertoires où conserver les enregistrements, les images, les polices et les vidéos utilisés dans ce tutoriel. Enfin, nous allons créer le fichier Python qui contiendra le code nous permettant d'utiliser FFmpeg pour créer et éditer une vidéo.

Ouvrez votre terminal et entrez les commandes suivantes pour créer le dossier du projet et vous rendre à l’intérieur :

mkdir twilio-turn-recording-to-video
cd twilio-turn-recording-to-video

Utilisez les commandes suivantes pour créer quatre sous-répertoires :

mkdir images
mkdir fonts
mkdir videos
mkdir recordings

Le répertoire images est l'endroit où nous conserverons les images d'arrière-plan de nos vidéos. Téléchargez cette image et enregistrez-la dans le répertoire images sous le nom bg.png. Cette image a initialement été téléchargée depuis Freepik.com.

Dans le répertoire fonts, nous conserverons les fichiers de police utilisés pour écrire du texte dans nos vidéos. Téléchargez cette police et enregistrez-la dans le répertoire fonts sous le nom LeagueGothic-CondensedRegular.otf. Cette police a initialement été téléchargée depuis fontsquirrel.com.

Le répertoire videos contient des vidéos et des animations qui seront ajoutées par-dessus l'image d'arrière-plan. Téléchargez cette vidéo d'un spinning record avec le logo Twilio au centre et enregistrez-la dans le répertoire videos sous le nom spinningRecord.mp4. L'image source utilisée dans cette vidéo a été téléchargée depuis flaticon.com.

Le répertoire recordings est l'endroit où nous conservons les enregistrements vocaux qui seront transformés en vidéos. Ajoutez un ou plusieurs enregistrements vocaux dans ce répertoire.

Maintenant que nous avons créé tous les répertoires nécessaires, ouvrez votre éditeur de code préféré et créez un fichier nommé main.py au premier niveau du répertoire du projet. Ce fichier contiendra le code responsable de la transformation de nos enregistrements en vidéos.

Si vous ne souhaitez pas suivre toutes les étapes du tutoriel, vous pouvez obtenir le code source complet du projet ici.

Transformer un fichier audio en vidéo

Dans cette section, nous allons ajouter le code qui nous permettra de transformer un enregistrement en une vidéo qui montre les ondes sonores de l'enregistrement.

Nous allons utiliser FFmpeg pour générer une vidéo à partir d'un fichier audio. Ainsi, pour appeler FFmpeg et les programmes associés de Python, nous allons utiliser le module de Python subprocess.

Exécution d'une commande

Ajoutez le code suivant dans le fichier main.py :

import subprocess


def run_command(command):
    p = subprocess.run(
        command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )
    print('Done!!!')
    print('stdout:\n{}'.format(p.stdout.decode()))

    return p.stdout.decode().strip()

Dans le bloc de code ci-dessus, nous avons importé le module subprocess et créé une fonction run_command(). Comme son nom l'indique, cette fonction est responsable de l'exécution d'une commande transmise dans l'argument. Une fois la commande terminée, nous imprimons la sortie et la retournons également à l'appelant.

Obtenir la durée d'un enregistrement

Ajoutez le code suivant sous la fonction run_command() :

def get_rec_duration(rec_name):
    rec_path = "./recordings/{}".format(rec_name)

    command = "ffprobe -i {rec_path} -show_entries format=duration -v quiet \
    -of csv=\"p=0\"".format(rec_path=rec_path)

    rec_duration = run_command(command)
    print("rec duration", rec_duration)

    return rec_duration

Ici, nous avons créé une fonction appelée get_rec_duration(). Cette fonction permet de récupérer la durée d'un enregistrement. La fonction reçoit un nom d'enregistrement (rec_name) comme argument, précédé du nom du répertoire des enregistrements et conservé dans la variable locale rec_path.

Le programme ffprobe, qui fait partie de FFmpeg, est utilisé pour créer une chaîne de commande permettant d'obtenir la durée de l'enregistrement. Nous appelons la fonction run_command() avec cette commande et conservons la valeur renvoyée dans rec_duration.

Enfin, nous imprimons et retournons la durée d'enregistrement obtenue.

La durée d'enregistrement est nécessaire pour que la durée de la vidéo qui sera générée y corresponde.

Conversion de l'audio en vidéo

Ajoutez le code suivant sous la fonction get_rec_duration() :

def turn_audio_to_video(rec_name, rec_duration):
    rec_path = "./recordings/{}".format(rec_name)
    bg_image_path = "./images/bg.png"
    video_name = "video_with_sound_waves.mp4"

    command = 'ffmpeg -y -i {rec_path} -loop 1 -i {bg_image_path} -t {rec_duration} \
    -filter_complex "[0:a]showwaves=s=1280x150:mode=cline:colors=00e5ff[fg];  \
    drawbox=x=0:y=285:w=1280:h=150:color=black@0.8:t=fill[bg]; \
    [bg][fg]overlay=format=auto:x=(W-w)/2:y=(H-h)/2 " \
    -map 0:a -c:v libx264 -preset fast -crf 18 -c:a aac \
    -shortest ./videos/{video_name}'.format(
        rec_path=rec_path,
        bg_image_path=bg_image_path,
        rec_duration=rec_duration,
        video_name=video_name,
    )

    print(video_name)
    run_command(command)
    return video_name

La fonction turn_audio_to_video() transforme les enregistrements en vidéos montrant les ondes sonores. La fonction prend comme argument le nom de l'enregistrement (rec_name) et la durée de l'enregistrement (rec_duration).

La commande FFmpeg qui génère la vidéo à partir de l'audio utilise le chemin d'enregistrement (rec_path), le chemin d'une image d'arrière-plan (bg_image_path), et le nom de fichier de sortie pour la vidéo (video_name).

Voyons de plus près la commande FFmpeg :

ffmpeg -y -i {rec_path} -loop 1 -i {bg_image_path} -t {rec_duration} \
-filter_complex \"[0:a]showwaves=s=1280x150:mode=cline:colors=00e5ff[fg];  \
drawbox=x=0:y=285:w=1280:h=150:color=black@0.8:t=fill[bg]; \
[bg][fg]overlay=format=auto:x=(W-w)/2:y=(H-h)/2 \" \
-map 0:a -c:v libx264 -preset fast -crf 18 -c:a aac -shortest ./videos/{video_name}

L'option -y indique à FFmpeg de remplacer le fichier de sortie s'il existe sur le disque.

L'option -i spécifie les entrées. Dans ce cas, nous disposons de 2 fichiers d'entrée, le fichier d'enregistrement, rec_path, et l'image que nous utilisons comme arrière-plan et qui est conservée sous bg_image_path.

L'option -loop pour générer une vidéo en répétant (en boucle) le ou les fichiers d'entrée. Ici, nous passons en boucle sur notre entrée d'image dans bg_image_path. La valeur par défaut est 0 (sans boucle), nous l'avons donc réglée sur 1 (boucle) pour répéter cette image dans toutes les images vidéo.

L'option -t spécifie une durée en secondes, ou en utilisant la syntaxe "hh:mm:ss[.xxx]". Ici, nous utilisons la valeur de la durée d'enregistrement (rec_duration) pour définir la durée de notre vidéo.

-filter_complex nous permet de définir un filtre graphique complexe, avec un nombre arbitraire d'entrées et/ou de sorties. Il s'agit d'une option complexe qui nécessite un certain nombre d'arguments, décrits ci-dessous.

Tout d'abord, nous utilisons le filtre showwaves pour convertir l'enregistrement vocal référencé comme [0:a] en sortie vidéo. Le paramètre s est utilisé pour spécifier la taille de la vidéo, que nous avons définie sur 1 280 x 150. Le paramètre mode permet de définir le tracé des ondes audio. Les valeurs disponibles sont les suivantes : pointlinep2p et cline. Le paramètre colors spécifie la couleur de la forme d'onde. Le tracé de la forme d'onde se voit attribuer l'étiquette [fg].

Nous utilisons le filtre drawbox pour dessiner un cadre coloré par-dessus notre image d'arrière-plan afin de faire ressortir la forme d'onde. Les paramètres x et y définissent les coordonnées du coin supérieur gauche de la zone, tandis que w et h définissent sa largeur et sa hauteur. Le paramètre color configure la couleur de la zone sur black avec une opacité de 80 %. Le paramètre t définit l'épaisseur de la bordure de la zone. En réglant la valeur sur fill, nous créons une zone pleine (opaque).

Pour terminer la définition de ce filtre, nous utilisons overlay pour placer le tracé de la forme d'onde par-dessus la zone noire. Le filtre overlay est configuré avec le paramètre format, qui définit automatiquement le format de pixel, et x et y, qui indiquent les coordonnées pour le placement de la superposition sur l'image vidéo. Nous procédons à quelques calculs pour préciser que x et y doivent être placés au centre de notre vidéo.

L'option -map permet de choisir les flux de la/des entrées à inclure ou à exclure de la/des sorties. Nous choisissons d'ajouter tous les flux de notre enregistrement à notre vidéo de sortie.

L'option -c:v est utilisée pour encoder un flux vidéo avec un certain codec. Nous disons à FFmpeg d'utiliser l'encodeur libx264.

L'option -preset sélectionne un ensemble d'options qui fourniront un certain taux de compression/vitesse d'encodage. Nous utilisons ici l'option fast, mais n'hésitez pas à modifier le préréglage en un réglage plus lent (meilleure qualité) ou plus rapide (qualité inférieure).

L'option -crf se rapporte au facteur de débit constant. Le contrôle de débit détermine le nombre de bits à utiliser pour chaque trame. Cela détermine la taille du fichier ainsi que la qualité de la vidéo de sortie. Une valeur de 18 est recommandée pour obtenir une qualité visuelle sans perte.

L'option -c:a est utilisée pour encoder un flux audio avec un certain codec. Nous codons l'audio avec le codec AAC.

L'option -shortest indique à FFmpeg d'arrêter d'écrire la sortie lorsque le flux d'entrée le plus court se termine.

L'option ./videos/{video_name} à la fin de la commande spécifie le chemin d'accès de notre fichier de sortie.

Si vous êtes curieux, voici ce que produisent tous les modes de formes d'onde FFmpeg évoqués ci-dessus et leur apparence.

Point trace un point pour chaque échantillon :

Courte vidéo - Mode de forme d"onde à points

Line trace une ligne verticale pour chaque échantillon :

Courte vidéo - Mode de forme d"onde linéaire

P2p trace un point pour chaque échantillon et une ligne entre ces points :

Courte vidéo : Mode de forme d"onde P2p

Cline trace une ligne centrale verticale pour chaque échantillon. Voici la forme d'onde que nous utilisons dans ce tutoriel :

Courte vidéo - Mode de forme d"onde Cline

Ajoutez le code suivant sous la fonction turn_audio_to_video() :

def main():
    rec_name = "rec_1.mp3"
    rec_duration = get_rec_duration(rec_name)
    turn_audio_to_video(rec_name,rec_duration)


main()

Dans ce nouveau code, nous avons une fonction appelée main(). Elle permet de conserver le nom de l'enregistrement dans une variable appelée rec_name. Vous devez mettre à jour cette ligne pour inclure le nom de votre propre fichier d'enregistrement vocal.

Ensuite, nous appelons la fonction get_rec_duration() pour obtenir la durée de l'enregistrement.

Puis, nous appelons la fonction turn_audio_to_video avec le nom et la durée de l'enregistrement, et nous enregistrons la valeur renvoyée dans une variable nommée video_with_sound_waves.

Pour finir, nous appelons la fonction main() pour exécuter le processus complet. N'oubliez pas de remplacer la valeur de la variable rec_name par le nom de l'enregistrement que vous souhaitez traiter.

Revenez à votre terminal et exécutez la commande suivante pour générer la vidéo :

python main.py

Recherchez un fichier nommé video_with_sound_waves.mp4 dans le répertoire videos. Ouvrez-le. Il devrait ressembler à ceci :

Vidéo montrant le rendu de forme d"onde

Ajouter une vidéo sur l'arrière-plan

Dans cette section, nous allons ajouter une vidéo d'un spinning record dans le coin inférieur gauche de la vidéo générée. La vidéo que nous allons ajouter est conservée dans le fichier nommé spinningRecord.mp4, dans le répertoire videos.

Animation du spinning record

Retournez à votre éditeur de code, ouvrez le fichier main.py, et ajoutez le code suivant sous la fonction turn_audio_to_video() :

def add_spinning_record(video_name, rec_duration):
    video_path = "./videos/{}".format(video_name)
    spinning_record_video_path = "./videos/spinningRecord.mp4"
    new_video_name = "video_with_spinning_record.mp4"

    command = 'ffmpeg -y -i {video_path} -stream_loop -1 -i {spinning_record_video_path} \
    -t {rec_duration} -filter_complex "[1:v]scale=w=200:h=200[fg]; \
    [0:v] scale=w=1280:h=720[bg], [bg][fg]overlay=x=25:y=(H-225)" \
    -c:v libx264 -preset fast -crf 18 -c:a copy \
    ./videos/{new_video_name}'.format(
        video_path=video_path,
        spinning_record_video_path=spinning_record_video_path,
        rec_duration=rec_duration,
        new_video_name=new_video_name,
    )

    print(new_video_name)
    run_command(command)
    return new_video_name

Ici, nous avons créé une fonction appelée add_spinning_record(). Cette fonction sera responsable de l'ajout de la vidéo spinningRecord.mp4 par-dessus la vidéo montrant des ondes sonores. Elle prend comme argument le nom de la vidéo générée précédemment (video_name) et la durée d'enregistrement (rec_duration).

Cette fonction exécute également FFmpeg. Voici la commande en détail :

$ ffmpeg -y -i {video_path} -stream_loop -1 -i {spinning_record_video_path} \
-t {rec_duration} -filter_complex \"[1:v]scale=w=200:h=200[fg]; \
 [0:v] scale=w=1280:h=720[bg], [bg][fg]overlay=x=25:y=(H-225)\" \
-c:v libx264 -preset fast -crf 18 -c:a copy ./videos/{new_video_name}

La commande ci-dessus propose les options suivantes :

Les options –y-t-c:v-preset, et –crf sont identiques à celles de la commande FFmpeg qui ont permis de générer les ondes audio.

L'option –i a également été utilisée auparavant, mais dans le cas présent, nous avons 2 vidéos en tant que fichiers d'entrée – le fichier vidéo généré à l'étape précédente et le fichier vidéo du spinning record.

L'option -stream_loop nous permet de définir le nombre de fois qu'un flux d'entrée doit être mis en boucle. Une valeur égale à 0 signifie que la boucle est désactivée, tandis que -1 signifie que la boucle est infinie. Nous avons réglé la vidéo pour une boucle infinie. Ainsi, FFmpeg encoderait la vidéo de sortie indéfiniment, mais comme nous avons également spécifié la durée de la vidéo de sortie, FFmpeg arrêtera l'encodage de la vidéo lorsqu'elle atteindra cette durée.

L'option -filter_complex est également dotée de la même fonction qu'auparavant, mais ici, nous avons deux vidéos en fichiers d'entrée – la vidéo créée dans la section précédente [0:v] et la vidéo du spinning record [1:v].

Le filtre utilise d'abord l'option scale pour redimensionner la vidéo du spinning record de façon à ce qu'elle ait des dimensions de 200 x 200 et lui attribue l'étiquette [fg]. Nous utilisons ensuite le même filtre scale pour définir la vidéo créée dans la section précédente à une taille de 1 280 x 720, avec l'étiquette [bg]. Enfin, nous utilisons le filtre overlay pour placer la vidéo du spinning record par-dessus la vidéo créée dans la section précédente, aux coordonnées x=25 et y=H-225 (le H représentant la hauteur de la vidéo).

L'option -c:a avait également été introduite dans la section précédente, mais dans le cas présent, nous utilisons la valeur spéciale copy pour indiquer à FFmpeg de copier le flux audio de la vidéo source sans le réencoder.

La dernière partie de la commande, ./videos/{new_video_name}, définit le chemin pour notre fichier de sortie.

Remplacez le code à l'intérieur de la fonction main() par ce qui suit, ce qui permettra d'appeler la fonction add_spinning_record() :

def main():
    rec_name = "rec_1.mp3"
    rec_duration = get_rec_duration(rec_name)
    video_with_sound_waves = turn_audio_to_video(rec_name, rec_duration)
    add_spinning_record(video_with_sound_waves, rec_duration)

Exécutez la commande suivante dans votre terminal pour générer une vidéo :

python main.py

Recherchez un fichier nommé video_with_spinning_record.mp4 dans le répertoire videos. Ouvrez-le. Il devrait ressembler à ceci :

Courte vidéo - Rendu de forme d"onde avec spinning record

Ajouter du texte à la vidéo

Dans cette section, nous allons ajouter un titre dans la partie supérieure de la vidéo. Cette procédure nous permettra d'apprendre à utiliser FFmpeg pour dessiner du texte, modifier la couleur, la taille, la police et la position.

Retournez à votre éditeur de code, ouvrez le fichier main.py, et ajoutez le code suivant sous la fonction add_spinning_record :

def add_text_to_video(video_name):
    video_path = "./videos/{}".format(video_name)
    new_video_name = "video_with_text.mp4"
    font_path = "./fonts/LeagueGothic-CondensedRegular.otf"

    command = "ffmpeg -y -i {video_path} -vf \"drawtext=fontfile={font_path}:  \
    text='Turning your Twilio voice recordings into videos':fontcolor=black: \
    fontsize=90:box=1:boxcolor=white@0.5 \
    :boxborderw=5:x=((W/2)-(tw/2)):y=100\" \
    -c:a copy ./videos/{new_video_name}".format(
        video_path=video_path,
        font_path=font_path,
        new_video_name=new_video_name
    )

    print(new_video_name)
    run_command(command)
    return new_video_name

Dans cette fonction, nous avons créé une fonction appelée add_text_to_video() qui appelle une nouvelle commande de FFmpeg pour dessiner le texte. Voyons de plus près la commande FFmpeg :

ffmpeg -y -i {video_path} -vf \"drawtext=fontfile={font_path}:  \
text='Turning your Twilio voice recordings into videos':fontcolor=black: \
fontsize=90:box=1:boxcolor=white@0.5:boxborderw=5:x=((W/2)-(tw/2)):y=100\" \
-c:a copy ./videos/{new_video_name}

Les options –y et -c:a sont utilisées exactement comme auparavant.

L'option –i , qui définit les entrées, ne comporte désormais qu'un seul fichier d'entrée, le fichier vidéo généré à la section précédente.

L'option -vf nous permet de créer un fichier filtergraph simple et de l'utiliser pour filtrer le flux. Ici, nous utilisons le filtre drawtext pour dessiner le texte par-dessus la vidéo, avec un certain nombre de paramètres : fontfile est le fichier de polices utilisé pour dessiner le texte, text définit le texte à dessiner (vous pouvez le modifier à votre guise), fontcolor définit la couleur de texte (noir), fontsize définit la taille du texte, box permet d'activer une zone autour du texte, boxcolor définit la couleur de cette zone sur white avec une opacité de 50 %, boxborderw définit la largeur de la bordure de la zone, et x et y définissent la position où le texte doit apparaître sur la vidéo. Nous avons fait quelques calculs pour centrer le texte.

L'option ./videos/{new_video_name} à la fin définit le fichier de sortie, comme dans les commandes FFmpeg précédentes.

Remplacez le code à l'intérieur de la fonction main() par ce qui suit, ce qui permettra d'ajouter le titre :

def main():
    rec_name = "rec_1.mp3"
    rec_duration = get_rec_duration(rec_name)
    video_with_sound_waves = turn_audio_to_video(rec_name, rec_duration)
    video_with_spinning_record = add_spinning_record(video_with_sound_waves, rec_duration)
    video_with_text = add_text_to_video(video_with_spinning_record)

Revenez à votre terminal et exécutez la commande suivante pour générer la vidéo avec le titre :

python main.py

Recherchez un fichier nommé video_with_text.mp4 dans le répertoire videos. Ouvrez-le. Il devrait ressembler à ceci :

Courte vidéo - Rendu de forme d"onde avec spinning record et titre

Conclusion

Dans ce tutoriel, nous avons appris à utiliser certaines des options avancées de FFmpeg pour transformer un enregistrement vocal en une vidéo qui peut être partagée sur les réseaux sociaux. J'espère que cela vous a donné envie d'en savoir plus sur FFmpeg.

Le code de l'ensemble de l'application est disponible dans le repo suivant : https://github.com/CSFM93/twilio-turn-recording-to-video.

Carlos Mucuho est un ex-géologue mozambicain devenu développeur qui aime utiliser la programmation pour donner vie à ses idées. https://github.com/CSFM93.