escapi 3.0.5

Extremely Simple Capture API (ESCAPI) - simple webcam API for windows
extern crate winapi;
extern crate libc;
extern crate kernel32;

use std::ffi::CString;
use std::mem::transmute;
use kernel32::{LoadLibraryA, GetProcAddress, FreeLibrary, GetLastError};

#[repr(C)]
struct SimpleCapParams {
    buf: *mut libc::c_int,
    width: libc::c_uint,
    height: libc::c_uint,
}

#[derive(Copy, Clone)]
#[repr(C)]
struct DeviceNo(libc::c_uint);

#[allow(non_snake_case)]
pub struct Cameras {
    countCaptureDevices: extern fn() -> libc::c_int,
    initCapture: extern fn(DeviceNo, *const SimpleCapParams) -> libc::c_int,
    deinitCapture: extern fn(DeviceNo),
    doCapture: extern fn(DeviceNo) -> libc::c_int,
    isCaptureDone: extern fn(DeviceNo) -> libc::c_int,
    getCaptureDeviceName: extern fn(DeviceNo, *mut libc::c_char, libc::c_int),
    ESCAPIVersion: extern fn() -> libc::c_uint,
    #[allow(dead_code)]
    getCapturePropertyValue: extern fn(DeviceNo, libc::c_int) -> libc::c_float,
    #[allow(dead_code)]
    getCapturePropertyAuto: extern fn(DeviceNo, libc::c_int) -> libc::c_int,
    #[allow(dead_code)]
    setCaptureProperty: extern fn(DeviceNo, libc::c_int, libc::c_float, libc::c_int),
    getCaptureErrorLine: extern fn(DeviceNo) -> libc::c_int,
    getCaptureErrorCode: extern fn(DeviceNo) -> libc::c_int,
    capdll: winapi::HMODULE,
}

unsafe impl Sync for Cameras {}
unsafe impl Send for Cameras {}

impl Drop for Cameras {
    fn drop(&mut self) {
        unsafe {
            assert!(FreeLibrary(self.capdll) == 1, "failed to release escapi.dll");
        }
    }
}

impl<'a> Device<'a> {
    pub fn capture(&mut self, timeout: u32) -> Result<&[u8], Error> {
        (self.cameras.doCapture)(self.device_no);
        for _ in 0..(timeout / 5) {
            std::thread::sleep(std::time::Duration::from_millis(5));
            if (self.cameras.isCaptureDone)(self.device_no) == 1 {
                let pixels = self.pixels();
                return Ok(unsafe { std::slice::from_raw_parts(pixels.as_ptr() as *const u8, pixels.len() * 4) } );
            }
        }
        Err(CaptureTimeout)
    }
    pub fn name(&self) -> String {
        let mut v = vec![0u8; 100];
        (self.cameras.getCaptureDeviceName)(self.device_no, v.as_mut_ptr() as *mut i8, v.len() as i32);
        let null = v.iter().position(|&c| c == 0).expect("null termination character");
        v.truncate(null);
        String::from_utf8(v).expect("device name contains invalid utf8 characters")
    }
    pub fn error_code(&self) -> i32 {
        (self.cameras.getCaptureErrorCode)(self.device_no)
    }
    pub fn error_line(&self) -> i32 {
        (self.cameras.getCaptureErrorLine)(self.device_no)
    }
    fn pixels(&mut self) -> &[i32] {
        &self.buf
    }
    pub fn width(&self) -> u32 {
        self.params.width
    }
    pub fn height(&self) -> u32 {
        self.params.height
    }
}

pub struct Device<'a> {
    device_no: DeviceNo,
    buf: Box<[i32]>,
    params: Box<SimpleCapParams>,
    cameras: &'a Cameras,
}

impl<'a> Drop for Device<'a> {
    fn drop(&mut self) {
        (self.cameras.deinitCapture)(self.device_no)
    }
}

