Recherche avancée

Médias (91)

Autres articles (50)

  • 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 ;

  • Publier sur MédiaSpip

    13 juin 2013

    Puis-je poster des contenus à partir d’une tablette Ipad ?
    Oui, si votre Médiaspip installé est à la version 0.2 ou supérieure. Contacter au besoin l’administrateur de votre MédiaSpip pour le savoir

  • Les formats acceptés

    28 janvier 2010, par

    Les commandes suivantes permettent d’avoir des informations sur les formats et codecs gérés par l’installation local de ffmpeg :
    ffmpeg -codecs ffmpeg -formats
    Les format videos acceptés en entrée
    Cette liste est non exhaustive, elle met en exergue les principaux formats utilisés : h264 : H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10 m4v : raw MPEG-4 video format flv : Flash Video (FLV) / Sorenson Spark / Sorenson H.263 Theora wmv :
    Les formats vidéos de sortie possibles
    Dans un premier temps on (...)

Sur d’autres sites (9375)

  • FFMPEG avformat_open_input returning AVERROR_INVALIDDATA [on hold]

    10 août 2016, par Victor.dMdB

    I’m trying to use ffmpeg to take in video data from a buffer but keep on getting the same error.

    I’ve implemented the same code as described here :
    http://www.ffmpeg.org/doxygen/trunk/doc_2examples_2avio_reading_8c-example.html

    Here is the code (I’ve tried removing the unnecessary bits)

    VideoInputFile *video_input_file;
    VideoDataConf *video_data_conf;

    video_data_conf->width = 1920;
    video_data_conf->height = 1080;

    int vbuf_size = 9 * cmd_data_ptr->video_data_conf.width * cmd_data_ptr->video_data_conf.height + 10000;
    uint8_t *buffer = (uint8_t *) av_malloc(vbuf_size);

    video_data_conf->input_ptr.ptr = buffer;
    video_data_conf->input_ptr.size = vbuf_size;
    strncpy(video_data_conf->filename, "localmem");

    video_input_file->vbuf_size = 9 * video_data_conf->width * video_data_conf->height + 10000;
    video_input_file->vbuf = (uint8_t *) av_malloc(video_input_file->vbuf_size);
    video_input_file->av_io_ctx = avio_alloc_context(video_input_file->vbuf, video_input_file->vbuf_size, 0, &video_data_conf->input_ptr, &read_function, NULL, NULL);

    if ( !video_input_file->av_io_ctx ) {
       fprintf(stdout,"Failed to create the buffer avio context\n");
    }

    video_input_file->av_fmt_ctx = avformat_alloc_context();

    if ( !video_input_file->av_fmt_ctx ) {
       printf(stdout,"Failed to create the video avio context\n");
    }

    video_input_file->av_fmt_ctx->pb = video_input_file->av_io_ctx;

    open_res = avformat_open_input(&video_input_file->av_fmt_ctx, video_data_conf->filename, NULL, NULL);

    Read function :

    static int read_function(void* opaque, uint8_t* buf, int buf_size) {
       BufferData *bd = (BufferData *) opaque;
       buf_size = FFMIN(buf_size, bd->size);
       memcpy(buf, bd->ptr, buf_size);
       bd->ptr  += buf_size;
       bd->size -= buf_size;
       return buf_size;
    }

    BufferData structure

    typedef struct {
       uint8_t *ptr;
       size_t size; ///< size left in the buffer
    } BufferData;

    It starts to work if I initialise the buffer and vbuf_size with a real file like so :

     uint8_t *buffer;
     size_t vbuf_size;
     av_file_map("/path/to/image.png", &buffer, &vbuf_size, 0, NULL);
  • Downscaling a video from 1080p to 480p using swscale and encoding to x265 gives a glitched output

    5 mai 2023, par lokit khemka

    I am basically first scaling a frame and then sending the frame to the encoder as below :

    


    scaled_frame->pts = input_frame->pts;
scaled_frame->pkt_dts = input_frame->pkt_dts;
scaled_frame->pict_type = input_frame->pict_type;
sws_scale_frame(encoder->sws_ctx, scaled_frame, input_frame);
if (encode_video(decoder, encoder, scaled_frame))
     return -1;


    


    The scaling context is configured as :

    


    scaled_frame->width = 854;
