Skip to main content

FrameSync

Struct FrameSync 

Source
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. However, frames returned by capture methods borrow from the FrameSync and are not Send.

§Example

let framesync = FrameSync::new(receiver)?;

// FrameSync captures always return immediately
if let Some(video) = framesync.capture_video(ScanType::Progressive)? {
    println!("Video: {}x{}", video.width(), video.height());
}

// Audio capture uses an explicit request; None means "use source value".
let audio = framesync.capture_audio(FrameSyncAudioRequest::Capture {
    sample_rate: Some(NonZeroI32::new(48_000).unwrap()),
    channels: Some(NonZeroI32::new(2).unwrap()),
    samples: NonZeroI32::new(1_024).unwrap(),
})?;
println!("Audio: {} samples", audio.num_samples());

Implementations§

Source§

impl FrameSync

Source

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)?;
Source

pub fn receiver(&self) -> &Receiver

Access the underlying receiver.

Use this to access receiver functionality like tally, PTZ, or status queries while the FrameSync is active.

Source

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.

Source

pub fn capture_video( &self, field_type: ScanType, ) -> Result<Option<FrameSyncVideoRef<'_>>>

Capture video with time-base correction.

This function always returns immediately. 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.

§Arguments
  • field_type - The desired field format. Use ScanType::Progressive for 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 frame
  • Ok(None) - The SDK returned the documented all-zero “no video yet” state
  • Err(Error::InvalidFrame(_)) - The SDK returned non-empty malformed 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...");
    }
}
Source

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 frame
  • Ok(None) - No video has been received yet
§Errors

Returns an error if the SDK returns non-empty malformed frame metadata.

Source

pub fn capture_audio( &self, request: FrameSyncAudioRequest, ) -> Result<FrameSyncAudioRef<'_>>

Capture audio with dynamic resampling.

This function always returns immediately. 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.

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, None sample rate or channels means “use the current source value”.
§Querying Input Format

Use FrameSyncAudioRequest::QueryInput 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 Error::InvalidFrame if the SDK returns malformed audio metadata, or Error::InvalidConfiguration if the request contains a negative NonZeroI32 value.

§Example
let framesync = FrameSync::new(receiver)?;

// Audio callback - called by sound card at its rate
let request = FrameSyncAudioRequest::Capture {
    sample_rate: Some(NonZeroI32::new(48_000).unwrap()),
    channels: Some(NonZeroI32::new(2).unwrap()),
    samples: NonZeroI32::new(1_024).unwrap(),
};

loop {
    // Request 1024 stereo samples at 48kHz
    let audio = framesync.capture_audio(request)?;

    // Process audio samples
    let samples = audio.data();
    println!("Got {} samples", samples.len());

    // Check for a validated query/no-source empty state
    if audio.is_empty() {
        println!("No audio source yet");
    }
}
Source

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

Returns an error if the request is invalid or the SDK returns malformed audio metadata.

Source

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§

Source§

impl Debug for FrameSync

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for FrameSync

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl Send for FrameSync

§Safety

The NDI SDK documentation states that framesync operations are thread-safe. The FrameSync struct only holds an opaque pointer returned by the SDK.

Source§

impl Sync for FrameSync

§Safety

The NDI SDK guarantees that framesync capture functions are internally synchronized and can be called concurrently from multiple threads.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.