diff --git a/ChangeLog b/ChangeLog
index e69de29..a25389d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -0,0 +1,4 @@
+
+2021-09-08:
+- grab video from old type of cameras is working
+
diff --git a/gui.cc b/gui.cc
index a3b9744..fca6062 100644
--- a/gui.cc
+++ b/gui.cc
@@ -25,11 +25,19 @@ gboolean cb_window_delete_event (GtkWidget *widget, GdkEvent *event, gpointer
return FALSE;
};
+
+
void cb_window_show (GtkWidget *widget, gpointer data) {
-// GtkBuilder *builder = (GtkBuilder *) data;
-// g_timeout_add(100, gui_loop, NULL);
+ 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__);
+
+ gtk_widget_set_sensitive(btnstart, true);
+ gtk_widget_set_sensitive(btnstop, false);
};
+
void displayerror (std::string error) {
GtkWidget *dialog;
GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (_builder_, "main-window"));
@@ -44,3 +52,4 @@ void displayerror (std::string error) {
};
+
diff --git a/gui.h b/gui.h
index 6a7ed5c..aed1b21 100644
--- a/gui.h
+++ b/gui.h
@@ -33,8 +33,12 @@ G_MODULE_EXPORT void cb_window_show (GtkWidget *widget, gpointer data);
G_MODULE_EXPORT gboolean cb_window_delete_event (GtkWidget *widget,
GdkEvent *event, gpointer data);
-G_MODULE_EXPORT void cb_video_refreshlist (GtkWidget *widget, gpointer data);
+//
+// video and video devices
+G_MODULE_EXPORT void cb_video_btnrefreshlist (GtkWidget *widget, gpointer data);
+G_MODULE_EXPORT void cb_video_btnrec (GtkWidget *widget, gpointer data);
+G_MODULE_EXPORT void cb_video_btnstop (GtkWidget *widget, gpointer data);
//
// thread handles
@@ -45,5 +49,7 @@ G_MODULE_EXPORT gboolean cb_thread_video (gpointer data);
#endif
+extern float get_cycletime(struct timeval *t);
+
#endif // _GUI_H_
diff --git a/simpleskycam.ui b/simpleskycam.ui
index 1234aff..f3be78e 100644
--- a/simpleskycam.ui
+++ b/simpleskycam.ui
@@ -23,7 +23,7 @@
True
True
-
False
diff --git a/video.cc b/video.cc
index f112426..66144b4 100644
--- a/video.cc
+++ b/video.cc
@@ -10,8 +10,253 @@
#include "video.h"
VideoDev videodev;
+GtkWidget *video_da = NULL;
+GdkPixbuf *video_pixbuf;
-void cb_video_refreshlist (GtkWidget *widget, gpointer data) {
+
+//
+// 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);
+};
+
+
+
+void video_convert (VideoFrame *vf, GdkPixbuf *pixbuf) {
+ int xs, ys;
+ int xd, yd;
+ int dst_w, dst_h;
+ unsigned char r,g,b;
+ unsigned char cb, cr, y1;
+ unsigned char *ptrdst = gdk_pixbuf_get_pixels(pixbuf);
+ unsigned char *ptrsrc = vf->data;
+
+ /* check if there is a format and buffer for the current frame.. */
+ if (vf == NULL || vf->data == NULL || ptrdst == NULL)
+ return;
+
+ dst_w = gdk_pixbuf_get_width(pixbuf);
+ dst_h = gdk_pixbuf_get_height(pixbuf);
+
+ switch (vf->format) {
+ case (V4L2_PIX_FMT_RGB32):
+ for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
+ for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) {
+ /* read the pixel */
+
+ ptrsrc++;
+ r = *(ptrsrc++);
+ g = *(ptrsrc++);
+ b = *(ptrsrc++);
+
+ /* only paint the image if the source is within the destination */
+ if (xd < dst_w) {
+ /* set the pixel */
+ *(ptrdst++) = b;
+ *(ptrdst++) = g;
+ *(ptrdst++) = r;
+ xd++;
+ }
+ }
+
+ /* if the source image is too small ignore the other places.. */
+ if (xd < dst_w)
+ ptrdst += 3 * (dst_w - xd);
+ yd++;
+ }
+ break;
+
+ case (V4L2_PIX_FMT_BGR32):
+ for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
+ for (xs = 0, xd = 0; xs < (signed int)vf->w; xs++) {
+ /* read the pixel */
+
+ b = *(ptrsrc++);
+ g = *(ptrsrc++);
+ r = *(ptrsrc++);
+ ptrsrc++;
+
+ /* only paint the image if the source is within the destination */
+ if (xd < dst_w) {
+ /* set the pixel */
+ *(ptrdst++) = b;
+ *(ptrdst++) = g;
+ *(ptrdst++) = r;
+ xd++;
+ }
+ }
+
+ /* if the source image is too small ignore the other places.. */
+ if (xd < dst_w)
+ ptrdst += 3 * (dst_w - xd);
+ yd++;
+ }
+ break;
+
+ case (V4L2_PIX_FMT_UYVY):
+ for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
+ for (xs = 0, xd = 0; xs < (signed int)vf->w; 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 < dst_w) {
+ /* set the pixel */
+ *(ptrdst++) = b;
+ *(ptrdst++) = g;
+ *(ptrdst++) = r;
+ xd++;
+ }
+ }
+
+ /* if the source image is too small ignore the other places.. */
+ if (xd < dst_w)
+ ptrdst += 3 * (dst_w - xd);
+ yd++;
+ }
+ break;
+
+ case (V4L2_PIX_FMT_YUYV):
+ for (ys = 0, yd = 0; ys < (signed int)vf->h; ys++) {
+ if (yd < dst_h) {
+ for (xs = 0, xd = 0; xs < (signed int)vf->w; 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 < dst_w) {
+ /* set the pixel */
+ *(ptrdst++) = r;
+ *(ptrdst++) = g;
+ *(ptrdst++) = b;
+ xd++;
+ }
+ }
+
+ /* if the source image is too small ignore the other places.. */
+ if (xd < dst_w)
+ ptrdst += 3 * (dst_w - xd);
+ yd++;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+};
+
+
+
+
+void video_draw_image (VideoFrame *vf) {
+ int pix_w;
+ int pix_h;
+
+ if (video_da == NULL) {
+ video_da = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "video-da"));
+ if (video_da == NULL) {
+ printf ("%s:%d could not find drawingarea.\n", __FILE__, __LINE__);
+ return;
+ }
+ }
+
+ // check if the pixbuf is already allocated?
+ if (video_pixbuf) {
+ pix_h = gdk_pixbuf_get_height(video_pixbuf);
+ pix_w = gdk_pixbuf_get_width(video_pixbuf);
+ }
+ else pix_h = 0;
+
+ //
+ // changes in resolution?
+ if (video_pixbuf == NULL || pix_h != vf->h || pix_w != vf->w) {
+ if (video_pixbuf != NULL) g_object_unref (video_pixbuf);
+
+ printf ("%s:%d new pixbuf\n", __FILE__, __LINE__);
+ video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, vf->w, vf->h);
+ pix_w = vf->w;
+ pix_h = vf->h;
+
+// int channels = gdk_pixbuf_get_n_channels (video_pixbuf);
+// int rowstride = gdk_pixbuf_get_rowstride (video_pixbuf);
+ }
+
+ video_convert(vf, video_pixbuf);
+
+ GdkWindow* window = gtk_widget_get_window(video_da);
+ cairo_t *cr = gdk_cairo_create(window);
+
+ gdk_cairo_set_source_pixbuf(cr, video_pixbuf, 0, 0);
+ cairo_paint(cr);
+ cairo_fill (cr);
+
+// cairo_move_to(cr, 30, 30);
+// cairo_set_font_size(cr,15);
+// cairo_show_text(cr, "hello world");
+
+ cairo_destroy (cr);
+
+ /*
+ //
+ // "convert" the G*t*kWidget to G*d*kWindow (no, it's not a GtkWindow!)
+ GdkWindow* window = gtk_widget_get_window(video_da);
+ cairo_region_t * cairoRegion = cairo_region_create();
+ GdkDrawingContext * drawingContext = gdk_window_begin_draw_frame (window,cairoRegion);
+ cairo_t * cr = gdk_drawing_context_get_cairo_context (drawingContext);
+
+
+ gdk_window_end_draw_frame(window,drawingContext);
+ cairo_region_destroy(cairoRegion);
+
+ */
+};
+
+
+//
+// refresh the possible devices
+void cb_video_btnrefreshlist (GtkWidget *widget, gpointer data) {
GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
GtkListStore *model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(cbox)));
@@ -34,4 +279,61 @@ void cb_video_refreshlist (GtkWidget *widget, gpointer data) {
-1);
}
}
-}
+};
+
+
+//
+// start recording from the videodev (will start a new thread)
+void cb_video_btnrec (GtkWidget *widget, gpointer data) {
+ GtkWidget *cbox = GTK_WIDGET(gtk_builder_get_object (GTK_BUILDER(_builder_), "cb-videodev"));
+ 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 *cbdevice = gtk_bin_get_child(GTK_BIN(cbox));
+
+ std::string device = gtk_entry_get_text(GTK_ENTRY(cbdevice));
+ device = device.substr (0, device.find(' '));
+
+ printf ("%s:%d %s open device: '%s'\n", __FILE__, __LINE__, __FUNCTION__, device.c_str());
+ gtk_widget_set_sensitive(btnstart, false);
+ gtk_widget_set_sensitive(btnstop, true);
+
+ videodev.Start(device, cb_thread_video);
+};
+
+
+//
+// stop recording from the videodev (will stop the running thread)
+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__);
+
+ videodev.Stop();
+ gtk_widget_set_sensitive(btnstart, true);
+ gtk_widget_set_sensitive(btnstop, false);
+};
+
+
+
+gboolean cb_thread_video (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"));
+ VideoFrame *vf;
+
+ if (data == NULL) {
+ printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__);
+ videodev.Stop();
+ gtk_widget_set_sensitive(btnstart, true);
+ gtk_widget_set_sensitive(btnstop, false);
+ }
+ else {
+ // printf ("%s:%d %s got video data\n", __FILE__, __LINE__, __FUNCTION__);
+ vf = videodev.FrameGet();
+ if (vf != NULL) video_draw_image(vf);
+ videodev.FrameNext();
+ }
+
+ return false;
+};
+
diff --git a/video.h b/video.h
index b25b6bb..a334781 100644
--- a/video.h
+++ b/video.h
@@ -9,32 +9,107 @@
#include
#include
+
+#include
+
+
#include "gui.h"
#include "config.h"
+
+enum {
+ IOMODE_READ,
+ IOMODE_MMAP,
+ IOMODE_USERPTR
+};
+
+
enum {
VDEV_STATUS_UNKNOWN,
VDEV_STATUS_OK,
+ VDEV_STATUS_AGAIN,
VDEV_STATUS_ERROR
};
+
+// callback status
+enum {
+ VDEV_CBSTATUS_NOTHING = 1,
+ VDEV_CBSTATUS_NEWFRAME,
+ VDEV_CBSTATUS_ERROR
+};
+
+
+struct {
+ unsigned int size;
+ unsigned char* data;
+} typedef VideoInBuffer;
+
+
+struct {
+ int w;
+ int h;
+ uint32_t format;
+ uint32_t size;
+ uint32_t maxsize;
+ unsigned char *data;
+} typedef VideoFrame;
+
+
+#define VIDEOBUFFERS 3
class VideoDev {
private:
std::string conf_device;
std::string conf_devicename;
gboolean (*callback)(gpointer data);
- int status;
+ int running;
+ GMutex mutex;
+ GThread *thread;
+
+ VideoFrame vf[VIDEOBUFFERS];
+ VideoInBuffer *inbuffers;
+ unsigned int inbuffers_cnt;
+ struct v4l2_cropcap cropcap;
+ struct v4l2_crop crop;
+ struct v4l2_format fmt;
+
+
+ int vf_rec;
+ int vf_get;
+ int io; // IO Mode
+ int fd;
+
+ int InitMMap();
+ int InitUserPtr();
+ int CaptureStart();
+ int CaptureStop();
+ int UnInit();
+
+ int OpenInit ();
+ int Close ();
+ int Grab();
+ int xioctl (int fd, int request, void *arg);
public:
VideoDev();
~VideoDev();
+ void Thread();
+
int Start(std::string dev, gboolean (*callback_func)(gpointer data)); // will start a new thread
int Stop(); // stop video processing and stop the pthread
int GetDeviceList(std::list *list);
+ int IsRunning() { return running; };
- int GetStatus() { return status; };
+ VideoFrame *FrameGet();
+ void FrameNext();
+ void PrintCaps (uint32_t caps);
+ void PrintFmt(struct v4l2_format *f);
};
+
+extern VideoDev videodev;
+
+
#endif // _VIDEO_H_
diff --git a/videodev.cc b/videodev.cc
index ee5d643..d5100b1 100644
--- a/videodev.cc
+++ b/videodev.cc
@@ -4,23 +4,49 @@
*
*****************************************************************************************/
+#include
#include
#include
#include
#include
-#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
#include
#include
+#include "gui.h"
#include "video.h"
+#define CLEAR(x) memset (&(x), 0, sizeof (x))
+
+//
+// C / C++ Wrapper
+gpointer _VideoDevThread (gpointer data) {
+ videodev.Thread ();
+ return NULL;
+};
+
+
VideoDev::VideoDev() {
- conf_device = "";
- conf_devicename = "";
- status = VDEV_STATUS_OK;
+ inbuffers = NULL;
+ inbuffers_cnt = 0;
+ running = 0;
callback = NULL;
+ thread = NULL;
+ fd = -1;
+ io = IOMODE_MMAP;
+ CLEAR(cropcap);
+ CLEAR(crop);
+ CLEAR(fmt);
+ g_mutex_init (&mutex);
+ Close(); // will reset almost everything
};
@@ -65,11 +91,660 @@ int VideoDev::GetDeviceList(std::list *list) {
}
+//
+// start the video thread (on error, call cb_thread_videodev with NULL data)
int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) {
+ if (running != 0 || thread != NULL) return VDEV_STATUS_ERROR;
+
+ running = 1;
+ conf_device = dev;
+ callback = callback_func;
+ thread = g_thread_new("network thread", _VideoDevThread, NULL);
+
return VDEV_STATUS_ERROR;
};
int VideoDev::Stop() {
+ if (running == 1) {
+ running = 0;
+ }
+
+ if (thread) {
+ g_thread_join (thread);
+ thread = NULL;
+ }
+
return VDEV_STATUS_OK;
+};
+
+
+//
+// try to read a video every 0.05ms (25hz)
+#define CYCLETIME 0.050
+void VideoDev::Thread() {
+ struct timeval cycle_timestamp;
+ int lastsec = 0;
+ float cycle_time, cycle_wait;
+ int i;
+ fd_set fds;
+ struct timeval tv;
+
+ printf ("%s:%d %s Enter\n", __FILE__, __LINE__, __FUNCTION__);
+ cycle_time = get_cycletime(&cycle_timestamp); // just start counting
+
+ //
+ // open and init device buffers device
+ if (OpenInit() != VDEV_STATUS_OK) {
+ printf ("%s:%d %s something went wrong on Open()\n", __FILE__, __LINE__, __FUNCTION__);
+ running = 0;
+ }
+
+ //
+ // start capturing
+ CaptureStart();
+
+ //
+ // read untill something bad happens..
+ while (running > 0) {
+ //
+ // read data
+ g_mutex_lock(&mutex);
+ if (vf_rec == -1) vf_rec = 0;
+ if (vf_rec != vf_get) {
+ g_mutex_unlock(&mutex);
+
+ FD_ZERO (&fds);
+ FD_SET (fd, &fds);
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ i = select (fd + 1, &fds, NULL, NULL, &tv);
+
+ /* error while select */
+ if (i == -1 && EINTR == errno) // system interrupt. something went wrong
+ running = 0;
+ else if (i == 1) {
+ switch (Grab()) {
+ case VDEV_STATUS_OK:
+ // got valid data
+ g_mutex_lock(&mutex);
+ if (vf_get == -1) vf_get = vf_rec;
+ if ((++vf_rec) >= VIDEOBUFFERS) vf_rec = 0;
+ if (callback) gdk_threads_add_idle(callback, (void*)VDEV_CBSTATUS_NEWFRAME);
+ g_mutex_unlock(&mutex);
+ break;
+
+ case VDEV_STATUS_AGAIN:
+ break;
+
+ default:
+ running = 0;
+ }
+ }
+ }
+ else
+ g_mutex_unlock(&mutex);
+
+ //
+ // keep 25fps, write every second a message
+ cycle_time = get_cycletime(&cycle_timestamp);
+ cycle_wait = (CYCLETIME - cycle_time) + cycle_wait;
+ if (lastsec != cycle_timestamp.tv_sec) {
+ printf ("%s:%d %s cycle_time:%f \r", __FILE__, __LINE__, __FUNCTION__, cycle_time);
+ lastsec = cycle_timestamp.tv_sec;
+ }
+ if (cycle_wait > 0.0 && cycle_wait < 1.0 ) usleep ((int)(cycle_wait * 1000000.0));
+ }
+
+ //
+ // stop capturing
+ CaptureStop();
+ UnInit();
+
+ if (callback) gdk_threads_add_idle(callback, NULL);
+ 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");
+ if (caps & V4L2_CAP_META_CAPTURE) printf (" V4L2_CAP_META_CAPTURE\n");
+ 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() {
+ unsigned int i;
+ struct v4l2_buffer buf;
+
+ if (vf_rec < 0 || vf_rec >= VIDEOBUFFERS) return VDEV_CBSTATUS_NOTHING;
+
+ switch (io) {
+ case IOMODE_READ:
+ if (-1 == read (fd, vf[vf_rec].data, fmt.fmt.pix.sizeimage)) {
+ 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;
+ }
+ }
+ 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_CBSTATUS_NOTHING;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+ /* fall through */
+ default:
+ printf ( "%s:%d error on VIDIOC_DQBUF %s\n", __FILE__, __LINE__, strerror(errno));
+ return VDEV_CBSTATUS_ERROR;
+ }
+ }
+
+ if (buf.index < inbuffers_cnt) {
+ memcpy (vf[vf_rec].data, inbuffers[buf.index].data, buf.bytesused);
+ vf[vf_rec].size = buf.bytesused;
+ }
+
+ if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
+ printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
+ exit (1);
+ }
+ break;
+
+ case IOMODE_USERPTR:
+ CLEAR(buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
+ switch (errno) {
+ case EAGAIN:
+ return VDEV_CBSTATUS_NOTHING;
+
+ case EIO:
+ /* Could ignore EIO, see spec. */
+ /* fall through */
+ default:
+ printf ( "%s:%d error on VIDIOC_DQBUF %s\n", __FILE__, __LINE__, strerror(errno));
+ exit (1);
+ }
+ }
+
+ for (i = 0; i < inbuffers_cnt; ++i)
+ if (buf.m.userptr == (unsigned long)inbuffers[i].data && buf.length == inbuffers[i].size)
+ break;
+
+ if (i < inbuffers_cnt) {
+ memcpy (vf[vf_rec].data, inbuffers[buf.index].data, buf.bytesused);
+ vf[vf_rec].size = buf.bytesused;
+ }
+
+ if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
+ printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
+ exit (1);
+ }
+ break;
+ }
+
+ return VDEV_STATUS_OK;
+}
+
+//
+// Open Device
+int VideoDev::OpenInit() {
+ int i;
+ struct v4l2_capability vcap;
+
+ 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
+ if((fd = open(conf_device.c_str(), O_RDWR | O_NONBLOCK)) == -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;
+ struct v4l2_control control;
+ uint32_t min;
+
+ printf (" Controls: \n");
+ memset (&queryctrl, 0, sizeof (queryctrl));
+ for (i = V4L2_CID_BASE; i < V4L2_CID_LASTP1; i++) {
+ queryctrl.id = i;
+ if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
+ if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED)
+ printf (" *");
+
+ memset (&control, 0, sizeof (control));
+ control.id = i;
+ ioctl (fd, VIDIOC_G_CTRL, &control);
+ printf (" [%d] %s:%d\n", i, queryctrl.name, control.value);
+ }
+ }
+ printf ("\n Private Controls: ");
+ for (i = V4L2_CID_PRIVATE_BASE;; i++) {
+ queryctrl.id = i;
+ if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) {
+ printf (" [%d] %s\n", i, queryctrl.name);
+ } else {
+ break;
+ }
+ }
+ printf ("\n");
+
+ //
+ // 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 (" max image size: %d + %d : %d x %d fmt size:%d\n", cropcap.defrect.left, cropcap.defrect.top, cropcap.defrect.width, cropcap.defrect.height, fmt.fmt.pix.sizeimage);
+
+ CLEAR (fmt);
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = cropcap.defrect.width;
+ fmt.fmt.pix.height = cropcap.defrect.height;
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32;
+ 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;
+ PrintFmt (&fmt);
+
+ // allocate buffers for readin from other threads
+ for (i = 0; i < VIDEOBUFFERS; i++) {
+ vf[i].maxsize = fmt.fmt.pix.sizeimage;
+ vf[i].size = fmt.fmt.pix.sizeimage;
+ vf[i].format = fmt.fmt.pix.pixelformat;
+ vf[i].h = fmt.fmt.pix.height;
+ vf[i].w = fmt.fmt.pix.width;
+ vf[i].data = (unsigned char*) malloc (vf[i].size);
+ }
+
+ // init buffers
+ switch (io) {
+ case IOMODE_MMAP:
+ if (InitMMap() == VDEV_STATUS_ERROR)
+ Close();
+ break;
+ case IOMODE_USERPTR:
+ if (InitUserPtr() == VDEV_STATUS_ERROR)
+ Close();
+ break;
+ case IOMODE_READ:
+ default:
+ break;
+ }
+
+ return VDEV_STATUS_OK;
+};
+
+
+//
+// prepare memory mapped buffers
+int VideoDev::InitMMap() {
+ struct v4l2_requestbuffers req;
+ printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
+
+ CLEAR(req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+
+ if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
+ 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 (req.count < 2) {
+ printf ( "Insufficient buffer memory on %s\n", conf_device.c_str());
+ return VDEV_STATUS_ERROR;
+ }
+
+ inbuffers = (VideoInBuffer*) calloc(req.count, sizeof(*inbuffers));
+ if (!inbuffers) {
+ fprintf(stderr, "Out of memory\\n");
+ exit(1);
+ }
+
+ for (inbuffers_cnt = 0; inbuffers_cnt < req.count; ++inbuffers_cnt) {
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = inbuffers_cnt;
+
+ if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) {
+ printf ( "%s:%d Insufficient buffer memory on %s\n", __FILE__, __LINE__, conf_device.c_str());
+ exit (1);
+ }
+
+ inbuffers[inbuffers_cnt].size = buf.length;
+ inbuffers[inbuffers_cnt].data = (unsigned char*)mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
+
+ if (MAP_FAILED == inbuffers[inbuffers_cnt].data) {
+ printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno));
+ exit (1);
+ }
+ }
+
+ return VDEV_STATUS_OK;
+}
+
+
+//
+// prepare memory mapped buffers
+int VideoDev::InitUserPtr() {
+ struct v4l2_requestbuffers req;
+ printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
+
+ CLEAR(req);
+
+ req.count = 4;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_USERPTR;
+
+ if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
+ if (EINVAL == errno) {
+ printf("%s does not support "
+ "memory mappingn", conf_device.c_str());
+ return VDEV_STATUS_ERROR;
+ } else {
+ printf ( "%s:%d error on UserPtr %s\n", __FILE__, __LINE__, strerror(errno));
+ exit (1);
+ }
+ }
+
+ inbuffers = (VideoInBuffer*)calloc(4, sizeof(*inbuffers));
+
+ if (!inbuffers) {
+ fprintf(stderr, "Out of memory\\n");
+ exit(1);
+ }
+
+ for (inbuffers_cnt = 0; inbuffers_cnt < 4; ++inbuffers_cnt) {
+ inbuffers[inbuffers_cnt].size = vf[0].maxsize;
+ inbuffers[inbuffers_cnt].data = (unsigned char*)malloc(inbuffers[inbuffers_cnt].size);
+
+ if (!inbuffers[inbuffers_cnt].data) {
+ fprintf(stderr, "Out of memory\n");
+ exit(1);
+ }
+ }
+
+ return VDEV_STATUS_OK;
+}
+
+
+//
+// send the start capture signal to the cam
+int VideoDev::CaptureStart() {
+ unsigned int i;
+ 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:
+ for (i = 0; i < inbuffers_cnt; ++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)) {
+ printf ( "%s:%d VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
+ return VDEV_STATUS_ERROR;
+ }
+ }
+ 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;
+
+ case IOMODE_USERPTR:
+ for (i = 0; i < inbuffers_cnt; ++i) {
+ struct v4l2_buffer buf;
+
+ CLEAR(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.index = i;
+ buf.m.userptr = (unsigned long)inbuffers[i].data;
+ buf.length = inbuffers[i].size;
+
+ if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) {
+ printf ( "%s:%d VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno));
+ return VDEV_STATUS_ERROR;
+ }
+ }
+ 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;
+ }
+
+ return VDEV_STATUS_OK;
+};
+
+
+int VideoDev::CaptureStop() {
+ 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:
+ case IOMODE_USERPTR:
+ 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;
+ }
+ return VDEV_STATUS_OK;
+};
+
+
+int VideoDev::UnInit() {
+ unsigned int i;
+ printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
+
+ switch (io) {
+ case IOMODE_READ:
+ break;
+
+ case IOMODE_MMAP:
+ for (i = 0; i < inbuffers_cnt; ++i)
+ if (-1 == munmap(inbuffers[i].data, inbuffers[i].size)){
+ fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno));
+ exit(1);
+ }
+ break;
+
+ case IOMODE_USERPTR:
+ for (i = 0; i < inbuffers_cnt; ++i)
+ free(inbuffers[i].data);
+ break;
+ }
+
+ free(inbuffers);
+ inbuffers = NULL;
+ inbuffers_cnt = 0;
+ 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;
+ for (int i = 0; i < VIDEOBUFFERS; i++) {
+ vf[i].data = NULL;
+ vf[i].w = 0;
+ vf[i].h = 0;
+ vf[i].size = 0;
+ vf[i].format = 0;
+ }
+
+ return VDEV_STATUS_OK;
+};
+
+
+//
+// get current frame, if no new frame is avaiable return NULL
+VideoFrame *VideoDev::FrameGet() {
+ VideoFrame *res = NULL;
+
+ g_mutex_lock(&mutex);
+ if (vf_get != -1)
+ res = &vf[vf_get];
+ g_mutex_unlock(&mutex);
+
+ return res;
+};
+
+
+//
+// release frame, and prepare next frame
+void VideoDev::FrameNext() {
+ g_mutex_lock(&mutex);
+ vf_get++;
+ if (vf_get >= VIDEOBUFFERS) vf_get = 0;
+ if (vf_get == vf_rec) vf_get = -1;
+ g_mutex_unlock(&mutex);
+};
+
+
+
+//
+// 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;
+};
+