Générez des GIF animées à partir de captures d'écrans vidéos

Vous développez un nouveau jeu ? Vous êtes fier de votre nouvelle création et vous voulez sans plus tarder en faire la démonstration sur Discord ou sur notre site Internet ? Vous aurez probalement envie de faire une GIF animée pour exhiber votre chef-d’œuvre à toute la communauté. Alors, apprenez à maîtriser META Screen Recorder : un nouvel outil de capture pour la META.

Avancé

1h

Tutoriels & Doc
Générez des GIF animées à partir de captures d'écrans vidéos
Tuto terminé ?

Aperçu général

Vous allez apprendre ici à configurer et utiliser META Screen Recorder pour tous vos projets Gamebuino. Cet utilitaire vient compléter l'outil de capture intégré à la META (accessible par le bouton Home), pour vous offrir plus de souplesse, plus de simplicité et plus de fonctionnalités :

  • Vous allez pouvoir déclencher ou arrêter l’enregistrement de l'écran quand bon vous semble. En effet, vous n'aurez plus à interrompre le déroulement de votre application pour lancer un enregistrement. Un simple appui prolongé sur le bouton Menu suffit. Et pour stopper l'enregistrement ? Rappuyez brièvement sur le bouton Menu. C'est aussi simple que ça !

  • Vous pourrez aussi déclencher et arrêter l'enregistrement directement dans votre code ! Cette fonctionnalité vous permettra d'enregistrer une scène de jeu avec plus de précision, ou lorsque certains événements particuliers surviennent (ne ratez plus le moment où vous mettez le boss KO).

  • Vous pourrez également profiter des pleines capacités de la META et enregistrer vos applications qui tournent en Haute Résolution !

Pour mettre en application ce que vous allez apprendre ici, vous devez naturellement posséder une Gamebuino META, qui est indispensable pour pouvoir effectuer un enregistrement.

Démonstration

Voici une démonstration du processus d'enregistrement réalisé avec META Screen Recorder :

Architecture

META Screen Recorder s'articule autour de deux composants :

  • ScreenRecorder : une classe C++ que vous devrez intégrer dans votre projet. Cette classe se charge d'envoyer toutes les données d'affichage, pendant l'exécution de votre application, sur le port série lorsque votre console est connectée à votre ordinateur via un câble USB.

  • screenrecord : un script PHP autonome que vous devrez installer et exécuter sur votre ordinateur pour réceptionner toutes les données envoyées par la META sur le port série. Ce script se charge de décoder toutes les données qu'il reçoit et de reconstruire les captures d'écrans qui se succèdent pour les enregistrer dans des fichiers PNG.

Il ne vous restera plus, ensuite, qu'à compiler l'ensemble de ces fichiers PNG avec un outil spécialisé (ImageMagick) pour générer une magnifique GIF animée restituant fidèlement ce qui s'est déroulé sur l'écran de la console.

Environnement logiciel prérequis

Pour pouvoir utiliser META Screen Recorder et générer des GIF animées, vous aurez besoin d'installer les logiciels libres suivants sur votre ordinateur :

  • PHP 7.x en version CLI (Command Line Interface), c’est-à-dire accessible depuis votre interpréteur de commandes.

  • L'extension GD pour PHP, qui permet de générer des images dans un grand choix de formats.

  • ImageMagick, qui comprend une bibliothèque et un ensemble d’utilitaires en ligne de commande permettant de créer, de convertir, de modifier et d’afficher des images dans un très grand nombre de formats.

Nous ne détaillerons pas ici les procédures d'installation de ces logiciels.
Néanmoins, vous pourrez trouver ces procédures sur la documentation détaillée de META Screen Recorder.

Avant de continuer, assurez-vous d'avoir bien installé ces différents logiciels.
Pour le vérifier, ouvrez un interpréteur de commandes et exécutez les commandes suivantes :

$ php -v              # affiche la version de PHP qui est installée
$ php -m              # affiche la liste des extensions installées pour PHP
$ convert -version    # affiche la version d'ImageMagick qui est installée

Téléchargement de la classe ScreenRecorder

Nous allons maintenant intégrer la classe ScreenRecorder à votre projet C++.
Commencez par télécharger les deux fichiers suivants (clic-droit + Enregistrez le lien sous...) :

Une fois téléchargés, placez ces deux fichiers à la racine du répertoire de votre projet Gamebuino.

Configuration de l'enregistrement manuel

Rappelons ici que META Screen Recorder vous permet d'effectuer des captures d'écrans de deux manières :

  • Manuellement, en utilisant le bouton Menu* pour déclencher et arrêter l'enregistrement.
  • Automatiquement, en insérant des instructions dans le code pour déclencher et arrêter l'enregistrement.

