Table of Contents

VAST.NDI Library

The VAST.NDI library provides support for NewTek's Network Device Interface (NDI) protocol, enabling low-latency video and audio transmission over local networks. It supports receiving and sending NDI streams, automatic device discovery, PTZ camera control, and integration with the VASTreaming capture device framework.

Overview

Feature Description
NDI Source Receive video and audio from NDI sources on the network
NDI Sink Send video and audio as an NDI source
Device Discovery Automatic enumeration of NDI sources
PTZ Control Full PTZ camera control for supported devices
Failover Automatic failover source configuration
Capture Integration Seamless integration with capture device framework

Requirements

  • .NET: .NET 6.0 or later
  • Dependencies: VAST.Common
  • External Dependencies: NDI SDK runtime libraries (available from NDI.tv)

NDI SDK Setup

The VAST.NDI library requires the NDI SDK runtime libraries. Configure the library paths before using any NDI functionality:

// Configure NDI library paths
VAST.NDI.NdiGlobal.DynamicLibrary32BitPath = @"C:\Program Files\NDI\NDI 5 Runtime\v5\Processing.NDI.Lib.x86.dll";
VAST.NDI.NdiGlobal.DynamicLibrary64BitPath = @"C:\Program Files\NDI\NDI 5 Runtime\v5\Processing.NDI.Lib.x64.dll";
Important

The NDI SDK must be installed separately. Download it from ndi.tv/sdk.

Receiving NDI Streams

NdiSource

The NdiSource class receives video and audio from NDI sources on the network.

URI Format

ndi://<source-name>?vast-url=<url-address>
Parameter Description
source-name The NDI source name (e.g., MACHINE-NAME (Source))
vast-url Optional direct IP address and port for faster connection

Usage

var source = new VAST.NDI.NdiSource();
source.Uri = "ndi://MACHINE-NAME (OBS)";

source.NewStream += (sender, e) =>
{
    Console.WriteLine($"Stream {e.StreamIndex}: {e.MediaType.ContentType}");
};

source.NewSample += (sender, e) =>
{
    ProcessSample(e.Sample);
};

source.StateChanged += (sender, state) =>
{
    if (state == MediaState.Opened)
    {
        source.Start();
    }
};

source.Open();

Direct Connection

For faster connection when the IP address is known:

source.Uri = "ndi://MACHINE-NAME (OBS)?vast-url=192.168.1.100:5961";

Supported Pixel Formats

Format Description
UYVY YUV 4:2:2 packed
BGRA 32-bit BGRA
RGBA 32-bit RGBA

Supported Audio Format

Format Description
FLTP 32-bit float planar

PTZ Camera Control

PTZ camera control is available when using NDI sources through the capture device framework via CreateVideoCapture. The video capture source provides access to camera control functionality through its CameraControl property.

// Create video capture source for NDI device
var capture = VAST.Media.SourceFactory.CreateVideoCapture("[NDI]MACHINE-NAME (PTZ Camera)");

capture.StateChanged += (sender, state) =>
{
    if (state == MediaState.Started)
    {
        // Check if PTZ is supported
        if (capture.CameraControl != null && capture.CameraControl.IsPanTiltSupported)
        {
            var ptz = capture.CameraControl;

            // Set pan/tilt position (-1.0 to +1.0)
            ptz.SetPanTilt(0.5f, 0.0f);

            // Set zoom level (0.0 to 1.0)
            ptz.CurrentZoom = 0.5f;
        }

        capture.Start();
    }
};

capture.Open();

PTZ Features

Feature Method/Property Description
Pan/Tilt SetPanTilt(pan, tilt) Set absolute position (-1.0 to +1.0)
Pan/Tilt Speed SetPanTiltSpeed(panSpeed, tiltSpeed) Continuous movement
Zoom CurrentZoom Set zoom level (0.0 to 1.0)
Zoom Speed SetZoomSpeed(speed) Continuous zoom (-1.0 to +1.0)
Auto Focus Focus() Trigger auto-focus
Manual Focus SetManualFocus(value) Set focus (0.0=infinity to 1.0=close)
Focus Speed SetManualFocusSpeed(speed) Continuous focus adjustment
Presets StorePreset(n), RecallPreset(n, speed) Save and recall positions
White Balance SetAutoWhiteBalance(preset) Auto, Indoor, Outdoor, OneShot
Manual WB SetManualWhiteBalance(red, blue) Manual color adjustment
Auto Exposure SetAutoExposure() Enable auto exposure
Manual Exposure SetManualExposure(level) Set exposure level
Full Manual SetManualExposure(iris, gain, shutter) Full manual control

Web Control

Some NDI sources provide a web control interface. The URL is available via the WebControlUri property after the source is opened.

source.StateChanged += (sender, state) =>
{
    if (state == MediaState.Opened)
    {
        if (!string.IsNullOrEmpty(source.WebControlUri))
        {
            Console.WriteLine($"Web control: {source.WebControlUri}");
        }

        source.Start();
    }
};

Sending NDI Streams

NdiSink

The NdiSink class broadcasts video and audio as an NDI source visible to other devices on the network.

URI Format

ndi://<source-name>?vast-group-name=<group>&vast-failover-name=<failover>
Parameter Description
source-name The name that will identify this source on the network
vast-group-name Optional NDI group for source organization
vast-failover-name Optional failover source name

Usage

var sink = new VAST.NDI.NdiSink();
sink.Uri = "ndi://My Application";

