Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
grafton-ndi
High-performance, idiomatic Rust bindings for the NDI® 6 SDK, enabling real-time, low-latency IP video streaming. Built for production use with zero-copy performance and comprehensive async support.
Features
- Zero-copy receive - Eliminates ~475 MB/s of memcpy at 1080p@60fps with borrowed frames
- Zero-copy send - Async video transmission with completion callbacks
- Memory safe - Eliminated 5+ classes of UB through compile-time enforcement
- Source caching - Thread-safe
SourceCacheeliminates ~150 lines of boilerplate - Image encoding - One-line PNG/JPEG encoding and base64 data URLs (optional feature)
- Async runtime support - Native integration with Tokio and async-std (optional features)
- Thread-safe by design - Safe concurrent access with Rust's ownership model
- Ergonomic API - Consistent, idiomatic Rust interface ready for 1.0
- Comprehensive type safety - Strongly-typed with forward-compatible
#[non_exhaustive]enums - Cross-platform - Full support for Windows, Linux, and macOS
- Battle-tested - Used in production video streaming applications
- Advanced SDK support - Optional features for NDI Advanced SDK users
Quick Start
use ;
use Duration;
Installation
Add to your Cargo.toml:
[]
= "0.9"
# For NDI Advanced SDK features (optional)
# grafton-ndi = { version = "0.9", features = ["advanced_sdk"] }
# For image encoding support (PNG/JPEG)
# grafton-ndi = { version = "0.9", features = ["image-encoding"] }
# For async runtime integration
# grafton-ndi = { version = "0.9", features = ["tokio"] }
# grafton-ndi = { version = "0.9", features = ["async-std"] }
Prerequisites
-
NDI SDK: Download and install the NDI SDK for your platform.
- Windows: Installs to
C:\Program Files\NDI\NDI 6 SDKby default - Linux: Extract to
/usr/share/NDI SDK for Linuxor setNDI_SDK_DIR - macOS: Installs to
/Library/NDI SDK for Appleby default
- Windows: Installs to
-
Rust: Requires Rust 1.75 or later
-
Build Dependencies:
- Windows: Visual Studio 2019+ or Build Tools, LLVM/Clang for bindgen
- Linux: GCC/Clang, pkg-config, LLVM
- macOS: Xcode Command Line Tools
-
Runtime: NDI runtime libraries must be available:
- Windows: Ensure
%NDI_SDK_DIR%\Bin\x64is in your PATH - Linux: Install NDI Tools or add library path to LD_LIBRARY_PATH
- macOS: Install NDI Tools or configure DYLD_LIBRARY_PATH
- Windows: Ensure
Documentation
For complete API documentation and detailed examples:
- API Documentation - Full API reference with examples
- Examples Directory - Runnable examples for common use cases
- CHANGELOG.md - Version history and migration guides
Core Types
NDI - Runtime Management
The main entry point that manages NDI library initialization and lifecycle.
let ndi = NDInew?; // Reference-counted, thread-safe
Finder - Source Discovery
Discovers NDI sources on the network.
let finder_options = builder
.show_local_sources
.groups
.build;
let finder = new?;
Receiver - Video/Audio Reception
Receives video, audio, and metadata from NDI sources.
use Duration;
// Assuming source is from finder.find_sources() or finder.sources()
let options = builder
.color
.bandwidth
.build; // Infallible
let receiver = new?;
// Capture a video frame (blocks until success or timeout)
let frame = receiver.capture_video?;
// Or use zero-copy for maximum performance
let frame_ref = receiver.capture_video_ref?;
let data = frame_ref.data; // Direct reference, no copy!
Sender - Video/Audio Transmission
Sends video, audio, and metadata as an NDI source.
let options = builder
.clock_video
.build; // Infallible
let mut sender = new?; // Must be mut for async send
// Synchronous send
sender.send_video;
// Or async zero-copy send
let token = sender.send_video_async;
Frame Types
-
Owned Frames:
VideoFrame- Owned video frame data with resolution, pixel format, and timingAudioFrame- Owned 32-bit float audio samples with channel configurationMetadataFrame- Owned XML metadata for tally, PTZ, and custom data
-
Borrowed Frames (Zero-Copy):
VideoFrameRef<'rx>- Zero-copy video frame reference (eliminates ~475 MB/s memcpy @ 1080p60)AudioFrameRef<'rx>- Zero-copy audio frame referenceMetadataFrameRef<'rx>- Zero-copy metadata frame referenceBorrowedVideoFrame<'buf>- Zero-copy send frame (for async transmission)
Thread Safety
All primary types (Finder, Receiver, Sender) are Send + Sync as the underlying NDI SDK is thread-safe. You can safely share instances across threads, though performance is best when keeping instances thread-local.
Performance Considerations
- Zero-copy: Frame data directly references NDI's internal buffers when possible
- Bandwidth modes: Use
ReceiverBandwidth::Lowestfor preview quality - Frame recycling: Reuse frame allocations in tight loops
- Thread affinity: Keep NDI operations on consistent threads for best performance
Receiver Status Monitoring
use ;
use Duration;
// Assuming you already have a source from discovery
let options = builder.build;
let receiver = new?;
// Check connection status
if receiver.is_connected
// Poll for status changes (tally, connections, etc.)
if let Some = receiver.poll_status_change?
Examples
See the examples/ directory for complete applications:
Discovery & Monitoring
NDIlib_Find.rs- Discover NDI sources on the networkstatus_monitor.rs- Monitor receiver status and performance
Receiving
NDIlib_Recv_Audio.rs- Receive and process audio streamsNDIlib_Recv_Audio_16bpp.rs- Receive 16-bit audio samplesNDIlib_Recv_PNG.rs- Receive video and save as PNG imagesNDIlib_Recv_PTZ.rs- Control PTZ camerasconcurrent_capture.rs- Capture from multiple sources simultaneously
Sending
NDIlib_Send_Audio.rs- Send audio streamsNDIlib_Send_Video.rs- Send video streamsasync_send.rs- Async video sending with completion callbackszero_copy_send.rs- Zero-copy video transmission
Run examples with:
Platform Support
| Platform | Status | Notes |
|---|---|---|
| Windows | ✅ Fully supported | Tested on Windows 10/11 |
| Linux | ✅ Fully supported | Tested on Ubuntu 20.04+ |
| macOS | ⚠️ Experimental | Limited testing |
Contributing
Contributions are welcome! Please see our Contributing Guidelines.
License
Licensed under the Apache License, Version 2.0. See LICENSE for details.
Disclaimer
This is an unofficial community project and is not affiliated with NewTek or Vizrt.
NDI® is a registered trademark of Vizrt NDI AB.
What's New in 0.9
Version 0.9.0 is a major milestone toward 1.0, with comprehensive API stabilization and significant improvements:
🎯 API Stabilization for 1.0
- Duration-based timeouts - All timeout parameters now use
std::time::Durationinstead ofu32milliseconds - Consistent builders - Both
ReceiverandSenderhave symmetric, infallible builders - Simplified capture - 2 clear variants instead of 3 confusing ones (
capture_*andcapture_*_timeout) - Type renames -
FourCCVideoType→PixelFormat,FrameFormatType→ScanType,AudioType→AudioFormat - Forward compatibility - All enums marked
#[non_exhaustive]for future SDK versions - Cleaner naming - Removed
get_prefixes per Rust API guidelines
🚀 Zero-Copy Performance
- Zero-copy receive - New
VideoFrameRef,AudioFrameRef,MetadataFrameReftypes - Performance gain - Eliminates ~475 MB/s of memcpy at 1080p@60fps
- Lifetime-safe - Frame refs bound to
Receiverlifetime, preventing use-after-free at compile-time
🔒 Memory Safety & Correctness
- Sound async send - Fixed critical use-after-free in
send_video_async - Typed stride/size - Eliminated UB from union field access with typed
LineStrideOrSizeenum - Non-null FFI - All source pointers validated at FFI boundary
- Safe callbacks - Fixed memory leaks and races in async completion callbacks
🛠️ Ergonomics & Features
- Source caching - Thread-safe
SourceCacheeliminates ~150 lines of boilerplate - Image encoding - One-line PNG/JPEG export (optional
image-encodingfeature) - Async runtimes - Native Tokio and async-std integration (optional features)
- Audio fixed - Audio sending now actually works with proper
AudioLayoutsupport
⚠️ Breaking Changes
This release contains extensive breaking changes necessary for API stabilization. See CHANGELOG.md for the comprehensive migration guide with before/after examples.
Quick migration:
// 0.8.1
finder.wait_for_sources;
let sources = finder.get_sources?;
let frame = receiver.capture_video_blocking?;
// 0.9.0
use Duration;
finder.wait_for_sources?;
let sources = finder.sources?;
let frame = receiver.capture_video?;
See CHANGELOG.md for complete details and migration guide.