Recherche avancée

Médias (91)

Autres articles (68)

  • MediaSPIP v0.2

    21 juin 2013, par

    MediaSPIP 0.2 est la première version de MediaSPIP stable.
    Sa date de sortie officielle est le 21 juin 2013 et est annoncée ici.
    Le fichier zip ici présent contient uniquement les sources de MediaSPIP en version standalone.
    Comme pour la version précédente, il est nécessaire d’installer manuellement l’ensemble des dépendances logicielles sur le serveur.
    Si vous souhaitez utiliser cette archive pour une installation en mode ferme, il vous faudra également procéder à d’autres modifications (...)

  • Mise à disposition des fichiers

    14 avril 2011, par

    Par défaut, lors de son initialisation, MediaSPIP ne permet pas aux visiteurs de télécharger les fichiers qu’ils soient originaux ou le résultat de leur transformation ou encodage. Il permet uniquement de les visualiser.
    Cependant, il est possible et facile d’autoriser les visiteurs à avoir accès à ces documents et ce sous différentes formes.
    Tout cela se passe dans la page de configuration du squelette. Il vous faut aller dans l’espace d’administration du canal, et choisir dans la navigation (...)

  • MediaSPIP version 0.1 Beta

    16 avril 2011, par

    MediaSPIP 0.1 beta est la première version de MediaSPIP décrétée comme "utilisable".
    Le fichier zip ici présent contient uniquement les sources de MediaSPIP en version standalone.
    Pour avoir une installation fonctionnelle, il est nécessaire d’installer manuellement l’ensemble des dépendances logicielles sur le serveur.
    Si vous souhaitez utiliser cette archive pour une installation en mode ferme, il vous faudra également procéder à d’autres modifications (...)

Sur d’autres sites (5160)

  • Sending raw h264 video and aac audio frames to an RTMP server using ffmpeg

    7 décembre 2022, par codeimpaler

    I am receiving raw h264 and aac audio frames from an even driven source. I am trying to send these frames to an rtmp server. 
