ffmpeg 从avio_read 到 file_read

#############################################
author: hjjdebug
date:   2024年 03月 13日 星期三 15:39:30 CST
description: ffmpeg 从avio_read 到 file_read
#############################################

int nRet = avio_open(&pReadCtx, "200M.ts"), AVIO_FLAG_READ);
只分析一句话.
char buf[10240];
int nret=avio_read(pReadCtx, buf, sizeof(buf));

先看一下程序调用栈,看起来还挺深奥的样子!
0 in file_read of libavformat/file.c:110
1 in retry_transfer_wrapper of libavformat/avio.c:370
2 in ffurl_read of libavformat/avio.c:405
3 in read_packet_wrapper of libavformat/aviobuf.c:521
4 in fill_buffer of libavformat/aviobuf.c:570
5 in avio_read of libavformat/aviobuf.c:663
6 in main of main.cpp:43
------------------------------------------------------------
第一层: aviobuf.c中, AVIOContext对象当家, 这就是pReadCtx.
------------------------------------------------------------
int nret=avio_read(pReadCtx, buf, sizeof(buf));
通俗说就是,嘿!pReadCtx, 给我10240个数据. 放到buf地址处.
pReadCtx 是谁啊? 是个对象,就是说在它的周围有一帮小弟(属性,函数指针等)可以使用.
这个对象是跟200M.ts 文件关联的
下面看看它的执行过程:
它看了看自己缓冲区内容, 一个字节都没有, len=s->buf_end-s->buf_ptr;
哪来10240个数据,于是调用了把缓冲区填满命令. 
fill_buffer(s);
这个s 有一个自己的资源 buffer, 它的开始地址是s->buffer, 大小s->buffer_size=32768
fill_buffer(s) 调用
read_packer_wrapper(s,dst,len) 来向内部缓冲区灌数.
这个wrapper 会调用s->read_packet 指针函数,并传递s->opaque 为第一参数.
ret = s->read_packet(s->opaque, buf, size); 

s->opaque 在aviobuf.c中是一个void指针, 进入下一层函数后,它是一个URLContext 对象指针.
s->read_packet 被赋值的是ffurl_read 函数, 这当然都是在初始化时完成的(avio_open时)
于是程序进入到下一层avio.c 中, 在这里,由URLContext 当家

------------------------------------------------------------
第二层: avio.c中, URLContext 当家, 简记为h.
------------------------------------------------------------
int ffurl_read(URLContext *h, unsigned char *buf, int size)
嘿! URLContext, 给我往buf中填size 个数据.
URLContext 对象设计的目的是, 不管任何协议,我都用一套接口来读写数据.
于是它调用了
    retry_transfer_wrapper(h, buf, size, 1, h->prot->url_read);

多出来的两个参数是,最少读1个byte, 调用h->prot-url_read 来进行实际的读写
于是我们就进入了h->prot->url_read, 它正式file_read, 乖乖,神奇! 它是怎么实现的? 初始化时是怎样赋值的?
先按下不表,一会说清楚, 再继续跟踪代码. 看看数据如何读取.

------------------------------------------------------------
第三层: file.c中, FileContext 对象当家,简记为c.
------------------------------------------------------------
static int file_read(URLContext *h, unsigned char *buf, int size)
该函数传来的是URLContext, 但这里是file.c 文件, 当家的是FileContext, 从URLContext 中可以得到它.
    FileContext *c = h->priv_data;
    ret = read(c->fd, buf, size);  // 这就是libc 的read 了,从fd中读取size个数据到buf, 追到底了!
    返回实际读取到的字节数. 
    欲读32768, 现在返回了32768
    于是打道回府,file_read 返回32768, retry_transfer_wrapper 返回32768,ffurl_read返回32768,
    read_packer_wrapper 返回32768,
    fill_buffer(s) 也调整了自己的参数 s->buf_ptr=dst, s->buf_end=dst+len,s->pos+=len,s->bytes_read+=len
出来后再循环,发现avio_read 还等着10240个数据呢, 咱有32768个数据,它要10240,小case, 给它吧.
            memcpy(buf, s->buf_ptr, len);
            s->buf_ptr += len;
            buf += len;
            size -= len;
