ラズパイ4とGStreamerでストリーミングサーバーを作ろう


やること

ラズパイ4(Raspberry Pi 4 メモリ 4GB)にカメラをつけてストリーミング配信の環境を作ります。 ラズパイに付けられるマイクがなかったので、音声なし映像のみです。
※ラズパイ3 (Raspberry Pi 3 model B+でもインストールできることを確認しました)

とりあえずファイルが欲しい場合

ストリーミングサーバをたてず、.m3u8,.tsファイルが欲しい場合は、こちらからダウンロードください。

テスト用にHLSライブストリーミング配信データおいときます

参考

構成図

カメラを付けたラズパイを、GStreamerとNodejsでストリーミングサーバにします。

更に、CORSを設定し別のWEBサーバでもストリーミングデータが利用できるようにします。

環境

  • ラズパイ
    • Raspberry Pi 4 Model B 4GB
$ lsb_release -a
No LSB modules are available.
Distributor ID: Raspbian
Description:    Raspbian GNU/Linux 10 (buster)
Release:        10
Codename:       buster
  • カメラ

参照元

カメラがサポートするフォーマット

$ v4l2-ctl --list-formats-ext
ioctl: VIDIOC_ENUM_FMT
        Type: Video Capture

        [0]: 'YU12' (Planar YUV 4:2:0)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [1]: 'YUYV' (YUYV 4:2:2)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [2]: 'RGB3' (24-bit RGB 8-8-8)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [3]: 'JPEG' (JFIF JPEG, compressed)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [4]: 'H264' (H.264, compressed)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [5]: 'MJPG' (Motion-JPEG, compressed)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [6]: 'YVYU' (YVYU 4:2:2)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [7]: 'VYUY' (VYUY 4:2:2)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [8]: 'UYVY' (UYVY 4:2:2)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [9]: 'NV12' (Y/CbCr 4:2:0)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [10]: 'BGR3' (24-bit BGR 8-8-8)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [11]: 'YV12' (Planar YVU 4:2:0)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [12]: 'NV21' (Y/CrCb 4:2:0)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2
        [13]: 'BGR4' (32-bit BGRA/X 8-8-8-8)
                Size: Stepwise 32x32 - 2592x1944 with step 2/2

このようにラズパイに接続します。(写真はイメージです。基板はラズパイ4ではありません)

GStreamerをインストールしよう

ストリーミング配信に必要なアプリGStreamerをインストールします。以下2つのコマンドを実行すればインストール完了です。(ということがわかるまで苦労しました)

$ sudo apt install autoconf automake libtool
$ sudo apt install gstreamer1.0-tools gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev

GStreamerの動作確認しよう

早速ストリーミングデータを作成してみます。

$ mkdir test
$ cd test
$ 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=./segment%05d.ts \
 playlist-location=output.m3u8 \
 playlist-root=./

実行時はこんな感じです。

実行すると、このように.m3u8,.tsファイルが作成されます。

$ ls
output.m3u8  segment00000.ts  segment00001.ts  segment00002.ts

GStreamerの出力ファイルを確認しよう

.m3u8の中は、.tsファイルとの相対パスが書かれてました。実際の相対パスとずれていないか確認します。(これに気が付かず苦労しました)

$ cat output.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-ALLOW-CACHE:NO
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-TARGETDURATION:9

#EXTINF:8.7807645797729492,
./segment00000.ts
#EXTINF:7.9985880851745605,
./segment00001.ts
#EXTINF:6.8206477165222168,
./segment00002.ts

.tsファイルを確認しよう

.tsファイルをWindowsにコピーしてダブルクリックで実行します。カメラ映像が再生されれば成功です。(.tsファイルは動画データなんですね、へぇー)

Nodejsをインストールしよう

GStreamerでストリーミングデータが作れるようになりました。そのデータをWebブラウザで開くことができるように、NodejsをインストールしてラズパイをWebサーバにします。

# npmを更新
sudo npm install npm@latest -g
# npmのバージョン確認
$ npm -v
# -> 6.13.4

$ sudo apt update
$ sudo apt install curl
$ curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
$ sudo apt install nodejs -y

