FFmpeg 示例-avio_reading从自定义回调函数输入数据

目录

  1. 参考
  2. 示例说明
  3. 示例代码

1. 参考

  • [1] FFmpeg/doc/examples/avio_reading.c

2. 示例说明

示例程序[1]演示了如何通过AVIOContext从从自定义回调函数输入数据。

示例流程如下。


FFmpeg_avio_reading.png

关键函数说明:

  • av_file_map():读取文件,并将其内容放入一个新分配的buffer中。使用av_file_unmap()来释放内存。
  • av_malloc():这里用于分配了AVIOContext中需要用到的buffer。
  • avio_alloc_context():为缓冲I/O分配和初始化AVIOContext。使用avio_context_free()来释放内存。

av_file_map()的定义在libavutil/file.h,如下所示。

/**
 * Read the file with name filename, and put its content in a newly
 * allocated buffer or map it with mmap() when available.
 * In case of success set *bufptr to the read or mmapped buffer, and
 * *size to the size in bytes of the buffer in *bufptr.
 * Unlike mmap this function succeeds with zero sized files, in this
 * case *bufptr will be set to NULL and *size will be set to 0.
 * The returned buffer must be released with av_file_unmap().
 *
 * @param log_offset loglevel offset used for logging
 * @param log_ctx context used for logging
 * @return a non negative number in case of success, a negative value
 * corresponding to an AVERROR error code in case of failure
 */
av_warn_unused_result
int av_file_map(const char *filename, uint8_t **bufptr, size_t *size,
                int log_offset, void *log_ctx);

说明:

  1. bufptr将被设置为指向分配的buffer,如果文件是空的,则bufptr会被设置为NULL。
  2. *size将被赋值为buffer中字节的大小。如果文件是空的,则*size会被设置为0。
  3. buffer使用完毕时候,需要使用av_file_unmap()来释放内存。

avio_alloc_context()的定义在libavformat/avio.h,如下所示。

/**
 * Allocate and initialize an AVIOContext for buffered I/O. It must be later
 * freed with avio_context_free().
 *
 * @param buffer Memory block for input/output operations via AVIOContext.
 *        The buffer must be allocated with av_malloc() and friends.
 *        It may be freed and replaced with a new buffer by libavformat.
 *        AVIOContext.buffer holds the buffer currently in use,
 *        which must be later freed with av_free().
 * @param buffer_size The buffer size is very important for performance.
 *        For protocols with fixed blocksize it should be set to this blocksize.
 *        For others a typical size is a cache page, e.g. 4kb.
 * @param write_flag Set to 1 if the buffer should be writable, 0 otherwise.
 * @param opaque An opaque pointer to user-specific data.
 * @param read_packet  A function for refilling the buffer, may be NULL.
 *                     For stream protocols, must never return 0 but rather
 *                     a proper AVERROR code.
 * @param write_packet A function for writing the buffer contents, may be NULL.
 *        The function may not change the input buffers content.
 * @param seek A function for seeking to specified byte position, may be NULL.
 *
 * @return Allocated AVIOContext or NULL on failure.
 */
AVIOContext *avio_alloc_context(
                  unsigned char *buffer,
                  int buffer_size,
                  int write_flag,
                  void *opaque,
                  int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
                  int64_t (*seek)(void *opaque, int64_t offset, int whence));

说明:

  1. buffer:用于AVIOContext的输入或输出操作的数据buffer。
  2. buffer_size:buffer的大小。
  3. write_flag:如果需要对buffer进行写需要设置为1,否则设置为0。
  4. opaque:一个指向用户自定数据的指针。
  5. read_packet:用于往buffer填充数据的函数,对于流协议,绝对不能返回0,而必须返回正确的AVERROR。在从自定义buffer读取数据的时候,需要设置为对应的填充数据的函数。
  6. write_packet:用于输出buffer中内容的函数。函数不能改变buffer中的内容。在从自定义buffer获取输出的数据的时候,需要设置为对应的输出数据处理函数。

3. 示例程序

示例程序来自[1]。

/**
 * @file
 * libavformat AVIOContext API example.
 *
 * Make libavformat demuxer access media content through a custom
 * AVIOContext read callback.
 * @example avio_reading.c
 */

#include 
#include 
#include 
#include 

struct buffer_data {
    uint8_t *ptr;
    size_t size; ///< size left in the buffer
};

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

    if (!buf_size)
        return AVERROR_EOF;
    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];

    /* 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;

    if (!(fmt_ctx = avformat_alloc_context())) {
        ret = AVERROR(ENOMEM);
        goto end;
    }

    avio_ctx_buffer = av_malloc(avio_ctx_buffer_size);
    if (!avio_ctx_buffer) {
        ret = AVERROR(ENOMEM);
        goto end;
    }
    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;
    }
    fmt_ctx->pb = avio_ctx;

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

    av_dump_format(fmt_ctx, 0, input_filename, 0);

end:
    avformat_close_input(&fmt_ctx);
    /* 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;
}

说明:

  1. avio_alloc_context使用注意事项。
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,
                                  0, &bd, &read_packet, NULL, NULL);
  • avio_ctx_buffer为传入的AVIOContext需要使用的buffer。
  • bd为指向自定义对象的指针,在read_packet回调函数中的其中一个参数,示例中是标识输入数据buffer的读取状态的类型。
  • read_packet()函数为设置给avio_alloc_context()的填充输入数据的函数。
  1. avformat_open_input函数中会调用read_packet()回调函数,read_packet()功能是从存储了文件内容的buffer往回调函数中的buf中填充数据直到数据读完毕。

你可能感兴趣的:(FFmpeg 示例-avio_reading从自定义回调函数输入数据)