ffmpeg中的http協議相關代碼閱讀筆記
今天閑來無事,嘗試看了下ffmpeg中的相關http協議傳輸處理代碼
先簡單說下這個代碼在整個倉庫裡面的位置:
ffmpeg/libavformat/http.h
ffmpeg/libavformat/http.c
avoi.h中的函數調用分析
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();
int64_t url_seek(URLContext *h, int64_t pos, int whence);int url_close(URLContext *h);找其中的一個分析,url_open:int url_open(URLContext **puc, const char *filename, int flags){ int ret = url_alloc(puc, filename, flags); if (ret) return ret; ret = url_connect(*puc); if (!ret) return 0; url_close(*puc); *puc = NULL; return ret;}url_open函數中使用到了url_alloc, url_connect, url_close,這3個函數可能會在不同的情況下進行使用,由於代碼太多,就不貼了,僅記錄如下url_alloc函數:
- 處理協議探測,如果url參數中沒有正常的協議,就預設填上"file://"然後作為檔案協議處理
- 處理探測的過程是在所有的已經註冊上的協議鏈表中搜尋對應名稱的協議,這個名稱,就是URLProtocol.name
- 如果探測成功會把puc,即URLContext結構體也幫忙分配好,並作為參數out
url_connect:
- 和url_open函數一樣,url_connect會調用協議中的http_open函數進行http請求初始化: uc->prot->url_open()
- url_connect返回非零值代表失敗
url_close:
- 如果url_connect失敗,返回是非0值,就會調用url_close,並返回整個url_open函數失敗
http.c檔案的一些關鍵點分析http_open函數
http_open函數會嘗試調用其中的http_open_cnx,然後http_open_cnx會調用靜態成員函數http_connect來進行http請求,其中所有的請求發送都是通過http_write函數來進行的。
http_read函數
相關的函數:
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協議中資料發送和接受的處理
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);
這樣就很好的把http的底層tcp協議比較好的串接起來總結
通過看ffmpeg的代碼,總結了下,其實ffmpeg本身並不是很神秘,他本身只是提供了一個很好的很平等並可以無限擴充的架構。而在ffmpeg這個架構上,才能有很多codec, decodec和mutex, io, protocol很好的共存處理。
很欣賞,能作這個完美架構的大師,只有這種完美的架構才能吸引更多的開源愛好著去研究ffmpeg的代碼,並不斷的豐富它!