用nginx-rtmp-module直播寫程式碼
前言
前段時間用nginx-rtmp-module搭了一個直播系統,測試的時候用來直播了一下桌面,感覺評價還不錯,應邀寫個簡單的教程。
伺服器
安裝
伺服器端我用的是nginx-rtmp-module,作業系統用的是CentOS 7,部署其實很簡單, 它主頁上就有教程。
當然為了系統比較乾淨,推薦還是簡單打個包,CentOS的話我推薦從nginx的官方源下載 原始碼包,在configure的引數上加一條--add-module=/path/to/nginx-rtmp-module
即可。
如果是archlinux的話可以考慮用aur的裡的包。
基本配置
nginx的基本配置可以參考如下:
user nginx; worker_processes 1; error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid; events { worker_connections 1024; } rtmp { server { listen 1935; application live { live on; drop_idle_publisher 10s; pull <src.url>name=<src.name> static; } } }
如果單純想用rtmp作推流和播放的話,以上的配置就足夠了,pull可以讓nginx-rtmp-module自動從其他地址拖流在本地播放, 如果不需要可以去掉。
如果伺服器有SELinux的話,可以考慮關掉,主要是因為1935這個埠號。
認證與動態拉流
這裡有兩個問題,第一是任何一個知道這個url的使用者都可以向這個地址推流,第二是拉流的地址是固定好的,如果你臨時起意 想從某個地址拉流,那麼你必須修改這個配置檔案並且重啟nginx(注意reload是無效的,因為rtmp是一個有狀態的長連線, reload並不能讓nginx切到新的配置,這個算是nginx-rtmp-module的bug吧)。
nginx-rtmp-module提供了一個方案是notify, 也就是採用回撥的方式對使用者的身份進行驗證,同時也允許你動態定義從什麼地址動態拉流。
簡單說來就是,每當有一個播放或推流請求時,nginx-rtmp-module都會向你指定的地址傳送一個http請求,並帶上一些引數, 如請求型別(connect, play, publish等),請求地址,url(會帶上rtmp的引數)。針對伺服器返回的值,nginx-rtmp-module 會採用不同的行為。2xx會正常放行,3xx會從另一個地址拖流,其他返回值則使這個請求被中斷。接下來就完全由的你想象來決定 你的rtmp伺服器有什麼樣的訪問控制了。
另外在連線斷開的時候也有類似的回撥請求, 但是伺服器的返回值不會對nginx-rtmp-module的行為造成影響。可以用來記錄線上人數,播放時長等。
相關的配置如下:
... rtmp { server { listen 1935; application live { live on; drop_idle_publisher 10s; on_play ; on_play_done ; on_publish ; on_publish_done ; } } } http { include /etc/nginx/mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; #gzip on; server { location /live_control { proxy_pass <live_control_url>; proxy_redirect off; proxy_set_header Host $host; proxy_ssl_verify on; proxy_ssl_verify_depth 3; proxy_ssl_trusted_certificate /etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem; allow 127.0.0.1; deny all; } } }
需要注意的是,nginx-rtmp-module的所有回撥都不支援https,所以如果想用https在遠端作控制需要用http在本地中轉一次。
Dash與HLS
rtmp雖然是一種老牌的流媒體協議,但是它存在一些固有的問題。比如對現有的CDN基礎設施不友好,相對於HTTP更復雜, 有狀態。現在的流媒體也有傾向於採用HTTP協議來進行流媒體傳輸的趨勢。但是HTTP協議存在延時大的問題,算是各有利弊。
nginx-rtmp-module內建了對hls和dash的支援。其中hls我用了一段時間,感覺還好,dash沒用過,我就不評價了。
其實hls的支援也很簡單,就是簡單地進行了切片。如果需要轉碼、多位元速率等功能,需要自己用push和 exec_push拼一下,我之前試的時候效果並 不好,所以不太推薦,當然也可能是我之前機器不太好(因為ffmpeg可能會吃掉所有CPU,機器好不好影響挺大的)。
hls的配置如下
... rtmp { server { listen 1935; application live { live on; drop_idle_publisher 10s; hls on; hls_path /tmp/hls; hls_fragment 3s; } } } http { ... server { location /hls { types { application/vnd.apple.mpegurl m3u8; } root /tmp; add_header Cache-Control no-cache; # To avoid issues with cross-domain HTTP requests add_header Access-Control-Allow-Origin *; } location ~* ^/hls/.*\.m3u8$ { types { application/vnd.apple.mpegurl; } root /tmp; expires -1; # disable cache } location ~* ^/hls/.*\.ts$ { types { video/mp2t ts; } root /tmp; expires @5m; } } }
簡單說,nginx-rtmp-module會幫你把hls切好,你需要自己用http伺服器把它服務出去。
狀態監視
之前說了可以用notify實現線上人數監視,不過這個也不那麼可靠(畢竟HTTP請求失敗了nginx-rtmp-module不會重試),另外 相對也挺複雜的。其實nginx-rtmp-module內建了一個狀態資訊。
配置如下:
... http { ... server { location / { rtmp_stat all; rtmp_stat_stylesheet stat.xsl; allow <your-network>; deny all; } location /stat.xsl { root /srv/stat; } } }
簡單地在nginx的http配置里加上rtmp_stat就可以了,會返回一個xml檔案,如果你想在瀏覽器裡比較舒服地看這個xml檔案, 可以從這裡下載xsl檔案放到伺服器上,並且加上 後面的配置。最後如果不想被圍觀,可以加上acl。(如果你已經寫了一個回撥http伺服器的話,也可以用 這個模組,效果拔群。
Alternative
我在接觸直播之後用的第一rtmp伺服器就是nginx-rtmp-module,感覺用起來還不錯,就一直用下來的。前天討論的時候感謝 @typcn 童鞋指出了,nginx-rtmp-module的一些問題:
- 沒有修 pts,只是簡單複製,相容性差
- 沒有GOP重傳,使用者加入之後拿到的第一幀不是關鍵幀,導致使用者開始播放後,會有數秒的黑屏,直到收到下一個IDR幀
他的原話是
nginx-rtmp 是最差的 rtmp 伺服器:
他指出的2我已經驗證過了。即使如此,我覺得nginx-rtmp也有一些可圈可點之處:
- 在直播的過程中,不可避免的還是會用到http服務(認證、HLS、狀態),nginx作為久經考驗的http伺服器,還是值得信賴的。 既然會用上nginx,能all-in-one的話還是不錯的,而且配置也能放在一起,比較方便維護。
- 文件質量很高。nginx-rtmp-module的reference非常 清晰,結構清楚。
@typcn 和 @youngcow 老師相對於 nginx-rtmp-module 都更推薦 srs。感興趣的可以去嘗試。
客戶端
現在我們已經有一個能正常運轉的rtmp伺服器了,接下來需要的是是從本地把桌面的視訊和音訊通過rtmp發到伺服器上。
這是我在Linux下用的命令:
~> ffmpeg -video_size 1920x1080 -framerate 25 -f x11grab -i :0.0+0,0 -f pulse -ac 2 -i default -f flv -codec:v libx264 -preset slow -crf 22 -x264opts keyint=100:min-keyint=20:scenecut=-1 -codec:a aac "rtmp://{{server_ip}}/live/tuna"
-video_size 1920x1080 -framerate 25 -f x11grab -i :0.0+0,0
從我的X伺服器螢幕上抓了1920x1080的視訊。-f pulse -ac 2 -i default
從我的pulseaudio伺服器把音訊抓了出來。理論上從alsa抓也可以,但是我沒有成功。arch下如果要從pulseaudio抓音訊的話,只需要裝pulseaudio這個包,重啟一下機器,然後裝pavucontrol,執行上面的命令之後在pavucontrol的record這個tab下就會看到我們的流,選擇一個合適音效卡就可以了。-codec:v libx264 -preset slow -crt 22 -codec:a aac
編碼成h264和aac。這是nginx-rtmp-module官方支援的編碼。-x264opts keyint=100:min-keyint=20:scenecut=-1
是為了避免 @typcn 提出的問題2,也就是故意在視訊中插入較多的關鍵 幀,減少黑屏的出現,相應的也會增高位元速率。- 最後是rtmp的url,因為我們用了rtmp的預設埠,可以不用寫埠號,live是application的名字,tuna則是channel的名字。
其他作業系統的使用者可以參照這裡。