sink.AddStream(0, videoMediaType);
sink.AddStream(1, audioMediaType);

sink.StateChanged += (sender, state) =>
{
    if (state == MediaState.Opened)
    {
        sink.Start();
    }
};

sink.Open();

// Push media samples
sink.PushMedia(0, videoSample);
sink.PushMedia(1, audioSample);

// When done
sink.Stop();

NDI Groups

Use groups to organize NDI sources:

sink.Uri = "ndi://Camera Feed?vast-group-name=Production";

Failover Configuration

Configure a backup source that receivers will use if this source becomes unavailable:

sink.Uri = "ndi://Primary Feed?vast-failover-name=Backup Feed";

Supported Pixel Formats for Sending

Format Description
UYVY YUV 4:2:2 packed
P216 YUV 4:2:2 16-bit
YV12 YUV 4:2:0 planar
IYUV YUV 4:2:0 planar (I420)
NV12 YUV 4:2:0 semi-planar
BGRA 32-bit BGRA
RGBA 32-bit RGBA
Note

If the input is encoded or its pixel format is not in the supported list, the sink automatically decodes/converts it to a supported format.

Supported Audio Format for Sending

Format Description
FLTP 32-bit float planar
Note

If the input is encoded or its audio format is not FLTP, the sink automatically decodes/converts it.

Device Discovery

Warning

NDI device discovery is notoriously unreliable and can take a long time, especially on larger networks. The library uses multiple optimizations to mitigate these issues, including caching and background enumeration. However, for best results, configure your application with the exact NDI source names beforehand and connect directly without relying on discovery.

Enumerating NDI Sources

The NdiGlobal class provides methods to discover NDI sources on the network.

Video Sources

var videoDevices = await VAST.NDI.NdiGlobal.EnumerateVideoSources();

foreach (var device in videoDevices)
{
    Console.WriteLine($"Video: {device.Name} ({device.DeviceId})");
}

Audio Sources

var parameters = new AudioDeviceEnumeratorParameters
{
    EnableChannelFilter = true,
    GroupChannelsBy = 2  // Group channels in pairs (stereo)
};

var audioDevices = await VAST.NDI.NdiGlobal.EnumerateAudioSources(parameters);

foreach (var device in audioDevices)
{
    Console.WriteLine($"Audio: {device.Name} ({device.DeviceId})");
}

Discovery Configuration

Configure source discovery behavior:

// Set enumeration timeout (default: 10 seconds)
VAST.NDI.NdiGlobal.SourceEnumerationTimeout = 50000000L; // 5 seconds in 100ns units

// Set cache expiration (default: 10 seconds)
VAST.NDI.NdiGlobal.SourceEnumerationExpiration = 100000000L; // 10 seconds

// Specify NDI groups to search
VAST.NDI.NdiGlobal.SourceEnumerationGroups = "Production,Test";

// Add extra IP addresses to search
VAST.NDI.NdiGlobal.SourceEnumerationExtraIps = "192.168.1.100,192.168.1.101";

// Filter source types
VAST.NDI.NdiGlobal.AllowVideoSources = true;
VAST.NDI.NdiGlobal.AllowAudioSources = true;

Pre-registering Known Devices

For faster startup, pre-register known NDI devices:

VAST.NDI.NdiGlobal.RegisterKnownDevice(
    "MACHINE-NAME (OBS)",           // Source name
    "192.168.1.100:5961",           // IP address and port
    audioDetected: true,
    videoDetected: true
);

Capture Device Integration

NDI sources integrate seamlessly with the VASTreaming capture device framework, allowing them to be used alongside local capture devices.

Video Capture

// Use with IVideoCaptureSource2
var capture = VAST.Media.SourceFactory.CreateVideoCapture("[NDI]MACHINE-NAME (OBS)");
capture.Open();

Audio Capture

// Use with IAudioCaptureSource2
var capture = VAST.Media.SourceFactory.CreateAudioCapture("[NDI]MACHINE-NAME (OBS)");
capture.Open();

Shared Source Optimization

When both video and audio capture sources are created from the same NDI source, they internally share a single NdiSource instance. This optimization reduces network bandwidth and CPU usage by avoiding duplicate NDI connections to the same source.

// Both capture sources share the same underlying NdiSource
var videoCapture = VAST.Media.SourceFactory.CreateVideoCapture("[NDI]MACHINE-NAME (OBS)");
var audioCapture = VAST.Media.SourceFactory.CreateAudioCapture("[NDI]MACHINE-NAME (OBS)");

videoCapture.Open();
audioCapture.Open();

Cleanup

When finished using NDI functionality, unload the library:

VAST.NDI.NdiGlobal.Unload();

Otherwise it may prevent your app from being closed.

Troubleshooting

Common Issues

Issue Cause Solution
No sources found NDI SDK not installed Install NDI SDK from ndi.video/for-developers/ndi-sdk
Library load failure Wrong library path Verify DynamicLibrary paths
Source not discovered Network firewall Allow NDI traffic (TCP/UDP 5960-5969)
Slow discovery Large network Use SourceEnumerationExtraIps for known IPs
PTZ not working Source doesn't support PTZ Check CameraControl is not null before using
Format conversion Unsupported input format Automatic conversion handles this

Network Requirements

Protocol Ports Description
TCP 5960-5969 NDI discovery and control
UDP 5960-5969 NDI video/audio data
mDNS 5353 NDI source discovery

See Also