Network Device Interface (NDI) is a high performance standard that allows anyone to use real time, ultra low latency video on existing IP video networks.
NDI は IP ネットワーク上でビデオやオーディオを効率よく伝達する方式のひとつです。
iOS 向けの SDK も公開されていてダウンロードすることができます。ライブラリはパス /Library/NDI SDK for Apple/lib/
にプラットフォームごとにインストールされますが、実体は fat な static library です。
$ file /Library/NDI\ SDK\ for\ Apple/lib/iOS/libndi_ios.a
/Library/NDI SDK for Apple/lib/iOS/libndi_ios.a: Mach-O universal binary with 4 architectures: [arm_v7:current ar archivecurrent ar archive] [i386] [x86_64] [arm64]
/Library/NDI SDK for Apple/lib/iOS/libndi_ios.a (for architecture armv7): current ar archive
/Library/NDI SDK for Apple/lib/iOS/libndi_ios.a (for architecture i386): current ar archive
/Library/NDI SDK for Apple/lib/iOS/libndi_ios.a (for architecture x86_64): current ar archive
/Library/NDI SDK for Apple/lib/iOS/libndi_ios.a (for architecture arm64): current ar archive
これを Swift Package から利用することを考えます。そのために少し作業が必要になったのですが、pkgConfig に情報を記述する方法が参考になったのでここにまとめておきます。
参考になった記事
こちらの stackoverflow の記事が大変参考になりました。そこには3通りのリンクの方法が記載されています。
- XCFramework を生成する方法
- module.modulemap を記述して system library を定義し、Build Settings の Library Search Paths にパスを指定する方法
- module.modulemap を記述して system library を定義し、pkgConfig でライブラリの情報を提供する方法
3 種類の方法をそれぞれ試しました。XCFramework を生成する方法 は stackoverflow では GitHub の公開設定を理由にして最終的な回答にはされていませんが、試したところ No such module
エラーが解消できずに上手くいかなかったため、2. と 3. の方法のみをまとめています。
パッケージとしてアプリに組み込む手順
ここで解説している構成はプロジェクトにまとめて下記のリポジトリにあげています。NDI SDK は別途インストールしてください。
pkgConfig を使わない方法
まずは準備として pkgConfig を使わない方法を解説します。プロジェクトの構成は以下のようになっています。
- NDIApp.xcodeproj
- NDIApp(アプリ本体)
- Packages(ローカルパッケージ)
- Package.swift
- Sources
- NDI(NDIのラッパーライブラリ)
- NDI.swift
- libndi(システムライブラリ)
- module.modulemap
- broadcast-upload(App Extension)
システムライブラリとして libndi
を定義し、そのラッパーとして NDI
を定義しています。アプリ本体からインポートするのは NDI
のみです。Package.swift
は以下のような記述になっています。
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "NDI",
products: [
.library(name: "NDI", targets: ["NDI"]),
],
dependencies: [
],
targets: [
.target(
name: "NDI",
dependencies: ["libndi"],
linkerSettings: [
.linkedFramework("Accelerate"),
.linkedFramework("VideoToolbox"),
.linkedLibrary("c++")
]
),
.systemLibrary(name: "libndi")
]
)
NDI
は libndi
に依存しています。libndi
ディレクトリ内にある module.modulemap
は以下のように記述しています。
module libndi {
umbrella header "/Library/NDI SDK for Apple/include/Processing.NDI.Lib.h"
link "ndi_ios"
export *
}
umbrella header として、mac 上にインストールされた NDI SDK のヘッダーファイルのパスを指定しています。NDI SDK の実体がある場所を教える必要があるので、プロジェクトファイルの Build Settings → Library Search Paths に "/Library/NDI SDK for Apple/lib/iOS"
を追加します。
上記までの作業で NDI パッケージ内で NDI SDK の機能を利用できるようになります。プロジェクトファイルに設定値を書き込まずに、pkgConfig を記述する方法もあります。
pkgConfig を使う方法
pkgConfig を使用する場合、Library Search Paths にパスの指定が必要なくなるのでプロジェクトファイルから値を削除しておきます。
pkgConfig ファイルは 4つのパス かもしくは環境変数 PKG_CONFIG_PATH
で指定されたパスから検索されるようです。僕は /usr/local/lib/pkgconfig/libndi_ios.pc
に設置しました。
NDI_SDK_ROOT=/Library/NDI\ SDK\ for\ Apple
Name: NDI SDK for iOS
Description: The NDI SDK for iOS
Version: 5.1.1
Cflags: -I${NDI_SDK_ROOT}/include
Libs: -L${NDI_SDK_ROOT}/lib/iOS -lndi_ios
pkgConfig ファイルを設置したら、Package.swift
の libndi
に pkgConfig のファイル名を指定します。
.systemLibrary(name: "libndi")
↓
.systemLibrary(name: "libndi", pkgConfig: "libndi_ios")
これでアプリのプロジェクトファイルに手を加えずに、 NDI をパッケージ上でリンクして使えるようになりました🙌
import NDI
...
try NDISender.shared.send(pixelBuffer: pixelBuffer)