diff --git a/ChangeLog b/ChangeLog index a25389d..94a677a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,3 +2,8 @@ 2021-09-08: - grab video from old type of cameras is working +2021-09-15: +- grab video from cammeras only supporting mjpeg stream + + + diff --git a/video.cc b/video.cc index 0ece61b..6dc6312 100644 --- a/video.cc +++ b/video.cc @@ -11,7 +11,7 @@ VideoDev videodev; GtkWidget *video_da = NULL; -GdkPixbuf *video_pixbuf; +GdkPixbuf *video_pixbuf = NULL; @@ -32,7 +32,6 @@ void videoframe_to_pixbuf(GdkPixbuf* dest, VideoFrame *src) { } - void cb_videoda_draw(GtkWidget *area, cairo_t *cr, int w, int h, gpointer data) { if (video_da == NULL) return; @@ -98,7 +97,7 @@ void video_draw_image (VideoFrame *vf) { 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__); + printf ("%s:%d %s New Pixbuf: %d x %d\n", __FILE__, __LINE__, __FUNCTION__, vf->w, vf->h); video_pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, false, 8, vf->w, vf->h); pix_w = vf->w; pix_h = vf->h; @@ -172,18 +171,13 @@ void cb_video_btnstop (GtkWidget *widget, gpointer data) { gboolean cb_thread_video (gpointer data) { - 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 (vf == NULL) { printf ("%s:%d %s something went wrong\n", __FILE__, __LINE__, __FUNCTION__); videodev.Stop(); @@ -191,9 +185,9 @@ gboolean cb_thread_video (gpointer data) { gtk_widget_set_sensitive(btnstart, true); gtk_widget_set_sensitive(btnstop, false); } + videodev.LockMutex(); video_draw_image(vf); - -// printf ("%s:%d Time for %s ... %f ms\n", __FILE__, __LINE__, __FUNCTION__, get_cycletime(&tv)); + videodev.UnLockMutex(); return false; }; diff --git a/video.h b/video.h index 1bc9493..c25ff24 100644 --- a/video.h +++ b/video.h @@ -63,7 +63,7 @@ struct { unsigned char *data; } typedef VideoFrame; - +#define VDEV_INBUFFERS 2 class VideoDev { private: std::string conf_device; @@ -73,10 +73,12 @@ private: GMutex mutex; GThread *thread; - VideoInBuffer inbuffer; + int inbuffer_idx; + VideoInBuffer inbuffer[VDEV_INBUFFERS]; struct v4l2_cropcap cropcap; struct v4l2_crop crop; struct v4l2_format fmt; + VideoFrame vf; struct jpg_error_mgr jerr; struct jpeg_decompress_struct cinfo; @@ -112,6 +114,9 @@ public: void PrintCaps (uint32_t caps); void PrintFmt(struct v4l2_format *f); + + void LockMutex(); + void UnLockMutex(); }; diff --git a/videodev.cc b/videodev.cc index ecc6f56..6534013 100644 --- a/videodev.cc +++ b/videodev.cc @@ -76,13 +76,22 @@ gpointer _VideoDevThread (gpointer data) { VideoDev::VideoDev() { - inbuffer.size = 0; - inbuffer.data = NULL; + int i; + + for (i = 0; i < VDEV_INBUFFERS; i++) { + inbuffer[i].size = 0; + inbuffer[i].data = NULL; + } + vf.data = NULL; + vf.h = 0; + vf.w = 0; + vf.size = 0; running = 0; callback = NULL; thread = NULL; fd = -1; io = IOMODE_MMAP; + inbuffer_idx = 0; CLEAR(cropcap); CLEAR(crop); CLEAR(fmt); @@ -150,7 +159,7 @@ int VideoDev::Start(std::string dev, gboolean (*callback_func)(gpointer data)) { int VideoDev::Stop() { if (running == 1) { - running = 0; + running = 0; // we can jump directly to 0 } if (thread) { @@ -164,26 +173,24 @@ int VideoDev::Stop() { // // 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 // #define CYCLETIME 0.050 void VideoDev::Thread() { struct timeval cycle_timestamp; int lastsec = 0; - float cycle_time, cycle_wait; + float cycle_time = 0.0; + float cycle_wait = 0.0; 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 // // 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; - } + if (OpenInit() != VDEV_STATUS_OK) running = 0; // // start capturing @@ -191,7 +198,7 @@ void VideoDev::Thread() { // // read untill something bad happens.. - while (running > 0) { + while (running) { i = Grab(&vf); switch (i) { @@ -215,12 +222,13 @@ void VideoDev::Thread() { } if (cycle_wait > 0.0 && cycle_wait < 1.0 ) usleep ((int)(cycle_wait * 1000000.0)); } + // // stop capturing + if (callback) gdk_threads_add_idle(callback, NULL); + CaptureStop(); UnInit(); - - if (callback) gdk_threads_add_idle(callback, NULL); Close(); printf ("%s:%d %s Exit\n", __FILE__, __LINE__, __FUNCTION__); @@ -266,7 +274,7 @@ int VideoDev::Grab(VideoFrame *vf) { switch (io) { case IOMODE_READ: - if ((len = read (fd, inbuffer.data, fmt.fmt.pix.sizeimage)) == -1) { + if ((len = read (fd, inbuffer[0].data, fmt.fmt.pix.sizeimage)) == -1) { switch (errno) { case EAGAIN: return VDEV_STATUS_AGAIN; @@ -276,8 +284,11 @@ int VideoDev::Grab(VideoFrame *vf) { return VDEV_STATUS_ERROR; } } - else - Convert(vf, inbuffer.data, len, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); + 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: @@ -285,7 +296,6 @@ int VideoDev::Grab(VideoFrame *vf) { buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; - buf.index = 0; if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { switch (errno) { @@ -294,19 +304,16 @@ int VideoDev::Grab(VideoFrame *vf) { 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_STATUS_ERROR; } } -// 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 (buf.index == 0) { - Convert(vf, inbuffer.data, buf.bytesused, fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); + 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)) { @@ -314,8 +321,7 @@ int VideoDev::Grab(VideoFrame *vf) { 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); + if (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0; break; default: @@ -404,12 +410,10 @@ int VideoDev::OpenInit() { 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.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.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)) { @@ -449,10 +453,12 @@ int VideoDev::InitMMap() { struct v4l2_buffer bufinfo; printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + int i; + CLEAR(bufreq); CLEAR(bufinfo); - bufreq.count = 1; + bufreq.count = VDEV_INBUFFERS; bufreq.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; bufreq.memory = V4L2_MEMORY_MMAP; @@ -472,24 +478,25 @@ int VideoDev::InitMMap() { return VDEV_STATUS_ERROR; } - CLEAR(bufinfo); - - bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - bufinfo.memory = V4L2_MEMORY_MMAP; - bufinfo.index = 0; - - if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){ - perror("VIDIOC_QUERYBUF"); - exit(1); - } + for (i = 0; i < VDEV_INBUFFERS; i++) { + CLEAR(bufinfo); + bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + bufinfo.memory = V4L2_MEMORY_MMAP; + bufinfo.index = i; - 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); - } + 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; } @@ -508,17 +515,20 @@ int VideoDev::CaptureStart() { case IOMODE_MMAP: struct v4l2_buffer buf; - CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = 0; + 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)); - exit (1); + 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)); @@ -532,16 +542,6 @@ int VideoDev::CaptureStart() { } 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; - } jpeg_create_decompress(&cinfo); } @@ -584,10 +584,15 @@ int VideoDev::UnInit() { break; case IOMODE_MMAP: - if (-1 == munmap(inbuffer.data, inbuffer.size)){ - fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno)); - exit(1); - } + 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: @@ -661,7 +666,7 @@ int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uin if (dest->data != NULL && dest->w != srcw && dest->h != srch) { free (dest->data); - dest->data == NULL; + dest->data = NULL; } if (dest->data == NULL) { @@ -799,6 +804,14 @@ int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uin 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); @@ -818,3 +831,15 @@ int VideoDev::Convert (VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uin return VDEV_STATUS_OK; }; + +void VideoDev::LockMutex() { + g_mutex_lock(&mutex); +}; + + +void VideoDev::UnLockMutex() { + g_mutex_unlock(&mutex); +}; + + +