IdentifiantMot de passe
Loading...
Mot de passe oubli� ?Je m'inscris ! (gratuit)
Voir le flux RSS

ericb2

[Actualit�] Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam

Noter ce billet
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 :

Code : S�lectionner tout - Visualiser dans une fen�tre � part
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 - Visualiser dans une fen�tre � part
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 - Visualiser dans une fen�tre � part
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 - Visualiser dans une fen�tre � part
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 - Visualiser dans une fen�tre � part
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 !!

Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog Viadeo Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog Twitter Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog Google Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog Facebook Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog Digg Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog Delicious Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog MySpace Envoyer le billet � Produire ses vid�os (audio + vid�o synchronis�s) sous Linux, pour le prix de la webcam � dans le blog Yahoo

Mis � jour 05/06/2020 � 08h37 par LittleWhite (Mise en forme)

Cat�gories
Programmation , C , C++

Commentaires

  1. Avatar de lilivve
    • |
    • permalink
    Bonjour Eric,

    [Comme je te le disais ailleurs] j'ai trouv� difficile il y a quelques temps d'utiliser ffmpeg dans un projet en C++.

    Ma difficult� tenait � deux choses :
    - Utiliser une API en C, n'�tant pas familier de la programmation en C. M�me si c'est loin d'�tre insurmontable si on est habitu� au C++, c'�tait quand m�me un peu d�routant.
    - Le manque de documentation concernant ffmpeg. J'ai peut-�tre rat� quelque chose, mais je garde le souvenir d'avoir surtout trouv� des exemples (pas tant que cela) que j'essayais d'adapter, avec le sentiment d'�tre un peu dans l'obscurit�. J'aurais aim� trouver une vue d'ensemble du fonctionnement et de la logique de ffmpeg sans avoir besoin de plonger direct dans son code. J'ai �t� tr�s �tonn� de ne rien trouver de mieux, ffmpeg est un formidable outil, utilis� dans pas mal de logiciels, je me suis demand� comment s'en sortait les autres d�veloppeurs.

    J'ai l'impression que si un jour j'ai nouveau besoin de faire de l'encodage vid�o dans un programme ton wrapper pourra m"aider (je dis wrapper mais j'ai l'impression que c'est un peu plus non ?), d'autant plus qu'il est fourni avec de nombreux exemples. Je t'encourage donc � continuer, et merci pour ce partage.
  2. Avatar de ericb2
    • |
    • permalink
    Citation Envoy� par lilivve
    Bonjour Eric,

    [Comme je te le disais ailleurs] j'ai trouv� difficile il y a quelques temps d'utiliser ffmpeg dans un projet en C++.

    Ma difficult� tenait � deux choses :
    - Utiliser une API en C, n'�tant pas familier de la programmation en C. M�me si c'est loin d'�tre insurmontable si on est habitu� au C++, c'�tait quand m�me un peu d�routant.
    - Le manque de documentation concernant ffmpeg. J'ai peut-�tre rat� quelque chose, mais je garde le souvenir d'avoir surtout trouv� des exemples (pas tant que cela) que j'essayais d'adapter, avec le sentiment d'�tre un peu dans l'obscurit�. J'aurais aim� trouver une vue d'ensemble du fonctionnement et de la logique de ffmpeg sans avoir besoin de plonger direct dans son code. J'ai �t� tr�s �tonn� de ne rien trouver de mieux, ffmpeg est un formidable outil, utilis� dans pas mal de logiciels, je me suis demand� comment s'en sortait les autres d�veloppeurs.

    J'ai l'impression que si un jour j'ai nouveau besoin de faire de l'encodage vid�o dans un programme ton wrapper pourra m"aider (je dis wrapper mais j'ai l'impression que c'est un peu plus non ?), d'autant plus qu'il est fourni avec de nombreux exemples. Je t'encourage donc � continuer, et merci pour ce partage.
    Pour �tre honn�te, c'est surtout celui qui a cr�� ffmpeg-cpp qui a bien boss�. Mais la version Linux n'avan�ait pas (malgr� les demandes) et le code n'est pas si clean (par exemple, pb de visibilit� avec les contextes AVFormat, qui n'est toujours pas r�solu , ce qui m'a forc� � utiliser 2 threads). Il reste aussi plein de warnings, avec des re-d�finitions excessives qui doivent encore causer quelques bugs.

    Mais tu as raison, ffmpeg est plus compliqu� � utiliser sous forme C/C++ qu'en script. En ce qui me concerne, c'est en aidant plein de monde � corriger des warnings (par exemple : https://github.com/starkdg/phvideoca...8ac0520f86359c) et en faisant des essais que j'ai commenc� � y voir plus clair. Remarque : �a fait quand m�me plus d'un an que je suis dedans.

    Et c'est le fait de pouvoir utiliser des "briques" pour extraire / associer du son, des images m'a d�cid� � l'utiliser, car quand on n'a plus � mettre les mains dans le cambouis, c'est un vrai plaisir.

    Si tu as besoin d'aide, pas de probl�me : attention, la plupart des d�mos fonctionnent bien, mais les r�sultats demandent quelques ajustement (en particulier MPEG2). En cas de besoin, tu ouvres une issue sur le framagit et je verrai si je peux aider. amha, le truc, c'est de bien d�finir ce que tu veux faire, et le reste, c'est normalement facile � faire.

    Ce qui m'impressionne le plus, ce sont les filtres :

    - rotation 90�, crop, zoom : �a fonctionne vraiment comme avec ffmpeg, mais dans le code (un vrai plaisir). Le gars qui a impl�ment� �a (ce n'est pas moi) a vraiment �t� bon.

    Pour la suite, je compte impl�menter VAAPI : si j'ai bien compris, il me manque la cr�ation d'un contexte hardware, et l'acc�l�ration mat�rielle (Intel seulement) devrait fonctionner quasi directement, toujours avec les filtres.


    En tout cas, merci encore pour le retour et les encouragements :-)

    Edit : correction du lien
    Mis � jour 04/06/2020 � 17h03 par ericb2