通常では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を使用することで、そのような機能が利用できるようです。つまり圧縮をかけて無線などでも軽量に扱えるようにするには、個人の自作だと難しいということですね。

https://ndi.video/advanced/

もしくは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

以上です。