=================================================================
AVIOContext结构体和其相关的函数分析:
FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析
FFmpeg源码:read_packet_wrapper、fill_buffer函数分析
=================================================================
一、read_packet_wrapper函数
(一)read_packet_wrapper函数的定义
read_packet_wrapper函数定义在FFmpeg源码(本文演示用的FFmpeg源码版本为7.0.1)的源文件libavformat/aviobuf.c中:
cpp
static int read_packet_wrapper(AVIOContext *s, uint8_t *buf, int size)
{
int ret;
if (!s->read_packet)
return AVERROR(EINVAL);
ret = s->read_packet(s->opaque, buf, size);
av_assert2(ret || s->max_packet_size);
return ret;
}
该函数作用是:对本地媒体文件或网络流进行读取,将读上来的数据保存到形参buf指向的缓冲区中。简单来讲,就是通过文件描述符去读取本地媒体文件中的数据,或者通过socket接收网络流中的数据,保存到内存(buf指向的缓冲区)中。
形参s:输入型参数。指向一个AVIOContext(字节流上下文结构体)变量。关于AVIOContext结构体可以参考:《FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析》。
形参buf:输出型参数。保存读上来的数据的缓冲区。
形参size:输入型参数。要读取的字节数。
返回值:返回一个非负数表示成功,此时返回实际读取到的字节数;返回一个负数表示出错。
(二)read_packet_wrapper函数的内部实现分析
s->read_packet是函数指针,指向读取数据包的回调函数:
cpp
typedef struct AVIOContext {
//...
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
//...
}
read_packet_wrapper函数内部,首先会判断s->read_packet是否指向了回调函数。如果没有指向任何回调函数,read_packet_wrapper函数返回AVERROR(EINVAL)表示失败(无效的参数):
cpp
if (!s->read_packet)
return AVERROR(EINVAL);
如果s->read_packet有指向回调函数,调用对应的回调函数。回调函数一般是ffurl_read2(关于该函数用法可以参考:《FFmpeg源码:retry_transfer_wrapper、ffurl_read2、ffurl_write2函数分析》),通过ffurl_read2函数对本地媒体文件或网络流进行读取:
cpp
ret = s->read_packet(s->opaque, buf, size);
备注:s->read_packet指向的回调函数一般是ffurl_read2函数,但也可能是其它:比如FFmpeg源码目录下doc/examples/avio_read_callback.c中的read_packet函数、libavformat/wtvdec.c中的wtvfile_read_packet函数或者tools/target_dem_fuzzer.c中的io_read函数。
二、fill_buffer函数
(一)fill_buffer函数的定义
fill_buffer函数定义在libavformat/aviobuf.c中:
cpp
static void fill_buffer(AVIOContext *s)
{
FFIOContext *const ctx = (FFIOContext *)s;
int max_buffer_size = s->max_packet_size ?
s->max_packet_size : IO_BUFFER_SIZE;
uint8_t *dst = s->buf_end - s->buffer + max_buffer_size <= s->buffer_size ?
s->buf_end : s->buffer;
int len = s->buffer_size - (dst - s->buffer);
/* can't fill the buffer without read_packet, just set EOF if appropriate */
if (!s->read_packet && s->buf_ptr >= s->buf_end)
s->eof_reached = 1;
/* no need to do anything if EOF already reached */
if (s->eof_reached)
return;
if (s->update_checksum && dst == s->buffer) {
if (s->buf_end > s->checksum_ptr)
s->checksum = s->update_checksum(s->checksum, s->checksum_ptr,
s->buf_end - s->checksum_ptr);
s->checksum_ptr = s->buffer;
}
/* make buffer smaller in case it ended up large after probing */
if (s->read_packet && ctx->orig_buffer_size &&
s->buffer_size > ctx->orig_buffer_size && len >= ctx->orig_buffer_size) {
if (dst == s->buffer && s->buf_ptr != dst) {
int ret = set_buf_size(s, ctx->orig_buffer_size);
if (ret < 0)
av_log(s, AV_LOG_WARNING, "Failed to decrease buffer size\n");
s->checksum_ptr = dst = s->buffer;
}
len = ctx->orig_buffer_size;
}
len = read_packet_wrapper(s, dst, len);
if (len == AVERROR_EOF) {
/* do not modify buffer if EOF reached so that a seek back can
be done without rereading data */
s->eof_reached = 1;
} else if (len < 0) {
s->eof_reached = 1;
s->error= len;
} else {
s->pos += len;
s->buf_ptr = dst;
s->buf_end = dst + len;
ffiocontext(s)->bytes_read += len;
s->bytes_read = ffiocontext(s)->bytes_read;
}
}
该函数作用是:对本地媒体文件或网络流进行读取,将读上来的数据保存到AVIOContext输入缓冲区中。简单来讲,就是通过文件描述符去读取本地媒体文件中的数据,或者通过socket接收网络流中的数据,让读上来的数据填满整个AVIOContext输入缓冲区。该函数跟read_packet_wrapper函数的区别是:read_packet_wrapper函数是读取指定的字节数,而fill_buffer函数是读取不固定的字节数但会填满整个AVIOContext输入缓冲区。
形参s:既是输入型参数也是输出型参数。指向一个AVIOContext(字节流上下文结构体)变量。执行fill_buffer函数后,如果读取本地媒体文件或网络流成功:
s->pos的值会增加实际读取到的字节数大小;
s->buf_ptr会指向读上来的数据的开头,一般是原来的s->buf_end,也就是原来输入缓冲区有效数据的末尾;
s->buf_end会指向新的输入缓冲区中有效数据的末尾;
s->buf_end = s->buf_ptr + 实际读取到的字节数;
s->bytes_read的值会增加实际读取到的字节数大小。
返回值:无
(二)fill_buffer函数的内部实现分析
可以看到fill_buffer函数中,执行了read_packet_wrapper函数来对本地媒体文件或网络流进行读取:
cpp
static void fill_buffer(AVIOContext *s)
{
//...
len = read_packet_wrapper(s, dst, len);
//...
}