【FFmpeg】AVIOContext结构体
示例工程:
【FFmpeg】调用ffmpeg库实现264软编
【FFmpeg】调用ffmpeg库实现264软解
【FFmpeg】调用ffmpeg库进行RTMP推流和拉流
【FFmpeg】调用ffmpeg库进行SDL2解码后渲染
流程分析:
【FFmpeg】编码链路上主要函数的简单分析
【FFmpeg】解码链路上主要函数的简单分析
结构体分析:
【FFmpeg】AVCodec结构体
【FFmpeg】AVCodecContext结构体
【FFmpeg】AVStream结构体
【FFmpeg】AVFormatContext结构体
1.AVIOContext结构体的定义
AVIOContext定义了输入输出上下文之中的信息,涉及到码流的输入输出,是非常重要的结构体,位于libavformat\avio.h中,定义如下
/**
* Bytestream IO Context.
* New public fields can be added with minor version bumps.
* Removal, reordering and changes to existing public fields require
* a major version bump.
* sizeof(AVIOContext) must not be used outside libav*.
*
* @note None of the function pointers in AVIOContext should be called
* directly, they should only be set by the client application
* when implementing custom I/O. Normally these are set to the
* function pointers specified in avio_alloc_context()
*/
// 码流输入输出的上下文
// @note AVIOContext中的所有函数指针都不应该被直接调用,它们只应该由客户端应用程序在实现自定义I/O时设置
// 通常将这些设置为avio_alloc_context()中指定的函数指针
typedef struct AVIOContext {
/**
* A class for private options.
*
* If this AVIOContext is created by avio_open2(), av_class is set and
* passes the options down to protocols.
*
* If this AVIOContext is manually allocated, then av_class may be set by
* the caller.
*
* warning -- this field can be NULL, be sure to not pass this AVIOContext
* to any av_opt_* functions in that case.
*/
// 用于私有配置的选项
// 如果这个AVIOContext是由avio_open2创建的,设置av_class并将选项向下传递给协议
// 如果这个AVIOContext是手动分配的,那么av_class可以由调用者设置
// warning: 这个字段可以为NULL,在这种情况下,请确保不要将这个AVIOContext传递给任何av_opt_*函数
const AVClass *av_class;
/*
* 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: | | |
* +-------------+----------------------------------------------+
*
*/
// buffer的起始地址
unsigned char *buffer; /**< Start of the buffer. */
// 最大的buffer大小
int buffer_size; /**< Maximum buffer size */
// 当前buffer指针指向的位置
unsigned char *buf_ptr; /**< Current position in the buffer */
// 如果read函数返回的数据少于请求,则数据的结束值可能小于buffer+buffer_size,例如,对于尚未接收到更多数据的流
// 当前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. */
// 私有指针,传递给read/write/seek/… 函数
void *opaque; /**< A private pointer, passed to the read/write/seek/...
functions. */
// 读取packet
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
// 写入packet
int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size);
// 查找
int64_t (*seek)(void *opaque, int64_t offset, int whence);
// 当前缓冲区在文件中的位置
int64_t pos; /**< position in the file of the current buffer */
// 如果由于错误或eof而无法读取,则为True
int eof_reached; /**< true if was unable to read due to error or eof */
// 错误码
int error; /**< contains the error code or 0 if no error happened */
// 如果是写入状态,则置为true
int write_flag; /**< true if open for writing */
// 最大的packet大小
int max_packet_size;
// 最小的packet大小(在刷新数据之前,尝试至少缓冲这么多的数据)
int min_packet_size; /**< Try to buffer at least this amount of data
before flushing it. */
// checksum用于验证数据完整性
// 存储了校验值,以验证数据的完整性。通过将数据块的大小累加到checksum,然后对结果进行按位反转来实现的
// 这个机制主要用于检测数据传输过程中可能发生的错误,尤其是在网络传输或文件读写中
unsigned long checksum;
unsigned char *checksum_ptr;
unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
/**
* Pause or resume playback for network streaming protocols - e.g. MMS.
*/
// 暂停或恢复网络流协议的播放
int (*read_pause)(void *opaque, int pause);
/**
* Seek to a given timestamp in stream with the specified stream_index.
* Needed for some network streaming protocols which don't support seeking
* to byte position.
*/
// 使用指定的stream_index在流中查找给定的时间戳
// 需要一些网络流协议,不支持寻求字节位置
int64_t (*read_seek)(void *opaque, int stream_index,
int64_t timestamp, int flags);
/**
* A combination of AVIO_SEEKABLE_ flags or 0 when the stream is not seekable.
*/
/*
流查找的行为,有两个定义
* Seeking works like for a local file.
#define AVIO_SEEKABLE_NORMAL (1 << 0)
* Seeking by timestamp with avio_seek_time() is possible.
#define AVIO_SEEKABLE_TIME (1 << 1)
*/
int seekable;
/**
* avio_read and avio_write should if possible be satisfied directly
* instead of going through a buffer, and avio_seek will always
* call the underlying seek function directly.
*/
// 如果可能的话,应该直接满足Avio_read和avio_write,而不是通过缓冲区,avio_seek将始终直接调用底层的seek函数
int direct;
/**
* ',' separated list of allowed protocols.
*/
// 协议的白名单
const char *protocol_whitelist;
/**
* ',' separated list of disallowed protocols.
*/
// 协议的黑名单
const char *protocol_blacklist;
/**
* A callback that is used instead of write_packet.
*/
// 一个用来代替write_packet的回调
int (*write_data_type)(void *opaque, const uint8_t *buf, int buf_size,
enum AVIODataMarkerType type, int64_t time);
/**
* If set, don't call write_data_type separately for AVIO_DATA_MARKER_BOUNDARY_POINT,
* but ignore them and treat them as AVIO_DATA_MARKER_UNKNOWN (to avoid needlessly
* small chunks of data returned from the callback).
*/
// 如果设置了,不要为AVIO_DATA_MARKER_BOUNDARY_POINT单独调用write_data_type,
// 而是忽略它们并将它们视为AVIO_DATA_MARKER_UNKNOWN(以避免从回调返回不必要的小块数据)
int ignore_boundary_point;
/**
* Maximum reached position before a backward seek in the write buffer,
* used keeping track of already written data for a later flush.
*/
// 写缓冲区中向后寻道之前达到的最大位置,用于跟踪已写入的数据,以便以后刷新
unsigned char *buf_ptr_max;
/**
* Read-only statistic of bytes read for this AVIOContext.
*/
// 只读统计为该AVIOContext读取的字节数
int64_t bytes_read;
/**
* Read-only statistic of bytes written for this AVIOContext.
*/
// 只读统计写入该AVIOContext的字节数。
int64_t bytes_written;
} AVIOContext;
AVIOContext当中比较重要的信息包括:
(1)unsigned char *buffer:buffer的起始地址
(2)int buffer_size:buffer的大小
(3)unsigned char *buf_ptr:buffer当前的指针
(4)unsigned char *buf_end:buffer末尾的地址
(5)void *opaque:URLContext
(6)int (*read_packet)(void *opaque, uint8_t *buf, int buf_size):读取packet
(7)int (*write_packet)(void *opaque, const uint8_t *buf, int buf_size):写入packet
(8)int64_t (*seek)(void *opaque, int64_t offset, int whence):定位查找
(9)unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size):更新校验信息checksum
(10)int (*read_pause)(void *opaque, int pause):暂停流的读取
(11)const char *protocol_XXXlist:协议的黑白名单
相对比于雷博记录的内容,新版本的AVIOContext增加的比较重要的是协议的黑白名单,白名单当中的protocol表示可用,黑名单当中的protocol表示不可用。在解码的时候,buffer会存储FFmpeg读取的数据,例如解码已经编码好的.h264文件。
URLContext的定义如下,位于libavformat\url.h中
typedef struct URLContext {
const AVClass *av_class; /**< information for av_log(). Set by url_open(). */
const struct URLProtocol *prot;
void *priv_data;
char *filename; /**< specified URL */
int flags;
int max_packet_size; /**< if non zero, the stream is packetized with this max packet size */
int is_streamed; /**< true if streamed (no seek possible), default = false */
int is_connected;
AVIOInterruptCB interrupt_callback;
int64_t rw_timeout; /**< maximum time to wait for (network) read/write operation completion, in mcs */
const char *protocol_whitelist;
const char *protocol_blacklist;
int min_packet_size; /**< if non zero, the stream is packetized with this min packet size */
} URLContext;
相对于老版本的FFmpeg,这里一个比较大的改变是增加了protocol的黑白名单。URLProtocol的定义如下,同样位于libavformat\url.h中
typedef struct URLProtocol {
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
/**
* This callback is to be used by protocols which open further nested
* protocols. options are then to be passed to ffurl_open_whitelist()
* or ffurl_connect() for those nested protocols.
*/
// 此回调将由打开进一步嵌套协议的协议使用。然后将这些嵌套协议的选项传递给ffurl_open_whitelist()或ffurl_connect()
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
int (*url_accept)(URLContext *s, URLContext **c);
int (*url_handshake)(URLContext *c);
/**
* Read data from the protocol.
* If data is immediately available (even less than size), EOF is
* reached or an error occurs (including EINTR), return immediately.
* Otherwise:
* In non-blocking mode, return AVERROR(EAGAIN) immediately.
* In blocking mode, wait for data/EOF/error with a short timeout (0.1s),
* and return AVERROR(EAGAIN) on timeout.
* Checking interrupt_callback, looping on EINTR and EAGAIN and until
* enough data has been read is left to the calling function; see
* retry_transfer_wrapper in avio.c.
*/
// 从protocol读取数据
// 如果数据立即可用(甚至小于大小),达到EOF或发生错误(包括EINTR),则立即返回
// 在非阻塞模式下,立即返回AVERROR(EAGAIN)。在阻塞模式下,等待数据/EOF/错误有一个短超时(0.1s),并在超时时返回AVERROR(EAGAIN)
// 检查interrupt_callback,在EINTR和EAGAIN上循环,直到有足够的数据被读取给调用函数;参见avio.c中的retry_transfer_wrapper
int (*url_read)( URLContext *h, unsigned char *buf, int size);
int (*url_write)(URLContext *h, const unsigned char *buf, int size);
int64_t (*url_seek)( URLContext *h, int64_t pos, int whence);
int (*url_close)(URLContext *h);
int (*url_read_pause)(void *urlcontext, int pause);
int64_t (*url_read_seek)(void *urlcontext, int stream_index,
int64_t timestamp, int flags);
int (*url_get_file_handle)(URLContext *h);
int (*url_get_multi_file_handle)(URLContext *h, int **handles,
int *numhandles);
int (*url_get_short_seek)(URLContext *h);
int (*url_shutdown)(URLContext *h, int flags);
const AVClass *priv_data_class;
int priv_data_size;
int flags;
// 相对比于老版本的FFmpeg,这里还增加了dir的操作
int (*url_check)(URLContext *h, int mask);
int (*url_open_dir)(URLContext *h);
int (*url_read_dir)(URLContext *h, AVIODirEntry **next);
int (*url_close_dir)(URLContext *h);
int (*url_delete)(URLContext *h);
int (*url_move)(URLContext *h_src, URLContext *h_dst);
const char *default_whitelist;
} URLProtocol;
在实际的结构体定义如下所示,定义是file的protocol,其中还定义了白名单为"file,crypto,data"
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"
};
又比如tcp的protocol,不过这里没有定义默认的白名单
const URLProtocol ff_tcp_protocol = {
.name = "tcp",
.url_open = tcp_open,
.url_accept = tcp_accept,
.url_read = tcp_read,
.url_write = tcp_write,
.url_close = tcp_close,
.url_get_file_handle = tcp_get_file_handle,
.url_get_short_seek = tcp_get_window_size,
.url_shutdown = tcp_shutdown,
.priv_data_size = sizeof(TCPContext),
.flags = URL_PROTOCOL_FLAG_NETWORK,
.priv_data_class = &tcp_class,
};
CSDN : https://blog.csdn.net/weixin_42877471
Github : https://github.com/DoFulangChen