first frame is visible

main
Steffen Pohle 3 months ago
parent 5682fcd9b8
commit 168c5a831f

@ -12,8 +12,7 @@ DEFAULT_SERVERPORT=20010
CXX=g++
CXXFLAGS= -ggdb -fPIC -Wall -std=c++11 -I/usr/local/include
LDFLAGS= -lUDPTCPNetwork -L/usr/local/lib -ljpeg
OBJFILES= webserver.o configuration.o main.o video.o convert.o debayer.o
OBJFILES= webserver.o configuration.o main.o video.o convert.o debayer.o inmemoryfile.o videoframe.o
all: dep miniwebcam
miniwebcam: dep $(OBJFILES)
@ -45,6 +44,9 @@ rebuild: clean all
dep:
$(CXX) -MM `ls *.cc` $(CXXFLAGS) > $(DEPENDFILE)
keygen:
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout ssl-key.pem -out ssl-cert.pem
clean:
rm *.s -rf
rm *.o -rf
@ -56,6 +58,8 @@ clean:
rm -rf miniwebcam
rm -rf config.h
rm -rf Makefile.rules
rm -rf ssl-key.pem
rm -rf ssl-cert.pem
cleanall: clean

@ -18,8 +18,15 @@ Configuration::Configuration() {
https_port = DEFAULT_HTTPS_PORT;
filename = DEFAULT_CONFIG_FILE;
runasdaemon = 0;
vdev_iomode = DEFAULT_VDEV_IOMODE;
vdev_device = DEFAULT_VDEV_DEVICE;
ssl_key = DEFAULT_SSL_KEY;
ssl_cert = DEFAULT_SSL_CERT;
initflags = 0;
vdev_height = DEFAULT_VDEV_HEIGHT;
vdev_width = DEFAULT_VDEV_WIDTH;
web_height = DEFAULT_WEB_HEIGHT;
web_width = DEFAULT_WEB_WIDTH;
};
Configuration::~Configuration() {
@ -39,6 +46,14 @@ int Configuration::PrintConfig() {
jp.AddObject("http_port", http_port);
jp.AddObject("https_port", https_port);
jp.AddObject("ssl-key-file", ssl_key);
jp.AddObject("ssl-cert-file", ssl_cert);
jp.AddObject("vdev-iomode", vdev_iomode);
jp.AddObject("vdev-device", vdev_device);
jp.AddObject("vdev-height", vdev_height);
jp.AddObject("vdev-width", vdev_width);
jp.AddObject("web-height", web_height);
jp.AddObject("web-width", web_width);
//
// output the json string
@ -55,6 +70,7 @@ int Configuration::PrintConfig() {
int Configuration::LoadFile(std::string fn) {
JSONParse jp;
int i;
std::string s;
//
// save current language and set to default C
@ -69,6 +85,15 @@ int Configuration::LoadFile(std::string fn) {
if (jp.GetValueInt("http_port", &i)) http_port = i;
if (jp.GetValueInt("https_port", &i)) https_port = i;
if (jp.GetValueString("ssl-key-file", &s)) ssl_key = s;
if (jp.GetValueString("ssl-cert-file", &s)) ssl_cert = s;
if (jp.GetValueInt("web-height", &i)) web_height = i;
if (jp.GetValueInt("web-width", &i)) web_width = i;
if (jp.GetValueString("vdev-device", &s)) vdev_device = s;
if (jp.GetValueInt("vdev-iomode", &i)) vdev_iomode = i;
if (jp.GetValueInt("vdev-height", &i)) vdev_height = i;
if (jp.GetValueInt("vdev-width", &i)) vdev_width = i;
//
// restore language
@ -92,6 +117,7 @@ int Configuration::LoadArgs(int argc, char **argv) {
ErrorExit("config file missing", -1);
}
if (strcmp(argv[i], "-h") == 0) initflags |= CONF_INITFLAGS_HELP;
if (strcmp(argv[i], "--help") == 0) initflags |= CONF_INITFLAGS_HELP;
if (strcmp(argv[i], "-http_port") == 0) {
if (++i < argc) {
@ -107,6 +133,51 @@ int Configuration::LoadArgs(int argc, char **argv) {
else
ErrorExit("missing port parameter", -1);
}
if (strcmp(argv[i], "-websize") == 0) {
if ((i=i+2) < argc) {
web_width = atoi(argv[i-1]);
web_height = atoi(argv[i]);
}
else
ErrorExit("missing web resolution parameter", -1);
}
if (strcmp(argv[i], "-vdevsize") == 0) {
if ((i=i+2) < argc) {
vdev_width = atoi(argv[i-1]);
vdev_height = atoi(argv[i]);
}
else
ErrorExit("missing video device resolution parameter", -1);
}
if (strcmp(argv[i], "-vdeviomode") == 0) {
if (++i < argc) {
vdev_iomode = atoi(argv[i]);
}
else
ErrorExit("missing iomode parameter", -1);
}
if (strcmp(argv[i], "-vdevdevice") == 0) {
if (++i < argc) {
vdev_device = argv[i];
}
else
ErrorExit("missing device parameter", -1);
}
if (strcmp(argv[i], "-sslkey") == 0) {
if (++i < argc) {
ssl_key = argv[i];
}
else
ErrorExit("missing sslkey file", -1);
}
if (strcmp(argv[i], "-sslcert") == 0) {
if (++i < argc) {
ssl_cert = argv[i];
}
else
ErrorExit("missing sslcertfile", -1);
}
}
return 1;
@ -120,6 +191,14 @@ void Configuration::Help() {
printf ("\n");
printf (" -http_port INT port to listen for http connections\n");
printf (" -https_port INT port to listen for https connections\n");
printf (" -sslkey FILE ssl key file\n");
printf (" -sslcert FILE ssl certfile\n");
printf ("\n");
printf (" -websize INT INT define the web output resolution\n");
printf ("\n");
printf (" -vdeviomode INT IOMode to read the video data, 0-read, 1-MMap\n");
printf (" -vdevdevice FILE Device File i.e. /dev/video2\n");
printf (" -vdevsize INT INT define video input resolution\n");
printf ("\n");
printf (" -config FILE load this configfile\n");
printf (" -dump_config print the config file\n");

@ -8,22 +8,40 @@
#include "config.h"
#define DEFAULT_HTTP_PORT 10080
#define DEFAULT_HTTPS_PORT 10081
#define DEFAULT_CONFIG_FILE "/etc/miniwebcam.conf"
#define DEFAULT_HTTP_PORT 8080
#define DEFAULT_HTTPS_PORT 8081
#define DEFAULT_CONFIG_FILE "/etc/miniwebcam/miniwebcam.conf"
#define DEFAULT_SSL_KEY "/etc/miniwebcam/ssl-key.pem"
#define DEFAULT_SSL_CERT "/etc/miniwebcam/ssl-cert.pem"
#define DEFAULT_VDEV_IOMODE 1
#define DEFAULT_VDEV_DEVICE "/dev/video0"
#define DEFAULT_VDEV_HEIGHT -1
#define DEFAULT_VDEV_WIDTH -1
#define DEFAULT_WEB_HEIGHT 1024
#define DEFAULT_WEB_WIDTH 786
#define CONF_INITFLAGS_PRINT 0x0001
#define CONF_INITFLAGS_HELP 0x0002
class Configuration {
private:
public:
int http_port;
int https_port;
std::string ssl_key;
std::string ssl_cert;
int runasdaemon;
std::string filename;
int initflags;
public:
int web_height;
int web_width;
int vdev_height;
int vdev_width;
int vdev_iomode;
std::string vdev_device;
Configuration();
~Configuration();
@ -35,8 +53,6 @@ public:
int PrintConfig(); // print current configuration
void Help(); // print Help
//
};
extern Configuration config;

@ -7,8 +7,7 @@
#include <stdint.h>
#include <jpeglib.h>
#include <setjmp.h>
#include "video.h"
#include "videoframe.h"
#ifndef CLEAR
#define CLEAR(x) memset (&(x), 0, sizeof (x))
@ -18,6 +17,7 @@ struct {
struct jpeg_decompress_struct cinfo;
} typedef ConvertData;
int Convert (ConvertData *cdata, VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch);
int ConvertStart(ConvertData *cdata, uint32_t pixelformat);
int ConvertStop(ConvertData *cdata, uint32_t pixelformat);

@ -0,0 +1,57 @@
#include <stdlib.h>
#include <UDPTCPNetwork.h>
#include "inmemoryfile.h"
InMemoryFile::InMemoryFile() {
mem = malloc (INMEMORYFILE_ALLOCATEBLOCK);
memallocsize = INMEMORYFILE_ALLOCATEBLOCK;
memsize = 0;
};
InMemoryFile::~InMemoryFile() {
if (mem != NULL) free (mem);
mem = NULL;
memallocsize = 0;
memsize = 0;
};
int InMemoryFile::Allocate(size_t newsize) {
// shrink size only if new memory block is smaller then 25%
if ((newsize < memallocsize / 4) || (newsize > memallocsize)) {
memallocsize = (1+(newsize/INMEMORYFILE_ALLOCATEBLOCK))*INMEMORYFILE_ALLOCATEBLOCK;
memsize = newsize;
mem = realloc(mem, memallocsize);
if (mem == NULL || memsize > memallocsize) {
debug ("could not reallocate memory memsize:%d memallocsize:%d\n.", memsize, memallocsize);
exit (1);
}
}
return 1;
};
InMemoryFile InMemoryFile::operator=(InMemoryFile rightside) {
memsize = rightside.memsize;
memallocsize = 1+(memsize/INMEMORYFILE_ALLOCATEBLOCK)*INMEMORYFILE_ALLOCATEBLOCK;
mem = realloc(mem, memallocsize+1);
if (mem == NULL || memsize > memallocsize) {
debug ("could not reallocate memory: %s\n.", strerror(errno));
exit (1);
}
memcpy (mem, rightside.mem, memsize);
return *this;
};
/// @brief copy the memory.
/// @param srcptr pointer to the source memory
/// @param srcsize size of the memory
/// @return 1 on success, 0 on error
int InMemoryFile::CopyFrom(void *srcptr, size_t srcsize) {
if (Allocate (srcsize) == 0) return 0;
memcpy (mem, srcptr, memsize);
return 1;
}

@ -0,0 +1,22 @@
#ifndef _INMEMORYFILE_H_
#define _INMEMORYFILE_H_
#define INMEMORYFILE_ALLOCATEBLOCK 4096
class InMemoryFile {
private:
size_t memallocsize;
public:
void *mem;
size_t memsize;
InMemoryFile operator=(InMemoryFile rightside);
InMemoryFile();
~InMemoryFile();
int Allocate(size_t newsize);
int CopyFrom(void *srcptr, size_t srcsize);
};
#endif

@ -1,18 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <signal.h>
#include <string>
#include "configuration.h"
#include "miniwebcam.h"
int running = 1;
static void sig_int(int);
int SetupSignals();
VideoFrame currentimage;
void ErrorExit(std::string text, int errorcode) {
printf ("Error: %s\n", text.c_str());
exit (errorcode);
};
int main(int argc, char **argv) {
printf ("MiniWebCam:\n");
if (SetupSignals() == 0) return 0;
config.LoadArgs (argc, argv);
config.LoadFile (config.GetFilename());
if (config.GetInitFlags() & CONF_INITFLAGS_PRINT) {
@ -25,16 +37,74 @@ int main(int argc, char **argv) {
return 0;
}
printf ("MiniWebCam:\n");
VideoFrame v;
VideoFrameFloat vf;
VideoDevice_V4L2 vdev;
WebCamServer webserver;
currentimage.TestScreen(1920, 1080);
webserver.SetupPorts(config.http_port, config.https_port);
webserver.SetupSSL(config.ssl_key, config.ssl_cert);
webserver.Start();
if (vdev.SetDevice (config.vdev_device, config.vdev_width, config.vdev_height) == 0) {
debug ("could not setup device.\n");
return 0;
}
if (vdev.SetIOMode (config.vdev_iomode) == 0) {
debug ("could not setup iomode.\n");
return 0;
}
if (vdev.Start() == 0) {
debug ("Error: could not start video.\n");
return 0;
}
printf ("VideoFrame Size:\n");
v.SetSize(100,100);
printf ("VideoFrameFloat Size:\n");
vf.SetSize(100,100);
while (running) {
if (vdev.GetFrame(&currentimage) == 0) {
vdev.Stop();
vdev.Start();
}
webserver.Loop();
usleep (1000);
}
vdev.Stop();
webserver.Stop();
return 0;
};
int SetupSignals() {
if (signal(SIGINT, sig_int) == SIG_ERR) {
errorexit ("%s:%d could not set signal for SIGINT\n", __FILE__, __LINE__);
return 0;
}
if (signal(SIGTERM, sig_int) == SIG_ERR) {
errorexit ("%s:%d could not set signal for SIGTERM\n", __FILE__, __LINE__);
return 0;
}
if (signal(SIGHUP, sig_int) == SIG_ERR) {
errorexit ("%s:%d could not set signal for SIGHUB\n", __FILE__, __LINE__);
return 0;
}
if (signal(SIGUSR1, sig_int) == SIG_ERR) {
errorexit ("%s:%d could not set signal for SIGHUB\n", __FILE__, __LINE__);
return 0;
}
return 1;
};
static void sig_int(int sig) {
if (signal (SIGINT, sig_int) == SIG_ERR) errorexit ("could not set up signal SIGINT\n");
printf ("\n\nSignal Int\n\n");
running = 0;
}

@ -9,9 +9,19 @@
#include "configuration.h"
#include "miniwebcam.h"
#include "video.h"
#include "inmemoryfile.h"
class WebCamServer : public WebServer {
private:
protected:
public:
int HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient);
};
void ErrorExit(std::string text, int errorcode);
extern VideoFrame currentimage;
#endif

@ -1,108 +1,520 @@
#include "miniwebcam.h"
#include "video.h"
#include "convert.h"
#include <stdio.h>
#include <stdlib.h>
VideoFrame::VideoFrame() {
mem = NULL;
width = 0;
height = 0;
mem_allocated = 0;
VideoDevice::VideoDevice() {
};
VideoFrame::~VideoFrame() {
FreeFrame();
VideoDevice::~VideoDevice() {
};
void VideoFrame::FreeFrame() {
if (mem != NULL) {
free (mem);
mem = NULL;
width = 0;
height = 0;
mem_allocated = 0;
}
VideoDevice_V4L2::VideoDevice_V4L2() {
conf_height = 0;
conf_width = 0;
conf_videodev = "";
conf_iomode = 0;
fd = -1;
};
void VideoFrame::AllocateFrame() {
printf ("VideoFrame::AllocateFrame()\n");
int memnewsize = width * height * 3;
if (memnewsize >= mem_allocated) return;
else if (memnewsize == 0) FreeFrame();
VideoDevice_V4L2::~VideoDevice_V4L2() {
if (fd != 0) Stop();
};
int VideoDevice_V4L2::SetDevice(std::string newdevice, int nwidth, int nheight) {
int isrunning = 0;
if (fd != -1) {
Stop();
isrunning = 1;
}
mem = (unsigned char *) realloc (mem, memnewsize);
mem_allocated = memnewsize;
conf_videodev = newdevice;
conf_width = nwidth;
conf_height = nheight;
if (mem == NULL) {
debug ("Error on allocation new frame\n");
exit (1);
if (isrunning) {
if (Start() == 0) return 0;
}
return 1;
};
int VideoFrame::SetSize(int w, int h) {
if (w < 0 && h < 0) return 0;
int VideoDevice_V4L2::SetIOMode(int newiomode) {
int isrunning = 0;
width = w;
height = h;
if (fd != -1) {
Stop();
isrunning = 1;
}
AllocateFrame();
conf_iomode = newiomode;
if (isrunning) {
if (Start() == 0) return 0;
}
return 1;
};
/*********************************************************************/
int VideoDevice_V4L2::Open() {
int i;
struct v4l2_capability vcap;
VideoDevCtrl vctl;
char txt[32];
debug ("Device: '%s'", conf_videodev.c_str());
if (fd != -1) return 0;
//
// open device and get device name and capabilities | O_NONBLOCK
if((fd = open(conf_videodev.c_str(), O_RDWR)) == -1){
debug ("could not open device error: %s", strerror(errno));
return 0;
}
if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1)
strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card));
debug ("VideoDevice_V4L2::Open Devicefile:%s Card:%s fd:%d", conf_videodev.c_str(), vcap.card, fd);
debug ("Capabilities: %u", vcap.capabilities);
PrintCaps(vcap.capabilities);
debug ("Device Capabilities: %u", vcap.device_caps);
PrintCaps(vcap.device_caps);
if (!(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
debug ("Error: device has no video capture capabilities");
return 0;
Close();
}
//
// query controls
struct v4l2_queryctrl queryctrl;
uint32_t min;
debug ("Get Controls");
vidctrls.clear();
memset (&queryctrl, 0, sizeof (queryctrl));
for (i = V4L2_CID_BASE; i < V4L2_CID_DETECT_CLASS_BASE+0x1000; i++) {
queryctrl.id = i;
if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
vctl.name = (char*)queryctrl.name;
vctl.id = queryctrl.id;
vctl.min = queryctrl.minimum;
vctl.max = queryctrl.maximum;
GetDevCtrl(queryctrl.id, &vctl.value);
vidctrls.push_back(vctl);
}
}
debug ("got %d controls.", vidctrls.size());
//
// check for cropping.. if we have it setup default
debug ("setup cropping");
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)) {
debug ("VIDEOC_S_CROP Errorcode: %s", strerror(errno));
}
}
//
// prepare resolution and pixelformat
debug ("setup video resolution and pixeltype");
CLEAR (fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (conf_width != -1 && conf_height != -1) { // resolution
fmt.fmt.pix.width = conf_width;
fmt.fmt.pix.height = conf_height;
}
else {
fmt.fmt.pix.width = 1920;
fmt.fmt.pix.height = 1080;
}
// fixme: hardcoded video format?????
fmt.fmt.pix.pixelformat = convert_to_pixelformat(conf_videofmt);
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) {
debug ("VIDIOC_S_FMT : %s", strerror (errno));
close (fd);
fd = -1;
return 0;
}
// Note VIDIOC_S_FMT may change width and height.
// Buggy driver paranoia. - as written in the v4l2 api documentation
min = fmt.fmt.pix.width * 2;
if (fmt.fmt.pix.bytesperline < min)
fmt.fmt.pix.bytesperline = min;
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
if (fmt.fmt.pix.sizeimage < min)
fmt.fmt.pix.sizeimage = min;
conf_width = fmt.fmt.pix.width;
conf_height = fmt.fmt.pix.height;
snprintf (txt, 32, "%c%c%c%c", ((char*)&fmt.fmt.pix.pixelformat)[0],
((char*)&fmt.fmt.pix.pixelformat)[1],
((char*)&fmt.fmt.pix.pixelformat)[2],
((char*)&fmt.fmt.pix.pixelformat)[3]);
conf_videofmt = txt;
PrintFmt (&fmt);
// init buffers
switch (conf_iomode) {
case IOMODE_MMAP:
if (InitMMap() == 0) {
debug ("could not setup MMap (memory mapping)");
Close();
}
break;
case IOMODE_READ:
for (i = 0; i < VDEV_INBUFFERS; i++) {
inbuffer[i].size = fmt.fmt.pix.sizeimage;
inbuffer[i].data = (unsigned char*) malloc (fmt.fmt.pix.sizeimage);
}
default:
break;
}
return 1;
};
void VideoFrameFloat::AllocateFrame() {
printf ("VideoFrameFloat::AllocateFrame()\n");
int memnewsize = width * height * 3 * sizeof(float);
if (memnewsize >= mem_allocated) return;
else if (memnewsize == 0) FreeFrame();
int VideoDevice_V4L2::InitMMap() {
struct v4l2_requestbuffers bufreq;
struct v4l2_buffer bufinfo;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
mem = (unsigned char *) realloc (mem, memnewsize);
mem_allocated = memnewsize;
int i;
CLEAR(bufreq);
CLEAR(bufinfo);
bufreq.count = VDEV_INBUFFERS;
bufreq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufreq.memory = V4L2_MEMORY_MMAP;
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &bufreq)) {
if (EINVAL == errno) {
printf("%s does not support memory mapping", conf_videodev.c_str());
return 0;
} else {
printf ("%s:%d %s Error %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno));
return 0;
}
}
if (bufreq.count < 1) {
printf ( "Insufficient buffer memory on %s\n", conf_videodev.c_str());
return 0;
}
if (mem == NULL) {
debug ("Error on allocation new frame\n");
exit (1);
for (i = 0; i < VDEV_INBUFFERS; i++) {
CLEAR(bufinfo);
bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
bufinfo.memory = V4L2_MEMORY_MMAP;
bufinfo.index = i;
if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){
perror("VIDIOC_QUERYBUF");
exit(1);
}
inbuffer[i].size = bufinfo.length;
inbuffer[i].data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, bufinfo.m.offset);
if (inbuffer[i].data == MAP_FAILED) {
printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno));
exit (1);
}
}
return 1;
}
int VideoDevice_V4L2::Close() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (fd >= 0) {
UnInit();
close (fd);
fd = -1;
}
return 1;
};
/*********************************************************************/
int VideoDevice_V4L2::UnInit() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (conf_iomode) {
case IOMODE_READ:
for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) {
if (inbuffer[inbuffer_idx].data) {
free (inbuffer[inbuffer_idx].data);
inbuffer[inbuffer_idx].data = NULL;
inbuffer[inbuffer_idx].size = 0;
}
}
break;
case IOMODE_MMAP:
for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++)
if (inbuffer[inbuffer_idx].data) {
if (-1 == munmap(inbuffer[inbuffer_idx].data, inbuffer[inbuffer_idx].size)){
fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno));
exit(1);
}
inbuffer[inbuffer_idx].data = NULL;
inbuffer[inbuffer_idx].size = 0;
}
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return 0;
}
return 1;
};
VideoDevice::VideoDevice() {
videofd = 0;
int VideoDevice_V4L2::Start() {
enum v4l2_buf_type type;
debug ("");
if (Open() == 0) {
debug ("VideoDevice_V4L2::Start Open Device Failed.");
return 0;
}
switch (conf_iomode) {
case IOMODE_READ:
/* Nothing to do. */
break;
case IOMODE_MMAP:
struct v4l2_buffer buf;
for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) {
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = inbuffer_idx;
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
debug ("error on VIDIOC_QBUF %s", strerror(errno));
return 0;
}
}
inbuffer_idx = 0;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) {
debug ("VIDIOC_STREAMON %s", strerror(errno));
return 0;
}
break;
default:
debug ("wrong IOMODE?");
return 0;
}
ConvertStart(&cdata, fmt.fmt.pix.pixelformat);
return 1;
};
VideoDevice::~VideoDevice() {
int VideoDevice_V4L2::Stop() {
enum v4l2_buf_type type;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
ConvertStop(&cdata, fmt.fmt.pix.pixelformat);
switch (conf_iomode) {
case IOMODE_READ:
/* Nothing to do. */
break;
case IOMODE_MMAP:
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) {
fprintf(stderr, "%s:%d VIDIOC_STREAMOFF Error:%s\n", __FILE__, __LINE__, strerror(errno));
return 0;
}
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return 0;
}
Close();
return 1;
};
int VideoDevice_V4L2::GetFrame(VideoFrame *destframe) {
struct v4l2_buffer buf;
int len;
if (destframe == NULL) return 0;
switch (conf_iomode) {
case IOMODE_READ:
if ((len = read (fd, inbuffer[0].data, fmt.fmt.pix.sizeimage)) == -1) {
switch (errno) {
case EAGAIN:
return -1;
case EIO:
default:
debug ("v4l2_grab IOM_READ: %s dest:%p size:%d", strerror (errno), destframe, fmt.fmt.pix.sizeimage);
return 0;
}
}
else {
Convert(&cdata, destframe, inbuffer[0].data, len,
fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
}
break;
case IOMODE_MMAP:
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:
debug ("error on VIDIOC_DQBUF EIO %s", strerror(errno));
return 0;
default:
debug ("error on VIDIOC_DQBUF %s", strerror(errno));
return 0;
}
}
if (buf.index >= 0 && buf.index < VDEV_INBUFFERS) {
Convert(&cdata, destframe, inbuffer[buf.index].data, buf.bytesused,
fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
}
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
debug ("error on VIDIOC_QBUF %s", strerror(errno));
return 0; }
if (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0;
break;
default:
debug ("wrong IOMODE?");
return 0;
}
return 1;
};
int VideoDevice::SetDevice() {
return 0;
int VideoDevice_V4L2::xioctl(int fd, int request, void *arg) {
int r;
int errnoioctl;
struct timeval to1;
struct timeval to2;
float to;
gettimeofday(&to1, NULL);
do {
r = ioctl(fd, request, arg);
errnoioctl = errno;
gettimeofday(&to2, NULL);
to = (float)(to2.tv_sec - to1.tv_sec) + ((to2.tv_usec - to1.tv_usec) / 1000000.0);
} while (r == -1 && errnoioctl == EINTR && to < 2.0);
return r;
};
int VideoDevice::Start(int w, int h) {
return 0;
int VideoDevice_V4L2::SetDevCtrl(unsigned int id, int value) {
struct v4l2_control ctrl;
CLEAR(ctrl);
ctrl.id = id;
ctrl.value = value;
if (-1 == xioctl (fd, VIDIOC_S_CTRL, &ctrl)) {
return 0;
}
return 1;
};
int VideoDevice::Stop() {
return 0;
int VideoDevice_V4L2::GetDevCtrl(unsigned int id, int *value) {
struct v4l2_control ctrl;
CLEAR(ctrl);
ctrl.id = id;
if (-1 == xioctl (fd, VIDIOC_G_CTRL, &ctrl)) {
return 0;
}
*value = ctrl.value;
return 1;
};
int VideoDevice::GetFrame(VideoFrame *destframe) {
return 0;
void VideoDevice_V4L2::PrintCaps(uint32_t caps) {
printf ("%s:%d %s Caps: %x\n", __FILE__, __LINE__, __FUNCTION__, caps);
if (caps & V4L2_CAP_VIDEO_CAPTURE) printf (" V4L2_CAP_VIDEO_CAPTURE\n");
if (caps & V4L2_CAP_EXT_PIX_FORMAT) printf (" V4L2_CAP_EXT_PIX_FORMAT\n");
#ifdef V4L2_CAP_META_CAPTURE
if (caps & V4L2_CAP_META_CAPTURE) printf (" V4L2_CAP_META_CAPTURE\n");
#endif
if (caps & V4L2_CAP_STREAMING) printf (" V4L2_CAP_STREAMING\n");
if (caps & V4L2_CAP_DEVICE_CAPS) printf (" V4L2_CAP_DEVICE_CAPS\n");
if (caps & V4L2_CAP_TUNER) printf (" V4L2_CAP_TUNER\n");
if (caps & V4L2_CAP_MODULATOR) printf (" V4L2_CAP_MODULATOR\n");
if (caps & V4L2_CAP_READWRITE) printf (" V4L2_CAP_READWRITE\n");
if (caps & V4L2_CAP_ASYNCIO) printf (" V4L2_CAP_ASYNCIO\n");
};
void VideoDevice_V4L2::PrintFmt(struct v4l2_format *f) {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
printf (" type : %u\n", f->type);
printf (" fmt.pix.width : %d\n", f->fmt.pix.width);
printf (" fmt.pix.height : %d\n", f->fmt.pix.height);
printf (" fmt.fmt.pix.pixelformat : %c%c%c%c\n", ((char*)&f->fmt.pix.pixelformat)[0],
((char*)&f->fmt.pix.pixelformat)[1],
((char*)&f->fmt.pix.pixelformat)[2],
((char*)&f->fmt.pix.pixelformat)[3]);
printf (" fmt.pix.field : %d\n", f->fmt.pix.field);
printf (" fmt.pix.bytesperline : %d\n", f->fmt.pix.bytesperline);
printf (" fmt.pix.sizeimage : %d\n", f->fmt.pix.sizeimage);
};

@ -23,8 +23,12 @@
#include <list>
#include <string>
#include <jpeglib.h>
#include "convert.h"
#include "inmemoryfile.h"
#include "miniwebcam.h"
#include "videoframe.h"
#define VDEV_INBUFFERS 3
enum {
IOMODE_READ,
IOMODE_MMAP
@ -41,49 +45,78 @@ struct jpg_error_mgr {
typedef struct jpg_error_mgr *jpg_error_ptr;
//
// only contain 24bit each color 8Bit
class VideoFrame {
private:
virtual void AllocateFrame();
protected:
void FreeFrame();
int mem_allocated;
unsigned char *mem;
int height;
int width;
public:
VideoFrame();
~VideoFrame();
struct {
unsigned int id;
int min;
int max;
int value;
std::string name;
} typedef VideoDevCtrl;
int GetHeight() { return height; };
int GetWidth() { return width; };
unsigned char *GetPixBuf() { return mem; };
struct {
unsigned int size;
unsigned char* data;
} typedef VideoInBuffer;
int SetSize(int w, int h);
};
class VideoFrameFloat : public VideoFrame {
#include "convert.h"
class VideoDevice {
private:
void AllocateFrame();
protected:
std::string conf_videodev;
std::string conf_videofmt;
int conf_width;
int conf_height;
int conf_iomode;
ConvertData cdata;
public:
VideoDevice();
~VideoDevice();
virtual int SetDevice(std::string newdevice, int nwidth, int nheight) { return 0; };
virtual int SetIOMode(int newiomode) { return 0; };
virtual int Start() { return 0; };
virtual int Stop() { return 0; };
virtual int GetFrame (VideoFrame *destframe) { return 0; };
virtual int SetDevCtrl(unsigned int id, int value) { return 0; };
virtual int GetDevCtrl(unsigned int id, int *value) { return 0; };
};
class VideoDevice {
class VideoDevice_V4L2 : public VideoDevice {
private:
int videofd;
std::list<VideoDevCtrl> vidctrls;
int inbuffer_idx;
VideoInBuffer inbuffer[VDEV_INBUFFERS];
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
int fd; // filedescriptor to the video device file
int Open();
int Close();
int UnInit();
int InitMMap();
int xioctl(int fd, int request, void *arg);
protected:
public:
VideoDevice();
~VideoDevice();
int SetDevice();
int Start(int w, int h);
VideoDevice_V4L2();
~VideoDevice_V4L2();
int SetDevice(std::string newdevice, int nwidth, int nheight);
int SetIOMode(int newiomode);
int Start();
int Stop();
int GetFrame(VideoFrame *destframe);
int GetFrame (VideoFrame *destframe);
int SetDevCtrl(unsigned int id, int value);
int GetDevCtrl(unsigned int id, int *value);
void PrintCaps(uint32_t caps);
void PrintFmt(struct v4l2_format *f);
};

@ -0,0 +1,144 @@
#include <stdlib.h>
#include <stdio.h>
#include <jpeglib.h>
#include <UDPTCPNetwork.h>
#include "videoframe.h"
#include "inmemoryfile.h"
VideoFrame::VideoFrame() {
mem = NULL;
width = 0;
height = 0;
mem_allocated = 0;
};
VideoFrame::~VideoFrame() {
FreeFrame();
};
void VideoFrame::FreeFrame() {
if (mem != NULL) {
free (mem);
mem = NULL;
width = 0;
height = 0;
mem_allocated = 0;
}
};
void VideoFrame::AllocateFrame() {
int memnewsize = width * height * 3;
if (memnewsize < mem_allocated) return;
else if (memnewsize == 0) FreeFrame();
mem = (unsigned char *) realloc (mem, memnewsize);
mem_allocated = memnewsize;
if (mem == NULL) {
debug ("Error on allocation new frame\n");
exit (1);
}
};
int VideoFrame::SetSize(int w, int h) {
if (w < 0 && h < 0) return 0;
width = w;
height = h;
AllocateFrame();
return 1;
};
int VideoFrame::ConvertToJpeg(InMemoryFile *imf, int quality) {
unsigned char *outbuffer = NULL;
size_t outbuffersize;
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
if (imf == NULL) return 0;
if (height == 0 || width == 0) return 0;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
jpeg_mem_dest(&cinfo, &outbuffer, &outbuffersize);
cinfo.image_width = width; /* image width and height, in pixels */
cinfo.image_height = height;
cinfo.input_components = 3; /* # of color components per pixel */
cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
jpeg_start_compress(&cinfo, TRUE);
row_stride = width * 3; /* JSAMPLEs per row in image_buffer */
while (cinfo.next_scanline < cinfo.image_height) {
row_pointer[0] = & mem[cinfo.next_scanline * row_stride];
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
imf->CopyFrom(outbuffer, outbuffersize);
free (outbuffer);
return 1;
};
int VideoFrame::TestScreen(int w, int h) {
int x, y;
unsigned char r, g, b;
SetSize (w, h);
r = g = b = 0;
for (x = 0; x < w; x++) {
for (g = 0, y = 0; y < h; y++) {
b = r + g;
mem[3*(x + y * width) + 0] = r;
mem[3*(x + y * width) + 1] = g;
mem[3*(x + y * width) + 2] = b;
g++;
}
r ++;
}
return 1;
}
/*********************************************************************/
void VideoFrameFloat::AllocateFrame() {
printf ("VideoFrameFloat::AllocateFrame()\n");
int memnewsize = width * height * 3 * sizeof(float);
if (memnewsize >= mem_allocated) return;
else if (memnewsize == 0) FreeFrame();
mem = (unsigned char *) realloc (mem, memnewsize);
mem_allocated = memnewsize;
if (mem == NULL) {
debug ("Error on allocation new frame\n");
exit (1);
}
};

@ -0,0 +1,38 @@
#ifndef _VIDEOFRAME_H_
#define _VIDEOFRAME_H_
#include "inmemoryfile.h"
//
// only contain 24bit each color 8Bit
class VideoFrame {
private:
virtual void AllocateFrame();
protected:
void FreeFrame();
int mem_allocated;
unsigned char *mem;
int height;
int width;
public:
VideoFrame();
~VideoFrame();
int GetHeight() { return height; };
int GetWidth() { return width; };
unsigned char *GetPixBuf() { return mem; };
int SetSize(int w, int h);
int ConvertToJpeg(InMemoryFile *imf, int quality);
int TestScreen(int w, int h);
};
class VideoFrameFloat : public VideoFrame {
private:
void AllocateFrame();
protected:
public:
};
#endif

@ -1,2 +1,57 @@
#include "webserver.h"
#include <unistd.h>
#include "miniwebcam.h"
#include "UDPTCPNetwork.h"
std::string GenerateHtmlFile();
InMemoryFile GenerateJpgFile(VideoFrame *vf);
int WebCamServer::HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient) {
if (requestbuffer == NULL || webclient == NULL) return 0;
std::string request = requestbuffer->GetRequest();
printf ("SimpleWebServerClient::HandleRequest() Request:%s\n", request.c_str());
if (request.compare ("/") == 0) request = "/index.html";
if (request.find("/index.html") != std::string::npos) {
std::string htmlfile = GenerateHtmlFile();
if (webclient->SendResponseFileFromMemory(requestbuffer, request, "",
(void*) htmlfile.c_str(),
strlen(htmlfile.c_str())) != 1) return 0;
}
else if (request.find("/snapshot.jpg") != std::string::npos) {
InMemoryFile jpgfile;
currentimage.ConvertToJpeg(&jpgfile, 99);
if (webclient->SendResponseFileFromMemory(requestbuffer, request, "",
(void*) jpgfile.mem,
jpgfile.memsize) != 1) return 0;
}
else {
return 0;
}
requestbuffer->Clear();
return 1;
};
std::string GenerateHtmlFile() {
std::string res;
res = "<html><head><title>MiniWebCam</title></head><body>";
res += "<img id=\"myimage\" src=\"snapshot.jpg\"></img>";
res += "<script>\n";
res += " function reloadImage() {\n";
res += " const img = document.getElementById('myimage');\n";
res += " let src = img.src.split('?')[0];\n";
res += " src = src + '?' + new Date().getTime();\n";
res += " img.src = src;\n";
res += "}\n\n";
res += "setInterval(reloadImage, 1000);\n";
res += "\n\n";
res += "</script>\n";
res += "</body></html>";
return res;
};

@ -1,6 +0,0 @@
#ifndef _WEBSERVER_H_
#define _WEBSERVER_H_
#include "miniwebcam.h"
#endif
Loading…
Cancel
Save