使用ffmpeg api时,编码后的gif输出到动态应用的缓冲区

问题描述 投票:0回答:1

目的:我想用ffmpeg将rbg8的数据编码为gif格式。目前可以编码成文件输出。

但是我不知道如何将gif直接存储在缓冲区中而不是保存在文件中。

下面是代码正文: 输入_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;
}

生成文件

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)

尝试过宏定义中的内容是我在网上找到的相关解决方案。不幸的是,我得到的数据是原始的rgb8格式,而不是gif格式。ffmpeg writes data to fileData dumped from buffer 从缓冲区转储的数据似乎没有gif header

我的预期结果我想将编码后的gif直接输出到缓冲区,但我不知道该怎么办。

ffmpeg gif codec libavcodec
1个回答
0
投票

使用avio_alloc_context可以将此流输出到缓冲区。

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);
}
© www.soinside.com 2019 - 2024. All rights reserved.