Recherche avancée

Médias (10)

Mot : - Tags -/wav

Autres articles (94)

  • Organiser par catégorie

    17 mai 2013, par

    Dans MédiaSPIP, une rubrique a 2 noms : catégorie et rubrique.
    Les différents documents stockés dans MédiaSPIP peuvent être rangés dans différentes catégories. On peut créer une catégorie en cliquant sur "publier une catégorie" dans le menu publier en haut à droite ( après authentification ). Une catégorie peut être rangée dans une autre catégorie aussi ce qui fait qu’on peut construire une arborescence de catégories.
    Lors de la publication prochaine d’un document, la nouvelle catégorie créée sera proposée (...)

  • Ajouter des informations spécifiques aux utilisateurs et autres modifications de comportement liées aux auteurs

    12 avril 2011, par

    La manière la plus simple d’ajouter des informations aux auteurs est d’installer le plugin Inscription3. Il permet également de modifier certains comportements liés aux utilisateurs (référez-vous à sa documentation pour plus d’informations).
    Il est également possible d’ajouter des champs aux auteurs en installant les plugins champs extras 2 et Interface pour champs extras.

  • 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

Sur d’autres sites (8344)

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

  • Failed to decode h264 key frame with DXVA2.0 because returned buffer is to small

    16 mai 2023, par grill2010

    I hava a strange problem on Windows with DXVA2 h264 decoding. I recently figured out a ffmpeg decoding limitation for DXVA2 and D3D11VA on Windows and how to solve it, this solution completly fixes the problem with D3D11VA but DXVA2 still has some problems with certain keyframes. Upon further investigation it turned out that the decoding of these certain keyframes fail because the buffer returned from the IDirectXVideoDecoder_GetBuffer function was too small. FFmpeg is printing out these logs when the decoding fails :

    &#xA;

    Error: [h264 @ 0000028b2e5796c0] Buffer for type 5 was too small. size: 58752, dxva_size: 55296&#xA;Error: [h264 @ 0000028b2e5796c0] Failed to add bitstream or slice control buffer&#xA;Error: [h264 @ 0000028b2e5796c0] hardware accelerator failed to decode picture&#xA;

    &#xA;

    Why is this returned buffer too low ? What kind of factors inside ffmpeg do have an effect on this buffer size or is this a limitation of DXVA2 in general ? All other decoders like Cuvid, D3D11VA or the software decoder are not affected by this problem and can decode all keyframes.

    &#xA;

    I have an example javacv project on github that can reproduce the problem. I also provide the source code of the main class here. The keyframe example data with prepended SPS and PPS in hex form can be downloaded here.

    &#xA;

    import javafx.application.Application;&#xA;import javafx.scene.Scene;&#xA;import javafx.scene.layout.Pane;&#xA;import javafx.scene.layout.StackPane;&#xA;import javafx.stage.Stage;&#xA;import org.bytedeco.ffmpeg.avcodec.AVCodec;&#xA;import org.bytedeco.ffmpeg.avcodec.AVCodecContext;&#xA;import org.bytedeco.ffmpeg.avcodec.AVCodecHWConfig;&#xA;import org.bytedeco.ffmpeg.avcodec.AVPacket;&#xA;import org.bytedeco.ffmpeg.avutil.AVBufferRef;&#xA;import org.bytedeco.ffmpeg.avutil.AVDictionary;&#xA;import org.bytedeco.ffmpeg.avutil.AVFrame;&#xA;import org.bytedeco.ffmpeg.avutil.LogCallback;&#xA;import org.bytedeco.javacpp.BytePointer;&#xA;import org.bytedeco.javacpp.IntPointer;&#xA;import org.bytedeco.javacpp.Pointer;&#xA;import org.tinylog.Logger;&#xA;&#xA;import java.io.IOException;&#xA;import java.io.InputStream;&#xA;import java.nio.charset.StandardCharsets;&#xA;import java.util.Objects;&#xA;import java.util.function.Consumer;&#xA;&#xA;import static org.bytedeco.ffmpeg.avcodec.AVCodecContext.FF_THREAD_SLICE;&#xA;import static org.bytedeco.ffmpeg.global.avcodec.*;&#xA;import static org.bytedeco.ffmpeg.global.avutil.*;&#xA;&#xA;public class App extends Application {&#xA;&#xA;    /**** decoder variables ****/&#xA;&#xA;    private AVHWContextInfo hardwareContext;&#xA;&#xA;    private AVCodec decoder;&#xA;    private AVCodecContext m_VideoDecoderCtx;&#xA;&#xA;    private AVCodecContext.Get_format_AVCodecContext_IntPointer formatCallback;&#xA;&#xA;    private final int streamResolutionX = 1920;&#xA;    private final int streamResolutionY = 1080;&#xA;&#xA;    // AV_HWDEVICE_TYPE_CUDA // example works with cuda&#xA;    // AV_HWDEVICE_TYPE_DXVA2 // producing Invalid data found on keyframe&#xA;    // AV_HWDEVICE_TYPE_D3D11VA // producing Invalid data found on keyframe&#xA;    private static final int HW_DEVICE_TYPE = AV_HWDEVICE_TYPE_DXVA2;&#xA;&#xA;    private static final boolean USE_HW_ACCEL = true;&#xA;&#xA;    private static final boolean USE_AV_EF_EXPLODE = true;&#xA;&#xA;    public static void main(final String[] args) {&#xA;        //System.setProperty("prism.order", "d3d,sw");&#xA;        System.setProperty("prism.vsync", "false");&#xA;        Application.launch(App.class);&#xA;    }&#xA;&#xA;    @Override&#xA;    public void start(final Stage primaryStage) {&#xA;        final Pane dummyPane = new Pane();&#xA;        dummyPane.setStyle("-fx-background-color: black");&#xA;        final Scene scene = new Scene(dummyPane, this.streamResolutionX, this.streamResolutionY);&#xA;        primaryStage.setScene(scene);&#xA;        primaryStage.show();&#xA;        primaryStage.setMinWidth(480);&#xA;        primaryStage.setMinHeight(360);&#xA;&#xA;        this.initializeFFmpeg(result -> {&#xA;            if (!result) {&#xA;                Logger.error("FFmpeg could not be initialized correctly, terminating program");&#xA;                System.exit(1);&#xA;                return;&#xA;            }&#xA;            scene.setRoot(new StackPane());&#xA;            this.performTestFramesFeeding();&#xA;        });&#xA;    }&#xA;&#xA;    private void initializeFFmpeg(final Consumer<boolean> finishHandler) {&#xA;        FFmpegLogCallback.setLevel(AV_LOG_DEBUG); // Increase log level until the first frame is decoded&#xA;        FFmpegLogCallback.set();&#xA;        Pointer pointer = new Pointer((Pointer) null);&#xA;        AVCodec c;&#xA;        while ((c = av_codec_iterate(pointer)) != null) {&#xA;            if (av_codec_is_decoder(c) > 0)&#xA;                Logger.debug("{}:{} ", c.name().getString(), c.type());&#xA;        }&#xA;&#xA;        this.decoder = avcodec_find_decoder(AV_CODEC_ID_H264); // usually decoder name is h264 and without hardware support it&#x27;s yuv420p otherwise nv12&#xA;        if (this.decoder == null) {&#xA;            Logger.error("Unable to find decoder for format {}", "h264");&#xA;            finishHandler.accept(false);&#xA;            return;&#xA;        }&#xA;        Logger.info("Current decoder name: {}, {}", this.decoder.name().getString(), this.decoder.long_name().getString());&#xA;&#xA;        if (true) {&#xA;            for (; ; ) {&#xA;                this.m_VideoDecoderCtx = avcodec_alloc_context3(this.decoder);&#xA;                if (this.m_VideoDecoderCtx == null) {&#xA;                    Logger.error("Unable to find decoder for format AV_CODEC_ID_H264");&#xA;                    if (this.hardwareContext != null) {&#xA;                        this.hardwareContext.free();&#xA;                        this.hardwareContext = null;&#xA;                    }&#xA;                    continue;&#xA;                }&#xA;&#xA;                if (App.USE_HW_ACCEL) {&#xA;                    this.hardwareContext = this.createHardwareContext();&#xA;                    if (this.hardwareContext != null) {&#xA;                        Logger.info("Set hwaccel support");&#xA;                        this.m_VideoDecoderCtx.hw_device_ctx(this.hardwareContext.hwContext()); // comment to disable hwaccel&#xA;                    }&#xA;                } else {&#xA;                    Logger.info("Hwaccel manually disabled");&#xA;                }&#xA;&#xA;                // Always request low delay decoding&#xA;                this.m_VideoDecoderCtx.flags(this.m_VideoDecoderCtx.flags() | AV_CODEC_FLAG_LOW_DELAY);&#xA;&#xA;                // Allow display of corrupt frames and frames missing references&#xA;                this.m_VideoDecoderCtx.flags(this.m_VideoDecoderCtx.flags() | AV_CODEC_FLAG_OUTPUT_CORRUPT);&#xA;                this.m_VideoDecoderCtx.flags2(this.m_VideoDecoderCtx.flags2() | AV_CODEC_FLAG2_SHOW_ALL);&#xA;&#xA;                if (App.USE_AV_EF_EXPLODE) {&#xA;                    // Report decoding errors to allow us to request a key frame&#xA;                    this.m_VideoDecoderCtx.err_recognition(this.m_VideoDecoderCtx.err_recognition() | AV_EF_EXPLODE);&#xA;                }&#xA;&#xA;                // Enable slice multi-threading for software decoding&#xA;                if (this.m_VideoDecoderCtx.hw_device_ctx() == null) { // if not hw accelerated&#xA;                    this.m_VideoDecoderCtx.thread_type(this.m_VideoDecoderCtx.thread_type() | FF_THREAD_SLICE);&#xA;                    this.m_VideoDecoderCtx.thread_count(2/*AppUtil.getCpuCount()*/);&#xA;                } else {&#xA;                    // No threading for HW decode&#xA;                    this.m_VideoDecoderCtx.thread_count(1);&#xA;                }&#xA;&#xA;                this.m_VideoDecoderCtx.width(this.streamResolutionX);&#xA;                this.m_VideoDecoderCtx.height(this.streamResolutionY);&#xA;                this.m_VideoDecoderCtx.pix_fmt(this.getDefaultPixelFormat());&#xA;&#xA;                this.formatCallback = new AVCodecContext.Get_format_AVCodecContext_IntPointer() {&#xA;                    @Override&#xA;                    public int call(final AVCodecContext context, final IntPointer pixelFormats) {&#xA;                        final boolean hwDecodingSupported = context.hw_device_ctx() != null &amp;&amp; App.this.hardwareContext != null;&#xA;                        final int preferredPixelFormat = hwDecodingSupported ?&#xA;                                App.this.hardwareContext.hwConfig().pix_fmt() :&#xA;                                context.pix_fmt();&#xA;                        int i = 0;&#xA;                        while (true) {&#xA;                            final int currentSupportedFormat = pixelFormats.get(i&#x2B;&#x2B;);&#xA;                            System.out.println("Supported pixel formats " &#x2B; currentSupportedFormat);&#xA;                            if (currentSupportedFormat == AV_PIX_FMT_NONE) {&#xA;                                break;&#xA;                            }&#xA;                        }&#xA;&#xA;                        i = 0;&#xA;                        while (true) {&#xA;                            final int currentSupportedFormat = pixelFormats.get(i&#x2B;&#x2B;);&#xA;                            if (currentSupportedFormat == preferredPixelFormat) {&#xA;                                Logger.info("[FFmpeg]: pixel format in format callback is {}", currentSupportedFormat);&#xA;                                return currentSupportedFormat;&#xA;                            }&#xA;                            if (currentSupportedFormat == AV_PIX_FMT_NONE) {&#xA;                                break;&#xA;                            }&#xA;                        }&#xA;&#xA;                        i = 0;&#xA;                        while (true) { // try again and search for yuv&#xA;                            final int currentSupportedFormat = pixelFormats.get(i&#x2B;&#x2B;);&#xA;                            if (currentSupportedFormat == AV_PIX_FMT_YUV420P) {&#xA;                                Logger.info("[FFmpeg]: Not found in first match so use {}", AV_PIX_FMT_YUV420P);&#xA;                                return currentSupportedFormat;&#xA;                            }&#xA;                            if (currentSupportedFormat == AV_PIX_FMT_NONE) {&#xA;                                break;&#xA;                            }&#xA;                        }&#xA;&#xA;                        i = 0;&#xA;                        while (true) { // try again and search for nv12&#xA;                            final int currentSupportedFormat = pixelFormats.get(i&#x2B;&#x2B;);&#xA;                            if (currentSupportedFormat == AV_PIX_FMT_NV12) {&#xA;                                Logger.info("[FFmpeg]: Not found in second match so use {}", AV_PIX_FMT_NV12);&#xA;                                return currentSupportedFormat;&#xA;                            }&#xA;                            if (currentSupportedFormat == AV_PIX_FMT_NONE) {&#xA;                                break;&#xA;                            }&#xA;                        }&#xA;&#xA;                        Logger.info("[FFmpeg]: pixel format in format callback is using fallback {}", AV_PIX_FMT_NONE);&#xA;                        return AV_PIX_FMT_NONE;&#xA;                    }&#xA;                };&#xA;                this.m_VideoDecoderCtx.get_format(this.formatCallback);&#xA;&#xA;                final AVDictionary options = new AVDictionary(null);&#xA;                final int result = avcodec_open2(this.m_VideoDecoderCtx, this.decoder, options);&#xA;                if (result &lt; 0) {&#xA;                    Logger.error("avcodec_open2 was not successful");&#xA;                    finishHandler.accept(false);&#xA;                    return;&#xA;                }&#xA;                av_dict_free(options);&#xA;                break;&#xA;            }&#xA;        }&#xA;&#xA;        if (this.decoder == null || this.m_VideoDecoderCtx == null) {&#xA;            finishHandler.accept(false);&#xA;            return;&#xA;        }&#xA;        finishHandler.accept(true);&#xA;    }&#xA;&#xA;    private AVHWContextInfo createHardwareContext() {&#xA;        AVHWContextInfo result = null;&#xA;        for (int i = 0; ; i&#x2B;&#x2B;) {&#xA;            final AVCodecHWConfig config = avcodec_get_hw_config(this.decoder, i);&#xA;            if (config == null) {&#xA;                break;&#xA;            }&#xA;&#xA;            if ((config.methods() &amp; AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) &lt; 0) {&#xA;                continue;&#xA;            }&#xA;            final int device_type = config.device_type();&#xA;            if (device_type != App.HW_DEVICE_TYPE) {&#xA;                continue;&#xA;            }&#xA;            final AVBufferRef hw_context = av_hwdevice_ctx_alloc(device_type);&#xA;            if (hw_context == null || av_hwdevice_ctx_create(hw_context, device_type, (String) null, null, 0) &lt; 0) {&#xA;                Logger.error("HW accel not supported for type {}", device_type);&#xA;                av_free(config);&#xA;                av_free(hw_context);&#xA;            } else {&#xA;                Logger.info("HW accel created for type {}", device_type);&#xA;                result = new AVHWContextInfo(config, hw_context);&#xA;            }&#xA;            break;&#xA;        }&#xA;&#xA;        return result;&#xA;    }&#xA;&#xA;    @Override&#xA;    public void stop() {&#xA;        this.releaseNativeResources();&#xA;    }&#xA;&#xA;    /*****************************/&#xA;    /*** test frame processing ***/&#xA;    /*****************************/&#xA;    &#xA;    private void performTestFramesFeeding() {&#xA;        final AVPacket pkt = av_packet_alloc();&#xA;        if (pkt == null) {&#xA;            return;&#xA;        }&#xA;        try (final BytePointer bp = new BytePointer(65_535 * 15)) {&#xA;&#xA;&#xA;            for (int i = 0; i &lt; 1; i&#x2B;&#x2B;) {&#xA;                final byte[] frameData = AVTestFrames.h264KeyTestFrame;&#xA;&#xA;                bp.position(0);&#xA;&#xA;                bp.put(frameData);&#xA;                bp.limit(frameData.length);&#xA;&#xA;                pkt.data(bp);&#xA;                pkt.capacity(bp.capacity());&#xA;                pkt.size(frameData.length);&#xA;                pkt.position(0);&#xA;                pkt.limit(frameData.length);&#xA;                //pkt.flags(AV_PKT_FLAG_KEY);&#xA;                final AVFrame avFrame = av_frame_alloc();&#xA;                System.out.println("frameData.length " &#x2B; frameData.length);&#xA;&#xA;                final int err = avcodec_send_packet(this.m_VideoDecoderCtx, pkt); //fill_scaling_lists&#xA;                if (err &lt; 0) {&#xA;                    final BytePointer buffer = new BytePointer(512);&#xA;                    av_strerror(err, buffer, buffer.capacity());&#xA;                    final String string = buffer.getString();&#xA;                    System.out.println("Error on decoding test frame " &#x2B; err &#x2B; " message " &#x2B; string);&#xA;                    av_frame_free(avFrame);&#xA;                    return;&#xA;                }&#xA;&#xA;                final int result = avcodec_receive_frame(this.m_VideoDecoderCtx, avFrame);&#xA;                final AVFrame decodedFrame;&#xA;                if (result == 0) {&#xA;                    if (this.m_VideoDecoderCtx.hw_device_ctx() == null) {&#xA;                        decodedFrame = avFrame;&#xA;                        System.out.println("SUCESS with SW decoding");&#xA;                    } else {&#xA;                        final AVFrame hwAvFrame = av_frame_alloc();&#xA;                        if (av_hwframe_transfer_data(hwAvFrame, avFrame, 0) &lt; 0) {&#xA;                            System.out.println("Failed to transfer frame from hardware");&#xA;                            av_frame_unref(hwAvFrame);&#xA;                            decodedFrame = avFrame;&#xA;                        } else {&#xA;                            av_frame_unref(avFrame);&#xA;                            decodedFrame = hwAvFrame;&#xA;                            System.out.println("SUCESS with HW decoding");&#xA;                        }&#xA;                    }&#xA;&#xA;                    av_frame_unref(decodedFrame);&#xA;                } else {&#xA;                    final BytePointer buffer = new BytePointer(512);&#xA;                    av_strerror(result, buffer, buffer.capacity());&#xA;                    final String string = buffer.getString();&#xA;                    System.out.println("error " &#x2B; result &#x2B; " message " &#x2B; string);&#xA;                    av_frame_free(avFrame);&#xA;                }&#xA;            }&#xA;        } finally {&#xA;            if (pkt.stream_index() != -1) {&#xA;                av_packet_unref(pkt);&#xA;            }&#xA;            pkt.releaseReference();&#xA;        }&#xA;    }&#xA;&#xA;    final Object releaseLock = new Object();&#xA;    private volatile boolean released = false;&#xA;&#xA;    private void releaseNativeResources() {&#xA;        if (this.released) {&#xA;            return;&#xA;        }&#xA;        this.released = true;&#xA;        synchronized (this.releaseLock) {&#xA;            // Close the video codec&#xA;            if (this.m_VideoDecoderCtx != null) {&#xA;                avcodec_free_context(this.m_VideoDecoderCtx);&#xA;                this.m_VideoDecoderCtx = null;&#xA;            }&#xA;&#xA;            // close the format callback&#xA;            if (this.formatCallback != null) {&#xA;                this.formatCallback.close();&#xA;                this.formatCallback = null;&#xA;            }&#xA;&#xA;            // close hw context&#xA;            if (this.hardwareContext != null) {&#xA;                this.hardwareContext.free();&#xA;            }&#xA;        }&#xA;    }&#xA;&#xA;    private int getDefaultPixelFormat() {&#xA;        return AV_PIX_FMT_YUV420P; // Always return yuv420p here&#xA;    }&#xA;&#xA;&#xA;    /*********************/&#xA;    /*** inner classes ***/&#xA;    /*********************/&#xA;&#xA;    public static final class HexUtil {&#xA;&#xA;        private HexUtil() {&#xA;        }&#xA;&#xA;        public static byte[] unhexlify(final String argbuf) {&#xA;            final int arglen = argbuf.length();&#xA;            if (arglen % 2 != 0) {&#xA;                throw new RuntimeException("Odd-length string");&#xA;            } else {&#xA;                final byte[] retbuf = new byte[arglen / 2];&#xA;&#xA;                for (int i = 0; i &lt; arglen; i &#x2B;= 2) {&#xA;                    final int top = Character.digit(argbuf.charAt(i), 16);&#xA;                    final int bot = Character.digit(argbuf.charAt(i &#x2B; 1), 16);&#xA;                    if (top == -1 || bot == -1) {&#xA;                        throw new RuntimeException("Non-hexadecimal digit found");&#xA;                    }&#xA;&#xA;                    retbuf[i / 2] = (byte) ((top &lt;&lt; 4) &#x2B; bot);&#xA;                }&#xA;&#xA;                return retbuf;&#xA;            }&#xA;        }&#xA;    }&#xA;&#xA;    public static final class AVHWContextInfo {&#xA;        private final AVCodecHWConfig hwConfig;&#xA;        private final AVBufferRef hwContext;&#xA;&#xA;        private volatile boolean freed = false;&#xA;&#xA;        public AVHWContextInfo(final AVCodecHWConfig hwConfig, final AVBufferRef hwContext) {&#xA;            this.hwConfig = hwConfig;&#xA;            this.hwContext = hwContext;&#xA;        }&#xA;&#xA;        public AVCodecHWConfig hwConfig() {&#xA;            return this.hwConfig;&#xA;        }&#xA;&#xA;        public AVBufferRef hwContext() {&#xA;            return this.hwContext;&#xA;        }&#xA;&#xA;        public void free() {&#xA;            if (this.freed) {&#xA;                return;&#xA;            }&#xA;            this.freed = true;&#xA;            av_free(this.hwConfig);&#xA;            av_free(this.hwContext);&#xA;        }&#xA;&#xA;&#xA;        @Override&#xA;        public boolean equals(Object o) {&#xA;            if (this == o) return true;&#xA;            if (o == null || getClass() != o.getClass()) return false;&#xA;            AVHWContextInfo that = (AVHWContextInfo) o;&#xA;            return freed == that.freed &amp;&amp; Objects.equals(hwConfig, that.hwConfig) &amp;&amp; Objects.equals(hwContext, that.hwContext);&#xA;        }&#xA;&#xA;        @Override&#xA;        public int hashCode() {&#xA;            return Objects.hash(hwConfig, hwContext, freed);&#xA;        }&#xA;&#xA;        @Override&#xA;        public String toString() {&#xA;            return "AVHWContextInfo[" &#x2B;&#xA;                    "hwConfig=" &#x2B; this.hwConfig &#x2B; ", " &#x2B;&#xA;                    "hwContext=" &#x2B; this.hwContext &#x2B; &#x27;]&#x27;;&#xA;        }&#xA;    }&#xA;&#xA;    public static final class AVTestFrames {&#xA;&#xA;        private AVTestFrames() {&#xA;&#xA;        }&#xA;&#xA;        static {&#xA;            InputStream inputStream = null;&#xA;            try {&#xA;                inputStream = AVTestFrames.class.getClassLoader().getResourceAsStream("h264_test_key_frame.txt");&#xA;                final byte[] h264TestFrameBuffer = inputStream == null ? new byte[0] : inputStream.readAllBytes();&#xA;                final String h264TestFrame = new String(h264TestFrameBuffer, StandardCharsets.UTF_8);&#xA;                AVTestFrames.h264KeyTestFrame = HexUtil.unhexlify(h264TestFrame);&#xA;            } catch (final IOException e) {&#xA;                Logger.error(e, "Could not parse test frame");&#xA;            } finally {&#xA;                if (inputStream != null) {&#xA;                    try {&#xA;                        inputStream.close();&#xA;                    } catch (final IOException e) {&#xA;                        Logger.error(e, "Could not close test frame input stream");&#xA;                    }&#xA;                }&#xA;            }&#xA;        }&#xA;&#xA;        public static byte[] h264KeyTestFrame;&#xA;    }&#xA;&#xA;    public static class FFmpegLogCallback extends LogCallback {&#xA;&#xA;        private static final org.bytedeco.javacpp.tools.Logger logger = org.bytedeco.javacpp.tools.Logger.create(FFmpegLogCallback.class);&#xA;&#xA;        static final FFmpegLogCallback instance = new FFmpegLogCallback().retainReference();&#xA;&#xA;        public static FFmpegLogCallback getInstance() {&#xA;            return instance;&#xA;        }&#xA;&#xA;        /**&#xA;         * Calls {@code avutil.setLogCallback(getInstance())}.&#xA;         */&#xA;        public static void set() {&#xA;            setLogCallback(getInstance());&#xA;        }&#xA;&#xA;        /**&#xA;         * Returns {@code av_log_get_level()}.&#xA;         **/&#xA;        public static int getLevel() {&#xA;            return av_log_get_level();&#xA;        }&#xA;&#xA;        /**&#xA;         * Calls {@code av_log_set_level(level)}.&#xA;         **/&#xA;        public static void setLevel(int level) {&#xA;            av_log_set_level(level);&#xA;        }&#xA;&#xA;        @Override&#xA;        public void call(int level, BytePointer msg) {&#xA;            switch (level) {&#xA;                case AV_LOG_PANIC, AV_LOG_FATAL, AV_LOG_ERROR -> logger.error(msg.getString());&#xA;                case AV_LOG_WARNING -> logger.warn(msg.getString());&#xA;                case AV_LOG_INFO -> logger.info(msg.getString());&#xA;                case AV_LOG_VERBOSE, AV_LOG_DEBUG, AV_LOG_TRACE -> logger.debug(msg.getString());&#xA;                default -> {&#xA;                    assert false;&#xA;                }&#xA;            }&#xA;        }&#xA;    }&#xA;}&#xA;</boolean>

    &#xA;