uvc 0.2.0

Safe and ergonomic wrapper around libuvc, allowing capture of webcam streams
Documentation
use glium::{implement_vertex, uniform};

use std::error::Error;
use std::sync::{Arc, Mutex};

use glium::Surface;
use uvc::{Context, Frame};

fn frame_to_raw_image(
    frame: &Frame,
) -> Result<glium::texture::RawImage2d<'static, u8>, Box<dyn Error>> {
    let new_frame = frame.to_rgb()?;
    let data = new_frame.to_bytes();

    let image = glium::texture::RawImage2d::from_raw_rgb(
        data.to_vec(),
        (new_frame.width(), new_frame.height()),
    );

    Ok(image)
}

fn callback_frame_to_image(
    frame: &Frame,
    data: &mut Arc<Mutex<Option<glium::texture::RawImage2d<u8>>>>,
) {
    let image = frame_to_raw_image(frame);
    match image {
        Err(x) => println!("{:#?}", x),
        Ok(x) => {
            let mut data = Mutex::lock(&data).unwrap();
            *data = Some(x);
        }
    }
}

fn main() {
    let ctx = Context::new().expect("Could not create context");
    let dev = ctx
        .find_device(None, None, None)
        .expect("Could not find device");

    let description = dev.description().unwrap();
    println!(
        "Found device: Bus {:03} Device {:03} : ID {:04x}:{:04x} {} ({})",
        dev.bus_number(),
        dev.device_address(),
        description.vendor_id,
        description.product_id,
        description.product.unwrap_or_else(|| "Unknown".to_owned()),
        description
            .manufacturer
            .unwrap_or_else(|| "Unknown".to_owned())
    );

    // Open multiple devices by enumerating:
    // let mut list = ctx.devices().expect("Could not get devices");
    // let dev = list.next().expect("No device available");

    let devh = dev.open().expect("Could not open device");

    let format = devh
        .get_preferred_format(|x, y| {
            if x.fps >= y.fps && x.width * x.height >= y.width * y.height {
                x
            } else {
                y
            }
        })
        .unwrap();

    println!("Best format found: {:?}", format);
    let mut streamh = devh.get_stream_handle_with_format(format).unwrap();

    println!(
            "Scanning mode: {:?}\nAuto-exposure mode: {:?}\nAuto-exposure priority: {:?}\nAbsolute exposure: {:?}\nRelative exposure: {:?}\nAboslute focus: {:?}\nRelative focus: {:?}",
            devh.scanning_mode(),
            devh.ae_mode(),
            devh.ae_priority(),
            devh.exposure_abs(),
            devh.exposure_rel(),
            devh.focus_abs(),
            devh.focus_rel(),
        );

    let frame = Arc::new(Mutex::new(None));
    let _stream = streamh
        .start_stream(callback_frame_to_image, frame.clone())
        .unwrap();

    use glium::glutin;
    let events_loop = glutin::event_loop::EventLoop::new();
    let window = glutin::window::WindowBuilder::new().with_title("Mirror");
    let context = glutin::ContextBuilder::new();
    let display = glium::Display::new(window, context, &events_loop).unwrap();

    #[derive(Copy, Clone)]
    pub struct QuadVertex {
        pos: (f32, f32),
    }

    implement_vertex!(QuadVertex, pos);

    let vertices: [QuadVertex; 4] = [
        QuadVertex { pos: (-1.0, -1.0) },
        QuadVertex { pos: (-1.0, 1.0) },
        QuadVertex { pos: (1.0, -1.0) },
        QuadVertex { pos: (1.0, 1.0) },
    ];

    let indices: [u8; 6] = [0, 1, 2, 1, 3, 2];

    let vertex_shader_source = r#"
    #version 140

    in vec2 pos;

    out vec2 v_position;

    void main() {
        v_position = (pos + 1.0)/2.0;
        gl_Position = vec4(-pos.x, -pos.y, 0.0, 1.0);
    }
    "#;

    let fragment_shader_source = r#"
    #version 140

    in vec2 v_position;

    out vec4 colour;

    uniform sampler2D u_image;

    void main() {
        vec2 pos = v_position;

        colour = texture(u_image, pos);
    }
    "#;

    let vertices = glium::VertexBuffer::new(&display, &vertices).unwrap();
    let indices = glium::IndexBuffer::new(
        &display,
        glium::index::PrimitiveType::TrianglesList,
        &indices,
    )
    .unwrap();
    let program =
        glium::Program::from_source(&display, vertex_shader_source, fragment_shader_source, None)
            .unwrap();

    let mut buffer: Option<glium::texture::SrgbTexture2d> = None;
    events_loop.run(move |event, _, control_flow| {
        if let glutin::event::Event::WindowEvent { event, .. } = event {
            if let glutin::event::WindowEvent::CloseRequested = event {
                *control_flow = glutin::event_loop::ControlFlow::Exit;
                return;
            }
        }

        let mut target = display.draw();
        target.clear_color(0.0, 0.0, 1.0, 1.0);

        let mut mutex = Mutex::lock(&frame).unwrap();

        match mutex.take() {
            None => {
                // No new frame to render
            }
            Some(image) => {
                let image = glium::texture::SrgbTexture2d::new(&display, image)
                    .expect("Could not use image");
                buffer = Some(image);
            }
        }

        if let Some(ref b) = buffer {
            let uniforms = uniform! { u_image: b };
            target
                .draw(
                    &vertices,
                    &indices,
                    &program,
                    &uniforms,
                    &Default::default(),
                )
                .unwrap();
        }

        target.finish().unwrap();
    });
}