Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ The following operating systems and compilers have been tested.

* Ubuntu 24.04 with GCC 13
* Windows 10 with Visual Studio 2022
* macOS 26.7.1 (Apple Silicon) with Apple Clang 21

See [Building and installation](doc/user/building.md) for the C library and
the [Rust workspace guide](rust/README.md) for the daemon and GStreamer plugins.

## Contributing

Expand Down
54 changes: 50 additions & 4 deletions doc/user/building.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ SPDX-License-Identifier: Apache-2.0

## Pre-Build Requirements

### C++ Compiler

**macOS**

Install the Xcode Command Line Tools, which provide Apple Clang and the macOS SDK.

```sh
xcode-select --install
```

macOS 26.7.1 on Apple Silicon with Apple Clang 21 has been tested.

### Python

Having Python 3 isn't an absolute requirement but it makes the subsequent steps to install the dependencies easier.
Expand All @@ -22,6 +34,11 @@ Use the system package manager to install Python 3 and the `venv` module (on Deb
sudo apt install -y python3 python3-venv
```

**macOS**

Install Python 3 from [python.org](https://www.python.org/downloads/macos/) or your package manager.
Use `python3` for the virtual environment commands below.

**Windows**

Download the Python 3 installer from [python.org](https://www.python.org/downloads/windows/) and run it manually, or use the following PowerShell commands.
Expand All @@ -48,7 +65,7 @@ python --version

For manual builds, a **virtual environment** in the repository root (for example, `.venv`) is recommended. It isolates the Python environment used for Conan and other `pip`-installed build tools from the system interpreter, pins compatible tool versions on `PATH`, and on Linux avoids PEP 668 "externally managed" errors when using the system Python.

**Linux**
**Linux and macOS**

```sh
python3 -m venv .venv
Expand Down Expand Up @@ -77,15 +94,16 @@ python -m venv .venv
python -m pip install --upgrade pip
```

To exit the virtual environment later, run `deactivate` (same on Linux and Windows).
To exit the virtual environment later, run `deactivate` (same on Linux, macOS and Windows).

### CMake

The project requires CMake 3.17 or higher; dependencies may require a higher version.

There are x86_64 and arm64 packages for CMake 3.30.0 on the [Python Package Index (PyPI)](https://pypi.org/) which have been tested.
CMake 3.31.10 has been tested on macOS.

**Linux**
**Linux and macOS**

```sh
pip install cmake~=3.17
Expand Down Expand Up @@ -125,6 +143,26 @@ compiler.version=13
os=Linux
```

**macOS**

```sh
pip install conan~=2.2 --upgrade
conan profile detect
```

Conan 2.33.0 with the following Apple Silicon profile has been tested.

```ini
[settings]
arch=armv8
build_type=Release
compiler=apple-clang
compiler.cppstd=gnu17
compiler.libcxx=libc++
compiler.version=21
os=Macos
```

**Windows**

```PowerShell
Expand Down Expand Up @@ -152,7 +190,7 @@ os=Windows

## Building the NvNmos Library

**Linux**
**Linux and macOS**

Prepare a `build` directory adjacent to the `src` directory.

Expand Down Expand Up @@ -194,6 +232,10 @@ Build the library and example application.
cmake --build build --parallel
```

On macOS, the shared library is `build/libnvnmos.dylib` and the example application is `build/nvnmos-example`.
Use `--parallel 2` to limit concurrent build jobs if needed.
The shared Release build and example application have been tested on Apple Silicon.

**Windows**

Prepare a `build` directory adjacent to the `src` directory.
Expand Down Expand Up @@ -296,3 +338,7 @@ apt install -y dbus avahi-daemon
Install and start the Bonjour Service.

See [Download Bonjour Print Services for Windows v2.0.2](https://support.apple.com/kb/DL999).

**macOS**

Bonjour (mDNSResponder) is included with macOS; no separate Avahi or Bonjour installation is required.
57 changes: 48 additions & 9 deletions rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ Rust components for the NMOS daemon and GStreamer plugin family.
## Quick Start

Three terminals on Linux: build once, start the daemon, then run a sender and
receiver. The example scripts use RTP/UDP (`transport=udp`, ST 2110-style
receiver. Linux commands are shown below, with macOS differences noted where needed. The example scripts use RTP/UDP (`transport=udp`, ST 2110-style
multicast) with `auto-activate=true`, so video starts without an NMOS
Controller or IS-05 PATCH step.

Tested on **Ubuntu 24.04** (same as CI).
Tested on **Ubuntu 24.04** (same as CI) and **macOS 26.7.1 (Apple Silicon)**.

### Prerequisites

**System packages** — C++ compiler, CMake, Python, libclang (`bindgen` for
`nvnmos-sys`), and GStreamer headers plus runtime plugins for the example
pipelines:
pipelines. On Ubuntu:

```sh
sudo apt-get update
Expand All @@ -45,6 +45,17 @@ sudo apt-get install --no-install-recommends -y \
`iproute2` (for `ss`), `curl`, and `jq` are only required by the example
pipeline scripts and the interactive demo.

**On macOS**, install the Xcode Command Line Tools and Python as described in
[`building.md`](../doc/user/building.md), then use [Homebrew](https://brew.sh/):

```sh
brew install gstreamer pkgconf bash jq
export LIBCLANG_PATH="$(dirname "$(xcrun --find clang)")/../lib"
```

This uses Xcode's libclang for bindgen. The interactive demo needs Bash 4.3+;
macOS's bundled Bash 3.2 is too old.

CMake must be **3.17+**. Ubuntu 24.04's `cmake` package is sufficient; on older
distros use `pip install cmake~=3.17` inside the venv below (see
[`building.md`](../doc/user/building.md#cmake)).
Expand All @@ -63,11 +74,12 @@ conan profile detect # creates ~/.conan2/profiles/default
```

Keep the venv activated while running `conan install` in the next section.
On macOS, also install CMake in that venv with `pip install cmake~=3.17`.

**Rust** — install [rustup](https://rustup.rs/) if `cargo` is not on your
`PATH`. Building inside `rust/` picks up [`rust-toolchain.toml`](rust-toolchain.toml).

### Build `libnvnmos.so`
### Build `libnvnmos.so` or `libnvnmos.dylib` (macOS)

From the **repository root** (full detail in
[`building.md`](../doc/user/building.md#building-the-nvnmos-library)):
Expand All @@ -87,13 +99,22 @@ cmake -B build \
cmake --build build --parallel
```

On macOS, the same build commands produce `build/libnvnmos.dylib` ins.

Point the Rust build at the shared library:

```sh
export NVNMOS_LIB_DIR=$PWD/build
export LD_LIBRARY_PATH=$NVNMOS_LIB_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
```

**On macOS**, use `DYLD_LIBRARY_PATH` instead of `LD_LIBRARY_PATH` in each
terminal's setup throughout this guide:

```sh
export DYLD_LIBRARY_PATH="$NVNMOS_LIB_DIR${DYLD_LIBRARY_PATH:+:$DYLD_LIBRARY_PATH}"
```

### Build `nvnmosd` and the GStreamer plugin

```sh
Expand All @@ -102,6 +123,14 @@ cargo build -p nvnmosd -p gst-nmos-rs
export GST_PLUGIN_PATH=$PWD/target/debug
```

The plugin is `libgstnmos.so` on Linux or `libgstnmos.dylib` on macOS;
`GST_PLUGIN_PATH` points to the containing directory on both platforms.
If the macOS plugin scanner cannot find Homebrew runtime libraries, set:

```sh
export DYLD_FALLBACK_LIBRARY_PATH="$(brew --prefix)/lib${DYLD_FALLBACK_LIBRARY_PATH:+:$DYLD_FALLBACK_LIBRARY_PATH}"
```

### Start the daemon (terminal 1)

```sh
Expand All @@ -122,7 +151,7 @@ export LD_LIBRARY_PATH=$NVNMOS_LIB_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
export GST_PLUGIN_PATH=/path/to/nvnmos/rust/target/debug

cd rust/gst-nmos-rs
# Optional: pick the NIC that carries multicast (defaults to first non-loopback).
# Optional: pick the NIC that carries multicast (defaults to first non-loopback and in macOS defaults to the default-route interface).
# export DEMO_NIC_IP=203.0.113.1
./scripts/example-pipelines/1080p25-sender-udp.sh
```
Expand Down Expand Up @@ -154,6 +183,13 @@ Use `RUST_LOG=info` on the daemon for verbose logs. Headless hosts: set

**Next steps:** The interactive lab with IS-05 menu actions is
[`gst-nmos-rs/scripts/gst-nmos-rs-demo.sh`](gst-nmos-rs/scripts/gst-nmos-rs-demo.sh).
On macOS, run it from `rust/` with Homebrew Bash and select UDP:

```sh
DEMO_TRANSPORT=udp DEMO_VIDEO_SINK=fakesink DEMO_AUDIO_SINK=fakesink \
"$(brew --prefix)/bin/bash" gst-nmos-rs/scripts/gst-nmos-rs-demo.sh
```

For MXL (`transport=mxl`), build the [MXL SDK](https://github.com/dmf-mxl/mxl)
first, then see [`pipeline-examples.md`](gst-nmos-rs/pipeline-examples.md) for
example pipelines. For compliant ST 2110 (`transport=nvdsudp`)
Expand Down Expand Up @@ -183,19 +219,22 @@ For a combined image with `nvnmosd` + `gst-nmos-rs` + plugins for `transport=mxl

The workspace **MSRV** is **Rust 1.87** (`rust-version` in [`Cargo.toml`](Cargo.toml)). Development, CI, and container builds pin **1.92** in [`rust-toolchain.toml`](rust-toolchain.toml) — the minimum needed for gst-plugins-rs `0.15.2` in [`docker/gst-nmos-rs`](../docker/gst-nmos-rs/).

`nvnmos-sys` links against a pre-built `libnvnmos.so`. The Rust crate does **not** build the C library itself; use the CMake/Conan flow under `../src/` ([Quick start — Build `libnvnmos.so`](#build-libnvnmosso)), then:
`nvnmos-sys` links against a pre-built `libnvnmos.so` (`libnvnmos.dylib` on macOS). The Rust crate does **not** build the C library itself; use the CMake/Conan flow under `../src/` ([Quick start — Build `libnvnmos.so`](#build-libnvnmosso)), then:

```sh
export NVNMOS_LIB_DIR=/absolute/path/to/build # contains libnvnmos.so
export NVNMOS_LIB_DIR=/absolute/path/to/build # contains libnvnmos.so or libnvnmos.dylib
cargo build --workspace
```

`NVNMOS_LIB_DIR` is optional when `libnvnmos.so` is installed on the system linker path.
`NVNMOS_LIB_DIR` is optional when `libnvnmos` is installed on the system linker path.

`NVNMOS_INCLUDE_DIR` defaults to `../src/` (where `nvnmos.h` lives in-tree). Override only to bindgen against a different header location.

`protoc` is vendored via [`protobuf-src`](https://crates.io/crates/protobuf-src) — no system `protoc` is required.

For integration test prerequisites, see
[`gst-nmos-rs/tests/README.md`](gst-nmos-rs/tests/README.md).

## Scale Benchmark (`nvnmosd-bench`)

Measures daemon memory usage and RPC/HTTP latencies across preset scenarios. See
Expand All @@ -216,7 +255,7 @@ RPC-only regression client — no GStreamer. For a full media walk-through, use
[Quick start](#quick-start) instead.

```sh
# Terminal 1: daemon (with NVNMOS_LIB_DIR / LD_LIBRARY_PATH set)
# Terminal 1: daemon (with NVNMOS_LIB_DIR and LD_LIBRARY_PATH or DYLD_LIBRARY_PATH set)
cargo run --bin nvnmosd

# Terminal 2: example client
Expand Down
65 changes: 60 additions & 5 deletions rust/gst-nmos-rs/scripts/env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,18 @@ export DEMO_HOST_NAME=${DEMO_HOST_NAME:-}
# Override with DEMO_NIC_IP.
_default_nic_ip() {
local addr
if command -v ip >/dev/null 2>&1; then
if [[ "$(uname -s)" == Darwin ]]; then
local iface
iface=$(route -n get default 2>/dev/null | awk '/interface:/ {print $2; exit}')
if [[ -n "$iface" ]]; then
addr=$(ipconfig getifaddr "$iface" 2>/dev/null)
fi
elif command -v ip >/dev/null 2>&1; then
addr=$(ip -4 -o addr show 2>/dev/null \
| awk '$2 != "lo" {print $4; exit}' \
| cut -d/ -f1)
fi
if [[ -z "${addr:-}" ]]; then
if [[ -z "${addr:-}" && "$(uname -s)" != Darwin ]]; then
addr=$(hostname -I 2>/dev/null | awk '{print $1}')
fi
echo "${addr:-127.0.0.1}"
Expand Down Expand Up @@ -237,10 +243,59 @@ require_cmd() {
}
}

# Native tools for Unix socket liveness and ownership checks.
_demo_require_socket_tools() {
if [[ "$(uname -s)" == Darwin ]]; then
require_cmd nc
require_cmd netstat
else
require_cmd ss iproute2
fi
}

# A stale socket pathname is not a listening daemon.
demo_uds_socket_listening() {
local path=$1
if [[ "$(uname -s)" == Darwin ]]; then
nc -w 1 -U "$path" </dev/null >/dev/null 2>&1
else
ss -xlpn 2>/dev/null | grep -F "LISTEN" | grep -qF "$path"
fi
}

# Verify that the expected process owns the socket, not another daemon.
demo_uds_listener_owned_by_pid() {
local pid=$1 path=$2
if [[ "$(uname -s)" == Darwin ]]; then
# Verbose Darwin netstat reports the owning PID and SO_ACCEPTCONN
# (0x0002). Accepted connections can have the same pathname.
netstat -anv -f unix 2>/dev/null | awk -v pid="$pid" -v path="$path" '
$1 == "Address" {
for (i = 1; i <= NF; i++) {
if ($i == "process:pid") proc = i
if ($i == "options") opts = i
if ($i == "Addr") addr = i
}
next
}
proc && opts && addr && $2 == "stream" &&
$proc ~ (":" pid "$") && $opts ~ /[2367abefABEF]$/ {
name = $0
for (i = 1; i < addr; i++)
sub(/^[[:space:]]*[^[:space:]]+[[:space:]]*/, "", name)
if (name == path) found = 1
}
END { exit !found }
'
else
ss -xlpn 2>/dev/null | grep -F "$path" | grep -qE "pid=$pid[,)]"
fi
}

# Example pipelines prerequisites.
require_nvnmosd() {
require_cmd ss iproute2
if ! ss -xlpn 2>/dev/null | grep -F "LISTEN" | grep -qF "$DEMO_DAEMON_SOCK"; then
_demo_require_socket_tools
if ! demo_uds_socket_listening "$DEMO_DAEMON_SOCK"; then
echo "[error] $DEMO_DAEMON_SOCK is not listening — start nvnmosd first" >&2
exit 1
fi
Expand All @@ -249,7 +304,7 @@ require_nvnmosd() {
# Interactive demo prerequisites.
require_demo_tools() {
# for daemon socket checks
require_cmd ss iproute2
_demo_require_socket_tools
# for making IS-04/IS-05/IS-08 HTTP requests
require_cmd curl curl
# for JSON body parsing/creation
Expand Down
Loading