Open24

NDI SDK v5を眺めながらメモ

Nariaki IwataniNariaki Iwatani

普通のSDKとAdvanced SDKとある。
dylibがそれぞれあるのと、includeは

  • Advanced
  • AVSync
  • Embedded
  • Genlock
    の4ファイルが追加されているだけなので、ofxNDIの実装にあたってはこの4ファイルだけを見ると良さそう。

※Mac版のSDKについてしか確認してない。でもたぶんWindows版も同じ

Nariaki IwataniNariaki Iwatani

scatter gather listがaudio frameやvideo frameについて扱えるインターフェースがあるが、使い所不明・・・。
メモリ厳しい端末で動かすとき用?

compressed frameを送るときのプロパティと実データについてもこれを使うらしい。なるほど。

Nariaki IwataniNariaki Iwatani

AVSyncについてはドキュメント読んでってヘッダに書いてある。

ヘッダとサンプルコードを読んだだけだとわからなかったけど、
NDIlib_avsync_ret_e NDIlib_avsync_synchronize(NDIlib_avsync_instance_t p_avsync, const NDIlib_video_frame_v2_t* p_video_frame, NDIlib_audio_frame_v3_t* p_audio_frame);
これでvideoに同期したaudioが取得できるらしいけど、別スレッドで動かしてるとかの場合は大丈夫?

videoをキャプチャしてからaudioを取り出す仕様なので、キャプチャしてレコーディングとかのときに便利?なのか?

ドキュメントを読んだら追記する

読んだけどよくわからん。
試しに実装してみたらめっちゃ音プチプチした。

Nariaki IwataniNariaki Iwatani

audioとvideoが複数あって全部別スレッドで動いてても大丈夫
genlockオブジェクトはアプリで一つ?
You want to only have one genlock source in your application if possible and so you might need to trigger an event and do the sending across many senders on another thread.
-> たぶん同期したい単位につきひとつってことか

Nariaki IwataniNariaki Iwatani

Advanced.hをざーっと見た感じ、フレームの圧縮にまつわる機能が追加されている感じ

Nariaki IwataniNariaki Iwatani

カスタムアロケータについて
p_dataのallocate/deallocateをやるための自前の関数を設定できる。
NDI SDK的にknownでないフォーマットの信号を扱うときに便利?そんなことある?
たぶんメモリプールをアプリ側で持ってて、そこで使いまわしたいとかそういうことかな。
いちいちmalloc/freeしなくても良くなるということか。

Nariaki IwataniNariaki Iwatani

Forwardのサンプルは何をやろうとしてる・・・?
もしかしてこのコードでhighQ優先でだめならlowQを受け取るとかできるの?

Nariaki IwataniNariaki Iwatani

サンプルコードをざっと眺め終わった。
ドキュメントを読む。

Nariaki IwataniNariaki Iwatani

インストール後は環境変数NDI_RUNTIME_DIR_V4でdllのパスがわかるらしい。
Macでも?addon_config.mkを書くときに再度チェック

ドキュメントにあった

On the Mac, it is not possible to specify global environment variables
and so there is no standard way for the application to provide to specify a path.
For this reason the redistributable on MacOS is installed within /usr/local/lib.

Redistのインストールで.dylibや.aが/usr/local/libに置かれるということだろうが、SDKのインストールでは置かれない。
ユーザーと環境を揃えるにはこちらでもRedist版を改めてインストールする必要があるということか・・・。

Redistパッケージではadvance版のライブラリはインストールされない。
フォーラムに質問投げ中

Nariaki IwataniNariaki Iwatani

The libraries (DLLs) for the latest version of NDI should be entirely backwards compatible with NDI v4
とのこと

Nariaki IwataniNariaki Iwatani

ビデオフレームのパフォーマンスについて

  • YCbCrを使え
  • 変換はCPUで自前でやるよりSDKに任せろ
  • 送りたいデータがGPUにあって、GPUでエンコードできるならやってくれ
Nariaki IwataniNariaki Iwatani

メタデータで<ndi_hwaccel enabled="true"/>を送るとハードウェアアクセラレーションを明示的に使える。
モダンなバージョンのNDIでは自動で判別するが、ストリームの数やスレッドの数、コアの数など様々なので、全てに通用するルールはないことは覚えておくとよい。

Nariaki IwataniNariaki Iwatani

マルチキャストUDPもサポートしているが、ルーター側でマルチキャストがデフォルト状態ではブロードキャストとして実装されていることが多く、ネットワークリソースをめちゃ食うのでNDI側でデフォルトでオフにしてある。

Nariaki IwataniNariaki Iwatani

Advanced SDKではFinder, Receiver, Senderそれぞれに設定JSONが渡せて、これは

  • Machine name
  • NIC
  • format
    などなどを別々に設定できるということで、これはつよい
Nariaki IwataniNariaki Iwatani

Wireshark入れてると接続できなかったり遅くなったりするかも。
Windowsの設定ファイルはC:\ProgramData\NDI\ndi-config.v1.json.にあって、これはAccessManagerから大体設定できる

Nariaki IwataniNariaki Iwatani

MacでReliable UDPを使うには、IPv6が使えるMacOS10.14以降が必要。それ以前でも動くがReliable UDPは使えない。

NDI|HX version 1を使うならXcodeで下記設定

“Targets->Signing & Capabilities” and ensure that the option “Hardened Runtime -> Disable Library Validation”

iOS14とXcode12ではNDI DiscoveryにmDNS and Bonjourを使うためにUnder “Bonjour Services”, one should assign “Item 0” as being “_ndi._tcp.”が必要

MacもiOSも設定ファイルは$HOME/.ndi/ndi-config.v1.json.

Nariaki IwataniNariaki Iwatani

