1. 程式人生 > >用nginx-rtmp-module直播寫程式碼

用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的一些問題:

  1. 沒有修 pts,只是簡單複製,相容性差
  2. 沒有GOP重傳,使用者加入之後拿到的第一幀不是關鍵幀,導致使用者開始播放後,會有數秒的黑屏,直到收到下一個IDR幀

他的原話是

nginx-rtmp 是最差的 rtmp 伺服器:

他指出的2我已經驗證過了。即使如此,我覺得nginx-rtmp也有一些可圈可點之處:

  1. 在直播的過程中,不可避免的還是會用到http服務(認證、HLS、狀態),nginx作為久經考驗的http伺服器,還是值得信賴的。 既然會用上nginx,能all-in-one的話還是不錯的,而且配置也能放在一起,比較方便維護。
  2. 文件質量很高。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"
  1. -video_size 1920x1080 -framerate 25 -f x11grab -i :0.0+0,0從我的X伺服器螢幕上抓了1920x1080的視訊。
  2. -f pulse -ac 2 -i default從我的pulseaudio伺服器把音訊抓了出來。理論上從alsa抓也可以,但是我沒有成功。arch下如果要從pulseaudio抓音訊的話,只需要裝pulseaudio這個包,重啟一下機器,然後裝pavucontrol,執行上面的命令之後在pavucontrol的record這個tab下就會看到我們的流,選擇一個合適音效卡就可以了。
  3. -codec:v libx264 -preset slow -crt 22 -codec:a aac編碼成h264和aac。這是nginx-rtmp-module官方支援的編碼。
  4. -x264opts keyint=100:min-keyint=20:scenecut=-1是為了避免 @typcn 提出的問題2,也就是故意在視訊中插入較多的關鍵 幀,減少黑屏的出現,相應的也會增高位元速率。
  5. 最後是rtmp的url,因為我們用了rtmp的預設埠,可以不用寫埠號,live是application的名字,tuna則是channel的名字。

其他作業系統的使用者可以參照這裡