自作の見守りカメラや定点カメラ、監視・防犯カメラ構想において1クライアント1画面への配信なら残すは設置のみかと思ったものの、複数デバイスで同時に映像確認できた方が...と思うに至ってみれば、以前、RTSP/RTPに触れる機会があったこともあってRTPでLAN内の複数クライアントと併せ、同一クライアント内のVLCやffplayにも同時配信してみた話。
尚、ここでは、video(映像)のみの配信であり、audio(音声)については考慮していません。
音声については別件でBluetoothイヤホンのマイクを認識させるべく、PulseAudioから移行したPipeWireにおいて何れでも違和感1つなく使えたMurmur/Mumbleで実装してみようかなとか。
ハードウェアと言っても要は複数台のパソコンやスマホとネットワークカメラ|IPカメラ。
今回使ったパソコンとスマホは以下の通り。
今回、IPカメラとして使ってみたのは、次の通り。
と言ってもESP32-S3は本番用として検証では、他の2ボードにArduino IDEでRTSP配信のみに特化したスケッチESP32-CAM_RTSPを拝借、アップロードしたものを使用。
Wi-FiのSSIDとPASSPHRASE、LAN内での固定IPアドレスは、src/wifi_config.hを修正・追記。
また、Arduino IDEで使うにあたっては、格納ディレクトリ名と.inoファイル名を一致させておきます。
尤も、そうでない場合でも、.inoファイルを開く際に注意されつつ、その時点でも作成できますが。
尚、当該スケッチを使うに当たり、注意事項が2点。
まず、1点めは、Arduino IDEの[ファイル] => [環境設定]で[コンパイラの警告]を[初期値]( or [なし])に設定すること。
そうしないと、少なくとも、これが[全て]となっていると[src/CRtspSession.cpp]ファイルの287行目付近のsnprintf関数でエラーとなり、ことごとく文字列項目を外さないと通らず、かと言って、そうすると肝心なRTSP配信できなくなるので。
2点めは、WROVER-Eチップが載ったOV2640カメラボードについては、スケッチに当該ボードのカメラ用ピンアサインなどの情報がないため、要追記。
具体的には、[src/OV2640.cpp]ファイルにおいて[camera_config_t esp32cam_wrover_kit_config{}]などとしてArduino IDEのESP32サンプルスケッチCameraWebServerのcamera_pins.hの[CAMERA_MODEL_WROVER_KIT]を参照するなどして設定値群を追記。
そしてOV2640.hの[extern camera_config_t]行末尾にでもカンマに続けてconfig名を追記。
また、これに伴い、.inoファイルの37行め付近の[esp_err_t err = cam.init();]の()内は同名の*_configを設定し、一致させること。
更にWROVER-Eチップ+OV2640カメラボードのArduino IDEにおけるボード設定は[ESP32 Wrover Module]ではなく、[ESP32 Wrover Kit(all versions)]にし、PSRAMを[Enabled]、[Partition Scheme]を[Huge APP 3MB No OTA/1MB SPIFFS]に設定すること。
他方、ESP32-CAMの場合は、スケッチ既存の[esp32cam_aithinker_config]、ボードには[AI Thinker ESP32-CAM]、[Partition Scheme]には、[Huge APP 3MB No OTA/1MB SPIFFS]を設定。
また、ESP32-S3カメラボードの場合は、Arduino IDEでFreenoveのスケッチ等もしくは、それらを見るにESP32サンプルスケッチCameraWebServerのcamera_pins.hの[CAMERA_MODEL_ESP32S3_EYE]を参照、前述の要領で、これ用の[*_config]設定をOV2640.cppとOV2640.hに追記、設定の上、ボードには[ESP32S3 Dev Module]を設定することになるでしょう(フラッシュメモリは8MBと余裕があるので同サイズ以下なら[Partition Scheme]は任意)。
遅まきながら、Arduino IDE 1.8.x系においてFreenove ESP32-S3カメラボードでサンプル以外のスケッチ(rtsp配信するほぼ本番用)を試してみたところ、config設定で微ハマったので追記。
ハマりどころは、Freenoveの[Sketch_07.2_As_VideoWebServer]にある[camera_pins.h]の[CAMERA_MODEL_ESP32S3_EYE]では、なぜか、これだけGPIO部分が、[Y2_GPIO_NUM]から始まり、[Y9_GPIO_NUM]の順となっている点。
他のconfigは、[Y9_GPIO_NUM]〜[Y2_GPIO_NUM]の順になっており、この順で参照して[OV2640.cpp]の[.pin_d7]〜[.pin_d0]の順に反映させないと無駄にハマります。
当該部分を間違えてもスケッチのコンパイルも完了してしまい、ボードにもアップロードできてしまう、指定したIPアドレスにpingすると通る、mDNSを盛り込んでいた場合も通る...
にも関わらず、シリアルモニタを眺めながら、端末からffplay rtsp://IP_ADDRESS/mjpeg/1などとしてアクセスするとrebootしたり、Camerra init errorになったり、Guru Meditation Error core 0 panic'ed (loadProhibited)とかになったりして当然、映像も表示されない...
こんなカオスにはさよならを。
また、ESP32-S3-WROOM-1開発ボードなら、PSRAMには、[OPI PSRAM]をセットします(WROVERボードでいけた[QSPI PSRAM]だとPSRAMがないとかなんとか...、ちゃんと違いを調べろ自分...)。
あとボード設定は、現時点では[ESP32S3 Dev Module]。
これだけでいけるはずですが、強いて言うなら、Flash Size:は、せっかくなのでボードが備える[8MB(64Mb)]、Partition Scheme:は、16Mや32M(や謎なRainMaker)でなければ、なんでも良いと思いますが、最も適していそうなのは[8M with spiffs(3MB APP/1.5MB SPIFFS)]。
それと関係あるか否か、それが何なのかさえ、わかりませんが、[.ledc_timer = LEDC_TIMER_1]は、[.ledc_timer = LEDC_TIMER_0]に、[.ledc_channel = LEDC_CHANNEL_1]は、[.ledc_channel = LEDC_CHANNEL_0]に変更しました。
ちなみにArduinoOTA.hを使った無線アップロードもできました。
あれ、WROVERボードよりESP32-S3-WROOM-1開発ボードの方が遅延が長い(レイテンシが大きい)...、RTSP(前者約1秒、後者約3秒)もRTP(前者約5秒、後者約10秒)も...なんで?どっかいじれば同程度くらいにはなるの...か?
と思ったら、[.frame_size]を[FRAMESIZE_UXGA]にしていたからでWROVERボード同様、[FRAMESIZE_SVGA]にしたら遅延も同じくらいになりました。
となるとフレームは、もっと小さくても十分だから、それがあれば、もう少し、遅延なくなるのか...と、[FRAMESIZE_QVGA]にしてみると小さく表示はされるものの、遅延とは無縁のようでレイテンシに変化はありませんでした。
お、opencvで試すと同じHVGAサイズでもRTSPストリームはほぼ遅延はないと言えるレベル。
また、ここまではcvlc(Debian上なのでより正確には、flatpakのVLCによるcvlc)でのRTP配信で遅延5〜6秒といった感じでしたが、opencvでRTPストリームを開いてみると体感で1秒ほどレイテンシが小さくなって遅延4秒くらいか...。
ん?少し画質が荒く見える?実用には耐える程度も実際荒いから?
ちなみに、このようにした後も、既存含むこれらの*_config設定値が不足しているエラーとなる場合、PSRAMがある前提で[.fb_count = 2]の後にカンマ(,)を足し、次行に順次[.fb_location = CAMERA_FB_IN_PSRAM,]、[.grab_mode = CAMERA_GRAB_LATEST,]、[.sccb_i2c_port = 1]を追記のこと。
ソフトウェアは主に次の通り。
尚、自身が使用しているDebian|Raspberry Pi OSにおいては、#981439、#982299、The VideoLAN Forums、askubuntu.comにあるようにliblivemediaがフリーではないコードを含むことからDebian GNU/Linuxプロジェクトのポリシーにマッチせず、苦渋の決断としてVLCにおけるRTSP/RTP配信あたりを無効にしているとのことで、ある時点から今に至るまでDebianのリポジトリからVLCをインストールするとこれら一部機能が利用できないようです。
単に当該ライブラリをnonfreeに収録すれば良いという単純な話ではなく、ましてフリーでオープンソースなVLC自体をnonfreeに収録することはできなかったってことなんでしょうね。
が、そんなDebian|Raspberry Pi OSでも何れも同様にsnapやflatpakからVLCをインストールすれば、RTP/RTSP配信及び関連設定もできるようになります。
RTPストリーミングを開始する前に[ツール]、[設定]から[ストリーム出力]、[出力手段]の[HOP上限値(TTL)](デフォルト-1)を2とか5とか10とか任意の正の値にしておく必要があるでしょう(そうしないとRTPでストリーミングできないかと思います)。
[設定]で[rtp]や[rtsp]で検索すると関連する項目が複数ありますが、今回の検証では、[HOP上限値(TTL)]以外の他の項目を変更する必要はありませんでした。
ちなみに、その中には[RTP/RTSP]の[RTPオーバーRTSP(TCP)の使用]という設定項目もあり、これにチェックを入れたとしても全てがそうなるわけではないのか、TCPでも可能にしてくれるのか、マルチキャスト/マルチクライアント対応もできました。
が、そう言えば、ここまで遅くなる?と思うほど、めちゃめちゃ遅延したことがありましたが、これを設定した時だったのかも...。
その上で複数のデバイスやコマンド、VLCなど複数クライアントでストリーミングの同時再生において、ここで行なったRTP/RTSPストリーミングの配信・受信の概要は次の通り、
マルチキャストのアドレス範囲は、224.0.0.0〜239.255.255.255、ポートのデフォルトは554な模様。
RTSPについては、デフォルトのポートは8554、付加するパスは/mjpeg/1がデフォルトというか例示されることも多い様子、今回使用のスケッチでは、ポートの変更は、スケッチ上で、パスの変更もsrc/CRtspSession.cpp内でできる模様。
ファイアウォールを使っている場合、当該ポートがオープンになっていることを確認、開いていなければ、開放する必要があります。
確認した環境においては、映像自体は動きも割と滑らかな一方、何れのアプリでも受信開始から5〜6秒でしょうか、そこそこの遅延がある関係で送信側・受信側の段取りの順番が重要なのか?と思わされることがありましたが、実際は、どちらが先でも問題ないようです。
さておき、RTP/RTSPストリーム送信側の設定。
この時、最後の画面では、次のようなストリーム出力文字列が表示されるでしょう。
[:sout=#transcode{vcodec=h264,vb=800,acodec=mpga,ab=128,channels=2,samplerate=44100,scodec=none}:rtp{dst=239.1.1.1,port=5004,mux=ts} :no-sout-all :sout-keep]
結果、[Converting rtsp://...]というタイトルで三角コーンが表示されたVLCが起動した状態になります。
配信が開始されてもタイムは、00:00のまま、開始中は、動きを見せる場合もあるも、いざ開始するとインジケータも動きなしで再生ボタンが押された(停止ボタン表示)状態。
続いてRTPストリーム受信側。
mpvやcvlcでも同様にパスを渡すだけでOK。
結果、同一LAN内の複数デバイス、同一デバイスでも複数のアプリで同じRTPパスにアクセスすることで同時に配信されるようになります。
とりあえず、同時に4つのクライアントにRTPによるマルチキャスト配信してみました。
画像は、RTSP配信のESP32カメラボードのrtspパスをRTP配信しているRaspberry Pi 400パソコン/端末+ffplayとAndroidスマホ/VLC、それぞれでRTPストリームを受信中の様子。
更にdynabookでも端末を2つ起動し、それぞれffplayでrtpアドレスを渡して再生し、RTPストリームを受信している様子。
送信側としてはVLC、受信側としてはRTSP、RTP共にffplay、VLCのほか、MPVも使えましたが、MPVは他に比べ重く、遅延が大きい、MPlayer 1.4では何れも受信・再生できませんでした。
尚、PythonスクリプトでOpenCVを使ってcv2.VideoCapture()にrtsp://IP_ADDRESS:8554/mjpeg/1やrtp://IP_ADDRESS:5004などを指定、フレームを渡してcv2.imshow()してRTSPによる1デバイス1クライアントに対するストリーミングやRTPによるマルチクライアント・マルチキャストストリーミング再生をするとVLCやffplayより1〜2秒遅延時間が短かい(少ない)印象です。
実は、このマルチキャストビデオストリーム、自作したい防犯カメラはもとより、ドアホンでも使えないかと、ついてはCLI操作、ひいてはスクリプトにできればということでVLCのCLI版cvlc。
が、数秒の遅延があり、ドアホン用途には微妙ゆえ、代替策を模索中。
尤もリンク先のcvlcのより複雑な例などを見るにマルチキャストしつつ、特定デバイスにユニキャストなんてこともできるようなのでマルチキャスト受信デバイスは補助として遅延を容認、メインデバイスにユニキャストすれば、メインデバイスでの遅延は限りなく小さくなる?なら、全くナシでもないですが。
っていうかrtsp配信+ffplayなどで受信、それ以上にrtsp配信+opencvで受信だとほぼ遅延がないことからするとマルチキャストでこの程度の遅延は必然?ルーターの性能にもよる?
とは言え、どうもVLCがやってくれるRTPストリーミング部分をPythonなどのスクリプトでやっている情報はなさ気だったので成功体験の為、cvlcでお試し。
flatpak版VLCの場合、cvlcコマンドでのRTSP/RTP配信は、こんな感じ。
Debian/Raspberry Pi OS以外でディストロのリポジトリにあるVLCでいける場合は、flatpak run --command=cvlc org.videolan.VLC部分を単にcvlcに替えれば良いかと。
これをflatpak版VLCのcvlcでRTPストリームを受信すべくアクセスする場合、こんな感じ。
flatpak版ではなく、ディストロのリポジトリにあるVLCなら、前述の通り、単にcvlc rtp://239.255.12.42:5004のようにすればいけるかと。
もちろん、マルチキャストなので他デバイスでも他端末で同様にRTPパスを渡したffplayやmpvでも複数実行可能なマルチクライアントに対応。
管理者権限でWiresharkを起動、LANにつながるネットワークインタフェースを選択、ダブルクリックするなりすると同ネットワークインタフェースを行き交うパケットが表示されます。
RTPについては、デフォルトのままだと全てキャプチャできないと思われるので[分析]、[有効化したプロトコル...]をクリック、ポップアップ表示される[Wireshark 有効にしたプロトコル]画面でアルファベット順になっているリストからRTPを辿り、適宜未チェックの項目にチェックを入れ[OK]で閉じます。
続いて[電話]、[RTP]、[RTPストリーム]をクリック、既にRTP配信中であれば、該当するリストが表示されるようになります。
もちろん、この時、Wiresharkを起動しているパソコン上(のVLCなど)でRTP配信します。
ローカルのネットワークインタフェースにおけるパケットのキャプチャなので。
すると、ここでは、RTP配信ホストとRTSP配信しているESP32ボードのIPアドレスとポートから相互にそれぞれパケットを送っている様子が見て取れます。
もちろん、Wiresharkなら、RTPストリームのみならず、RTSPストリームもキャプチャできます。
テストしてたら、VLCが起動しない!起動しなくなった!
そんな時は、GUIやCLIに関わらず、既に起動しているVLC(のプロセス)がないか確認してみましょう。
特に端末から実行して[Ctrl]+[c]で終了させたつもりが、意に反してプロセスは残っていたりするので要注意。
テストしてたら、VLCでRTP/RTSP配信や受信ができなくなった!
そんな場合、万人共通、起動と同じく、既存のVLC、cvlcプロセスがないか要確認。
もしかしてDebianやRaspberry Pi OSな人?もしかしてdebianやpiリポジトリのVLCも入れたまま?起動するVLCを間違えてるかも?
もしかしてcvlcコマンドそのまま使ってない?それdebianリポジトリのVLCのだよ?
ってあたりを確認してみると良いかも。