目录
- 参考
- 示例说明
- 示例代码
1. 参考
2. 示例说明
示例程序[1]演示了如何通过AVIOContext从从自定义回调函数输入数据。
示例流程如下。
关键函数说明:
- 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);
说明:
- bufptr将被设置为指向分配的buffer,如果文件是空的,则bufptr会被设置为NULL。
- *size将被赋值为buffer中字节的大小。如果文件是空的,则*size会被设置为0。
- 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));
说明:
- buffer:用于AVIOContext的输入或输出操作的数据buffer。
- buffer_size:buffer的大小。
- write_flag:如果需要对buffer进行写需要设置为1,否则设置为0。
- opaque:一个指向用户自定数据的指针。
- read_packet:用于往buffer填充数据的函数,对于流协议,绝对不能返回0,而必须返回正确的AVERROR。在从自定义buffer读取数据的时候,需要设置为对应的填充数据的函数。
- 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 <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
};
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;
}
说明:
- 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()的填充输入数据的函数。
- avformat_open_input函数中会调用read_packet()回调函数,read_packet()功能是从存储了文件内容的buffer往回调函数中的buf中填充数据直到数据读完毕。