Nginxとffmpegで映像プレイヤーの検証環境を構築(RTMP、HLS)
背景
FlowPlayerを仕事で使うことになり、デザインの変更やFlash Failbackの検証などを行うための軽量な環境が欲しかった。
やりたいこと
- 映像ソースを送る側と、それを配信するサーバーをローカル環境上に立ち上げたい(LIVE配信で!!)
- 同一LANの他の端末から接続できるようにしたい
- RTMPとHLSどちらも検証したい
- コマンドひとつで環境を立ち上げたい!
構成
そこで以下のような構成を作ることにしました。
コンテナ1の作成(Web、配信サーバ)
要件
- Webサーバの機能を持つこと
- RTMPサーバの機能を持つこと
- HLSのセグメント化もできること
設定ファイル
nginxのRTMPモジュールを使えば、nginxだけで上記の機能のすべてを賄えるとのこと。
インストール方法はこちらを参照。
これをベースにDockerfileを作成します。
FROM centos:latest RUN yum update -y && \ yum -y install gcc pcre-devel openssl openssl-devel wget unzip tar git make && \ useradd --shell /sbin/nologin nginx && \ wget http://nginx.org/download/nginx-1.10.2.tar.gz && \ tar -zxvf nginx-1.10.2.tar.gz && \ wget https://github.com/arut/nginx-rtmp-module/archive/master.zip && \ unzip master && \ cd nginx-1.10.2 && \ ./configure \ --user=www-data \ --group=www-data \ --with-http_ssl_module \ --with-http_realip_module \ --add-module=../nginx-rtmp-module-master && \ make && \ make install COPY ./nginx.conf /usr/local/nginx/conf/nginx.conf WORKDIR /usr/local/nginx/html EXPOSE 80 1935 ENTRYPOINT ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;", "-c", "/usr/local/nginx/conf/nginx.conf"]
RTMPサーバの設定については、以下の記述をnginx.conf
に追記すればOKのようです。
参考:nginx-rtmp-module/wiki
rtmp { server { listen 1935; application live { live on; wait_video on; exec ffmpeg -i rtmp://localhost/live/$name -c:a libfdk_aac -b:a 32k -c:v libx264 -b:v 128K -f flv rtmp://localhost/hls/$name_low -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 256k -f flv rtmp://localhost/hls/$name_mid -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv rtmp://localhost/hls/$name_hi; } application hls { live on; hls on; hls_path /usr/local/nginx/html/hls; hls_nested on; hls_fragment 5s; hls_variant _low BANDWIDTH=160000; hls_variant _mid BANDWIDTH=320000; hls_variant _hi BANDWIDTH=640000; } } }
まずrtmp://{IPアドレス}/live/{ストリーム名}
にストリームを送ると、
それを受け取ったあと、
exec ffmpeg -i rtmp://localhost/live/$name -c:a libfdk_aac -b:a 32k -c:v libx264 -b:v 128K -f flv rtmp://localhost/hls/$name_low -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 256k -f flv rtmp://localhost/hls/$name_mid -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv rtmp://localhost/hls/$name_hi;
上記が実行されrtmp://localhost/hls/$name_low
、rtmp://localhost/hls/$name_mid
、rtmp://localhost/hls/$name_hi
の3ストリームに更に流されるという仕組みのようです。
そして、下記の部分でHLSについての設定が記述してあるので、ここでセグメント化がされるという流れみたいです。
application hls { live on; hls on; hls_path /usr/local/nginx/html/hls; hls_nested on; hls_fragment 5s; hls_variant _low BANDWIDTH=160000; hls_variant _mid BANDWIDTH=320000; hls_variant _hi BANDWIDTH=640000; }
しかし、自分の環境ではこのHLS化の部分がうまく動作してくれなかったので、nginxでストリームを受け取ってからHLSに流すのではなく、最初から映像ソースを送信するffmpeg側でRTMP+HLS×3の4ストリーム流すことで対応しました。
最終的に、nginx.conf
は以下のようになりました。
user nginx nginx; worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root /usr/local/nginx/html; index index.html index.htm; expires -1; sendfile off; } location /hls { types { application/vnd.apple.mpegurl m3u8; } root /usr/local/nginx/html; add_header Cache-Control no-cache; # To avoid issues with cross-domain HTTP requests (e.g. during development) add_header Access-Control-Allow-Origin *; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } include servers/*; } rtmp { server { listen 1935; application live { live on; wait_video on; } application hls { live on; hls on; hls_path /usr/local/nginx/html/hls; hls_nested on; hls_fragment 5s; hls_variant _low BANDWIDTH=160000; hls_variant _mid BANDWIDTH=320000; hls_variant _hi BANDWIDTH=640000; } } }
参考
コンテナ2の作成(映像ソース)
要件
このあたり↓を参考に設定させていただきました。
テスト用の自動配信環境をDockerとFFmpegで構築してみる
というかDockerfile
に関しては全く同じです(汗)。
FROM jrottenberg/ffmpeg:3.2-centos COPY source.mp4 ./ COPY start.sh ./ COPY ./ffserver.conf /etc/ffserver.conf EXPOSE 80 1935 ENTRYPOINT bash start.sh
ただし、前述のようにffmpeg側で4ストリーム流す必要があったので、実行するシェルスクリプトを以下のように書き換えました。
#!/bin/bash ffmpeg -re -stream_loop -1 -i 'source.mp4' -acodec libfdk_aac -vcodec libx264 -f flv "${TARGET_RTMP_IP}/live/{設定したストリーム名}" \ -c:a libfdk_aac -b:a 32k -c:v libx264 -b:v 128K -f flv "${TARGET_RTMP_IP}/hls/{設定したストリーム名}_low" \ -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 256k -f flv "${TARGET_RTMP_IP}/hls/{設定したストリーム名}_mid" \ -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv "${TARGET_RTMP_IP}/hls/{設定したストリーム名}_hi"
この状態でDockerコンテナを立ち上げれば映像ソースを以下の4つに対して延々と配信してくれるはずです。
${TARGET_RTMP_IP}/live/{設定したストリーム名} ${TARGET_RTMP_IP}/hls/{設定したストリーム名}_low ${TARGET_RTMP_IP}/hls/{設定したストリーム名}_mid ${TARGET_RTMP_IP}/hls/{設定したストリーム名}_hi
docker-composeでの立ち上げ
ffmpeg(コンテナ2)からnginx(コンテナ1)に映像ストリームを送る際にはコンテナ間通信となるため、コンテナをリンクさせる必要がある(??Dockerまだあまり詳しくないので多分です。。。)ので、docker-composeを使ってコンテナをリンクさせて立ち上げるようにします。
結果的に以下のようなdocker-compose.yml
となりました。
version: '2' services: nginx: build: context: ./ dockerfile: ./Dockerfile_nginx container_name: nginx ports: - "80:80" - "1935:1935" volumes: - ./:/usr/local/nginx/html ffmpeg: build: context: ./ dockerfile: ./Dockerfile_ffmpeg container_name: ffmpeg environment: - TARGET_RTMP_IP=rtmp://{Dockerが動作するゲストOSのIPアドレス}:1935 links: - nginx
TARGET_RTMP_IP
として映像ソースの送信先を環境変数として渡しています。
コンテナ2で記述したシェルスクリプトがこの宛先に向けて映像を送ります。
{Dockerが動作するゲストOSのIPアドレス}
については、ゲストOSのIPアドレスなので、次項で設定するVagrant上のCentOSのIPアドレスを記載します。
参考
ゲストOSの立ち上げ
要件
- LAN内の外部端末から接続できるような基盤とすること
- 立ち上げる際にDockerも自動で実行できること
ここまでで、メインの機能となるDockerコンテナは作成できました。 そしていよいよそのDockerの土台となるゲストOSを立ち上げてきます。
Dockerのコンテナに別の端末から接続する際には、そのDockerが動作しているOSのIPアドレスを指定すれば可能なようです。 Docker の基本学習 ~ コンテナ間のリンク
また、Vagrantで立ち上げる仮想OSも以下の記述を加えるだけで簡単に外部からのアクセスが可能になるようです。
config.vm.network "public_network", :ip => "{IPアドレス}" , :bridge => "en0: Wi-Fi (AirPort)"
[Vagrant] LAN内の別マシンから仮想マシンにアクセスする
よって、Vagrantで立ち上げるOSに上記の設定を加えれば、その上で動作しているDockerコンテナのサーバにも外部から接続ができるようになるということです。
更に、Vagrantで仮想OSを立ち上げる際に、同時にDocker、docker-composeも実行したいです。
dockerをVagrantのprovisionerとして使う方法は公式に記述してあります。 DOCKER PROVISIONER
また、docker-composeをprovisionerとして使うためのプラグインもあり、
$ vagrant plugin install vagrant-docker-compose
でインストールできるようです。 leighmcculloch/vagrant-docker-compose
設定
結果的に以下のようなVagrantfileになりました。
Vagrant.configure("2") do |config| config.vm.box = "bento/centos-7.2" # LAN内部に公開するため、public_networkを選択 # IPアドレスはホストOSと同じセグメントのIPアドレスでないと接続できない config.vm.network "public_network", :ip => "192.168.0.100" , :bridge => "en0: Wi-Fi (AirPort)" config.vm.synced_folder "./", "/vagrant", type: "virtualbox" # provisionerとしてdockerとdocker-composeが使えるようにする # 起動のたびにコンテナを起動 config.vm.provision :docker config.vm.provision :docker_compose, yml: "/vagrant/docker-compose.yml", run: "always" end
参考
- Docker の基本学習 ~ コンテナ間のリンク
- [Vagrant] LAN内の別マシンから仮想マシンにアクセスする
- DOCKER PROVISIONER
- leighmcculloch/vagrant-docker-compose
プレイヤーを作る
今回はFlowPlayerの検証のために環境を作ることが目的だったので、FlowPlayerのセットアップ方法に従いindexhtml
を作成しました。
まずは、flowplayerのライブラリを読み込みます。
<head> <!-- flow player --> <link rel="stylesheet" href="//releases.flowplayer.org/6.0.5/skin/minimalist.css"> <script src="//code.jquery.com/jquery-1.12.4.min.js"></script> <script src="//releases.flowplayer.org/6.0.5/flowplayer.min.js"></script>
今回はFlowPlayer上でHLSも使いたいので、hls.jsも読み込みます。
<!-- load the Flowplayer hlsjs engine, including hls.js --> <script src="//releases.flowplayer.org/hlsjs/flowplayer.hlsjs.min.js"></script> <!-- optional: the quality selector plugin for quality selection when hlsjs is not supported --> <script src="//releases.flowplayer.org/quality-selector/flowplayer.quality-selector.min.js"></script> </head>
プレイヤーを作成するには、<div>
要素に対して、jsでソースを指定します。
RTMPは下記のように指定。
var rtmp_player_flv = document.getElementById("rtmp_player_flv"); flowplayer(rtmp_player_flv, { autoplay : true, clip: { sources: [ { type: "video/flash", rtmpt : false, src: "rtmp://{配信サーバのIPアドレス}:1935/live/mdst" }, ] } });
HLSは下記のように指定。
var hls_player = document.getElementById("hls_player"); flowplayer(hls_player,{ autoplay : true, live : true, clip: { sources: [ { type: "application/x-mpegurl", src: "http://{配信サーバのIPアドレス}/hls/mdst.m3u8" }, ] }, });
最終的にindex.html
は以下のような形になりました。
<!DOCTYPE html> <html lang="en" class=""> <head> <!-- flow player --> <link rel="stylesheet" href="//releases.flowplayer.org/6.0.5/skin/minimalist.css"> <script src="//code.jquery.com/jquery-1.12.4.min.js"></script> <script src="//releases.flowplayer.org/6.0.5/flowplayer.min.js"></script> <!-- load the Flowplayer hlsjs engine, including hls.js --> <script src="//releases.flowplayer.org/hlsjs/flowplayer.hlsjs.min.js"></script> <!-- optional: the quality selector plugin for quality selection when hlsjs is not supported --> <script src="//releases.flowplayer.org/quality-selector/flowplayer.quality-selector.min.js"></script> </head> <body> <div> <h2>RTMP</h2> <div id="rtmp_player_flv"></div> </div> <div> <h2>HTTP Live Streaming</h2> <div id="hls_player"></div> </div> <p id="vinfo" class="info"> </p> <script type="text/javascript"> flowplayer(function (api) { api.on("load", function (e, api, video) { $("#vinfo").text(api.engine.engineName + " engine playing " + video.type); }); }); var rtmp_player_flv = document.getElementById("rtmp_player_flv"); flowplayer(rtmp_player_flv, { autoplay : true, clip: { sources: [ { type: "video/flash", rtmpt : false, src: "rtmp://{配信サーバのIPアドレス}:1935/live/mdst" }, ] } }); var hls_player = document.getElementById("hls_player"); flowplayer(hls_player,{ autoplay : true, live : true, clip: { sources: [ { type: "application/x-mpegurl", src: "http://{配信サーバのIPアドレス}/hls/mdst.m3u8" }, ] }, }); </script> </body> </html>
起動
起動する際は、Vagrantfile
があるディレクトリで下記のコマンドを実行します。
$ vagrant plugin install vagrant-docker-compose $ vagrant up
まとめ
これにて、コマンド一つ(実際には2つ)で動画配信と視聴の環境を構築することができました。
あとはindex.html
を修正することで様々なプレイヤーの検証を行うことができます。