1. 程式人生 > >Nginx搭建flv視訊點播伺服器

Nginx搭建flv視訊點播伺服器

  前一段時間使用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