通常ではGStreamerにはNDIの機能はありませんが、NDI SDK(Software Development Kit)とGitで公開されているgst-plugin-ndiを使うことでGStreamerで取得したカメラ映像をNDI化してネットワークに流すことができます。
GStreamerでわざわざNDIを使う理由は、2つあります。異なる技術で比較するものではないのですが、RTSP/RTMPなどより低遅延で高品質です。しっかりとした映像伝送を目的としているので、無線Wifiなどのような通信が不安定な状況での利用には、あまり適していません。施設内の映像伝送に使うには最適化もしれませんね。
NDIとして映像が流れてしまえば、IPの設定なども意識しなくてよいし、複数の端末から低遅延で高品質に映像を見ることができます。
直接gitを見てできる人はgst-plugin-ndiを見た方が早いでしょう。
https://github.com/teltek/gst-plugin-ndi
GStreamerとは
GStreamerは、マルチメディアコンテンツの作成、処理、および再生を扱うための強力なフレームワークです。Linux、Windows、macOSなど、さまざまなオペレーティングシステム上で動作し、高度なメディアハンドリングを可能にするオープンソースのライブラリとして設計されています。FFmpegと同様に、GStreamerも多様なオーディオおよびビデオフォーマットをサポートし、エンコード、デコード、トランスコード、フィルタリング、ストリーミングなどの機能を提供しますが、そのアーキテクチャと使用法において独自の特徴を持っています。
https://gstreamer.freedesktop.org/
NDI とは
NDI(Network Device Interface)は、NewTekによって開発されたビデオオーバーIPのための技術です。NDI SDK(Software Development Kit)を使用すると、開発者はネットワーク経由で高品質のビデオとオーディオをリアルタイムで送受信できるアプリケーションを作成できます。無線より有線で使うべき伝送技術で、有線ネットワークを介して使用することが推奨されており、映像の品質を重視したものです。なので監視カメラの用途に最適化といえば、状況によるでしょう。
https://tricaster.jp/ndi-central/
OS・USBカメラ・ハードウェア構成
USBカメラはELP USB Webcamシリーズ、OSはUbuntu 22.04、ミニPCはCHUWI LarkBox X AMD Ryzen 3750U 最大4.0GHz 4コア8スレッド 8GB RAM+256GBストレージTera Term 5で作業するので最初にSSHを入れています。その辺は適宜設定してください。
ubuntu22.04でGStreamerとNDI SDKを使ってUSBカメラの映像をNDI化してネットワークに流してみた。gst-plugin-ndiなど構築
OpenSSHサーバーをインストール、UFW(Uncomplicated Firewall)をインストールして、ファイアウォール設定、ポート22(デフォルトのSSHポート)への着信接続を許可します
sudo apt install openssh-server
sudo apt install ufw
sudo ufw allow 22
パッケージリストを更新して、最新バージョンにアップグレードします
sudo apt update
sudo apt dist-upgrade
GStreamerおよびその基本プラグイン、追加のメディアフォーマット用のプラグインをインストールします
sudo apt-get install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-ugly
Git、Curlをインストールします
sudo apt install git
sudo apt install curl
Rustのインストーラーをダウンロードし、実行します。これにより、Rust言語とCargoパッケージマネージャがインストールされます。1) Proceed with installation (default)を選択します。Rustの環境変数を現在のシェルセッションに読み込みます。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
以下サイトから所定の登録を進めるとEメールでInstall_NDI_SDK_v5_Linux.tar.gz をダウンロードできます。NDI SDKのLinux版をダウンロードします。バージョンによって名前やダウンロード方法など変わるでしょう。
https://ndi.video/download-ndi-sdk/
ダウンロードしたNDI SDKのアーカイブを解凍します。ファイル名など適宜最適にしてください。
tar -xzvf Install_NDI_SDK_v5_Linux.tar.gz
NDI SDKのインストーラースクリプトを実行します。インストール時の質問には10番までエンターでスキップしてから`Y`を押して同意します。10番を超えたら1回ずつエンターを押さないとスキップしてしまいます。
sudo ./Install_NDI_SDK_v5_Linux.sh
NDI SDKのインストールディレクトリに移動します。ファイル名などバージョンによって若干ちがうかもしれませんので、lsなどで確認してください。要はlibのなかのx86_64-linux-gnuに進みます。
cd 'NDI SDK for Linux'
cd lib
cd x86_64-linux-gnu
NDIライブラリをシステムのライブラリディレクトリにコピーします。ライブラリキャッシュを更新します。直接ライブラリを移動すれば、環境変数など変更しなくてよいからです。lsでx86_64-linux-gnuのなかにlibndi.so.5.6.0などのファイルが3つぐらいあるはずです。それらを全部移動します。libndi.so.5.6.0だけ移動するのでもよいのかもしれません。
sudo cp libndi.so* /usr/local/lib/
sudo ldconfig
/sbin/ldconfig.real: /usr/local/lib/libndi.so.5 はシンボリックリンクではありませんなど表示されるでしょう。libndi.so.5が実際のファイルであることを確認し削除します。
sudo rm /usr/local/lib/libndi.so.5
libndi.so.5.6.0への正しいシンボリックリンクを作成します。再度ldconfigを実行して、ライブラリキャッシュを更新します。エラーが無くなるはず。
sudo ln -s libndi.so.5.6.0 /usr/local/lib/libndi.so.5
sudo ldconfig
GStreamer用NDIプラグインのリポジトリをクローンします。cd ~で元の階層に戻ります。それでリポジトリのフォルダーに移動します。
cd ~
git clone https://github.com/teltek/gst-plugin-ndi.git
cd gst-plugin-ndi
RusのインストールマネージャーrustupをSnap経由でインストールします。Cargo、Rustのパッケージマネージャをインストールします。GStreamerのNDIプラグインをビルドします。
sudo snap install rustup --classic
sudo apt install cargo
sudo cargo build --release
sudo apt install cargoで動作が止まる(もしくは時間がかかる?)場合は、途中で中止にして、何度か入力すると設定が進みます。
次は、ビルドされたプラグインをGStreamerのプラグインディレクトリにインストールします。このコマンドにより、ビルドされたNDIプラグインがGStreamerによって認識され、利用可能になります。これによって、GStreamerを使用しているアプリケーションでNDIストリームの受信や送信が可能になるわけです。
sudo install -m 755 target/release/libgstndi.so /usr/lib/x86_64-linux-gnu/gstreamer-1.0/
sudo ldconfig
念のため現在のユーザーを`video`グループに追加します。ビデオ関連のデバイスへのアクセス権を与えるためです。
sudo usermod -aG video $USER
GStreamerフレームワーク内でNDI関連のプラグインが機能しているか確認してみましょう。
gst-inspect-1.0 ndi
gst-inspect-1.0 ndisrc
gst-inspect-1.0 ndisink
正常に動作すれば完了です。USBカメラを接続します。
sudo apt install v4l-utils
sudo v4l2-ctl --list-devices
大抵/dev/video00か1にUSBカメラが来ているはずです。このカメラはvideo0とvideo1の二つが表示されますが、video0が映像のようですね。
映像を送信するコマンドを入れてみます。これでネットワーク内にCamera1のNDIが流れます。
sudo gst-launch-1.0 -v v4l2src device=/dev/video0 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! ndisink ndi-name="Camera1"
NDIを受信できました。GStreamerにNDI SDK(Software Development Kit)とGitで公開されているgst-plugin-ndiを使うことでGStreamerで取得したカメラ映像をNDI化してネットワークに流すことができました。
圧縮したいが出来ない!!!
FHDサイズで80Mbpsぐらいのデータ量です。もっと減らしたいと思い、h264やh265の圧縮も使ってみます。例えばこんな感じです。
gst-launch-1.0 -v v4l2src device=/dev/video0 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! x264enc bitrate=5000 speed-preset=ultrafast tune=zerolatency ! h264parse ! decodebin ! queue ! ndisink ndi-name="Camera1"
WARNING: erroneous pipeline: エレメント “h264parse” がありませんとエラーが出ましたので以下のgstreamer1.0-plugins-badを追加します。
sudo apt-get update sudo apt-get install gstreamer1.0-plugins-bad
NDI SDKでは、送信されるデータは圧縮されません。いろいろ試行錯誤しましたが、うまくいきませんでした。どうやらNDI HXという規格が圧縮機能も備えたもので、企業向けに有料で提供されているようです。
NDI HXに対応しているエンコーダー製品には、ビットレートを設定できるものがあります。企業向けに有料で提供されるNDI SDK Advanceを使用することで、そのような機能が利用できるようです。つまり圧縮をかけて無線などでも軽量に扱えるようにするには、個人の自作だと難しいということですね。
もしくはWindowsならNDI Toolに含まれるNDI Bridgeを使うと、圧縮のビットレートを指定することが可能です。こちらは無料で使えます。
カメラ何台まで同時に送信できるかテスト
ハードウェアは、CHUWI LarkBox X AMD Ryzen 3750Uです。2022年3月に4万円ぐらいで購入したものです。ubuntuのサーバー用にいろいろ使えて便利です。
LANが2個あり、USB3ポートも4つあります。FHDのUSB2.0カメラを3つまで動作しました。エンコードの設定を工夫すれば4つも動きそうですが。。。どうでしょうね。
カメラ3つを接続してデバイスを確認します。
sudo v4l2-ctl --list-devices
こんな感じでリストアップされます。たぶんcam 0/cam 2/cam5が映像が出てくるカメラだと思われます。
どの番号にカメラが来ても良いように.shファイルを作成しました。
#!/bin/bash
sudo gst-launch-1.0 -v v4l2src device=/dev/video0 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera1" &
sudo gst-launch-1.0 -v v4l2src device=/dev/video1 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera2" &
sudo gst-launch-1.0 -v v4l2src device=/dev/video2 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera3" &
sudo gst-launch-1.0 -v v4l2src device=/dev/video3 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera4" &
sudo gst-launch-1.0 -v v4l2src device=/dev/video4 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera5" &
sudo gst-launch-1.0 -v v4l2src device=/dev/video5 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera6" &
sudo gst-launch-1.0 -v v4l2src device=/dev/video6 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera7" &
sudo gst-launch-1.0 -v v4l2src device=/dev/video7 ! "image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! videoconvert ! timeoverlay ! ndisink ndi-name="Camera8" &
wait
camera.shを実行すると3つのUSBカメラの映像がNDIで流れます。
./camera.sh
こんな感じにNDIを受信できました。
shファイルをサービス化して自動起動させれば、USBカメラを監視カメラのように扱うこともできるでしょう。もう少しcamera.shをスマートにします。
#!/bin/bash
# カメラの数を定義
NUM_CAMERAS=8
# カメラごとにループ
for ((i=0; i<NUM_CAMERAS; i++)); do
# GStreamerのパイプラインを実行
gst-launch-1.0 -v v4l2src device=/dev/video$i ! \
"image/jpeg,width=1920,height=1080,framerate=30/1" ! jpegdec ! \
videoconvert ! timeoverlay ! ndisink ndi-name="Camera$((i+1))" &
done
wait
実行権限を付けます。
chmod +x camera.sh
sudo usermod -aG video $USER
以上です。