使用FFmpeg API将RGB8编码为GIF并输出到动态缓冲区的问题
需求目的
我希望使用FFmpeg将RGB8格式的数据编码为GIF格式,目前已实现编码后输出到文件,但不知道如何直接将编码后的GIF存储到缓冲区而非文件中。
相关文件与代码
- 输入文件: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)
尝试情况
我尝试了网上找到的通过宏定义GET_DATA_TO_BUFF启用动态缓冲区的方案,但得到的数据仍是原始RGB8格式,没有GIF头,并非GIF格式。FFmpeg写入文件的数据包含完整的GIF结构,而从缓冲区导出的数据仅为编码后的帧数据,缺失文件头和尾部信息。
期望结果
我需要将编码后的完整GIF数据直接输出到动态申请的缓冲区中,寻求可行的解决方法。
内容的提问来源于stack exchange,提问作者yangjinhui2936
相关产品推荐
相关产品推荐

