本文後續的内容将在這裡更新:《基于nginx-rtmp-module子產品實作的HTTP-FLV直播子產品(nginx-http-flv-module)續》。注意:下文的配置很多已經不能用了,因為現在的實作跟早期的實作相差有點大。而為了看到整個項目的變遷史,是以保留了下來,下文的更新中提到了為什麼有些配置項不能再使用的原因。現在使用的配置可查找下文中的README.CN.md。
近幾年直播行業火爆,開源的直播軟體解決方案有SRS(Simple-RTMP-Server)和nginx-rtmp-module,前者是國人發起的一個優秀的開源項目,目前國内很多公司都使用它作為直播解決方案,由C++編寫;後者依賴Nginx,以第三方子產品的方式提供直播功能,由C編寫。SRS采用多線程方式(經網友提醒更正:是單線程+協程方式),性能優秀,經受住了衆多場景的考驗,但是SRS3已經閉源(更正:是有一段時間閉源了,現在又開源了);nginx-rtmp-module是采用多程序方式,Nginx的性能優秀,但是據網友測試,nginx-rtmp-module的性能不如SRS,并且nginx-rtmp-module的作者已經很久沒有更新版本了,支援的功能也有限,例如不支援HTTP方式的FLV直播,而這是國内直播行業普遍采用的方式;再如推流不支援upstream,無法分布式部署功能;還有飽受诟病的播放響應延遲時間很長的問題(即俗稱的不能秒播)等。
我在nginx-rtmp-module的基礎上實作了基于HTTP方式的FLV直播功能,支援GOP緩存,減少播放響應延遲時間;支援流式和Transfer-Encoding: chunked兩種HTTP響應格式;修複nginx-rtmp-module沒有listen配置項時,推流失敗的問題;解決nginx-rtmp-module已知的bug,見nginx-http-flv-module,歡迎下載下傳測試和修複bug。有問題或者建議,可以加Q群:711969608詳聊。目前已經有廠商準備将本子產品商用,目前已知有9家,其中一家廠商是華為,據另一家廠商的員工稱他們的客戶包括映客和微吼,其他的目前都還在測試中,有廠商陸續回報過不少bug,修複後功能已經越來越穩定,在此表示感謝。
nginx-http-flv-module與nginx-rtmp-module的功能對比:
功能 | nginx-http-flv-module | nginx-rtmp-module | 備注 |
HTTP-FLV | √ | × | nginx-http-flv-module支援HTTPS-FLV |
GOP緩存 | √ | × | |
vhost | √ | × | |
省略listen配置 | √ | x | |
RTMP 302 | Beta | × | nginx-http-flv-module作為伺服器或者用戶端 |
如果不想推流,可以用一個現成的直播位址rtmp://live.hkstv.hk.lxdns.com/live/hks。
典型的nginx.conf如下:
worker_processes 4; #Nginx開啟4個子程序,子程序個數最好跟CPU的核心數一樣
worker_cpu_affinity 0001 0010 0100 1000; #CPU的mask,子程序使用它來綁定CPU核心,避免程序切換造成性能損失
error_log logs/error.log error; #錯誤日志位置和日志級别,如果使用預設編譯選項,位置為/usr/local/nginx/logs/error.log,error表示隻列印錯誤日志
events {
worker_connections 1024; #Nginx處理的最大連接配接數
}
http {
include mime.types;
default_type application/octet-stream;
keepalive_timeout 65;
server {
listen 80; #Nginx監聽的HTTP請求端口
location / {
root /var/www; #HTTP請求URL映射到伺服器的位置
index index.html index.htm; #HTTP請求優先請求的檔案,如http://localhost/,如果有index.html在/var/www目錄下,那麼請求的是/var/www/index.html
}
error_page 500 502 503 504 /50x.html; #如果遇到這些HTTP請求錯誤,Nginx傳回50x.html的内容
location = /50x.html {
root html; #因為/配置了root /var/www,是以這兒html對應的是/var/www/html,是以50x.html的路徑是/var/www/html/50x.html
}
location /live {
flv_live on; #當HTTP請求以/live結尾,比對這兒,這個選項表示開啟了flv直播播放功能
chunked on; #HTTP協定開啟Transfer-Encoding: chunked;方式回複
}
}
}
rtmp_auto_push on; #因為Nginx可能開啟多個子程序,這個選項表示推流時,媒體流會釋出到多個子程序
rtmp_auto_push_reconnect 1s;
rtmp_socket_dir /tmp; #多個子程序情況下,推流時,最開始隻有一個子程序在競争中接收到資料,然後它再relay給其他子程序,他們之間通過unix domain socket傳輸資料,這個選項表示unix domain socket的路徑
rtmp {
out_queue 4096;
out_cork 8;
max_streams 64; #Nginx能接受的最大的推流數
server {
listen 1935; #Nginx監聽的RTMP推流/拉流端口,可以省略,預設監聽1935
application myapp {
live on; #當推流時,RTMP路徑中的APP(RTMP中一個概念)比對myapp時,開啟直播
gop_cache on; #開啟GOP(Group of Picture)緩存,播放器解碼時,收到一個完整的GOP才會開始播放,這個是減少播放延遲的選項
pull rtmp://live.hkstv.hk.lxdns.com/live/hks; #如果懶得推流,那可以用這個,香港衛視的直播推流
}
#以下配置項已廢棄
application app1 {
proxy_pass rtmp://host(ip or domain name)[:host]/app2; #将推流反向代理到上遊伺服器,并将app1自動轉化為app2
#proxy_pass rtmp://backend; #将推流反向代理到上遊伺服器,見upstream配置
}
}
server {
listen 1935;
server_name *.test.com; #或者www.test.*/www.test.com
application myapp {
live on;
gop_cache on;
}
}
#以下配置項已廢棄,原因在下文更新中
upstream backend {
#開啟負載均衡
server host1:port1;
server host2:port2;
}
}
啟動Nginx,在vlc播放器中以“網絡”方式打開媒體,填入http://localhost/live?stream=hks即可。
通用URL:http://example.com[:port]/dir?[srv=index&app=xxx&]stream=xxx。
如果http配置塊裡的監聽端口不是80(預設),那麼必須加上:port,如:8080。
如果rtmp配置塊裡有多個server配置塊,如果想要播放的流的配置是在第二個server配置塊中,那麼必須加上srv=1(從0開始計數)。
如果rtmp配置塊中的某個server塊下有多個application配置塊,如果想要播放的流的APP(RTMP中的一個概念)的名稱是test,那麼必須指明app=test,stream對應的是推流的名稱。
推流的通用指令:ffmpeg -i -re xxx.mp4(或者與RTMP相容的媒體檔案)-vcodec copy -acodec copy -f flv rtmp://example.com[:port]/app/stream,後面也可以像HTTP的URL那樣加參數,目前沒仔細研究過,如果想推流到myapp,那麼app換成myapp,stream随便取名,播放的時候跟它保持一緻就可以。
其他的見nginx-rtmp-module的wiki說明。
測試效果圖如下:

