From 66b6ab6073a0544b4705bdf5b55a5fb15158bcd3 Mon Sep 17 00:00:00 2001 From: Steffen Pohle Date: Tue, 20 Jan 2026 20:58:56 +0100 Subject: [PATCH] adding dumpfile for debugging --- configuration.cc | 20 +++ configuration.h | 2 + convert.cc | 105 ++++++++++++++- main.cc | 23 ++-- miniwebcam.h | 3 + video.cc | 345 ++++++++++++++++++++++++++++++++++++++++++++++- video.h | 53 ++++++-- videoframe.h | 1 + 8 files changed, 530 insertions(+), 22 deletions(-) diff --git a/configuration.cc b/configuration.cc index 2e4638f..1938242 100644 --- a/configuration.cc +++ b/configuration.cc @@ -21,6 +21,8 @@ Configuration::Configuration() { vdev_iomode = DEFAULT_VDEV_IOMODE; vdev_device = DEFAULT_VDEV_DEVICE; vdev_format = ""; + vdev_dumpfile = ""; + vdev_dumppath = ""; ssl_key = DEFAULT_SSL_KEY; ssl_cert = DEFAULT_SSL_CERT; initflags = 0; @@ -56,6 +58,7 @@ int Configuration::PrintConfig() { jp.AddObject("vdev-format", vdev_format); jp.AddObject("web-height", web_height); jp.AddObject("web-width", web_width); + jp.AddObject("vdev-dumpath", vdev_dumppath); // // output the json string @@ -94,6 +97,7 @@ int Configuration::LoadFile(std::string fn) { if (jp.GetValueString("vdev-device", &s)) vdev_device = s; if (jp.GetValueString("vdev-format", &s)) vdev_format = s; + if (jp.GetValueString("vdev-dumppath", &s)) vdev_dumppath = s; if (jp.GetValueInt("vdev-iomode", &i)) vdev_iomode = i; if (jp.GetValueInt("vdev-height", &i)) vdev_height = i; if (jp.GetValueInt("vdev-width", &i)) vdev_width = i; @@ -174,6 +178,20 @@ int Configuration::LoadArgs(int argc, char **argv) { else ErrorExit("missing device parameter", -1); } + if (strcmp(argv[i], "-vdevdumpfile") == 0) { + if (++i < argc) { + vdev_dumpfile = argv[i]; + } + else + ErrorExit("missing dump file parameter", -1); + } + if (strcmp(argv[i], "-vdevdumppath") == 0) { + if (++i < argc) { + vdev_dumppath = argv[i]; + } + else + ErrorExit("missing dump path parameter", -1); + } if (strcmp(argv[i], "-sslkey") == 0) { if (++i < argc) { ssl_key = argv[i]; @@ -210,6 +228,8 @@ void Configuration::Help() { printf (" -vdevdevice FILE Device File i.e. /dev/video2\n"); printf (" -vdevsize INT INT define video input resolution\n"); printf (" -vdevformat FORMAT 4 Char Format code - see v4l2 documentation (videodev2.h)\n"); + printf (" -vdevdumpfile FILE file to read raw image data\n"); + printf (" -vdevdumppath PATH path to save dump data to\n"); printf ("\n"); printf (" -config FILE load this configfile\n"); printf (" -dump_config print the config file\n"); diff --git a/configuration.h b/configuration.h index 4749c6a..0d69cbc 100644 --- a/configuration.h +++ b/configuration.h @@ -42,6 +42,8 @@ public: int vdev_iomode; std::string vdev_device; std::string vdev_format; + std::string vdev_dumpfile; + std::string vdev_dumppath; Configuration(); ~Configuration(); diff --git a/convert.cc b/convert.cc index da779e2..93894ce 100644 --- a/convert.cc +++ b/convert.cc @@ -17,6 +17,8 @@ #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, @@ -39,6 +41,104 @@ uint32_t convert_pixelformats [] = { * helper part for converting different video types */ +/* + * 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 // @@ -87,6 +187,8 @@ int ConvertStart(ConvertData *cdata, uint32_t pixelformat) { }; int ConvertStop(ConvertData *cdata, uint32_t pixelformat) { + convert_debug_close(); + if (cdata == NULL) return 0; if (pixelformat == V4L2_PIX_FMT_MJPEG) jpeg_destroy_decompress(&cdata->cinfo); @@ -105,6 +207,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); struct jpg_error_mgr jerr; if (cdata == NULL) return 0; @@ -440,5 +543,3 @@ int PixCopy(unsigned char *srcdata, uint32_t srcpixfmt, int srcw, int srch, return 1; } - - diff --git a/main.cc b/main.cc index 8c211e7..919fb58 100644 --- a/main.cc +++ b/main.cc @@ -23,6 +23,8 @@ void ErrorExit(std::string text, int errorcode) { int main(int argc, char **argv) { + VideoDevice *vdev= NULL; + printf ("MiniWebCam:\n"); if (SetupSignals() == 0) return 0; @@ -38,7 +40,12 @@ int main(int argc, char **argv) { return 0; } - VideoDevice_V4L2 vdev; + if (config.vdev_dumpfile.length() > 0) { + vdev = new VideoDevice_Dump(); + } + else { + vdev = new VideoDevice_V4L2(); + } WebCamServer webserver; currentimage.TestScreen(1920, 1080); @@ -46,17 +53,17 @@ int main(int argc, char **argv) { webserver.SetupSSL(config.ssl_key, config.ssl_cert); webserver.Start(); - if (vdev.SetDevice (config.vdev_device, config.vdev_width, config.vdev_height, convert_to_pixelformat(config.vdev_format)) == 0) { + if (vdev->SetDevice (config.vdev_device, config.vdev_width, config.vdev_height, convert_to_pixelformat(config.vdev_format)) == 0) { debug ("could not setup device.\n"); return 0; } - if (vdev.SetIOMode (config.vdev_iomode) == 0) { + if (vdev->SetIOMode (config.vdev_iomode) == 0) { debug ("could not setup iomode.\n"); return 0; } - if (vdev.Start() == 0) { + if (vdev->Start() == 0) { debug ("Error: could not start video.\n"); return 0; } @@ -64,9 +71,9 @@ int main(int argc, char **argv) { currentimage.SetSize (config.web_width, config.web_height); while (running) { - if (vdev.GetFrame(&inputimage) == 0) { - vdev.Stop(); - vdev.Start(); + if (vdev->GetFrame(&inputimage) == 0) { + vdev->Stop(); + vdev->Start(); } inputimage.CopyTo(¤timage, config.web_width, config.web_height); @@ -74,7 +81,7 @@ int main(int argc, char **argv) { usleep (1000); } - vdev.Stop(); + vdev->Stop(); webserver.Stop(); return 0; diff --git a/miniwebcam.h b/miniwebcam.h index 84d7da5..bbbdf6d 100644 --- a/miniwebcam.h +++ b/miniwebcam.h @@ -11,6 +11,9 @@ #include "video.h" #include "inmemoryfile.h" +#define LEN_FILENAME 256 +#define LEN_FULLFILENAME 512 + class WebCamServer : public WebServer { private: protected: diff --git a/video.cc b/video.cc index b3e7f47..15daac2 100644 --- a/video.cc +++ b/video.cc @@ -1,22 +1,42 @@ +#define _LARGEFILE64_SOURCE 1 +#define _FILE_OFFSET_BITS 64 + + #include "miniwebcam.h" #include "video.h" #include "convert.h" #include #include +#include +#include +#include +#include + +#ifdef BUILD_WINDOWS +#include +#include +#include +#else +#include +#endif +#include VideoDevice::VideoDevice() { + debug (""); }; VideoDevice::~VideoDevice() { - + debug (""); }; +/*********************************************************************************************************/ VideoDevice_V4L2::VideoDevice_V4L2() { + debug (""); conf_height = 0; conf_width = 0; conf_videodev = ""; @@ -27,6 +47,7 @@ VideoDevice_V4L2::VideoDevice_V4L2() { VideoDevice_V4L2::~VideoDevice_V4L2() { + debug (""); if (fd != 0) Stop(); }; @@ -301,7 +322,7 @@ int VideoDevice_V4L2::UnInit() { int VideoDevice_V4L2::Start() { enum v4l2_buf_type type; - debug (""); + debug ("V4L2"); if (Open() == 0) { debug ("VideoDevice_V4L2::Start Open Device Failed."); @@ -515,3 +536,323 @@ void VideoDevice_V4L2::PrintFmt(struct v4l2_format *f) { 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 367c3a5..138b0fe 100644 --- a/video.h +++ b/video.h @@ -65,6 +65,7 @@ struct { class VideoDevice { private: protected: + std::list vidctrls; std::string conf_videodev; uint32_t conf_videofmt; int conf_width; @@ -73,22 +74,21 @@ class VideoDevice { ConvertData cdata; public: VideoDevice(); - ~VideoDevice(); + virtual ~VideoDevice(); - virtual int SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt) { return 0; }; - virtual int SetIOMode(int newiomode) { return 0; }; - virtual int Start() { return 0; }; - virtual int Stop() { return 0; }; - virtual int GetFrame (VideoFrame *destframe) { return 0; }; - virtual int SetDevCtrl(unsigned int id, int value) { return 0; }; - virtual int GetDevCtrl(unsigned int id, int *value) { return 0; }; + virtual int SetDevice(std::string newdevice, int nwidth, int nheight, uint32_t pixfmt) { debug (""); return 0; }; + virtual int SetIOMode(int newiomode) { debug (""); return 0; }; + virtual int Start() { debug (""); return 0; }; + virtual int Stop() { debug (""); return 0; }; + virtual int GetFrame (VideoFrame *destframe) { debug (""); return 0; }; + virtual int SetDevCtrl(unsigned int id, int value) { debug (""); return 0; }; + virtual int GetDevCtrl(unsigned int id, int *value) { debug (""); return 0; }; }; + class VideoDevice_V4L2 : public VideoDevice { private: - std::list vidctrls; - int inbuffer_idx; VideoInBuffer inbuffer[VDEV_INBUFFERS]; struct v4l2_cropcap cropcap; @@ -120,4 +120,37 @@ class VideoDevice_V4L2 : public VideoDevice { }; + +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/videoframe.h b/videoframe.h index ff87d80..1c2d93e 100644 --- a/videoframe.h +++ b/videoframe.h @@ -25,6 +25,7 @@ class VideoFrame { int ConvertToJpeg(InMemoryFile *imf, int quality); int TestScreen(int w, int h); int CopyTo(VideoFrame *dest, int destw, int desth); + int CopyFrom(int sw, int sh, int ssize, unsigned char *sdata); };