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也就可以理解了.

相关推荐
shelutai12 小时前
FFMPEG: Overlay a video on a video after x seconds
ffmpeg
aqi0019 小时前
FFmpeg开发笔记(五十三)移动端的国产直播录制工具EasyPusher
android·ffmpeg·音视频·直播·流媒体
「QT(C++)开发工程师」1 天前
【FFmpeg应用场景概述】
ffmpeg
cuijiecheng20181 天前
音视频入门基础:AAC专题(6)——FFmpeg源码中解码ADTS格式的AAC的Header的实现
ffmpeg·音视频·aac
yunhuibin1 天前
ffmpeg面向对象——参数配置秘密探索及其设计模式
学习·设计模式·ffmpeg
superconvert2 天前
主流流媒体的综合性能大 PK ( smart_rtmpd, srs, zlm, nginx rtmp )
websocket·ffmpeg·webrtc·hevc·rtmp·h264·hls·dash·rtsp·srt·flv
cuijiecheng20183 天前
音视频入门基础:AAC专题(8)——FFmpeg源码中计算AAC裸流AVStream的time_base的实现
ffmpeg·音视频·aac
0点51 胜3 天前
[ffmpeg] 视频格式转换
ffmpeg
Jerry 二河小鱼3 天前
在Linux中安装FFmpeg
linux·运维·服务器·ffmpeg
0点51 胜4 天前
[ffmpeg] 音视频编码
ffmpeg·音视频