
Il s'agit d'une petite API en C++ qui encapsule les biblioth�ques qui constituent ffmpeg (libavformat, libavutil, libavdevice, libavcodec, ...)
L'utilisation basique de la webcam est donn�e dans la petite d�mo qui s'appelle remux_webcam (voir ci-dessous). Mais les filtres, dont je ne parle pas ici, c'est encore plus g�nial !
L'API est bas�e sur la notion de flux entr�e/sortie :
- source audio : raw (par exemple venant de la webcam), ou frame (bande son venant d'une vid�o donn�e ;
- source vid�o (un fichier, un flux r�seau, un flux raw (venant d'une webcam)
- l'extraction du son ou de l'image ;
- le filtrage (plus de 100 filtres, issus de ffmpeg : rotation, zoom, crop etc) ;
- le d�multiplexage : on extrait le son ou l'image d'un flux (le d�codage est automatique) ;
- on peut ensuite m�langer les flux que l'on a extrait avec un multiplexeur.
En gros, on peut faire ce qu'on veut, quelle que soit la source, et SANS �tre oblig� d'utiliser l'interface graphique du d�veloppeur. Comme �a, vous fa�tes ce que vous voulez.
Exemple : j'ai associ� la bande son de Hells Bells (AC-DC) au d�but de la vid�o BigBuckBunny. �a rend pas mal du tout :-)
Le d�p�t (framagit) est l� : ffmpeg-cpp2 sur framagit
Afin de prot�ger mon travail, j'ai mis la licence GPL V3, mais je pense repasser un jour sous LGPL.
Dans les d�mos, on a plein d'exemples qui devraient fonctionner tout droit.
Voir : d�mos
Application � la webcam :
- j'utilise alsa + v4l2 sous Linux (sous Windows, �a pourrait �tre dshow � la place de v4l2, et DirectSound � la place d'alsa)
- le d�multiplexeur pour la webcam (partie vid�o) est le m�me que pour les sources type fichier, mais j'ai surcharg� le constructeur, pour des raisons d'utilisabilit�
- pour le son, j'ai impl�ment� une nouvelle classe, d�rivant d'une source classique
Les probl�mes rencontr�s : la synchronisation, et la bande son qui ne durait quasiment pas. Le probl�me venait du fait qu'il fallait deux fils d'ex�cution s�par�s pour le son et la vid�o sur la m�me webcam. Sinon alsa (avec pulse ou hw:1,0 etc) fonctionne super bien (j'utilise Linuxmint)
Les d�pendances :
- ffmpeg (toutes les biblioth�ques associ�es en fait. J'utilise la version 4.2.2
- la libpthread (pour l'enregistrement s�par� son / image synchrone)
- certaines biblioth�ques (mais on peut s'en passer) de codecs (h264, liblamemp3, libvpx-vp9, etc
- v4l2 , qui est l'API standard pour Linux
- la libasound (biblioth�que alsasound)
- la biblioth�que c++ standard : std::chrono (pour l'instant, on enregistre une dur�e donn�e, mais quand il y aura un bouton start/stop, la dur�e ne sera plus un probl�me)
- std::fstream pour la suppression des fichiers audio et vid�o interm�diaires
TODO : impl�menter VAAPI pour le d�codage et l'encodage
Le contenu du programme est trivial, mais pour une vraie application, j'aurais cr�� des nouvelles classes (TODO). Les commentaires sont dans le code.
Initialisation :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | /* * File remux_webcam.cpp * Copyright Eric Bachard / 2020 05 08 * This document is under GPL v3 license * see : http://www.gnu.org/licenses/gpl-3.0.html */ #include <iostream> #include <chrono> #include <ffmpegcpp.h> #include <thread> // std::thread #include <fstream> // std::remove static bool bRecording = false; using namespace ffmpegcpp; using std::string; using std::cerr; //#ifdef ALSA_BUFFER_SIZE_MAX #undef ALSA_BUFFER_SIZE_MAX #define ALSA_BUFFER_SIZE_MAX 524288 // les fichiers temporaires contenant le son et les images const char * audio_file = "../videos/audio.mp4"; // aac (s32le ?) const char * video_file = "../videos/video_H264.mp4"; // h264 |
Enregistrement Audio :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | void record_Audio() { const char * audioDevice = "pulse"; // fonctionne avec pulseaudio ici //const char * audioDevice = "hw:1,0"; const char * audioDeviceFormat = "alsa"; // on cr�e une instance du container audio Muxer* Amuxer = new Muxer(audio_file); // param�tres usuels int audioSampleRate = 44100; int audioChannels = 2; // on choisit d'encoder le son avec le codec aac, avec une instance de l'encodeur qui suit AudioCodec * audioCodec = new AudioCodec(AV_CODEC_ID_AAC); AudioEncoder * audioEncoder = new AudioEncoder(audioCodec, Amuxer); // d�finition de la source audio (voir le constructeur pour retrouver le m�canisme // habituel de ffmpeg RawAudioFileSource * audioFile = new RawAudioFileSource( audioDevice, audioDeviceFormat, audioSampleRate, audioChannels, audioEncoder); // pr�paration audioFile->PreparePipeline(); // bRecording est une variable globale, car on doit pouvoir terminer le fil d'ex�cution while (!audioFile->IsDone()) { audioFile->Step(); if (bRecording == false) audioFile->Stop(); } Amuxer->Close(); if (audioEncoder != nullptr) delete audioEncoder; delete Amuxer; } |
M�me chose pour la vid�o :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | void record_Video() { int width = 1280; // 1920; int height = 720; // 1080; int fps = 24; // Logitech prefered fps value AVRational frameRate = { 24, 1 }; // These are example video and audio sources used below. const char * videoDevice = "/dev/video0"; AVPixelFormat outputPixFormat= AV_PIX_FMT_NV12; Muxer* Vmuxer = new Muxer(video_file); H264Codec * vcodec = new H264Codec(); VideoEncoder * videoEncoder = new VideoEncoder(vcodec, Vmuxer, frameRate, outputPixFormat); Demuxer * demuxer = new Demuxer(videoDevice, width, height, fps); demuxer->DecodeBestVideoStream(videoEncoder); demuxer->PreparePipeline(); while (!demuxer->IsDone()) { demuxer->Step(); if (bRecording == false) { demuxer->Stop(); } } // close the first muxers and save separately audio and video files to disk Vmuxer->Close(); if (videoEncoder != nullptr) delete videoEncoder; delete Vmuxer; } |
Cr�ation et assemblage de la vid�o finale :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | void create_final_Video() { const char * final_file = "../videos/final_video.mp4"; // h264 + aac (or vp9 + aac) Muxer* AVmuxer = new Muxer(final_file); AudioCodec * faudioCodec = new AudioCodec(AV_CODEC_ID_AAC); H264Codec * fvcodec = new H264Codec(); try { // Create encoders for both VideoEncoder* fvideoEncoder = new VideoEncoder(fvcodec, AVmuxer); AudioEncoder* faudioEncoder = new AudioEncoder(faudioCodec, AVmuxer); // Load both audio and video from a container Demuxer* videoContainer = new Demuxer(video_file); Demuxer* audioContainer = new Demuxer(audio_file); // Tie the best stream from each container to the output videoContainer->DecodeBestVideoStream(fvideoEncoder); audioContainer->DecodeBestAudioStream(faudioEncoder); // Prepare the pipeline. We want to call this before the rest of the loop // to ensure that the muxer will be fully ready to receive data from // multiple sources. videoContainer->PreparePipeline(); audioContainer->PreparePipeline(); // Pump the audio and video fully through. // To avoid big buffers, we interleave these calls so that the container // can be written to disk efficiently. while ( (!videoContainer->IsDone()) || (!audioContainer->IsDone())) { if (!videoContainer->IsDone()) videoContainer->Step(); if (!audioContainer->IsDone()) audioContainer->Step(); } // Save everything to disk by closing the muxer. AVmuxer->Close(); } catch (FFmpegException e) { cerr << e.what() << "\n"; throw e; } delete AVmuxer; } |
Et enfin, le programme principal :
Code : | S�lectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | int main(void) { // obligatoire sinon la webcam n'est pas intialis�e correctement avdevice_register_all(); //avformat_network_init(); // future use // globale, permet de terminer proprement les 2 fils audio et vid�o bRecording = true; // on d�marre l'enregistrement, en appelant s�par�ment les deux // process qui seront de fait quasi-synchronis�s std::thread first (record_Audio); std::thread second (record_Video); // on va enregistrer 1 min = 60 secondes auto start = std::chrono::steady_clock::now(); auto current_time = std::chrono::steady_clock::now(); std::chrono::duration<double> elapsed_seconds = current_time - start; do { current_time = std::chrono::steady_clock::now(); elapsed_seconds = current_time - start; } while ((elapsed_seconds.count()) < (60)); // on annonce aux fils d'ex�cution qu'il faut terminer bRecording = false; // on recolle les morceaux, ce qui donne le temps de finaliser les // 2 fichiers temporaires, et de ne pas utiliser des fichiers en cours d'�criture first.join(); second.join(); // assemblage des 2 fichiers temporaires (audio / vid�o) create_final_Video(); // c'est fini !! std::cout << "Encoding complete!" << "\n"; // il peut �tre int�ressant de conserver les fichiers temporaires #define TEST #ifdef TEST std::remove(audio_file); std::remove(video_file); bool failed = (std::ifstream(audio_file) || std::ifstream(video_file)); if(failed) { std::perror("Error opening deleted file"); return 1; } #endif return 0; } |
Toute retour d'exp�rience et/ou aide est bienvenu, �videmment !!
Vous avez lu gratuitement 2 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer � vous proposer des publications.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer � vous proposer des publications.