當(dāng)前流行的視頻網(wǎng)站,關(guān)于視頻的在線拖動觀看技術(shù)的小試,
1.flv文件
flv視頻文件,想大家都比較清楚,只要從關(guān)鍵幀的位置切開,并加上"FLV\x1\x1\0\0\0\x9\0\0\0\x9"公共頭標(biāo)志信息,后對任何支持flv視頻文件dencoder的播放器都是可以正常播放的
"FLV\x1\x1\0\0\0\x9\0\0\0\x9" + keyframe(body)
2.lighttpd中的mod_flv_streaming.c模塊 (重點)
http://www.lighttpd.net/ http://blog.lighttpd.net/articles/2006/03/09/flv-streaming-with-lighttpd 這里通過http的get方法,向服務(wù)器請求指定的視頻片段
形如: http://youserver.com/flv/abcdefg.flv?start=12345
mod_flv_streaming.c默認(rèn)的方式是,從start(文件的物理位置offset)的開始,默認(rèn)到文件結(jié)束。
相關(guān)服務(wù)器代碼如下:
- for (k = 0; k < p->conf.extensions->used; k++) {
- data_string *ds = (data_string *)p->conf.extensions->data[k];
- int ct_len = ds->value->used - 1;
-
- if (ct_len > s_len) continue;
- if (ds->value->used == 0) continue;
-
- if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
- data_string *get_param;
- stat_cache_entry *sce = NULL;
- buffer *b;
- int start;
- char *err = NULL;
-
-
-
- array_reset(p->get_params);
- buffer_copy_string_buffer(p->query_str, con->uri.query);
- split_get_params(p->get_params, p->query_str);
-
- if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {
- return HANDLER_GO_ON;
- }
-
-
- if (get_param->value->used < 2) return HANDLER_GO_ON;
-
-
- start = strtol(get_param->value->ptr, &err, 10);
- if (*err != '\0') {
- return HANDLER_GO_ON;
- }
-
- if (start <= 0) return HANDLER_GO_ON;
-
-
- if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
- return HANDLER_GO_ON;
- }
-
- if (start > sce->st.st_size) {
- return HANDLER_GO_ON;
- }
-
-
- b = chunkqueue_get_append_buffer(con->write_queue);
-
-
- buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));
-
-
- http_chunk_append_file(srv, con, con->physical.path, start, sce->st.st_size - start);
-
- response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));
-
- con->file_finished = 1;
-
- return HANDLER_FINISHED
for (k = 0; k < p->conf.extensions->used; k++) {data_string *ds = (data_string *)p->conf.extensions->data[k];int ct_len = ds->value->used - 1;if (ct_len > s_len) continue;if (ds->value->used == 0) continue;if (0 == strncmp(con->physical.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {data_string *get_param;stat_cache_entry *sce = NULL;buffer *b;int start;char *err = NULL;/* if there is a start=[0-9]+ in the header use it as start,* otherwise send the full file */array_reset(p->get_params);buffer_copy_string_buffer(p->query_str, con->uri.query);split_get_params(p->get_params, p->query_str);/*這個是重點,獲得客戶端的播放器通過get方法傳來的start(文件拖動的開始位置offset)*/if (NULL == (get_param = (data_string *)array_get_element(p->get_params, "start"))) {return HANDLER_GO_ON;}/* too short */if (get_param->value->used < 2) return HANDLER_GO_ON;/* check if it is a number */start = strtol(get_param->value->ptr, &err, 10);if (*err != '\0') {return HANDLER_GO_ON;}if (start <= 0) return HANDLER_GO_ON;/* check if start is > filesize */if (HANDLER_GO_ON != stat_cache_get_entry(srv, con, con->physical.path, &sce)) {return HANDLER_GO_ON;}if (start > sce->st.st_size) {return HANDLER_GO_ON;}/* we are safe now, let's build a flv header */b = chunkqueue_get_append_buffer(con->write_queue);/*準(zhǔn)備發(fā)送給客戶端播放器的flv公共頭信息13字節(jié)*/buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));/*發(fā)送seek到的拖動關(guān)鍵楨的位置keyframe, 并計算出從拖動點到該視頻文件結(jié)束的長度*/http_chunk_append_file(srv, con, con->physical.path, start, sce->st.st_size - start);/* http相關(guān)信息的填充*/response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));con->file_finished = 1;/*done, 處理拖動完成*/return HANDLER_FINISHED
3 對客戶端的播放器而言,服務(wù)器對flv文件的拖動支持是透明的,
只要播放器安照合法的get方法向服務(wù)器請求,即可得到可以播放的視頻片段,
但是這里的start=xxxx,一定是flv視頻文件的關(guān)鍵楨位置,否則不能播放
一般而言,對flv視頻文件可以使用
yamdi
http://yamdi.sourceforge.net/ 加上metadata信息,即可完美支持拖動,
播放器可以再第一次請求視頻文件的時候獲得該視頻的metadata信息(即包含關(guān)鍵楨,時間,等等與視頻相關(guān)的信息)
4.以上討論的,都是lighttpd-mod_flv_streaming.c默認(rèn)的對flv的拖動支持,
其實可以,做一些簡單的擴(kuò)展,
可以增加end參數(shù),即是增加拖動的結(jié)束參數(shù),
假設(shè),很多用戶并不會觀看視頻到結(jié)束,
每次請求N字節(jié)
http://youserver.com/flv/abcdefg.flv?start=23456&end=23456+N
播放器可以增加這樣的功能,
a.先請求某一段視頻
b.當(dāng)當(dāng)前視頻片段沒有播放完畢之前,不會去請求下一個片段,
只有當(dāng)當(dāng)前視頻片段在即將播放結(jié)束的時候,再去請求下一個片段,
這樣,當(dāng)用戶,看了前面的某一片段后,突然關(guān)閉該播放頁,以至于不會白白浪費掉那已經(jīng)download到本地,但是并沒有觀看的視頻,節(jié)余帶寬
粗略代碼實現(xiàn)如下:
- if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "start")))
- {
-
-
- if (get_param->value->used < 2)
- return HANDLER_GO_ON;
-
-
- start = strtol(get_param->value->ptr, &err, 10);
- if (*err != '\0')
- {
- return HANDLER_GO_ON;
- }
-
-
- if (start < 0)
- return HANDLER_GO_ON;
-
-
- if (start > sce->st.st_size)
- {
- return HANDLER_GO_ON;
- }
- }
- else
- {
- return HANDLER_GO_ON;
- }
-
-
-
-
- if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "end")))
- {
-
- if (get_param->value->used < 2)
- return HANDLER_GO_ON;
-
-
- end = strtol(get_param->value->ptr, &err, 10);
- if (*err != '\0')
- {
- return HANDLER_GO_ON;
- }
-
-
-
-
-
-
- if (end <= 0 || start >= end)
- return HANDLER_GO_ON;
-
-
- if (end > sce->st.st_size)
- {
-
- tflvend = sce->st.st_size;
- }
- }
- else
- {
- return HANDLER_GO_ON;
-
-
- }
-
-
- b = chunkqueue_get_append_buffer(con->write_queue);
-
-
- buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));
-
-
- http_chunk_append_file(srv, con, con->physical.path, start, end - start);
- response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));
- con->file_finished = 1;
- return HANDLER_FINISHED
if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "start"))){/* too short */if (get_param->value->used < 2)return HANDLER_GO_ON;/* check if it is a number */start = strtol(get_param->value->ptr, &err, 10);if (*err != '\0'){return HANDLER_GO_ON;}/* check if tflvbegin is >= 0 */if (start < 0)return HANDLER_GO_ON;/* check if start is > filesize */if (start > sce->st.st_size){return HANDLER_GO_ON;}}else{return HANDLER_GO_ON;}/* if there is a start=[0-9]+ in the header use it as end,* otherwise send the full file *//*這里重點,仿照lighttpd如何獲得get方法的中的start,同理獲得end參數(shù), 并做一些必要的合法性檢查*/if (NULL != (get_param = (data_string *)array_get_element(p->get_params, "end"))){/* too short */if (get_param->value->used < 2)return HANDLER_GO_ON;/* check if it is a number */end = strtol(get_param->value->ptr, &err, 10);if (*err != '\0'){return HANDLER_GO_ON;}/*參數(shù)檢查,必須*//* check if end is > 0* check if start < end* make sure star > 0* */if (end <= 0 || start >= end)return HANDLER_GO_ON;/* check if end is > filesize */if (end > sce->st.st_size){//return HANDLER_GO_ON;tflvend = sce->st.st_size; /* path tflvend is not right */}}else{return HANDLER_GO_ON;}/* we are safe now, let's build a flv header */b = chunkqueue_get_append_buffer(con->write_queue);/*準(zhǔn)備發(fā)送給客戶端播放器的flv公共頭信息13字節(jié)*/buffer_copy_string_len(b, CONST_STR_LEN("FLV\x1\x1\0\0\0\x9\0\0\0\x9"));/*這個是重點,以前默認(rèn)的是發(fā)送的數(shù)據(jù)長度是用 fileseize-start, 現(xiàn)在要用end替代filesize, 即是 end-start*/http_chunk_append_file(srv, con, con->physical.path, start, end - start);response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("video/x-flv"));con->file_finished = 1;return HANDLER_FINISHED