2016/03/14

おきつね鯖に・・・

・・・レイアウトした RTMP鯖としての nginxの状態を参考までに公開。


■ 尚、このTipsは、
サイトの公開準備(ドメイン確保、DDNS設定など)が済んでいる必要はある点に注意。
また、LAN内で DNSサーバ ないし、簡易DNS機能憑きのルータ使用していないと 面倒事が多いが、その点には触れていない。

あくまで "Webサイトくらいは公開出来ている" コトが このログの内容を活用する為の最低条件となる。
ツマり、誰にでも判るようなログにはしていない、察しが悪い向きには この内容では解説になっていないだろう。
■ このログの設定サンプルは、
使用しているLANボードのプロパティで、ネットワークアクセスで常用するメインIPアドレスの他に、
RTMP鯖として配信で用いるIPアドレス 192.168.xa.xb を追加設定してあるコトが前提となっている。
xa は メインアドレスと同じに、xb は 未使用の数字を指定する。

また、3つ配信を処理する状態を例としているが、必要の無いユーザーフォルダと application設定は削除していい。
■ フォルダ構成
配布されている nginx_1.7.4_rtmp_1.1.4 のフォルダレイアウ トでは、WindowsServerで稼動している おきつね鯖の IISとの 連動に不便である為、usersフォルダを追加し、liveフォルダ を移動、配信ユーザー名に変更してある。
nginxで Webサーバを構成してある場合は サイトフ
ォルダとして usersフォルダが使用されているケース
もあるので、利用環境によっては この構成も一考が
必要になる。

ただ、Windowsであれば WindowsServerでなく
ても、IIS7.x以降を導入でき、Webサイトを立ち上げ
るコトは出来る。 因ってnginxをWebサーバとして
構成する必要はナイだろう。
また、usersフォルダ直下のフォルダ名が配信URLとなる。 ソレを踏まえた上で このフォルダレイアウトを基準に 以下サンプルソース群を参照して欲しい。
ユーザーフォルダ MainUser ExternalUserA ExternalUserB は、任意に名称を変更し、配信のURLとして用いる。
■ nginx.conf
confフォルダ配下に格納されている nginxの基本的な動作を定義する設定が収まるファイル。

以下サンプル中の MainUser ExternalUserA ExternalUserB は、それぞれのユーザーフォルダ名であると同時に、
配信URLとして扱われる。 因って それらを、任意に変更したユーザーフォルダ名に置換するだけで使用が可能となる。
#user nobody; worker_processes 4; error_log logs/error.log crit; events { worker_connections 1024; } http { access_log off; # MIME Settings include mime.types; types { application/x-mpegURL m3u8; video/MP2T ts; } default_type application/octet-stream; sendfile on; keepalive_timeout 65; # WebServerSettings #server { # listen 80; # server_name localhost; # # location / { # root html; # index index.html index.htm; # } #} # RTMPServerSettings server { server_name live.okitsunesama.com; listen 8888; # Root location / { root users; index index.html index.htm; } # RTMP statistics in XML (stat.xsl) Settings location /stat.xsl { root users; } location /stat { allow 127.0.0.1; allow 192.168.0.0/16; allow 172.16.0.0/12; allow 10.0.0.0/8; deny all; rtmp_stat all; rtmp_stat_stylesheet stat.xsl; } # rtmp control location /control { allow 127.0.0.1; allow 192.168.0.0/16; allow 172.16.0.0/12; allow 10.0.0.0/8; deny all; rtmp_control all; } } } rtmp { server { listen 1935; access_log logs/rtmp_access.log; ping 30s; ping_timeout 10s; #chunk_size 8192; #max_streams 10; drop_idle_publisher 15s; # MainUser Settings ▼▼▼▼▼ application MainUser { live on; wait_video on; #wait_key on; meta copy; #idle_streams off; #publish_notify on; allow publish 192.168.xa.xb; #allow publish 127.0.0.1; #allow publish 192.168.0.0/16; #allow publish 172.16.0.0/12; #allow publish 10.0.0.0/8; #deny publish all; hls on; hls_path users/MainUser/ts; hls_continuous on; hls_playlist_length 18s; } # ▲▲▲▲▲ # ExternalUserA Settings ▼▼▼▼▼ application ExternalUserA { live on; wait_video on; #wait_key on; meta copy; #idle_streams off; #publish_notify on; allow publish 192.168.xa.xb; #allow publish 127.0.0.1; #allow publish 192.168.0.0/16; #allow publish 172.16.0.0/12; #allow publish 10.0.0.0/8; #deny publish all; hls on; hls_path users/ExternalUserA/ts; hls_continuous on; hls_playlist_length 18s; } # ▲▲▲▲▲ # ExternalUserB Settings ▼▼▼▼▼ application ExternalUserB { live on; wait_video on; #wait_key on; meta copy; #idle_streams off; #publish_notify on; allow publish 192.168.xa.xb; #allow publish 127.0.0.1; #allow publish 192.168.0.0/16; #allow publish 172.16.0.0/12; #allow publish 10.0.0.0/8; #deny publish all; hls on; hls_path users/ExternalUserB/ts; hls_continuous on; hls_playlist_length 18s; } # ▲▲▲▲▲ }
■ utils.js
・・・各ユーザーフォルダに納まっているファイル。