impl Cameras {
    pub fn num_devices(&self) -> usize {
        (self.countCaptureDevices)() as usize
    }
    pub fn version(&self) -> u32 {
        (self.ESCAPIVersion)()
    }
    pub fn init(&self, index: u32, wdt: u32, hgt: u32, timeout: u32) -> Result<Device, Error> {
        let mut data = vec![0; (wdt*hgt) as usize].into_boxed_slice();
        let params = Box::new(SimpleCapParams {
            width: wdt,
            height: hgt,
            buf: data.as_mut_ptr(),
        });
        if (self.initCapture)(DeviceNo(index), &*params) == 1 {
            let device = Device {
                device_no: DeviceNo(index),
                cameras: &self,
                buf: data,
                params: params,
            };
            assert!(device.error_code() == 0);
            (device.cameras.doCapture)(device.device_no);
            for _ in 0..(timeout * 100) {
                assert!(device.error_code() == 0);
                std::thread::sleep(std::time::Duration::from_millis(10));
                if (device.cameras.isCaptureDone)(device.device_no) == 1 {
                    return Ok(device);
                }
            }
            Err(OpenTimeout)
        } else {
            Err(CouldNotOpenDevice(unsafe { GetLastError() }))
        }
    }
}

#[derive(Debug)]
pub enum Error {
    CouldNotLoadEscapiDLL(libc::c_ulong),
    CouldNotOpenDevice(libc::c_ulong),
    CaptureTimeout,
    OpenTimeout,
}

impl std::fmt::Display for Error {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            CouldNotLoadEscapiDLL(i) => write!(fmt,"could not load escapi.dll, errorcode: {}", i),
            CouldNotOpenDevice(i) => write!(fmt, "could not open camera device, errorcode: {}", i),
            CaptureTimeout => write!(fmt, "timeout during image capture"),
            OpenTimeout => write!(fmt, "timeout during camera connection"),
        }
    }
}

impl std::error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            CouldNotLoadEscapiDLL(_) => "could not load escapi.dll",
            CouldNotOpenDevice(_) => "could not open camera device",
            CaptureTimeout => "timeout during image capture",
            OpenTimeout => "timeout during camera connection",
        }
    }
}

use self::Error::*;

#[allow(non_snake_case)]
pub fn init() -> Result<Cameras, Error> {
    unsafe {
        let escapi_dll = CString::new("escapi.dll").unwrap();
        let capdll = LoadLibraryA(escapi_dll.as_ptr());
        if capdll == std::ptr::null_mut() {
            return Err(CouldNotLoadEscapiDLL(GetLastError()));
        }

        let countCaptureDevices = CString::new("countCaptureDevices").unwrap();
        let initCapture = CString::new("initCapture").unwrap();
        let deinitCapture = CString::new("deinitCapture").unwrap();
        let doCapture = CString::new("doCapture").unwrap();
        let isCaptureDone = CString::new("isCaptureDone").unwrap();
        let initCOM = CString::new("initCOM").unwrap();
        let getCaptureDeviceName = CString::new("getCaptureDeviceName").unwrap();
        let ESCAPIVersion = CString::new("ESCAPIVersion").unwrap();
        let getCapturePropertyValue = CString::new("getCapturePropertyValue").unwrap();
        let getCapturePropertyAuto = CString::new("getCapturePropertyAuto").unwrap();
        let setCaptureProperty = CString::new("setCaptureProperty").unwrap();
        let getCaptureErrorLine = CString::new("getCaptureErrorLine").unwrap();
        let getCaptureErrorCode = CString::new("getCaptureErrorCode").unwrap();

        transmute::<_, extern fn()>(GetProcAddress(capdll, initCOM.as_ptr()))();

        Ok(Cameras {
            countCaptureDevices: transmute(GetProcAddress(capdll, countCaptureDevices.as_ptr())),
            initCapture: transmute(GetProcAddress(capdll, initCapture.as_ptr())),
            deinitCapture: transmute(GetProcAddress(capdll, deinitCapture.as_ptr())),
            doCapture: transmute(GetProcAddress(capdll, doCapture.as_ptr())),
            isCaptureDone: transmute(GetProcAddress(capdll, isCaptureDone.as_ptr())),
            getCaptureDeviceName: transmute(GetProcAddress(capdll, getCaptureDeviceName.as_ptr())),
            ESCAPIVersion: transmute(GetProcAddress(capdll, ESCAPIVersion.as_ptr())),
            getCapturePropertyValue: transmute(GetProcAddress(capdll, getCapturePropertyValue.as_ptr())),
            getCapturePropertyAuto: transmute(GetProcAddress(capdll, getCapturePropertyAuto.as_ptr())),
            setCaptureProperty: transmute(GetProcAddress(capdll, setCaptureProperty.as_ptr())),
            getCaptureErrorLine: transmute(GetProcAddress(capdll, getCaptureErrorLine.as_ptr())),
            getCaptureErrorCode: transmute(GetProcAddress(capdll, getCaptureErrorCode.as_ptr())),
            capdll: capdll,
        })
    }
}