Theta Sやウェブカメラをラズパイ上のgstreamer+HLSでストリーミング配信してGearVRとかで再生
概要
前回mjpegをmjpegストリーマーで配信する方法を紹介しましたが、少なくともUnity上ではfpsがあまり出ない欠点がありました。
今回は、Raspberry Pi 2でWebCamやTheta S(live view)からの動画を、gstreamerというソフトウェアを使ってHLS(HTTP Live Streaming)方式でストリーミング配信する方法です。
- メリット
- デメリット
- 現状では録画時間と再生時間の間に30~60秒前後の大きなラグが生じる
前回と同じような構成で配信できます。
HLSとは?
HTTP Live Streamingの略で、Appleが定めた、httpだけでストリーミングを行うための仕様です。> HTTP Live Streaming (HLS) - Apple Developer
以下は自分の今の認識ですが、仕様からするとおかしいところがあるかもしれません。
Apacheやnginxといった一般的なウェブサーバーで以下のファイルを配信することで、ストリーミング配信を実現できます。
-
- 中に実際のストリームファイルへのリンクURLが書いてある。
連番のストリームファイル(stream00001.ts, stream00002.tsのような連番ファイル)
- こちらに動画が数秒分ずつ入っている。
gstreamer等を使って、新しい連番ストリームファイルを生成して、ファイルを置くたびに.m3u8ファイルを書き換えていくことで、プレイヤーが最新の情報を取得できるようにしておきます。
これを再生する動画プレイヤー側は、まず.m3u8ファイルを読み込んで、ストリームファイルへのリンクを取得し、さらにリンクをたどってストリームを取得します。m3u8には複数のストリームファイルが書かれているので、順番に読んでいくことができます。定期的にm3u8ファイルを読み込み直すと、更新された情報を得ることができ、さらに新しい連番ストリームファイルを得ることができます。
対応プレイヤー
ブラウザ系 (htmlファイル)
ブラウザ上で再生するためにはvideoタグが書かれたhtmlファイルも一緒においておく必要があります。
直接起動
HLS配信の難しいところ
m3u8をネットから読めるようにウェブサーバーに置くまでは良いですが、.m3u8の中に書かれたストリームファイルのURLもちゃんと読める場所(ホスト、アドレス)を示している必要があります。 たとえばですが、Bonjourが有効な環境で読める なんとか.local といったアドレスを.m3u8 の中のURLに入れてしまうと、Bonjourが無効な環境では再生できなくなってしまいます。 なお、EasyMovieTextureでは、m3u8ファイル内のストリームURIについて、 /stream/stream00001.ts のようにホスト名を省略することが可能でした。他のプレイヤーでは対応していない可能性がありますが、一応仕様的にはアリっぽいです。(※後述「手法A」)
実装
今回の手法
- gstreamerのhlssinkを利用して.m3u8ファイルと.tsファイル群を生成します。
- 重い処理であるh.264へのエンコードに、OpenMaxアクセラレーションを利用するomxh264encを利用します。
- nginxをdockerコンテナで起動し、HTTP配信を担わせます。
前提
以前の記事で書いたような、HypriotOSを入れた状態を想定していますが、通常のRaspbianからでも実行可能と思います。(dockerまわりは自分でなんとかする方向ということで)
gstreamerをインストール
sudo apt-get update sudo apt-get install gstreamer1.0-*
ストリーミング実行
Theta S向け
準備
前回と同様、THETA SをLIVE VIEWモードで起動して、ラズパイにUSB接続してください。そこで、/dev/video0 というデバイスが現れているかどうかを確認してください。
手法A : ストリームファイルをホスト名抜きのパス指定する方法
最後のplaylist= のところがホスト名を除外したパスになっています。AndroidやiOSのVLCではこの方法でいけます。この時点でホスト名とか意識しなくて良い分、楽といえます。仕様上もPlaylist(m3u8)に対して相対指定ができると書いてあるので良さそうです。
mkdir /var/www/stream sudo gst-launch-1.0 -v -e v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720 ! jpegdec ! omxh264enc target-bitrate=2000000 control-rate=variable ! video/x-h264,profile=baseline ! h264parse ! mpegtsmux ! hlssink max-files=8 target-duration=5 location=/var/www/stream/segment%05d.ts playlist-location=/var/www/stream/output.m3u8 playlist-root=/stream/
mkdir /var/www/stream sudo gst-launch-1.0 -v -e v4l2src device=/dev/video0 ! image/jpeg,width=1280,height=720 ! jpegdec ! omxh264enc target-bitrate=2000000 control-rate=variable ! video/x-h264,profile=baseline ! h264parse ! mpegtsmux ! hlssink max-files=8 target-duration=5 location=/var/www/stream/segment%05d.ts playlist-location=/var/www/stream/output.m3u8 playlist-root=http://(ラズパイのホスト名またはIPアドレス)/stream/
解説
# デバイス(USB,OpenMax)の関係でスーパーユーザー(sudo)で実行します。 sudo gst-launch-1.0 -v -e # V4L2というドライバでWebCamのデバイスを読みます。 v4l2src device=/dev/video0 # motion jpegフォーマットの1280x720のものを取り出します ! image/jpeg,width=1280,height=720 # motion jpegをデコードします ! jpegdec # OpenMaxアクセラレーションを利用してH.264形式にエンコードします ! omxh264enc target-bitrate=2000000 control-rate=variable # H.264形式の詳細方式を指定します ! video/x-h264,profile=baseline # H.264を適切な形にしてくれる魔法(え ! h264parse # MPEGの入れ物(TS)につっこみます ! mpegtsmux # HLS配信モジュールでファイルを出力します。5秒ぐらいでファイルを切り分け、8ファイルまで維持します。その他、m3u8ファイルの出力先、tsファイルの出力先、m3u8ファイル内でのtsファイルの位置、などを指定します。 ! hlssink max-files=8 target-duration=5 location=/var/www/stream/segment%05d.ts playlist-location=/var/www/stream/output.m3u8 playlist-root=/stream/
h.264対応USBウェブカメラ向け
Logicool C920のような、元からh.264を出力できるWebCamの場合は、以下のようなコマンドラインで、ラズパイにあまり負荷をかけずに配信することができます。ウェブカメラからh.264フォーマットで入力して、それをそのままmpeg-TSでつつんでHLS配信します。
sudo gst-launch-1.0 -v -e v4l2src device=/dev/video0 ! video/x-h264,width=640,height=480,framerate=15/1 ! h264parse ! mpegtsmux ! hlssink max-files=8 target-duration=5 location=/var/www/stream/segment%05d.ts playlist-location=/var/www/stream/output.m3u8 playlist-root=/stream/
その他のUSBウェブカメラ向け
通常のWebCam向けのコマンドラインです。OpenMaxアクセラレーションを利用してh.264エンコードします。
sudo gst-launch-1.0 -v -e v4l2src device=/dev/video0 ! video/x-raw,width=1280,height=720,framerate=15/1 ! omxh264enc ! h264parse ! mpegtsmux ! hlssink max-files=8 target-duration=5 location=/var/www/stream/segment%05d.ts playlist-location=/var/www/stream/output.m3u8 playlist-root=/stream/
gstreamer動作確認
HLS用のgstreamerが動作しているか、確認する方法です。
ls -al /var/www/stream/
を何回か実行して、以下を確認しましょう。
- .tsファイルのサイズが増えているか?
- しばらくすると.m3u8 ファイルが更新されているか?
- .tsファイルがある程度の容量になったら次の.tsファイルが作られているか?
動きが無かったり、1つのファイルのサイズがいつまでも増え続けていたりしたら異常です。
ウェブサーバー(nginx)設定
上記まででできているのは、「とあるディレクトリにm3u8ファイルと.tsファイルを置く」ということまでです。ですので、これを配信してあげる必要があります。 ここではLAN内での配信とします。
dockerがインストールされた環境を前提とします。
$ docker run -v /var/www:/usr/share/nginx/html/ -p 80:80 -d mhamanaka/rpi-nginx-hls:0.1
実行すると、自動的にイメージのダウンロード、展開からnginxの実行まで自動的に行われます。
docker ps
で稼働状況を確認等してください。
docker logs
とするとnginxの最新のログを得られます。
コマンドラインについて
-v /var/www:/usr/share/nginx/html/
は、ローカルの/var/wwwを、docker container側の/usr/share/nginx/html/
にマウントするという意味です。コンテナのnginxは/usr/share/nginx/html/
にあるファイルを配信する設定になっているため、こう書いておくと、ローカル側の/var/www
の内容を配信することになります。mhamanaka/rpi-nginx-hls:0.1 イメージについて
mhamanaka/rpi-nginx-hls:0.1 というイメージの内容が謎かと思いますが、これは lalyos/rpi-nginx のmime.types (/etc/nginx/mime.types) に、以下のHLS用設定を追加したものです。
application/x-mpegURL m3u8; video/MP2T ts;
ブラウザ再生向けhtmlファイル
ブラウザで再生する場合は、以下のように、videoタグを含んだhtmlファイルを /var/www/viewer.html のような場所に置きます。 そうすると、上記の設定であれば http://(ラズパイのホスト名またはIPアドレス)/viewer.html にアクセスするとHLS再生用ページが開きます。再生ボタンを押すと再生できます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"> <title>HTTP Live Streaming Test</title> </head> <body> <header> <h1>HTTP Live Streaming Test</h1> </header> <div> <video width="1280" height="720" src="/stream/output.m3u8" preload="none" onclick="this.play()" controls /> </div> </body> </html>
再生方法
Unity,VLC(ウェブブラウザ以外)
m3u8へのURLを指定して再生します。
http://(ホスト名またはIPアドレス)/stream/output.m3u8
Unity Easy Movie Texture (有料Asset. $55)の場合
Media Player Controlコンポーネントの、
Str File Name
のところに上記文字列を入れてください。Easy Movie Textureのサンプルシーンでの設定例
実行例
GearVR向け設定例
こちらの記事 のプロジェクトの、Web Cam Drawer のところを Media Player Controlに置き換えます。
実行の様子
-
「ネットワークストリームを開く」から上記アドレスを入力してください。
Windows版の場合
m3u8ファイル内に完全なURLが入る、上記「手法B」が必要のようです。
ウェブブラウザの場合
videoタグを埋め込んだhtmlファイルへのURLを指定して開きます。
http://(ホスト名またはIPアドレス)/viewer.html
ただし、Androidのブラウザ以外は上記設定で成功していません・・・!
Tips
Theta S + gstreamerでのframerate指定がやばい
一般的にTheta Sは15fpsと言われているので、 image/jpeg,width=1280,height=720,framerate=15/1
のような指定をしてしまいがちですが、これは動きません!
正解は15fpsよりわずかに低い、 image/jpeg,width=1280,height=720,framerate=2997/200
です。
ってわかるか!!!
なお、framerateの指定なしであれば、勝手にこの値を選んでくれます。
できなかったこと
omxmjpegdecの利用
- 本来ならMJPEGデコードに、jpegdec ではなく、OpenMaxアクセラレーションの効く omxmjpegdec が使えるはずという気がするのだが、全然動作しなかった。(結果を吐かずにずっと止まってしまう)
音声ストリーミング
- 音声も一緒にmpeg tsに載せられるはずですが、muxまわりの指定方法がよくわかっていないため、できていません。
iOS Mobile SafariやWindows Firefoxでの再生
- 何か設定に問題がある?
gstreamerのDockerコンテナ化
- OpenMax利用部分がどうしても解決しなかった。
omxまわりのバージョン更新 (Hypriotの関係か、v1.0.0がインストールされていた。本来はv1.2.0系があるはず)