MainUser と なっている箇所を 任意に変更したユーザーフォルダ名に置換するだけで使用可能。
function GetFlashVersion(){var m,f,o;try{o=navigator.plugins["Shockwave Flash"];if(o[0].enabledPlugin!=null){f=o.description.slice(16)}}catch(p){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7");f=m&&m.GetVariable("$version")}catch(n){try{m=new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6");f=m&&m.GetVariable("$version")}catch(l){}}}f=/(\d+)[^\d]+(\d+)[^\d]*(\d*)/.exec(f);return f?[1*f[1],1*f[(f[1]*1>9?2:3)]*1]:[0,0]} if (GetFlashVersion()[0] == 0 && !navigator.platform.match("Win")) { location.href = "./index_m.html"; } var arg = (location.search.substring(1) || "").toLowerCase(); function SetDisplay(element, value) { document.getElementById(element).style.display = value; } function SetOpacity(element, value) { element.style.opacity = value; } function SetOpacityIE(element, value) { element.style.filter = "alpha(opacity=" + (value * 100) + ")"; } function OnHlsEnabled(text) { if (navigator.platform.match("Win")) { //SetDisplay("linkMobile", "none"); SetDisplay("linkQR", "inline"); } else { SetDisplay("linkMobile", "inline"); //SetDisplay("linkQR", "none"); } } function OnHlsDisabled(text) { SetDisplay("linkMobile", "none"); SetDisplay("linkQR", "none"); } function CheckHLS() { try { var xhr = new XMLHttpRequest(); } catch (e) { return false; } xhr.open("GET", "./ts/MainUser.m3u8", true); xhr.setRequestHeader("Cache-Control", "no-cache"); xhr.setRequestHeader("If-Modified-Since", "Thu, 01 Jun 1970 00:00:00 GMT"); xhr.onreadystatechange = function() { if (this.readyState === 4 && this.status === 200) { OnHlsEnabled(this.responseText); } else if (this.readyState === 4 && this.status !== 200) { OnHlsDisabled(); } } xhr.send(null); } function Body_OnLoad(self) { if (arg == "window") { SetDisplay("bottom", "none"); } else { if (arg == "small") { SetDisplay("linkSmall", "none"); Bottom_SetTimer(); } else { SetDisplay("linkNormal", "none"); Bottom_SetTimer(30); } } if (typeof document.getElementById("bottom").style.opacity != "string") { SetOpacity = SetOpacityIE; } CheckHLS(); } function Bottom_OnMouseover(self) { SetOpacity(document.getElementById("bottom"), 1.0); Bottom_SetTimer(); } function QR_OnClick(self) { self.style.display = "none"; } var timerBottom = 0; function Bottom_Hide() { if (timerBottom != 0) { clearTimeout(timerBottom); } timerBottom = 0; Bottom_Fadeout(); } function Bottom_SetTimer(duration) { if (timerBottom != 0) { clearTimeout(timerBottom) } timerBottom = setTimeout(Bottom_Hide, 1000 * (duration || 10)); } function Bottom_Fadeout() { var alphaBottom = 1; var timerFade = setInterval(function(){ if (timerBottom == 0) { alphaBottom -= 0.09; if (alphaBottom < 0.15) { alphaBottom = 0.15; clearInterval(timerFade); } SetOpacity(document.getElementById("bottom"), alphaBottom); } else { clearInterval(timerFade); } }, 100); } function ShowQRCODE() { var container = document.getElementById("QR"); container.style.display = (container.style.display == "none") ? "block" : "none"; var uri = location.href.match(/(.*\/).*$/)[1]; container.innerHTML = '<img src="http://chart.apis.google.com/chart?\chs=150x150&cht=qr&chl=' + escape(uri) + '"/> ' + uri; } function SetupPlayer() { if (arg) { var w, h; if (arg == "window") { w = "100%"; h = "100%"; with (document.getElementsByTagName("body")[0].style) { margin = 0; padding = 0; } document.getElementById("container").style.height = h; } else if (arg == "small") { w = "640px"; h = "360px"; } else { w = "864px"; h = "486px"; } document.getElementById("container").style.width = w; document.getElementById("player").style.height = h; } flowplayer("player", "./flowplayer/flowplayer-3.2.18.swf", { plugins: { influxis: { url: "./flowplayer/flowplayer.rtmp-3.2.13.swf", netConnectionUrl: 'rtmp://' + location.hostname + '/MainUser/' } }, clip: { url: 'MainUser', live: true, autoPlay: true, scaling: 'fit', provider: 'influxis', onMetaData: function() { setTimeout(CheckHLS, 20 * 1000); }, onMetaDataChange: function() { setTimeout(CheckHLS, 20 * 1000); } } }); }
このサンプルは 参考構成中の users\MainUser にレイアウトする前提として記述されている。
■ index.html
・・・各ユーザーフォルダに納まっている 各配信のWebページを処理するHTMLファイル。
上述 nginx.conf内 [rtmp]-[server] 各[application] で hls を無効にしている場合は機能しない。
因みに、見た目の変更が必要ナイ場合は、配布されたままの状態で使用出来るファイルである。
<html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="default.css" type="text/css"> <!-- link rel="stylesheet" type="text/css" href="任意のCSSファイルが格納されているURL"--> <title>ページタイトル</title> <script src="./utils.js"></script> <script src="./flowplayer/flowplayer-3.2.13.min.js"></script> </head> <body class="CatalogView" onLoad=Body_OnLoad(this)> <div id="container"> <div id="player" class="iExp" style="background-color:#5588cc;color:#eeeeee;max-width:1500px !important;"></div> <script type="text/javascript">SetupPlayer();</script> </div> <p id="bottom" onMouseover=Bottom_OnMouseover(this)><a id="linkSmall" href="?small">小</a><a id="linkNormal" href="./">中</a><a href="?window">窓</a><a id="linkMobile" href="./index_m.html">モバイル</a><a id="linkQR" href="javascript:ShowQRCODE();">QRコード</a></p> <div id="QR" style="display: none;" onClick=QR_OnClick(this)></div> </body> </html>
このサンプルでは配布版に対し、bobyへクラスを割り当て、外部CSSへのアクセスを行う行を準備している。
■ その他重要なポイント
・上記3つ以外のファイルは 配布されているまま 変更する事無く利用出来る。
・nginxフォルダ内の構成は、稼動する上で重要な要素であり、理解していないうちは変更を推奨しない。
・hlsを有効にし、配信ページも機能させ、その装飾を任意に構成したい場合は 各ユーザーフォルダ配下の default.css へ定義を列挙する。

・IISへは、取得したドメインをバインドさせるよう新規Webサイトを追加し、ソコへ仮想フォルダとして nginx配下の各ユーザーフォルダを設置する。
■ ひまストで配信するだけなら ココまでで設定は完了となるのだが・・・
・・・他の配信サイトへも同時配信したい場合は、その分 nginx.confへ 追記事項が増える。
ただ、ひまストと異なり、外部配信サーバからの配信を受け付ける機能の無いサービスが殆どだ。
その場合、nginxを用いたマルチキャストを実行するには、配信データを対象サービスへpushしてやる必要がある。

要は、ひまスト鯖とOBSを直接接続する配信と同じ状態を、他の配信サービスへ 平行して実行するよう
nginxに指定するコトになると思えば判り易いだろう。
■ 具体的には、nginx.conf内 [rtmp]-[server] 各[application] 配下に
#Twitch.tv (Asia: Tokyo, Japan) push rtmp://live-tyo.twitch.tv/app/{Twitchから配布されたユーザーキー};
・・の様な形式で追記する。 その他のライブ配信サービスを対象にする場合の書式は、コチラを参考にすると良いだろう。 ■ 上述のTwitchを例に抜粋すると、コンなカンジ ▼
application MainUser { live on; wait_video on; #wait_key on; meta copy; #idle_streams off; #publish_notify on; allow publish 192.168.xa.xb; #allow publish 127.0.0.1; #allow publish 192.168.0.0/16; #allow publish 172.16.0.0/12; #allow publish 10.0.0.0/8; #deny publish all; hls on; hls_path users/MainUser/ts; hls_continuous on; hls_playlist_length 18s; #Twitch.tv (Asia: Tokyo, Japan) push rtmp://live-tyo.twitch.tv/app/{witchから配布されたユーザーキー}; }
複数対象を追記し、多数のサービスへ同時配信させるコトも可能。 但し いずれの場合も、回線のトラフィックは増大する、利用しているISPの規約や性能を考慮すべきだ。