intial commit
This commit is contained in:
commit
ce321a6e33
10
CMakeLists.txt
Normal file
10
CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
cmake_minimum_required(VERSION 2.4)
|
||||
project(videosender)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
add_executable(videosender main.cpp webcam.cpp jpeg_img.cpp)
|
||||
|
||||
target_link_libraries( videosender -ljpeg )
|
||||
add_definitions("-s -Wall")
|
||||
|
||||
install(TARGETS videosender RUNTIME DESTINATION bin)
|
43
image.h
Normal file
43
image.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
class Image
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<unsigned char> data = nullptr;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
uint8_t channels;
|
||||
const uint32_t size()
|
||||
{
|
||||
return width*height*channels;
|
||||
}
|
||||
void save(const char* filename)
|
||||
{
|
||||
std::ofstream outfile(filename);
|
||||
outfile.write((char *) data.get(), size());
|
||||
outfile.close();
|
||||
}
|
||||
bool open(const char* filename, const uint32_t widthI, const uint32_t heightI, const uint32_t channelsI)
|
||||
{
|
||||
height = heightI;
|
||||
width = widthI;
|
||||
channels = channelsI;
|
||||
std::ifstream infile( filename, std::ios::binary);
|
||||
if(!infile.is_open() || infile.bad()) return false;
|
||||
|
||||
infile.seekg (0, infile.end);
|
||||
uint32_t filelength = infile.tellg();
|
||||
infile.seekg (0, infile.beg);
|
||||
if(filelength != size()) return false;
|
||||
|
||||
data = std::shared_ptr<unsigned char>(new unsigned char[filelength]);
|
||||
infile.read((char*)data.get(), filelength);
|
||||
|
||||
infile.close();
|
||||
return true;
|
||||
}
|
||||
};
|
104
jpeg_img.cpp
Normal file
104
jpeg_img.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
#include "jpeg_img.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <jpeglib.h>
|
||||
#include <jerror.h>
|
||||
|
||||
Image decompressJpegImage(const unsigned char* buffer, size_t size)
|
||||
{
|
||||
struct jpeg_decompress_struct info; //for our jpeg info
|
||||
struct jpeg_error_mgr err; //the error handler
|
||||
|
||||
info.err = jpeg_std_error( &err );
|
||||
jpeg_create_decompress( &info ); //fills info structure
|
||||
|
||||
jpeg_mem_src(&info, buffer, size);
|
||||
jpeg_read_header( &info, true );
|
||||
|
||||
jpeg_start_decompress( &info );
|
||||
|
||||
Image image;
|
||||
|
||||
image.width = info.output_width;
|
||||
image.height = info.output_height;
|
||||
image.channels = info.num_components; // 3 = RGB, 4 = RGBA
|
||||
|
||||
// read RGB(A) scanlines one at a time into jdata[]
|
||||
image.data = std::shared_ptr<unsigned char>(new unsigned char[image.size()]);
|
||||
unsigned char* rowptr;
|
||||
while(info.output_scanline < image.height)
|
||||
{
|
||||
rowptr = image.data.get() + info.output_scanline * image.width * image.channels;
|
||||
jpeg_read_scanlines( &info, &rowptr, 1 );
|
||||
}
|
||||
|
||||
jpeg_finish_decompress( &info );
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
Image decompressJpegImage(FILE *file)
|
||||
{
|
||||
struct jpeg_decompress_struct info; //for our jpeg info
|
||||
struct jpeg_error_mgr err; //the error handler
|
||||
|
||||
info.err = jpeg_std_error( &err );
|
||||
jpeg_create_decompress( &info ); //fills info structure
|
||||
|
||||
jpeg_stdio_src( &info, file );
|
||||
jpeg_read_header( &info, true );
|
||||
|
||||
jpeg_start_decompress( &info );
|
||||
|
||||
Image image;
|
||||
|
||||
image.width = info.output_width;
|
||||
image.height = info.output_height;
|
||||
image.channels = info.num_components; // 3 = RGB, 4 = RGBA
|
||||
|
||||
// read RGB(A) scanlines one at a time into jdata[]
|
||||
image.data = std::shared_ptr<unsigned char>(new unsigned char[image.size()]);
|
||||
unsigned char* rowptr;
|
||||
while ( info.output_scanline < image.height )
|
||||
{
|
||||
rowptr = image.data.get() + info.output_scanline * image.width * image.channels;
|
||||
jpeg_read_scanlines( &info, &rowptr, 1 );
|
||||
}
|
||||
|
||||
jpeg_finish_decompress( &info );
|
||||
|
||||
fclose( file );
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
void compressJpegImage(FILE *file, Image *image)
|
||||
{
|
||||
struct jpeg_compress_struct info;
|
||||
struct jpeg_error_mgr err;
|
||||
|
||||
info.err = jpeg_std_error( &err );
|
||||
|
||||
jpeg_create_compress(&info);
|
||||
jpeg_stdio_dest(&info, file);
|
||||
|
||||
/* Setting the parameters of the output file here */
|
||||
info.image_width = image->width;
|
||||
info.image_height= image->height;
|
||||
info.input_components = image->channels;
|
||||
info.in_color_space = JCS_RGB;
|
||||
|
||||
jpeg_set_defaults( &info );
|
||||
/* Now do the compression .. */
|
||||
jpeg_start_compress( &info, TRUE );
|
||||
unsigned char* rowptr;
|
||||
while ( info.next_scanline < image->height )
|
||||
{
|
||||
rowptr = image->data.get() + info.next_scanline * image->width * image->channels;
|
||||
jpeg_write_scanlines( &info, &rowptr, 1 );
|
||||
}
|
||||
|
||||
jpeg_finish_compress( &info );
|
||||
|
||||
jpeg_destroy_compress( &info );
|
||||
}
|
9
jpeg_img.h
Normal file
9
jpeg_img.h
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "image.h"
|
||||
|
||||
Image decompressJpegImage(const unsigned char* buffer, size_t size);
|
||||
|
||||
Image decompressJpegImage(FILE *file);
|
||||
|
||||
void compressJpegImage(FILE *file, Image *image);
|
71
main.cpp
Normal file
71
main.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <cstdint>
|
||||
|
||||
#include "image.h"
|
||||
#include "jpeg_img.h"
|
||||
#include "webcam.h"
|
||||
#include "options.h"
|
||||
|
||||
|
||||
void overlayImage(Image& target, Image& tool)
|
||||
{
|
||||
if( target.size() != tool.size() || target.width != tool.width || target.channels != 3 )
|
||||
throw std::runtime_error("Overlay Image needs to be same size as target");
|
||||
else
|
||||
{
|
||||
for(uint32_t i = 0; i < target.size(); i+=3)
|
||||
{
|
||||
if(tool.data.get()[i] != 0xFF || tool.data.get()[i+1] != 0x00 || tool.data.get()[i+2] != 0xFF)
|
||||
{
|
||||
target.data.get()[i] = tool.data.get()[i];
|
||||
target.data.get()[i+1] = tool.data.get()[i+1];
|
||||
target.data.get()[i+2] = tool.data.get()[i+2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printUsage()
|
||||
{
|
||||
std::cout<<"useage:\nvideosender [video device] [ip] [port] [overlay]\n";
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
Config config;
|
||||
argp_parse(&argp, argc, argv, 0, 0, &config);
|
||||
|
||||
std::cout<<"UVOS webcam sender v0.1\n";
|
||||
|
||||
Webcam webcam(config.device, config.Xres, config.Yres);
|
||||
|
||||
std::stringstream ss;
|
||||
ss<<"nc "<<config.url<<' '<<config.port;
|
||||
|
||||
FILE* netcat = popen(ss.str().c_str(), "w");
|
||||
|
||||
Image overlay;
|
||||
if(!config.overlay.empty())
|
||||
{
|
||||
if(!overlay.open(config.overlay.c_str(), config.Xres, config.Yres, 3))
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!ferror(netcat))
|
||||
{
|
||||
usleep(100);
|
||||
Image frame = webcam.frame(10);
|
||||
if(!config.overlay.empty())
|
||||
overlayImage(frame, overlay);
|
||||
compressJpegImage(netcat, &frame);
|
||||
fflush(netcat);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
76
options.h
Normal file
76
options.h
Normal file
@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <argp.h>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
|
||||
const inline char *argp_program_version = "VideoSender";
|
||||
const inline char *argp_program_bug_address = "<carl@uvos.xyz>";
|
||||
static char doc[] = "Application that sends video4linux streams over the network with minimal latency";
|
||||
static char args_doc[] = "";
|
||||
|
||||
static struct argp_option options[] =
|
||||
{
|
||||
{"device", 'd', "[PATH]", 0, "device to use" },
|
||||
{"width", 'x', "[NUMBER]", 0, "Width at witch to capture frames from the camera"},
|
||||
{"height", 'y', "[NUMBER]", 0, "Heigt at witch to capture frames from the camera"},
|
||||
{"overlay", 'o', "[PATH]", 0, "Overlay file to use"},
|
||||
{"mjpeg", 'j', 0, 0, "Use mjpeg as the capture format for the camera"},
|
||||
{"addr", 'a', "[IP_ADDRESS]",0, "Ip address of the remote machine"},
|
||||
{"port", 'p', "[PORT]", 0, "Port of the remote machine"},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
struct Config
|
||||
{
|
||||
std::string url = "10.0.0.1";
|
||||
int port = 9874;
|
||||
std::filesystem::path device = "/dev/video0";
|
||||
int Xres = 640;
|
||||
int Yres = 480;
|
||||
std::filesystem::path overlay;
|
||||
bool mjpeg = false;
|
||||
};
|
||||
|
||||
static error_t parse_opt (int key, char *arg, struct argp_state *state)
|
||||
{
|
||||
Config *config = reinterpret_cast<Config*>(state->input);
|
||||
|
||||
try
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 'd':
|
||||
config->device = arg;
|
||||
break;
|
||||
case 'x':
|
||||
config->Xres = std::stoi(arg);
|
||||
break;
|
||||
case 'y':
|
||||
config->Yres = std::stoi(arg);
|
||||
case 'o':
|
||||
config->overlay = arg;
|
||||
break;
|
||||
case 'j':
|
||||
config->mjpeg = true;
|
||||
break;
|
||||
case 'a':
|
||||
config->url = arg;
|
||||
break;
|
||||
case 'p':
|
||||
config->port = std::stoi(arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
}
|
||||
catch(const std::invalid_argument& ex)
|
||||
{
|
||||
std::cout<<arg<<" passed for argument -"<<static_cast<char>(key)<<" is not a valid number.\n";
|
||||
return ARGP_KEY_ERROR;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct argp argp = {options, parse_opt, args_doc, doc};
|
BIN
overlay.data
Normal file
BIN
overlay.data
Normal file
Binary file not shown.
400
webcam.cpp
Normal file
400
webcam.cpp
Normal file
@ -0,0 +1,400 @@
|
||||
/*
|
||||
|
||||
(c) 2016-2023 Carl Philipp Klemm <carl@uvos.xyz>
|
||||
(c) 2014 Séverin Lemaignan <severin.lemaignan@epfl.ch>
|
||||
(c) 2008 Hans de Goede <hdegoede@redhat.com> for yuyv_to_rgb24
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or (at
|
||||
your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
|
||||
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h> /* low-level i/o */
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h> // strerrno
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include "webcam.h"
|
||||
#include "jpeg_img.h"
|
||||
|
||||
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
||||
|
||||
using namespace std;
|
||||
|
||||
static int xioctl(int fh, unsigned long int request, void *arg)
|
||||
{
|
||||
int r;
|
||||
|
||||
do {
|
||||
r = ioctl(fh, request, arg);
|
||||
} while (-1 == r && EINTR == errno);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#define CLIP(color) (unsigned char)(((color) > 0xFF) ? 0xff : (((color) < 0) ? 0 : (color)))
|
||||
|
||||
static void v4lconvert_yuyv_to_rgb24(const unsigned char *src,
|
||||
unsigned char *dest,
|
||||
int width, int height,
|
||||
int stride)
|
||||
{
|
||||
int j;
|
||||
|
||||
while (--height >= 0) {
|
||||
for (j = 0; j + 1 < width; j += 2) {
|
||||
int u = src[1];
|
||||
int v = src[3];
|
||||
int u1 = (((u - 128) << 7) + (u - 128)) >> 6;
|
||||
int rg = (((u - 128) << 1) + (u - 128) +
|
||||
((v - 128) << 2) + ((v - 128) << 1)) >> 3;
|
||||
int v1 = (((v - 128) << 1) + (v - 128)) >> 1;
|
||||
|
||||
*dest++ = CLIP(src[0] + v1);
|
||||
*dest++ = CLIP(src[0] - rg);
|
||||
*dest++ = CLIP(src[0] + u1);
|
||||
|
||||
*dest++ = CLIP(src[2] + v1);
|
||||
*dest++ = CLIP(src[2] - rg);
|
||||
*dest++ = CLIP(src[2] + u1);
|
||||
src += 4;
|
||||
}
|
||||
src += stride - (width * 2);
|
||||
}
|
||||
}
|
||||
|
||||
Webcam::Webcam(const string& device, int width, int height, bool mjpeg) :
|
||||
device(device),
|
||||
xres(width),
|
||||
yres(height),
|
||||
format(mjpeg ? V4L2_PIX_FMT_JPEG : V4L2_PIX_FMT_YUYV)
|
||||
{
|
||||
open_device();
|
||||
init_device();
|
||||
// xres and yres are set to the actual resolution provided by the cam
|
||||
|
||||
// frame stored as RGB888 (ie, RGB24)
|
||||
rgb_frame.width = xres;
|
||||
rgb_frame.height = yres;
|
||||
rgb_frame.channels = 3;
|
||||
rgb_frame.data = std::shared_ptr<unsigned char>(new unsigned char[rgb_frame.size()]);
|
||||
|
||||
start_capturing();
|
||||
}
|
||||
|
||||
Webcam::~Webcam()
|
||||
{
|
||||
stop_capturing();
|
||||
uninit_device();
|
||||
close_device();
|
||||
}
|
||||
|
||||
const Image& Webcam::frame(int timeout)
|
||||
{
|
||||
for (;;) {
|
||||
fd_set fds;
|
||||
struct timeval tv;
|
||||
int r;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(fd, &fds);
|
||||
|
||||
/* Timeout. */
|
||||
tv.tv_sec = timeout;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
r = select(fd + 1, &fds, NULL, NULL, &tv);
|
||||
|
||||
if (-1 == r) {
|
||||
if (EINTR == errno)
|
||||
continue;
|
||||
throw runtime_error("select");
|
||||
}
|
||||
|
||||
if (0 == r) {
|
||||
throw runtime_error(device + ": select timeout");
|
||||
}
|
||||
int idx = read_frame();
|
||||
if (idx != -1) {
|
||||
if (format == V4L2_PIX_FMT_YUYV)
|
||||
{
|
||||
v4lconvert_yuyv_to_rgb24((unsigned char *) buffers[idx].data,
|
||||
rgb_frame.data.get(),
|
||||
xres,
|
||||
yres,
|
||||
stride);
|
||||
}
|
||||
else if(format == V4L2_PIX_FMT_JPEG)
|
||||
{
|
||||
rgb_frame = decompressJpegImage((unsigned char *) buffers[idx].data, buffers[idx].size);
|
||||
}
|
||||
return rgb_frame;
|
||||
}
|
||||
/* EAGAIN - continue select loop. */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Webcam::read_frame()
|
||||
{
|
||||
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
CLEAR(buf);
|
||||
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
|
||||
switch (errno) {
|
||||
case EAGAIN:
|
||||
return -1;
|
||||
|
||||
case EIO:
|
||||
/* Could ignore EIO, see spec. */
|
||||
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
throw runtime_error("VIDIOC_DQBUF");
|
||||
}
|
||||
}
|
||||
|
||||
assert(buf.index < n_buffers);
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
||||
throw runtime_error("VIDIOC_QBUF");
|
||||
|
||||
return buf.index;
|
||||
}
|
||||
|
||||
void Webcam::open_device(void)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (-1 == stat(device.c_str(), &st)) {
|
||||
throw runtime_error(device + ": cannot identify! " + to_string(errno) + ": " + strerror(errno));
|
||||
}
|
||||
|
||||
if (!S_ISCHR(st.st_mode)) {
|
||||
throw runtime_error(device + " is no device");
|
||||
}
|
||||
|
||||
fd = open(device.c_str(), O_RDWR /* required */ | O_NONBLOCK, 0);
|
||||
|
||||
if (-1 == fd) {
|
||||
throw runtime_error(device + ": cannot open! " + to_string(errno) + ": " + strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Webcam::init_mmap(void)
|
||||
{
|
||||
struct v4l2_requestbuffers req;
|
||||
|
||||
CLEAR(req);
|
||||
|
||||
req.count = 1;
|
||||
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
req.memory = V4L2_MEMORY_MMAP;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
|
||||
if (EINVAL == errno) {
|
||||
throw runtime_error(device + " does not support memory mapping");
|
||||
} else {
|
||||
throw runtime_error("VIDIOC_REQBUFS");
|
||||
}
|
||||
}
|
||||
|
||||
/*if (req.count < 2) {
|
||||
throw runtime_error(string("Insufficient buffer memory on ") + device);
|
||||
}*/
|
||||
|
||||
buffers = (buffer*) calloc(req.count, sizeof(*buffers));
|
||||
|
||||
if (!buffers) {
|
||||
throw runtime_error("Out of memory");
|
||||
}
|
||||
|
||||
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
CLEAR(buf);
|
||||
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = n_buffers;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
|
||||
throw runtime_error("VIDIOC_QUERYBUF");
|
||||
|
||||
buffers[n_buffers].size = buf.length;
|
||||
buffers[n_buffers].data =
|
||||
mmap(NULL /* start anywhere */,
|
||||
buf.length,
|
||||
PROT_READ | PROT_WRITE /* required */,
|
||||
MAP_SHARED /* recommended */,
|
||||
fd, buf.m.offset);
|
||||
|
||||
if (MAP_FAILED == buffers[n_buffers].data)
|
||||
throw runtime_error("mmap");
|
||||
}
|
||||
}
|
||||
|
||||
void Webcam::close_device(void)
|
||||
{
|
||||
if (-1 == close(fd))
|
||||
throw runtime_error("close");
|
||||
|
||||
fd = -1;
|
||||
}
|
||||
|
||||
void Webcam::init_device(void)
|
||||
{
|
||||
struct v4l2_capability cap;
|
||||
struct v4l2_cropcap cropcap;
|
||||
struct v4l2_crop crop;
|
||||
struct v4l2_format fmt;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
|
||||
if (EINVAL == errno) {
|
||||
throw runtime_error(device + " is no V4L2 device");
|
||||
} else {
|
||||
throw runtime_error("VIDIOC_QUERYCAP");
|
||||
}
|
||||
}
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
|
||||
throw runtime_error(device + " is no video capture device");
|
||||
}
|
||||
|
||||
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
|
||||
throw runtime_error(device + " does not support streaming i/o");
|
||||
}
|
||||
|
||||
/* Select video input, video standard and tune here. */
|
||||
|
||||
|
||||
CLEAR(cropcap);
|
||||
|
||||
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
|
||||
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
crop.c = cropcap.defrect; /* reset to default */
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
|
||||
switch (errno) {
|
||||
case EINVAL:
|
||||
/* Cropping not supported. */
|
||||
break;
|
||||
default:
|
||||
/* Errors ignored. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Errors ignored. */
|
||||
}
|
||||
|
||||
|
||||
CLEAR(fmt);
|
||||
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (force_format) {
|
||||
fmt.fmt.pix.width = xres;
|
||||
fmt.fmt.pix.height = yres;
|
||||
fmt.fmt.pix.pixelformat = format;
|
||||
fmt.fmt.pix.field = V4L2_FIELD_ANY;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
|
||||
throw runtime_error("VIDIOC_S_FMT");
|
||||
|
||||
if (fmt.fmt.pix.pixelformat != format)
|
||||
{
|
||||
// note that libv4l2 (look for 'v4l-utils') provides helpers
|
||||
// to manage conversions
|
||||
throw runtime_error("Webcam does not support the requested format. Support for more formats need to be added!");
|
||||
}
|
||||
|
||||
/* Note VIDIOC_S_FMT may change width and height. */
|
||||
xres = fmt.fmt.pix.width;
|
||||
yres = fmt.fmt.pix.height;
|
||||
|
||||
stride = fmt.fmt.pix.bytesperline;
|
||||
|
||||
|
||||
} else {
|
||||
/* Preserve original settings as set by v4l2-ctl for example */
|
||||
if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
|
||||
throw runtime_error("VIDIOC_G_FMT");
|
||||
}
|
||||
|
||||
init_mmap();
|
||||
}
|
||||
|
||||
|
||||
void Webcam::uninit_device(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < n_buffers; ++i)
|
||||
if (-1 == munmap(buffers[i].data, buffers[i].size))
|
||||
throw runtime_error("munmap");
|
||||
|
||||
free(buffers);
|
||||
}
|
||||
|
||||
void Webcam::start_capturing(void)
|
||||
{
|
||||
unsigned int i;
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
for (i = 0; i < n_buffers; ++i) {
|
||||
struct v4l2_buffer buf;
|
||||
|
||||
CLEAR(buf);
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
buf.index = i;
|
||||
|
||||
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
||||
throw runtime_error("VIDIOC_QBUF");
|
||||
}
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
|
||||
throw runtime_error("VIDIOC_STREAMON");
|
||||
}
|
||||
|
||||
void Webcam::stop_capturing(void)
|
||||
{
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
|
||||
throw runtime_error("VIDIOC_STREAMOFF");
|
||||
}
|
||||
|
||||
|
68
webcam.h
Normal file
68
webcam.h
Normal file
@ -0,0 +1,68 @@
|
||||
/** Small C++ wrapper around V4L example code to access the webcam
|
||||
**/
|
||||
|
||||
#include <string>
|
||||
#include <memory> // unique_ptr
|
||||
#include <cstdint>
|
||||
|
||||
#include "image.h"
|
||||
|
||||
struct buffer {
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
|
||||
class Webcam {
|
||||
|
||||
public:
|
||||
Webcam(const std::string& device = "/dev/video0",
|
||||
int width = 640,
|
||||
int height = 480, bool mjpeg = false);
|
||||
|
||||
~Webcam();
|
||||
|
||||
/** Captures and returns a frame from the webcam.
|
||||
*
|
||||
* The returned object contains a field 'data' with the image data in RGB888
|
||||
* format (ie, RGB24), as well as 'width', 'height' and 'size' (equal to
|
||||
* width * height * 3)
|
||||
*
|
||||
* This call blocks until a frame is available or until the provided
|
||||
* timeout (in seconds).
|
||||
*
|
||||
* Throws a runtime_error if the timeout is reached.
|
||||
*/
|
||||
const Image& frame(int timeout = 2);
|
||||
|
||||
private:
|
||||
void init_mmap();
|
||||
|
||||
void open_device();
|
||||
void close_device();
|
||||
|
||||
void init_device();
|
||||
void uninit_device();
|
||||
|
||||
void start_capturing();
|
||||
void stop_capturing();
|
||||
|
||||
bool read_frame();
|
||||
|
||||
std::string device;
|
||||
int fd;
|
||||
|
||||
Image rgb_frame;
|
||||
struct buffer *buffers;
|
||||
unsigned int n_buffers;
|
||||
|
||||
size_t xres, yres;
|
||||
size_t stride;
|
||||
|
||||
bool force_format = true;
|
||||
uint32_t format;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user