When using ffmpeg api, the encoded gif is output to the dynamically applied buffer

56 Views Asked by At

Purpose:I want to use ffmpeg to encode data from rbg8 to gif format.Currently can be encoded into file output.

But I don't know how to store the gif directly in the buffer instead of saving it in the file.

Below is the text of the code: input_w560_h1280.rgb8

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>

#define CONFIG_FBDEV_OUTDEV 1
#define GET_DATA_TO_BUFF 0

int readFileToBuffer(uint8_t *dst, const char* filename, long* fileSize) {
    FILE* file = fopen(filename, "rb");
    if (!file || dst == NULL) {
        fprintf(stderr, "cannot open file: %s %p\n", filename, dst);
        return -1;
    }

    fseek(file, 0, SEEK_END);
    *fileSize = ftell(file);
    fseek(file, 0, SEEK_SET);

    uint8_t *buffer = NULL;
    buffer = dst;
    if (!buffer) {
        fprintf(stderr, "buffer error\n");
        fclose(file);
        return -1;
    }

    // fread file to buffer
    if (fread(buffer, 1, *fileSize, file) != *fileSize) {
        fprintf(stderr, "read file failed size %ld\n", *fileSize);
        fclose(file);
        free(buffer);
        return -1;
    }

    fclose(file);
    return 0;
}

int writeBufferToFile(const char* filename, uint8_t* buffer, long bufferSize) {
    FILE* file = fopen(filename, "wb");
    if (!file) {
        fprintf(stderr, "cannot open: %s\n", filename);
        return -1;
    }

    if (fwrite(buffer, 1, bufferSize, file) != bufferSize) {
        fprintf(stderr, "cannot fwrite\n");
        fclose(file);
        return -1;
    }

    fclose(file);
    return 0;
}

int main() {
    const char *output_filename = "output.gif";
    const char *input_filename = "input_w560_h1280.rgb8";
    int width = 560;
    int height = 1280;

    avcodec_register_all();
    avdevice_register_all();
    av_register_all();

    av_log_set_level(AV_LOG_MAX_OFFSET);

    AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_GIF);
    if (!codec) {
        printf("GIF encoder not found.\n");
        return 1;
    }
    
    AVCodecContext *codecContext = avcodec_alloc_context3(codec);
    if (!codecContext) {
        printf("Could not allocate codec codecContext.\n");
        return 1;
    }

    codecContext->width = width;
    codecContext->height = height;
    codecContext->pix_fmt = AV_PIX_FMT_RGB8;
    codecContext->time_base = (AVRational){1, 25};
    
    if (avcodec_open2(codecContext, codec, NULL) < 0) {
        printf("Could not open codec.\n");
        return 1;
    }

    AVFormatContext *outputFormatContext = NULL;
    if (avformat_alloc_output_context2(&outputFormatContext, NULL, NULL, output_filename) < 0) {
        printf("Could not allocate output format codecContext.\n");
        return 1;
    }

    AVStream *stream = avformat_new_stream(outputFormatContext, NULL);
    if (!stream) {
        printf("Could not create stream.\n");
        return 1;
    }

    AVCodecParameters *codec_params = stream->codecpar;
    avcodec_parameters_from_context(codec_params, codecContext);

    if (avio_open(&outputFormatContext->pb, output_filename, AVIO_FLAG_WRITE) < 0) {
        printf("Could not open output file.\n");
        return 1;
    }

    /* write header for output */
    if(avformat_write_header(outputFormatContext, NULL) < 0)
    {
        printf("avformat_write_header failed\n");
    }

    // creat and init frame
    AVFrame *frame = av_frame_alloc();
    frame->format = AV_PIX_FMT_RGB8;
    frame->width = width;
    frame->height = height;
    /* check frame buff */
    if (av_frame_get_buffer(frame, 0) < 0) {
        printf("Failed to allocate frame buffer.\n");
        return -1;
    }

    /* read rgb data to  frame->data[0] */
    uint8_t *rgb8data = (uint8_t *)av_malloc(width * height * 3);
    long read_size = 0;
    int ret = readFileToBuffer(rgb8data, input_filename, &read_size);
    if (ret != 0 || read_size == 0)
    {
        printf("error argbData %p read_size %ld\n", rgb8data, read_size);
    }
    /* cp input data to frame */
    av_image_copy_plane(frame->data[0], frame->linesize[0], rgb8data, width, width, height);

    // Init IOcontext (try to get the gif stream)
    #if GET_DATA_TO_BUFF
    AVIOContext *outputIoContext = NULL;

    if (avio_open_dyn_buf(&outputIoContext) < 0) {
        printf("Failed to open output buffer.\n");
        return -1;
    }
    #endif
    // Encoding loop (simulated frames)
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    avcodec_send_frame(codecContext, frame);

    while (avcodec_receive_packet(codecContext, &pkt) == 0) {
        pkt.stream_index = stream->index;
        #if GET_DATA_TO_BUFF // try to get the gif stream
        avio_write(outputIoContext, pkt.data, pkt.size);
        #endif
        av_write_frame(outputFormatContext, &pkt);
        av_packet_unref(&pkt);
    }
    /* write end */
    av_write_trailer(outputFormatContext);
    // try to get the gif stream
    #if GET_DATA_TO_BUFF
    uint8_t *outputBuffer;
    int outputBufferSize = avio_close_dyn_buf(outputIoContext, &outputBuffer);
    writeBufferToFile("bf_1.gif", outputBuffer, outputBufferSize);
    #endif
    /* free */
    avformat_close_input(&outputFormatContext);
    av_frame_free(&frame);
    avcodec_free_context(&codecContext);
    av_free(rgb8data);

    return 0;
}

MAKEFILE

CC = gcc
CFLAGS = -Wall -g
CFLAGS += -I ../output/include
CFLAGS += -L ../output/lib
LIBS = -l:libavdevice.a -l:libavformat.a -l:libavcodec.a -lavfilter -lswresample -lswscale -l:libavutil.a -lpthread -lz  -lbz2 -lm -ldl

all: encode_to_gif

encode_to_gif: encode_to_gif.c
    $(CC) $(CFLAGS) -o $@ $< $(LIBS)

Tried The content in the macro definition is the relevant solution I found online. Unfortunately, the data I got is in original rgb8 format, not in gif format.ffmpeg writes data to file and Data dumped from buffer The data dumped from the buffer seems to have no gif header

My expected result I want to output the encoded gif directly to the buffer,but i don't know what to do.

1

There are 1 best solutions below

0
yangjinhui2936 On

use avio_alloc_context can output this stream to buffer.

int write_buffer(void *opaque, uint8_t *buf, int buf_size){
    if (opaque != NULL)
    {
        OUT_BUFF *outBuffer = (OUT_BUFF *)opaque;
        if (outBuffer->buffer != NULL)
        {
            memcpy(outBuffer->buffer + outBuffer->size, buf, buf_size);
            outBuffer->size += buf_size;
            printf("size %d buf_size %d\n", outBuffer->size, buf_size);
        }
        return outBuffer->size;
    }
    else
    {
        printf("opaque is null\n");
    }

    return -1;
}
{
    OUT_BUFF stOutBuffer = {0};
    int buffer_size = width*height*3;
    stOutBuffer.buffer = (uint8_t *)malloc(buffer_size);
    stOutBuffer.size = 0;
    unsigned char *output_buffer = (unsigned char *)malloc(buffer_size);
    int output_buffer_size = buffer_size;
    outputIoContext = avio_alloc_context(output_buffer, output_buffer_size, 1, (void *)&stOutBuffer, NULL, write_buffer, NULL);
}