Skip to main content

Module framesync

Module framesync 

Source
Expand description

Frame synchronization for clock-corrected video/audio capture.

The FrameSync type provides a “pull” interface for receiving NDI streams with automatic time-base correction. This transforms the NDI SDK’s push-based capture model into a pull model suitable for:

  • Video playback synced to GPU v-sync
  • Audio playback synced to sound card clock
  • Multi-source mixing with a common output clock

§When to Use FrameSync vs Raw Capture

Use CaseRaw ReceiverFrameSync
Recording (preserving timing)
Playback to GPU
Playback to sound card
Multi-source mixing
Analysis/processing pipeline
Low-latency monitoring

§Why FrameSync Exists

Computer clocks drift. The NDI SDK documentation explains:

Computer clocks rely on crystals which while all rated for the same frequency are still not exact. If your sending computer has an audio clock that it “thinks” is 48000Hz, to the receiver computer that has a different audio clock this might be 48001Hz or 47998Hz.

Without time-base correction, this causes:

  • Video jitter: When sender/receiver clocks are nearly aligned, naive frame buffering causes visible jitter as frames occasionally repeat or skip.
  • Audio drift: Accumulated clock difference causes audio to fall out of sync or cause glitches as the receiver runs out of or accumulates too many samples.

FrameSync solves these by:

  • Using hysteresis-based video timing to determine optimal frame repeat/skip points
  • Dynamically resampling audio with high-order filters to track clock differences

§Example

use grafton_ndi::{
    NDI, Finder, FinderOptions, ReceiverOptions, Receiver, FrameSync,
    FrameSyncAudioRequest, ScanType,
};
use std::{num::NonZeroI32, time::Duration};

fn main() -> Result<(), grafton_ndi::Error> {
    let ndi = NDI::new()?;
    let finder = Finder::new(&ndi, &FinderOptions::default())?;
    finder.wait_for_sources(Duration::from_secs(1))?;
    let sources = finder.current_sources()?;

    let options = ReceiverOptions::builder(sources[0].clone()).build();
    let receiver = Receiver::new(&ndi, &options)?;

    // Create frame-sync from receiver
    let framesync = FrameSync::new(receiver)?;

    // Capture video synced to your output timing
    if let Some(frame) = framesync.capture_video(ScanType::Progressive)? {
        println!("{}x{} frame", frame.width(), frame.height());
    }

    // Capture audio at your sound card's rate
    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());

    Ok(())
}

Structs§

FrameSync
Frame synchronizer for clock-corrected capture.

Enums§

FrameSyncAudioRequest
Explicit audio operation for FrameSync::capture_audio.

Type Aliases§

FrameSyncAudioRef
A zero-copy borrowed audio frame from a FrameSync capture.
FrameSyncVideoRef
A zero-copy borrowed video frame from a FrameSync capture.