libfunnel 0.1.0

Easy app-to-app frame sharing using PipeWire
Documentation

libfunnel-rs

Rust bindings for libfunnel - a library to make creating PipeWire video streams easy, using zero-copy DMA-BUF frame sharing. "Spout2 / Syphon, but for Linux".

Status

  • Bindings for the core libfunnel API
  • Bindings for the vulkan integration
  • Bindings for the egl integration
  • Bindings for the gbm integration
  • Example using ash
  • Example using wgpu (requires explicit wait semaphores)

Documentation

See libfunnel Documentation for the upstream docs. The documentation for the bindings are available on docs.rs.

Specifically the funnel_mode and Buffer synchronization guide pages are very helpful to read.

Installation

Run cargo add libfunnel to add it to your Cargo.toml.

[dependencies]
libfunnel = "0.1.0"

You'll also need the C libfunnel library installed on your system.

Usages

Here's an rough outline of how you would integrate this library into the draw loop of a vulkan app. A fully working example can be found in ./examples.

use libfunnel::*;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create funnel context and stream
    let ctx = FunnelContext::new()?;
    let mut stream = ctx.create_stream(c"MyStream")?;

    // Initialize Vulkan integration
    unsafe {
        stream.init_vulkan(vk_instance, vk_physical_device, vk_device)?;
    }
    stream.vk_set_usage(VK_IMAGE_USAGE_TRANSFER_DST_BIT)?;
    stream.vk_add_format(VK_FORMAT_B8G8R8A8_SRGB, true, VK_FORMAT_FEATURE_BLIT_DST_BIT)?;

    // Configure stream
    stream.set_size(1920, 1080)?;
    stream.set_mode(funnel_mode::FUNNEL_ASYNC)?;
    stream.set_rate(funnel_fraction::VARIABLE, 1.into(), 144.into())?;
    stream.configure()?;
    stream.start()?;

    // Render loop
    loop {
        // Try to get a funnel buffer to stream the frame
        let mut funnel_buffer = stream.dequeue()?;

        // Render your application...

        // If we have a funnel buffer, copy to it
        if let Some(buffer) = &mut funnel_buffer {
            let vk_image = buffer.vk_get_image()?;
            let (acquire_sema, release_sema) = unsafe { buffer.vk_get_semaphores()? };
            let fence = unsafe { buffer.vk_get_fence()? };

            // Submit GPU commands with synchronization:
            //   wait_semaphores: [..., acquire_sema]
            //   signal_semaphores: [..., release_sema]
            //   fence: fence
        }

        // Enqueue buffer back to stream for PipeWire to send
        if let Some(buffer) = funnel_buffer {
            unsafe { stream.enqueue(buffer)?; }
        }
    }
}