par , 03/06/2020 � 18h23 (5186 Affichages)
Pour ceux que cela int�resse, j'ai repris un projet (on dit "fork�") et j'ai ajout� ce dont j'avais besoin pour cr�er ffmpeg-cpp2 sous Linux.
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 :
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 :
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 :
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 :
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 :
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 !!