今天闲来无事,尝试看了下ffmpeg中的相关http协议传输处理代码
先简单说下这个代码在整个仓库里面的位置:
ffmpeg/libavformat/http.h
ffmpeg/libavformat/http.c
avoi.h是ffmpeg中libavformat/目录下的一个重要的头文件,这个文件主要处理 了一些传输协议的传输封装。他的封装过程是通过函数指针来实现的。
可以先看http.c文件中的最后一个结构体:
URLProtocol http_protocol = {
"http",
http_open,
http_read,
http_write,
http_seek,
http_close,
.url_get_file_handle = http_get_file_handle,
.priv_data_size = sizeof(HTTPContext),
.priv_data_class = &httpcontext_class,
};
URLProtocol这个结构是在avoi.h中一个很重要的结构,他的存在的意义就是为了统一接口,可以在avoi.h中查到他的定义:
typedef struct URLProtocol {
const char *name;
int (*url_open)(URLContext *h, const char *url, int flags);
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);
struct URLProtocol *next;
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 priv_data_size;
const AVClass *priv_data_class;
} URLProtocol;
针对http.c文件处理的http协议,最主要的就是实现URLProtocol中的函数指针,至于每个函数的具体意义,相信看命名就可以知道。
那么avoi.h中的函数又是怎么使用到这个内容的呢?这就可以从avoi.h中的几个函数看出来:
int url_open();
int url_read();
int url_write();
url_connect:
url_close:
http_open函数会尝试调用其中的http_open_cnx,然后http_open_cnx会调用静态成员函数http_connect来进行http请求,其中所有的请求发送都是通过http_write函数来进行的。
相关的函数:
http_read, http_getline, http_getc
http_getc会有个1024的缓冲区,如果发现缓冲区满了才会继续读取,如果没有慢就每次读缓冲区里面的一个字符
static int http_getc(HTTPContext *s)
{
int len;
if (s->buf_ptr >= s->buf_end) {
len = url_read(s->hd, s->buffer, BUFFER_SIZE);
if (len < 0) {
return AVERROR(EIO);
} else if (len == 0) {
return -1;
} else {
s->buf_ptr = s->buffer;
s->buf_end = s->buffer + len;
}
}
return *s->buf_ptr++;
}
http.c函数的URLProtocol结构体中有个PrivateData的指针,这个指针存储了http协议中的一些内部使用到的数据变量,内部处理的数据变量结构体如下:
typedef struct {
const AVClass *class;
URLContext *hd;
unsigned char buffer[BUFFER_SIZE], *buf_ptr, *buf_end;
int line_count;
int http_code;
int64_t chunksize; /**< Used if "Transfer-Encoding: chunked" otherwise -1. */
int64_t off, filesize;
char location[MAX_URL_SIZE];
HTTPAuthState auth_state;
unsigned char headers[BUFFER_SIZE];
int willclose; /**< Set if the server correctly handles Connection: close and will close the connection after feeding us the content. */
} HTTPContext;
可以看到一个比较重要的信息是:该私有的内部数据中竟然也有一个URLContext,那这个是什么呢?这个URLContext的协议又是什么呢?我通过阅读代码发现这个URLContext就是指向的tcp协议,用来通过tcp读取和返回数据。很感慨阿。这样很简单的就可以同时支持http,tcp协议,而且把tcp,udp协议封装起来以后,基本上其他的像rtsp, mms等协议也可以通过制定他们自己的内在协议来处理了。
在http.c中的http_open_cnx函数中有一句代码:
ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
通过看ffmpeg的代码,总结了下,其实ffmpeg本身并不是很神秘,他本身只是提供了一个很好的很平等并可以无线扩展的架构。而在ffmpeg这个架构上,才能有很多codec, decodec和mutex, io, protocol很好的共存处理。
很欣赏,能作这个完美架构的大师,只有这种完美的架构才能吸引更多的开源爱好着去研究ffmpeg的代码,并不断的丰富它!