I started working from the ffmpeg example muxing.c which successfully sends a custom stream to the rtmp server. I figure I just need to replace their frame data with my own.I found this suggestion online. I have tried How to pack raw h264 stream to flv container and send over rtmp using ffmpeg (not command) 
and
How to publish selfmade stream with ffmpeg and c++ to rtmp server ?
and a few other suggestions but none have worked for me. 
I have tried to directly memcpy my byte buffer but my code keeps failing
at ret = avcodec_encode_video2(c, &pkt, frame, &got_packet).
Specifically, I get an invalid access error.
For a little more context, anytime I receive a frame (which is event driven), void RTMPWriter::WriteVideoFrame(...) is called. Assume the constructor has already been called before the first frame is received. 
I am not that familiar with ffmpeg and there could be several things wrong with the code. Any input will be really appreciated.

    



        #define STREAM_FRAME_RATE 25 /* 25 images/s */
    #define STREAM_PIX_FMT    AV_PIX_FMT_YUV420P /* default pix_fmt */
    #define SCALE_FLAGS SWS_BICUBIC
    RTMPWriter::RTMPWriter()
      : seenKeyFrame(false),
        video_st({ 0 }), 
        audio_st({ 0 }),
        have_video(0), 
        have_audio(0)
    {

        const char *filename;
        AVCodec *audio_codec = NULL, *video_codec = NULL;
        int ret;

        int encode_video = 0, encode_audio = 0;
        AVDictionary *opt = NULL;
        int i;

        /* Initialize libavcodec, and register all codecs and formats. */
        av_register_all();

        avformat_network_init();

       String^ StreamURL = "StreamURL";
       String^ out_uri = safe_cast(ApplicationData::Current->LocalSettings->Values->Lookup(StreamURL));
       std::wstring out_uriW(out_uri->Begin());
       std::string out_uriA(out_uriW.begin(), out_uriW.end());
       filename = out_uriA.c_str();  

       /* allocate the output media context */
       avformat_alloc_output_context2(&oc, NULL, "flv", filename);
       if (!oc)
       {
           OutputDebugString(L"Could not deduce output format from file extension: using MPEG.\n");
           avformat_alloc_output_context2(&oc, NULL, "mpeg", filename);
       }
       if (!oc)
       {
           OutputDebugString(L"Could not allocate  using MPEG.\n");
       }


       fmt = oc->oformat;

       /* Add the audio and video streams using the default format codecs
       * and initialize the codecs. */
       if (fmt->video_codec != AV_CODEC_ID_NONE) {
           add_stream(&video_st, oc, &video_codec, fmt->video_codec);
           have_video = 1;
           encode_video = 1;
       }
       if (fmt->audio_codec != AV_CODEC_ID_NONE) {
           add_stream(&audio_st, oc, &audio_codec, fmt->audio_codec);
           have_audio = 1;
           encode_audio = 1;
       }

       /* Now that all the parameters are set, we can open the audio and
        * video codecs and allocate the necessary encode buffers. */
       if (have_video)
       {
           open_video(oc, video_codec, &video_st, opt);
       }

       if (have_audio)
       {
           open_audio(oc, audio_codec, &audio_st, opt);
       }

       av_dump_format(oc, 0, filename, 1);

       /* open the output file, if needed */
       if (!(fmt->flags & AVFMT_NOFILE))
       {
           ret = avio_open(&oc->pb, filename, AVIO_FLAG_WRITE);
           if (ret < 0)
           {
               OutputDebugString(L"Could not open ");
               OutputDebugString(out_uri->Data());
           }
       }

       /* Write the stream header, if any. */
       ret = avformat_write_header(oc, &opt);
       if (ret < 0)
       {
           OutputDebugString(L"Error occurred when writing stream header \n");
       }

    }

    void RTMPWriter::WriteVideoFrame(
        boolean isKeyFrame,
        boolean hasDiscontinuity,
        UINT64 frameId,
        UINT32 videoBufferLength,
        BYTE *videoBytes)
    {

        int ret;
        AVCodecContext *c;
        AVFrame* frame;
        int got_packet = 0;
        AVPacket pkt = { 0 };

        c = video_st.enc;

        frame = get_video_frame(videoBufferLength, videoBytes);

        /* encode the image */
        ret = avcodec_encode_video2(c, &pkt, frame, &got_packet);
        if (ret < 0) {
             OutputDebugString(L"Error encoding video frame: \n")
        }

        if (got_packet) 
        {
            ret = write_frame(oc, &c->time_base, video_st.st, &pkt);
        }
        else {
            ret = 0;
        }

        if (ret < 0) {
             OutputDebugString(L"Error while writing video frame: %s\n");
        }
    }

    AVFrame * RTMPWriter::get_video_frame(
       UINT32 videoBufferLength,
       BYTE *videoBytes)
    {
        AVCodecContext *c = video_st.enc;

        if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
            /* as we only generate a YUV420P picture, we must convert it
            * to the codec pixel format if needed */
            if (!video_st.sws_ctx) {
                video_st.sws_ctx = sws_getContext(c->width, c->height,
                    AV_PIX_FMT_YUV420P,
                    c->width, c->height,
                    c->pix_fmt,
                    SCALE_FLAGS, NULL, NULL, NULL);
                if (!video_st.sws_ctx) {
                    fprintf(stderr,
                        "Could not initialize the conversion context\n");
                        exit(1);
                }
            }
            fill_yuv_image(video_st.tmp_frame, video_st.next_pts, c->width, c->height, videoBufferLength, videoBytes);
            sws_scale(video_st.sws_ctx,
            (const uint8_t * const *)video_st.tmp_frame->data, video_st.tmp_frame->linesize,
            0, c->height, video_st.frame->data, video_st.frame->linesize);
        }
        else {
            fill_yuv_image(video_st.frame, video_st.next_pts, c->width, c->height, videoBufferLength, videoBytes);
        }

        video_st.frame->pts = video_st.next_pts++;

        return video_st.frame;
    }

    /* Prepare a dummy image. */
    void  RTMPWriter::fill_yuv_image(
         AVFrame *pict, 
         int frame_index,
         int width, 
         int height, 
         UINT32 videoBufferLength,
         BYTE *videoBytes)
    {
        //int x, y, i, ret;

        /* when we pass a frame to the encoder, it may keep a reference to it
        * internally;
        * make sure we do not overwrite it here
        */
        ret = av_frame_make_writable(pict);
        if (ret < 0) 
        {
             OutputDebugString(L"Unable to make piture writable");
        }

        memcpy(pict->data, videoBytes, videoBufferLength);

        //i = frame_index;

        ///* Y */
        //for (y = 0; y < height; y++)
        //  for (x = 0; x < width; x++)
        //      pict->data[0][y * pict->linesize[0] + x] = x + y + i * 3;

        ///* Cb and Cr */
        //for (y = 0; y < height / 2; y++) {
        //  for (x = 0; x < width / 2; x++) {
        //      pict->data[1][y * pict->linesize[1] + x] = 128 + y + i * 2;
        //      pict->data[2][y * pict->linesize[2] + x] = 64 + x + i * 5;
        //  }
        //}
    }

    void RTMPWriter::WriteAudioFrame()
    {

    }

    /* Add an output stream. */
    void  RTMPWriter::add_stream(
        OutputStream *ost, 
        AVFormatContext *oc,
        AVCodec **codec,
        enum AVCodecID codec_id)
   {
    AVCodecContext *c;
    int i;

    /* find the encoder */
    *codec = avcodec_find_encoder(codec_id);
    if (!(*codec)) {
        OutputDebugString(L"Could not find encoder for '%s'\n");
        //avcodec_get_name(codec_id));
        exit(1);
    }

    ost->st = avformat_new_stream(oc, NULL);
    if (!ost->st) {
        OutputDebugString(L"Could not allocate stream\n");
        exit(1);
    }
    ost->st->id = oc->nb_streams - 1;
    c = avcodec_alloc_context3(*codec);
    if (!c) {
        OutputDebugString(L"Could not alloc an encoding context\n");
        exit(1);
    }
    ost->enc = c;

    switch ((*codec)->type) {
    case AVMEDIA_TYPE_AUDIO:
        c->sample_fmt = (*codec)->sample_fmts ?
            (*codec)->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
        c->bit_rate = 64000;
        c->sample_rate = 44100;
        if ((*codec)->supported_samplerates) {
            c->sample_rate = (*codec)->supported_samplerates[0];
            for (i = 0; (*codec)->supported_samplerates[i]; i++) {
                if ((*codec)->supported_samplerates[i] == 44100)
                    c->sample_rate = 44100;
            }
        }
        c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
        c->channel_layout = AV_CH_LAYOUT_STEREO;
        if ((*codec)->channel_layouts) {
            c->channel_layout = (*codec)->channel_layouts[0];
            for (i = 0; (*codec)->channel_layouts[i]; i++) {
                if ((*codec)->channel_layouts[i] == AV_CH_LAYOUT_STEREO)
                    c->channel_layout = AV_CH_LAYOUT_STEREO;
            }
        }
        c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
        ost->st->time_base = /*(AVRational)*/{ 1, c->sample_rate };
        break;

    case AVMEDIA_TYPE_VIDEO:
        c->codec_id = codec_id;

        c->bit_rate = 400000;
        /* Resolution must be a multiple of two. */
        c->width = 352;
        c->height = 288;
        /* timebase: This is the fundamental unit of time (in seconds) in terms
        * of which frame timestamps are represented. For fixed-fps content,
        * timebase should be 1/framerate and timestamp increments should be
        * identical to 1. */
        ost->st->time_base = /*(AVRational)*/{ 1, STREAM_FRAME_RATE };
        c->time_base = ost->st->time_base;

        c->gop_size = 12; /* emit one intra frame every twelve frames at most */
        c->pix_fmt = STREAM_PIX_FMT;
            if (c->codec_id == AV_CODEC_ID_MPEG2VIDEO) {
                /* just for testing, we also add B-frames */
                c->max_b_frames = 2;
            }
            if (c->codec_id == AV_CODEC_ID_MPEG1VIDEO) {
                /* Needed to avoid using macroblocks in which some coeffs overflow.
                * This does not happen with normal video, it just happens here as
                * the motion of the chroma plane does not match the luma plane. */
                c->mb_decision = 2;
            }
            break;

        default:
            break;
        }

         /* Some formats want stream headers to be separate. */
        if (oc->oformat->flags & AVFMT_GLOBALHEADER)
            c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }

