From faf51fc2dfadd8004798448fab18793c550621b8 Mon Sep 17 00:00:00 2001 From: Steffen Pohle Date: Wed, 21 Jan 2026 22:14:57 +0100 Subject: [PATCH] splitting videodevices into multiple source files. --- Makefile | 4 +- convert.cc | 100 +----- debayer.cc | 29 +- video.cc | 825 -------------------------------------------- video.h | 69 +--- videodevice_dump.cc | 419 ++++++++++++++++++++++ videodevice_dump.h | 42 +++ videodevice_v4l2.cc | 507 +++++++++++++++++++++++++++ videodevice_v4l2.h | 41 +++ 9 files changed, 1034 insertions(+), 1002 deletions(-) create mode 100644 videodevice_dump.cc create mode 100644 videodevice_dump.h create mode 100644 videodevice_v4l2.cc create mode 100644 videodevice_v4l2.h diff --git a/Makefile b/Makefile index 9627533..6a24bdf 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,9 @@ DEFAULT_SERVERPORT=20010 CXX=g++ CXXFLAGS= -ggdb -fPIC -Wall -std=c++11 -I/usr/local/include LDFLAGS= -lUDPTCPNetwork -L/usr/local/lib -ljpeg -OBJFILES= webserver.o configuration.o main.o video.o convert.o debayer.o inmemoryfile.o videoframe.o +OBJFILES= webserver.o configuration.o main.o inmemoryfile.o \ + convert.o debayer.o videoframe.o \ + video.o videodevice_v4l2.o videodevice_dump.o all: dep miniwebcam miniwebcam: $(OBJFILES) diff --git a/convert.cc b/convert.cc index 93894ce..46a082e 100644 --- a/convert.cc +++ b/convert.cc @@ -17,8 +17,6 @@ #include "debayer.h" int debayer_mode = 1; // testing 0 or 1 -int convert_debug_fd = -1; -struct timeval convert_debug_tv; uint32_t convert_pixelformats [] = { V4L2_PIX_FMT_MJPEG, @@ -44,100 +42,6 @@ uint32_t convert_pixelformats [] = { /* * will be called on first frame */ -int convert_debug_open(uint32_t pixelformat, int srcw, int srch) { - time_t t = time(NULL); - struct tm *tmptr; - char fname[LEN_FILENAME]; - char fullfname[LEN_FULLFILENAME]; - - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - if (config.vdev_dumppath.length() == 0) return -1; - - // - // check to create file, if not possible try creating the directory and create the file again - tmptr = localtime(&t); - strftime(fname, LEN_FILENAME, "%Y%m%d-%H%M%S", tmptr); - snprintf (fullfname, LEN_FULLFILENAME, "%s/%s.videodump", config.vdev_dumppath.c_str(), fname); - - if ((convert_debug_fd = creat (fullfname, 0666)) == -1) { - printf ("%s:%d could not create file '%s'. Error:%s\n", __FILE__, __LINE__, fullfname, strerror(errno)); - printf ("%s:%d try to create folder\n", __FILE__, __LINE__); - - // create folder - // FIXME: how to do thin on windows -#ifdef BUILD_WINDOWS - if ((mkdir (config.debugpath)) == -1) { -#else - if ((mkdir (config.vdev_dumppath.c_str(), 0777)) == -1) { -#endif - printf ("%s:%d could not create debug folder.\n", __FILE__, __LINE__); - return -1; - } - if ((convert_debug_fd = creat (fullfname, 0666)) == -1) { - printf ("%s:%d could again not create file '%s'. Error:%s\n", __FILE__, __LINE__, fullfname, strerror(errno)); - return -1; - } - } - - - // - // file is open for writing, write header information - uint32_t wwidth = htonl(srcw); - uint32_t wheight = htonl(srch); - uint32_t wpixfmt = htonl(pixelformat); - write (convert_debug_fd, &wwidth, 4); - write (convert_debug_fd, &wheight, 4); - write (convert_debug_fd, &wpixfmt, 4); - - // - // save start time, this is needed to calculate the time between the frames - gettimeofday (&convert_debug_tv, NULL); - - return 0; -} - - - -/* - * if fd is not open open file and set up header - */ -void convert_debug_dumpframe(unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch) { - struct timeval tv; - uint32_t wts, ts; - uint32_t wsize; - - if (ptrsrc == NULL) return; - if (config.vdev_dumppath.length() == 0) return; - if (convert_debug_fd == -1) - if (convert_debug_open(pixelformat, srcw, srch) == -1) return; - - // - // construct and write header - wsize = htonl(srcsize); - gettimeofday (&tv, NULL); - ts = 1000 * (tv.tv_sec - convert_debug_tv.tv_sec) + - (tv.tv_usec - convert_debug_tv.tv_usec) / 1000; - wts = htonl(ts); - - write (convert_debug_fd, &wsize, 4); - write (convert_debug_fd, &wts, 4); - - // - // write frame - write (convert_debug_fd, ptrsrc, srcsize); -}; - - -/* - * close file and reset all data - */ -void convert_debug_close() { - if (convert_debug_fd != -1) { - close (convert_debug_fd); - convert_debug_fd = -1; - } -} - // // jpeg: replacement for error_exit @@ -187,7 +91,7 @@ int ConvertStart(ConvertData *cdata, uint32_t pixelformat) { }; int ConvertStop(ConvertData *cdata, uint32_t pixelformat) { - convert_debug_close(); + dumpframe_close(); if (cdata == NULL) return 0; if (pixelformat == V4L2_PIX_FMT_MJPEG) jpeg_destroy_decompress(&cdata->cinfo); @@ -207,7 +111,7 @@ int Convert (ConvertData *cdata, VideoFrame *dest, unsigned char *ptrsrc, int sr unsigned char *ptrdst = NULL; debug ("srcsize:%d pixfmt:%s size: %dx%d", srcsize, convert_from_pixelformat(pixelformat).c_str(), srcw, srch); - if (config.vdev_dumppath.length() > 0) convert_debug_dumpframe(ptrsrc, srcsize, pixelformat, srcw, srch); + if (config.vdev_dumppath.length() > 0) dumpframe(ptrsrc, srcsize, pixelformat, srcw, srch); struct jpg_error_mgr jerr; if (cdata == NULL) return 0; diff --git a/debayer.cc b/debayer.cc index a2a3e99..6e79fd1 100644 --- a/debayer.cc +++ b/debayer.cc @@ -371,10 +371,17 @@ void debayer_grbg8_bilinear (uint8_t * src, int src_w, int src_h, } int MAX(int a, int m) { if (a > m) return m; else return a; }; - +#define PRGGB10P_U ((uint16_t)(*(psrc-src_w*2*10/8))) +#define PRGGB10P_D ((uint16_t)(*(psrc+src_w*2*10/8))) +#define PRGGB10P_L ((uint16_t)(*(psrc-2)*10/8)) +#define PRGGB10P_R ((uint16_t)(*(psrc+2)*10/8)) +#define PRGGB10P_UL ((uint16_t)(*(psrc-2-src_w*2*10/8))) +#define PRGGB10P_UR ((uint16_t)(*(psrc+2-src_w*2*10/8))) +#define PRGGB10P_DL ((uint16_t)(*(psrc-2-src_w*2*10/8))) +#define PRGGB10P_DR ((uint16_t)(*(psrc+2+src_w*2*10/8))) void debayer_rggb10packet_simple (uint8_t * src, int src_w, int src_h, uint8_t * dst, int dst_w, int dst_h) { -/* int s, d; + int s, d; int xs, ys, xd, yd; unsigned char r, g, b; int max = dst_w * dst_h * 3; @@ -400,24 +407,24 @@ void debayer_rggb10packet_simple (uint8_t * src, int src_w, int src_h, if (ys&1) { if (xs&1) { b = *psrc; - g = (P_L + P_R + P_U + P_D)/4; - r = (P_UL + P_DL + P_UR + P_UL)/4; + g = (PRGGB10P_L + PRGGB10P_R + PRGGB10P_U + PRGGB10P_D)/4; + r = (PRGGB10P_UL + PRGGB10P_DL + PRGGB10P_UR + PRGGB10P_UL)/4; } else { - b = (P_L + P_R)/2; + b = (PRGGB10P_L + PRGGB10P_R)/2; g = *psrc; - r = (P_U + P_D)/2; + r = (PRGGB10P_U + PRGGB10P_D)/2; } } else { if (xs&1) { - b = (P_U + P_D)/2; + b = (PRGGB10P_U + PRGGB10P_D)/2; g = *psrc; - r = (P_L + P_R)/2; + r = (PRGGB10P_L + PRGGB10P_R)/2; } else { - g = (P_L + P_R + P_U + P_D)/4; - b = (P_UL + P_DL + P_UR + P_UL)/4; + g = (PRGGB10P_L + PRGGB10P_R + PRGGB10P_U + PRGGB10P_D)/4; + b = (PRGGB10P_UL + PRGGB10P_DL + PRGGB10P_UR + PRGGB10P_UL)/4; r = *psrc; } } @@ -440,7 +447,7 @@ void debayer_rggb10packet_simple (uint8_t * src, int src_w, int src_h, psrc++; if (xs && (xs % 4 == 0)) psrc++; } - } */ + } }; diff --git a/video.cc b/video.cc index 15daac2..9a54595 100644 --- a/video.cc +++ b/video.cc @@ -22,8 +22,6 @@ #endif #include - - VideoDevice::VideoDevice() { debug (""); }; @@ -33,826 +31,3 @@ VideoDevice::~VideoDevice() { debug (""); }; -/*********************************************************************************************************/ - -VideoDevice_V4L2::VideoDevice_V4L2() { - debug (""); - conf_height = 0; - conf_width = 0; - conf_videodev = ""; - conf_iomode = 0; - conf_videofmt = 0; - fd = -1; -}; - - -VideoDevice_V4L2::~VideoDevice_V4L2() { - debug (""); - if (fd != 0) Stop(); -}; - - - -int VideoDevice_V4L2::SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt) { - int isrunning = 0; - - if (fd != -1) { - Stop(); - isrunning = 1; - } - - conf_videodev = newdevice; - conf_width = nwidth; - conf_height = nheight; - conf_videofmt = pixfmt; - - if (isrunning) { - if (Start() == 0) return 0; - } - - return 1; -}; - - -int VideoDevice_V4L2::SetIOMode(int newiomode) { - int isrunning = 0; - - if (fd != -1) { - Stop(); - isrunning = 1; - } - - conf_iomode = newiomode; - - if (isrunning) { - if (Start() == 0) return 0; - } - - return 1; -}; - - -int VideoDevice_V4L2::Open() { - int i; - struct v4l2_capability vcap; - VideoDevCtrl vctl; - - debug ("Device: '%s'", conf_videodev.c_str()); - if (fd != -1) return 0; - - // - // open device and get device name and capabilities | O_NONBLOCK - if((fd = open(conf_videodev.c_str(), O_RDWR)) == -1){ - debug ("could not open device error: %s", strerror(errno)); - return 0; - } - - if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1) - strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card)); - debug ("VideoDevice_V4L2::Open Devicefile:%s Card:%s fd:%d", conf_videodev.c_str(), vcap.card, fd); - - debug ("Capabilities: %u", vcap.capabilities); - PrintCaps(vcap.capabilities); - debug ("Device Capabilities: %u", vcap.device_caps); - PrintCaps(vcap.device_caps); - - if (!(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { - debug ("Error: device has no video capture capabilities"); - return 0; - Close(); - } - - // - // query controls - struct v4l2_queryctrl queryctrl; - uint32_t min; - debug ("Get Controls"); - - vidctrls.clear(); - memset (&queryctrl, 0, sizeof (queryctrl)); - for (i = V4L2_CID_BASE; i < V4L2_CID_DETECT_CLASS_BASE+0x1000; i++) { - queryctrl.id = i; - if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { - vctl.name = (char*)queryctrl.name; - vctl.id = queryctrl.id; - vctl.min = queryctrl.minimum; - vctl.max = queryctrl.maximum; - GetDevCtrl(queryctrl.id, &vctl.value); - vidctrls.push_back(vctl); - } - } - debug ("got %d controls.", vidctrls.size()); - - // - // check for cropping.. if we have it setup default - debug ("setup cropping"); - CLEAR (cropcap); - cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) { - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c = cropcap.defrect; // reset to default - - if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) { - debug ("VIDEOC_S_CROP Errorcode: %s", strerror(errno)); - } - } - - // - // prepare resolution and pixelformat - debug ("setup video resolution and pixeltype"); - CLEAR (fmt); - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (conf_width != -1 && conf_height != -1) { // resolution - fmt.fmt.pix.width = conf_width; - fmt.fmt.pix.height = conf_height; - } - else { - fmt.fmt.pix.width = 1920; - fmt.fmt.pix.height = 1080; - } - - // fixme: hardcoded video format????? - fmt.fmt.pix.pixelformat = conf_videofmt; - fmt.fmt.pix.field = V4L2_FIELD_NONE; - if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) { - debug ("VIDIOC_S_FMT : %s", strerror (errno)); - close (fd); - fd = -1; - return 0; - } - - // Note VIDIOC_S_FMT may change width and height. - // Buggy driver paranoia. - as written in the v4l2 api documentation - min = fmt.fmt.pix.width * 2; - if (fmt.fmt.pix.bytesperline < min) - fmt.fmt.pix.bytesperline = min; - min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; - if (fmt.fmt.pix.sizeimage < min) - fmt.fmt.pix.sizeimage = min; - conf_width = fmt.fmt.pix.width; - conf_height = fmt.fmt.pix.height; - conf_videofmt = fmt.fmt.pix.pixelformat; - PrintFmt (&fmt); - - // init buffers - switch (conf_iomode) { - case IOMODE_MMAP: - if (InitMMap() == 0) { - debug ("could not setup MMap (memory mapping)"); - Close(); - } - break; - case IOMODE_READ: - for (i = 0; i < VDEV_INBUFFERS; i++) { - inbuffer[i].size = fmt.fmt.pix.sizeimage; - inbuffer[i].data = (unsigned char*) malloc (fmt.fmt.pix.sizeimage); - } - - default: - break; - } - - return 1; -}; - - -int VideoDevice_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 mapping", conf_videodev.c_str()); - return 0; - } else { - printf ("%s:%d %s Error %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno)); - return 0; - } - } - - if (bufreq.count < 1) { - printf ( "Insufficient buffer memory on %s\n", conf_videodev.c_str()); - return 0; - } - - for (i = 0; i < VDEV_INBUFFERS; i++) { - CLEAR(bufinfo); - bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - bufinfo.memory = V4L2_MEMORY_MMAP; - bufinfo.index = i; - - if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){ - perror("VIDIOC_QUERYBUF"); - exit(1); - } - - inbuffer[i].size = bufinfo.length; - inbuffer[i].data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE, - MAP_SHARED, fd, bufinfo.m.offset); - if (inbuffer[i].data == MAP_FAILED) { - printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno)); - exit (1); - } - } - return 1; -} - - -int VideoDevice_V4L2::Close() { - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - - if (fd >= 0) { - UnInit(); - close (fd); - fd = -1; - } - - return 1; -}; - - -int VideoDevice_V4L2::UnInit() { - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - - switch (conf_iomode) { - case IOMODE_READ: - for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) { - if (inbuffer[inbuffer_idx].data) { - free (inbuffer[inbuffer_idx].data); - inbuffer[inbuffer_idx].data = NULL; - inbuffer[inbuffer_idx].size = 0; - } - } - - break; - - case IOMODE_MMAP: - for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) - if (inbuffer[inbuffer_idx].data) { - if (-1 == munmap(inbuffer[inbuffer_idx].data, inbuffer[inbuffer_idx].size)){ - fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno)); - exit(1); - } - inbuffer[inbuffer_idx].data = NULL; - inbuffer[inbuffer_idx].size = 0; - } - break; - - default: - printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); - return 0; - } - - - return 1; -}; - - -int VideoDevice_V4L2::Start() { - enum v4l2_buf_type type; - - debug ("V4L2"); - - if (Open() == 0) { - debug ("VideoDevice_V4L2::Start Open Device Failed."); - return 0; - } - - switch (conf_iomode) { - case IOMODE_READ: - /* Nothing to do. */ - break; - - case IOMODE_MMAP: - struct v4l2_buffer buf; - - for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) { - CLEAR(buf); - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - buf.index = inbuffer_idx; - - if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { - debug ("error on VIDIOC_QBUF %s", strerror(errno)); - return 0; - } - } - - inbuffer_idx = 0; - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) { - debug ("VIDIOC_STREAMON %s", strerror(errno)); - return 0; - } - break; - - default: - debug ("wrong IOMODE?"); - return 0; - } - - ConvertStart(&cdata, fmt.fmt.pix.pixelformat); - - return 1; -}; - - -int VideoDevice_V4L2::Stop() { - enum v4l2_buf_type type; - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - ConvertStop(&cdata, fmt.fmt.pix.pixelformat); - - switch (conf_iomode) { - case IOMODE_READ: - /* Nothing to do. */ - break; - case IOMODE_MMAP: - type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) { - fprintf(stderr, "%s:%d VIDIOC_STREAMOFF Error:%s\n", __FILE__, __LINE__, strerror(errno)); - return 0; - } - break; - default: - printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); - return 0; - } - Close(); - return 1; -}; - - -int VideoDevice_V4L2::GetFrame(VideoFrame *destframe) { - struct v4l2_buffer buf; - int len; - - if (destframe == NULL) return 0; - - switch (conf_iomode) { - case IOMODE_READ: - if ((len = read (fd, inbuffer[0].data, fmt.fmt.pix.sizeimage)) == -1) { - switch (errno) { - case EAGAIN: - return -1; - case EIO: - default: - debug ("v4l2_grab IOM_READ: %s dest:%p size:%d", strerror (errno), destframe, fmt.fmt.pix.sizeimage); - return 0; - } - } - else { - Convert(&cdata, destframe, inbuffer[0].data, len, - fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); - } - break; - - case IOMODE_MMAP: - CLEAR(buf); - - buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf.memory = V4L2_MEMORY_MMAP; - - if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { - switch (errno) { - case EAGAIN: - return -1; - - case EIO: - debug ("error on VIDIOC_DQBUF EIO %s", strerror(errno)); - return 0; - - default: - debug ("error on VIDIOC_DQBUF %s", strerror(errno)); - return 0; - } - } - - if (buf.index >= 0 && buf.index < VDEV_INBUFFERS) { - Convert(&cdata, destframe, inbuffer[buf.index].data, buf.bytesused, - fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); - } - - if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { - debug ("error on VIDIOC_QBUF %s", strerror(errno)); - return 0; } - - if (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0; - break; - - default: - debug ("wrong IOMODE?"); - return 0; - } - - return 1; -}; - - -int VideoDevice_V4L2::xioctl(int fd, int request, void *arg) { - int r; - int errnoioctl; - struct timeval to1; - struct timeval to2; - float to; - - gettimeofday(&to1, NULL); - do { - r = ioctl(fd, request, arg); - errnoioctl = errno; - gettimeofday(&to2, NULL); - to = (float)(to2.tv_sec - to1.tv_sec) + ((to2.tv_usec - to1.tv_usec) / 1000000.0); - } while (r == -1 && errnoioctl == EINTR && to < 2.0); - - return r; -}; - - -int VideoDevice_V4L2::SetDevCtrl(unsigned int id, int value) { - struct v4l2_control ctrl; - - CLEAR(ctrl); - ctrl.id = id; - ctrl.value = value; - if (-1 == xioctl (fd, VIDIOC_S_CTRL, &ctrl)) { - return 0; - } - - return 1; -}; - - -int VideoDevice_V4L2::GetDevCtrl(unsigned int id, int *value) { - struct v4l2_control ctrl; - - CLEAR(ctrl); - ctrl.id = id; - if (-1 == xioctl (fd, VIDIOC_G_CTRL, &ctrl)) { - return 0; - } - *value = ctrl.value; - - return 1; -}; - - -void VideoDevice_V4L2::PrintCaps(uint32_t caps) { - printf ("%s:%d %s Caps: %x\n", __FILE__, __LINE__, __FUNCTION__, caps); - - if (caps & V4L2_CAP_VIDEO_CAPTURE) printf (" V4L2_CAP_VIDEO_CAPTURE\n"); - if (caps & V4L2_CAP_EXT_PIX_FORMAT) printf (" V4L2_CAP_EXT_PIX_FORMAT\n"); -#ifdef V4L2_CAP_META_CAPTURE - if (caps & V4L2_CAP_META_CAPTURE) printf (" V4L2_CAP_META_CAPTURE\n"); -#endif - if (caps & V4L2_CAP_STREAMING) printf (" V4L2_CAP_STREAMING\n"); - if (caps & V4L2_CAP_DEVICE_CAPS) printf (" V4L2_CAP_DEVICE_CAPS\n"); - if (caps & V4L2_CAP_TUNER) printf (" V4L2_CAP_TUNER\n"); - if (caps & V4L2_CAP_MODULATOR) printf (" V4L2_CAP_MODULATOR\n"); - if (caps & V4L2_CAP_READWRITE) printf (" V4L2_CAP_READWRITE\n"); - if (caps & V4L2_CAP_ASYNCIO) printf (" V4L2_CAP_ASYNCIO\n"); -}; - - -void VideoDevice_V4L2::PrintFmt(struct v4l2_format *f) { - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - printf (" type : %u\n", f->type); - printf (" fmt.pix.width : %d\n", f->fmt.pix.width); - printf (" fmt.pix.height : %d\n", f->fmt.pix.height); - printf (" fmt.fmt.pix.pixelformat : %c%c%c%c\n", ((char*)&f->fmt.pix.pixelformat)[0], - ((char*)&f->fmt.pix.pixelformat)[1], - ((char*)&f->fmt.pix.pixelformat)[2], - ((char*)&f->fmt.pix.pixelformat)[3]); - printf (" fmt.pix.field : %d\n", f->fmt.pix.field); - printf (" fmt.pix.bytesperline : %d\n", f->fmt.pix.bytesperline); - printf (" fmt.pix.sizeimage : %d\n", f->fmt.pix.sizeimage); -}; - - -/*********************************************************************************************************/ - -VideoDevice_Dump::VideoDevice_Dump() { - debug (""); - filesize = 0; - filepos = 0; - fd = -1; - w = 0; - h = 0; - pixformat = 0; - inframe = NULL; - inframe_size = 0; - inframe_maxsize = 0; - inframe_nexttime = 0; - fixedframesize = 0; -}; - - -VideoDevice_Dump::~VideoDevice_Dump() { - debug (""); - Stop(); - Close(); -}; - - -// -// Open Device -// prepare the buffer, InitMMAP and read all controls -// -int VideoDevice_Dump::Open() { - debug ("OPEN"); - if (config.vdev_device == "") return 0; - - VideoDevCtrl vctl; - uint32_t inbuf[3]; - int i; - struct stat s; - - std::string fname = config.vdev_dumpfile; - - printf ("%s:%d %s file %s\n", __FILE__, __LINE__, __FUNCTION__, fname.c_str()); - - if (fd >= 0) close (fd); - fd = -1; - - - // - // read filesize - if (stat (fname.c_str(), &s) != 0) { - printf ("%s:%d %s could not read stat of file '%s'. Error:%s\n", __FILE__, __LINE__, __FUNCTION__, - fname.c_str(), strerror(errno)); - Close(); - return 0; - } - filesize = s.st_size; - -#ifdef BUILD_WINDOWS - if ((fd = open(fname.c_str(), O_RDONLY | O_BINARY)) == -1) -#else - if ((fd = open(fname.c_str(), O_RDONLY)) == -1) -#endif - { - printf ("%s:%d could not open file '%s' error:%s\n", __FILE__, __LINE__, fname.c_str(), strerror(errno)); - return 0; - } - - // - // read header - if (read (fd, inbuf, 12) != 12) { - printf ("%s:%d could not read all header data.\n", __FILE__, __LINE__); - close (fd); - fd = -1; - return 0; - } - i = 0; - filepos = 12; - conf_width = w = ntohl(inbuf[i++]); - conf_height = h = ntohl(inbuf[i++]); - conf_videofmt = ntohl(inbuf[i++]); - - vidctrls.clear(); - vctl.name = "FilePosition"; - vctl.id = 1; - vctl.min = 0; - vctl.max = 255; - vctl.value = 0; - vidctrls.push_back(vctl); - - return 1; -}; - - -// -// Close Device -// Free videobuffer -// -int VideoDevice_Dump::Close() { - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - - if (fd >= 0) { - close(fd); - fd = -1; - filesize = 0; - filepos = 0; - } - - return 1; -}; - - - -// -// send the start capture signal to the cam -// -int VideoDevice_Dump::Start() { - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - - Close(); - if (Open() != 1) return 0; - gettimeofday(&starttv, NULL); - - return 1; -}; - - -int VideoDevice_Dump::Stop() { - printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); - - if (fd >= 0) { - close(fd); - fd = -1; - } - - if (inframe != NULL) { - free (inframe); - inframe_size = 0; - inframe = NULL; - } - return 1; -}; - -// -// 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. -// -#define SIZE_FRAMEHEADER 8 -#define SIZE_DUMPHEADER 12 -int VideoDevice_Dump::GetFrame(VideoFrame *destframe) { - struct timeval curtv; - unsigned int diff = 0; - std::list::iterator ctrl; - - if (fd == -1) return 0; - - // - // is it time? - do { - gettimeofday(&curtv, NULL); - diff = 1000 * (curtv.tv_sec - starttv.tv_sec) + - (curtv.tv_usec - starttv.tv_usec) / 1000; - - if (diff < inframe_nexttime) - usleep ((inframe_nexttime-diff)*1000); - } while (diff < inframe_nexttime); - - ReadFrame(); - Convert(&cdata, destframe, inframe, inframe_size, - conf_videofmt, w, h); - - ctrl = vidctrls.begin(); - if (ctrl->value == 0) { - // fixed framesize -> calculate frames - switch (pixformat) { - case(V4L2_PIX_FMT_RGB24): - case(V4L2_PIX_FMT_BGR24): - fixedframesize = 3 * w * h; - break; - case(V4L2_PIX_FMT_RGB32): - case(V4L2_PIX_FMT_BGR32): - fixedframesize = 4 * w * h; - break; - case(V4L2_PIX_FMT_SGRBG8): - fixedframesize = 1 * w * h; - break; - case(V4L2_PIX_FMT_SGRBG16): - fixedframesize = 2 * w * h; - break; - default: - ctrl->max = 0; - ctrl->value = -1; - fixedframesize = 0; - break; - } - if (fixedframesize > 0) - ctrl->max = (filesize-SIZE_DUMPHEADER) / (fixedframesize + SIZE_FRAMEHEADER); - } - - if (ctrl->value != -1) ctrl->value++; - - ReadFrame(); - - return 1; -} - - -// -// Read Frame -// -int VideoDevice_Dump::ReadFrame() { - uint32_t inbuf[2]; - - if (fd < 0) return 0; - - // - // check position, if end of file restart from beginning - if (filepos == filesize) { - printf ("%s:%d end of file start with first frame\n", __FILE__, __LINE__); - std::list::iterator ctrl; - ctrl = vidctrls.begin(); - ctrl->value = 0; - if (lseek(fd, 12, SEEK_SET) != 12) { - printf ("%s:%d %s lseek returned: %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno)); - Close(); - } - else { // reset filepos and starttime - filepos = 12; - gettimeofday(&starttv, NULL); - } - } - - // - // read frame - if (read (fd, inbuf, 4*2) != 4*2) { - printf ("%s:%d could not read frame header: %s\n", __FILE__, __LINE__, strerror(errno)); - Close(); - } - filepos += (4*2); - inframe_size = ntohl(inbuf[0]); - inframe_nexttime = ntohl(inbuf[1]); - - // read header - if (inframe == NULL) { - inframe = (unsigned char*) malloc (inframe_size); - inframe_maxsize = inframe_size; - } - else if (inframe_maxsize < inframe_size) { - inframe = (unsigned char*) realloc (inframe, inframe_size); - inframe_maxsize = inframe_size; - } - - // allocate memory and read frame - if (inframe == NULL) { - Close(); - printf ("%s:%d could not allocate enough memory\n", __FILE__, __LINE__); - return 0; - } - if (read (fd, inframe, inframe_size) != inframe_size) { - printf ("%s:%d could not read frame: %s\n", __FILE__, __LINE__, strerror(errno)); - Close(); - } - filepos += inframe_size; - - return 1; -} - - - -// -// set video control identified by id -// -int VideoDevice_Dump::SetDevCtrl(unsigned int id, int value) { - std::list::iterator ctrl; - int framerest; - off_t newfilepos = 0; - printf ("%s:%d VideoDev_Dumpfile::SetDevCtrl Set Offset to %d id:%d fixedframesize:%d\n", __FILE__, __LINE__, value, id, fixedframesize); - struct timeval curtv; - - if (id == 1) { - ctrl = vidctrls.begin(); - if (value != ctrl->value && value < ctrl->max) { - filepos = SIZE_DUMPHEADER + ((off_t)value * (SIZE_FRAMEHEADER + (off_t)fixedframesize)); - printf ("%s:%d filepos:%ld\n", __FILE__, __LINE__, filepos); - - if ((newfilepos = lseek (fd, filepos, SEEK_SET)) < 0) { - printf ("%s:%d ******* lseek error:%s\n", __FILE__, __LINE__, strerror(errno)); - } - else { - framerest = (newfilepos - SIZE_DUMPHEADER)%(SIZE_FRAMEHEADER + fixedframesize); - ctrl->value = (newfilepos - SIZE_DUMPHEADER)/(SIZE_FRAMEHEADER + fixedframesize); - if (ctrl->value != value || framerest != 0) { - printf ("%s:%d could not set file to correct position. ctrl->value:%d value:%d Framerest:%d\n", __FILE__, __LINE__, ctrl->value, value, framerest); - } - else { - // - // read first frame - ReadFrame(); - gettimeofday(&curtv, NULL); - starttv.tv_sec = curtv.tv_sec - (inframe_nexttime/1000); - } - } - } - - // else printf ("%s:%d could not set video position (ctrl->value:%d value:%d max:%d)\n", - // __FILE__, __LINE__, ctrl->value, value, ctrl->max); - } - - return 1; -}; - - -// -// get video control identified by id -// -int VideoDevice_Dump::GetDevCtrl(unsigned int id, int *value) { - return 1; -}; - diff --git a/video.h b/video.h index 138b0fe..8f3a240 100644 --- a/video.h +++ b/video.h @@ -85,72 +85,7 @@ class VideoDevice { virtual int GetDevCtrl(unsigned int id, int *value) { debug (""); return 0; }; }; - - -class VideoDevice_V4L2 : public VideoDevice { - private: - int inbuffer_idx; - VideoInBuffer inbuffer[VDEV_INBUFFERS]; - struct v4l2_cropcap cropcap; - struct v4l2_crop crop; - struct v4l2_format fmt; - - int fd; // filedescriptor to the video device file - - int Open(); - int Close(); - int UnInit(); - int InitMMap(); - int xioctl(int fd, int request, void *arg); - protected: - public: - VideoDevice_V4L2(); - ~VideoDevice_V4L2(); - - int SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt); - int SetIOMode(int newiomode); - int Start(); - int Stop(); - int GetFrame (VideoFrame *destframe); - int SetDevCtrl(unsigned int id, int value); - int GetDevCtrl(unsigned int id, int *value); - - void PrintCaps(uint32_t caps); - void PrintFmt(struct v4l2_format *f); -}; - - - -class VideoDevice_Dump : public VideoDevice { - private: - int fd; - uint32_t w; - uint32_t h; - uint32_t pixformat; - off_t filesize; - off_t filepos; - struct timeval starttv; - unsigned char *inframe; - uint32_t inframe_nexttime; - int inframe_maxsize; - int inframe_size; - int fixedframesize; - int Open(); - int Close(); - int ReadFrame(); - protected: - public: - VideoDevice_Dump(); - ~VideoDevice_Dump(); - - int SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt) { config.vdev_device = newdevice; return 1; }; - int SetIOMode(int newiomode) { return 1; }; - int Start(); - int Stop(); - int GetFrame (VideoFrame *destframe); - int SetDevCtrl(unsigned int id, int value); - int GetDevCtrl(unsigned int id, int *value); -}; - +#include "videodevice_v4l2.h" +#include "videodevice_dump.h" #endif diff --git a/videodevice_dump.cc b/videodevice_dump.cc new file mode 100644 index 0000000..30e9378 --- /dev/null +++ b/videodevice_dump.cc @@ -0,0 +1,419 @@ + +#include "video.h" +#include "videoframe.h" + +int convert_debug_fd = -1; +struct timeval convert_debug_tv; + +int dumpframe_open(uint32_t pixelformat, int srcw, int srch) { + time_t t = time(NULL); + struct tm *tmptr; + char fname[LEN_FILENAME]; + char fullfname[LEN_FULLFILENAME]; + + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + if (config.vdev_dumppath.length() == 0) return -1; + + // + // check to create file, if not possible try creating the directory and create the file again + tmptr = localtime(&t); + strftime(fname, LEN_FILENAME, "%Y%m%d-%H%M%S", tmptr); + snprintf (fullfname, LEN_FULLFILENAME, "%s/%s.videodump", config.vdev_dumppath.c_str(), fname); + + if ((convert_debug_fd = creat (fullfname, 0666)) == -1) { + printf ("%s:%d could not create file '%s'. Error:%s\n", __FILE__, __LINE__, fullfname, strerror(errno)); + printf ("%s:%d try to create folder\n", __FILE__, __LINE__); + + // create folder + // FIXME: how to do thin on windows +#ifdef BUILD_WINDOWS + if ((mkdir (config.debugpath)) == -1) { +#else + if ((mkdir (config.vdev_dumppath.c_str(), 0777)) == -1) { +#endif + printf ("%s:%d could not create debug folder.\n", __FILE__, __LINE__); + return -1; + } + if ((convert_debug_fd = creat (fullfname, 0666)) == -1) { + printf ("%s:%d could again not create file '%s'. Error:%s\n", __FILE__, __LINE__, fullfname, strerror(errno)); + return -1; + } + } + + + // + // file is open for writing, write header information + uint32_t wwidth = htonl(srcw); + uint32_t wheight = htonl(srch); + uint32_t wpixfmt = htonl(pixelformat); + write (convert_debug_fd, &wwidth, 4); + write (convert_debug_fd, &wheight, 4); + write (convert_debug_fd, &wpixfmt, 4); + + // + // save start time, this is needed to calculate the time between the frames + gettimeofday (&convert_debug_tv, NULL); + + return 0; +} + + + +/* + * if fd is not open open file and set up header + */ +void dumpframe(unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch) { + struct timeval tv; + uint32_t wts, ts; + uint32_t wsize; + + if (ptrsrc == NULL) return; + if (config.vdev_dumppath.length() == 0) return; + if (convert_debug_fd == -1) + if (dumpframe_open(pixelformat, srcw, srch) == -1) return; + + // + // construct and write header + wsize = htonl(srcsize); + gettimeofday (&tv, NULL); + ts = 1000 * (tv.tv_sec - convert_debug_tv.tv_sec) + + (tv.tv_usec - convert_debug_tv.tv_usec) / 1000; + wts = htonl(ts); + + write (convert_debug_fd, &wsize, 4); + write (convert_debug_fd, &wts, 4); + + // + // write frame + write (convert_debug_fd, ptrsrc, srcsize); +}; + + +/* + * close file and reset all data + */ +void dumpframe_close() { + if (convert_debug_fd != -1) { + close (convert_debug_fd); + convert_debug_fd = -1; + } +} + + + +VideoDevice_Dump::VideoDevice_Dump() { + debug (""); + filesize = 0; + filepos = 0; + fd = -1; + w = 0; + h = 0; + pixformat = 0; + inframe = NULL; + inframe_size = 0; + inframe_maxsize = 0; + inframe_nexttime = 0; + fixedframesize = 0; +}; + + +VideoDevice_Dump::~VideoDevice_Dump() { + debug (""); + Stop(); + Close(); +}; + + +// +// Open Device +// prepare the buffer, InitMMAP and read all controls +// +int VideoDevice_Dump::Open() { + debug ("OPEN"); + if (config.vdev_device == "") return 0; + + VideoDevCtrl vctl; + uint32_t inbuf[3]; + int i; + struct stat s; + + std::string fname = config.vdev_dumpfile; + + printf ("%s:%d %s file %s\n", __FILE__, __LINE__, __FUNCTION__, fname.c_str()); + + if (fd >= 0) close (fd); + fd = -1; + + + // + // read filesize + if (stat (fname.c_str(), &s) != 0) { + printf ("%s:%d %s could not read stat of file '%s'. Error:%s\n", __FILE__, __LINE__, __FUNCTION__, + fname.c_str(), strerror(errno)); + Close(); + return 0; + } + filesize = s.st_size; + +#ifdef BUILD_WINDOWS + if ((fd = open(fname.c_str(), O_RDONLY | O_BINARY)) == -1) +#else + if ((fd = open(fname.c_str(), O_RDONLY)) == -1) +#endif + { + printf ("%s:%d could not open file '%s' error:%s\n", __FILE__, __LINE__, fname.c_str(), strerror(errno)); + return 0; + } + + // + // read header + if (read (fd, inbuf, 12) != 12) { + printf ("%s:%d could not read all header data.\n", __FILE__, __LINE__); + close (fd); + fd = -1; + return 0; + } + i = 0; + filepos = 12; + conf_width = w = ntohl(inbuf[i++]); + conf_height = h = ntohl(inbuf[i++]); + conf_videofmt = ntohl(inbuf[i++]); + + vidctrls.clear(); + vctl.name = "FilePosition"; + vctl.id = 1; + vctl.min = 0; + vctl.max = 255; + vctl.value = 0; + vidctrls.push_back(vctl); + + return 1; +}; + + +// +// Close Device +// Free videobuffer +// +int VideoDevice_Dump::Close() { + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + + if (fd >= 0) { + close(fd); + fd = -1; + filesize = 0; + filepos = 0; + } + + return 1; +}; + + + +// +// send the start capture signal to the cam +// +int VideoDevice_Dump::Start() { + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + + Close(); + if (Open() != 1) return 0; + gettimeofday(&starttv, NULL); + + return 1; +}; + + +int VideoDevice_Dump::Stop() { + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + + if (fd >= 0) { + close(fd); + fd = -1; + } + + if (inframe != NULL) { + free (inframe); + inframe_size = 0; + inframe = NULL; + } + return 1; +}; + +// +// 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. +// +#define SIZE_FRAMEHEADER 8 +#define SIZE_DUMPHEADER 12 +int VideoDevice_Dump::GetFrame(VideoFrame *destframe) { + struct timeval curtv; + unsigned int diff = 0; + std::list::iterator ctrl; + + if (fd == -1) return 0; + + // + // is it time? + do { + gettimeofday(&curtv, NULL); + diff = 1000 * (curtv.tv_sec - starttv.tv_sec) + + (curtv.tv_usec - starttv.tv_usec) / 1000; + + if (diff < inframe_nexttime) + usleep ((inframe_nexttime-diff)*1000); + } while (diff < inframe_nexttime); + + ReadFrame(); + Convert(&cdata, destframe, inframe, inframe_size, + conf_videofmt, w, h); + + ctrl = vidctrls.begin(); + if (ctrl->value == 0) { + // fixed framesize -> calculate frames + switch (pixformat) { + case(V4L2_PIX_FMT_RGB24): + case(V4L2_PIX_FMT_BGR24): + fixedframesize = 3 * w * h; + break; + case(V4L2_PIX_FMT_RGB32): + case(V4L2_PIX_FMT_BGR32): + fixedframesize = 4 * w * h; + break; + case(V4L2_PIX_FMT_SGRBG8): + fixedframesize = 1 * w * h; + break; + case(V4L2_PIX_FMT_SGRBG16): + fixedframesize = 2 * w * h; + break; + default: + ctrl->max = 0; + ctrl->value = -1; + fixedframesize = 0; + break; + } + if (fixedframesize > 0) + ctrl->max = (filesize-SIZE_DUMPHEADER) / (fixedframesize + SIZE_FRAMEHEADER); + } + + if (ctrl->value != -1) ctrl->value++; + + ReadFrame(); + + return 1; +} + + +// +// Read Frame +// +int VideoDevice_Dump::ReadFrame() { + uint32_t inbuf[2]; + + if (fd < 0) return 0; + + // + // check position, if end of file restart from beginning + if (filepos == filesize) { + printf ("%s:%d end of file start with first frame\n", __FILE__, __LINE__); + std::list::iterator ctrl; + ctrl = vidctrls.begin(); + ctrl->value = 0; + if (lseek(fd, 12, SEEK_SET) != 12) { + printf ("%s:%d %s lseek returned: %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno)); + Close(); + } + else { // reset filepos and starttime + filepos = 12; + gettimeofday(&starttv, NULL); + } + } + + // + // read frame + if (read (fd, inbuf, 4*2) != 4*2) { + printf ("%s:%d could not read frame header: %s\n", __FILE__, __LINE__, strerror(errno)); + Close(); + } + filepos += (4*2); + inframe_size = ntohl(inbuf[0]); + inframe_nexttime = ntohl(inbuf[1]); + + // read header + if (inframe == NULL) { + inframe = (unsigned char*) malloc (inframe_size); + inframe_maxsize = inframe_size; + } + else if (inframe_maxsize < inframe_size) { + inframe = (unsigned char*) realloc (inframe, inframe_size); + inframe_maxsize = inframe_size; + } + + // allocate memory and read frame + if (inframe == NULL) { + Close(); + printf ("%s:%d could not allocate enough memory\n", __FILE__, __LINE__); + return 0; + } + if (read (fd, inframe, inframe_size) != inframe_size) { + printf ("%s:%d could not read frame: %s\n", __FILE__, __LINE__, strerror(errno)); + Close(); + } + filepos += inframe_size; + + return 1; +} + + + +// +// set video control identified by id +// +int VideoDevice_Dump::SetDevCtrl(unsigned int id, int value) { + std::list::iterator ctrl; + int framerest; + off_t newfilepos = 0; + printf ("%s:%d VideoDev_Dumpfile::SetDevCtrl Set Offset to %d id:%d fixedframesize:%d\n", __FILE__, __LINE__, value, id, fixedframesize); + struct timeval curtv; + + if (id == 1) { + ctrl = vidctrls.begin(); + if (value != ctrl->value && value < ctrl->max) { + filepos = SIZE_DUMPHEADER + ((off_t)value * (SIZE_FRAMEHEADER + (off_t)fixedframesize)); + printf ("%s:%d filepos:%ld\n", __FILE__, __LINE__, filepos); + + if ((newfilepos = lseek (fd, filepos, SEEK_SET)) < 0) { + printf ("%s:%d ******* lseek error:%s\n", __FILE__, __LINE__, strerror(errno)); + } + else { + framerest = (newfilepos - SIZE_DUMPHEADER)%(SIZE_FRAMEHEADER + fixedframesize); + ctrl->value = (newfilepos - SIZE_DUMPHEADER)/(SIZE_FRAMEHEADER + fixedframesize); + if (ctrl->value != value || framerest != 0) { + printf ("%s:%d could not set file to correct position. ctrl->value:%d value:%d Framerest:%d\n", __FILE__, __LINE__, ctrl->value, value, framerest); + } + else { + // + // read first frame + ReadFrame(); + gettimeofday(&curtv, NULL); + starttv.tv_sec = curtv.tv_sec - (inframe_nexttime/1000); + } + } + } + + // else printf ("%s:%d could not set video position (ctrl->value:%d value:%d max:%d)\n", + // __FILE__, __LINE__, ctrl->value, value, ctrl->max); + } + + return 1; +}; + + +// +// get video control identified by id +// +int VideoDevice_Dump::GetDevCtrl(unsigned int id, int *value) { + return 1; +}; + diff --git a/videodevice_dump.h b/videodevice_dump.h new file mode 100644 index 0000000..4ed7503 --- /dev/null +++ b/videodevice_dump.h @@ -0,0 +1,42 @@ + +#ifndef _VIDEODEVICE_DUMP_H_ +#define _VIDEODEVICE_DUMP_H_ + +#include "video.h" + +int dumpframe_open(uint32_t pixelformat, int srcw, int srch); +void dumpframe(unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch); +void dumpframe_close(); + +class VideoDevice_Dump : public VideoDevice { + private: + int fd; + uint32_t w; + uint32_t h; + uint32_t pixformat; + off_t filesize; + off_t filepos; + struct timeval starttv; + unsigned char *inframe; + uint32_t inframe_nexttime; + int inframe_maxsize; + int inframe_size; + int fixedframesize; + int Open(); + int Close(); + int ReadFrame(); + protected: + public: + VideoDevice_Dump(); + ~VideoDevice_Dump(); + + int SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt) { config.vdev_device = newdevice; return 1; }; + int SetIOMode(int newiomode) { return 1; }; + int Start(); + int Stop(); + int GetFrame (VideoFrame *destframe); + int SetDevCtrl(unsigned int id, int value); + int GetDevCtrl(unsigned int id, int *value); +}; + +#endif diff --git a/videodevice_v4l2.cc b/videodevice_v4l2.cc new file mode 100644 index 0000000..fae998c --- /dev/null +++ b/videodevice_v4l2.cc @@ -0,0 +1,507 @@ + +#include "video.h" +#include "videoframe.h" + + +VideoDevice_V4L2::VideoDevice_V4L2() { + debug (""); + conf_height = 0; + conf_width = 0; + conf_videodev = ""; + conf_iomode = 0; + conf_videofmt = 0; + fd = -1; +}; + + +VideoDevice_V4L2::~VideoDevice_V4L2() { + debug (""); + if (fd != 0) Stop(); +}; + + + +int VideoDevice_V4L2::SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt) { + int isrunning = 0; + + if (fd != -1) { + Stop(); + isrunning = 1; + } + + conf_videodev = newdevice; + conf_width = nwidth; + conf_height = nheight; + conf_videofmt = pixfmt; + + if (isrunning) { + if (Start() == 0) return 0; + } + + return 1; +}; + + +int VideoDevice_V4L2::SetIOMode(int newiomode) { + int isrunning = 0; + + if (fd != -1) { + Stop(); + isrunning = 1; + } + + conf_iomode = newiomode; + + if (isrunning) { + if (Start() == 0) return 0; + } + + return 1; +}; + + +int VideoDevice_V4L2::Open() { + int i; + struct v4l2_capability vcap; + VideoDevCtrl vctl; + + debug ("Device: '%s'", conf_videodev.c_str()); + if (fd != -1) return 0; + + // + // open device and get device name and capabilities | O_NONBLOCK + if((fd = open(conf_videodev.c_str(), O_RDWR)) == -1){ + debug ("could not open device error: %s", strerror(errno)); + return 0; + } + + if(ioctl(fd, VIDIOC_QUERYCAP, &vcap) == -1) + strncpy ((char*)&vcap.card, "unknown", sizeof(vcap.card)); + debug ("VideoDevice_V4L2::Open Devicefile:%s Card:%s fd:%d", conf_videodev.c_str(), vcap.card, fd); + + debug ("Capabilities: %u", vcap.capabilities); + PrintCaps(vcap.capabilities); + debug ("Device Capabilities: %u", vcap.device_caps); + PrintCaps(vcap.device_caps); + + if (!(vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { + debug ("Error: device has no video capture capabilities"); + return 0; + Close(); + } + + // + // query controls + struct v4l2_queryctrl queryctrl; + uint32_t min; + debug ("Get Controls"); + + vidctrls.clear(); + memset (&queryctrl, 0, sizeof (queryctrl)); + for (i = V4L2_CID_BASE; i < V4L2_CID_DETECT_CLASS_BASE+0x1000; i++) { + queryctrl.id = i; + if (0 == ioctl (fd, VIDIOC_QUERYCTRL, &queryctrl)) { + vctl.name = (char*)queryctrl.name; + vctl.id = queryctrl.id; + vctl.min = queryctrl.minimum; + vctl.max = queryctrl.maximum; + GetDevCtrl(queryctrl.id, &vctl.value); + vidctrls.push_back(vctl); + } + } + debug ("got %d controls.", vidctrls.size()); + + // + // check for cropping.. if we have it setup default + debug ("setup cropping"); + CLEAR (cropcap); + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) { + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + crop.c = cropcap.defrect; // reset to default + + if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) { + debug ("VIDEOC_S_CROP Errorcode: %s", strerror(errno)); + } + } + + // + // prepare resolution and pixelformat + debug ("setup video resolution and pixeltype"); + CLEAR (fmt); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (conf_width != -1 && conf_height != -1) { // resolution + fmt.fmt.pix.width = conf_width; + fmt.fmt.pix.height = conf_height; + } + else { + fmt.fmt.pix.width = 1920; + fmt.fmt.pix.height = 1080; + } + + // fixme: hardcoded video format????? + fmt.fmt.pix.pixelformat = conf_videofmt; + fmt.fmt.pix.field = V4L2_FIELD_NONE; + if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt)) { + debug ("VIDIOC_S_FMT : %s", strerror (errno)); + close (fd); + fd = -1; + return 0; + } + + // Note VIDIOC_S_FMT may change width and height. + // Buggy driver paranoia. - as written in the v4l2 api documentation + min = fmt.fmt.pix.width * 2; + if (fmt.fmt.pix.bytesperline < min) + fmt.fmt.pix.bytesperline = min; + min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; + if (fmt.fmt.pix.sizeimage < min) + fmt.fmt.pix.sizeimage = min; + conf_width = fmt.fmt.pix.width; + conf_height = fmt.fmt.pix.height; + conf_videofmt = fmt.fmt.pix.pixelformat; + PrintFmt (&fmt); + + // init buffers + switch (conf_iomode) { + case IOMODE_MMAP: + if (InitMMap() == 0) { + debug ("could not setup MMap (memory mapping)"); + Close(); + } + break; + case IOMODE_READ: + for (i = 0; i < VDEV_INBUFFERS; i++) { + inbuffer[i].size = fmt.fmt.pix.sizeimage; + inbuffer[i].data = (unsigned char*) malloc (fmt.fmt.pix.sizeimage); + } + + default: + break; + } + + return 1; +}; + + +int VideoDevice_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 mapping", conf_videodev.c_str()); + return 0; + } else { + printf ("%s:%d %s Error %s\n", __FILE__, __LINE__, __FUNCTION__, strerror(errno)); + return 0; + } + } + + if (bufreq.count < 1) { + printf ( "Insufficient buffer memory on %s\n", conf_videodev.c_str()); + return 0; + } + + for (i = 0; i < VDEV_INBUFFERS; i++) { + CLEAR(bufinfo); + bufinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + bufinfo.memory = V4L2_MEMORY_MMAP; + bufinfo.index = i; + + if(ioctl(fd, VIDIOC_QUERYBUF, &bufinfo) < 0){ + perror("VIDIOC_QUERYBUF"); + exit(1); + } + + inbuffer[i].size = bufinfo.length; + inbuffer[i].data = (unsigned char*)mmap(NULL, bufinfo.length, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, bufinfo.m.offset); + if (inbuffer[i].data == MAP_FAILED) { + printf ( "%s:%d error on mmap %s\n", __FILE__, __LINE__, strerror(errno)); + exit (1); + } + } + return 1; +} + + +int VideoDevice_V4L2::Close() { + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + + if (fd >= 0) { + UnInit(); + close (fd); + fd = -1; + } + + return 1; +}; + + +int VideoDevice_V4L2::UnInit() { + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + + switch (conf_iomode) { + case IOMODE_READ: + for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) { + if (inbuffer[inbuffer_idx].data) { + free (inbuffer[inbuffer_idx].data); + inbuffer[inbuffer_idx].data = NULL; + inbuffer[inbuffer_idx].size = 0; + } + } + + break; + + case IOMODE_MMAP: + for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) + if (inbuffer[inbuffer_idx].data) { + if (-1 == munmap(inbuffer[inbuffer_idx].data, inbuffer[inbuffer_idx].size)){ + fprintf(stderr, "Fatal Error @ %s:%d munmap Error:%s\n", __FILE__, __LINE__, strerror(errno)); + exit(1); + } + inbuffer[inbuffer_idx].data = NULL; + inbuffer[inbuffer_idx].size = 0; + } + break; + + default: + printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); + return 0; + } + + + return 1; +}; + + +int VideoDevice_V4L2::Start() { + enum v4l2_buf_type type; + + debug ("V4L2"); + + if (Open() == 0) { + debug ("VideoDevice_V4L2::Start Open Device Failed."); + return 0; + } + + switch (conf_iomode) { + case IOMODE_READ: + /* Nothing to do. */ + break; + + case IOMODE_MMAP: + struct v4l2_buffer buf; + + for (inbuffer_idx = 0; inbuffer_idx < VDEV_INBUFFERS; inbuffer_idx++) { + CLEAR(buf); + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = inbuffer_idx; + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { + debug ("error on VIDIOC_QBUF %s", strerror(errno)); + return 0; + } + } + + inbuffer_idx = 0; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)) { + debug ("VIDIOC_STREAMON %s", strerror(errno)); + return 0; + } + break; + + default: + debug ("wrong IOMODE?"); + return 0; + } + + ConvertStart(&cdata, fmt.fmt.pix.pixelformat); + + return 1; +}; + + +int VideoDevice_V4L2::Stop() { + enum v4l2_buf_type type; + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + ConvertStop(&cdata, fmt.fmt.pix.pixelformat); + + switch (conf_iomode) { + case IOMODE_READ: + /* Nothing to do. */ + break; + case IOMODE_MMAP: + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)) { + fprintf(stderr, "%s:%d VIDIOC_STREAMOFF Error:%s\n", __FILE__, __LINE__, strerror(errno)); + return 0; + } + break; + default: + printf ("%s:%d %s wrong IOMODE?\n", __FILE__, __LINE__, __FUNCTION__); + return 0; + } + Close(); + return 1; +}; + + +int VideoDevice_V4L2::GetFrame(VideoFrame *destframe) { + struct v4l2_buffer buf; + int len; + + if (destframe == NULL) return 0; + + switch (conf_iomode) { + case IOMODE_READ: + if ((len = read (fd, inbuffer[0].data, fmt.fmt.pix.sizeimage)) == -1) { + switch (errno) { + case EAGAIN: + return -1; + case EIO: + default: + debug ("v4l2_grab IOM_READ: %s dest:%p size:%d", strerror (errno), destframe, fmt.fmt.pix.sizeimage); + return 0; + } + } + else { + Convert(&cdata, destframe, inbuffer[0].data, len, + fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); + } + break; + + case IOMODE_MMAP: + CLEAR(buf); + + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) { + switch (errno) { + case EAGAIN: + return -1; + + case EIO: + debug ("error on VIDIOC_DQBUF EIO %s", strerror(errno)); + return 0; + + default: + debug ("error on VIDIOC_DQBUF %s", strerror(errno)); + return 0; + } + } + + if (buf.index >= 0 && buf.index < VDEV_INBUFFERS) { + Convert(&cdata, destframe, inbuffer[buf.index].data, buf.bytesused, + fmt.fmt.pix.pixelformat, fmt.fmt.pix.width, fmt.fmt.pix.height); + } + + if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)) { + debug ("error on VIDIOC_QBUF %s", strerror(errno)); + return 0; } + + if (++inbuffer_idx >= VDEV_INBUFFERS) inbuffer_idx = 0; + break; + + default: + debug ("wrong IOMODE?"); + return 0; + } + + return 1; +}; + + +int VideoDevice_V4L2::xioctl(int fd, int request, void *arg) { + int r; + int errnoioctl; + struct timeval to1; + struct timeval to2; + float to; + + gettimeofday(&to1, NULL); + do { + r = ioctl(fd, request, arg); + errnoioctl = errno; + gettimeofday(&to2, NULL); + to = (float)(to2.tv_sec - to1.tv_sec) + ((to2.tv_usec - to1.tv_usec) / 1000000.0); + } while (r == -1 && errnoioctl == EINTR && to < 2.0); + + return r; +}; + + +int VideoDevice_V4L2::SetDevCtrl(unsigned int id, int value) { + struct v4l2_control ctrl; + + CLEAR(ctrl); + ctrl.id = id; + ctrl.value = value; + if (-1 == xioctl (fd, VIDIOC_S_CTRL, &ctrl)) { + return 0; + } + + return 1; +}; + + +int VideoDevice_V4L2::GetDevCtrl(unsigned int id, int *value) { + struct v4l2_control ctrl; + + CLEAR(ctrl); + ctrl.id = id; + if (-1 == xioctl (fd, VIDIOC_G_CTRL, &ctrl)) { + return 0; + } + *value = ctrl.value; + + return 1; +}; + + +void VideoDevice_V4L2::PrintCaps(uint32_t caps) { + printf ("%s:%d %s Caps: %x\n", __FILE__, __LINE__, __FUNCTION__, caps); + + if (caps & V4L2_CAP_VIDEO_CAPTURE) printf (" V4L2_CAP_VIDEO_CAPTURE\n"); + if (caps & V4L2_CAP_EXT_PIX_FORMAT) printf (" V4L2_CAP_EXT_PIX_FORMAT\n"); +#ifdef V4L2_CAP_META_CAPTURE + if (caps & V4L2_CAP_META_CAPTURE) printf (" V4L2_CAP_META_CAPTURE\n"); +#endif + if (caps & V4L2_CAP_STREAMING) printf (" V4L2_CAP_STREAMING\n"); + if (caps & V4L2_CAP_DEVICE_CAPS) printf (" V4L2_CAP_DEVICE_CAPS\n"); + if (caps & V4L2_CAP_TUNER) printf (" V4L2_CAP_TUNER\n"); + if (caps & V4L2_CAP_MODULATOR) printf (" V4L2_CAP_MODULATOR\n"); + if (caps & V4L2_CAP_READWRITE) printf (" V4L2_CAP_READWRITE\n"); + if (caps & V4L2_CAP_ASYNCIO) printf (" V4L2_CAP_ASYNCIO\n"); +}; + + +void VideoDevice_V4L2::PrintFmt(struct v4l2_format *f) { + printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); + printf (" type : %u\n", f->type); + printf (" fmt.pix.width : %d\n", f->fmt.pix.width); + printf (" fmt.pix.height : %d\n", f->fmt.pix.height); + printf (" fmt.fmt.pix.pixelformat : %c%c%c%c\n", ((char*)&f->fmt.pix.pixelformat)[0], + ((char*)&f->fmt.pix.pixelformat)[1], + ((char*)&f->fmt.pix.pixelformat)[2], + ((char*)&f->fmt.pix.pixelformat)[3]); + printf (" fmt.pix.field : %d\n", f->fmt.pix.field); + printf (" fmt.pix.bytesperline : %d\n", f->fmt.pix.bytesperline); + printf (" fmt.pix.sizeimage : %d\n", f->fmt.pix.sizeimage); +}; + diff --git a/videodevice_v4l2.h b/videodevice_v4l2.h new file mode 100644 index 0000000..a52bd63 --- /dev/null +++ b/videodevice_v4l2.h @@ -0,0 +1,41 @@ + +#ifndef _VIDEODEVICE_V4L2_H_ +#define _VIDEODEVICE_V4L2_H_ + +#include "video.h" + + +class VideoDevice_V4L2 : public VideoDevice { + private: + int inbuffer_idx; + VideoInBuffer inbuffer[VDEV_INBUFFERS]; + struct v4l2_cropcap cropcap; + struct v4l2_crop crop; + struct v4l2_format fmt; + + int fd; // filedescriptor to the video device file + + int Open(); + int Close(); + int UnInit(); + int InitMMap(); + int xioctl(int fd, int request, void *arg); + protected: + public: + VideoDevice_V4L2(); + ~VideoDevice_V4L2(); + + int SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt); + int SetIOMode(int newiomode); + int Start(); + int Stop(); + int GetFrame (VideoFrame *destframe); + int SetDevCtrl(unsigned int id, int value); + int GetDevCtrl(unsigned int id, int *value); + + void PrintCaps(uint32_t caps); + void PrintFmt(struct v4l2_format *f); +}; + + +#endif