2017-09-18更新:
反向代理和負載均衡的功能已經基本可用,但是之前并為考慮到如果推流數很多,例如1000路推流,這可能對伺服器造成沉重的負擔。那為什麼HTTP協定使用反向代理和負載均衡沒有這個問題呢?那是因為HTTP請求占用的帶寬很有限,負載瞬時可能很高,但是不會太持久。
2017-10-07更新:
虛拟主機功能已基本可用,即可以像HTTP配置那樣配置server_name了,由于可以通過虛拟主機查找配置,是以不再支援參數srv=index,添加了一個參數port,如果不指定,預設為1935,用于指定以該port查找推流對應的配置。通用URL變為:http://example.com[:port]/dir?[port=xxx&]app=xxx&stream=xxx
2017-11-10更新:
RTMP的302重定向已基本可用,但是由于很多點傳播放器不支援重定向,是以該功能很受限,目前隻有JW Player測試通過,VLC無法解析傳回的重定向資訊,其他播放器沒有測試過。關于RTMP的302重定向,可以參考Adobe的官網裡的application.redirectConnection部分說明:https://helpx.adobe.com/adobe-media-server/ssaslr/application-class.html。
設定如下:在server塊或者application塊中添加配置,假設推流時的app為myapp,要重定向到test,保持流的名稱不變:
rewrite '^/app/(.*)' '/test/$1';
這樣,就可以在本機上将推流或者播放都從app重定向到test上。
如果要推流到其他主機,則可以設定為:
rewrite '^/app/(.*)' rtmp://otherhost:otherport/otherapp/$1;
這樣,就可以将本機上的推流或者播放都重定向到其他主機上,這也是一種負載均衡的方法。
PS:不太願意将rewrite分支merge到master上,畢竟受限太多,功能有點雞肋。
2017-11-12更新:
今天在筆記本上進行壓力測試,用的是srs給的測試工具,而它不支援推mp4檔案流,隻支援flv格式,結果一測試就出現問題,HTTP方式播放無法正常運作,查了下代碼,已經修複bug。
2017-11-22更新:
有網友提到同時使用HTTP和RTMP方式直播時,停止RTMP方式播放會導緻HTTP方式播放也停止,這個bug幾天前測試的時候已經發現,不過最近由于工作比較忙,沒來得及改,今天修複了這個bug。
2017-12-10更新:
評論中有網友指出不知道如何使用HTTP方式播放直播流,可以檢視github上的README.CN.md,這個檔案是中文說明,README.md是英文說明。這兩天專門更新了一下這兩個檔案,沒有添加新的功能。測試截圖如下,其中網頁是用RTMP方式播放,VLC是HTTP方式播放:
插個使用flv.js播放的截圖(2018-04-06):
2017-12-30更新:
2017年最後一次更新,由于之前已經提及為什麼反向代理和負載均衡在實際生活中不太實用,是以已經把README檔案裡的反向代理和負載均衡的說明删除了,不過代碼還沒有删除,後續會陸陸續續删除。對于評論中有網友提到的問題,有些還沒修複,我很抱歉,平時上班比較忙,年底連續上了12天班,通宵1晚,是以來不及修複問題。有興趣的網友可以自己hack代碼,代碼風格是嚴格按照nginx的官方要求格式寫的,我自認為看着還行,至于有些邏輯問題,我也沒搞太清楚,隻知道那樣寫沒問題。
最後,最近重寫了http-flv直播的功能,組裝資料和發送全部使用HTTP的架構,不再使用一些“裸露”的組裝資料的方法,如"HTTP/1.1 200 OK"CRLF,發送也使用ngx_http_send_header和ngx_http_output_filter完成,不再使用自定義的發送函數,為什麼有這個想法,源于nginx從1.3.9版本後原生支援HTTP的chunked傳輸,沒有必要再自己搞一套組裝和發送chunked資料的方法,并且對于非chunked傳輸,nginx的HTTP子產品更不在話下,是以幹脆全部用nginx的HTTP架構了。
最後,上面說的代碼不會送出了,因為我發現有人fork代碼後,又删除了fork,然後在自己的代碼裡加了些我的項目裡的代碼,盡管改了變量名什麼的,還是看得出痕迹。BSD-2-Clause開源協定本來要求很簡單,你修改,再釋出甚至商用,LICENSE檔案裡署上原作者資訊即可。這點都辦不到,那我也隻能小心眼了。
2018-01-02更新:
反向代理和負載均衡的代碼已經從master分支删除,vhost分支與master分支代碼是一樣的,upstream分支還保留有反向代理和負載均衡的代碼,有需要的可以檢視這個分支,後續不再維護這兩個功能。
2018-01-03更新:
感謝一些網友指出nginx-http-flv-module因為nginx的版本變更造成不能編譯的問題,目前已經把一些已發現的相容問題修複了,測試到最舊的nginx版本是1.2.6,考慮到nginx-1.2.6已經是2012年的版本了,是以絕大多數情況下應該不會使用比它更舊的版本,是以不再測試nginx-http-flv-module和更舊的nginx版本的相容性了。
2018-01-12更新:
最近使用srs-bench推流測試nginx-http-flv-module的穩定性,發現在播放測試視訊第三遍時(偶爾第一遍、第二遍)會出現CPU使用率暴增,nginx不接受任何服務,播放器畫面靜止不動的問題(我用過的播放器都會出現這問題,是以不是播放器的問題)。經調試,發現是在釋放已使用的連結清單(并不是釋放記憶體,是把記憶體連結清單鍊入一個free指針)時,無限循環了,即已使用的連結清單形成了環。後來确認是重複釋放已使用的連結清單造成的問題,修改代碼後,播放測試視訊十幾遍(半個多小時)沒再出現問題,代碼已經更新。謹慎猜測nginx-rtmp-module也有此問題,但是沒有測試過。
2018-02-07更新:
有網友送出代碼了,包括定時輸出日志和FCPublish等指令的處理,代碼已經合并。另外有網友在伺服器上試用,32GB的記憶體6個小時耗盡,顯然有記憶體洩漏,目前已經修複。不得不佩服nginx-rtmp-module的原作者,記憶體連結清單使用了引用計數器,配置設定和釋放對計數器的操作避免了多次釋放造成連結清單環的形成。還修複了一個因為GOP緩存數目為2時,會造成瞬間發送資料的速率太高,造成播放器來不及接收資料,進而造成播放卡頓的bug。用wireshark抓包可以看出有'TCP Window Full'的問題,經查造成此問題的原因就是播放器來不及接收資料。
2018-02-27更新:
有網友提出想在Windows上運作帶有nginx-http-flv-module的nginx,而我之前一直将重心放在Linux上,并且Mac OS X上也能編譯通過,但是沒怎麼測試,昨晚在Windows上編譯時,發現好多編譯錯誤,并且如果開啟了“chunked on;”配置項,播放會崩潰,現一并修複了這些bug,非常感謝網友們的測試與建議。
由于本文已經很長,後續的内容将在這裡更新:《基于nginx-rtmp-module子產品實作的HTTP-FLV直播子產品(nginx-http-flv-module)續》。
from:https://blog.csdn.net/winshining/article/details/74910586