AVFrame * RTMPWriter::alloc_audio_frame(
    enum AVSampleFormat sample_fmt,
    uint64_t channel_layout,
    int sample_rate, int nb_samples)
{
    AVFrame *frame = av_frame_alloc();
    int ret;

    if (!frame) {
        OutputDebugString(L"Error allocating an audio frame\n");
        exit(1);
    }

    frame->format = sample_fmt;
    frame->channel_layout = channel_layout;
    frame->sample_rate = sample_rate;
    frame->nb_samples = nb_samples;

    if (nb_samples) {
        ret = av_frame_get_buffer(frame, 0);
        if (ret < 0) {
            OutputDebugString(L"Error allocating an audio buffer\n");
            exit(1);
        }
    }

        return frame;
    }




void  RTMPWriter::open_audio(
    AVFormatContext *oc, 
    AVCodec *codec, 
    OutputStream *ost, 
    AVDictionary *opt_arg)
{
    AVCodecContext *c;
    int nb_samples;
    int ret;
    AVDictionary *opt = NULL;

    c = ost->enc;

    /* open it */
    av_dict_copy(&opt, opt_arg, 0);
    ret = avcodec_open2(c, codec, &opt);
    av_dict_free(&opt);
    if (ret < 0) {
        OutputDebugString(L"Could not open audio codec: %s\n");// , av_err2str(ret));
        exit(1);
    }

    /* init signal generator */
    ost->t = 0;
    ost->tincr = 2 * M_PI * 110.0 / c->sample_rate;
    /* increment frequency by 110 Hz per second */
    ost->tincr2 = 2 * M_PI * 110.0 / c->sample_rate / c->sample_rate;

    if (c->codec->capabilities & AV_CODEC_CAP_VARIABLE_FRAME_SIZE)
        nb_samples = 10000;
    else
        nb_samples = c->frame_size;

    ost->frame = alloc_audio_frame(c->sample_fmt, c->channel_layout,
        c->sample_rate, nb_samples);
    ost->tmp_frame = alloc_audio_frame(AV_SAMPLE_FMT_S16, c->channel_layout,
        c->sample_rate, nb_samples);

    /* copy the stream parameters to the muxer */
    ret = avcodec_parameters_from_context(ost->st->codecpar, c);
    if (ret < 0) {
        OutputDebugString(L"Could not copy the stream parameters\n");
        exit(1);
    }

    /* create resampler context */
    ost->swr_ctx = swr_alloc();
    if (!ost->swr_ctx) {
        OutputDebugString(L"Could not allocate resampler context\n");
        exit(1);
    }

    /* set options */
    av_opt_set_int(ost->swr_ctx, "in_channel_count", c->channels, 0);
    av_opt_set_int(ost->swr_ctx, "in_sample_rate", c->sample_rate, 0);
    av_opt_set_sample_fmt(ost->swr_ctx, "in_sample_fmt", AV_SAMPLE_FMT_S16, 0);
    av_opt_set_int(ost->swr_ctx, "out_channel_count", c->channels, 0);
    av_opt_set_int(ost->swr_ctx, "out_sample_rate", c->sample_rate, 0);
    av_opt_set_sample_fmt(ost->swr_ctx, "out_sample_fmt", c->sample_fmt, 0);

    /* initialize the resampling context */
    if ((ret = swr_init(ost->swr_ctx)) < 0) {
        OutputDebugString(L"Failed to initialize the resampling context\n");
        exit(1);
    }
}

int RTMPWriter::write_frame(
    AVFormatContext *fmt_ctx, 
    const AVRational *time_base, 
    AVStream *st, 
    AVPacket *pkt)
{
    /* rescale output packet timestamp values from codec to stream timebase */
    av_packet_rescale_ts(pkt, *time_base, st->time_base);
    pkt->stream_index = st->index;

    /* Write the compressed frame to the media file. */
    //log_packet(fmt_ctx, pkt);
    OutputDebugString(L"Actually sending video frame: %s\n");
    return av_interleaved_write_frame(fmt_ctx, pkt);
}


