#tokio #v4l2 #linux #v4l #async

tokio-linux-video

使用 tokio 与 Linux V4L2 设备交互

2 个版本

0.1.1 2023 年 8 月 5 日
0.1.0 2023 年 5 月 22 日

#1013 in Unix API

26 每月下载量

MIT 许可证

240KB
6.5K SLoC

为 Rust 提供的 Tokio Linux V4L2 API

github crate docs MIT CI

这个 crate 的目的是在不限制任何情况下提供对 Linux V4L2 API 的访问。

主要设计目标是安全性和开销之间的最佳平衡。实现方式更接近系统调用,接口类型封装内核类型以避免不必要的复制。

次要目标是提供原始 API 的完整功能集。

这最终是我谦虚地尝试正确做事的一种方式。

crates

使用示例

枚举设备

use tokio_linux_video::Device;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let mut devs = Device::list().await?;

    while let Some(path) = devs.fetch_next().await? {
        let dev = Device::open(&path).await?;

        let caps = dev.capabilities().await?;

        println!("path: {}, {caps}", path.display());
    }

    Ok(())
}

获取能力和控件

use tokio_linux_video::Device;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let dev = Device::open("/dev/video0").await?;

    let caps = dev.capabilities().await?;

    println!("Capabilities: {caps}");

    println!("Controls:");
    let mut controls = dev.controls(None);

    while let Some(ctrl) = controls.fetch_next().await? {
        println!("  {ctrl}");

        if let Some(mut items) = dev.control_items(&ctrl) {
            while let Some(item) = items.fetch_next().await? {
                println!("    {item}");
            }
        }
    }

    Ok(())
}

获取支持的格式

use tokio_linux_video::{types::BufferType, Device};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let dev = Device::open("/dev/video0").await?;

    let caps = dev.capabilities().await?;

    for type_ in BufferType::ALL {
        if type_.is_supported(caps.capabilities()) {
            println!("{type_} formats:");
            let mut fmts = dev.formats(type_);

            if let Some(fmt) = fmts.fetch_next().await? {
                println!("  {fmt}");

                if type_.content().is_video() {
                    let mut sizes = dev.sizes(fmt.pixel_format());

                    while let Some(size) = sizes.fetch_next().await? {
                        println!("    {size}");

                        for size in size.sizes() {
                            println!("      {size}");
                            let mut intervals = dev.intervals(fmt.pixel_format(), size.width(), size.height());

                            while let Some(interval) = intervals.fetch_next().await? {
                                println!("        {interval}");
                            }
                        }
                    }
                }
            }
        }
    }

    Ok(())
}

使用控件

use tokio_linux_video::{types::*, Device};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let dev = Device::open("/dev/video0").await?;

    // Get control from device by identifier
    let contrast_ctrl = dev.control(CtrlId::Contrast).await?;

    // Create a value for control
    let mut contrast = Value::from(&contrast_ctrl);

    // Get control value from device
    dev.get_control(&mut contrast).await?;

    // Get reference to value data
    let contrast_value = contrast.try_ref::<i32>().unwrap();

    println!("Current contrast: {contrast_value:?}");

    // Set new value by reference
    *contrast.try_mut::<i32>().unwrap() = contrast_value + 10;

    println!("Updated contrast: {:?}", contrast.try_ref::<i32>().unwrap());

    // Set new control value to device
    dev.set_control(&contrast).await?;

    Ok(())
}

捕获视频数据

use tokio_linux_video::{types::*, Device};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let dev = Device::open("/dev/video0").await?;

    // Get current format
    let mut fmt = dev.format(BufferType::VideoCapture).await?;
    println!("  {fmt}");

    // Start video capture stream
    let stream = dev.stream::<In, Mmap>(ContentType::Video, 4)?;

    let mut i = 0;
    while let Ok(buffer) = stream.next().await {
        let buffer = buffer.lock();
        println!("#{i} {buffer}");

        // Get reference to frame buffer contents
        let _data: &[u8] = buffer.as_ref();

        i += 1;
        if i > 30 {
            break;
        }
    }

    Ok(())
}

输出视频数据

use tokio_linux_video::{types::*, Device};

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let dev = Device::open("/dev/video0").await?;

    // Get current format
    let mut fmt = dev.format(BufferType::VideoOutput).await?;
    println!("  {fmt}");

    // Start video output stream
    let stream = dev.stream::<Out, Mmap>(ContentType::Video, 4)?;

    let mut i = 0;
    while let Ok(mut buffer) = stream.next().await {
        let mut buffer = buffer.lock();
        println!("#{i} {buffer}");

        // Get reference to frame buffer contents
        let _data: &mut [u8] = buffer.as_mut();

        i += 1;
        if i > 30 {
            break;
        }
    }

    Ok(())
}

依赖关系

~7–18MB
~229K SLoC