Pour prendre une capture d'écran statique d'une partie sélectionnée de mon écran, j'utilise souvent scrot
avec -s shot.png
. C'est idéal pour ajouter des illustrations aux publications StackExchange. J'ai même trouvé ce script pour télécharger automatiquement une telle capture d'écran sur Imgur.com et mettre un lien dans mon presse-papiers X!
Disons transformer cela en douze : Comment puis-je de la même manière créer un screencast GIF?
Il y a des programmes comme recordmydesktop
, byzanz
& co comme discuté sur Ask Ubunt qui visent à être "conviviaux" ", mais d'après mon expérience, ils sont bogués, inefficaces, pour la plupart inscriptibles et inadaptés à de petites choses ponctuelles comme celle-ci.
Je veux juste sélectionner une zone et enregistrer un GIF, avec une commande de console que je peux comprendre, pas une monstruosité obscure de l'interface graphique.
Comment puis-je faire ceci?
J'ai commencé ffcast
, ai vim
, quitte ffcast
, puis convert
ed .avi
→ .gif
.
J'ai exécuté les commandes d'enregistrement dans un autre terminal. Script raffiné pour votre $PATH
à la fin de cette réponse.
FFcast aide l'utilisateur à sélectionner de manière interactive une région d'écran et passe la géométrie à une commande externe, telle que FFmpeg, pour l'enregistrement d'écran.
ffcast
est le produit glorieux de certains hacking à la communauté Arch Linux (principalement lolilolicon ) ). Vous pouvez le trouver sur github (ou dans l'AUR pour Arch ers). Sa liste de dépendances est juste bash
et ffmpeg
, bien que vous souhaitiez xrectsel
( lien AUR ) pour interactif sélection de rectangle.
Vous pouvez également ajouter des drapeaux ffmpeg
juste après la commande. J'ai mis -r 15
pour capturer à 15 images par seconde et -codec:v huffyuv
pour un enregistrement sans perte. (Jouez avec ceux-ci pour ajuster le compromis taille/qualité.)
ImageMagick peut lire .avi
vidéos et propose quelques astuces d'optimisation GIF qui réduisent considérablement la taille des fichiers tout en préservant la qualité: le -layers Optimize
à convert
invoque l'optimiseur à usage général. Le manuel ImageMagick a ne page sur les optimisations avancées aussi.
Voici ce que j'ai dans mon $PATH
. Il enregistre dans un fichier temporaire avant la conversion.
#!/bin/bash
TMP_AVI=$(mktemp /tmp/outXXXXXXXXXX.avi)
ffcast -s % ffmpeg -y -f x11grab -show_region 1 -framerate 15 \
-video_size %s -i %D+%c -codec:v huffyuv \
-vf crop="iw-mod(iw\\,2):ih-mod(ih\\,2)" $TMP_AVI \
&& convert -set delay 10 -layers Optimize $TMP_AVI out.gif
Merci à BenC pour son travail de détective pour trouver les bons indicateurs après la récente mise à jour de ffcast
.
Si vous souhaitez installer les dépendances sur une distribution basée sur Debian, Louis a écrit notes d'installation utiles .
Pour moi, la réponse était d'utiliser ffcast
avec ffmpeg
comme ceci:
ffcast -w % ffmpeg -f x11grab -show_region 1 -framerate 20 -video_size %s -i %D+%c -codec:v huffyuv -vf crop="iw-mod(iw\\,2):ih-mod(ih\\,2)" out.avi
J'ai ensuite utilisé ffmpeg
pour faire la conversion d'avi en gif - c'est très rapide et ça garde le framerate intact:
ffmpeg -i out.avi -pix_fmt rgb24 out.gif
Enfin, j'ai utilisé convert de la même manière que @ ankoanswer pour optimiser le gif, mais j'ai défini une limite d'utilisation des ressources pour empêcher convert
de quitter avec un message killed
, et j'ai supprimé le délai car ffmpeg
l'a déjà géré:
convert -limit memory 1 -limit map 1 -layers Optimize out.gif out_optimised.gif
pour ma configuration (ubuntu 16.04), ffcast ne fonctionne pas bien car il n'est pas mis à jour sur github pendant un bon moment.
j'ai donc mis en place un script en utilisant slop ( https://github.com/naelstrof/slop ) et ffmpeg.
un exemple:
#!/bin/bash
read -r X Y W H G ID < <(slop -f "%x %y %w %h %g %i")
TMP_AVI=$(mktemp /tmp/outXXXXXXXXXX.avi)
ffmpeg -s "$W"x"$H" -y -f x11grab -i :0.0+$X,$Y -vcodec
huffyuv -r 25 $TMP_AVI
convert -set delay 5 -layers Optimize $TMP_AVI out.gif
Mise à jour: Code mis à jour, ffmpeg renvoie le code de sortie 2 qui empêche convert
de s'exécuter comme mentionné par Rub. Après l'enregistrement, appuyez sur ctrl + C pour quitter ffmpeg et exécuter convert
pour générer le fichier out.gif.
Ce référentiel vous aide à créer votre gif à partir de la région de sélection et également à l'optimiser pour vous
J'avais écrit un script wrapper interactif pour les bureaux Unix pour cette raison, et après un an d'utilisation, je suis heureux de le partager là-bas!
Fabriqué avec byzanz
, gifsicle
, xdotool
, et le script est écrit en php
.
Exemple de sortie:
[1020 pixels, largeur de gif non redimensionnée 1020 pixels, 70 secondes, 50 couleurs, 65 Ko ]
Il fournit de bons gifs compressés et est une bonne vitrine pour cette question.
Il s'agit d'une base assez simple, prête à être piratée par vous.
Fonctionnalités : enregistrement Gif aux positions de la souris ou plein écran, redimensionnement, compression, compression des couleurs, inversion/fusion, téléchargement sur giphy.com curl.
Pour démarrer un enregistrement gif de 10 secondes: gif 10
Pour enregistrer plusieurs fois avec les mêmes paramètres: gif !
Pour démarrer un enregistrement gif de 5 secondes en plein écran: gif 5 --fullscreen
Exécution du script, s'enregistrant agréablement:
[ 45 secondes, largeur 645px, couleurs pleines, 976kb]
Script complet de 5 Ko:
#!/usr/bin/php
<?php
#> php xdotool byzanz gifsicle curl
#@ https://webdev23.github.io/gif/gif
echo "Usage: ./gif [time in seconds|!] [--fullscreen|-f]\n";
echo "--------------------------------------------------\n";
echo "Gif recorder tool\n";
echo "gif ! to call back last settings\n";
echo "Please move your mouse at the top left corner point\n";
echo "of the wanted gif area. Then press enter.\n";
echo "\n";
#~ Nico KraZhtest | 05/2017 | https://github.com/webdev23/gif
#~ Create fluid GIF's fastly
#~ You can set the gif record time as argument: ./gif 10
#~ Default record time is 1 seconde, or set it now:
$recordTime = 1;
#~ ----------------
$t = @$argv[1];
$x1;$y1;$x2;$y2;$gw;$gh;$defc;$rw;
if (!isset($argv[1]) || @$argv[1] === "!") {
$t = $recordTime;
}
if (@$argv[1] === "!") {
$pos = file_get_contents("./.config/gif/pos");
$pos = explode("\n", $pos);
$x1 = $pos[0];
$y1 = $pos[1];
$x2 = $pos[2];
$y2 = $pos[3];
$gw = $pos[4];
$gh = $pos[5];
$t = $pos[6];
@$GLOBALS['defc'] = $pos[7];
@$GLOBALS['$rw'] = $pos[8];
#~ echo $x1." ".$y1." ".$x2." ".$y2." ".$gw." ".$gh." ".$t." ".$defc." ".@$rw;
}
else if (@$argv[2] === "fullscreen" || @$argv[2] === "--fullscreen" || @$argv[2] === "-f" || @$argv[2] === "f") {
echo "############\nStarting fullscreen record\n";
$fs = system("xdpyinfo | grep 'dimensions:'");
echo "\n";
$fs = explode(" ",$fs);
echo $fs[1];
$fs = explode(" ",$fs[1]);
echo $fs[0];
$fs = explode("x",$fs[0]);
echo $fs[0]."\n";
echo $fs[1];
$x1 = "0";
$y1 = "0";
$x2 = "fs";
$y2 = "fs";
$gw = $fs[0];
$gh = $fs[1];
$t = $argv[1];
system("mkdir -p ./.config/gif/");
system("cd ./.config/gif/ && \
echo '$x1\n$y1\n$x2\n$y2\n$gw\n$gh\n$t\n\n\n\n' > pos");
}
else {
$stdin = fopen('php://stdin', 'r');
$response = rtrim(fgets(STDIN));
$p1 = system("xdotool getmouselocation");
$pos1 = explode(" ",$p1);
$x1 = $pos1[0];
$x1 = explode(":",$x1);
$x1 = $x1[1];
echo "X1: ".$x1;
$y1 = $pos1[1];
$y1 = explode(":",$y1);
$y1 = $y1[1];
echo " Y1: ".$y1;
echo "\nNow move your mousse at the bottom right corner.\nThen enter\n";
$stdin = fopen('php://stdin', 'r');
$response = rtrim(fgets(STDIN));
$p2 = system("xdotool getmouselocation");
$pos2 = explode(" ",$p2);
$x2 = $pos2[0];
$x2 = explode(":",$x2);
$x2 = $x2[1];
echo "X2: ".$x2;
$y2 = $pos2[1];
$y2 = explode(":",$y2);
$y2 = $y2[1];
echo " Y2: ".$y2;
$gw = ($x2 - $x1);
echo "\nGif width: ".$gw;
$gh = ($y2 - $y1);
echo "\nGif height: ".$gh;
echo "\n".$x1." ".$y1." ".$x2." ".$y2." ".$gw." ".$gh." ".$t."\n";
system("mkdir -p ./.config/gif/");
system("cd ./.config/gif/ && \
echo '$x1\n$y1\n$x2\n$y2\n$gw\n$gh\n$t\n\n\n\n' > pos");
}
$unix = date_timestamp_get(date_create());
echo "\n".$unix." | Starting ".$t."s gif record\n";
@system("byzanz-record \
-v \
--duration=$t \
--x=$x1 \
--y=$y1 \
--width=$gw \
--height=$gh \
~/Pictures/gif$unix.gif");
$named = "gif".$unix;
echo "Saved as ~/Pictures/".$named.".gif\n";
echo "\nOptimize | How many colors to keep? (default 100, max 256) \n";
if (@$argv[1] === "!"){
$pos = file_get_contents("./.config/gif/pos");
$pos = explode("\n", $pos);
$defc = $pos[7];
}
if (!isset($defc)){
$defc = readline("Colors: ");
}
if (empty($defc)){
$defc = "100";
}
echo "\nKeeping ".$defc." colors\n";
system("gifsicle --verbose -i ~/Pictures/$named.gif -O5 --colors=$defc -o ~/Pictures/$named\_reduced.gif");
echo "\nOptimize | Resize width in pixels (default 360px) \n";
if (@$argv[1] === "!"){
$pos = file_get_contents("./.config/gif/pos");
$pos = explode("\n", $pos);
$rw = $pos[8];
}
if (!isset($rw)){
$rw = readline("Width : ");
}
if (empty($rw)){
$rw = "360";
}
echo "\nResized by ".$rw." pixels width\n";
@system("gifsicle --verbose -i ~/Pictures/$named\_reduced.gif --resize-width $rw -o ~/Pictures/".$named."_optimized.gif");
$opt = "~/Pictures/".$named."_optimized.gif";
usleep(5000000);
echo "\nSpecial | Reverse and merge?\n";
system("xdg-open ~/Pictures/".$named."_optimized.gif > /dev/null");
if (@$argv[1] === "!"){
$pos = file_get_contents("./.config/gif/pos");
$pos = explode("\n", $pos);
$rev = $pos[9];
}
if (!isset($rev)){
$stdin = fopen('php://stdin', 'r');
$rev = rtrim(fgets(STDIN));
$rev = "1";
}
if (!isset($rev)){
$rev = "0";
}
@system("cd ./.config/gif/ && sed -i '8s/.*/$defc/' pos");
@system("cd ./.config/gif/ && sed -i '9s/.*/$rw/' pos");
@system("cd ./.config/gif/ && sed -i '10s/.*/$rev/' pos");
if ($rev === "1"){
@system("gifsicle \
-i ~/Pictures/$named\_reduced.gif \
'#-2-1' \
-o ~/Pictures/".$named."_reversed.gif");
$inv = "~/Pictures/".$named."_reversed.gif";
usleep(400000);
@system("gifsicle \
-i ~/Pictures/$named\_reduced.gif \
--append $inv \
--resize-width $rw \
-o ~/Pictures/".$named."_merged.gif");
usleep(3000000);
system("xdg-open ~/Pictures/".$named."_merged.gif > /dev/null");
}
echo "\n####################";
echo "\nUpload to giphy.com?\n";
$stdin = fopen('php://stdin', 'r');
$response = rtrim(fgets(STDIN));
$m = "~/Pictures/".$named."_merged.gif";
$f = system("du -h $m");
$f = explode(" ",$f);
$f = $f[1];
$www = system('curl \
--progress-bar \
-v \
-F "file=@'.$f.'" \
-F "api_key=dc6zaTOxFJmzC" \
"http://upload.giphy.com/v1/gifs"');
$www = json_decode($www);
echo "\n\nhttps://i.giphy.com/".$www->data->id.".gif\n";
echo "\nThanks YOU!\n";
Capacité d'inversion/fusion, pour créer des trucs artistiques.
Original (435kb)
Inversé, fusionné: (826kb)
Pour installer, en utilisant phi :
php <(curl https://webdev23.github.io/phi/phi) install https://webdev23.github.io/gif/gif
Plein écran:
[1920 * 1080px, gif 400px, 50 secondes , 100 couleurs, 2 Mo ]
Source, avec quelques explications supplémentaires et mises à jour potentielles: https://github.com/webdev23/gif
Construire sur TC Zhang réponse (j'ai aimé la simplicité, mais je ne pouvais pas voir comment l'arrêter), et prendre gifsicle de NVRM réponse (très belle qualité je dois dire), j'ai créé ma propre version.
Il commence par demander la zone à enregistrer (en silence), puis une petite boîte avec des boutons est placée juste sous le coin inférieur droit de votre sélection (sans interférer avec l'enregistrement. Elle reste en haut, mais sans saisir la mise au point.
Vous pouvez mettre en pause/continuer ou simplement arrêter, à tout moment. Je considère une douleur d'avoir à préciser la durée à l'avance, soyez libre!
Le résultat est placé dans ~/Téléchargements/avec un horodatage comme 2020-02-29--0237.gif (je préfère ne pas passer de temps à spécifier où/comment enregistrer).
N'hésitez pas à personnaliser selon vos besoins. J'ai essayé de le rendre très explicite.
J'ai appris un tas de choses à assembler (yad, ffmpeg et signaux, bash, etc.)
Si quelqu'un trouve de meilleurs paramètres pour la qualité d'enregistrement/conversion, faites-le moi savoir. Je suis heureux de le mettre à jour.
Cordialement, moi.
Exemple:
#!/bin/bash
# Inspired on script from https://unix.stackexchange.com/questions/113695/gif-screencasting-the-unix-way
# Failsafe, just in case interactive stop fails, recording can be large
# If you are goint to pause consider increasing it as clock is still ticking while paused
DURATION=40;
# Destination for output gif file
GIF_OUT_FOLDER="/home/$USER/Downloads"
GIF_OUT_FILE=$GIF_OUT_FOLDER/$(date +'%Y-%m-%d--%H%M').gif
# Request section to record interactively
read -r X Y W H G ID < <(slop -f "%x %y %w %h %g %i")
# Create the name for the temp file with video
TMP_AVI=$(mktemp /tmp/outXXXXXXXXXX.avi)
# Export variables
export TMP_AVI DURATION GIF_OUT_FOLDER GIF_OUT_FILE X Y W H G ID
# Define functions and export them
#--------------------------------------------
stop_recording_and_create_gif() {
killall --user $USER --ignore-case --signal INT ffmpeg ; convert -layers removeDups -layers Optimize -delay 13 -loop 0 $TMP_AVI $GIF_OUT_FILE && rm -I "$TMP_AVI";
# Optimize gif file
gifsicle --verbose --batch --interlace $GIF_OUT_FILE --optimize=05 --colors=128; # --output out.gif
}
export -f stop_recording_and_create_gif
#--------------------------------------------
start_recording_screen_section () {
# Record and convert to gif file
#3 not-original / added -t n for n seconds recording
ffmpeg -t $DURATION -s "$W"x"$H" -y -f x11grab -i :0.0+$X,$Y -vcodec huffyuv -r 25 $TMP_AVI;
}
export -f start_recording_screen_section
#--------------------------------------------
FContinue () { killall --user $USER --ignore-case --signal SIGCONT ffmpeg ;}
FPause () { killall --user $USER --ignore-case --signal SIGSTOP ffmpeg ; }
export -f FContinue FPause
####################################
# Collect the YAD options
cmd=(yad
--width 5
--height 5
--on-top
--skip-taskbar
--borders=0
#--undecorated (so you have something to grab it if you want to move it)
--columns 1
--no-focus
#--mouse (it gets a bit inside the recording window)
--geometry=5x5+$(($X+$W+10))+$(($Y+$H+10))
#--no-buttons (avoids having buttons we don't need)
# This is a trick to have the buttons vertical
--form
--title="Screencast to GIF"
--field='Start recording!stop!Start recording (maximum defined duration)':fbtn 'bash -c "start_recording_screen_section"'
--field='Stop recording!gtk-quit!Stop recording and create gif file in output folder':fbtn 'bash -c "stop_recording_and_create_gif & kill -SIGUSR1 $YAD_PID"'
--field='Pause!gtk-pause!':fbtn 'bash -c "FPause"'
--field='Continue!gtk-continue!':fbtn 'bash -c "FContinue"'
--button='Quit!gtk-ok':'bash -c "kill -SIGUSR1 $YAD_PID"'
)
# Run yad
"${cmd[@]}"
# Cleanup before leaving
unset TMP_AVI DURATION GIF_OUT_FOLDER GIF_OUT_FILE X Y W H G ID
unset stop_recording_and_create_gif
unset start_recording_screen_section
unset FContinue FPause