* Vous êtes libre de modifier le code de la classe ScreenRecorder si vous souhaitez redéfinir une combinaison de boutons de votre choix à la place du bouton Menu.

Intéressons-nous, dans un premier temps, à la procédure d'enregistrement manuelle.

Souvenez-vous, ScreenRecorder est capable d'effectuer des captures d'écrans dans la résolution standard (80x64), mais également en haute résolution (160x128). Nous allons distinguer ici ces deux cas de figure.

Pour la résolution standard

Ouvrez votre croquis principal et insérez les lignes suivantes :

#include <Gamebuino-Meta.h>
#include "ScreenRecorder.h" // <-- insérez cette ligne

void setup() {
    gb.begin();
    // vos instructions
    // d'initialisation
    ScreenRecorder::init();          // <-- insérez cette ligne
    ScreenRecorder::setForWindows(); // <-- insérez aussi celle-ci si vous êtes sur Windows
}

void loop() {
    while (!gb.update());
    // vos instructions qui peuvent
    // effectuer des tracés à l'écran
    // en utilisant gb.display
    ScreenRecorder::monitor(gb.display._buffer); // <-- insérez cette ligne
}

C’est tout ce que vous avez à faire !… Simple, nan ?

Remarque importante à propos de Windows

L'implémentation de la fonction PHP fread sous Windows est buguée : en effet, le tampon de lecture ne révèle les données accumulées que par paquets de 8K. Par conséquent, nous sommes contraints d'employer une astuce pour nous affranchir de ce bug. Donc il est impératif d'ajouter la ligne suivante pour préciser que la réception des données sera effectuée sur Windows :

ScreenRecorder::setForWindows();

Si vous êtes sur macOS ou Linux, ne rajoutez pas cette ligne.

Pour la haute résolution

Dans le cas d’une application développée pour la haute résolution, les choses sont un peu différentes. En effet, vous ne pourrez pas utiliser les méthodes d’affichage traditionnelles offertes par gb.display... Je vous encourage à lire l’excellent article d’Andy sur le sujet : High Resolution without gb.display, qui vous explique sommairement les raisons pour lesquelles ça n’est pas possible et vous livre une méthode de contournement qui consiste à utiliser directement gb.tft.

J’ai rédigé un tutoriel très complet sur l’approfondissement de cette technique. Vous pouvez le lire si cela vous intéresse (les débutants auront peut-être quelques difficultés à tout assimiler) : Éclairage Tamisé en Haute Résolution. Il vous permettra de comprendre en profondeur comment appliquer cette technique dans vos applications.

Prenons le cas de l'exemple issu de l'article d'Andy, que j'ai légèrement modifié pour l'adapter à un découpage de l'écran en tranches horizontales. Téléchargez le code SketchExampleForHD.ino (clic-droit + Enregistrez le lien sous...), créez un nouveau répertoire et placez-y le code source téléchargé.

Puis ouvrez le croquis SketchExampleForHD.ino et vérifiez que les lignes suivantes y figurent bien :

#include <Gamebuino-Meta.h>
#include "ScreenRecorder.h" // <-- chargement de la classe ScreenRecorder

void setup() {
    gb.begin();
    gb.display.init(0, 0, ColorMode::rgb565);
    gb.setFrameRate(32);
    ScreenRecorder::init(SLICE_HEIGHT); // <-- initialisation de ScreenRecorder
    ScreenRecorder::setForWindows();    // <-- !!! insérez cette ligne si vous êtes sur Windows !!!
}

void loop() {
    while (!gb.update());
    for (
        uint8_t sliceIndex = 0;
        sliceIndex < SCREEN_HEIGHT / SLICE_HEIGHT;
        sliceIndex++
    ) {
        // 
        // à la fin de la boucle...
        // 
        customDrawBuffer(0, sliceY, buffer, SCREEN_WIDTH, SLICE_HEIGHT);
        ScreenRecorder::monitor(buffer, sliceIndex); // <-- monitoring de l'écran
    }
    waitForPreviousDraw();
}

Vous remarquerez que, contrairement à la configuration pour une résolution standard :

  • on initialise ScreenRecorder en lui transmettant la hauteur des tranches de découpage de l’écran,

  • et on lui transmet également l’indice de la tranche courante pendant le monitoring de l’écran : c’est nécessaire pour qu’il démarre la transmission vers le script PHP dès qu’il détecte la première tranche (celle d’indice zéro).