再绕回去,size就等于0了,循环退出,avio_read读到数据返回.

这里我们再总结一下:
URLContext h对象地址付给了 AVIOContext 对象成员 s->oopaque
FileContext c对象地址付给了 URLContext 对象成员 h->priv_data
这样各对象关系就明确了.
h->prot 是什么? 是协议protocal, 也是一个对象指针, 
它被付给了ff_file_protocol, 它是怎么付给的? 是在一个协议列表中找协议name为"file" 而找到的.
该对象就在file.c中第356行, 是一个在全局数据区构建的对象
const URLProtocol ff_file_protocol = {
    .name                = "file",
    .url_open            = file_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_seek            = file_seek,
    .url_close           = file_close,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
    .url_delete          = file_delete,
    .url_move            = file_move,
    .priv_data_size      = sizeof(FileContext),
    .priv_data_class     = &file_class,
    .url_open_dir        = file_open_dir,
    .url_read_dir        = file_read_dir,
    .url_close_dir       = file_close_dir,
    .default_whitelist   = "file,crypto,data"
};
有了这个对象,就有了h->prot->url_read, 它就是file_read
有了这些基础,再去读avio_open也就可以理解了.
 

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
avio_alloc_contextFFmpeg 中用于创建 AVIOContext 的函数。AVIOContextFFmpeg 中的 IO 抽象层,用于封装各种输入输出操作,例如从文件读取数据、向网络发送数据等。avio_alloc_context 函数用于分配并初始化 AVIOContext 结构体,以便进行输入输出操作。 函数原型如下: ``` 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`:指向用于缓存数据的内存地址。可以为 NULL,表示不需要缓存数据。 - `buffer_size`:缓存区的大小。如果 `buffer` 为 NULL,则该参数无效。 - `write_flag`:标志位,表示 AVIOContext 是否支持写操作。如果为 1,表示支持写操作;如果为 0,表示只支持读操作。 - `opaque`:指向用户自定义的数据结构的指针,用于在读写操作中传递额外的数据。 - `read_packet`:读数据的回调函数指针。函数原型为 `int (*read_packet)(void *opaque, uint8_t *buf, int buf_size)`,返回值为读取的字节数,如果读取失败则返回负数。 - `write_packet`:写数据的回调函数指针。函数原型为 `int (*write_packet)(void *opaque, uint8_t *buf, int buf_size)`,返回值为写入的字节数,如果写入失败则返回负数。 - `seek`:定位操作的回调函数指针。函数原型为 `int64_t (*seek)(void *opaque, int64_t offset, int whence)`,返回值为定位后的文件位置,如果定位失败则返回负数。 下面是一个简单的示例,演示如何使用 avio_alloc_context 函数创建 AVIOContext: ```c #include <libavformat/avformat.h> int main(int argc, char *argv[]) { // 打开一个文件 FILE *file = fopen("test.mp4", "rb"); if (!file) { printf("Failed to open file.\n"); return -1; } // 创建 AVIOContext uint8_t *buffer = (uint8_t *)av_malloc(4096); AVIOContext *avio_ctx = avio_alloc_context(buffer, 4096, 0, file, NULL, NULL, NULL); if (!avio_ctx) { printf("Failed to allocate AVIOContext.\n"); return -1; } // 打开输入格式上下文 AVFormatContext *fmt_ctx = avformat_alloc_context(); fmt_ctx->pb = avio_ctx; int ret = avformat_open_input(&fmt_ctx, NULL, NULL, NULL); if (ret < 0) { printf("Failed to open input.\n"); return -1; } // 释放资源 avformat_close_input(&fmt_ctx); av_free(avio_ctx->buffer); av_free(avio_ctx); return 0; } ``` 在该示例中,首先打开了一个文件。然后,创建 AVIOContext 的缓存区,并将文件指针作为 opaque 参数传递给 avio_alloc_context 函数。最后,将 AVIOContext 指定为输入格式上下文的 pb 字段,调用 avformat_open_input 函数打开输入。 需要注意的是,在使用完 AVIOContext 后,需要释放它所占用的内存空间,以避免内存泄漏。可以使用 av_free 函数释放缓存区,使用 avio_context_free 函数释放 AVIOContext 结构体。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值