AVFrame  *RTMPWriter::alloc_picture(
    enum AVPixelFormat pix_fmt, 
    int width, 
    int height)
{
    AVFrame *picture;
    int ret;

    picture = av_frame_alloc();
    if (!picture)
        return NULL;

    picture->format = pix_fmt;
    picture->width = width;
    picture->height = height;

    /* allocate the buffers for the frame data */
    ret = av_frame_get_buffer(picture, 32);
    if (ret < 0) {
        fprintf(stderr, "Could not allocate frame data.\n");
        exit(1);
    }

    return picture;
}

void RTMPWriter::open_video(
    AVFormatContext *oc, 
    AVCodec *codec, 
    OutputStream *ost, 
    AVDictionary *opt_arg)
{
    int ret;
    AVCodecContext *c = ost->enc;
    AVDictionary *opt = NULL;

    av_dict_copy(&opt, opt_arg, 0);

    /* open the codec */
    ret = avcodec_open2(c, codec, &opt);
    av_dict_free(&opt);
    if (ret < 0) {
        OutputDebugString(L"Could not open video codec: %s\n");// , av_err2str(ret));
        exit(1);
    }

    /* allocate and init a re-usable frame */
    ost->frame = alloc_picture(c->pix_fmt, c->width, c->height);
    if (!ost->frame) {
        OutputDebugString(L"Could not allocate video frame\n");
        exit(1);
    }

    /* If the output format is not YUV420P, then a temporary YUV420P
    * picture is needed too. It is then converted to the required
    * output format. */
    ost->tmp_frame = NULL;
    if (c->pix_fmt != AV_PIX_FMT_YUV420P) {
        ost->tmp_frame = alloc_picture(AV_PIX_FMT_YUV420P, c->width, c->height);
        if (!ost->tmp_frame) {
            OutputDebugString(L"Could not allocate temporary picture\n");
            exit(1);
        }
    }

    /* copy the stream parameters to the muxer */
    ret = avcodec_parameters_from_context(ost->st->codecpar, c);
    if (ret < 0) {
        OutputDebugString(L"Could not copy the stream parameters\n");
        exit(1);
    }
}

void RTMPWriter::close_stream(AVFormatContext *oc, OutputStream *ost)
{
    avcodec_free_context(&ost->enc);
    av_frame_free(&ost->frame);
    av_frame_free(&ost->tmp_frame);
    sws_freeContext(ost->sws_ctx);
    swr_free(&ost->swr_ctx);
}