# nodejsのバージョン確認
$ node -v
# -> v12.14.0

Nodejsがインストールできたので、プロジェクトを作成します。

$ mkdir nodejs
$ cd nodejs
$ npm init
$ npm install --save express
$ touch app.js
$ mkdir wwwroot
$ touch wwwroot/test.html

app.jsファイルを以下のようにします。

app.js
var express = require('express');
var app = express();

app.use(express.static('wwwroot'));

var port = 3000;
app.listen(port,function(){
    console.log("サーバがポート%dで起動しました。モード:%s",port,app.settings.env)
});

test.htmlファイルを以下のようにします。

wwwroot/test.html
<!DOCTYPE html>
<html><head></head><body>
TEST
</body></html>

Webサーバとして動作するか試運転します。

$ pwd
/home/pi/nodejs
 $ node app.js
サーバがポート3000で起動しました。モード:development

ブラウザでhttp://<ラズパイのIPアドレス>:3000/test.htmlを開き、Webサイトが開けば成功です。

ストリーミングサーバにしよう!

ストリーミングデータの作成とWebサーバを用意できました。これら2つを使ってストリーミングサーバを作ります。

具体的には、ストリーミングデータをhtmlの<video> </video>タグで表示できるようにします。そのためには、video.jsというモジュールを使うことでChromeでも表示できるようになります。

必要なファイル(.m3u8, .ts以外)は、githubに置きました。これをwwwroot配下に格納します。
https://github.com/zgw426/GStreamerSample/tree/master/sample01

ストリーミングデータ含め、各ファイルがこのように配置されるようにします。

$ pwd
/home/pi/nodejs/wwwroot
$ tree
.
├── output.m3u8
├── segment00056.ts
├── segment00057.ts
├── segment00058.ts
├── segment00059.ts
├── segment00060.ts
├── segment00061.ts
├── segment00062.ts
├── segment00063.ts
├── static
│   ├── css
│   │   └── video-js.min.css
│   └── js
│       ├── video.min.js
│       ├── videojs-contrib-hls.min.js
│       └── videojs-contrib-media-sources.min.js
└── streamtest.html

ストリーミングサイトを開いてみよう

http://<ラズパイのIPアドレス>:3000/streamtest.htmlを開くとこのような画面が表示されます。

動画を再生して、ラズパイに付けたカメラ映像が動画として表示されれば成功です。

これで、ストリーミングサーバが完成しました。

CORSを有効にしよう

冒頭に紹介した2つ目の構成にします。

別にWebサーバーを用意して、そちらのサイトでストリーミング配信ができるようにします。これには、CORS(オリジン間リソース共有)を有効にする必要があります。

CORSが有効でないと、Access to XMLHttpRequest at 'http://xxx' from origin 'http://yyy' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.というエラーが発生し再生できません。

NodejsにCORSモジュールをインストールしよう

ラズパイのNodejsにCORSを有効にするため、モジュールをインストールします。

$ npm install cors

app.jsを以下のようにします。これで、CORS設定が完了です。

※注意※
この設定では、どのサーバからもリソースが利用できる状態で、セキュリティ的には危険な状態です。本来なら、特定のサーバからのみアクセス許可するなど、許可する範囲を最小にします。

app.js
var express = require('express');
var cors = require('cors');
var app = express();

app.use(cors());
app.use(express.static('wwwroot'));

var port = 3000;
app.listen(port,function(){
        console.log("サーバがポート%dで起動しました。モード:%s",port,app.settings.env)
});

CORSを有効にすると、このように別PCのWebサーバでもストリーミング配信ができるようになります。

stream.html は、streamtest.htmlをコピーして、以下のように編集すると作れます。

[変更前] <source src="./output.m3u8" type="application/x-mpegURL">
[変更後] <source src="http://{ラズパイIP or ホスト名}:{ポート番号}/output.m3u8" type="application/x-mpegURL">

おまけ:遅延テスト

ストリーミング配信にはいくらか遅延があります。配信時間が長くなった場合、遅延がどうなるか検証してみました。16時間連続稼働でもそれほど遅くならず、個人的には満足な値でした。

結果

遅延
開始時 18秒
1.5時間後 42秒
16時間後 27秒