adding support for multiple dirvers

test16bit
Steffen Pohle 4 years ago
parent 615adc8930
commit 3e7fa34222

@ -1,3 +1,5 @@
2021-11-15:
- adding support for multiple drivers
2021-10-25:
- adding support for different resolution and pixelformat

@ -4,7 +4,7 @@ APP = simpleskycam
-include Makefile.rules
OBJECTS = gui.oo main.oo video.oo videoframe.oo videodev.oo filter.oo detect.oo json.oo configuration.oo
OBJECTS = gui.oo main.oo video.oo videoframe.oo videodev.oo videodev-v4l2.oo convert.oo filter.oo detect.oo json.oo configuration.oo
DISTNAME=simpleskycam-$(VERSION)
ifeq ($(TARGET),)

@ -2,7 +2,7 @@
TARGET = $(APP)
CPP = c++
CPPFLAGS = -std=c++11 -ggdb -Wall -O0 `pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -I/usr/include -DBUILD_LINUX=1
CPPFLAGS = -std=c++11 -ggdb -Wall -Werror -O0 `pkg-config --cflags gtk+-3.0 gmodule-export-2.0` -Wl,--export-dynamic -I/usr/include -DBUILD_LINUX=1
INCLUDES =
LDFLAGS =
LIBS = `pkg-config --libs gtk+-3.0 gmodule-export-2.0` -L/usr/lib -ljpeg

@ -5,7 +5,7 @@
#include <string>
extern VideoDev videodev;
extern VideoDev *videodev;
extern GtkBuilder *_builder_; // work around for threads
@ -71,7 +71,12 @@ void Configuration::SaveConfig(std::string filename) {
cb = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videofmt"));
cbe = gtk_bin_get_child(GTK_BIN(cb));
jp.AddObject("video_format", (string) gtk_entry_get_text(GTK_ENTRY(cbe)));
jp.AddObject("device", videodev.GetDevice());
if (videodev != NULL) {
jp.AddObject("device", videodev->GetDevice());
}
else {
jp.AddObject("device", "");
}
//
// save button config

@ -0,0 +1,253 @@
#include <stdlib.h>
#include "convert.h"
#include "gui.h"
#include "video.h"
/*
* helper part for converting different video types
*/
//
// jpeg: replacement for error_exit
//
METHODDEF(void) jpg_error_exit (j_common_ptr cinfo) {
jpg_error_ptr myerr = (jpg_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
//
// clamp and convert2rgb is build on the sample of the v4l2 api documentation
//
inline unsigned char clamp (double x) {
int r = (int)x;
if (r < 0) return 0;
else if (r > 255) return 255;
else return r;
};
inline void convert2rgb (unsigned char Y1, unsigned char Cb, unsigned char Cr,
unsigned char *ER, unsigned char *EB, unsigned char *EG) {
register int y1, pb, pr;
y1 = Y1 - 16;
pb = Cb - 128;
pr = Cr - 128;
*ER = clamp (y1 + 1.402 * pr);
*EB = clamp (y1 - 0.344 * pb - 0.714 * pr);
*EG = clamp (y1 + 1.772 * pb);
};
int ConvertStart(ConvertData *cdata, uint32_t pixelformat) {
if (cdata == NULL) return VDEV_STATUS_ERROR;
if (pixelformat == V4L2_PIX_FMT_MJPEG) {
jpeg_create_decompress(&cdata->cinfo);
}
return VDEV_STATUS_OK;
};
int ConvertStop(ConvertData *cdata, uint32_t pixelformat) {
if (cdata == NULL) return VDEV_STATUS_ERROR;
if (pixelformat == V4L2_PIX_FMT_MJPEG) jpeg_destroy_decompress(&cdata->cinfo);
return VDEV_STATUS_OK;
};
/*
* converts the video from input type to RGB24 type - 24Bit
*/
int Convert (ConvertData *cdata, VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch) {
int xs, ys;
int xd, yd;
unsigned char r,g,b;
unsigned char cb, cr, y1;
unsigned char *ptrdst = NULL;
struct jpg_error_mgr jerr;
if (cdata == NULL) return VDEV_STATUS_ERROR;
// check if there is a destination and that the destination is large to keep
// the full image
if (dest == NULL || ptrsrc == NULL)
return VDEV_STATUS_ERROR;
if (dest->data != NULL && dest->w != srcw && dest->h != srch) {
free (dest->data);
dest->data = NULL;
}
if (dest->data == NULL) {
dest->w = srcw;
dest->h = srch;
dest->size = srcw * srch * 3;
dest->data = (unsigned char*) malloc (dest->size);
}
ptrdst = dest->data;
switch (pixelformat) {
case (V4L2_PIX_FMT_RGB32):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
ptrsrc++;
r = *(ptrsrc++);
g = *(ptrsrc++);
b = *(ptrsrc++);
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_BGR32):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
b = *(ptrsrc++);
g = *(ptrsrc++);
r = *(ptrsrc++);
ptrsrc++;
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_UYVY):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
if (xs & 1) {
y1 = (unsigned char)*(ptrsrc + 1);
cr = (unsigned char)*(ptrsrc);
cb = (unsigned char)*(ptrsrc - 2);
}
else {
y1 = (unsigned char)*(ptrsrc + 1);
cr = (unsigned char)*(ptrsrc + 2);
cb = (unsigned char)*(ptrsrc);
}
convert2rgb (y1, cr, cb, &r, &g, &b);
ptrsrc += 2;
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_YUYV):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
if (yd < dest->h) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
if (xs & 1) {
y1 = *(ptrsrc);
cb = *(ptrsrc + 1);
cr = *(ptrsrc - 1);
}
else {
y1 = *(ptrsrc);
cb = *(ptrsrc + 3);
cr = *(ptrsrc + 1);
}
convert2rgb (y1, cr, cb, &r, &g, &b);
ptrsrc += 2;
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = r;
*(ptrdst++) = g;
*(ptrdst++) = b;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
}
break;
case (V4L2_PIX_FMT_MJPEG):
cdata->cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpg_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
// jpeg data and allocations will be destroyed via StopCapture.
printf ("%s:%d %s JPEG Error\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
jpeg_mem_src(&cdata->cinfo, ptrsrc, srcsize);
jpeg_read_header(&cdata->cinfo, TRUE);
jpeg_start_decompress(&cdata->cinfo);
while (cdata->cinfo.output_scanline < cdata->cinfo.output_height) {
unsigned char *temp_array[] = {ptrdst + (cdata->cinfo.output_scanline) * srcw * 3};
jpeg_read_scanlines(&cdata->cinfo, temp_array, 1);
}
jpeg_finish_decompress(&cdata->cinfo);
break;
default:
break;
}
return VDEV_STATUS_OK;
};

@ -0,0 +1,24 @@
#ifndef _CONVERT_H_
#define _CONVERT_H_
#include <list>
#include <string>
#include <setjmp.h>
#include "video.h"
#include "videoframe.h"
#ifndef CLEAR
#define CLEAR(x) memset (&(x), 0, sizeof (x))
#endif
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);
#endif

@ -76,7 +76,7 @@ void Detect::Thread() {
objectX = -1;
objectY = -1;
output.detmatrix = malloc (sizeof(uint32_t) * DET_MAXSHIFT * DET_MAXSHIFT);
output.detmatrix = (uint32_t*)malloc (sizeof(uint32_t) * DET_MAXSHIFT * DET_MAXSHIFT);
while (running) {
// check for new frame
@ -127,7 +127,8 @@ void Detect::Thread() {
output.posx = objectX;
output.posy = objectY;
output.image = &imagegtk;
memcpy (output.detmatrix, detmatrix, sizeof(uint32_t) * DET_MAXSHIFT * DET_MAXSHIFT);
if (detmatrix != NULL)
memcpy (output.detmatrix, detmatrix, sizeof(uint32_t) * DET_MAXSHIFT * DET_MAXSHIFT);
UnLockMutex(); // unlock Config

@ -29,7 +29,7 @@ enum {
struct {
VideoFrame *image; // detected image
uint16_t *detmatrix;
uint32_t *detmatrix;
int posx; // position of the detected object
int posy; // position of the detected object
} typedef DetectOutput;

@ -11,11 +11,14 @@
#include <unistd.h>
#include "gui.h"
#include "video.h"
#include "videodev.h"
#include "videodev-v4l2.h"
#include "filter.h"
#include "detect.h"
#include "configuration.h"
VideoDev videodev;
VideoDev *videodev = NULL;
GThread *videodev_thread = NULL;
GtkWidget *video_da = NULL;
GdkPixbuf *video_pixbuf = NULL;
extern GtkBuilder *_builder_; // work around for threads
@ -36,8 +39,10 @@ gboolean videoctrl_update(gpointer data) {
int max;
int i;
if (videodev == NULL) return TRUE;
for (i = 0; (label = gtk_grid_get_child_at(GTK_GRID(grid), 0, i)) != NULL; i++) {
if (videodev.GetCtrlMinMaxValue(gtk_label_get_text(GTK_LABEL(label)), &min, &max, &value) == VDEV_STATUS_OK) {
if (videodev->GetCtrlMinMaxValue(gtk_label_get_text(GTK_LABEL(label)), &min, &max, &value) == VDEV_STATUS_OK) {
scale = gtk_grid_get_child_at(GTK_GRID(grid), 1, i);
if (!gtk_widget_has_focus(scale))
gtk_range_set_value(GTK_RANGE(scale), value);
@ -250,24 +255,33 @@ void cb_video_btnrefreshlist (GtkWidget *widget, gpointer data) {
gtk_list_store_clear(GTK_LIST_STORE(model));
if (videodev.GetDeviceList(&devlist)) {
for (iter = devlist.begin(); iter != devlist.end(); iter++) {
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), NULL, -1,
0, iter->c_str(),
-1);
}
devlist.clear();
VideoDev vdef1; vdef1.GetDeviceList(&devlist);
VideoDev_V4L2 vdef2; vdef2.GetDeviceList(&devlist);
for (iter = devlist.begin(); iter != devlist.end(); iter++) {
gtk_list_store_insert_with_values(GTK_LIST_STORE(model), NULL, -1,
0, iter->c_str(),
-1);
}
};
gpointer videodev_threadprocess_wrapper (gpointer data) {
if (videodev == NULL) return NULL;
videodev->ThreadProcess();
return NULL;
}
/*
* start recording from the videodev (will start a new thread)
* load list of controls from the video device and create the GtkGrid
* with all elements. Also connect the signals to the callback functions.
*/
void cb_video_btnrec (GtkWidget *widget, gpointer data) {
std::list<std::string> list;
std::list<std::string>::iterator iter;
GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
GtkWidget *cbdevice = gtk_bin_get_child(GTK_BIN(cbox));
GtkWidget *cbres = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videores"));
@ -276,15 +290,34 @@ void cb_video_btnrec (GtkWidget *widget, gpointer data) {
GtkWidget *cbfmtentry = gtk_bin_get_child(GTK_BIN(cbfmt));
GtkWidget *btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
GtkWidget *gridchild = NULL;
int i, w, h;
//
// read values from GUI elements
// read values from GUI elements, and start videodev with the correct driver
std::string device = gtk_entry_get_text(GTK_ENTRY(cbdevice));
std::string driver;
std::string parameter;
i = device.find(' ');
driver = device.substr (0, i);
device = device.substr (i+1, std::string::npos);
device = device.substr (0, device.find(' '));
if (videodev != NULL) {
//
// before we can delete this object we must stop the thread.
//
videodev->Stop();
if (videodev_thread != NULL) g_thread_join (videodev_thread);
videodev_thread = NULL;
delete videodev;
videodev = NULL;
}
if (driver.compare("V4L2") == 0) videodev = new VideoDev_V4L2;
else if (driver.compare("DUMMY") == 0) videodev = new VideoDev;
else videodev = new VideoDev;
std::string format = gtk_entry_get_text(GTK_ENTRY(cbfmtentry));
std::string resolution = gtk_entry_get_text(GTK_ENTRY(cbresentry));
w = h = -1;
@ -294,8 +327,33 @@ void cb_video_btnrec (GtkWidget *widget, gpointer data) {
gtk_widget_set_sensitive(btnstart, false);
gtk_widget_set_sensitive(btnstop, true);
videodev.Start(device, w, h, format, cb_thread_video);
videodev.GetCtrlList(&list);
videodev->SetConfig(device, w, h, format, parameter, cb_thread_video);
videodev_thread = g_thread_new(driver.c_str(), videodev_threadprocess_wrapper, NULL);
// FIXME: workaround, soltuion should be: create a timer and request all controls
};
/*
* after the video is started and we get the first callback from the VideoDev thread
* we will call this function to read all ctrls and creade the grid table.
* function gezts called if: threaddata.runnin == 1
*/
void video_refreshctrls () {
std::list<std::string> list;
std::list<std::string>::iterator iter;
GtkWidget *cbres = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videores"));
GtkWidget *cbresentry = gtk_bin_get_child(GTK_BIN(cbres));
GtkWidget *cbfmt = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videofmt"));
GtkWidget *cbfmtentry = gtk_bin_get_child(GTK_BIN(cbfmt));
GtkWidget *grid = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "vidctrl-grid"));
GtkWidget *gridchild = NULL;
int i, w, h;
std::string format;
std::string resolution;
if (videodev == NULL) return;
videodev->GetCtrlList(&list);
//
// clear grid
@ -310,7 +368,7 @@ void cb_video_btnrec (GtkWidget *widget, gpointer data) {
int max = 100;
int value = 50;
videodev.GetCtrlMinMaxValue((*iter), &min, &max, &value);
videodev->GetCtrlMinMaxValue((*iter), &min, &max, &value);
//
// label
@ -346,25 +404,32 @@ void cb_video_btnrec (GtkWidget *widget, gpointer data) {
//
// read video information
videodev.GetVideoInfo(&w, &h, &format);
videodev->GetVideoInfo(&w, &h, &format);
resolution = std::to_string(w) + "x" + std::to_string(h);
gtk_entry_set_text (GTK_ENTRY(cbresentry), resolution.c_str());
gtk_entry_set_text (GTK_ENTRY(cbfmtentry), format.c_str());
};
/*
* stop recording from the videodev (will stop the running thread)
* we need to wait for the thread to complete and then we can delete this object.
*/
void cb_video_btnstop (GtkWidget *widget, gpointer data) {
GtkWidget *btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
GtkWidget *btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (videodev == NULL) return;
videodev.Stop();
videodev->Stop();
gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false);
if (videodev_thread != NULL) g_thread_join (videodev_thread);
videodev_thread = NULL;
delete videodev;
videodev = NULL;
};
@ -375,7 +440,15 @@ void cb_video_btnstop (GtkWidget *widget, gpointer data) {
gboolean cb_thread_video (gpointer data) {
GtkWidget *btnstart;
GtkWidget *btnstop;
VideoFrame *vf = (VideoFrame *) data;
VideoDevThreadData *cbdata = (VideoDevThreadData*) data;
VideoFrame *vf = NULL;
if (cbdata != NULL) {
vf = &cbdata->vf;
if (cbdata->running == 1) video_refreshctrls();
if (vf->w <= 0 || vf->h <= 0 || vf->data == NULL) vf = NULL;
}
if (videodev == NULL) return false;
btnstop = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-stop"));
btnstart = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "btn-video-rec"));
@ -383,21 +456,24 @@ gboolean cb_thread_video (gpointer data) {
if (video_da == NULL)
video_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "video-da"));
if (vf == NULL) {
printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__);
videodev.Stop();
if (video_pixbuf == NULL) {
video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, 100, 100);
}
if (video_pixbuf == NULL) {
video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, 100, 100);
memset (gdk_pixbuf_get_pixels(video_pixbuf), 0, 3 * gdk_pixbuf_get_height(video_pixbuf) * gdk_pixbuf_get_width(video_pixbuf));
}
if (cbdata == NULL) {
printf ("%s:%d %s something went wrong CBData == NULL\n", __FILE__, __LINE__, __FUNCTION__);
videodev->Stop();
memset (gdk_pixbuf_get_pixels(video_pixbuf), 0, 3 * gdk_pixbuf_get_height(video_pixbuf) * gdk_pixbuf_get_width(video_pixbuf));
gtk_widget_set_sensitive(btnstart, true);
gtk_widget_set_sensitive(btnstop, false);
gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true);
}
else {
else if (vf != NULL) {
int pix_h, pix_w;
videodev.LockMutex();
videodev->LockMutex();
if (video_pixbuf) {
pix_h = gdk_pixbuf_get_height(video_pixbuf);
pix_w = gdk_pixbuf_get_width(video_pixbuf);
@ -414,9 +490,9 @@ gboolean cb_thread_video (gpointer data) {
}
videoframe_to_pixbuf(video_pixbuf, vf);
detect.NewFrame(vf);
videodev.UnLockMutex();
videodev->UnLockMutex();
gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true);
}
gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true);
return false;
};
@ -426,7 +502,8 @@ gboolean cb_thread_video (gpointer data) {
* set ctrl on the device
*/
void videoctrl_set(std::string name, int value) {
videodev.SetCtrlValue(name, value);
if (videodev == NULL) return;
videodev->SetCtrlValue(name, value);
}
@ -529,11 +606,13 @@ void cb_video_pre_click (GtkWidget *widget, gpointer data) {
int idx;
GtkWidget *btn;
if (videodev == NULL) return;
if (pushtime > BTNDOWN_TIME && presetbtn.idx >= 0 && presetbtn.idx < BTN_PRESET_MAX) {
//
// save the settings
//
list<VideoDevCtrl> ctrls = videodev.GetCtrlsMinMaxValue();
list<VideoDevCtrl> ctrls = videodev->GetCtrlsMinMaxValue();
config.SetPresetButton(presetbtn.idx, &ctrls);
//
@ -558,9 +637,9 @@ void cb_video_pre_click (GtkWidget *widget, gpointer data) {
for (retry = 5; retry > 0 && again; retry--) {
again = 0;
for (iter = ctrls.begin(); iter != ctrls.end(); iter++) {
videodev.SetCtrlValue(iter->name, iter->value);
videodev->SetCtrlValue(iter->name, iter->value);
usleep(10000);
videodev.GetCtrlMinMaxValue(iter->name, NULL, NULL, &i);
videodev->GetCtrlMinMaxValue(iter->name, NULL, NULL, &i);
if (i != iter->value) again = 1;
}
}

@ -18,128 +18,7 @@
#include "gui.h"
#include "config.h"
#include "videoframe.h"
enum {
IOMODE_READ,
IOMODE_MMAP
};
enum {
VDEV_STATUS_UNKNOWN,
VDEV_STATUS_OK,
VDEV_STATUS_AGAIN,
VDEV_STATUS_ERROR
};
//
// jpeg error handling
//
struct jpg_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct jpg_error_mgr *jpg_error_ptr;
// callback status
enum {
VDEV_CBSTATUS_NOTHING = 1,
VDEV_CBSTATUS_NEWFRAME,
VDEV_CBSTATUS_ERROR
};
struct {
unsigned int id;
int min;
int max;
int value;
std::string name;
} typedef VideoDevCtrl;
struct {
unsigned int size;
unsigned char* data;
} typedef VideoInBuffer;
#define VDEV_INBUFFERS 3
class VideoDev {
private:
std::string conf_device;
std::string conf_devicename;
std::string conf_format;
int conf_width;
int conf_height;
gboolean (*callback)(gpointer data);
int running;
GMutex mutex;
GThread *thread;
int inbuffer_idx;
VideoInBuffer inbuffer[VDEV_INBUFFERS];
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
VideoFrame vf;
std::list<VideoDevCtrl> vidctrls;
struct jpg_error_mgr jerr;
struct jpeg_decompress_struct cinfo;
int vf_rec;
int vf_get;
int io; // IO Mode
int fd;
int InitMMap();
int InitUserPtr();
int CaptureStart();
int CaptureStop();
int UnInit();
int Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize,
uint32_t pixelformat, int srcw, int srch);
int OpenInit ();
int Close ();
int Grab(VideoFrame *vf);
int SetDevCtrl(unsigned int id, int value);
int GetDevCtrl(unsigned int id, int *value);
int xioctl (int fd, int request, void *arg);
public:
VideoDev();
~VideoDev();
void Thread();
int Start(std::string dev, int w, int h, std::string format, gboolean (*callback_func)(gpointer data));
int Stop(); // stop video processing and stop the pthread
void GetVideoInfo(int *w, int *h, std::string *format);
int GetDeviceList(std::list<std::string> *list);
int GetCtrlList(std::list<std::string> *list);
int GetCtrlMinMaxValue(std::string name, int *min, int *max, int *value);
list<VideoDevCtrl> GetCtrlsMinMaxValue();
int SetCtrlValue(std::string name, int value);
int IsRunning() { return running; };
void PrintCaps (uint32_t caps);
void PrintFmt(struct v4l2_format *f);
void LockMutex() { g_mutex_lock(&mutex); };
void UnLockMutex() { g_mutex_unlock(&mutex); };
std::string GetDevice() { return conf_device; };
};
extern VideoDev videodev;
#include "videodev.h"
extern void videoframe_to_pixbuf(GdkPixbuf* dest, VideoFrame *src);

@ -0,0 +1,543 @@
#include "convert.h"
#include "videodev-v4l2.h"
VideoDev_V4L2::VideoDev_V4L2() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
io = IOMODE_MMAP;
fd = -1;
inbuffer_idx = 0;
};
VideoDev_V4L2::~VideoDev_V4L2() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (running > 0) CaptureStop();
if (fd >= 0) Close ();
}
/*
* try to send ioctl command as long as EINTR is valid. But abort after 2 seconds.
*/
int VideoDev_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;
};
/*
* return a list of /dev/video* devices found on the system, and read out its human friendly name
* output will be a lit of: "V4L2 /dev/videoX [Name]"
*/
int VideoDev_V4L2::GetDeviceList(std::list<std::string> *list) {
std::string device;
int devnum;
if (list == NULL) return 0;
for (devnum = 0; devnum < 255; devnum++) {
device = "/dev/video"+std::to_string(devnum);
if (device.compare (conf_device) != 0) {
int fd;
struct v4l2_capability vcap;
if((fd = open(device.c_str(), O_RDONLY)) == -1){
continue;
}
if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1)
strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card));
close(fd);
device += " [" + (std::string) ((char*)vcap.card) + "]";
}
else {
device += " [" + (std::string) conf_devicename + "]";
}
list->push_back("V4L2 " + device);
}
return 1;
}
/*
* print out the important capabilities, which are reported by the V4L2 device
*/
void VideoDev_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");
}
/*
* convert the V4L2 Format data into a text humans can read.
* the pixelformat is coded in 4 chars, maybe we need a general coding table
* convert still need the v4l2 pixelformats.
* as soon as we support GPhoto or something else we need our own pixelformat table.
*/
void VideoDev_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*)&fmt.fmt.pix.pixelformat)[0],
((char*)&fmt.fmt.pix.pixelformat)[1],
((char*)&fmt.fmt.pix.pixelformat)[2],
((char*)&fmt.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);
}
/*
* Open Device
* prepare the buffer, InitMMAP and read all controls
*/
int VideoDev_V4L2::Open() {
int i;
struct v4l2_capability vcap;
VideoDevCtrl vctl;
char txt[32];
printf ("%s:%d %s Device: '%s'\n", __FILE__, __LINE__, __FUNCTION__, conf_device.c_str());
if (fd != -1) return VDEV_STATUS_ERROR;
//
// open device and get device name and capabilities | O_NONBLOCK
if((fd = open(conf_device.c_str(), O_RDWR)) == -1){
return VDEV_STATUS_ERROR;
}
if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1)
strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card));
conf_devicename = (char*) vcap.card;
printf ("%s:%d %s Capabilities: %u\n", __FILE__, __LINE__, __FUNCTION__, vcap.capabilities);
PrintCaps(vcap.capabilities);
printf ("%s:%d %s Device Capabilities: %u\n", __FILE__, __LINE__, __FUNCTION__, vcap.device_caps);
PrintCaps(vcap.device_caps);
if (!(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
printf ("%s:%d %s device has no video capture capabilities\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
Close();
}
//
// query controls
struct v4l2_queryctrl queryctrl;
uint32_t min;
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);
}
}
//
// check for cropping.. if we have it setup default
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)) {
printf ("%s:%d %s VIDEOC_S_CROP Errorcode: %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno));
}
}
//
// prepare resolution and pixelformat
CLEAR (fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (conf_height != -1 && conf_width != -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;
}
if (conf_format.length() > 0) { // pixelformat
if (conf_format.compare("MJPG") == 0) fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
else if (conf_format.compare("YUYV") == 0) fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
else if (conf_format.compare("RGB4") == 0) fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
else fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
}
else {
// fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
}
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) {
fprintf (stderr, "%s:%d VIDIOC_S_FMT : %s\n", __FILE__, __LINE__, strerror (errno));
return VDEV_STATUS_ERROR;
}
// 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_format = txt;
PrintFmt (&fmt);
// init buffers
switch (io) {
case IOMODE_MMAP:
if (InitMMap() == VDEV_STATUS_ERROR)
Close();
break;
case IOMODE_READ:
default:
break;
}
return VDEV_STATUS_OK;
};
/*
* prepare memory mapped buffers
*/
int VideoDev_V4L2::InitMMap() {
struct v4l2_requestbuffers bufreq;
struct v4l2_buffer bufinfo;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
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 mappingn", conf_device.c_str());
return VDEV_STATUS_ERROR;
} else {
printf ("%s:%d %s Error %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno));
return VDEV_STATUS_ERROR;
}
}
if (bufreq.count < 1) {
printf ( "Insufficient buffer memory on %s\n", conf_device.c_str());
return VDEV_STATUS_ERROR;
}
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 VDEV_STATUS_OK;
}
/*
* Close Device
* Free videobuffer
*/
int VideoDev_V4L2::Close() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (fd >= 0) {
UnInit();
close (fd);
fd = -1;
}
conf_device = "";
conf_devicename = "";
return VDEV_STATUS_OK;
};
int VideoDev_V4L2::UnInit() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (io) {
case IOMODE_READ:
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 VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
};
/*****************************************************************************************************
* VideoGrabbing
*/
/*
* send the start capture signal to the cam
*/
int VideoDev_V4L2::CaptureStart() {
enum v4l2_buf_type type;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (io) {
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)) {
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR;
}
}
inbuffer_idx = 0;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) {
printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR;
}
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
ConvertStart(&cdata, fmt.fmt.pix.pixelformat);
return VDEV_STATUS_OK;
};
int VideoDev_V4L2::CaptureStop() {
enum v4l2_buf_type type;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
ConvertStop(&cdata, fmt.fmt.pix.pixelformat);
switch (io) {
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 VDEV_STATUS_ERROR;
}
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
};
/*
* try to grab one frame and convert it into RGB32.
* If something goes wrong return an error code.
* Return code VDEV_STATUS_AGAIN is not an error. There was no video image ready to read.
*/
int VideoDev_V4L2::Grab(VideoFrame *vf) {
struct v4l2_buffer buf;
int len;
if (vf == NULL) return VDEV_STATUS_ERROR;
switch (io) {
case IOMODE_READ:
if ((len = read (fd, inbuffer[0].data, fmt.fmt.pix.sizeimage)) == -1) {
switch (errno) {
case EAGAIN:
return VDEV_STATUS_AGAIN;
case EIO:
default:
printf ("v4l2_grab IOM_READ: %s dest:%p size:%d\n", strerror (errno), vf, fmt.fmt.pix.sizeimage);
return VDEV_STATUS_ERROR;
}
}
else {
LockMutex();
Convert(&cdata, vf, inbuffer[0].data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
UnLockMutex();
}
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 VDEV_STATUS_AGAIN;
case EIO:
printf ( "%s:%d error on VIDIOC_DQBUF EIO %s\n", __FILE__, __LINE__, strerror(errno));
default:
printf ( "%s:%d error on VIDIOC_DQBUF %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR;
}
}
if (buf.index >= 0 && buf.index < VDEV_INBUFFERS) {
LockMutex();
Convert(&cdata, vf, inbuffer[buf.index].data, buf.bytesused, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
UnLockMutex();
}
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR; }
if (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0;
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
}
/*****************************************************************************************************
* Controls
*/
/*
* set video control identified by id
*/
int VideoDev_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 VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
};
/*
* get video control identified by id
*/
int VideoDev_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 VDEV_STATUS_ERROR;
}
*value = ctrl.value;
return VDEV_STATUS_OK;
};

@ -0,0 +1,71 @@
#ifndef _H_VIDEODEV_V4L2_H_
#define _H_VIDEODEV_V4L2_H_
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <list>
#include <string>
#include "convert.h"
#include "gui.h"
#include "videodev.h"
enum {
IOMODE_READ,
IOMODE_MMAP
};
#define VDEV_INBUFFERS 3
class VideoDev_V4L2: public VideoDev {
private:
int io; // IO Mode
int fd;
int inbuffer_idx;
VideoInBuffer inbuffer[VDEV_INBUFFERS];
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
ConvertData cdata;
int Grab(VideoFrame *vf);
int Open();
int Close();
int CaptureStart();
int CaptureStop();
int SetDevCtrl(unsigned int id, int value);
int GetDevCtrl(unsigned int id, int *value);
int InitMMap();
int UnInit();
void PrintCaps(uint32_t caps);
void PrintFmt (struct v4l2_format *f);
int xioctl(int fd, int request, void *arg);
public:
VideoDev_V4L2();
~VideoDev_V4L2();
int GetDeviceList(std::list<std::string> *list);
};
#endif

@ -25,132 +25,45 @@
#include "gui.h"
#include "video.h"
#define CLEAR(x) memset (&(x), 0, sizeof (x))
//
// jpeg: replacement for error_exit
//
METHODDEF(void) jpg_error_exit (j_common_ptr cinfo) {
jpg_error_ptr myerr = (jpg_error_ptr) cinfo->err;
(*cinfo->err->output_message) (cinfo);
longjmp(myerr->setjmp_buffer, 1);
}
//
// clamp and convert2rgb is build on the sample of the v4l2 api documentation
//
inline unsigned char clamp (double x) {
int r = (int)x;
if (r < 0) return 0;
else if (r > 255) return 255;
else return r;
};
inline void convert2rgb (unsigned char Y1, unsigned char Cb, unsigned char Cr,
unsigned char *ER, unsigned char *EB, unsigned char *EG) {
register int y1, pb, pr;
y1 = Y1 - 16;
pb = Cb - 128;
pr = Cr - 128;
*ER = clamp (y1 + 1.402 * pr);
*EB = clamp (y1 - 0.344 * pb - 0.714 * pr);
*EG = clamp (y1 + 1.772 * pb);
};
//
// C / C++ Wrapper for the thread function
//
gpointer _VideoDevThread (gpointer data) {
videodev.Thread ();
return NULL;
};
#include "videoframe.h"
#include "videodev.h"
VideoDev::VideoDev() {
int i;
for (i = 0; i < VDEV_INBUFFERS; i++) {
inbuffer[i].size = 0;
inbuffer[i].data = NULL;
}
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
conf_device = "";
conf_devicename = "";
conf_format = "";
conf_height = -1;
conf_width = -1;
vf.data = NULL;
vf.h = 0;
vf.w = 0;
vf.size = 0;
conf_parameter = "";
conf_height = 800;
conf_width = 600;
running = 0;
callback = NULL;
thread = NULL;
fd = -1;
io = IOMODE_MMAP;
inbuffer_idx = 0;
CLEAR(cropcap);
CLEAR(crop);
CLEAR(fmt);
g_mutex_init (&mutex);
Close(); // will reset almost everything
};
#define THREAD_WAITWARNING_TO 10000000
VideoDev::~VideoDev() {
Stop();
}
//
// return list of devices in form of /dev/videoYY [Name]
//
int VideoDev::GetDeviceList(std::list<std::string> *list) {
std::string device;
int devnum;
if (list == NULL) return 0;
list->clear();
for (devnum = 0; devnum < 255; devnum++) {
device = "/dev/video"+std::to_string(devnum);
int to = 0;
if (device.compare (conf_device) != 0) {
int fd;
struct v4l2_capability vcap;
if((fd = open(device.c_str(), O_RDONLY)) == -1){
continue;
}
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1)
strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card));
close(fd);
device += " [" + (std::string) ((char*)vcap.card) + "]";
}
else {
device += " [" + (std::string) conf_devicename + "]";
if (running) Stop();
while (running > 0) {
if (--to <= 0) {
to = THREAD_WAITWARNING_TO;
printf ("%s:%d %s still waiting for complete\n", __FILE__, __LINE__, __FUNCTION__);
}
list->push_back(device);
}
return 1;
}
//
// return a list of strings for controls
//
/*
* fill the list with all found controls.
*/
int VideoDev::GetCtrlList(std::list<std::string> *list) {
std::list<VideoDevCtrl>::iterator iter;
if (list == NULL) return VDEV_STATUS_ERROR;
@ -168,9 +81,10 @@ int VideoDev::GetCtrlList(std::list<std::string> *list) {
};
//
// return the values for an control, on error VDEV_STATUS_UNKNOWN
//
/*
* if the control is found, fill out all the fields and return with VDEV_STATUS_OK,
* on error VDEV_STATUS_UNKNOWN. In case he control have not been found.
*/
int VideoDev::GetCtrlMinMaxValue(std::string name, int *min, int *max, int *value) {
std::list<VideoDevCtrl>::iterator iter;
LockMutex();
@ -205,70 +119,26 @@ int VideoDev::SetCtrlValue(std::string name, int value) {
};
//
// start the video, start capturing, start thread
// after return of this function we can call the Ctrl thread
//
int VideoDev::Start(std::string dev, int w, int h, std::string format, gboolean (*callback_func)(gpointer data)) {
if (running != 0 || thread != NULL) return VDEV_STATUS_ERROR;
running = 1;
/*
* before is it possible to Start the VideoGrabbing it is mandatory to set up some values
*/
void VideoDev::SetConfig(std::string dev, int w, int h, std::string format, std::string parameter, gboolean (*callback_func)(gpointer data)) {
conf_device = dev;
conf_format = format;
conf_parameter = parameter;
conf_width = w;
conf_height = h;
callback = callback_func;
//
// open and init device buffers device
if (OpenInit() != VDEV_STATUS_OK) {
Close();
return VDEV_STATUS_ERROR;
}
//
// start capturing
if (CaptureStart() != VDEV_STATUS_OK) {
Close();
return VDEV_STATUS_ERROR;
}
thread = g_thread_new("VideoDev", _VideoDevThread, NULL);
return VDEV_STATUS_OK;
};
int VideoDev::Stop() {
if (running == 1) {
running = 0; // we can jump directly to 0
}
if (thread) {
g_thread_join (thread);
thread = NULL;
}
return VDEV_STATUS_OK;
};
void VideoDev::GetVideoInfo(int *w, int *h, std::string *format) {
*format = conf_format;
*w = conf_width;
*h = conf_height;
}
//
// try to read a video every 0.05ms (25hz)
// running = 2 ... thread is running normaly
// running = 1 ... thread need to close wait for GTK to set running to 0
// running = 0 ... thread stopped and all handlers can be freed
//
/*
* start the video, start capturing, start thread
* after return of this function we can call the Ctrl thread
* this function should onyl be called after everything is setup with SetConfig(..)
*/
#define CYCLETIME 0.050
void VideoDev::Thread() {
void VideoDev::ThreadProcess() {
struct timeval cycle_timestamp;
int lastsec = 0;
float cycle_time = 0.0;
@ -278,22 +148,40 @@ void VideoDev::Thread() {
printf ("%s:%d %s Enter\n", __FILE__, __LINE__, __FUNCTION__);
cycle_time = get_cycletime(&cycle_timestamp); // just start counting
running = 1;
//
// read untill something bad happens..
// open and init device buffers device
if (Open() != VDEV_STATUS_OK) {
Close();
running = 0;
}
//
// start capturing
if (CaptureStart() != VDEV_STATUS_OK) {
Close();
running = 0;
}
threaddata.running = 1;
if (callback) gdk_threads_add_idle(callback, &threaddata);
threaddata.running = 2;
while (running) {
i = Grab(&vf);
i = Grab(&threaddata.vf);
switch (i) {
case VDEV_STATUS_OK:
if (callback) gdk_threads_add_idle(callback, &vf);
if (callback) gdk_threads_add_idle(callback, &threaddata);
break;
case VDEV_STATUS_AGAIN:
break;
default:
running = 0;
}
default:
running = 0;
break;
}
//
// keep 25fps, write every second a message
cycle_time = get_cycletime(&cycle_timestamp);
@ -308,661 +196,27 @@ void VideoDev::Thread() {
//
// stop capturing
if (callback) gdk_threads_add_idle(callback, NULL);
CaptureStop();
UnInit();
Close();
printf ("%s:%d %s Exit\n", __FILE__, __LINE__, __FUNCTION__);
};
void VideoDev::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 VideoDev::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*)&fmt.fmt.pix.pixelformat)[0],
((char*)&fmt.fmt.pix.pixelformat)[1],
((char*)&fmt.fmt.pix.pixelformat)[2],
((char*)&fmt.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);
}
int VideoDev::Grab(VideoFrame *vf) {
struct v4l2_buffer buf;
int len;
if (vf == NULL) return VDEV_STATUS_ERROR;
switch (io) {
case IOMODE_READ:
if ((len = read (fd, inbuffer[0].data, fmt.fmt.pix.sizeimage)) == -1) {
switch (errno) {
case EAGAIN:
return VDEV_STATUS_AGAIN;
case EIO:
default:
printf ("v4l2_grab IOM_READ: %s dest:%p size:%d\n", strerror (errno), vf[vf_rec].data, fmt.fmt.pix.sizeimage);
return VDEV_STATUS_ERROR;
}
}
else {
LockMutex();
Convert(vf, inbuffer[0].data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
UnLockMutex();
}
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 VDEV_STATUS_AGAIN;
case EIO:
printf ( "%s:%d error on VIDIOC_DQBUF EIO %s\n", __FILE__, __LINE__, strerror(errno));
default:
printf ( "%s:%d error on VIDIOC_DQBUF %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR;
}
}
if (buf.index >= 0 && buf.index < VDEV_INBUFFERS) {
LockMutex();
Convert(vf, inbuffer[buf.index].data, buf.bytesused, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height);
UnLockMutex();
}
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR; }
if (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0;
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
}
/*
* Open Device
*/
int VideoDev::OpenInit() {
int i;
struct v4l2_capability vcap;
VideoDevCtrl vctl;
char txt[32];
printf ("%s:%d %s Device: '%s'\n", __FILE__, __LINE__, __FUNCTION__, conf_device.c_str());
if (fd != -1) return VDEV_STATUS_ERROR;
//
// open device and get device name and capabilities | O_NONBLOCK
if((fd = open(conf_device.c_str(), O_RDWR)) == -1){
return VDEV_STATUS_ERROR;
}
if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1)
strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card));
conf_devicename = (char*) vcap.card;
printf ("%s:%d %s Capabilities: %u\n", __FILE__, __LINE__, __FUNCTION__, vcap.capabilities);
PrintCaps(vcap.capabilities);
printf ("%s:%d %s Device Capabilities: %u\n", __FILE__, __LINE__, __FUNCTION__, vcap.device_caps);
PrintCaps(vcap.device_caps);
if (!(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
printf ("%s:%d %s device has no video capture capabilities\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
Close();
}
//
// query controls
struct v4l2_queryctrl queryctrl;
uint32_t min;
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);
}
}
//
// check for cropping.. if we have it setup default
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)) {
printf ("%s:%d %s VIDEOC_S_CROP Errorcode: %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno));
}
}
printf ("%s:%d %s Exit Thread\n", __FILE__, __LINE__, __FUNCTION__);
//
// prepare resolution and pixelformat
CLEAR (fmt);
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (conf_height != -1 && conf_width != -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;
}
if (conf_format.length() > 0) { // pixelformat
if (conf_format.compare("MJPG") == 0) fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
else if (conf_format.compare("YUYV") == 0) fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
else if (conf_format.compare("RGB4") == 0) fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
else fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
}
else {
// fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
}
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) {
fprintf (stderr, "VIDIOC_S_FMT : %s", strerror (errno));
return VDEV_STATUS_ERROR;
}
// 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_format = txt;
PrintFmt (&fmt);
// init buffers
switch (io) {
case IOMODE_MMAP:
if (InitMMap() == VDEV_STATUS_ERROR)
Close();
break;
case IOMODE_READ:
default:
break;
}
return VDEV_STATUS_OK;
return;
};
/*
* set video control identified by id
*/
int VideoDev::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 VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
};
/*
* get video control identified by id
*/
int VideoDev::GetDevCtrl(unsigned int id, int *value) {
struct v4l2_control ctrl;
CLEAR(ctrl);
ctrl.id = id;
if (-1 == xioctl (fd, VIDIOC_G_CTRL, &ctrl)) {
return VDEV_STATUS_ERROR;
}
*value = ctrl.value;
return VDEV_STATUS_OK;
};
/*
* prepare memory mapped buffers
*/
int VideoDev::InitMMap() {
struct v4l2_requestbuffers bufreq;
struct v4l2_buffer bufinfo;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
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 mappingn", conf_device.c_str());
return VDEV_STATUS_ERROR;
} else {
printf ("%s:%d %s Error %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno));
return VDEV_STATUS_ERROR;
}
}
if (bufreq.count < 1) {
printf ( "Insufficient buffer memory on %s\n", conf_device.c_str());
return VDEV_STATUS_ERROR;
}
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 VDEV_STATUS_OK;
}
/*
* send the start capture signal to the cam
*/
int VideoDev::CaptureStart() {
enum v4l2_buf_type type;
void VideoDev::Stop() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (io) {
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)) {
printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR;
}
}
inbuffer_idx = 0;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) {
printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno));
return VDEV_STATUS_ERROR;
}
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
jpeg_create_decompress(&cinfo);
}
return VDEV_STATUS_OK;
};
int VideoDev::CaptureStop() {
enum v4l2_buf_type type;
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
jpeg_destroy_decompress(&cinfo);
}
switch (io) {
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 VDEV_STATUS_ERROR;
}
break;
default:
printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
};
int VideoDev::UnInit() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
switch (io) {
case IOMODE_READ:
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 VDEV_STATUS_ERROR;
}
return VDEV_STATUS_OK;
};
/*
* Close Device
* Free videobuffer
*/
int VideoDev::Close() {
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
if (fd >= 0) {
close (fd);
fd = -1;
}
conf_device = "";
conf_devicename = "";
vf_rec = -1;
vf_get = -1;
return VDEV_STATUS_OK;
};
/*
* try to send ioctl command as long as EINTR is valid. But abort after 2 seconds.
*/
int VideoDev::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;
if (running == 1) running = 0;
return;
};
/*
* converts the video from input type to RGB24 type - 24Bit
*/
int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch) {
int xs, ys;
int xd, yd;
unsigned char r,g,b;
unsigned char cb, cr, y1;
unsigned char *ptrdst = NULL;
// check if there is a destination and that the destination is large to keep
// the full image
if (dest == NULL || ptrsrc == NULL)
return VDEV_STATUS_ERROR;
if (dest->data != NULL && dest->w != srcw && dest->h != srch) {
free (dest->data);
dest->data = NULL;
}
if (dest->data == NULL) {
dest->w = srcw;
dest->h = srch;
dest->size = srcw * srch * 3;
dest->data = (unsigned char*) malloc (dest->size);
}
ptrdst = dest->data;
switch (pixelformat) {
case (V4L2_PIX_FMT_RGB32):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
ptrsrc++;
r = *(ptrsrc++);
g = *(ptrsrc++);
b = *(ptrsrc++);
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_BGR32):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
b = *(ptrsrc++);
g = *(ptrsrc++);
r = *(ptrsrc++);
ptrsrc++;
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_UYVY):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
if (xs & 1) {
y1 = (unsigned char)*(ptrsrc + 1);
cr = (unsigned char)*(ptrsrc);
cb = (unsigned char)*(ptrsrc - 2);
}
else {
y1 = (unsigned char)*(ptrsrc + 1);
cr = (unsigned char)*(ptrsrc + 2);
cb = (unsigned char)*(ptrsrc);
}
convert2rgb (y1, cr, cb, &r, &g, &b);
ptrsrc += 2;
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = b;
*(ptrdst++) = g;
*(ptrdst++) = r;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
break;
case (V4L2_PIX_FMT_YUYV):
for (ys = 0, yd = 0; ys < (signed int)srch; ys++) {
if (yd < dest->h) {
for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) {
/* read the pixel */
if (xs & 1) {
y1 = *(ptrsrc);
cb = *(ptrsrc + 1);
cr = *(ptrsrc - 1);
}
else {
y1 = *(ptrsrc);
cb = *(ptrsrc + 3);
cr = *(ptrsrc + 1);
}
convert2rgb (y1, cr, cb, &r, &g, &b);
ptrsrc += 2;
/* only paint the image if the source is within the destination */
if (xd < dest->w) {
/* set the pixel */
*(ptrdst++) = r;
*(ptrdst++) = g;
*(ptrdst++) = b;
xd++;
}
}
/* if the source image is too small ignore the other places.. */
if (xd < dest->w)
ptrdst += 3 * (dest->w - xd);
yd++;
}
}
break;
case (V4L2_PIX_FMT_MJPEG):
cinfo.err = jpeg_std_error(&jerr.pub);
jerr.pub.error_exit = jpg_error_exit;
if (setjmp(jerr.setjmp_buffer)) {
// jpeg data and allocations will be destroyed via StopCapture.
printf ("%s:%d %s JPEG Error\n", __FILE__, __LINE__, __FUNCTION__);
return VDEV_STATUS_ERROR;
}
jpeg_mem_src(&cinfo, ptrsrc, srcsize);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
while (cinfo.output_scanline < cinfo.output_height) {
unsigned char *temp_array[] = {ptrdst + (cinfo.output_scanline) * srcw * 3};
jpeg_read_scanlines(&cinfo, temp_array, 1);
}
jpeg_finish_decompress(&cinfo);
break;
default:
break;
}
return VDEV_STATUS_OK;
};
void VideoDev::GetVideoInfo(int *w, int *h, std::string *format) {
*format = conf_format;
*w = conf_width;
*h = conf_height;
}
list<VideoDevCtrl> VideoDev::GetCtrlsMinMaxValue() {
@ -970,4 +224,3 @@ list<VideoDevCtrl> VideoDev::GetCtrlsMinMaxValue() {
};

@ -0,0 +1,149 @@
#ifndef _VIDEODEV_H_
#define _VIDEODEV_H_
#include <stdint.h>
#include <jpeglib.h>
#include <setjmp.h>
#include <string.h>
#include <string>
#include <list>
#include "json.h"
#include "gui.h"
#include "config.h"
#include "video.h"
#include "videoframe.h"
enum {
VDEV_STATUS_UNKNOWN,
VDEV_STATUS_OK,
VDEV_STATUS_AGAIN,
VDEV_STATUS_ERROR
};
//
// jpeg error handling
//
struct jpg_error_mgr {
struct jpeg_error_mgr pub; /* "public" fields */
jmp_buf setjmp_buffer; /* for return to caller */
};
typedef struct jpg_error_mgr *jpg_error_ptr;
// callback status
enum {
VDEV_CBSTATUS_NOTHING = 1,
VDEV_CBSTATUS_NEWFRAME,
VDEV_CBSTATUS_ERROR
};
struct {
unsigned int id;
int min;
int max;
int value;
std::string name;
} typedef VideoDevCtrl;
struct {
unsigned int size;
unsigned char* data;
} typedef VideoInBuffer;
/*
* data which we send to the main thread
*/
struct {
int running;
VideoFrame vf;
} typedef VideoDevThreadData;
#ifndef CLEAR
#define CLEAR(x) memset (&(x), 0, sizeof (x))
#endif
/*
* the run Start(..) needs to be called from a created thread process. It will not return until
* another thread called the Stop() function or an error occured.
*
* New Devices will need to rewrite the following virtual functions:
* Grab(), Open(), Close(), SetDevCtrl(), GetDevGtrl(), GetDeviceList(), GetCtrlList()
*/
class VideoDev {
private:
std::string conf_device; // device or filename to connect to
std::string conf_devicename; // human friendly name of the device
std::string conf_format; // video or image format (should every device have)
std::string conf_parameter; // can hold additional parameters
int conf_height;
int conf_width;
int running; // 0 ... not running
// 1 ... initialized (init, first frame)
// 2 ... running
GMutex mutex;
gboolean (*callback)(gpointer data);
VideoDevThreadData threaddata;
std::list<VideoDevCtrl> vidctrls;
/* grabs a single frame, writes the RGB24 converted frame to the VideoFrame pointer */
virtual int Grab(VideoFrame *vf) { return VDEV_STATUS_AGAIN; };
/* opens the device, will need to fill the controls as well */
virtual int Open() { return VDEV_STATUS_OK; };
/* close the device */
virtual int Close() { return VDEV_STATUS_OK; };
/* close the device */
virtual int CaptureStart() { return VDEV_STATUS_OK; };
/* close the device */
virtual int CaptureStop() { return VDEV_STATUS_OK; };
/* set the control */
virtual int SetDevCtrl(unsigned int id, int value) { return VDEV_STATUS_OK; };
/* read a value from the control */
virtual int GetDevCtrl(unsigned int id, int *value) { return VDEV_STATUS_OK; };
friend class VideoDev_V4L2;
public:
VideoDev();
virtual ~VideoDev();
void SetConfig(std::string dev, int w, int h, std::string format, std::string parameter, gboolean (*callback_func)(gpointer data));
void ThreadProcess();
void Stop();
int IsRunning() { return running; };
void LockMutex() { g_mutex_lock(&mutex); };
void UnLockMutex() { g_mutex_unlock(&mutex); };
void GetVideoInfo(int *w, int *h, std::string *format);
std::string GetDevice() { return conf_device; };
int GetCtrlMinMaxValue(std::string name, int *min, int *max, int *value);
int SetCtrlValue(std::string name, int value);
/* fills the list with all found device */
virtual int GetDeviceList(std::list<std::string> *list) { return VDEV_STATUS_OK; };
int GetCtrlList(std::list<std::string> *list);
/* returns a list with all control and thier parameters */
list<VideoDevCtrl> GetCtrlsMinMaxValue();
};
#endif
Loading…
Cancel
Save