FFMPEG中的内存读取

前段时间在开发内存方式的音视频解码接口,所谓内存方式解码,就是解码器的输入是一段buffer数据,不像文件解码那样可以通过文件名去获取一些格式信息,内存解码需要去解析buffer中的二进制码流来得到输入数据的封装格式和编码格式。FFMPEG中其实已经给了内存读取的样例,可以拿来作为参考,下面就用avio_reading.c里面的代码来做一点分析。

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>

定义一个结构体,用来存放输入内存地址及其剩余大小

struct buffer_data {
    uint8_t *ptr;
    size_t size; ///< size left in the buffer
};
定义一个从内存中读取数据的函数,在avio_alloc_context中通过函数指针方式绑定,并在以后的数据读取中调用,函数的功能就是从地址opaque中读取数据到地址buf中,buf_size是每次读取的数据大小。 

 static int read_packet(void *opaque, uint8_t *buf, int buf_size)
 {
     struct buffer_data *bd = (struct buffer_data *)opaque;
     buf_size = FFMIN(buf_size, bd->size);
 
     printf("ptr:%p size:%zu\n", bd->ptr, bd->size);
 
     /* copy internal buffer data to buf */
     memcpy(buf, bd->ptr, buf_size);
     bd->ptr  += buf_size;
     bd->size -= buf_size;
 
     return buf_size;
 }
内存读取主函数:

int main(int argc, char *argv[])
{
    AVFormatContext *fmt_ctx = NULL;
    AVIOContext *avio_ctx = NULL;
    uint8_t *buffer = NULL, *avio_ctx_buffer = NULL;
    size_t buffer_size, avio_ctx_buffer_size = 4096;
    char *input_filename = NULL;
    int ret = 0;
    struct buffer_data bd = { 0 };

    if (argc != 2) {
        fprintf(stderr, "usage: %s input_file\n"
                "API example program to show how to read from a custom buffer "
                "accessed through AVIOContext.\n", argv[0]);
        return 1;
    }
    input_filename = argv[1];

    /* register codecs and formats and other lavf/lavc components*/
    av_register_all();
	/* 将文件内容读入到一段内存中 */
    /* slurp file content into buffer */
    ret = av_file_map(input_filename, &buffer, &buffer_size, 0, NULL);
    if (ret < 0)
        goto end;
   /*初始化输入指针*/
    /* fill opaque structure used by the AVIOContext read callback */
    bd.ptr  = buffer;
    bd.size = buffer_size;
    /* 为结构体AVFormatContext指针分配空间并初始化 */
    if (!(fmt_ctx = avformat_alloc_context())) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    /* 分配一个buffer,用于存放从内存中读取到的数据,如果输入数据量大于该buffer大小,则会多次读取 */
    avio_ctx_buffer = av_malloc(avio_ctx_buffer_size);
    if (!avio_ctx_buffer) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
	/* 为AVIOContext分配内存 */
    avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
                                  0, &bd, &read_packet, NULL, NULL);
    if (!avio_ctx) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
	/* 将分配好的AVIOContext赋值给AVFormatContext中的AVIOContext指针 */
    fmt_ctx->pb = avio_ctx;
    /* 现在,我们就可以通过avformat_open_input读取内存数据并获取数据基本信息了,由于所有信息未知,因此,函数后三个参数均为NULL */
    ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open input\n");
        goto end;
    }
    /* 获取输入中的视频流、音频流、字幕流以及数据流的各种格式信息 */
    ret = avformat_find_stream_info(fmt_ctx, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not find stream information\n");
        goto end;
    }
    /* 打印第0个输入流的详细信息 */
    av_dump_format(fmt_ctx, 0, input_filename, 0);

end:
    /* 操作完成后,关闭AVFormatContext,释放分配的内存空间 */
    avformat_close_input(&fmt_ctx);
	/*下面这一句一定不能省!!!因为avio_ctx在前面是由我们自己分配的,在使用过程中,*/
    /*avio_ctx地址可能会发生变化,包括其关联的buffer地址也会变化,因此,avformat_close_input在释放时,*/
    /*可能无法释放这部分内存,在我自己的解码器中,就遇到了这个问题,以为调用avformat_close_input就万事大吉了,*/
    /*没想到解码许多文件时,内存蹭蹭往上涨,直到耗尽所有内存资源。查了好久才发现这个大坑!*/ 
    /* note: the internal buffer could have changed, and be != avio_ctx_buffer */
    if (avio_ctx) {
        av_freep(&avio_ctx->buffer);
        av_freep(&avio_ctx);
    }
	/* 释放输入内存 */
    av_file_unmap(buffer, buffer_size);

    if (ret < 0) {
        fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
        return 1;
    }

    return 0;
}


猜你喜欢

转载自blog.csdn.net/DeliaPu/article/details/78884674