mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-15 06:12:18 +00:00
much more efficient and cleaned up WebcamWriter
This commit is contained in:
1 parent
e29d003973
commit
e2671e4380
2 files changed
+90
-200
No files matched your search
+5
-33
@@ -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<CaptureProvider>, 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<CaptureProvider>, 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 {
|
||||
|
||||
+85
-167
@@ -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<u8>,
|
||||
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<Self, io::Error>
|
||||
{
|
||||
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::<u8>(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<io::Error> for WebcamError {
|
||||
fn from(error: io::Error) -> Self {
|
||||
WebcamError::IoError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OpenGlError> for WebcamError {
|
||||
fn from(error: OpenGlError) -> Self {
|
||||
WebcamError::OpenGlError(error)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user