Recherche avancée

Médias (91)

Autres articles (16)

  • Personnaliser en ajoutant son logo, sa bannière ou son image de fond

    5 septembre 2013, par

    Certains thèmes prennent en compte trois éléments de personnalisation : l’ajout d’un logo ; l’ajout d’une bannière l’ajout d’une image de fond ;

  • Ecrire une actualité

    21 juin 2013, par

    Présentez les changements dans votre MédiaSPIP ou les actualités de vos projets sur votre MédiaSPIP grâce à la rubrique actualités.
    Dans le thème par défaut spipeo de MédiaSPIP, les actualités sont affichées en bas de la page principale sous les éditoriaux.
    Vous pouvez personnaliser le formulaire de création d’une actualité.
    Formulaire de création d’une actualité Dans le cas d’un document de type actualité, les champs proposés par défaut sont : Date de publication ( personnaliser la date de publication ) (...)

  • Librairies et logiciels spécifiques aux médias

    10 décembre 2010, par

    Pour un fonctionnement correct et optimal, plusieurs choses sont à prendre en considération.
    Il est important, après avoir installé apache2, mysql et php5, d’installer d’autres logiciels nécessaires dont les installations sont décrites dans les liens afférants. Un ensemble de librairies multimedias (x264, libtheora, libvpx) utilisées pour l’encodage et le décodage des vidéos et sons afin de supporter le plus grand nombre de fichiers possibles. Cf. : ce tutoriel ; FFMpeg avec le maximum de décodeurs et (...)