NDIlib_send_timecode_synthesizeでSDK側でタイムコードを生成してくれる。UNIX epoch。
指定しなくてもSystemTimeを使うので、気にしなくても良い。
同マシンならSystemTimeで同期するし、別マシンでもNTPが効いてれば大丈夫なんじゃね?

Nariaki IwataniNariaki Iwatani

iOSで使う場合、アプリがbackgroundに行く時はsender instanceを破棄するのがApple推奨

Nariaki IwataniNariaki Iwatani

Receiverは接続中のSenderがネットワークから消えても、再度見つかったときに自動でReconnectする。
NDIlib_recv_capture_v3の呼び出しはスレッドセーフ。
返されたフレームはユーザーが解放する必要がある。

Nariaki IwataniNariaki Iwatani

NDIlib_send_set_video_async_completionについて

通常はasync_sendしたフレームは、次のSynchronizing eventsまでいじっちゃいけないけど、NDIlib_send_set_video_async_completionにコールバックを登録すると、送信完了(=フレームをユーザーが自由にして良い)のタイミングが取得できる
// Synchronizing events are :
// - a call to NDIlib_send_send_video
// - a call to NDIlib_send_send_video_async with another frame to be sent
// - a call to NDIlib_send_send_video with p_video_data=NULL
// - a call to NDIlib_send_destroy

Nariaki IwataniNariaki Iwatani

NDIlib_source_v2_tにconst char* p_metadataがある
-> connectしなくてもconnection_metadataが読めるってこと?名前やIPアドレスだけでなく、メタデータでソースを選べるということか?

Nariaki IwataniNariaki Iwatani

CompressedなFrameを送る部分の実装を進める。
ptsとdtsが初耳なので調べる。

ドキュメントより

The PTS and DTS are not used directly by the NDI SDK, however they are important in order to ensure that frames may be decoded and displayed in correct order. While we recommend that you use 100 ns intervals for these values, any time-base is technically supported as long as the ordering of integers is correct.

NDI SDK的には別に何でも良さそう。

このリンク先が分かりやすかった。
ということは、ofxNDI的には具体的な値は気にせず、ユーザーに開いておけば良さそう
https://qiita.com/scleen_x_x/items/7f857f2d08de22dee274

Nariaki IwataniNariaki Iwatani

iPhone7にReceiverのテストアプリを入れようとしたらH.264のencode/decode関連?でライブラリへのリンクエラー。

Undefined symbols for architecture arm64:
  "_kVTEncodeFrameOptionKey_ForceKeyFrame", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::send_packet(NDI_recv_video_header_v2 const&, std::__1::pair<unsigned char*, long>) in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTCompressionSessionEncodeFrame", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::send_packet(NDI_recv_video_header_v2 const&, std::__1::pair<unsigned char*, long>) in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTSessionSetProperty", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::recreate_compress_session() in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTCompressionSessionInvalidate", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::~encode_h264() in libndi_advanced_ios.a(video_codec_interface.o)
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::recreate_compress_session() in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTCompressionSessionCreate", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::recreate_compress_session() in libndi_advanced_ios.a(video_codec_interface.o)
  "_kVTCompressionPropertyKey_RealTime", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::recreate_compress_session() in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTDecompressionSessionInvalidate", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::decode_h264::recreate_decompress_session() in libndi_advanced_ios.a(video_codec_interface.o)
      Processing::NDI::Codecs::details::video_codec_impl::decode_h264::~decode_h264() in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTDecompressionSessionCanAcceptFormatDescription", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::decode_h264::decode_packet(NDI_recv_video_header_v2 const&, std::__1::pair<unsigned char*, long>) in libndi_advanced_ios.a(video_codec_interface.o)
  "_kVTCompressionPropertyKey_AllowFrameReordering", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::encode_h264::recreate_compress_session() in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTDecompressionSessionDecodeFrame", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::decode_h264::decode_packet(NDI_recv_video_header_v2 const&, std::__1::pair<unsigned char*, long>) in libndi_advanced_ios.a(video_codec_interface.o)
  "_VTDecompressionSessionCreate", referenced from:
      Processing::NDI::Codecs::details::video_codec_impl::decode_h264::decode_h264(NDI_recv_video_header_v2 const&, std::__1::pair<unsigned char*, long>, unsigned long long) in libndi_advanced_ios.a(video_codec_interface.o)
      Processing::NDI::Codecs::details::video_codec_impl::decode_h264::recreate_decompress_session() in libndi_advanced_ios.a(video_codec_interface.o)
ld: symbol(s) not found for architecture arm64

ドキュメントに記載あり。

The current NDI implementation supports H.264 decoding without any installed plugins
across the Windows and macOS targets.
If you require Linux support, please contact NDI SDK support.

しかし NDIlib_recv_create_v4 (v3も同じ)か NDIlib_recv_destroy の呼び出しでこのリンクエラーが出るのでそれは無理。
iPhone7/iOS14.6だと非対応だとしたら悲しい

Nariaki IwataniNariaki Iwatani

iOSのSenderアプリは動いているっぽいが、MacでFindできない。
最近のiOSはlocal networkを探索する(される?)のにユーザーのパーミッションが必要とかなんとか

ドキュメントにあった。

In iOS 14 and XCode 12, a new setting was introduced to enable support for mDNS and Bonjour. Under “Bonjour Services”, one should assign “Item 0” as being “_ndi._tcp.” In order for NDI discovery operate correctly. It is also required to enable networking in the App sandbox settings.

NDIの話ではないがこちらの記事の1と2をなぞればOK。
2のBonjour servicesに書く値は_ndi._tcp.
https://qiita.com/KenNagami/items/e0a603221def205b71ea