diff --git a/Makefile.rules.linux b/Makefile.rules.linux index ee96141..3c2cb03 100644 --- a/Makefile.rules.linux +++ b/Makefile.rules.linux @@ -5,5 +5,5 @@ CPP = c++ CPPFLAGS = -ggdb -Wall -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 +LIBS = `pkg-config --libs gtk+-3.0 gmodule-export-2.0` -L/usr/lib -ljpeg diff --git a/gui.cc b/gui.cc index fca6062..248ab45 100644 --- a/gui.cc +++ b/gui.cc @@ -35,6 +35,7 @@ void cb_window_show (GtkWidget *widget, gpointer data) { gtk_widget_set_sensitive(btnstart, true); gtk_widget_set_sensitive(btnstop, false); + }; diff --git a/gui.h b/gui.h index 89c2626..f910a2b 100644 --- a/gui.h +++ b/gui.h @@ -40,6 +40,9 @@ 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); G_MODULE_EXPORT void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data); +G_MODULE_EXPORT gboolean video_display(gpointer data); + + // // thread handles diff --git a/simpleskycam.ui b/simpleskycam.ui index 0a05317..3dede4a 100644 --- a/simpleskycam.ui +++ b/simpleskycam.ui @@ -7,6 +7,7 @@ 1024 786 + True diff --git a/video.cc b/video.cc index 01089bd..0ece61b 100644 --- a/video.cc +++ b/video.cc @@ -14,187 +14,26 @@ GtkWidget *video_da = NULL; GdkPixbuf *video_pixbuf; -// -// 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; -}; +void videoframe_to_pixbuf(GdkPixbuf* dest, VideoFrame *src) { + int destw, desth; + unsigned char *destpixel; + if (dest == NULL || src == NULL) return; -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; + destw = gdk_pixbuf_get_height(dest); + desth = gdk_pixbuf_get_width(dest); + destpixel = gdk_pixbuf_get_pixels(dest); - y1 = Y1 - 16; - pb = Cb - 128; - pr = Cr - 128; + if (destw > src->w) destw = src->w; + if (desth > src->h) destw = src->h; - *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; - } -}; + memcpy (destpixel, src->data, 3*destw*desth); +} void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) { - GdkRGBA color; - GtkStyleContext *context; - - context = gtk_widget_get_style_context (area); - if (video_da == NULL) return; //cairo_move_to(cr, 30, 30); @@ -226,18 +65,46 @@ void video_draw_image (VideoFrame *vf) { } 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); + // display error screen? + if (vf == NULL) { + unsigned char *pixels; + int x, y; + + // need to allocate? + if (video_pixbuf == NULL) { + printf ("%s:%d new pixbuf\n", __FILE__, __LINE__); + video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, 640, 480); + pix_w = 640; + pix_h = 480; + } + pixels = gdk_pixbuf_get_pixels(video_pixbuf); + for (y = 0; y < pix_h; y++) for (x = 0; x < pix_w; x++) { + if ((x & 1) && (y & 1)) { + *(++pixels) = 0xFF; + *(++pixels) = 0xFF; + *(++pixels) = 0xFF; + } + else { + *(++pixels) = 0x0; + *(++pixels) = 0x0; + *(++pixels) = 0x0; + } + } - 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; } - - video_convert(vf, video_pixbuf); + else { + // + // 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; + } + videoframe_to_pixbuf(video_pixbuf, vf); + } gdk_window_invalidate_rect(gtk_widget_get_window(video_da), NULL, true); }; @@ -305,22 +172,28 @@ void cb_video_btnstop (GtkWidget *widget, gpointer data) { 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; + struct timeval tv; + float f; + GtkWidget *btnstart; + GtkWidget *btnstop; + VideoFrame *vf = (VideoFrame *) data; + +// get_cycletime(&tv); + + 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")); + - if (data == NULL) { + if (vf == NULL) { printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__); videodev.Stop(); + video_draw_image(NULL); gtk_widget_set_sensitive(btnstart, true); gtk_widget_set_sensitive(btnstop, false); } - else { - vf = videodev.FrameGet(); - if (vf != NULL) video_draw_image(vf); - else printf ("%s:%d %s could not get image data\n", __FILE__, __LINE__, __FUNCTION__); - videodev.FrameNext(); - } + video_draw_image(vf); + +// printf ("%s:%d Time for %s ... %f ms\n", __FILE__, __LINE__, __FUNCTION__, get_cycletime(&tv)); return false; }; diff --git a/video.h b/video.h index a334781..1bc9493 100644 --- a/video.h +++ b/video.h @@ -9,8 +9,9 @@ #include #include - +#include #include +#include #include "gui.h" @@ -19,8 +20,7 @@ enum { IOMODE_READ, - IOMODE_MMAP, - IOMODE_USERPTR + IOMODE_MMAP }; @@ -31,6 +31,16 @@ enum { 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 { @@ -49,14 +59,11 @@ struct { 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; @@ -66,13 +73,13 @@ private: GMutex mutex; GThread *thread; - VideoFrame vf[VIDEOBUFFERS]; - VideoInBuffer *inbuffers; - unsigned int inbuffers_cnt; + VideoInBuffer inbuffer; struct v4l2_cropcap cropcap; struct v4l2_crop crop; struct v4l2_format fmt; + struct jpg_error_mgr jerr; + struct jpeg_decompress_struct cinfo; int vf_rec; int vf_get; @@ -85,9 +92,11 @@ private: 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(); + int Grab(VideoFrame *vf); int xioctl (int fd, int request, void *arg); public: VideoDev(); @@ -101,8 +110,6 @@ public: int GetDeviceList(std::list *list); int IsRunning() { return running; }; - VideoFrame *FrameGet(); - void FrameNext(); void PrintCaps (uint32_t caps); void PrintFmt(struct v4l2_format *f); }; diff --git a/videodev.cc b/videodev.cc index d5100b1..ecc6f56 100644 --- a/videodev.cc +++ b/videodev.cc @@ -16,17 +16,58 @@ #include #include #include +#include #include #include +#include #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 // -// C / C++ Wrapper gpointer _VideoDevThread (gpointer data) { videodev.Thread (); return NULL; @@ -35,8 +76,8 @@ gpointer _VideoDevThread (gpointer data) { VideoDev::VideoDev() { - inbuffers = NULL; - inbuffers_cnt = 0; + inbuffer.size = 0; + inbuffer.data = NULL; running = 0; callback = NULL; thread = NULL; @@ -57,6 +98,7 @@ VideoDev::~VideoDev() { // // return list of devices in form of /dev/videoYY [Name] +// int VideoDev::GetDeviceList(std::list *list) { std::string device; int devnum; @@ -93,13 +135,14 @@ 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); + thread = g_thread_new("VideoDev", _VideoDevThread, NULL); return VDEV_STATUS_ERROR; }; @@ -121,6 +164,7 @@ int VideoDev::Stop() { // // try to read a video every 0.05ms (25hz) +// #define CYCLETIME 0.050 void VideoDev::Thread() { struct timeval cycle_timestamp; @@ -129,6 +173,7 @@ void VideoDev::Thread() { int i; fd_set fds; struct timeval tv; + VideoFrame vf; printf ("%s:%d %s Enter\n", __FILE__, __LINE__, __FUNCTION__); cycle_time = get_cycletime(&cycle_timestamp); // just start counting @@ -142,60 +187,34 @@ void VideoDev::Thread() { // // start capturing - CaptureStart(); + if (CaptureStart() != VDEV_STATUS_OK) running = 0; // // 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; + i = Grab(&vf); - default: - running = 0; - } - } - } - else - g_mutex_unlock(&mutex); + switch (i) { + case VDEV_STATUS_OK: + if (callback) gdk_threads_add_idle(callback, &vf); + break; + + case VDEV_STATUS_AGAIN: + break; + default: + running = 0; + } // // 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); + printf ("%s:%d %s cycle_time:%f Freq:%f Hz \r", __FILE__, __LINE__, __FUNCTION__, cycle_time, (1.0/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(); @@ -239,15 +258,15 @@ void VideoDev::PrintFmt(struct v4l2_format *f) { -int VideoDev::Grab() { - unsigned int i; +int VideoDev::Grab(VideoFrame *vf) { struct v4l2_buffer buf; + int len; - if (vf_rec < 0 || vf_rec >= VIDEOBUFFERS) return VDEV_CBSTATUS_NOTHING; + if (vf == NULL) return VDEV_STATUS_ERROR; switch (io) { case IOMODE_READ: - if (-1 == read (fd, vf[vf_rec].data, fmt.fmt.pix.sizeimage)) { + if ((len = read (fd, inbuffer.data, fmt.fmt.pix.sizeimage)) == -1) { switch (errno) { case EAGAIN: return VDEV_STATUS_AGAIN; @@ -257,6 +276,8 @@ int VideoDev::Grab() { return VDEV_STATUS_ERROR; } } + else + Convert(vf, inbuffer.data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); break; case IOMODE_MMAP: @@ -264,66 +285,42 @@ int VideoDev::Grab() { buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; + buf.index = 0; if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { switch (errno) { case EAGAIN: - return VDEV_CBSTATUS_NOTHING; + return VDEV_STATUS_AGAIN; case EIO: + printf ( "%s:%d error on VIDIOC_DQBUF EIO %s\n", __FILE__, __LINE__, strerror(errno)); /* 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); + return VDEV_STATUS_ERROR; } } - for (i = 0; i < inbuffers_cnt; ++i) - if (buf.m.userptr == (unsigned long)inbuffers[i].data && buf.length == inbuffers[i].size) - break; +// printf ("%s:%d DQBUF used:%d field:%d flags:%d index:%d len:%d seq:%d mem:%d\n", __FILE__, __LINE__, buf.bytesused, buf.field, +// buf.flags, buf.index, buf.length, buf.sequence, buf.memory); - if (i < inbuffers_cnt) { - memcpy (vf[vf_rec].data, inbuffers[buf.index].data, buf.bytesused); - vf[vf_rec].size = buf.bytesused; + if (buf.index == 0) { + Convert(vf, inbuffer.data, buf.bytesused, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); } if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { - printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno)); - exit (1); + printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno)); + exit (1); } + +// printf ("%s:%d QBUF used:%d field:%d flags:%d index:%d len:%d seq:%d mem:%d\n", __FILE__, __LINE__, buf.bytesused, buf.field, +// buf.flags, buf.index, buf.length, buf.sequence, buf.memory); break; + + default: + printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); + return VDEV_STATUS_ERROR; } return VDEV_STATUS_OK; @@ -339,8 +336,8 @@ int VideoDev::OpenInit() { 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){ + // open device and get device name and capabilities | O_NONBLOCK + if((fd = open(conf_device.c_str(), O_RDWR)) == -1){ return VDEV_STATUS_ERROR; } @@ -406,11 +403,13 @@ int VideoDev::OpenInit() { 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.width = cropcap.defrect.width; + // fmt.fmt.pix.height = cropcap.defrect.height; + fmt.fmt.pix.width = 1920; + fmt.fmt.pix.height = 1080; + // 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)) { @@ -428,26 +427,12 @@ int VideoDev::OpenInit() { 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; @@ -460,16 +445,18 @@ int VideoDev::OpenInit() { // // prepare memory mapped buffers int VideoDev::InitMMap() { - struct v4l2_requestbuffers req; + struct v4l2_requestbuffers bufreq; + struct v4l2_buffer bufinfo; printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - CLEAR(req); + CLEAR(bufreq); + CLEAR(bufinfo); - req.count = 4; - req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req.memory = V4L2_MEMORY_MMAP; + bufreq.count = 1; + bufreq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + bufreq.memory = V4L2_MEMORY_MMAP; - if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) { + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &bufreq)) { if (EINVAL == errno) { printf("%s does not support " "memory mappingn", conf_device.c_str()); @@ -480,83 +467,28 @@ int VideoDev::InitMMap() { } } - if (req.count < 2) { + if (bufreq.count < 1) { 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); - } + CLEAR(bufinfo); - 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); + bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + bufinfo.memory = V4L2_MEMORY_MMAP; + bufinfo.index = 0; - 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(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){ + perror("VIDIOC_QUERYBUF"); + exit(1); + } - if (!inbuffers[inbuffers_cnt].data) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } - } + inbuffer.size = bufinfo.length; + inbuffer.data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, bufinfo.m.offset); + if (inbuffer.data == MAP_FAILED) { + printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno)); + exit (1); + } return VDEV_STATUS_OK; } @@ -565,8 +497,8 @@ int VideoDev::InitUserPtr() { // // 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) { @@ -575,19 +507,18 @@ int VideoDev::CaptureStart() { break; case IOMODE_MMAP: - for (i = 0; i < inbuffers_cnt; ++i) { - struct v4l2_buffer buf; + struct v4l2_buffer buf; + CLEAR(buf); - CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = i; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = 0; - if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { - printf ( "%s:%d VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno)); - return VDEV_STATUS_ERROR; - } + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { + printf ( "%s:%d error on VIDIOC_QBUF %s\n", __FILE__, __LINE__, strerror(errno)); + exit (1); } + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) { printf ( "%s:%d VIDIOC_STREAMON %s\n", __FILE__, __LINE__, strerror(errno)); @@ -595,29 +526,24 @@ int VideoDev::CaptureStart() { } 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; + default: + printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); + return VDEV_STATUS_ERROR; + } - 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; + if (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) { + cinfo.err = jpeg_std_error(&jerr.pub); + jerr.pub.error_exit = jpg_error_exit; + if (setjmp(jerr.setjmp_buffer)) { + /* If we get here, the JPEG code has signaled an error. + * We need to clean up the JPEG object, close the input file, and return. + */ + printf ("%s:%d %s JPEG Error\n", __FILE__, __LINE__, __FUNCTION__); + jpeg_destroy_decompress(&cinfo); + return VDEV_STATUS_ERROR; } - break; - } + jpeg_create_decompress(&cinfo); + } return VDEV_STATUS_OK; }; @@ -627,26 +553,30 @@ 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: - 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; - } + default: + printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); + return VDEV_STATUS_ERROR; + } return VDEV_STATUS_OK; }; int VideoDev::UnInit() { - unsigned int i; printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); switch (io) { @@ -654,22 +584,18 @@ int VideoDev::UnInit() { 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); - } + if (-1 == munmap(inbuffer.data, inbuffer.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; - } + default: + printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); + return VDEV_STATUS_ERROR; + } + - free(inbuffers); - inbuffers = NULL; - inbuffers_cnt = 0; return VDEV_STATUS_OK; }; @@ -677,6 +603,7 @@ int VideoDev::UnInit() { // // Close Device // Free videobuffer +// int VideoDev::Close() { printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); @@ -690,46 +617,14 @@ int VideoDev::Close() { 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; @@ -748,3 +643,178 @@ int VideoDev::xioctl(int fd, int request, void *arg) { return r; }; + +// +// 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): + 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; +}; +