diff --git a/src/graphics_imgui.rs b/src/graphics_imgui.rs index 6dc77e772e..bd826e169e 100644 --- a/src/graphics_imgui.rs +++ b/src/graphics_imgui.rs @@ -26,41 +26,13 @@ use std::os::unix::io::AsRawFd; use std::path::Path; use std::sync::Arc; use std::time::{Instant, Duration}; -use webcam::write_frame_to_webcam; +use webcam::{WebcamWriter}; use std::error::Error; pub fn run(capture_provider: Arc, calibration_data: k4a_sys::k4a_calibration_t) { - //let mut file = File::create("/dev/video7").expect("write"); - let path = Path::new("/dev/video0"); - let mut file = OpenOptions::new() - //.append(true) - //.create(true) - .read(true) - .truncate(true) - .write(true) - .open(path) - .expect("file open"); - //let mut contents = Vec::new(); - //file.read_to_end(&mut contents).expect("read"); - - //file.set_len(0 as u64).expect("set length"); - //file.set_len(100).expect("set length"); - - //let mut mmap_read = unsafe { Mmap::map(&file).expect("mmmap") }; - //let mut mmap = unsafe { mmap_read.make_mut().expect("making mut") }; - //let mut mmap = unsafe { MmapMut::map_mut(&file).expect("mmmap") }; - - /*let size = 1280*720*3 * 10; - let size = 640*480*3; - let file_descriptor = file.as_raw_fd(); - println!("File descriptor: {}", file_descriptor); - let options = [ - MapReadable, - MapWritable, - MapFd(file_descriptor) - ]; - let mut mmap = MemoryMap::new(size as usize, &options).expect("mmap");*/ + let mut webcam_writer = WebcamWriter::open_file("/dev/video0", 1280, 720, 3) + .expect("should be able to create webcamwriter"); let sdl_context = sdl2::init().unwrap(); let video = sdl_context.video().unwrap(); @@ -244,12 +216,12 @@ pub fn run(capture_provider: Arc, calibration_data: k4a_sys::k4 }); } + webcam_writer.write_current_frame_to_file(texture.texture_id()) + .expect("should write"); //let change_delta = last_frame - last_change; //if change_delta > Duration::from_millis(5_000) { //} - //write_frame_to_webcam(&mut file, texture.texture_id()); - write_frame_to_webcam(&mut file, texture.texture_id()); /*if change_delta > Duration::from_millis(5000) { colorization_strategy = match colorization_strategy { diff --git a/src/webcam.rs b/src/webcam.rs index afd7c93f47..d523c5a0a0 100644 --- a/src/webcam.rs +++ b/src/webcam.rs @@ -1,189 +1,107 @@ use gl::types::*; use gl; use std::ffi::c_void; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::io::Write; -use opengl_wrapper::gl_get_error; +use opengl_wrapper::{gl_get_error, OpenGlError}; use image::imageops::FilterType; use memmap2::MmapMut; use mmap::MemoryMap; +use std::path::Path; +use std::{io, fmt}; +use std::fmt::Formatter; -pub fn write_frame_to_webcam(file: &mut File, texture_id: GLuint) { +/// WebcamWriter uses Webcamoid's akvcam kernel driver to stream output to a /dev/video* +/// file. Their wiki contains all of the setup instructions, and I'm checked in some example +/// configs into this repo. +/// +/// https://github.com/webcamoid/akvcam +pub struct WebcamWriter { + /// Internal byte buffer to pull from OpenGL texture and flush to file. + buffer: Vec, + buffer_size: usize, + /// File to flush raw RGB24 bytes to. + file: File, +} - //let mut buffer: [u8; 15_360_000] = [0; 15_360_000]; - /*let mut buffer = Vec::with_capacity(15_360_000); - for _ in 0..15_360_000 { - buffer.push(0); - }*/ - //let size = 800 * 800 * 4; +impl WebcamWriter { - let width = 1280; - let height = 720; - let depth = 3; + /// Create a WebcamWriter that outputs to the supplied file. + /// The file handle is created and owned by WebcamWriter. + pub fn open_file(filename: &str, width: usize, height: usize, bit_depth: usize) + -> Result + { + let path = Path::new(filename); + let file = OpenOptions::new() + .read(true) + .truncate(true) + .write(true) + .open(path)?; - let size = width * height * depth; + let buffer_size = width * height * bit_depth; - //let size = 15_360_000; - let mut buffer = Vec::with_capacity(size); - buffer.resize(size, 0); - /*for _ in 0 .. size { - buffer.push(0); - }*/ - let mut typed_buffer = buffer.as_mut_ptr() as *mut c_void; + let mut buffer = Vec::with_capacity(buffer_size); + buffer.resize(buffer_size, 0); - unsafe { - // > 600 * 800 - // 480000 - // > 600 * 800 * 4 - // 1920000 - /* - const width : usize = 640; - const height : usize = 480; - const bytes_per_pixel : usize = 4; - const size : usize = width * height * bytes_per_pixel; - let mut buffer: [u8; bytes_per_pixel] = [0; bytes_per_pixel]; - let mut typed_buffer = buffer.as_mut_ptr() as *mut c_void; - gl::ReadnPixels(0, 0, width as i32, height as i32, gl::RGB, gl::BYTE, size as i32, typed_buffer); - println!("Write buffer to camera"); - file.write(&buffer).expect("work"); - */ - // width = 800 - // height = 800 - // rgb8 = 24 - // size = 15_360_000 - println!("Declare memory size"); + Ok(Self { + buffer, + buffer_size, + file, + }) + } - println!("Read frame into buffer"); - //gl::Viewport(0, 0, 640, 480); - //gl::ReadPixels(0, 0, 640, 480, gl::RGB, gl::UNSIGNED_BYTE, typed_buffer); - - println!("Getting texture: {}", texture_id); - - // InvalidEnum - //gl::BindTexture(gl::TEXTURE_2D, texture_id); - //gl::GetnTexImage(gl::TEXTURE_2D, 0, gl::RGB8, gl::UNSIGNED_BYTE, 1_228_800, typed_buffer); - - println!("Bind"); - gl::BindTexture(gl::TEXTURE_2D, texture_id); - println!("getn"); - //gl::GetnTexImage(gl::TEXTURE_2D, 0, gl::RGB, gl::UNSIGNED_BYTE, 15_360_000, typed_buffer); - //gl::GetnTexImage(gl::TEXTURE_2D, 0, gl::RGB, gl::UNSIGNED_BYTE, 15_360_000, typed_buffer); // WORKS - gl::GetnTexImage(gl::TEXTURE_2D, 0, gl::RGB, gl::UNSIGNED_BYTE, size as i32, typed_buffer); - - println!("load from memory"); - - gl_get_error().unwrap(); - - println!("Write buffer to camera"); - - gl::BindTexture(gl::TEXTURE_2D, 0); - - //let img = image::load_from_memory(&buffer).unwrap(); - //let resized = img.resize(640, 480, FilterType::Nearest); - //let bytes = resized.to_bytes(); - - println!("Buffer width before: {}", buffer.len()); - /*let mut resizer = resize::new( - 800, - 800, - 640, - 480, - resize::Pixel::RGB24, - resize::Type::Lanczos3); - - let capacity = 640 * 480 * 3; - let mut dst_buffer = Vec::with_capacity(capacity); - dst_buffer.resize(capacity, 0);*/ - /*for _ in 0 .. capacity { - dst_buffer.push(0); - }*/ - - //resizer.resize(&buffer, &mut dst_buffer); - - /*println!("Buffer width after: {}", dst_buffer.len()); - - println!("Values: {}, {}, {}, {}, {}", - dst_buffer.get(0).unwrap(), - dst_buffer.get(10).unwrap(), - dst_buffer.get(100).unwrap(), - dst_buffer.get(5000).unwrap(), - dst_buffer.get(10000).unwrap());*/ - - //let width = 640 * 480 * 3; - //buffer.truncate(width); - //println!("Buffer width after: {}", buffer.len()); - - //gl::GetTextureImage(texture_id, 0, gl::RGB, gl::UNSIGNED_BYTE, 1_228_800, typed_buffer); - - /*let image_size = 640 * 480 * 3; - - println!("Writing mmap of size {}", file.len()); - println!("Writing bytes: {}", image_size); - - let mut image_buf = Vec::with_capacity(image_size); - image_buf.resize(image_size, 255); - - let typed_buffer = image_buf.as_mut_ptr(); + /// Grab the current texture held by `texture_id` and immediately write it to file. + pub fn write_current_frame_to_file(&mut self, texture_id: GLuint) -> Result<(), WebcamError> { + let mut typed_buffer = self.buffer.as_mut_ptr() as *mut c_void; unsafe { - std::ptr::copy_nonoverlapping::(typed_buffer, - file.data(), - image_size as usize); - }*/ + gl::BindTexture(gl::TEXTURE_2D, texture_id); + gl::GetnTexImage(gl::TEXTURE_2D, 0, gl::RGB, gl::UNSIGNED_BYTE, self.buffer_size as i32, typed_buffer); - /*let width = 1280; - let height = 720; - let depth = 3; - let size = width * height * depth; - let mut buffer= Vec::with_capacity(width * height); - buffer.resize(size, 255); + gl_get_error()?; - let use_width = true; - let mut i = 0; - for y in 0 .. height { - for x in 0 .. width { - /* - > approx_bytes = 2763000 - 1843500 - 919500 - > approx_bytes/3 - 306500 - > approx_bytes/3/720 - 425.69444444444446 - */ - // y < 720 is the height of the screen. Duh. - /*buffer[i + 0] = 0; - buffer[i + 1] = 0; - buffer[i + 2] = 0; - // byte # 1_843_500 is super close to the start. - if i < 1_843_500 { - buffer[i + 0] = 255; - } - // byte # 2_763_000 is super close to the end. - if i < 2_763_000 { - buffer[i + 1] = 255; - } - if i < 3_000_000 { - buffer[i + 2] = 255; - }*/ - if use_width { - // Width - buffer[i] = (x as f32 / width as f32 * 255.0) as u8; - buffer[i + 1] = (x as f32 / width as f32 * 255.0) as u8; - buffer[i + 2] = (x as f32 / width as f32 * 255.0) as u8; - } else { - // Height - buffer[i] = (y as f32 / height as f32 * 255.0) as u8; - buffer[i + 1] = (y as f32 / height as f32 * 255.0) as u8; - buffer[i + 2] = (y as f32 / height as f32 * 255.0) as u8; - } - i += depth; - } - }*/ + gl::BindTexture(gl::TEXTURE_2D, 0); + } - //(&mut file[..]).write_all(&buffer[0..1280*720*3]).expect("work"); - //(&mut file[..]).write_all(&buffer[0..1280*720*3]).expect("work"); - file.write_all(&buffer).expect("should write"); - file.flush().expect("flush"); + self.file.write_all(&self.buffer)?; + self.file.flush()?; + Ok(()) } } + +/// Errors from this module. +#[derive(Debug)] +pub enum WebcamError { + IoError(io::Error), + OpenGlError(OpenGlError), +} + +impl fmt::Display for WebcamError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let explain = match self { + WebcamError::IoError(e) => format!("IoError: {:?}", e), + WebcamError::OpenGlError(e) => format!("OpenGL error: {:?}", e), + }; + write!(f, "{}", explain) + } +} + +impl std::error::Error for WebcamError { + fn source(&self) -> Option<&(dyn std::error::Error +'static)> { + None // Generic error, no backtrace. + } +} + +impl From for WebcamError { + fn from(error: io::Error) -> Self { + WebcamError::IoError(error) + } +} + +impl From for WebcamError { + fn from(error: OpenGlError) -> Self { + WebcamError::OpenGlError(error) + } +} +