2020年7月13日月曜日

Nginxを使って素の状態のブラウザで視聴可低遅延ライブストリーミングをしてみる(Nginx-http-flv-module)

コロナ禍で!
コロナ禍でライブ配信が注目されてますね。ええ。コロナ禍と言いたかっただけです。
ZOOMのようなビデオチャットや、Youtube Live ニコニコ生放送のような
1対多の生放送アプリまで、実際に集まって行う会議やイベント、
あるいは授業なんかもライブ配信で済ませている。そんな時代が一気に訪れました。
ここではWebRTCなどを使った双方向のビデオチャットではなく、いわゆる映像配信と言われる1対多のストリーミングサービスの話題が中心です。

そんな中、セキュリティの関係もあり自分でライブ配信プラットフォームを作れないかという相談が寄せられた人もいることでしょう。
これまでは、Adobe Media ServerやWowzaを動かしてそこにRTMPで打ち上げ、Flash Playerで視聴といったアプリケーションが一般的でしたが、ライセンス料をはじめ、非常にサーバーのコストがかかりました。
また、映像送信側では現役で使われているRTMPも、現在では視聴環境側のブラウザでは、
RTMPの受信に必須であるFlash Playerのサポートを打ち切られているなどすでに終わった技術になりつつあります。
RTMPで打ち上げるのはいいけどじゃあどうやって見るの?という話になるわけです。

いくつか方法があり、
サーバーでHTTP Live Streaming形式に変換し、HTTPプロトコルでのストリーミング視聴を可能にする
同じくMPEG-DASH形式にしてHTTPプロトコルでのストリーミング視聴を可能にする
主に使われているのはこの2種類です。

どちらも仕組みとしては、配信したい映像を、一定時間に区切ったTSファイルにして、TSファイルの構成を示したプレイリストと一緒にブラウザでダウンロード、プレイリストにしたがって再生、プレイリストを逐次更新してまた新しいTSファイルをダウンロードという形で、HTTP GETを繰り返ししていくことにより映像をつながった状態で見られる仕組みです。

RTMPで打ち上げた映像をそのままHLS形式に変換してくれる機能まで持ったのが、Nginx-RTMP-moduleです。
https://github.com/arut/nginx-rtmp-module
WebサーバーとしてはおなじみのNginxのモジュールで、設定をするとRTMPサーバーとして動作、HLSファイルの生成をONにしてWebサーバーでホストするとTSファイルとM3U8プレイリストを生成して、HLSの配信環境も作ることができます。
これはすでに試している方も多いと思います。
HLSはサポートしているブラウザも多く、スマホでもPCでも安定して視聴できます。
しかし、先に述べた仕組みなので、
TSファイルの長さ分、録画した動画ファイルを生成してからプレイリストに乗せて、ブラウザがダウンロード、再生ということでその分遅延が大きくなります。
このへんは調べるか、実際にやってみるとよく分かると思います。
僕が限界までチューニングした結果3秒くらいの遅延までは縮められましたが、回線が遅くなったりするとすぐ映像が途切れてしまうピーキーな感じになってしまいました。

毎回前置きが長いですが、そこでタイトルのNginx-http-flv-moduleの出番です。
https://github.com/winshining/nginx-http-flv-module
まずはサクッとインストールから。
WSLでUbuntuを使っています。

sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install build-essential libpcre3 libpcre3-dev libssl-dev zlib1g-dev -y

git clone https://github.com/winshining/nginx-http-flv-module.git
wget http://nginx.org/download/nginx-1.19.1.tar.gz
tar -zxvf nginx-1.19.1.tar.gz
cd nginx-1.19.1/
./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf \
--error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock \
--http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp \
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \
--http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module \
--with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module \
--with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module \
--with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream \
--with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-g -O2 \
-fdebug-prefix-map=/data/builder/debuild/nginx-1.19.0/debian/debuild-base/nginx-1.19.0=. -fstack-protector-strong -Wformat -Werror=format-security \
-Wp,-D_FORTIFY_SOURCE=2 -fPIC' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -Wl,--as-needed -pie' \
--add-module=./../nginx-http-flv-module

make
sudo make install

sudo vi /etc/nginx/nginx.conf

