pub struct FrameSync { /* private fields */ }Expand description
Frame synchronizer for clock-corrected capture.
Converts push-based NDI streams into pull-based capture with automatic time-base correction and dynamic audio resampling.
§Ownership
FrameSync takes ownership of the Receiver (similar to how BufWriter
wraps a Write). Use receiver() to access the underlying
receiver for tally, PTZ, or status operations. Use
into_receiver() to recover the receiver when done.
§Thread Safety
FrameSync is Send + Sync like Receiver, as the underlying NDI SDK
frame-sync functions are thread-safe. Frames returned by capture methods
borrow from the FrameSync and are neither Send nor Sync. Convert them
to owned frames to transfer ownership to another thread. Immutable video
plane/row views can instead be moved or shared with scoped workers while
the frame guard stays on its capture thread until those workers finish.
Raw video capture through the underlying receiver is disabled while this
FrameSync exists. Self::into_receiver destroys the synchronizer and
restores raw video capture before returning the receiver.
§Example
let framesync = FrameSync::new(receiver)?;
// Sample available data without waiting for a new network frame
if let Some(video) = framesync.capture_video(ScanType::Progressive)? {
println!("Video: {}x{}", video.width(), video.height());
}
// Audio capture fixes the output dimensions before entering the SDK.
let audio = framesync.capture_audio(FrameSyncAudioRequest::capture_with(
Some(48_000), 2, 1_024,
)?)?;
println!("Audio: {} samples", audio.num_samples());Implementations§
Source§impl FrameSync
impl FrameSync
Sourcepub fn new(receiver: Receiver) -> Result<Self>
pub fn new(receiver: Receiver) -> Result<Self>
Create a frame synchronizer from a receiver.
Takes ownership of the receiver. Use receiver()
to access the underlying receiver for tally, PTZ, or status operations.
Use into_receiver() to recover the receiver.
§Errors
Returns an error if the NDI SDK fails to create the frame-sync instance.
§Example
let receiver = Receiver::new(&ndi, &options)?;
let framesync = FrameSync::new(receiver)?;Sourcepub fn receiver(&self) -> &Receiver<FrameSyncAttached>
pub fn receiver(&self) -> &Receiver<FrameSyncAttached>
Access the underlying receiver.
Use this to access receiver functionality like tally, PTZ, or status
queries while the FrameSync is active. Tally control publishes state
upstream; it does not query incoming tally. This receiver mode has no
raw-video method; use FrameSync capture methods or
recover the receiver with Self::into_receiver first.
Sourcepub fn into_receiver(self) -> Receiver
pub fn into_receiver(self) -> Receiver
Consume the FrameSync and recover the underlying Receiver.
This destroys the frame synchronizer and returns the receiver for continued use with raw capture or for creating a new FrameSync.
Sourcepub fn capture_video(
&self,
field_type: ScanType,
) -> Result<Option<FrameSyncVideoRef<'_>>>
pub fn capture_video( &self, field_type: ScanType, ) -> Result<Option<FrameSyncVideoRef<'_>>>
Capture video with time-base correction.
This does not wait for a new network frame. It returns the best frame for the current output timing, handling clock drift and jitter automatically. The same frame may be returned multiple times when capture rate exceeds source frame rate. Wrapper synchronization and SDK processing still take time.
§Arguments
field_type- The desired field format. UseScanType::Progressivefor most applications. For interlaced output, use the appropriate field type to maintain correct field ordering.
§Returns
Ok(Some(FrameSyncVideoRef))- A validated zero-copy reference to the captured frameOk(None)- The SDK returned the documented all-zero “no video yet” stateErr(_)- The SDK returned an unsupported format or malformed frame descriptor, including invalid dimensions, stride, buffers, or metadata
§Example
let framesync = FrameSync::new(receiver)?;
// Capture loop - call at your output frame rate
loop {
if let Some(frame) = framesync.capture_video(ScanType::Progressive)? {
// Process/display frame
println!("{}x{} @ {}/{} fps",
frame.width(), frame.height(),
frame.frame_rate_n(), frame.frame_rate_d());
} else {
// No video received yet - display placeholder
println!("Waiting for video...");
}
}Sourcepub fn capture_video_owned(
&self,
field_type: ScanType,
) -> Result<Option<VideoFrame>>
pub fn capture_video_owned( &self, field_type: ScanType, ) -> Result<Option<VideoFrame>>
Capture video and convert to an owned frame.
This is a convenience method that captures video and immediately converts
it to an owned VideoFrame that can be sent across threads.
§Arguments
field_type- The desired field format.
§Returns
Ok(Some(VideoFrame))- An owned copy of the captured frameOk(None)- No video has been received yet
§Errors
Propagates format and frame-validation errors from Self::capture_video
or conversion to an owned frame.
Sourcepub fn capture_audio(
&self,
request: FrameSyncAudioRequest,
) -> Result<FrameSyncAudioRef<'_>>
pub fn capture_audio( &self, request: FrameSyncAudioRequest, ) -> Result<FrameSyncAudioRef<'_>>
Capture audio with dynamic resampling.
This does not wait for new network audio. Capture requests ask the NDI SDK to resample incoming audio to match the requested sample rate, channel count, and sample count. Query requests return the current input format without requesting samples. Wrapper synchronization and resampling still take time.
Call this at the rate you need audio - the SDK will adapt the incoming signal to match your output timing using dynamic audio sampling.
§Arguments
request- Explicit capture or query operation. For capture requests,Nonesample rate means “use the current source rate”; channels are explicit.
§Querying Input Format
Use FrameSyncAudioRequest::query_input to query the current input
format without capturing samples. The returned frame contains the
input’s sample rate and channel count, or a validated empty no-source
state when no audio format has been received yet.
§Errors
Returns frame-validation errors if the SDK returns malformed audio dimensions, sample layout, buffers, or metadata. Request dimensions were already validated at construction.
§Example
let framesync = FrameSync::new(receiver)?;
// Audio callback - called by sound card at its rate
let request = FrameSyncAudioRequest::capture_with(Some(48_000), 2, 1_024)?;
loop {
// Request 1024 stereo samples at 48kHz
let audio = framesync.capture_audio(request)?;
// Process each channel without reading inter-channel padding.
for samples in audio.channels() {
println!("Got {} samples in a channel", samples.len());
}
// Check for a validated query/no-source empty state
if audio.is_empty() {
println!("No audio source yet");
}
}Sourcepub fn capture_audio_owned(
&self,
request: FrameSyncAudioRequest,
) -> Result<Option<AudioFrame>>
pub fn capture_audio_owned( &self, request: FrameSyncAudioRequest, ) -> Result<Option<AudioFrame>>
Capture audio and convert to an owned frame.
This is a convenience method that captures audio and immediately converts
it to an owned AudioFrame that can be sent across threads. Query or
no-source states that contain no sample buffer return Ok(None).
§Arguments
request- Explicit capture or query operation.
§Errors
Propagates returned-frame validation and owned-copy errors from
Self::capture_audio and AudioRef::to_owned.
Sourcepub fn audio_queue_depth(&self) -> i32
pub fn audio_queue_depth(&self) -> i32
Query the current audio queue depth.
Returns the approximate number of audio samples currently buffered. This can be useful when using an inaccurate timer for audio playback.
Note: This value may change immediately after being read as new
samples are continuously received. For most applications, simply call
capture_audio at your desired rate and let the SDK handle timing.
§Example
let framesync = FrameSync::new(receiver)?;
// Check available samples before capture
let available = framesync.audio_queue_depth();
println!("Audio samples available: {}", available);Trait Implementations§
impl Send for FrameSync
§Safety
The NDI SDK documentation states that framesync operations are thread-safe. The FrameSync owns its receiver, keeping the SDK runtime alive. Borrowed frame guards cannot be moved between threads and keep this owner borrowed until their buffers are freed; destruction requires exclusive ownership.
impl Sync for FrameSync
§Safety
The NDI SDK guarantees that framesync capture functions are internally
synchronized and can be called concurrently from multiple threads. Receiver
attachment disables raw video capture until the synchronizer is destroyed,
including when the receiver is accessed through FrameSync::receiver.