Par ailleurs, vous voyez que c’est uniquement après avoir terminé de remplir le tampon de la tranche courante que l’on invoque ScreenRecorder::monitor(). En effet, à ce moment là, tous les tracés sur la tranche courante qui sont destinés à être envoyés à l'écran par le biais du contrôleur DMA ont été réalisés.

Remarque importante

Lors de l’initialisation de ScreenRecorder, notez que la hauteur des tranches doit nécessairement être une puissance de 2 non nulle, dont la valeur maximale est 16... donc seules les valeurs 2, 4, 8 et 16 seront prises en compte. Si vous utilisez une valeur supérieure à 16, l’enregistrement sera simplement désactivé. Et si vous utilisez une valeur inférieure à 16 qui n’est pas une puissance de 2, vous obtiendrez un enregistrement… plutôt bizarre

Je vous conseille d’utiliser la valeur 8 qui ne nécessite que 2 x 2,5 = 5 ko en RAM.

Configuration de l’enregistrement automatique

Plutôt que de déclencher et arrêter l'enregistrement à l'aide du bouton Menu, vous pouvez également le faire de manière automatique en insérant les instructions correspondantes directement dans votre code. Ceci peut s’avérer très pratique lorsque vous souhaitez déclencher ou stopper l’enregistrement à des moments précis, ou encore lorsque certains événements se produisent. Pour cela, il vous suffit d’insérer chacune des deux instructions suivantes aux endroits appropriés :

ScreenRecorder::startRecording(); // pour lancer l'enregistrement
ScreenRecorder::stopRecording();  // pour stopper l'enregistrement

Téléchargement du script PHP screenrecord

Il ne nous reste plus qu'à télécharger et installer le script PHP screenrecord sur votre ordinateur pour réceptionner les données envoyées sur le port série par la classe ScreenRecorder, et convertir ces données en une série de fichiers PNG.

Commencez par télécharger le script (clic-droit + Enregistrez le lien sous...) : screenrecord
Et placez-le également dans le dossier de votre projet.

Notez, en passant, que plutôt que de dupliquer systématiquement le script screenrecord dans chacun de vos projets, il est tout à fait possible de l'installer de manière globale dans votre espace utilisateur ou au sein de votre système. Cette procédure est détaillée dans la documentation de META Screen Recorder.

Ensuite, ouvrez un interpréteur de commandes, placez-vous dans le dossier de votre projet, et vérifiez que vous êtes bien en mesure d'exécuter le script :

macOS & Linux

$ cd ~/path/to/your/project/folder # on se place dans le bon répertoire
$ chmod u+x screenrecord           # on autorise l'exécution du script
$ ./screenrecord -h                # on lance le script avec l'option help

Windows

$ cd C:\path\to\your\project\folder # on se place dans le bon répertoire
$ php screenrecord -h               # on lance le script avec l'option help

Vous devriez obtenir la sortie suivante :

+---------------------------------------+
|    Gamebuino META Screen Recorder     |
| © 2019 Stéphane Calderoni (aka Steph) |
|     https://gamebuino.com/@steph      |
+---------------------------------------+

Usage: screenrecord [options]

DESCRIPTION

	This utility records the Gamebuino META screen in Standard & High Definition
	using communication through a serial port

OPTIONS

	-h  displays the synopsis
	-p  sets the serial port (default: /dev/cu.usbmodem141401)
	-d  sets the output directory (default: current directory)
	-b  sets the image files basename (default: frame)
	-n  sets the number of digits to number the image files (default: 4)