#追加
include /etc/nginx/streams-enabled/*;

sudo vi /etc/nginx/sites-available/vod

server {
    listen       80;
    server_name  vod.example.com;
    server_name  localhost;

    location /flv {
  flv_live on;
  chunked_transfer_encoding on;
  add_header 'Access-Control-Allow-Origin' '*';
  add_header 'Access-Control-Allow-Credentials' true;
 }
}

sudo mkdir /etc/nginx/streams-available
sudo mkdir /etc/nginx/streams-enabled

sudo vi /etc/nginx/streams-available/rtmp

rtmp_auto_push on;
rtmp_auto_push_reconnect 1s;
rtmp_socket_dir /tmp;
rtmp {
        out_queue               4096;
        out_cork                8;
        max_streams             8;
        timeout                 15s;
        drop_idle_publisher     15s;

        log_interval            5s;
        log_size                1m;

        server {
                listen 1935;
                server_name rtmp.example.com;
  server_name localhost;

                application rtmp {
                        live on;
                }
        }
}

sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/vod /etc/nginx/sites-enabled/vod
sudo ln -s /etc/nginx/streams-available/rtmp /etc/nginx/streams-enabled/rtmp
sudo /usr/sbin/nginx

こんな感じで設定したらOBSなどでRTMPの配信を受けられます。
そして、プレイヤーの画面を作っていきます。
プレイヤーにはbilibiliのflv.jsを使います。
から、flv.min.jsをダウンロードしてwwwrootに置きます。
同じくプレイヤーのHTMLを作ります。

sudo vi /var/www/html/player.html

<script src="flv.js"></script>
<video id="videoElement" muted autoplay controls></video>
<script>
    if (flvjs.isSupported()) {
        var videoElement = document.getElementById('videoElement');
        var flvPlayer = flvjs.createPlayer({
            type: 'flv',
            url: '/flv?port=1935&app=ingest&stream=stream01'
        });
        flvPlayer.attachMediaElement(videoElement);
        flvPlayer.load();
        flvPlayer.play();
    }
</script>

これでhttp://localhost/player.htmlを見ると、
rtmp://localhost/rtmp/stream01宛にRTMPで送った映像が見られます。

仕組みとしては、 /etc/nginx/sites-available/vodに設定を書きましたが、
受け取ったRTMPストリームを、80番ポートで再配信しています。
これにより、RTMPストリーム(FLV)を直接HTTP GETできるようになっています。
Flash playerがないとRTMPを再生できないのは、ブラウザがrtmp://プロトコルをサポートしていないのが主な要因ですが、HTTPプロトコルでストリームが受信できるとなると話は別です。
flv.jsは、RTMP over HTTPのストリームをデコードしてHTML5プレイヤーで再生できます。
視聴中の通信イメージとしては、ずっと配信が続く限り無限に終わらない動画のダウンロードをしている感じです。
実際に試してみると、VPSサーバーに作った配信サーバーでも2秒くらいの遅延で配信ができました。




チャット付きのライブ配信や監視系など、遅延が大きすぎると問題なアプリケーションではとても威力を発揮しそうです。

今更ながらひだまりスケッチを最新巻まで一気読みした感想

Amazonが芳文社セールでKindleの芳文社単行本の一部が77円になっています。
https://amzn.to/2OeWBNvこの機会に、アニメを見てそれなりに好きだったひだまりスケッチを全巻買って、
週末一気に読んでいました。
最新巻だけ通常価格でしたが、一気読みして続きがあるのに読めないのは生殺し感があってそれもホイホイ買ってしまいました。思うつぼです。

ひだまりスケッチのこれまでのイメージ(アニメ1期を全部見ただけ)では、
ほぼほのぼの系のストーリーの中、天然だけどすごい頑張り屋さんのゆのっちがかわいくて応援したくなる感じだなぁくらいに思っていました。
実際1期分のストーリーではそんな感じなのですが、続きを読んで感想が変わりました。

内気で臆病なゆのっちは新しいことをするたびに不安がり、将来の目標もなかなか決まらず深く悩んでしまいます。そんなとき宮ちゃんはいつもコミカルに茶化しながら、さりげなくフォローしています。
これが読み進めていくごとにわかってきて、明るく破天荒ながら優しい宮ちゃんの魅力に引き込まれます。
卒業した沙英先輩の部屋へ行った宮ちゃん。いつも4人で集まりにぎやかだった部屋。
空っぽの部屋で一人、、
このシーンで、人に見せていないながらもやっぱり宮ちゃんはいろいろ考えていたんだろうなぁと確信しました。

他にも、いつもお金がなくお腹を空かせている苦学生の宮ちゃん。これ以上親を頼って大学進学していいのかという葛藤を抱えながら、実家に電話するシーンまで他の人には深く悩んでいる様子を見せませんでした。

10巻まで高校生活の2年半、局面ごとにはゆのっちと同じかそれ以上悩みや葛藤があったはずです。
もっと弱いところを見せてもいいのになと思いますが、もっとずっと弱いゆのっちを、持ち前の明るさで元気付けることで自分も元気をもらっていたのかなと思います。完璧なお姉さんです。

アニメを見た方もぜひ宮ちゃんに注目して単行本も読んでみてください。