??????? 前一段時間使用Nginx搭建的多媒體服務器只能在緩沖過的時間區域內拖放, 而不能拖放到未緩沖的地方. 這就帶來了一個問題: 如果視頻限速的速率很小, 那么客戶端觀看視頻時肯定不流暢, 而且用戶不能向前拖放, 用戶體驗很不好. 如果視頻限速的速率很大或者不限速, 服務器是承受不了的, 特別是在某個熱門視頻高并發訪問的情況下, 而且客戶端瀏覽器也在快速的從服務器接收數據, 同樣會造成客戶端視頻播放不流暢的問題, 對服務器的性能和網絡帶寬都是很大的挑戰. 所以很有必要將實現視頻服務器的點播功能, 這樣既可以對視頻進行限速, 避免大量不必要數據在網上的傳送, 又可以改善用戶體驗.
??????? 本文主要參考了 [1] 的實現, 期間會遇到各種意想不到問題, 然后從網上搜索到了解決方法. 本次搭建使用的Nginx版本是1.4.1, jwplayer的版本是6.6.
資料:
HTTP Live Streaming(縮寫是 HLS)是一個由蘋果公司提出的基于HTTP的流媒體 網絡傳輸協議。
HLS只請求基本的HTTP報文,與實時傳輸協議(RTP)不同,HLS可以穿過任何允許HTTP數據通過的防火墻或者代理服務器。它也很容易使用內容分發網絡來傳輸媒體流。
一 準備
搭建點播服務器需要如下幾個模塊:
- nginx_mod_h264_streaming: 使nginx支持h264編碼的視頻
- http_flv_module: 支持flv
- http_mp4_module: 支持mp4
- nginx-rtmp-module: 支持rtmp協議
其中http_flv_module和http_mp4_module兩個模塊是nginx自帶的, 可以在編譯的時候加上相應的選項.
nginx_mod_h264_streaming的下載地址:?http://h264.code-shop.com/trac/wiki/Mod-H264-Streaming-Nginx-Version2
nginx-rtmp-module托管在GitHub上:?https://github.com/arut/nginx-rtmp-module
注意在GitHub的"Build"部分有這樣一句話:
Several versions of nginx (1.3.14 - 1.5.0) require http_ssl_module to be added as well:
1 | ./configure --add-module=<path-to-nginx-rtmp-module> --with-http_ssl_module |
我用的nginx版本是1.4.1, 所以在configure的時候需要加上"--with-http_ssl_module"選項, 否則會出現錯誤:
1 2 3 4 5 | src/http/ngx_http_request.c: 在函數‘ngx_http_set_virtual_server’中: src/http/ngx_http_request.c:1992:9: 錯誤: 未知的類型名‘ngx_http_ssl_srv_conf_t’ src/http/ngx_http_request.c:1999:16: 錯誤: ‘ngx_http_ssl_module’未聲明(在此函數內第一次使用) src/http/ngx_http_request.c:1999:16: 附注: 每個未聲明的標識符在其出現的函數內只報告一次 src/http/ngx_http_request.c:2001:17: 錯誤: 在非結構或聯合中請求成員‘verify’ |
ssl的安裝可以參考網上教程, 也可以參考 [1] . 注意Ubuntu環境下SSL庫是libssl-dev:
sudo apt-get install libssl-dev |
?
關于ffmpeg及其依賴庫的安裝, 請參考我的上一篇文章:http://www.cnblogs.com/wanghetao/p/3386311.html, 在此不再贅述.
???
?二 安裝Nginx及其相關模塊
(Nginx使用的是Perl正則表達式, 所以需要首先安裝PCRE, 讀者能夠從網上找到PCRE的安裝過程, 在此不贅述. )
# mkdir nginx
# cd nginx
# cp ../nginx-1.4.1.tar.gz ../nginx_mod_h264_streaming-2.2.7.tar.gz ../nginx-rtmp-module-master.zip ./
#? tar -zxvf nginx-1.4.1.tar.gz?????????????????????????????????? (同樣地將其余兩個包解壓縮)
# cd nginx-1.4.1
# ./configure --prefix=/usr/local/nginx --add-module=../nginx_mod_h264_streaming-2.2.7 --add-module=../nginx-rtmp-module-master/ --with-http_flv_module --with-http_mp4_module --with-http_gzip_static_module --with-http_stub_status_module --with-cc-opt="-I/usr/local/ffmpeg2/include" --with-ld-opt="-L/usr/local/ffmpeg2/lib"
# make
這個時候出現了錯誤:
1 2 3 4 5 6 7 8 9 10 | ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c: 在函數‘esds_read’中: ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c:377:16: 錯誤: 變量‘stream_priority’被設定但未被使用 [-Werror=unused-but-set-variable] ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c:376:12: 錯誤: 變量‘stream_id’被設定但未被使用 [-Werror=unused-but-set-variable] ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c: 在函數‘stsd_parse_vide’中: ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c:529:22: 錯誤: 變量‘level_indication’被設定但未被使用 [-Werror=unused-but-set-variable] ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c:528:22: 錯誤: 變量‘profile_compatibility’被設定但未被使用 [-Werror=unused-but-set-variable] ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c:527:22: 錯誤: 變量‘profile_indication’被設定但未被使用 [-Werror=unused-but-set-variable] ../nginx_mod_h264_streaming-2.2.7/src/mp4_reader.c:526:22: 錯誤: 變量‘configuration_version’被設定但未被使用 [-Werror=unused-but-set-variable] cc1: all warnings being treated as errors make[1]: *** [objs/addon/src/mp4_reader.o] 錯誤 1 |
?# vim objs/Makefile (修改objs/Makefile文件, 去掉其中的"-Werror"), 然后就能夠正常編譯了.
# make
# make install
至此nginx安裝成功了.
?
三 修改nginx配置文件nginx.conf, 配置虛擬主機
這一部分請參考 [1] , 其中比較傳神的地方是限速功能的實現. 使用一個端口提供視頻文件視頻文件的訪問以及并發訪問量和限速功能(比如8081), 然后從另一個端口配置虛擬主機, 在網頁中使用http協議對8081的視頻文件進行訪問.
?
四 flv文件轉碼和元數據的注入
使用ffmpeg轉碼后的flv文件需要注入元數據(metadata)之后才能實現拖放, metadata中主要是關鍵幀. 我使用yamdi實現元數據的注入.?yamdi為flv文件增加了很多metadata信息,比如創建者、是否有關鍵幀、是否有視頻、是否有音頻,視頻高度和寬度等等。而yamdi加入的meta數據中,最有效的要數關鍵幀。被注入了關鍵幀的flv可以實現像土豆網、優酷網等大型視頻網站一樣的“拖進度”,提前拖到緩沖還未加載到的位置開始播放。另一個可用的工具是flvtool2, 網上一個文章提供了兩者的用法和比較:?http://blog.csdn.net/zhangxh1013/article/details/5896482.
yamdi的安裝也很簡單:
(1) 下載:?http://yamdi.sourceforge.net/
(2) # tar -zxvf yamdi-1.9.tar.gz
(3) # cd yamdi-1.9
(4) # make & make install
元數據注入:
1 | yamdi -i input.flv -o output.flv |
?
輸出的文件使用ffmpeg查看, 可以看到這樣的信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Metadata: ??? creator???????? : m_8efd9a8eb9c1c7434e76fc0e27052f91_md Tudou, Inc. ??? metadatacreator : Tudou Metadata Injector? for ?FLV - Version 1.0 ??? hasKeyframes??? :? true ??? hasVideo??????? :? true ??? hasAudio??????? :? true ??? hasMetadata???? :? true ??? canSeekToEnd??? :? false ??? datasize??????? : 14485828 ??? videosize?????? : 13399710 ??? audiosize?????? : 1041346 ??? lasttimestamp?? : 251 ??? lastkeyframetimestamp: 245 ??? lastkeyframelocation: 14458306 ??? encoder???????? : Lavf55.12.100 |
?這說明元數據注入成功, 并且輸出文件中含有關鍵幀.
當然, 在ffmpeg轉碼的同時也可以向flv文件中加入關鍵幀, 只需加入 "-keyint_min" 參數. ffmpeg文檔中對此的解釋是:
1 2 3 | ‘keyint_min integer (encoding,video)’ ???? Set minimum interval between IDR-frames.????? (設定IDR幀之間的最小間隔) |
?
但是用ffmpeg轉碼的方法添加元數據仍然不能對視頻隨意拖放, 用ffmpeg可以看到轉碼后的視頻的元數據信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ffmpeg -i output.flv ...... ? Metadata: ???? creator???????? : m_8efd9a8eb9c1c7434e76fc0e27052f91_md Tudou, Inc. ???? metadatacreator : Tudou Metadata Injector? for ?FLV - Version 1.0 ???? hasKeyframes??? :? true ???? hasVideo??????? :? true ???? hasAudio??????? :? true ???? hasMetadata???? :? true ???? canSeekToEnd??? :? false ???? datasize??????? : 14485828 ???? videosize?????? : 13399710 ???? audiosize?????? : 1041346 ???? lasttimestamp?? : 251 ???? lastkeyframetimestamp: 245 ???? lastkeyframelocation: 14458306 ???? encoder???????? : Lavf55.12.100 |
?它與使用yamdi方法產生的結果的位于區別是"canSeekToEnd"為false, 為什么這樣的flv視頻不能拖放還有待于以后研究.
?
五 jwplayer播放器的設定
通過以上四個步驟, flv視頻點播服務器已經基本搭建成功了. 下面要使用jwplayer播放視頻, 檢驗以上的設置是否成功. 編寫以下PHP文檔并在瀏覽器中打開:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <!DOCTYPE heml> <html> ???? <head> ???????? <script type= "text/javascript" ?src= "jwplayer/jwplayer.js" ></script> ???????? <meta http-equiv= "Content-Type" ?content= "text/html;charset=utf-8" ?/> ???? </head> ???? ????? <body>??????? ???????? <div id= "myElement" >Loading the page...</div> ???????? <script type= "text/javascript" > ???????????? var ?file_name =? "<?php echo? $_GET['id']; ?>" ; ???????????? jwplayer( "myElement" ).setup({ ???????????????? file:? "http://localhost:82/" ?+ file_name, ???????????????? // image: "data/myposter.jpg", ???????????????? title: file_name, ???????????? }); ???????? </script> ???? </body> </html> |
?瀏覽器中輸入URL:
1 | http://localhost/videoplayer/?id=output.flv |
?滿懷激動地心情, 結果還是失望了: 視頻仍然不能拖放!!! 為什么會這樣呢? 原因是jwplayer的設定不正確. 需要向jwplayer中添加SEEK功能, 告訴它要對文件進行seek, 這是通過"starttime"參數實現的. 可以參考:?http://www.longtailvideo.com/support/jw-player/28855/pseudo-streaming-in-flash. 對于不同編碼和不同視頻格式的文件, "starttime"的值各不相同. 對于h264編碼的flv格式文件, "starttiem"設為"start".
?
修改完成后, 在瀏覽器中再次輸入URL, 嘗試拖動進度條, 成功了!!! 真的很高興..來個高清大圖給乃們瞧瞧.
參考文獻:
[1]?http://5iqiong.blog.51cto.com/2999926/1132639