ANIMATED GIF CREATION

	You must have imagemagick installed to be able to create the animated GIF file.
	For example, to create an animation with a resolution of 160x128 pixels at 25 fps,
	simply type the following command:

	convert -delay 4 -loop 0 png_dir_path/*.png -scale 160x128 screenrecording.gif

L'option -h vous détaille la manière d'utiliser le script.
Nous nous intéresserons principalement ici aux options suivantes :

  • -p : qui permet de spécifier l'identifiant du port série sur lequel est connecté la META
  • -d : qui permet de spécifier le répertoire dans lequel seront enregistrés les fichiers PNG

Nous allons tout de suite créer un répertoire pour y enregistrer les fichiers PNG et éviter de mettre le boxon dans le répertoire de votre projet. Nous nommerons ce répertoire frames :

$ mkdir frames

Identifier le port série

Pour déterminer sur quel port série est connectée votre META, lancez l'IDE Arduino et rendez-vous dans le menu Tools :

Identifier le port série

Remarque concernant Windows

Vous devrez préalablement aller jeter un coup d’oeil dans le Gestionnaire de périphériques pour identifier le bon port :

Gestionnaire de périphériques

Lancer l'enregistrement

Compilez et téléchargez votre projet sur votre Gamebuino. Attendez que l'application se lance, puis lancez le script screenrecord pour qu'il se mette en écoute sur le port série (en indiquant le bon port) :

$ ./screenrecord -d frames -p /dev/cu.usbmodem141401  # <-- macOS & Linux
$ php screenrecord -d frames -p COM13                 # <-- Windows 10

Ensuite, appuyez longuement (au moins 1 seconde) sur le bouton Menu de la META pour lancer l'enregistrement. Vous remarquerez que les LEDs de la console émettent des flashs rouges à intervalles réguliers. Cela signifie qu’elle est bel et bien en train d’envoyer les données d’enregistrement sur le port série. Dans le même temps, vous verrez sur votre interpréteur de commande que le script réceptionne bien ces données et les enregistre dans l’ordre d’arrivée des captures. Vous devriez observer quelque-chose dans ce goût là :

Start listening to the serial port /dev/cu.usbmodem141401

Waiting for data... Start screen recording in high resolution

Decoding frame 0001... saved
Decoding frame 0002... saved
Decoding frame 0003... saved
Decoding frame 0004... saved
Decoding frame 0005... saved
Decoding frame 0006... saved
Decoding frame 0007... saved
Decoding frame 0008... saved
Decoding frame 0009... saved
Decoding frame 0010... saved
.
.
.

Pour arrêter l’enregistrement, rappuyez brièvement sur le bouton MENU... Les LEDs s’éteignent, le flux de données s’interrompt et le script s’arrête :

.
.
.
Decoding frame 0100... saved
Decoding frame 0101... saved
Decoding frame 0102... saved
Decoding frame 0103... saved
Decoding frame 0104... saved
Decoding frame 0105... saved
Decoding frame 0106... saved

106 PNG files have been recorded in 160x128 (high resolution).

You must have imagemagick installed to convert these PNG files into an animated GIF file.
For a 25 fps animation, simply type the following command:

convert -delay 4 -loop 0 frames/*.png -scale 160x128 screenrecording.gif

Vous pouvez d'ores et déjà vérifier que le script a bien enregistré une séquence de fichiers PNG dans le répertoire frames :

$ ls -l frames  # <-- macOS & Linux
$ dir frames    # <-- Windows

La commande permettant de générer une GIF animée à partir de la série de captures vous est indiquée :

$ convert -delay 4 -loop 0 frames/*.png -scale 160x128 screenrecording.gif

Remarque sur l’option delay

La valeur N attendue par l’option delay est un entier qui correspond à une fréquence d’animation de 100/N fps. Donc, si N=4, votre GIF animée aura une fréquence de 100/4 = 25 fps.

Dans le cas de notre exemple SketchExampleForHD.ino pour la haute résolution, souvenez-vous que nous avions fixé un framerate de 32 fps :

gb.setFrameRate(32);

Donc pour que notre GIF respecte grosso modo cette fréquence, nous devrons plutôt fixer N=3. Par ailleurs, on peut tout à fait générer une image avec des dimensions différentes de celles de la capture originale. Par exemple, passons de (160x128) à (320x256) :

$ convert -delay 3 -loop 0 frames/*.png -scale 320x256 screenrecording.gif

Et nous obtenons le fichier screenrecording.gif dont voici le superbe contenu :

GIF animée

Remarque

J’ai un peu triché… j’ai supprimé les frames superflus pour que l’animation en boucle soit parfaitement raccord... (il suffit ici de ne conserver que les 64 premiers frames). Il est en effet encore possible de corriger votre enregistrement, en supprimant des frames, ou en les réordonnant si nécessaire. Pratique, nan ?

Le mot de la fin

Voilà, nous sommes arrivés au terme de ce petit tuto. Vous devriez maintenant être en mesure de faire des captures vidéos de tous vos projets avec META Screen Recorder. J’espère que cet outil vous sera utile. J’en avais moi-même un grand besoin, et je suis très content de pouvoir le partager aujourd’hui. Si vous relevez des dysfonctionnements, si vous entrevoyez des améliorations possibles, ou si vous voulez me montrer les captures dont vous êtes le plus fier, n’hésitez pas à me laisser un petit commentaire sur la page de cette création !

Le code de la classe ScreenRecorder et celui du script screenrecord sont à votre disposition sur mon dépôt GitHub. Vous pouvez les modifier librement, et si vous les améliorez, on compte sur vous pour nous en faire profiter !

L'auteur

Steph

https://gamebuino.m1cr0lab.com

Voir son profil