=================================================================
AVIOContext结构体和其相关的函数分析:
FFmpeg源码:avio_r8、avio_rl16、avio_rl24、avio_rl32、avio_rl64函数分析
FFmpeg源码:read_packet_wrapper、fill_buffer函数分析
FFmpeg源码:ffurl_seek2、ffurl_seek、avio_size函数分析
=================================================================
一、引言
AVIOContext是FFmpeg(本文演示用的FFmpeg源码版本为5.0.3)中的字节流上下文结构体,用来管理输入输出数据。打开一个媒体文件的时候,需要先把数据从硬盘读到AVIOContext的缓冲区,然后会用到AVIOContext中的如下成员:
typedef struct AVIOContext {
//...
/*
* The following shows the relationship between buffer, buf_ptr,
* buf_ptr_max, buf_end, buf_size, and pos, when reading and when writing
* (since AVIOContext is used for both):
*
**********************************************************************************
* READING
**********************************************************************************
*
* | buffer_size |
* |---------------------------------------|
* | |
*
* buffer buf_ptr buf_end
* +---------------+-----------------------+
* |/ / / / / / / /|/ / / / / / /| |
* read buffer: |/ / consumed / | to be read /| |
* |/ / / / / / / /|/ / / / / / /| |
* +---------------+-----------------------+
*
* pos
* +-------------------------------------------+-----------------+
* input file: | | |
* +-------------------------------------------+-----------------+
*
*
**********************************************************************************
* WRITING
**********************************************************************************
*
* | buffer_size |
* |--------------------------------------|
* | |
*
* buf_ptr_max
* buffer (buf_ptr) buf_end
* +-----------------------+--------------+
* |/ / / / / / / / / / / /| |
* write buffer: | / / to be flushed / / | |
* |/ / / / / / / / / / / /| |
* +-----------------------+--------------+
* buf_ptr can be in this
* due to a backward seek
*
* pos
* +-------------+----------------------------------------------+
* output file: | | |
* +-------------+----------------------------------------------+
*
*/
unsigned char *buffer; /**< Start of the buffer. */
int buffer_size; /**< Maximum buffer size */
unsigned char *buf_ptr; /**< Current position in the buffer */
unsigned char *buf_end; /**< End of the data, may be less than
buffer+buffer_size if the read function returned
less data than requested, e.g. for streams where
no more data has been received yet. */
//...
int64_t pos; /**< position in the file of the current buffer */
//...
} AVIOContext;
从AVIOContext输入缓冲区读取数据的时候,
成员变量buffer:恒指向输入缓冲区的开头。
成员变量buffer_size:输入缓冲区的最大大小,单位为字节。该值可能大于(buf_end - buf_ptr)。
成员变量buf_ptr:指向输入缓冲区中当前读取到的位置。
成员变量buf_end:指向输入缓冲区有效数据的末尾。当实际读取到的数据小于要读取的数据时,该值可能会小于(buffer + buffer_size)。
成员变量pos:表示当前输入缓冲区在“被读取的本地媒体文件或网络流”中的位置。
简单来讲:
**********************************************************************************
*
* | buffer_size |
* |---------------------------------------|
* | |
*
* buffer buf_ptr buf_end
* +---------------+-----------------------+
* |/ / / / / / / /|/ / / / / / /| |
* read buffer: |/ / consumed / | to be read /| |
* |/ / / / / / / /|/ / / / / / /| |
* +---------------+-----------------------+
*
* pos
* +-------------------------------------------+-----------------+
* input file: | | |
* +-------------------------------------------+-----------------+
FFmpeg源码中通过下面函数读取AVIOContext结构体中成员变量buffer指向的输入缓冲区的数据。这些函数都声明在FFmpeg源码的头文件libavformat/avio.h中:
/**
* @name Functions for reading from AVIOContext
* @{
*
* @note return 0 if EOF, so you cannot use it if EOF handling is
* necessary
*/
int avio_r8 (AVIOContext *s);
unsigned int avio_rl16(AVIOContext *s);
unsigned int avio_rl24(AVIOContext *s);
unsigned int avio_rl32(AVIOContext *s);
uint64_t avio_rl64(AVIOContext *s);
unsigned int avio_rb16(AVIOContext *s);
unsigned int avio_rb24(AVIOContext *s);
unsigned int avio_rb32(AVIOContext *s);
uint64_t avio_rb64(AVIOContext *s);
二、avio_r8函数
(一)avio_r8函数的定义
avio_r8函数定义在FFmpeg源码的源文件libavformat/aviobuf.c中:
/* XXX: put an inline version */
int avio_r8(AVIOContext *s)
{
if (s->buf_ptr >= s->buf_end)
fill_buffer(s);
if (s->buf_ptr < s->buf_end)
return *s->buf_ptr++;
return 0;
}
该函数作用是:读取一个字节数据,如果AVIOContext输入缓冲区(内存)中还有数据未读取就从缓冲区中读取;如果已经读取完了缓冲区中的数据,通过文件描述符/socket去读取本地媒体文件/网络流中的数据,将其存入输入缓冲区中,然后从缓冲区中读取一个字节数据。
(二)avio_r8函数的内部实现分析
1.情况一:
如果还没有读取到输入缓冲区有效数据的末尾,直接返回读取到的该输入缓冲区中的一个字节数据,也就是返回s->buf_ptr指向的那一个字节数据,然后让s->buf_ptr指向下一个字节数据:
if (s->buf_ptr < s->buf_end)
return *s->buf_ptr++;
2.情况二:
如果已经读取到输入缓冲区的末尾,也就是说AVIOContext内存缓冲区中的数据已经全部被读完了,这时先调用fill_buffer函数(关于该函数用法可以参考:《FFmpeg源码:read_packet_wrapper、fill_buffer函数分析》)对本地媒体文件或网络流进行读取,将读上来的数据保存到AVIOContext输入缓冲区中:
if (s->buf_ptr >= s->buf_end)
fill_buffer(s);
如果出错或者已经读到本地媒体文件或网络流的结尾,返回0;否则返回刚刚读取到的该AVIOContext输入缓冲区中的一个字节数据,然后让s->buf_ptr指向下一个字节数据:
//...
if (s->buf_ptr < s->buf_end)
return *s->buf_ptr++;
return 0;
注意:avio_r8函数返回0可能表示下面几种情况:1.读取到输入缓冲区的结尾并且从本地媒体文件或网络流中也读取不到数据;2.出错;3.读取到ASCII值为0的字节。所以为了避免冲突,avio_r8函数要么只能读取字符串但可以判断是否读到了结尾或者出错,要么能读取二进制数据但不能判断是否到了结尾或者出错。一般来讲选择后者的用法,即用它读取二进制数据(包含ASCII值为0的数据)但不判断是否到了结尾或者出错。
三、avio_rl16函数的定义
avio_rl16函数定义在FFmpeg源码的源文件libavformat/aviobuf.c中:
unsigned int avio_rl16(AVIOContext *s)
{
unsigned int val;
val = avio_r8(s);
val |= avio_r8(s) << 8;
return val;
}
该函数作用是:如果还没有读取到输入缓冲区的结尾,返回按照小端模式读取到的该输入缓冲区中的二个字节数据,然后让s->buf_ptr指向下下个字节的数据。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。注意:该函数跟avio_r8函数一样,一般用它来读取二进制数据(包含ASCII值为0的数据)但不判断是否到了结尾或者出错。
四、其它相关函数
同理:
avio_rl24函数:如果还没有读取到输入缓冲区的结尾,返回按照小端模式读取到的该输入缓冲区中的三个字节数据,然后让s->buf_ptr的值+3。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。
avio_rl32函数:如果还没有读取到输入缓冲区的结尾,返回按照小端模式读取到的该输入缓冲区中的四个字节数据,然后让s->buf_ptr的值+4。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。
avio_rl64函数:如果还没有读取到输入缓冲区的结尾,返回按照小端模式读取到的该输入缓冲区中的八个字节数据,然后让s->buf_ptr的值+8。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。
avio_rb16函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的二个字节数据,然后让s->buf_ptr的值+2。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。
avio_rb24函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的三个字节数据,然后让s->buf_ptr的值+3。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。
avio_rb32函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的四个字节数据,然后让s->buf_ptr的值+4。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。
avio_rb64函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的八个字节数据,然后让s->buf_ptr的值+8。如果已经读取到输入缓冲区的末尾,对本地媒体文件或网络流进行读取。
所有函数跟avio_r8函数一样,一般用它们来读取二进制数据(包含ASCII值为0的数据)但不判断是否到了结尾或者出错。