【Jetson Nano】Jetson Nanoでネットワークカメラをつくる(Gstreamer + HLS Streaming)

【Jetson Nano】Jetson Nanoでネットワークカメラをつくる(Gstreamer + HLS Streaming)

せっかくのカメラのついたエッジAIデバイスなのでネットワークカメラを作ってみることにした。Jetson Nanoを題材にしているが、今回の記事の時点ではまだJetson特有の機能を使用していないので、Raspberry Piでも同じような手順でできるはず。

Overview

構築するストリーミングシステムの概要図を示す。

CSIカメラ(Raspberry Pi camera v2)からの映像をGstreamerパイプラインでエンコードし、ストリームデータとして出力。
コンソールからコマンド一発でここまで実現できる。最高!

ウェブサーバーとしてJetsonへlighttpdを実装する。lighttpdは軽量・高速を謳うウェブサーバーOSS。apacheでも良いのだろうけど、実装する機能は限られているし、組み込みで容量を抑えたいこともあってlighttpdを採用した。

ストリーミングのプロトコルにはHLSを使用する。httpを使用して難しいセットアップもなしに実装できる。 詳しい仕組みについては後述。

なお、本記事では192.168.0.8をJetsonのIPアドレスとする。

HLS(HTTP Live Streaming)

HLSとは

HLS(HTTP Live Streaming)はメディアをストリーミングするために定められたプロトコル。Appleによって実装されている。

カメラのストリーミング映像や画像データをインデックスファイル(.m3u8ファイル)とts file(.tsファイル)に分割する。ユーザーは普通にホームページを見るのと同様にこのデータをダウンロードし、ブラウザで動画として表示する。

インデックスファイルとセグメントファイル

実はこれは昔のmp3プレイヤーから使われている技術。自分の好きな曲を選択して「プレイリスト」をつくるとm3uファイルとして保存される。m3uファイルは選択されたmp3ファイルの場所と再生順序を記録している。プレイヤーはこのm3uファイルを参照して、プレイリストの通りに曲を再生する という仕組み。

HLSはこれを動画に当てはめて考えるだけ。

ストリーミングでは、セグメントファイルが動的に生成され、同時に古いファイルが破棄される。インデックスファイルの中身はセグメントファイルの順番を示していて、セグメントファイルが生成/破棄される度に内容が更新される。

これによって、例えば巨大な動画ファイルを動的にセグメントファイルに分割して配信する なんてこともできる。(言ってみれば、カメラのライブ映像って容量無限の動画ファイルみたいなもんだ)

ちなみにmp4などの動画ファイルの場合はffmpegというツールをつかってセグメントファイルに分割する。

lighttpdのセットアップ

ligthttpdをインストール

下記のコマンドでインストールする。

次に下記のコマンドで起動する。簡単!

と思いきや、そのままサービススタートしようとすると以下のようなエラーになる。

言われている通り、systemctl statusでステータスを確認してみる。

状態はfailedとなっていて、サービスの立ち上げに失敗する。
これはJetsonの問題というよりubuntuの問題な気がするので、同様のエラー報告がないかググってみたところ、下記のフォーラムのスレッドがヒットした。

Failed to start lighttpd Daemon on boot – Ask Ubuntu
https://askubuntu.com/questions/1072840/failed-to-start-lighttpd-daemon-on-boot

上記の通り、gaminというパッケージをインストールすると解消できる。理由は不明 (笑)

試しに、webサーバーにアクセスしてみる。
クライアントPCのwebブラウザにJetsonのIPアドレス192.168.0.8を入力。

上の画像のような画面が表示されればOK。この画面は、/var/www/html/index.lighttpd.htmlの内容が表示されている。このhtmlファイルはlighttpdをインストールした際に自動で生成されるテスト用のトップ画面。

ここまででwebサーバの動作確認は完了。

lighttpdの設定

次に、HLSでストリーム配信をするめにlighttpdのコンフィグレーションファイルの変更を行う。 /etc/lighttpd/lighttpd.conf/を編集する。

メディアタイプの定義(mimetype)

webサーバーのディレクトリに配置されたコンテンツがどのような種類のファイルであるのかを拡張子から定義する。

定義は[大分類]/[小分類]という感じで定義される。例えばhtmlファイルであれば、大分類はテキストファイル、小分類はhtmlとして定義し、".html" => "text/html",と記述する。

.htmlや.css, .phpなどの基本的なファイル形式に加え、今回はHLSのインデックスファイルとセグメントファイル(.m3u8, .ts)への定義を追加する。

m3u8がaudioになってるのが気になるけど、これはもう決まりなので気にしない。

詳細はwikipediaを参照

https://ja.wikipedia.org/wiki/メディアタイプ

ちなみに、include_shell "/usr/share/lighttpd/create-mime.assign.pl"を記述すると、大体のファイル形式の自動で定義してくれる。こちらを使う場合はmimetype.assign = (...)の記述は削除すること。
尚、.m3u8の定義は create-mime.assign.pl には入っていないので、自分で追記する必要がある。

ストリーム配信ページの作成

ストリーム配信ページの作成を行う。下記のパスへ格納。
/var/www/html/hls.html

h1タグのタイトルとビデオの埋め込みがあるだけのシンプルなテストページ。

videoタグでビデオの埋め込みをする。srcでインデックスファイル(.m3u8ファイル)のパスを指定する。hls.htmlと同じ場所に格納する予定なので、ファイル名のみ記述している。

Gstreamer

Gstreamerの以下のコマンドを打つ

一行になっていて見難いと思うので、改行を追加したものを以下に示す。

主要なエレメントとパラメータをざっくり解説していきます。

omxh264end

カメラからのストリームデータを.h264にエンコードする。
ビットレートとオーディオデータの有無を選択できる。

mpegtsmux

メディアデータをMPEG Trasport Streamデータへ変換する。HLSの.tsファイルと.m3u8ファイルを作る。

hlssink

シンク側の選択。hlsを指定。

max-files
tsファイルをいくつ作るかを指定する。

playlist-location
インデックスファイル(.m3u8ファイル)を生成する場所を指定する。
先ほど作成したhls.htmlの中ではhtmlファイルと同ディレクトリに設定しているので、/var/www/html/playlist.m3u8 と指定しておく。

location
.tsファイルを生成する場所。playlistと同じディレクトリを指定。
location=/var/www/html/segment%05d.ts
%05dとすることで、5ケタの連番ファイルが作成される。

動作確認

gstreamerのコマンドを実行する。下記のようなメッセージが表示されればOK。

次に、/var/www/htmlに.tsファイルが動的に作成されていることを確認する。

ここまで確認できれば、あとはクライアントPCからブラウザで映像が見えるか確認する。

ブラウザから 192.168.0.8/hls.html にアクセスし、カメラの画像が表示されればOK。
かなりレイテンシがおおきく、30~50secくらいの遅延がある。

Jetsonカテゴリの最新記事