RTMPWriter::~RTMPWriter()
{
    av_write_trailer(oc);
    /* Close each codec. */
    if (have_video)
        close_stream(oc, &video_st);
    if (have_audio)
        close_stream(oc, &audio_st);

    if (!(fmt->flags & AVFMT_NOFILE))
        /* Close the output file. */
        avio_closep(&oc->pb);

    /* free the stream */
    avformat_free_context(oc);
}


    


  • 6 Crucial Benefits of Conversion Rate Optimisation

    26 février 2024, par Erin

    Whether investing time or money in marketing, you want the best return on your investment. You want to get as many customers as possible with your budget and resources.

    That’s what conversion rate optimisation (CRO) aims to do. But how does it help you achieve this major goal ? 

    This guide explores the concrete benefits of conversion rate optimisation and how they lead to more effective marketing and ROI. We’ll also introduce specific CRO best practices to help unlock these benefits.

    What is conversion rate optimisation ?

    Conversion rate optimisation (CRO) is the process of examining your website for improvements and creating tests to increase the number of visitors who take a desired action, like purchasing a product or submitting a form.

    The conversion rate is the percentage of visitors who complete a specific goal.

    Illustration of what conversion rate optimisation is

    In order to improve your conversion rate, you need to figure out :

    • Where your customers come from
    • How potential customers navigate or interact with your website
    • Where potential customers are likely to exit your site (or abandon carts)
    • What patterns drive valuable actions like sign-ups and sales

    From there, you can gradually implement changes that will drive more visitors to convert. That’s the essence of conversion rate optimisation.

    6 top benefits of conversion rate optimisation (and best practices to unlock them)

    Conversion rate optimisation can help you get more out of your campaigns without investing more. CRO helps you in these six ways :

    1. Understand your visitors (and customers) better

    The main goal of CRO is to boost conversions, but it’s more than that. In the process of improving conversion rates, you’ll also benefit by gaining deep insights into user behaviour, preferences, and needs. 

    Using web analytics, tests and behavioural analytics, CRO helps marketers shape their website to match what users need.

    Best practices for understanding your customer :

    First, analyse how visitors act with full context (the pages they view, how long they stay and more). 

    In Matomo, you can use the Users Flow report to understand how visitors navigate through your site. This will help you visualise and identify trends in the buyer’s journey.

    User flow chart in Matomo analytics

    Then, you can dive deeper by defining and analysing journeys with Funnels. This shows you how many potential customers follow through each step in your defined journey and identify where you might have a leaky funnel. 

    Goal funnel chart in Matomo analytics

    In the above Funnel Report, nearly half of our visitors, just 44%, are moving forward in the buyer’s journey after landing on our scuba diving mask promotion page. With 56% of potential customers dropping off at this page, it’s a prime opportunity for optimising conversions.

    Think of Funnels as your map, and pages with high drop-off rates as valuable opportunities for improvement.

    Once you notice patterns, you can try to identify the why. Analyse the pages, do user testing and do your best to improve them.

    2. Deliver a better user experience

    A better understanding of your customers’ needs means you can deliver a better user experience.

    Illustration of improving the user experience

    For example, if you notice many people spend more time than expected on a particular step in the sign-up process, you can work to streamline it.

    Best practices for improving your user experience : 

    To do this, you need to come up with testable hypotheses. Start by using Heatmaps and Session Recordings to visualise the user experience and understand where visitors are hesitating, experiencing points of frustration, and exiting. 

    You need to outline what drives certain patterns in behaviour — like cart abandonment for specific products, and what you think can fix them.

    Example of a heatmap in Matomo analytics

    Let’s look at an example. In the screenshot above, we used Matomo’s Heatmap feature to analyse user behaviour on our website. 

    Only 65% of visitors scroll down far enough to encounter our main call to action to “Write a Review.” This insight suggests a potential opportunity for optimisation, where we can focus efforts on encouraging more users to engage with this key element on our site.

    Once you’ve identified an area of improvement, you need to test the results of your proposed solution to the problem. The most common way to do this is with an A/B test. 

    This is a test where you create a new version of the problematic page, trying different titles, comparing long, and short copy, adding or removing images, testing variations of call-to-action buttons and more. Then, you compare the results — the conversion rate — against the original. With Matomo’s A/B Testing feature, you can easily split traffic between the original and one or more variations.

    A/B testing in Matomo analytics

    In the example above from Matomo, we can see that testing different header sizes on a page revealed that the wider header led to a higher conversion rate of 47%, compared to the original rate of 35% and the smaller header’s 36%.

    Matomo’s report also analyses the “statistical significance” of the difference in results. Essentially, this is the likelihood that the difference comes from the changes you made in the variation. With a small sample size, random patterns (like one page receiving more organic search visits) can cause the differences.

    If you see a significant change over a larger sample size, you can be fairly certain that the difference is meaningful. And that’s exactly what a high statistical significance rating indicates in Matomo. 

    Once a winner is identified, you can apply the change and start a new experiment. 

    3. Create a culture of data-driven decision-making

    Marketers can no longer afford to rely on guesswork or gamble away budgets and resources. In our digital age, you must use data to get ahead of the competition. In 2021, 65% of business leaders agreed that decisions were getting more complex.

    CRO is a great way to start a company-wide focus on data-driven decision-making. 

    Best practices to start a data-driven culture :

    Don’t only test “hunches” or “best practices” — look at the data. Figure out the patterns that highlight how different types of visitors interact with your site.

    Try to answer these questions :

    • How do our most valuable customers interact with our site before purchasing ?
    • How do potential customers who abandon their carts act ?
    • Where do our most valuable customers come from ?

    Moreover, it’s key to democratise insights by providing multiple team members access to information, fostering informed decision-making company-wide.

    4. Lower your acquisition costs and get higher ROI from all marketing efforts

    Once you make meaningful optimisations, CRO can help you lower customer acquisition costs (CAC). Getting new customers through advertising will be cheaper.

    As a result, you’ll get a better return on investment (ROI) on all your campaigns. Every ad and dollar invested will get you closer to a new customer than before. That’s the bottom line of CRO.

    Best practices to lower your CAC (customer acquisition costs) through CRO adjustments :

    The easiest way to lower acquisition costs is to understand where your customers come from. Use marketing attribution to track the results of your campaigns, revealing how each touchpoint contributes to conversions and revenue over time, beyond just last-click attribution.

    You can then compare the number of conversions to the marketing costs of each channel, to get a channel-specific breakdown of CAC.

    This performance overview can help you quickly prioritise the best value channels and ads, lowering your CAC. But these are only surface-level insights. 

    You can also further lower CAC by optimising the pages these campaigns send visitors to. Start with a deep dive into your landing pages using features like Matomo’s Session Recordings or Heatmaps.

    They can help you identify issues with an unengaging user experience or content. Using these insights, you can create A/B tests, where you implement a new page that replaces problematic headlines, buttons, copy, or visuals.

    Example of a multivariate test for headlines

    When a test shows a statistically significant improvement in conversion rates, implement the new version. Repeat this over time, and you can increase your conversion rates significantly, getting more customers with the same spend. This will reduce your customer acquisition costs, and help your company grow faster without increasing your ad budget.

    5. Improve your average order value (AOV) and customer lifetime value (CLV)

    CRO isn’t only about increasing the number of customers you convert. If you adapt your approach, you can also use it to increase the revenue from each customer you bring in. 

    But you can’t do that by only tracking conversion rates, you also need to track exactly what your customers buy.

    If you only blindly optimise for CAC, you even risk lowering your CLV and the overall profitability of your campaigns. (For example, if you focus on Facebook Ads with a $6 CAC, but an average CLV of $50, over Google Ads with a $12 CAC, but a $100 CLV.)

    Best practices to track and improve CLV :

    First, integrate your analytics platform with your e-commerce (B2C) or your CRM (B2B). This will help you get a more holistic view of your customers. You don’t want the data to stop at “converted.” You want to be able to dive deep into the patterns of high-value customers.

    The sales report in Matomo’s ecommerce analytics makes it easy to break down average order value by channels, campaigns, and specific ads.

    Ecommerce sales report in Matomo analytics

    In the report above, we can see that search engines drive customers who spend significantly more, on average, than social networks — $241 vs. $184. But social networks drive a higher volume of customers and more revenue.

    To figure out which channel to focus on, you need to see how the CAC compares to the AOV (or CLV for B2B customers). Let’s say the CAC of social networks is $50, while the search engine CAC is $65. Search engine customers are more profitable — $176 vs. $134. So you may want to adjust some more budget to that channel.

    To put it simply :

    Profit per customer = AOV (or CLV) – CAC

    Example :

    • Profit per customer for social networks = $184 – $50 = $134
    • Profit per customer for search engines = $241 – $65 = $176

    You can also try to A/B test changes that may increase the AOV, like creating a product bundle and recommending it on specific sales pages.

    An improvement in CLV will make your campaigns more profitable, and help stretch your advertising budget even further.

    6. Improve your content and SEO rankings

    A valuable side-effect of focusing on CRO metrics and analyses is that it can boost your SEO rankings. 

    How ? 

    CRO helps you improve the user experience of your website. That’s a key signal Google (and other search engines) care about when ranking webpages. 

    Illustration of how better content improves SEO rankings

    For example, Google’s algorithm considers “dwell time,” AKA how long a user stays on your page. If many users quickly return to the results page and click another result, that’s a bad sign. But if most people stay on your site for a while (or don’t return to Google at all), Google thinks your page gives the user their answer.

    As a result, Google will improve your website’s ranking in the search results.

    Best practices to make the most of CRO when it comes to SEO :

    Use A/B Testing, Heatmaps, and Session Recordings to run experiments and understand user behaviour. Test changes to headlines, page layout, imagery and more to see how it impacts the user experience. You can even experiment with completely changing the content on a page, like substituting an introduction.

    Bring your CRO-testing mindset to important pages that aren’t ranking well to improve metrics like dwell time.

    Start optimising your conversion rate today

    As you’ve seen, enjoying the benefits of CRO heavily relies on the data from a reliable web analytics solution. 

    But in an increasingly privacy-conscious world (just look at the timeline of GDPR updates and fines), you must tread carefully. One of the dilemmas that marketing managers face today is whether to prioritise data quality or privacy (and regulations).

    With Matomo, you don’t have to choose. Matomo values both data quality and privacy, adhering to stringent privacy laws like GDPR and CCPA.

    Unlike other web analytics, Matomo doesn’t sample data or use AI and machine learning to fill data gaps. Plus, you can track without annoying visitors with a cookie consent banner – so you capture 100% of traffic while respecting user privacy (excluding in Germany and UK).

    And as you’ve already seen above, you’ll still get plenty of reports and insights to drive your CRO efforts. With User Flows, Funnels, Session Recordings, Form Analytics, and Heatmaps, you can immediately find insights to improve your bottom line.

    And our built-in A/B testing feature will help you test your hypotheses and drive reliable progress. If you’re ready to reliably optimise conversion rates (with accuracy and without privacy concerns), try Matomo for free for 21 days. No credit card required.

  • ffmpeg converting JPG slideshow to MKV

    17 décembre 2018, par phol

    I am trying to convert a set of jpg files to a slideshow with varying durations using ffconcat

    output.txt contents :

    ffconcat version 1.0
    file slide_0001.jpg
    duration 0.163
    file slide_0002.jpg
    duration 0.533
    file slide_0003.jpg
    duration 1.067
    file slide_0004.jpg
    duration 0.533
    file slide_0005.jpg
    etc.

    FFmpeg command :

    ffmpeg -i output.txt output.mkv

    I also tried :

    ffmpeg -i output.txt -c:v copy output.mkv

    However, when converting, the colours of the images become skewed.
    White becomes pink / purple and dark green becomes light green.
    The same happens when choosing different codecs and containers.
    Is there a way to solve this ?

    EDIT :
    I performed the same conversion again. I attached the log output as requested.
    However, in the meantime I have figured out that converting without ffconcat duration and then altering timecodes with mp4fpsmod is more efficient in terms of storage (3 vs 40 MB) and gives less issues in different players. The result is nonetheless the same : colours are still wrong, both on my workstation (Ubuntu 18.04) and laptop (macOS Mojave).

    I tried to alter things with pix_fmt and -vf format as can be seen in the second log output. This did not solve the problem.

    Background info :
    The input jpg files are from a lecture recording made with MediaSite, which has both a video stream for the room as well as this slideshow stream for the slides.

    Example of input :
    Input image
    Example of output :
    Screenshot of output
    log output MKV conversion :

       $ ffmpeg -i outputnew.txt output12345.mkv
    ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers
    built with Apple LLVM version 10.0.0 (clang-1000.11.45.5)
    configuration: --prefix=/usr/local/Cellar/ffmpeg/4.1 --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gpl --enable-libmp3lame --enable-libopus --enable-libsnappy --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxvid --enable-lzma --enable-opencl --enable-videotoolbox
    libavutil      56. 22.100 / 56. 22.100
    libavcodec     58. 35.100 / 58. 35.100
    libavformat    58. 20.100 / 58. 20.100
    libavdevice    58.  5.100 / 58.  5.100
    libavfilter     7. 40.101 /  7. 40.101
    libavresample   4.  0.  0 /  4.  0.  0
    libswscale      5.  3.100 /  5.  3.100
    libswresample   3.  3.100 /  3.  3.100
    libpostproc    55.  3.100 / 55.  3.100
    Input #0, concat, from 'outputnew.txt':
    Duration: N/A, start: 0.000000, bitrate: N/A
       Stream #0:0: Video: mjpeg, yuvj444p(pc, bt470bg/unknown/unknown), 1280x720, 25 tbr, 25 tbn, 25 tbc
    Stream mapping:
    Stream #0:0 -> #0:0 (mjpeg (native) -> h264 (libx264))
    Press [q] to stop, [?] for help
    [libx264 @ 0x7fb284802800] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
    [libx264 @ 0x7fb284802800] profile High 4:4:4 Predictive, level 3.1, 4:4:4 8-bit
    [libx264 @ 0x7fb284802800] 264 - core 152 r2854 e9a5903 - H.264/MPEG-4 AVC codec - Copyleft 2003-2017 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x1:0x111 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=4 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
    Output #0, matroska, to 'output12345.mkv':
    Metadata:
       encoder         : Lavf58.20.100
       Stream #0:0: Video: h264 (libx264) (H264 / 0x34363248), yuvj444p(pc), 1280x720, q=-1--1, 25 fps, 1k tbn, 25 tbc
       Metadata:
       encoder         : Lavc58.35.100 libx264
       Side data:
       cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1
    [mjpeg @ 0x7fb284061200] EOI missing, emulating=00:02:12.96 bitrate= 491.3kbits/s speed=14.3x        
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte= 487.2kbits/s speed=14.3x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte= 333.0kbits/s speed=22.3x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte= 338.8kbits/s speed=22.7x    
       Last message repeated 2 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte= 164.7kbits/s speed=41.6x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte= 122.7kbits/s speed=55.7x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte= 111.2kbits/s speed=60.5x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte= 105.3kbits/s speed=64.4x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  82.2kbits/s speed=82.7x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  79.7kbits/s speed=85.8x    
       Last message repeated 2 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  59.7kbits/s speed= 114x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  53.8kbits/s speed= 127x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  54.7kbits/s speed= 124x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  47.1kbits/s speed= 143x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  47.0kbits/s speed= 144x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  44.6kbits/s speed= 151x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  34.4kbits/s speed= 194x    
       Last message repeated 2 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  32.9kbits/s speed= 204x    
       Last message repeated 2 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  32.8kbits/s speed= 203x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  32.6kbits/s speed= 202x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  28.8kbits/s speed= 229x    
       Last message repeated 2 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  27.9kbits/s speed= 236x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  27.9kbits/s speed= 233x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  23.9kbits/s speed= 274x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  24.2kbits/s speed= 273x    
       Last message repeated 2 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  24.2kbits/s speed= 274x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  24.0kbits/s speed= 276x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  24.2kbits/s speed= 273x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  23.6kbits/s speed= 278x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  23.7kbits/s speed= 279x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  23.6kbits/s speed= 279x    
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  24.0kbits/s speed= 276x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  23.5kbits/s speed= 281x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  20.4kbits/s speed= 325x    
    [mjpeg @ 0x7fb28502a000] EOI missing, emulating
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  20.1kbits/s speed= 329x    
       Last message repeated 1 times
    [matroska @ 0x7fb284812e00] Starting new cluster due to timestampte=  20.1kbits/s speed= 330x    
    frame=  635 fps= 14 q=-1.0 Lsize=   37389kB time=04:22:06.76 bitrate=  19.5kbits/s speed= 343x    
    video:37378kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.029620%
    [libx264 @ 0x7fb284802800] frame I:16    Avg QP:11.98  size:154224
    [libx264 @ 0x7fb284802800] frame P:184   Avg QP:13.30  size: 79029
    [libx264 @ 0x7fb284802800] frame B:435   Avg QP:13.19  size: 48887
    [libx264 @ 0x7fb284802800] consecutive B-frames:  6.0%  4.4% 10.9% 78.7%
    [libx264 @ 0x7fb284802800] mb I  I16..4: 55.7%  0.0% 44.3%
    [libx264 @ 0x7fb284802800] mb P  I16..4: 32.4%  0.0% 21.5%  P16..4: 13.3% 15.1% 11.3%  0.0%  0.0%    skip: 6.4%
    [libx264 @ 0x7fb284802800] mb B  I16..4: 17.8%  0.0%  7.2%  B16..8: 19.1% 19.4%  6.5%  direct:14.6%  skip:15.3%  L0:47.0% L1:38.9% BI:14.1%
    [libx264 @ 0x7fb284802800] coded y,u,v intra: 63.7% 26.1% 56.3% inter: 46.5% 24.6% 43.5%
    [libx264 @ 0x7fb284802800] i16 v,h,dc,p: 19% 18% 58%  4%
    [libx264 @ 0x7fb284802800] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 35% 20% 25%  4%  4%  3%  3%  3%  3%
    [libx264 @ 0x7fb284802800] Weighted P-Frames: Y:5.4% UV:5.4%
    [libx264 @ 0x7fb284802800] ref P L0: 43.2%  4.4% 30.8% 21.4%  0.2%
    [libx264 @ 0x7fb284802800] ref B L0: 64.1% 27.3%  8.6%
    [libx264 @ 0x7fb284802800] ref B L1: 85.3% 14.7%
    [libx264 @ 0x7fb284802800] kb/s:19.25

    Log output mp4 conversion :

    $ ffmpeg -i slide_%04d.jpg -vf format=rgba -pix_fmt yuv420p -vcodec libx264 -movflags +faststart -preset veryslow output12345.mp4
    ffmpeg version 4.1 Copyright (c) 2000-2018 the FFmpeg developers
    built with Apple LLVM version 10.0.0 (clang-1000.11.45.5)
    configuration: --prefix=/usr/local/Cellar/ffmpeg/4.1 --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gpl --enable-libmp3lame --enable-libopus --enable-libsnappy --enable-libtheora --enable-libvorbis --enable-libvpx --enable-libx264 --enable-libx265 --enable-libxvid --enable-lzma --enable-opencl --enable-videotoolbox
    libavutil      56. 22.100 / 56. 22.100
    libavcodec     58. 35.100 / 58. 35.100
    libavformat    58. 20.100 / 58. 20.100
    libavdevice    58.  5.100 / 58.  5.100
    libavfilter     7. 40.101 /  7. 40.101
    libavresample   4.  0.  0 /  4.  0.  0
    libswscale      5.  3.100 /  5.  3.100
    libswresample   3.  3.100 /  3.  3.100
    libpostproc    55.  3.100 / 55.  3.100
    Input #0, image2, from 'slide_%04d.jpg':
    Duration: 00:00:25.36, start: 0.000000, bitrate: N/A
       Stream #0:0: Video: mjpeg, yuvj444p(pc, bt470bg/unknown/unknown), 1280x720, 25 fps, 25 tbr, 25 tbn, 25 tbc
    File 'output12345.mp4' already exists. Overwrite ? [y/N] y
    Stream mapping:
    Stream #0:0 -> #0:0 (mjpeg (native) -> h264 (libx264))
    Press [q] to stop, [?] for help
    [swscaler @ 0x7fb7e280b000] deprecated pixel format used, make sure you did set range correctly
    [libx264 @ 0x7fb7e1803c00] using cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2
    [libx264 @ 0x7fb7e1803c00] profile High, level 5.0
    [libx264 @ 0x7fb7e1803c00] 264 - core 152 r2854 e9a5903 - H.264/MPEG-4 AVC codec - Copyleft 2003-2017 - http://www.videolan.org/x264.html - options: cabac=1 ref=16 deblock=1:0:0 analyse=0x3:0x133 me=umh subme=10 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=24 chroma_me=1 trellis=2 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=6 lookahead_threads=1 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=8 b_pyramid=2 b_adapt=2 b_bias=0 direct=3 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=60 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00
    Output #0, mp4, to 'output12345.mp4':
    Metadata:
       encoder         : Lavf58.20.100
       Stream #0:0: Video: h264 (libx264) (avc1 / 0x31637661), yuv420p, 1280x720, q=-1--1, 25 fps, 12800 tbn, 25 tbc
       Metadata:
       encoder         : Lavc58.35.100 libx264
       Side data:
       cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: -1
    [mp4 @ 0x7fb7e1801800] Starting second pass: moving the moov atom to the beginning of the file    
    frame=  634 fps=8.5 q=-1.0 Lsize=    2252kB time=00:00:25.24 bitrate= 731.0kbits/s speed=0.337x    
    video:2245kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.307448%
    [libx264 @ 0x7fb7e1803c00] frame I:5     Avg QP:19.12  size: 49393
    [libx264 @ 0x7fb7e1803c00] frame P:131   Avg QP:22.18  size:  9379
    [libx264 @ 0x7fb7e1803c00] frame B:498   Avg QP:23.78  size:  1652
    [libx264 @ 0x7fb7e1803c00] consecutive B-frames:  3.2%  1.6%  3.3% 29.7%  7.9% 16.1%  7.7% 16.4% 14.2%
    [libx264 @ 0x7fb7e1803c00] mb I  I16..4: 45.7% 37.9% 16.4%
    [libx264 @ 0x7fb7e1803c00] mb P  I16..4:  6.0%  5.3%  1.3%  P16..4: 19.9%  1.9%  3.7%  0.3%  0.1%    skip:61.5%
    [libx264 @ 0x7fb7e1803c00] mb B  I16..4:  0.3%  0.4%  0.1%  B16..8: 17.5%  1.0%  0.2%  direct: 0.3%  skip:80.2%  L0:52.3% L1:46.9% BI: 0.8%
    [libx264 @ 0x7fb7e1803c00] 8x8 transform intra:43.0% inter:48.9%
    [libx264 @ 0x7fb7e1803c00] direct mvs  spatial:99.2% temporal:0.8%
    [libx264 @ 0x7fb7e1803c00] coded y,uvDC,uvAC intra: 16.9% 25.8% 16.6% inter: 1.1% 1.9% 1.2%
    [libx264 @ 0x7fb7e1803c00] i16 v,h,dc,p: 61% 28%  6%  6%
    [libx264 @ 0x7fb7e1803c00] i8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 20%  9% 50%  3%  4%  3%  3%  3%  5%
    [libx264 @ 0x7fb7e1803c00] i4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 18% 23% 19%  5%  7%  6%  6%  5% 10%
    [libx264 @ 0x7fb7e1803c00] i8c dc,h,v,p: 46% 40% 13%  1%
    [libx264 @ 0x7fb7e1803c00] Weighted P-Frames: Y:5.3% UV:5.3%
    [libx264 @ 0x7fb7e1803c00] ref P L0: 33.2%  3.1% 11.6%  7.3%  8.8%  6.4%  6.5%  3.1%  3.2%  2.6%  2.4%  2.5%  2.8%  2.8%  2.3%  1.4%
    [libx264 @ 0x7fb7e1803c00] ref B L0: 42.0% 15.1% 12.0%  5.4%  4.9%  4.3%  4.0%  1.9%  2.0%  1.8%  1.7%  1.8%  1.7%  1.1%  0.5%
    [libx264 @ 0x7fb7e1803c00] ref B L1: 83.5% 16.5%
    [libx264 @ 0x7fb7e1803c00] kb/s:725.10

    Player :
    QuickTime, VLC and Kodi on both Linux and Mac.

    EDIT 2 :
    Input files : https://drive.google.com/open?id=1kbBkSDSwe6ywgQ8lkjqkh-MKCzGA4bj8