Sur d’autres sites (4512)

  • record rtsp stream to file(muxing)

    11 avril 2014, par user3521863
    AVFormatContext *g_oc = NULL;
    AVStream *g_in_audio_st, *g_in_video_st;
    AVStream *g_out_audio_st, *g_out_video_st;
    int audio_pts = 0, video_pts = 0, audio_dts = 0, video_dts = 0;
    int last_video_pts = 0;
    AVPacket outpkt, *av_pkt;

    // initialize video codec
    static void init_video_codec(AVFormatContext *context) {
       LOGI(1, "enter init_video_codec");
       AVFormatContext *in_format_ctx = NULL;
       AVCodecContext *avcodec_ctx = NULL;
       int fps = 0;

       if(context->streams[1]->r_frame_rate.num != AV_NOPTS_VALUE &&
               context->streams[1]->r_frame_rate.den != 0)
           fps = context->streams[1]->r_frame_rate.num / context->streams[1]->r_frame_rate.den;
       else
           fps = 25;

       g_out_video_st = avformat_new_stream(g_oc, context->streams[1]->codec->codec);
       LOGI(1, "video avformat_new_stream");
       if( g_out_video_st == NULL ) {
           LOGE(1, "Fail to Allocate Output Video Stream");
           return ;
       }
       else {
           LOGI(1, "Allocated Video Stream");
           if( avcodec_copy_context(g_out_video_st->codec, context->streams[1]->codec) != 0 ) {
               LOGE(1, "Failed to video Copy Context");

               return ;
           }
           else {
               LOGI(1, "Success to video Copy Context");
    // how to setting video stream parameter?

               g_out_video_st->sample_aspect_ratio.den = g_in_video_st->codec->sample_aspect_ratio.den;
               g_out_video_st->sample_aspect_ratio.num = g_in_video_st->codec->sample_aspect_ratio.num;
               g_out_video_st->codec->codec_id         = g_in_video_st->codec->codec->id;
               g_out_video_st->codec->time_base.num    = 1;
               g_out_video_st->codec->time_base.den    = fps * (g_in_video_st->codec->ticks_per_frame);
               g_out_video_st->time_base.num           = 1;
               g_out_video_st->time_base.den           = 1000;
               g_out_video_st->r_frame_rate.num        = fps;
               g_out_video_st->r_frame_rate.den        = 1;
               g_out_video_st->avg_frame_rate.den      = 1;
               g_out_video_st->avg_frame_rate.num      = fps;
               g_out_video_st->codec->width            = g_frame_width;
               g_out_video_st->codec->height           = g_frame_height;
               g_out_video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
           }
       }

       LOGI(1, "end video init");
    }

    // initialize audio codec
    static void init_audio_codec(AVFormatContext *context) {
       LOGI(1, "enter init_audio_codec");
       AVFormatContext *in_format_ctx = NULL;
       AVCodecContext *avcodec_ctx = NULL;

       g_out_audio_st = avformat_new_stream(g_oc, context->streams[0]->codec->codec);
       LOGI(1, "audio avformat_new_stream");
       if( avcodec_copy_context(g_out_audio_st->codec, context->streams[0]->codec) != 0 ) {
           LOGE(1, "Failed to Copy audio Context");

           return ;
       }
       else {
           LOGI(1, "Success to Copy audio Context");
    // how to setting video stream parameter?
           g_out_audio_st->codec->codec_id         = g_in_audio_st->codec->codec_id;
           g_out_audio_st->codec->codec_tag        = 0;
           g_out_audio_st->pts                     = g_in_audio_st->pts;
           g_out_audio_st->time_base.num           = g_in_audio_st->time_base.num;
           g_out_audio_st->time_base.den           = g_in_audio_st->time_base.den;
           g_out_audio_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
       }

       LOGI(1, "end init audio");
    }

    // write video stream
    static void write_video_stream(AVPacket *pkt) {
       av_pkt = NULL;
       av_pkt = pkt;

       if( pkt == NULL || sizeof(*pkt) == 0 )
           return;

       av_rescale_q(av_pkt->pts, g_in_video_st->time_base, g_in_video_st->codec->time_base);
       av_rescale_q(av_pkt->dts, g_in_video_st->time_base, g_in_video_st->codec->time_base);

       av_init_packet(&outpkt);

       if( av_pkt->pts != AV_NOPTS_VALUE ) {
           if( last_video_pts == video_pts ) {
               video_pts++;
               last_video_pts = video_pts;
           }
           outpkt.pts = video_pts;
       }
       else {
           outpkt.pts = AV_NOPTS_VALUE;
       }

       if( av_pkt->dts == AV_NOPTS_VALUE )
           outpkt.dts = AV_NOPTS_VALUE;
       else
           outpkt.dts = video_pts;

       outpkt.data = av_pkt->data;
       outpkt.size = av_pkt->size;
       outpkt.stream_index = av_pkt->stream_index;
       outpkt.flags |= AV_PKT_FLAG_KEY;
       last_video_pts = video_pts;

       if(av_interleaved_write_frame(g_oc, &outpkt) < 0) {
    //  if(av_write_frame(g_oc, &outpkt) < 0) {
           LOGE(1, "Failed Video Write");
       }
       else {
           g_out_video_st->codec->frame_number++;
       }

       if( !&outpkt || sizeof(outpkt) == 0 )
           return;
       if( !av_pkt || sizeof(*av_pkt) == 0 )
           return;

       av_free_packet(&outpkt);
    }

    // write audio stream
    static void write_audio_stream(AVPacket *pkt) {
       av_pkt = NULL;
       av_pkt = pkt;

       if( pkt == NULL || sizeof(*pkt) == 0 )
               return;

       av_rescale_q(av_pkt->pts, g_in_audio_st->time_base, g_in_audio_st->codec->time_base);
       av_rescale_q(av_pkt->dts, g_in_audio_st->time_base, g_in_audio_st->codec->time_base);

       av_init_packet(&outpkt);

       if(av_pkt->pts != AV_NOPTS_VALUE)
           outpkt.pts = audio_pts;
       else
           outpkt.pts = AV_NOPTS_VALUE;

       if(av_pkt->dts == AV_NOPTS_VALUE)
           outpkt.dts = AV_NOPTS_VALUE;
       else {
           outpkt.dts = audio_pts;

           if( outpkt.pts >= outpkt.dts)
               outpkt.dts = outpkt.pts;

           if(outpkt.dts == audio_dts)
               outpkt.dts++;

           if(outpkt.pts < outpkt.dts) {
               outpkt.pts = outpkt.dts;
               audio_pts = outpkt.pts;
           }

           outpkt.data = av_pkt->data;
           outpkt.size = av_pkt->size;
           outpkt.stream_index = av_pkt->stream_index;
           outpkt.flags |= AV_PKT_FLAG_KEY;
           video_pts = audio_pts;
           audio_pts++;

           if( av_interleaved_write_frame(g_oc, &outpkt) < 0 ) {
    //      if( av_write_frame(g_oc, &outpkt) < 0 ) {
               LOGE(1, "Failed Audio Write");
           }
           else {
               g_out_audio_st->codec->frame_number++;
           }

           if( !&outpkt || sizeof(outpkt) == 0 )
               return;
           if( !av_pkt || sizeof(*av_pkt) == 0 )
               return;

           av_free_packet(&outpkt);
       }
    }

    here result : recorded file
    here full source : player.c

    I want to record rtsp stream to file on playing
    i try tested video and audio streams while changing the parameters
    but this result file does not match sync between video and audio
    i try search about ffmpeg but almost command run or video recording was only.
    please advice me.

  • Your guide to cookies, web analytics, and GDPR compliance

    25 février 2020, par Joselyn Khor — Analytics Tips, Privacy, Security

    It’s been almost two years since the GDPR came into effect and turned the online world on its head. Confusion around cookies/cookie consent/cookie compliance remains till today. So we’d like to take this chance to talk more about the supposed “big bad” of the latest century. 

    Online cookies seem to have a bad reputation, but are they as bad as they seem ?

    To start, what are cookies on the internet ?

    An internet cookie a.k.a. an HTTP cookie, is a small piece of data sent from websites that is stored on your computer or mobile when you visit that site.

    Are all cookies bad ?

    No. Cookies themselves are usually harmless as they can’t infect computers with malware. 

    They can also be helpful for both websites who use them and individuals visiting those websites. For example, when online shopping, cookies on ecommerce sites keep track of what you’re shopping for. If you didn’t have that tracking, your cart would be empty every time you moved away from that site.

    For businesses/websites, cookies can be used for authentication (logins) and tracking website user experience. For example, tracking multiple visits to the same site in order to provide better experiences to customers visiting their website.

    internet cookies tracking

    The not-so-sweet types of cookies :

    Cookies that contain personal data

    Another example of a bad cookie is when cookies contain personal data directly in the cookie itself. For example, when websites store demographics or your name in a cookie ; or when a website stores survey results in a cookie. Use of cookies in these ways is considered bad practice nowadays.

    Third-party cookies

    They can be used by websites to learn about your visit and activity across multiple websites. Cookies can enter harmful territory when employed for “big brother” types of tracking i.e. when they’re used to build a virtual fingerprint of individuals after their activity is tracked from website to website. For example most advertising networks create third party cookies in your browser when you view an ad, which lets these advertisers track users across these websites and let companies buy more targeted ads.

    Why does Matomo use cookies ?

    web analytics cookies

    For accurate reporting of new and returning visitors. Matomo uses cookies to store some information about visitors between visits. We also use cookies to remember if someone gave consent to tracking, or opted out of tracking. 

    Types of cookies Matomo uses :

    • Matomo by default uses first-party cookies, set on the domain of your site.
    • Cookies created by Matomo start with : _pk_ref_pk_cvar_pk_id_pk_ses. See a list of all Matomo cookies : https://matomo.org/faq/general/faq_146/

    Cookie-less tracking - disable cookies and ensure cookie compliance :

    It’s possible to disable tracking cookies in Matomo by adding a line on the javascript code. When cookies are disabled, Matomo data will become slightly less accurateAlso, when cookies are disabled, there may still be a few cookies created in specific cases.

    If you disable cookies, Matomo tries to detect unique visitors by a fingerprint based on a few browser attributes : operating system, browser, browser plugins, IP address and browser language.

    By disabling tracking cookies, you may also use Matomo without needing to display a cookie consent screen. You can also keep tracking when they reject cookie consent by keeping cookies disabled.

    Cookies and the GDPR

    In some countries and according to the GDPR, websites need to provide a way for users to opt-out of all tracking, in particular tracking cookies.

    The GDPR regulates the use of cookies when they compromise an individual’s privacy. When cookies can identify an individual, it is considered personal data.

    cookies and GDPR

    Cookie compliance and the GDPR

    To be GDPR compliant you must :

    • Receive user consent before using any cookies (except strictly necessary cookies). Read more on cookies that are “clearly exempt from consent”.
    • Provide accurate and specific information about the data each cookie tracks and its purpose in plain language before consent is received.
    • Document and store consent received from users.
    • Allow users to access your service even if they refuse to allow the use of certain cookies
    • Make it as easy for users to withdraw their consent as it was for them to give their consent in the first place.

    Source : https://gdpr.eu/cookies/

    When does GDPR require cookie consent ?

    The purpose of the GDPR is to give individuals control over their personal data. As such this regulation has provisions and requirements which regulate the processing of personal data to protect the privacy of individuals. 

    This means in order to use cookies, you will sometimes need explicit consent from those individuals.

    When does GDPR not require cookie consent ?

    Then there are many cookies that generally do NOT require consent (Source : https://wikis.ec.europa.eu/display/WEBGUIDE/04.+Cookies). 

    These are :

    • user input cookies, for the duration of a session
    • authentication cookies, for the duration of a session
    • user-centric security cookies, used to detect authentication abuses and linked to the functionality explicitly requested by the user, for a limited persistent duration
    • multimedia content player session cookies, such as flash player cookies, for the duration of a session
    • load balancing session cookies and other technical cookies, for the duration of session
    • user interface customisation cookies, for a browser session or a few hours, when additional information in a prominent location is provided (e.g. “uses cookies” written next to the customisation feature)

    Tracking cookies and consent vs legitimate interest

    cookie consent and GDPR legitimate interests

    User consent is not always required :

    We understand that whenever you collect and process personal data, you need – almost always – to ask for their consent. However, there are instances where you have to process data under “legitimate interests”. The GDPR states that processing of personal data is lawful “if processing is necessary for the purposes of the legitimate interests”. This means if you have “legitimate interests” you can avoid asking for consent for collecting and processing personal information. Learn more : https://cookieinformation.com/resources/blog/what-is-legitimate-interest-under-the-gdpr 

    A lawful basis for processing personal data (proceeding with caution) :

    We’ve also written about having a lawful basis for processing personal data under GDPR with Matomo. The caveat here is you need to have a strong argument for legitimate interests. If you are processing personal data which may represent a risk to the final user, then getting consent is, for us, still the right lawful basis. If you are not sure, at the time of writing ICO is providing a tool in order to help you make this decision.

    How is Matomo Analytics GDPR compliant ?

    Matomo can be configured to automatically anonymise data so you don’t process any personal data. This allows you to completely avoid GDPR. If you decide to process personal data, Matomo provides you with 12 steps to easily comply with the GDPR guidelines.

    New developments on cookies and the GDPR

    In the early days of the GDPR, a spate of cookie management platforms (CMPs) popped up to help websites and people comply with GDPR rules around cookies.

    These have become problematic in recent years. Europe’s highest court ruled pre-checked box for cookie boxes does not give enough consent

    As well as that, new research suggests most cookie consent pop-ups in the EU fall short of GDPR. A new study called, ‘Dark Patterns after the GDPR’ from MIT, UCL and Aarhus University found that a vast majority of websites aren’t following GDPR rules around cookies. The study found most cookie consent pop-ups in the EU to be undermining the GDPR by finding sneaky ways to convince website visitors to click ‘accept’.

    Disclaimer

    We are not lawyers and don’t claim to be. The information provided here is to help give an introduction to issues you may encounter when dealing cookies. We encourage every business and website to take data privacy seriously and discuss these issues with your lawyer if you have any concerns. 

    Additional resources :

  • Trying to cancel execution and delete file using ffmpeg C API

    6 mars 2020, par Vuwox

    The code below is a class that handle the conversion of multiples images, through add_frame() method, into a GIF with encode(). It also use a filter to generate and apply the palette. The usage is like this :

    Code call example

    std::unique_ptr gif_obj = nullptr;
    try
    {
       gif_obj = std::make_unique({1000,1000}, 12, "C:/out.gif",
                 "format=pix_fmts=rgb24,split [a][b];[a]palettegen[p];[b][p]paletteuse");

       // Example: a simple vector of images (usually process internally)
       for(auto img : image_vector)
            gif_obj->add_frame(img);

       // Once all frame were added, encode the final GIF with the filter applied.
       gif_obj->encode();
    }
    catch(const std::exception& e)
    {
       // An error occured! We must close FFMPEG properly and delete the created file.
       gif_obj->cancel();
    }

    I have the following issue. If the code for any reason throw an exception, I call ffmpeg->cancel() and it supposes to delete the GIF file on disk. But this is never working, I assume there is a lock on the file or something like that. So here are my question :

    What is the proper way to close/free ffmpeg object in order to remove the file afterward ?


    Full class code below

    Header

    // C++ Standard includes    
    #include <memory>
    #include <string>
    #include <vector>


    // 3rd Party incldues
    #ifdef __cplusplus
    extern "C" {
    #include "libavformat/avformat.h"
    #include "libavfilter/avfilter.h"
    #include "libavutil/opt.h"
    #include "libavfilter/buffersrc.h"
    #include "libavfilter/buffersink.h"
    #include "libswscale/swscale.h"
    #include "libavutil/imgutils.h"
    }
    #endif

    #define FFMPEG_MSG_LEN 2000

    namespace px
    {
       namespace GIF
       {
           class FFMPEG
           {
           public:
               FFMPEG(const px::Point2D<int>&amp; dim,
                      const int framerate,
                      const std::string&amp; filename,
                      const std::string&amp; filter_cmd);

               ~FFMPEG();

               void add_frame(pxImage * const img);
               void encode();
               void cancel();

           private:

               void init_filters();            // Init everything that needed to filter the input frame.
               void init_muxer();              // The muxer that creates the output file.
               void muxing_one_frame(AVFrame* frame);
               void release();

               int _ret = 0;                   // status code from FFMPEG.
               char _err_msg[FFMPEG_MSG_LEN];  // Error message buffer.


               int m_width = 0;                // The width that all futur images must have to be accepted.
               int m_height = 0;               // The height that all futur images must have to be accepted.

               int m_framerate = 0;            // GIF Framerate.
               std::string m_filename = "";    // The GIF filename (on cache?)
               std::string m_filter_desc = ""; // The FFMPEG filter to apply over the frames.

               bool as_frame = false;

               AVFrame* picture_rgb24 = nullptr;           // Temporary frame that will hold the pxImage in an RGB24 format (NOTE: TOP-LEFT origin)

               AVFormatContext* ofmt_ctx = nullptr;        // ouput format context associated to the
               AVCodecContext* o_codec_ctx = nullptr;      // output codec for the GIF

               AVFilterGraph* filter_graph = nullptr;      // filter graph associate with the string we want to execute
               AVFilterContext* buffersrc_ctx = nullptr;   // The buffer that will store all the frames in one place for the palette generation.
               AVFilterContext* buffersink_ctx = nullptr;  // The buffer that will store the result afterward (once the palette are used).

               int64_t m_pts_increment = 0;
           };
       };
    };
    </int></vector></string></memory>

    ctor

    px::GIF::FFMPEG::FFMPEG(const px::Point2D<int>&amp; dim,
                           const int framerate,
                           const std::string&amp; filename,
                           const std::string&amp; filter_cmd) :
       m_width(dim.x()),
       m_height(dim.y()),
       m_framerate(framerate),
       m_filename(filename),
       m_filter_desc(filter_cmd)
    {
    #if !_DEBUG
       av_log_set_level(AV_LOG_QUIET); // Set the FFMPEG log to quiet to avoid too much logs.
    #endif

       // Allocate the temporary buffer that hold the ffmpeg image (pxImage to AVFrame conversion).
       picture_rgb24 = av_frame_alloc();
       picture_rgb24->pts = 0;
       picture_rgb24->data[0] = NULL;
       picture_rgb24->linesize[0] = -1;
       picture_rgb24->format = AV_PIX_FMT_RGB24;
       picture_rgb24->height = m_height;
       picture_rgb24->width = m_width;

       if ((_ret = av_image_alloc(picture_rgb24->data, picture_rgb24->linesize, m_width, m_height, (AVPixelFormat)picture_rgb24->format, 24)) &lt; 0)
           throw px::GIF::Error("Failed to allocate the AVFrame for pxImage conversion with error: " +
                                std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)),
                                "GIF::FFMPEG CTOR");  

       //printf("allocated picture of size %d, linesize %d %d %d %d\n", _ret, picture_rgb24->linesize[0], picture_rgb24->linesize[1], picture_rgb24->linesize[2], picture_rgb24->linesize[3]);

       init_muxer();   // Prepare the GIF encoder (open it on disk).
       init_filters(); // Prepare the filter that will be applied over the frame.

       // Instead of hardcoder {1,100} which is the GIF tbn, we collect it from its stream.
       // This will avoid future problem if the codec change in ffmpeg.
       if (ofmt_ctx &amp;&amp; ofmt_ctx->nb_streams > 0)
           m_pts_increment = av_rescale_q(1, { 1, m_framerate }, ofmt_ctx->streams[0]->time_base);
       else
           m_pts_increment = av_rescale_q(1, { 1, m_framerate }, { 1, 100 });
    }
    </int>

    FFMPEG Initialization (Filter and muxer)

    void px::GIF::FFMPEG::init_filters()
    {
       const AVFilter* buffersrc = avfilter_get_by_name("buffer");
       const AVFilter* buffersink = avfilter_get_by_name("buffersink");

       AVRational time_base = { 1, m_framerate };
       AVRational aspect_pixel = { 1, 1 };

       AVFilterInOut* inputs = avfilter_inout_alloc();
       AVFilterInOut* outputs = avfilter_inout_alloc();

       filter_graph = avfilter_graph_alloc();

       try
       {
           if (!outputs || !inputs || !filter_graph)
               throw px::GIF::Error("Failed to 'init_filters' could not allocated the graph/filters.", "GIF::FFMPEG init_filters");

           char args[512];
           snprintf(args, sizeof(args),
                    "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
                    m_width, m_height,
                    picture_rgb24->format,
                    time_base.num, time_base.den,
                    aspect_pixel.num, aspect_pixel.den);

           if (avfilter_graph_create_filter(&amp;buffersrc_ctx, buffersrc, "in", args, nullptr, filter_graph) &lt; 0)
               throw px::GIF::Error("Failed to create the 'source buffer' in init_filer method.", "GIF::FFMPEG init_filters");


           if (avfilter_graph_create_filter(&amp;buffersink_ctx, buffersink, "out", nullptr, nullptr, filter_graph) &lt; 0)
               throw px::GIF::Error("Failed to create the 'sink buffer' in init_filer method.", "GIF::FFMPEG init_filters");

           // GIF has possible output of PAL8.
           enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_PAL8, AV_PIX_FMT_NONE };

           if (av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN) &lt; 0)
               throw px::GIF::Error("Failed to set the output pixel format.", "GIF::FFMPEG init_filters");

           outputs->name = av_strdup("in");
           outputs->filter_ctx = buffersrc_ctx;
           outputs->pad_idx = 0;
           outputs->next = nullptr;

           inputs->name = av_strdup("out");
           inputs->filter_ctx = buffersink_ctx;
           inputs->pad_idx = 0;
           inputs->next = nullptr;

           // GIF has possible output of PAL8.
           if (avfilter_graph_parse_ptr(filter_graph, m_filter_desc.c_str(), &amp;inputs, &amp;outputs, nullptr) &lt; 0)
               throw px::GIF::Error("Failed to parse the filter graph (bad string!).", "GIF::FFMPEG init_filters");

           if (avfilter_graph_config(filter_graph, nullptr) &lt; 0)
               throw px::GIF::Error("Failed to configure the filter graph (bad string!).", "GIF::FFMPEG init_filters");

           avfilter_inout_free(&amp;inputs);
           avfilter_inout_free(&amp;outputs);
       }
       catch (const std::exception&amp; e)
       {
           // Catch exception to delete element.
           avfilter_inout_free(&amp;inputs);
           avfilter_inout_free(&amp;outputs);
           throw e; // re-throuw
       }
    }


    void px::GIF::FFMPEG::init_muxer()
    {
       AVOutputFormat* o_fmt = av_guess_format("gif", m_filename.c_str(), "video/gif");

       if ((_ret = avformat_alloc_output_context2(&amp;ofmt_ctx, o_fmt, "gif", m_filename.c_str())) &lt; 0)
           throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " allocate output format.", "GIF::FFMPEG init_muxer");

       AVCodec* codec = avcodec_find_encoder(AV_CODEC_ID_GIF);
       if (!codec) throw px::GIF::Error("Could to find the 'GIF' codec.", "GIF::FFMPEG init_muxer");

    #if 0
       const AVPixelFormat* p = codec->pix_fmts;
       while (p != NULL &amp;&amp; *p != AV_PIX_FMT_NONE) {
           printf("supported pix fmt: %s\n", av_get_pix_fmt_name(*p));
           ++p;
       }
    #endif

       AVStream* stream = avformat_new_stream(ofmt_ctx, codec);

       AVCodecParameters* codec_paramters = stream->codecpar;
       codec_paramters->codec_tag = 0;
       codec_paramters->codec_id = codec->id;
       codec_paramters->codec_type = AVMEDIA_TYPE_VIDEO;
       codec_paramters->width = m_width;
       codec_paramters->height = m_height;
       codec_paramters->format = AV_PIX_FMT_PAL8;

       o_codec_ctx = avcodec_alloc_context3(codec);
       avcodec_parameters_to_context(o_codec_ctx, codec_paramters);

       o_codec_ctx->time_base = { 1, m_framerate };

       if (ofmt_ctx->oformat->flags &amp; AVFMT_GLOBALHEADER)
           o_codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

       if ((_ret = avcodec_open2(o_codec_ctx, codec, NULL)) &lt; 0)
           throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " open output codec.", "GIF::FFMPEG init_muxer");

       if ((_ret = avio_open(&amp;ofmt_ctx->pb, m_filename.c_str(), AVIO_FLAG_WRITE)) &lt; 0)
           throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " avio open error.", "GIF::FFMPEG init_muxer");

       if ((_ret = avformat_write_header(ofmt_ctx, NULL)) &lt; 0)
           throw px::GIF::Error(std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)) + " write GIF header", "GIF::FFMPEG init_muxer");

    #if _DEBUG
       // This print the stream/output format.
       av_dump_format(ofmt_ctx, -1, m_filename.c_str(), 1);
    #endif
    }

    Add frame (usually in a loop)

    void px::GIF::FFMPEG::add_frame(pxImage * const img)
    {
       if (img->getImageType() != PXT_BYTE || img->getNChannels() != 4)
           throw px::GIF::Error("Failed to 'add_frame' since image is not PXT_BYTE and 4-channels.", "GIF::FFMPEG add_frame");

       if (img->getWidth() != m_width || img->getHeight() != m_height)
           throw px::GIF::Error("Failed to 'add_frame' since the size is not same to other inputs.", "GIF::FFMPEG add_frame");

       const int pitch = picture_rgb24->linesize[0];
       auto px_ptr = getImageAccessor(img);

       for (int y = 0; y &lt; m_height; y++)
       {
           const int px_row = img->getOrigin() == ORIGIN_BOT_LEFT ? m_height - y - 1 : y;
           for (int x = 0; x &lt; m_width; x++)
           {
               const int idx = y * pitch + 3 * x;
               picture_rgb24->data[0][idx] = px_ptr[px_row][x].ch[PX_RE];
               picture_rgb24->data[0][idx + 1] = px_ptr[px_row][x].ch[PX_GR];
               picture_rgb24->data[0][idx + 2] = px_ptr[px_row][x].ch[PX_BL];
           }
       }

       // palettegen need a whole stream, just add frame to buffer.
       if ((_ret = av_buffersrc_add_frame_flags(buffersrc_ctx, picture_rgb24, AV_BUFFERSRC_FLAG_KEEP_REF)) &lt; 0)
           throw px::GIF::Error("Failed to 'add_frame' to global buffer with error: " +
                                std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)),
                                "GIF::FFMPEG add_frame");

       // Increment the FPS of the picture for the next add-up to the buffer.      
       picture_rgb24->pts += m_pts_increment;

       as_frame = true;
    }    

    Encoder (final step)

    void px::GIF::FFMPEG::encode()
    {
       if (!as_frame)
           throw px::GIF::Error("Please 'add_frame' before running the Encoding().", "GIF::FFMPEG encode");

       // end of buffer
       if ((_ret = av_buffersrc_add_frame_flags(buffersrc_ctx, nullptr, AV_BUFFERSRC_FLAG_KEEP_REF)) &lt; 0)
           throw px::GIF::Error("error add frame to buffer source: " + std::string(av_make_error_string(_err_msg, FFMPEG_MSG_LEN, _ret)), "GIF::FFMPEG encode");

       do {
           AVFrame* filter_frame = av_frame_alloc();
           _ret = av_buffersink_get_frame(buffersink_ctx, filter_frame);
           if (_ret == AVERROR(EAGAIN) || _ret == AVERROR_EOF) {
               av_frame_unref(filter_frame);
               break;
           }

           // write the filter frame to output file
           muxing_one_frame(filter_frame);

           av_frame_unref(filter_frame);
       } while (_ret >= 0);

       av_write_trailer(ofmt_ctx);
    }

    void px::GIF::FFMPEG::muxing_one_frame(AVFrame* frame)
    {
       int ret = avcodec_send_frame(o_codec_ctx, frame);
       AVPacket *pkt = av_packet_alloc();
       av_init_packet(pkt);

       while (ret >= 0) {
           ret = avcodec_receive_packet(o_codec_ctx, pkt);
           if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
               break;
           }

           av_write_frame(ofmt_ctx, pkt);
       }
       av_packet_unref(pkt);
    }

    DTOR, Release and Cancel

    px::GIF::FFMPEG::~FFMPEG()
    {
       release();
    }


    void px::GIF::FFMPEG::release()
    {
       // Muxer stuffs
       if (ofmt_ctx != nullptr) avformat_free_context(ofmt_ctx);
       if (o_codec_ctx != nullptr) avcodec_close(o_codec_ctx);
       if (o_codec_ctx != nullptr) avcodec_free_context(&amp;o_codec_ctx);

       ofmt_ctx = nullptr;
       o_codec_ctx = nullptr;

       // Filter stuffs
       if (buffersrc_ctx != nullptr) avfilter_free(buffersrc_ctx);
       if (buffersink_ctx != nullptr) avfilter_free(buffersink_ctx);
       if (filter_graph != nullptr) avfilter_graph_free(&amp;filter_graph);

       buffersrc_ctx = nullptr;
       buffersink_ctx = nullptr;
       filter_graph = nullptr;

       // Conversion image.
       if (picture_rgb24 != nullptr) av_frame_free(&amp;picture_rgb24);
       picture_rgb24 = nullptr;
    }

    void px::GIF::FFMPEG::cancel()
    {
       // In-case of failure we must close ffmpeg and exit.
       av_write_trailer(ofmt_ctx);

       // Release and close all elements.
       release();

       // Delete the file on disk.
       if (remove(m_filename.c_str()) != 0)
           PX_LOG0(PX_LOGLEVEL_ERROR, "GIF::FFMPEG - On 'cancel' failed to remove the file.");
    }