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

Vous �tes nouveau sur Developpez.com ? Cr�ez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et �tre connect� pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Cr�ez-en un en quelques instants, c'est enti�rement gratuit !

Si vous disposez d�j� d'un compte et qu'il est bien activ�, connectez-vous � l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oubli� ?
Cr�er un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Produire ses vid�os (audio + vid�o synchronis�s ) sous Linux
Pour le prix de la webcam

Le , par ericb2

0PARTAGES

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
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.

Une erreur dans cette actualit� ? Signalez-nous-la !