FFmpeg源码分析:AVIOContext、IO模型与协议
FFmpeg的IO模型从avio_open()方法开始,核心结构体由AVIOContext和URLProtocol组成。如果需要读取缓冲区buffer数据进行播放,可以通过自定义AVIOContext,并且实现read_packet、write_packet、seek三个方法。如果需要播放加密视频,可以自定义私有协议进行解密,实现URLProtocol的open、read、write、seek、c
FFmpeg的IO模型从avio_open()方法开始,核心结构体由AVIOContext和URLProtocol组成。如果需要读取缓冲区buffer数据进行播放,可以通过自定义AVIOContext,并且实现read_packet、write_packet、seek三个方法。如果需要播放加密视频,可以自定义私有协议进行解密,实现URLProtocol的open、read、write、seek、close等方法。
1、avio打开流程
外部调用avformat_open_input方法,内部会调用init_input/avio_open2方法,接着查找协议并且打开,另外是打开fifo。整体的avio打开流程如下:
avio_open()调用avio_open2(),而avio_open2()又调用ffio_open_whitelist(),代码如下:
int ffio_open_whitelist(AVIOContext **s, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
const char *whitelist, const char *blacklist
)
{
URLContext *h;
int err;
*s = NULL;
err = ffurl_open_whitelist(&h, filename, flags, int_cb, options, whitelist, blacklist, NULL);
if (err < 0)
return err;
err = ffio_fdopen(s, h);
if (err < 0) {
ffurl_close(h);
return err;
}
return 0;
}
int avio_open2(AVIOContext **s, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options)
{
return ffio_open_whitelist(s, filename, flags, int_cb, options, NULL, NULL);
}
int avio_open(AVIOContext **s, const char *filename, int flags)
{
return avio_open2(s, filename, flags, NULL, NULL);
}
2、AVIOContext结构体
AVIOContext的结构体主要有read_packet、write_packet、seek、read_seek等方法,内部有注释演示读写buffer过程,代码如下:
/**
* Bytestream IO Context.
* @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()
*/
typedef struct AVIOContext {
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
*
**********************************************************************************
* READING
**********************************************************************************
* | buffer_size |
* |---------------------------------------|
* | |
*
* buffer buf_ptr buf_end
* +---------------+-----------------------+
* |/ / / / / / / /|/ / / / / / /| |
* read buffer: |/ / consumed / | to be read /| |
* |/ / / / / / / /|/ / / / / / /| |
* +---------------+-----------------------+
*
**********************************************************************************
* 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
*
*/
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*/
void *opaque; /**< A private pointer, passed to the read/write/seek */
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
int (*write_packet)(void *opaque, 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 */
int eof_reached; /**< true if was unable to read due to error or eof */
int write_flag; /**< true if open for writing */
int max_packet_size;
int error; /**< contains the error code or 0 if no error happened */
int64_t (*read_seek)(void *opaque, int stream_index, int64_t timestamp, int flags);
int64_t maxsize;
int64_t bytes_read;
int orig_buffer_size;
int short_seek_threshold;
const char *protocol_whitelist;
const char *protocol_blacklist;
int64_t written;
unsigned char *buf_ptr_max;
int min_packet_size;
} AVIOContext;
3、URLProtocol结构体
URLProtocol的结构体url_open、url_accpet、url_handshake、url_read、url_write、url_seek、url_close等方法,其中url_open()是对url_open2()的封装,代码如下:
typedef struct URLProtocol {
const char *name;
int (*url_open)( URLContext *h, const char *url, int flags);
int (*url_open2)(URLContext *h, const char *url, int flags, AVDictionary **options);
int (*url_accept)(URLContext *s, URLContext **c);
int (*url_handshake)(URLContext *c);
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)(URLContext *h, int pause);
int64_t (*url_read_seek)(URLContext *h, 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;
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;
4、打开url
调用avio.c的ffurl_open_whitelist()打开url,内部调用ffurl_alloc和ffurl_connect,代码如下:
int ffurl_open_whitelist(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb, AVDictionary **options,
const char *whitelist, const char* blacklist,
URLContext *parent)
{
int ret = ffurl_alloc(puc, filename, flags, int_cb);
if (ret < 0)
return ret;
......
ret = ffurl_connect(*puc, options);
if (!ret)
return 0;
fail:
ffurl_closep(puc);
return ret;
}
其中ffurl_alloc主要实现2个功能:查找协议和分配协议,代码如下:
int ffurl_alloc(URLContext **puc, const char *filename, int flags,
const AVIOInterruptCB *int_cb)
{
const URLProtocol *p = NULL;
p = url_find_protocol(filename);
if (p)
return url_alloc_for_protocol(puc, p, filename, flags, int_cb);
*puc = NULL;
return AVERROR_PROTOCOL_NOT_FOUND;
}
url_find_protocol过程主要是遍历协议数组,根据scheme进行匹配:
static const struct URLProtocol *url_find_protocol(const char *filename)
{
const URLProtocol **protocols;
char proto_str[128], proto_nested[128], *ptr;
size_t proto_len = strspn(filename, URL_SCHEME_CHARS);
int i;
if (filename[proto_len] != ':' &&
(strncmp(filename, "subfile,", 8) || !strchr(filename + proto_len + 1, ':')) ||
is_dos_path(filename))
strcpy(proto_str, "file");
else
av_strlcpy(proto_str, filename,
FFMIN(proto_len + 1, sizeof(proto_str)));
av_strlcpy(proto_nested, proto_str, sizeof(proto_nested));
if ((ptr = strchr(proto_nested, '+')))
*ptr = '\0';
protocols = ffurl_get_protocols(NULL, NULL);
if (!protocols)
return NULL;
for (i = 0; protocols[i]; i++) {
const URLProtocol *up = protocols[i];
if (!strcmp(proto_str, up->name)) {
av_freep(&protocols);
return up;
}
if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME &&
!strcmp(proto_nested, up->name)) {
av_freep(&protocols);
return up;
}
}
av_freep(&protocols);
return NULL;
}
ffurl_connect主要是判断用url_open2还是url_open来打开:
int ffurl_connect(URLContext *uc, AVDictionary **options)
{
......
int err =
uc->prot->url_open2 ? uc->prot->url_open2(uc, uc->filename, uc->flags, options) :
uc->prot->url_open(uc, uc->filename, uc->flags);
......
return 0;
}
5、打开fifo
调用aviobuf.c的fifo_fdopen方法打开fifo,主要是分配buffer缓冲区、分配AVIOContext:
int ffio_fdopen(AVIOContext **s, URLContext *h)
{
// malloc buffer
buffer = av_malloc(buffer_size);
if (!buffer)
return AVERROR(ENOMEM);
// malloc context
*s = avio_alloc_context(buffer, buffer_size, h->flags & AVIO_FLAG_WRITE, h,
(int (*)(void *, uint8_t *, int)) ffurl_read,
(int (*)(void *, uint8_t *, int)) ffurl_write,
(int64_t (*)(void *, int64_t, int))ffurl_seek);
fail:
av_freep(&buffer);
return AVERROR(ENOMEM);
}
6、protocol协议列表
FFmpeg支持的protocol包括:file、http、tcp、udp、hls、rtmp等,在protocols.c中声明为全局常量,列表如下(有删减):
extern const URLProtocol ff_async_protocol;
extern const URLProtocol ff_concat_protocol;
extern const URLProtocol ff_crypto_protocol;
extern const URLProtocol ff_file_protocol;
extern const URLProtocol ff_ftp_protocol;
extern const URLProtocol ff_hls_protocol;
extern const URLProtocol ff_http_protocol;
extern const URLProtocol ff_httpproxy_protocol;
extern const URLProtocol ff_https_protocol;
extern const URLProtocol ff_pipe_protocol;
extern const URLProtocol ff_rtmp_protocol;
extern const URLProtocol ff_rtp_protocol;
extern const URLProtocol ff_tcp_protocol;
extern const URLProtocol ff_tls_protocol;
extern const URLProtocol ff_udp_protocol;
file协议默认白名单有file/crypto/data,位于libavformat/file.c,定义如下:
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"
};
http协议默认白名单有http、https、tls、rtp、tcp、udp、crypto、httpproxy、data,位于libavformat/http.c,定义如下:
const URLProtocol ff_http_protocol = {
.name = "http",
.url_open2 = http_open,
.url_accept = http_accept,
.url_handshake = http_handshake,
.url_read = http_read,
.url_write = http_write,
.url_seek = http_seek,
.url_close = http_close,
.url_get_file_handle = http_get_file_handle,
.url_get_short_seek = http_get_short_seek,
.url_shutdown = http_shutdown,
.priv_data_size = sizeof(HTTPContext),
.priv_data_class = &http_context_class,
.flags = URL_PROTOCOL_FLAG_NETWORK,
.default_whitelist = "http,https,tls,rtp,tcp,udp,crypto,httpproxy,data"
};
7、makefile配置
libavformat的makefile脚本,支持enable/disable某种协议,如果开关设为true最终xxx.o文件被链接到动态库或者静态库。具体如下:
# protocols I/O
OBJS-$(CONFIG_ASYNC_PROTOCOL) += async.o
OBJS-$(CONFIG_CONCAT_PROTOCOL) += concat.o
OBJS-$(CONFIG_CRYPTO_PROTOCOL) += crypto.o
OBJS-$(CONFIG_FILE_PROTOCOL) += file.o
OBJS-$(CONFIG_FTP_PROTOCOL) += ftp.o urldecode.o
OBJS-$(CONFIG_HLS_PROTOCOL) += hlsproto.o
OBJS-$(CONFIG_HTTP_PROTOCOL) += http.o httpauth.o urldecode.o
OBJS-$(CONFIG_HTTPPROXY_PROTOCOL) += http.o httpauth.o urldecode.o
OBJS-$(CONFIG_HTTPS_PROTOCOL) += http.o httpauth.o urldecode.o
OBJS-$(CONFIG_PIPE_PROTOCOL) += file.o
OBJS-$(CONFIG_RTMP_PROTOCOL) += rtmpproto.o rtmpdigest.o rtmppkt.o
OBJS-$(CONFIG_RTP_PROTOCOL) += rtpproto.o ip.o
OBJS-$(CONFIG_TCP_PROTOCOL) += tcp.o
TLS-OBJS-$(CONFIG_OPENSSL) += tls_openssl.o
OBJS-$(CONFIG_TLS_PROTOCOL) += tls.o $(TLS-OBJS-yes)
OBJS-$(CONFIG_UDP_PROTOCOL) += udp.o ip.o
更多推荐
所有评论(0)