scaled_frame->height=480; 
encoder->sws_ctx = sws_getContext(1920, 1080,
                            decoder->video_avcc->pix_fmt, 
                           scaled_frame->width, scaled_frame->height, decoder->video_avcc->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL );
    if (!encoder->sws_ctx){logging("Cannot Create Scaling Context."); return -1;}


    


    The encoder is configured as :

    


        encoder_sc->video_avcc->height = decoder_ctx->height; //1080
    encoder_sc->video_avcc->width = decoder_ctx->width; //1920
    encoder_sc->video_avcc->bit_rate = 2 * 1000 * 1000;
    encoder_sc->video_avcc->rc_buffer_size = 4 * 1000 * 1000;
    encoder_sc->video_avcc->rc_max_rate = 2 * 1000 * 1000;
    encoder_sc->video_avcc->rc_min_rate = 2.5 * 1000 * 1000;

    encoder_sc->video_avcc->time_base = av_inv_q(input_framerate);
    encoder_sc->video_avs->time_base = encoder_sc->video_avcc->time_base;


    


    When I get the output, the output video is 1080p and I have glitches like : enter image description here

    


    I changed the encoder avcc resolution to 480p (854 x 480). However, that is causing the video to get sliced to the top quarter of the original frame.
I am new to FFMPEG and video processing in general.

    


    EDIT : I am adding the minimal reproducible code sample. However, it is really long because I need to include code for decoding, scaling and then encoding because the possible error is either in scaling or encoding :

    


    #include <libavcodec></libavcodec>avcodec.h>&#xA;#include <libavformat></libavformat>avformat.h>&#xA;#include <libavutil></libavutil>timestamp.h>&#xA;#include <libavutil></libavutil>opt.h>&#xA;#include <libswscale></libswscale>swscale.h>&#xA;&#xA;#include &#xA;#include &#xA;&#xA;typedef struct StreamingContext{&#xA;    AVFormatContext* avfc;&#xA;    AVCodec *video_avc;&#xA;    AVCodec *audio_avc;&#xA;    AVStream *video_avs;&#xA;    AVStream *audio_avs;&#xA;    AVCodecContext *video_avcc;&#xA;    AVCodecContext *audio_avcc;&#xA;    int video_index;&#xA;    int audio_index;&#xA;    char* filename;&#xA;    struct SwsContext *sws_ctx;&#xA;}StreamingContext;&#xA;&#xA;&#xA;typedef struct StreamingParams{&#xA;    char copy_video;&#xA;    char copy_audio;&#xA;    char *output_extension;&#xA;    char *muxer_opt_key;&#xA;    char *muxer_opt_value;&#xA;    char *video_codec;&#xA;    char *audio_codec;&#xA;    char *codec_priv_key;&#xA;    char *codec_priv_value;&#xA;}StreamingParams;&#xA;&#xA;void logging(const char *fmt, ...)&#xA;{&#xA;    va_list args;&#xA;    fprintf(stderr, "LOG: ");&#xA;    va_start(args, fmt);&#xA;    vfprintf(stderr, fmt, args);&#xA;    va_end(args);&#xA;    fprintf(stderr, "\n");&#xA;}&#xA;&#xA;int fill_stream_info(AVStream *avs, AVCodec **avc, AVCodecContext **avcc)&#xA;{&#xA;    *avc = avcodec_find_decoder(avs->codecpar->codec_id);&#xA;    if (!*avc)&#xA;    {&#xA;        logging("Failed to find the codec.\n");&#xA;        return -1;&#xA;    }&#xA;&#xA;    *avcc = avcodec_alloc_context3(*avc);&#xA;    if (!*avcc)&#xA;    {&#xA;        logging("Failed to alloc memory for codec context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avcodec_parameters_to_context(*avcc, avs->codecpar) &lt; 0)&#xA;    {&#xA;        logging("Failed to fill Codec Context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avcodec_open2(*avcc, *avc, NULL) &lt; 0)&#xA;    {&#xA;        logging("Failed to open Codec.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    return 0;&#xA;}&#xA;&#xA;int open_media(const char *in_filename, AVFormatContext **avfc)&#xA;{&#xA;    *avfc = avformat_alloc_context();&#xA;&#xA;    if (!*avfc)&#xA;    {&#xA;        logging("Failed to Allocate Memory for Format Context");&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avformat_open_input(avfc, in_filename, NULL, NULL) != 0)&#xA;    {&#xA;        logging("Failed to open input file %s", in_filename);&#xA;        return -1;&#xA;    }&#xA;&#xA;    if (avformat_find_stream_info(*avfc, NULL) &lt; 0)&#xA;    {&#xA;        logging("Failed to get Stream Info.");&#xA;        return -1;&#xA;    }&#xA;}&#xA;&#xA;int prepare_decoder(StreamingContext *sc)&#xA;{&#xA;    for (int i = 0; i &lt; sc->avfc->nb_streams; i&#x2B;&#x2B;)&#xA;    {&#xA;        if (sc->avfc->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)&#xA;        {&#xA;            sc->video_avs = sc->avfc->streams[i];&#xA;            sc->video_index = i;&#xA;&#xA;            if (fill_stream_info(sc->video_avs, &amp;sc->video_avc, &amp;sc->video_avcc))&#xA;            {&#xA;                return -1;&#xA;            }&#xA;        }&#xA;        else&#xA;        {&#xA;            logging("Skipping Streams other than Video.");&#xA;        }&#xA;    }&#xA;    return 0;&#xA;}&#xA;&#xA;int prepare_video_encoder(StreamingContext *encoder_sc, AVCodecContext *decoder_ctx, AVRational input_framerate,&#xA;                          StreamingParams sp)&#xA;{&#xA;    encoder_sc->video_avs = avformat_new_stream(encoder_sc->avfc, NULL);&#xA;    encoder_sc->video_avc = avcodec_find_encoder_by_name(sp.video_codec);&#xA;    if (!encoder_sc->video_avc)&#xA;    {&#xA;        logging("Cannot find the Codec.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    encoder_sc->video_avcc = avcodec_alloc_context3(encoder_sc->video_avc);&#xA;    if (!encoder_sc->video_avcc)&#xA;    {&#xA;        logging("Could not allocate memory for Codec Context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    av_opt_set(encoder_sc->video_avcc->priv_data, "preset", "fast", 0);&#xA;    if (sp.codec_priv_key &amp;&amp; sp.codec_priv_value)&#xA;        av_opt_set(encoder_sc->video_avcc->priv_data, sp.codec_priv_key, sp.codec_priv_value, 0);&#xA;&#xA;    encoder_sc->video_avcc->height = decoder_ctx->height;&#xA;    encoder_sc->video_avcc->width = decoder_ctx->width;&#xA;    encoder_sc->video_avcc->sample_aspect_ratio = decoder_ctx->sample_aspect_ratio;&#xA;&#xA;    if (encoder_sc->video_avc->pix_fmts)&#xA;        encoder_sc->video_avcc->pix_fmt = encoder_sc->video_avc->pix_fmts[0];&#xA;    else&#xA;        encoder_sc->video_avcc->pix_fmt = decoder_ctx->pix_fmt;&#xA;&#xA;    encoder_sc->video_avcc->bit_rate = 2 * 1000 * 1000;&#xA;    encoder_sc->video_avcc->rc_buffer_size = 4 * 1000 * 1000;&#xA;    encoder_sc->video_avcc->rc_max_rate = 2 * 1000 * 1000;&#xA;    encoder_sc->video_avcc->rc_min_rate = 2.5 * 1000 * 1000;&#xA;&#xA;    encoder_sc->video_avcc->time_base = av_inv_q(input_framerate);&#xA;    encoder_sc->video_avs->time_base = encoder_sc->video_avcc->time_base;&#xA;&#xA;    &#xA;&#xA;    if (avcodec_open2(encoder_sc->video_avcc, encoder_sc->video_avc, NULL) &lt; 0)&#xA;    {&#xA;        logging("Could not open the Codec.");&#xA;        return -1;&#xA;    }&#xA;    avcodec_parameters_from_context(encoder_sc->video_avs->codecpar, encoder_sc->video_avcc);&#xA;    return 0;&#xA;}&#xA;&#xA;int encode_video(StreamingContext *decoder, StreamingContext *encoder, AVFrame *input_frame)&#xA;{&#xA;    if (input_frame)&#xA;        input_frame->pict_type = AV_PICTURE_TYPE_NONE;&#xA;&#xA;    AVPacket *output_packet = av_packet_alloc();&#xA;    if (!output_packet)&#xA;    {&#xA;        logging("Could not allocate memory for Output Packet.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    int response = avcodec_send_frame(encoder->video_avcc, input_frame);&#xA;&#xA;    while (response >= 0)&#xA;    {&#xA;        response = avcodec_receive_packet(encoder->video_avcc, output_packet);&#xA;        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF)&#xA;        {&#xA;            break;&#xA;        }&#xA;        else if (response &lt; 0)&#xA;        {&#xA;            logging("Error while receiving packet from encoder: %s", av_err2str(response));&#xA;            return -1;&#xA;        }&#xA;&#xA;        output_packet->stream_index = decoder->video_index;&#xA;        output_packet->duration = encoder->video_avs->time_base.den / encoder->video_avs->time_base.num / decoder->video_avs->avg_frame_rate.num * decoder->video_avs->avg_frame_rate.den;&#xA;&#xA;        av_packet_rescale_ts(output_packet, decoder->video_avs->time_base, encoder->video_avs->time_base);&#xA;        response = av_interleaved_write_frame(encoder->avfc, output_packet);&#xA;        if (response != 0)&#xA;        {&#xA;            logging("Error %d while receiving packet from decoder: %s", response, av_err2str(response));&#xA;            return -1;&#xA;        }&#xA;    }&#xA;&#xA;    av_packet_unref(output_packet);&#xA;    av_packet_free(&amp;output_packet);&#xA;&#xA;    return 0;&#xA;}&#xA;&#xA;int transcode_video(StreamingContext *decoder, StreamingContext *encoder, AVPacket *input_packet, AVFrame *input_frame, AVFrame *scaled_frame)&#xA;{&#xA;    int response = avcodec_send_packet(decoder->video_avcc, input_packet);&#xA;    if (response &lt; 0)&#xA;    {&#xA;        logging("Error while sending the Packet to Decoder: %s", av_err2str(response));&#xA;        return response;&#xA;    }&#xA;&#xA;    while (response >= 0)&#xA;    {&#xA;        response = avcodec_receive_frame(decoder->video_avcc, input_frame);&#xA;        &#xA;        if (response == AVERROR(EAGAIN) || response == AVERROR_EOF)&#xA;        {&#xA;            break;&#xA;        }&#xA;        else if (response &lt; 0)&#xA;        {&#xA;            logging("Error while receiving frame from Decoder: %s", av_err2str(response));&#xA;            return response;&#xA;        }&#xA;        if (response >= 0)&#xA;        {&#xA;            scaled_frame->pts = input_frame->pts;&#xA;            scaled_frame->pkt_dts = input_frame->pkt_dts;&#xA;            scaled_frame->pict_type = input_frame->pict_type;&#xA;            sws_scale_frame(encoder->sws_ctx, scaled_frame, input_frame);&#xA;            if (encode_video(decoder, encoder, scaled_frame))&#xA;                return -1;&#xA;        }&#xA;&#xA;        av_frame_unref(input_frame);&#xA;    }&#xA;    return 0;&#xA;}&#xA;&#xA;int main(int argc, char *argv[])&#xA;{&#xA;    StreamingParams sp = {0};&#xA;    sp.copy_audio = 1;&#xA;    sp.copy_video = 0;&#xA;    sp.video_codec = "libx265";&#xA;&#xA;&#xA;    StreamingContext *decoder = (StreamingContext *)calloc(1, sizeof(StreamingContext));&#xA;    decoder->filename = argv[1];&#xA;&#xA;    StreamingContext *encoder = (StreamingContext *)calloc(1, sizeof(StreamingContext));&#xA;    encoder->filename = argv[2];&#xA;&#xA;    if (sp.output_extension)&#xA;    {&#xA;        strcat(encoder->filename, sp.output_extension);&#xA;    }&#xA;&#xA;    if (open_media(decoder->filename, &amp;decoder->avfc))&#xA;        return -1;&#xA;    if (prepare_decoder(decoder))&#xA;        return -1;&#xA;&#xA;    avformat_alloc_output_context2(&amp;encoder->avfc, NULL, NULL, encoder->filename);&#xA;    if (!encoder->avfc)&#xA;    {&#xA;        logging("Could not allocate memory for output Format Context.");&#xA;        return -1;&#xA;    }&#xA;&#xA;        AVRational input_framerate = av_guess_frame_rate(decoder->avfc, decoder->video_avs, NULL);&#xA;        prepare_video_encoder(encoder, decoder->video_avcc, input_framerate, sp);&#xA;&#xA;&#xA;    if (encoder->avfc->oformat->flags &amp; AVFMT_GLOBALHEADER)&#xA;        encoder->avfc->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;&#xA;&#xA;    if (!(encoder->avfc->oformat->flags &amp; AVFMT_NOFILE))&#xA;    {&#xA;        if (avio_open(&amp;encoder->avfc->pb, encoder->filename, AVIO_FLAG_WRITE) &lt; 0)&#xA;        {&#xA;            logging("could not open the output file");&#xA;            return -1;&#xA;        }&#xA;    }&#xA;&#xA;    AVDictionary *muxer_opts = NULL;&#xA;&#xA;    if (sp.muxer_opt_key &amp;&amp; sp.muxer_opt_value)&#xA;    {&#xA;        av_dict_set(&amp;muxer_opts, sp.muxer_opt_key, sp.muxer_opt_value, 0);&#xA;    }&#xA;&#xA;    if (avformat_write_header(encoder->avfc, &amp;muxer_opts) &lt; 0)&#xA;    {&#xA;        logging("an error occurred when opening output file");&#xA;        return -1;&#xA;    }&#xA;&#xA;    AVFrame *input_frame = av_frame_alloc();&#xA;    AVFrame *scaled_frame = av_frame_alloc();&#xA;    if (!input_frame || !scaled_frame)&#xA;    {&#xA;        logging("Failed to allocate memory for AVFrame");&#xA;        return -1;&#xA;    }&#xA;&#xA;    // scaled_frame->format = AV_PIX_FMT_YUV420P;&#xA;    scaled_frame->width = 854;&#xA;    scaled_frame->height=480;    &#xA;&#xA;    //Creating Scaling Context&#xA;    encoder->sws_ctx = sws_getContext(1920, 1080,&#xA;                            decoder->video_avcc->pix_fmt, &#xA;                           scaled_frame->width, scaled_frame->height, decoder->video_avcc->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL );&#xA;    if (!encoder->sws_ctx){logging("Cannot Create Scaling Context."); return -1;}&#xA;&#xA;&#xA;    AVPacket *input_packet = av_packet_alloc();&#xA;    if (!input_packet)&#xA;    {&#xA;        logging("Failed to allocate memory for AVPacket.");&#xA;        return -1;&#xA;    }&#xA;&#xA;    while (av_read_frame(decoder->avfc, input_packet) >= 0)&#xA;    {&#xA;        if (decoder->avfc->streams[input_packet->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)&#xA;        {&#xA;                if (transcode_video(decoder, encoder, input_packet, input_frame, scaled_frame))&#xA;                    return -1;&#xA;                av_packet_unref(input_packet);&#xA;        }&#xA;        else&#xA;        {&#xA;            logging("Ignoring all nonvideo  packets.");&#xA;        }&#xA;    }&#xA;&#xA;    if (encode_video(decoder, encoder, NULL))&#xA;        return -1;&#xA;&#xA;    av_write_trailer(encoder->avfc);&#xA;&#xA;    if (muxer_opts != NULL)&#xA;    {&#xA;        av_dict_free(&amp;muxer_opts);&#xA;        muxer_opts = NULL;&#xA;    }&#xA;&#xA;    if (input_frame != NULL)&#xA;    {&#xA;        av_frame_free(&amp;input_frame);&#xA;        input_frame = NULL;&#xA;    }&#xA;&#xA;    if (input_packet != NULL)&#xA;    {&#xA;        av_packet_free(&amp;input_packet);&#xA;        input_packet = NULL;&#xA;    }&#xA;&#xA;    avformat_close_input(&amp;decoder->avfc);&#xA;&#xA;    avformat_free_context(decoder->avfc);&#xA;    decoder->avfc = NULL;&#xA;    avformat_free_context(encoder->avfc);&#xA;    encoder->avfc = NULL;&#xA;&#xA;    avcodec_free_context(&amp;decoder->video_avcc);&#xA;    decoder->video_avcc = NULL;&#xA;    avcodec_free_context(&amp;decoder->audio_avcc);&#xA;    decoder->audio_avcc = NULL;&#xA;&#xA;    free(decoder);&#xA;    decoder = NULL;&#xA;    free(encoder);&#xA;    encoder = NULL;&#xA;&#xA;    return 0;&#xA;}&#xA;

    &#xA;

    The video I am using for testing is available at the repo : https://github.com/leandromoreira/ffmpeg-libav-tutorial

    &#xA;

    The file name is small_bunny_1080p_60fps.mp4

    &#xA;

  • Overlaying a text stream on a video stream with ffmpeg in Node.js

    16 mai 2023, par Tchoune

    I am creating a streaming system with Node.js that uses ffmpeg to send video and text streams to a local RTMP server, then combines those streams and sends them to Twitch.

    &#xA;

    I'm using canvas to create a text image with a transparent background, and I need to change that text every time a new video in the playlist starts.

    &#xA;

    Currently in stream I see only the video stream of my video and not the text. But if I go via VLC to see each more separate, I see them

    &#xA;

    However, I'm running into a problem where the text stream doesn't appear in the final video stream on Twitch. In addition, I get the following error message :

    &#xA;

    Combine stderr: [NULL @ 0x1407069f0] Unable to find a suitable output format for &#x27;rtmp://live.twitch.tv/app/streamKey&#x27;&#xA;rtmp://live.twitch.tv/app/streamKey: Invalid argument&#xA;

    &#xA;

    Here is my current Node.js code :

    &#xA;

    &#xA;const createTextImage = (runner) => {&#xA;    return new Promise((resolve, reject) => {&#xA;        const canvas = createCanvas(1920, 1080);&#xA;        const context = canvas.getContext(&#x27;2d&#x27;);&#xA;&#xA;        // Fill the background with transparency&#xA;        context.fillStyle = &#x27;rgba(0,0,0,0)&#x27;;&#xA;        context.fillRect(0, 0, canvas.width, canvas.height);&#xA;&#xA;        // Set the text options&#xA;        context.fillStyle = &#x27;#ffffff&#x27;;&#xA;        context.font = &#x27;24px Arial&#x27;;&#xA;        context.textAlign = &#x27;start&#x27;;&#xA;        context.textBaseline = &#x27;middle&#x27;;&#xA;&#xA;        // Draw the text&#xA;        context.fillText(`Speedrun by ${runner}`, canvas.width / 2, canvas.height / 2);&#xA;&#xA;        // Define the images directory&#xA;        const imagesDir = path.join(__dirname, &#x27;images&#x27;, &#x27;runners&#x27;);&#xA;&#xA;        // Ensure the images directory exists&#xA;        fs.mkdirSync(imagesDir, { recursive: true });&#xA;&#xA;        // Define the file path&#xA;        const filePath = path.join(imagesDir, runner &#x2B; &#x27;.png&#x27;);&#xA;&#xA;        // Create the write stream&#xA;        const out = fs.createWriteStream(filePath);&#xA;&#xA;        // Create the PNG stream&#xA;        const stream = canvas.createPNGStream();&#xA;&#xA;        // Pipe the PNG stream to the write stream&#xA;        stream.pipe(out);&#xA;&#xA;        out.on(&#x27;finish&#x27;, () => {&#xA;            console.log(&#x27;The PNG file was created.&#x27;);&#xA;            resolve();&#xA;        });&#xA;&#xA;        out.on(&#x27;error&#x27;, reject);&#xA;    });&#xA;}&#xA;const streamVideo = (video) => {&#xA;    ffmpegLibrary.ffprobe(video.video, function (err, metadata) {&#xA;        if (err) {&#xA;            console.error(err);&#xA;            return;&#xA;        }&#xA;        currentVideoDuration = metadata.format.duration;&#xA;&#xA;        // Annulez le d&#xE9;lai pr&#xE9;c&#xE9;dent avant d&#x27;en cr&#xE9;er un nouveau&#xA;        if (nextVideoTimeoutId) {&#xA;            clearTimeout(nextVideoTimeoutId);&#xA;        }&#xA;&#xA;        // D&#xE9;placez votre appel setTimeout ici&#xA;        nextVideoTimeoutId = setTimeout(() => {&#xA;            console.log(&#x27;Fin de la vid&#xE9;o, passage &#xE0; la suivante...&#x27;);&#xA;            nextVideo();&#xA;        }, currentVideoDuration * 1000 &#x2B; 10000);&#xA;    })&#xA;&#xA;&#xA;    ffmpegVideo = childProcess.spawn(&#x27;ffmpeg&#x27;, [&#xA;        &#x27;-nostdin&#x27;, &#x27;-re&#x27;, &#x27;-f&#x27;, &#x27;concat&#x27;, &#x27;-safe&#x27;, &#x27;0&#x27;, &#x27;-i&#x27;, &#x27;playlist.txt&#x27;,&#xA;        &#x27;-vcodec&#x27;, &#x27;libx264&#x27;,&#xA;        &#x27;-s&#x27;, &#x27;1920x1080&#x27;,&#xA;        &#x27;-r&#x27;, &#x27;30&#x27;,&#xA;        &#x27;-b:v&#x27;, &#x27;5000k&#x27;,&#xA;        &#x27;-acodec&#x27;, &#x27;aac&#x27;,&#xA;        &#x27;-preset&#x27;, &#x27;veryfast&#x27;,&#xA;        &#x27;-f&#x27;, &#x27;flv&#x27;,&#xA;        `rtmp://localhost:1935/live/video` // envoie le flux vid&#xE9;o au serveur rtmp local&#xA;    ]);&#xA;&#xA;    createTextImage(video.runner).then(() => {&#xA;        ffmpegText = childProcess.spawn(&#x27;ffmpeg&#x27;, [&#xA;            &#x27;-nostdin&#x27;, &#x27;-re&#x27;,&#xA;            &#x27;-loop&#x27;, &#x27;1&#x27;, &#x27;-i&#x27;, `images/runners/${video.runner}.png`, // Utilise l&#x27;image cr&#xE9;&#xE9;e par Puppeteer&#xA;            &#x27;-vcodec&#x27;, &#x27;libx264rgb&#x27;, // Utilise le codec PNG pour conserver la transparence&#xA;            &#x27;-s&#x27;, &#x27;1920x1080&#x27;,&#xA;            &#x27;-r&#x27;, &#x27;30&#x27;,&#xA;            &#x27;-b:v&#x27;, &#x27;5000k&#x27;,&#xA;            &#x27;-acodec&#x27;, &#x27;aac&#x27;,&#xA;            &#x27;-preset&#x27;, &#x27;veryfast&#x27;,&#xA;            &#x27;-f&#x27;, &#x27;flv&#x27;,&#xA;            `rtmp://localhost:1935/live/text` // envoie le flux de texte au serveur rtmp local&#xA;        ]);&#xA;&#xA;        ffmpegText.stdout.on(&#x27;data&#x27;, (data) => {&#xA;            console.log(`text stdout: ${data}`);&#xA;        });&#xA;&#xA;        ffmpegText.stderr.on(&#x27;data&#x27;, (data) => {&#xA;            console.error(`text stderr: ${data}`);&#xA;        });&#xA;    }).catch(error => {&#xA;        console.error(`Erreur lors de la cr&#xE9;ation de l&#x27;image de texte: ${error}`);&#xA;    });&#xA;&#xA;    ffmpegCombine = childProcess.spawn(&#x27;ffmpeg&#x27;, [&#xA;        &#x27;-i&#x27;, &#x27;rtmp://localhost:1935/live/video&#x27;,&#xA;        &#x27;-i&#x27;, &#x27;rtmp://localhost:1935/live/text&#x27;,&#xA;        &#x27;-filter_complex&#x27;, &#x27;[0:v][1:v]overlay=main_w-overlay_w:0&#x27;,&#xA;        &#x27;-s&#x27;, &#x27;1920x1080&#x27;,&#xA;        &#x27;-r&#x27;, &#x27;30&#x27;,&#xA;        &#x27;-vcodec&#x27;, &#x27;libx264&#x27;,&#xA;        &#x27;-b:v&#x27;, &#x27;5000k&#x27;,&#xA;        &#x27;-acodec&#x27;, &#x27;aac&#x27;,&#xA;        &#x27;-preset&#x27;, &#x27;veryfast&#x27;,&#xA;        &#x27;-f&#x27;, &#x27;flv&#x27;,&#xA;        `rtmp://live.twitch.tv/app/${twitchStreamKey}` // envoie le flux combin&#xE9; &#xE0; Twitch&#xA;    ]);&#xA;&#xA;    ffmpegVideo.stdout.on(&#x27;data&#x27;, (data) => {&#xA;        console.log(`video stdout: ${data}`);&#xA;    });&#xA;&#xA;    ffmpegVideo.stderr.on(&#x27;data&#x27;, (data) => {&#xA;        console.error(`video stderr: ${data}`);&#xA;    });&#xA;&#xA;    ffmpegCombine.stdout.on(&#x27;data&#x27;, (data) => {&#xA;        console.log(`Combine stdout: ${data}`);&#xA;    });&#xA;&#xA;    ffmpegCombine.stderr.on(&#x27;data&#x27;, (data) => {&#xA;        console.error(`Combine stderr: ${data}`);&#xA;    });&#xA;&#xA;    ffmpegCombine.on(&#x27;close&#x27;, (code) => {&#xA;        console.log(`ffmpeg exited with code ${code}`);&#xA;        if (currentIndex >= playlist.length) {&#xA;            console.log(&#x27;End of playlist&#x27;);&#xA;            currentIndex = 0;&#xA;        }&#xA;    });&#xA;}&#xA;&#xA;

    &#xA;

    Locally I use nginx with rtmp module to manage multi-streams and combined into one to send to twitch

    &#xA;

    In NGINX it's my nginx.conf for module :

    &#xA;

    rtmp {&#xA;    server {&#xA;        listen 1935; # le port pour le protocole RTMP&#xA;        &#xA;        application live {&#xA;            live on; # active le streaming en direct&#xA;            record off; # d&#xE9;sactive l&#x27;enregistrement du flux&#xA;    &#xA;            # d&#xE9;finit l&#x27;endroit o&#xF9; les flux doivent &#xEA;tre envoy&#xE9;s&#xA;            push rtmp://live.twitch.tv/app/liveKey;&#xA;        }&#xA;    &#xA;        application text {&#xA;            live on; # active le streaming en direct&#xA;            record off; # d&#xE9;sactive l&#x27;enregistrement du flux&#xA;        }&#xA;    }&#xA;}&#xA;

    &#xA;

    I have checked that the codecs, resolution and frame rate are the same for both streams. I am also overlaying the text stream on top of the video stream with the -filter_complex command, but I am not sure if it works correctly.

    &#xA;

    Does each stream have to have the same parameters ?

    &#xA;

    I would like to know if anyone has any idea what could be causing this problem and how to fix it. Should I use a different format for the output stream to Twitch ? Or is there another approach I should consider for layering a dynamic text stream over a video stream ?

    &#xA;

    Also, I'm wondering if I'm handling updating the text stream correctly when the video changes. Currently, I create a new text image with Canvas every time the video changes, then create a new ffmpeg process for the text stream. Is this the right approach, or is there a better way to handle this ?

    &#xA;

    Thanks in advance for any help or advice.

    &#xA;