----------------------------------------
date:2022-08-19
author:hjjdebug
----------------------------------------
avio_reading.c 解读
演示了avformat 是如何通过avio 来读取数据的,下面来看分析
示例代码被我修改过(方便理解),附在后面.
1. ret = av_file_map(input_filename, &file_buf, &file_size, 0, NULL);
将输入文件读入内存到file_buf,并返回长度file_size, 把它放入对象filemap_obj中
filemap_obj的成员,就是一个指针,一个大小
2. fmt_ctx = avformat_alloc_context();
分配一个format context, 只是一个头而已
3. data_buf = av_malloc(4096);
简单的分配一块内存,用data_buf 来指向
4. avio_ctx = avio_alloc_context(data_buf, 4096,
0, &filemap_obj, &my_read_packet, NULL, NULL);
分配一个avio_ctx,但它传进入了几个参数.
a. data_buf指针及其大小4096, 这个缓冲的指针data_buf 及大小4096,将来调用my_read_packet函数时会被传入
b. 传入了filemap_obj指针,它关联了输入文件内存
c. 传入了my_read_packet 函数地址, 关联了读packet 操作.
我们可以打印一下avio_ctx 结构, 是一个复杂的结构,目前使用了如下成员变量,其它用到再介绍.
unsigned char *buffer; //它自己管理的缓存指针和大小
int buffer_size;
unsigned char *buf_ptr; //目前缓存的使用状况,指向了filemap_obj 的什么地方
unsigned char *buf_end;
void *opaque; //这个opaque 就是filemap_obj, 总的缓存数据的大小描述
int (*read_packet)(void *opaque, uint8_t *dst, int size); //读包函数,需要的时候填满缓冲区
其中opaque 就是filemap_obj指针, dst及size 就是data_buf 缓冲区
并把avio_ctx 付给fmt_ctx->pb
fmt_ctx->pb = avio_ctx;
5. avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
该函数会通过my_read_packet 读取一个包.目前一包为4096bytes
我们会看到bd指针向前长0x1000,长度减0x1000
因为my_read_packet 是我们自定义的函数了,可以加打印信息.
6. ret = avformat_find_stream_info(fmt_ctx, NULL);
该函数会通过my_read_packet 读入几十个上百个包不等,它要分析流的信息.
然后你就可以
7. av_dump_format(fmt_ctx, 0, input_filename, 0);
打印出流的信息. 如下是打印记录,信息不少!
Input #0, mpegts, from '/opt/test/test1.ts':
Duration: N/A, start: 1.458667, bitrate: N/A
Program 1
Metadata:
service_name : Service01
service_provider: FFmpeg
Stream #0:0[0x100]: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], 25 fps, 25 tbr, 90k tbn, 50 tbc
Stream #0:1[0x101](eng): Audio: aac (LC) ([15][0][0][0] / 0x000F), 48000 Hz, stereo, fltp, 137 kb/s
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/file.h>
struct buffer_struct {
uint8_t *ptr;
int size; ///< size left in the buffer
};
static int my_read_packet(void *opaque, uint8_t *data, int read_size)
{
static int count=0;
struct buffer_struct *filemap_obj = (struct buffer_struct *)opaque;
read_size = FFMIN(read_size, filemap_obj->size);
if (!read_size)
return AVERROR_EOF;
printf("count:%d, ptr:%p filemap_obj->size:%d,read_size:%u\n",count++, filemap_obj->ptr, filemap_obj->size,read_size);
/* copy internal data to dst */
memcpy(data, filemap_obj->ptr, read_size);
filemap_obj->ptr += read_size;
filemap_obj->size -= read_size;
return read_size;
}
int main(int argc, char *argv[])
{
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;
}
char *input_filename = argv[1];
//AV_LOG_PANIC(0), AV_LOG_VERBOSE(40)
av_log_set_level(56);
size_t file_size;
uint8_t *file_buffer = NULL;
/* slurp file content into buffer */
int ret = av_file_map(input_filename, &file_buffer, &file_size, 0, NULL);
if (ret < 0)
goto end;
struct buffer_struct filemap_obj = { 0 };
/* fill opaque structure used by the AVIOContext read callback */
filemap_obj.ptr = file_buffer;
filemap_obj.size = file_size;
AVFormatContext *fmt_ctx = NULL;
if (!(fmt_ctx = avformat_alloc_context())) {
ret = AVERROR(ENOMEM);
goto end;
}
// description: 创建一个AVIOContext 上下文对象,给fmt_ctx->pb, 它需要的时候会回调读数据函数!
/* 设定了一次读4096大小 bufsize=4096, write_flag=0,opaque=&filemap_obj, 读包函数my_read_packet
* 这里设置了一个缓冲区,一个读包函数及opaque指针, opaque指针会回传给读包函数.
* 后面的2个NULL 是写包函数和 seek 函数, 不用可写NULL
*/
size_t bufsize = 4096;
uint8_t *data_buf = av_malloc(bufsize);
if (!data_buf) {
ret = AVERROR(ENOMEM);
goto end;
}
AVIOContext *avio_ctx = avio_alloc_context(data_buf, bufsize,
0, &filemap_obj, &my_read_packet, NULL, NULL);
if (!avio_ctx) {
ret = AVERROR(ENOMEM);
goto end;
}
fmt_ctx->pb = avio_ctx;
printf("----begin avformat_open_input------\n");
ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
if (ret < 0) {
fprintf(stderr, "Could not open input\n");
goto end;
}
int64_t pos=avio_tell(fmt_ctx->pb);
printf("pos is %ld\n",pos);
printf("------begin avformat_find_stream_info-----\n");
ret = avformat_find_stream_info(fmt_ctx, NULL);
if (ret < 0) {
fprintf(stderr, "Could not find stream information\n");
goto end;
}
pos=avio_tell(fmt_ctx->pb);
printf("pos is %ld\n",pos);
// av_log_set_level(56);
printf("------begin av_dump_format-----\n");
av_dump_format(fmt_ctx, 0, input_filename, 0);
end:
avformat_close_input(&fmt_ctx);
/* note: the internal buffer could have changed, and be != data_buf */
if (avio_ctx)
av_freep(&avio_ctx->buffer);
avio_context_free(&avio_ctx);
av_file_unmap(file_buffer, file_size);
if (ret < 0) {
fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));
return 1;
}
return 0;
}