一、引言
AVIOContext是FFmpeg(本文演示用的FFmpeg源码版本为5.0.3)中的字节流上下文结构体,用来管理输入输出数据。打开一个媒体文件的时候,需要先把数据从硬盘读到缓冲区,然后会用到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;
从输入缓冲区读取数据的时候,
成员变量buffer:恒指向输入缓冲区的开头。
成员变量buffer_size:成员buffer指向的缓冲区的大小,单位为字节。
成员变量buf_ptr:指向输入缓冲区中当前读取到的位置。
成员变量buf_end:恒指向输入缓冲区的末尾。
简单来讲:
* READING
**********************************************************************************
*
* | buffer_size |
* |---------------------------------------|
* | |
*
* buffer buf_ptr buf_end
* +---------------+-----------------------+
* |/ / / / / / / /|/ / / / / / /| |
* read buffer: |/ / consumed / | to be read /| |
* |/ / / / / / / /|/ / / / / / /| |
* +---------------+-----------------------+
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函数定义在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;
}
该函数作用是:如果还没有读取到输入缓冲区的结尾,返回读取到的该输入缓冲区中的一个字节数据,也就是返回s->buf_ptr指向的一个字节数据,然后让s->buf_ptr指向下一个字节数据。如果已经读取到输入缓冲区的末尾,返回0。注意:读取到缓冲区的结尾时,该函数返回0,所以不能同时用它来读取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指向下下个字节的数据。如果已经读取到输入缓冲区的末尾,返回0。注意:该函数跟avio_r8函数一样,一般用它来读取二进制数据(包含ASCII值为0的数据)但不判断是否到了结尾。
四、其它相关函数
同理:
avio_rl24函数:如果还没有读取到输入缓冲区的结尾,返回按照小端模式读取到的该输入缓冲区中的三个字节数据,然后让s->buf_ptr的值+3。如果已经读取到输入缓冲区的末尾,返回0。
avio_rl32函数:如果还没有读取到输入缓冲区的结尾,返回按照小端模式读取到的该输入缓冲区中的四个字节数据,然后让s->buf_ptr的值+4。如果已经读取到输入缓冲区的末尾,返回0。
avio_rl64函数:如果还没有读取到输入缓冲区的结尾,返回按照小端模式读取到的该输入缓冲区中的八个字节数据,然后让s->buf_ptr的值+8。如果已经读取到输入缓冲区的末尾,返回0。
avio_rb16函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的二个字节数据,然后让s->buf_ptr的值+2。如果已经读取到输入缓冲区的末尾,返回0。
avio_rb24函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的三个字节数据,然后让s->buf_ptr的值+3。如果已经读取到输入缓冲区的末尾,返回0。
avio_rb32函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的四个字节数据,然后让s->buf_ptr的值+4。如果已经读取到输入缓冲区的末尾,返回0。
avio_rb64函数:如果还没有读取到输入缓冲区的结尾,返回按照大端模式读取到的该输入缓冲区中的八个字节数据,然后让s->buf_ptr的值+8。如果已经读取到输入缓冲区的末尾,返回0。
所有函数跟avio_r8函数一样,一般用它们来读取二进制数据(包含ASCII值为0的数据)但不判断是否到了结尾。