From 5682fcd9b8d6063142e93f71e789041f511e5b1d Mon Sep 17 00:00:00 2001 From: Steffen Pohle Date: Sun, 4 Jan 2026 23:43:49 +0100 Subject: [PATCH] working on running version --- Makefile | 64 ++++++ configuration.cc | 3 +- configuration.h | 6 +- convert.cc | 423 +++++++++++++++++++++++++++++++++++++ convert.h | 34 +++ debayer.cc | 366 ++++++++++++++++++++++++++++++++ debayer.h | 16 ++ json.cc | 534 ----------------------------------------------- json.h | 80 ------- main.cc | 10 +- meson.build | 25 --- miniwebcam.h | 7 + video.cc | 108 ++++++++++ video.h | 90 ++++++++ webserver.cc | 2 + webserver.h | 6 + 16 files changed, 1131 insertions(+), 643 deletions(-) create mode 100644 Makefile create mode 100644 convert.cc create mode 100644 convert.h create mode 100644 debayer.cc create mode 100644 debayer.h delete mode 100644 json.cc delete mode 100644 json.h delete mode 100644 meson.build create mode 100644 video.cc create mode 100644 video.h create mode 100644 webserver.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7da3183 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +# .SILENT: + +DEPENDFILE=.depend +VERSION=0.1 + +PREFIX=/usr/local +DATAPREFIX=/var/lib +RUNPID=/var/run/miniwebcam.pid +ETCPREFIX=/etc +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 + +all: dep miniwebcam + +miniwebcam: dep $(OBJFILES) + $(CXX) $(OBJFILES) -o $@ -L./ -I./ $(LDFLAGS) + +install: miniwebcam + cp -rf miniwebcam $(PREFIX)/bin + +uninstall: + rm -rf $(PREFIX)/bin/miniwebcam + +config: + echo "#ifndef _CONFIG_H_" > config.h + echo "#define _CONFIG_H_" >> config.h + echo "" >> config.h + echo "#define VERSION \"$(VERSION)\"" >> config.h + echo "" >> config.h + echo "#define PREFIX \"$(PREFIX)\"" >> config.h + echo "#define RUNPID \"$(RUNPID)\"" >> config.h + echo "#define ETCPREFIX \"$(ETCPREFIX)\"" >> config.h + echo "" >> config.h + echo "#define DEFAULT_SERVERPORT \""$(DEFUALT_SERVERPORT)"\"" >> config.h + echo "" >> config.h + echo "#endif" >> config.h + echo "" >> config.h + +rebuild: clean all + +dep: + $(CXX) -MM `ls *.cc` $(CXXFLAGS) > $(DEPENDFILE) + +clean: + rm *.s -rf + rm *.o -rf + rm *.oo -rf + rm *~ -rf + rm -rf .depend + rm -rf *.so + rm -rf *.so.* + rm -rf miniwebcam + rm -rf config.h + rm -rf Makefile.rules + +cleanall: clean + +source: cleanall + +-include $(DEPENDFILE) diff --git a/configuration.cc b/configuration.cc index b5fed0a..cf3fbbe 100644 --- a/configuration.cc +++ b/configuration.cc @@ -5,9 +5,8 @@ #include #include -#include "configuration.h" #include "miniwebcam.h" -#include "json.h" +#include "configuration.h" Configuration config; diff --git a/configuration.h b/configuration.h index 0294d11..a240314 100644 --- a/configuration.h +++ b/configuration.h @@ -1,12 +1,16 @@ + #ifndef _CONFIGURATION_H_ #define _CONFIGURATION_H_ +#include #include +#include "config.h" + #define DEFAULT_HTTP_PORT 10080 #define DEFAULT_HTTPS_PORT 10081 -#define DEFAULT_CONFIG_FILE "/etc/miniwebcam.conf.json" +#define DEFAULT_CONFIG_FILE "/etc/miniwebcam.conf" #define CONF_INITFLAGS_PRINT 0x0001 #define CONF_INITFLAGS_HELP 0x0002 diff --git a/convert.cc b/convert.cc new file mode 100644 index 0000000..b741350 --- /dev/null +++ b/convert.cc @@ -0,0 +1,423 @@ + +#include +#include +#include +#include +#include + +#ifdef BUILD_WINDOWS +#include "windows.h" +#else +#include +#endif + +#include "convert.h" +#include "video.h" +#include "configuration.h" +#include "debayer.h" + +int debayer_mode = 0; // testing 0 or 1 + +uint32_t convert_pixelformats [] = { + V4L2_PIX_FMT_MJPEG, + V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_RGB32, + V4L2_PIX_FMT_BGR32, + V4L2_PIX_FMT_RGB24, + V4L2_PIX_FMT_BGR24, + V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_SGRBG16, + V4L2_PIX_FMT_SGRBG8, + 0 +}; + + +/* + * helper part for converting different video types + */ + +// +// 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) { + 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); +}; + + +int ConvertStart(ConvertData *cdata, uint32_t pixelformat) { + if (cdata == NULL) return 0; + + if (pixelformat == V4L2_PIX_FMT_MJPEG) { + jpeg_create_decompress(&cdata->cinfo); + } + + return 1; +}; + +int ConvertStop(ConvertData *cdata, uint32_t pixelformat) { + if (cdata == NULL) return 0; + if (pixelformat == V4L2_PIX_FMT_MJPEG) jpeg_destroy_decompress(&cdata->cinfo); + + return 1; +}; + + +/* + * converts the video from input type to RGB24 type - 24Bit + */ +int Convert (ConvertData *cdata, 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; + + struct jpg_error_mgr jerr; + if (cdata == NULL) return 0; + + // check if there is a destination and that the destination is large to keep + // the full image + if (dest == NULL || ptrsrc == NULL) + return 0; + + dest->SetSize(srcw, srch); + ptrdst = dest->GetPixBuf(); + + 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 */ + + r = *(ptrsrc++); + g = *(ptrsrc++); + b = *(ptrsrc++); + ptrsrc++; + + /* only paint the image if the source is within the destination */ + if (xd < srcw) { + /* set the pixel */ + *(ptrdst++) = r; + *(ptrdst++) = g; + *(ptrdst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places.. */ + if (xd < srcw) + ptrdst += 3 * (srcw - 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 */ + + ptrsrc++; + b = *(ptrsrc++); + g = *(ptrsrc++); + r = *(ptrsrc++); + + /* only paint the image if the source is within the destination */ + if (xd < srcw) { + /* set the pixel */ + *(ptrdst++) = r; + *(ptrdst++) = g; + *(ptrdst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places.. */ + if (xd < srcw) + ptrdst += 3 * (srcw - xd); + yd++; + } + break; + case (V4L2_PIX_FMT_RGB24): + for (ys = 0, yd = 0; ys < (signed int)srch; ys++) { + for (xs = 0, xd = 0; xs < (signed int)srcw; xs++) { + /* read the pixel */ + + r = *(ptrsrc++); + g = *(ptrsrc++); + b = *(ptrsrc++); + + /* only paint the image if the source is within the destination */ + if (xd < srcw) { + /* set the pixel */ + *(ptrdst++) = r; + *(ptrdst++) = g; + *(ptrdst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places.. */ + if (xd < srcw) + ptrdst += 3 * (srcw - xd); + yd++; + } + break; + + case (V4L2_PIX_FMT_BGR24): + 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++); + + /* only paint the image if the source is within the destination */ + if (xd < srcw) { + /* set the pixel */ + *(ptrdst++) = r; + *(ptrdst++) = g; + *(ptrdst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places.. */ + if (xd < srcw) + ptrdst += 3 * (srcw - xd); + yd++; + } + break; + + case (V4L2_PIX_FMT_SGRBG16): + if (debayer_mode == 0) + debayer_grbg16_simple ((uint16_t *)ptrsrc, srcw, srch, ptrdst, srcw, srch); + else + debayer_grbg16_bilinear ((uint16_t *)ptrsrc, srcw, srch, ptrdst, srcw, srch); + break; + + case (V4L2_PIX_FMT_SGRBG8): + if (debayer_mode == 0) + debayer_grbg8_simple ((uint8_t *)ptrsrc, srcw, srch, ptrdst, srcw, srch); + else + debayer_grbg8_bilinear ((uint8_t *)ptrsrc, srcw, srch, ptrdst, srcw, srch); + 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 < srcw) { + /* set the pixel */ + *(ptrdst++) = r; + *(ptrdst++) = g; + *(ptrdst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places.. */ + if (xd < srcw) + ptrdst += 3 * (srcw - xd); + yd++; + } + break; + + case (V4L2_PIX_FMT_YUYV): + for (ys = 0, yd = 0; ys < (signed int)srch; ys++) { + if (yd < srch) { + 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 < srcw) { + /* set the pixel */ + *(ptrdst++) = r; + *(ptrdst++) = g; + *(ptrdst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places.. */ + if (xd < srcw) + ptrdst += 3 * (srcw - xd); + yd++; + } + } + break; + + case (V4L2_PIX_FMT_MJPEG): + cdata->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 0; + } + + jpeg_mem_src(&cdata->cinfo, ptrsrc, srcsize); + jpeg_read_header(&cdata->cinfo, TRUE); + jpeg_start_decompress(&cdata->cinfo); + + while (cdata->cinfo.output_scanline < cdata->cinfo.output_height) { + unsigned char *temp_array[] = {ptrdst + (cdata->cinfo.output_scanline) * srcw * 3}; + jpeg_read_scanlines(&cdata->cinfo, temp_array, 1); + } + + jpeg_finish_decompress(&cdata->cinfo); + + break; + default: + printf ("%s:%d Error no default possible, need to finish\n", __FILE__, __LINE__); + exit (-1); + break; + } + + return 1; +}; + + + +std::string convert_from_pixelformat (uint32_t fmt) { + char txt[5]; + + snprintf (txt, 5, "%c%c%c%c", ((char*)&fmt)[0], ((char*)&fmt)[1], ((char*)&fmt)[2], ((char*)&fmt)[3]); + return txt; +}; + + +uint32_t convert_to_pixelformat(std::string s) { + uint32_t u = 0; + + u = ((unsigned char) s[0]) + ((unsigned char) s[1] << 8) + + ((unsigned char) s[2] << 16) + ((unsigned char) s[3] << 24); + + return u; +}; + + +/* + * copy part of an raw image + * destination must be pointer in case we need to align the size of the destination image. + * this function will also realloc needed memory if needed. (only if: givin size < needed size) + * + * crop image to event set of dimensions + */ +int PixCopy(unsigned char *srcdata, uint32_t srcpixfmt, int srcw, int srch, + unsigned char **dstdataptr, int *dstsize, int *dstw, int *dsth, + int regionx, int regiony, int regionw, int regionh) { + + if (srcpixfmt == 0 || srcpixfmt == V4L2_PIX_FMT_MJPEG) return 0; + if (srcdata == NULL || dstdataptr == NULL) return 0; + + // crop size to an even number + (*dsth) = regionw & ~1; + (*dstw) = regionh & ~1; + + int bytesperpixel = 3; + int dsize = 0; + + switch (srcpixfmt) { + case (V4L2_PIX_FMT_SGRBG8): + bytesperpixel = 1; + break; + case (V4L2_PIX_FMT_SGRBG16): + bytesperpixel = 2; + break; + case (V4L2_PIX_FMT_BGR32): + case (V4L2_PIX_FMT_RGB32): + bytesperpixel = 4; + break; + case (V4L2_PIX_FMT_BGR24): + case (V4L2_PIX_FMT_RGB24): + bytesperpixel = 3; + break; + default: + return 0; + } + + // + // calculate image size and allocate memory if needed + dsize = (*dsth) * (*dstw) * bytesperpixel; + if ((*dstsize) < dsize || (*dstdataptr) == NULL) { + *dstdataptr = (unsigned char*) realloc (*dstdataptr, dsize); + if (*dstdataptr == NULL) { + errorexit((char*)"%s:%d could not realloc memory. dsize:%d error:%s\n", __FILE__, __LINE__, dsize, strerror(errno)); + } + *dstsize = dsize; + printf ("%s:%d reallocate memory for destination raw image\n", __FILE__, __LINE__); + } + + unsigned char *dptr, *sptr; + int y, dy, x; + + // debug_drawraw(srcdata, srcpixfmt, srcw, srch); + for (y = regiony & (~1), dy = 0; dy < *dsth && y < srch; y++, dy++) { + x = regionx & (~1); + dptr = (*dstdataptr) + bytesperpixel * (dy * *dstw); + sptr = (srcdata) + bytesperpixel * ( y * srcw + x); + memcpy (dptr, sptr, *dstw * bytesperpixel); + } + + return 1; +} + + + diff --git a/convert.h b/convert.h new file mode 100644 index 0000000..85e0af0 --- /dev/null +++ b/convert.h @@ -0,0 +1,34 @@ + +#ifndef _CONVERT_H_ +#define _CONVERT_H_ + +#include +#include +#include +#include +#include + +#include "video.h" + +#ifndef CLEAR +#define CLEAR(x) memset (&(x), 0, sizeof (x)) +#endif + +struct { + struct jpeg_decompress_struct cinfo; +} typedef ConvertData; + +int Convert (ConvertData *cdata, VideoFrame *dest, unsigned char *ptrsrc, int srcsize, uint32_t pixelformat, int srcw, int srch); +int ConvertStart(ConvertData *cdata, uint32_t pixelformat); +int ConvertStop(ConvertData *cdata, uint32_t pixelformat); + +int PixCopy(unsigned char *srcdata, uint32_t srcpixfmt, int srcw, int srch, + unsigned char **dstdataptr, int *dstsize, int *dstw, int *dsth, + int regionx, int regiony, int regionw, int regionh); + +extern uint32_t convert_pixelformats[]; +std::string convert_from_pixelformat (uint32_t fmt); +uint32_t convert_to_pixelformat(std::string s); + + +#endif diff --git a/debayer.cc b/debayer.cc new file mode 100644 index 0000000..fdcc03c --- /dev/null +++ b/debayer.cc @@ -0,0 +1,366 @@ +#include +#include + +/* + The function converts a 16bit GRBG 2x2 CFA coded image into an 8bit + RGB image by setting the missing RGB values to zero. + */ +void debayer_grbg16_simple (uint16_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h) { + + // GG RR + // BB GG + uint16_t t; + uint8_t r, g, b; + int ys, yd, xs, xd; + + for (ys = 0, yd = 0; ys < src_h && yd < dst_h; ys++, yd++) { + for (xs = 0, xd = 0; xs < src_w; xs++) { + + /* read the pixel but only the higher 8bit, assuming data is little endian */ + t = (*(src++) >> 8) & 0xff; + + if (xs & 1) { + if (ys & 1) { + // lower right green pixel + b = 0; g = t; r = 0; + } else { + // upper right red pixel + b = 0; g = 0; r = t; + } + } else { + if (ys & 1) { + // lower left blue pixel + b = t; g = 0; r = 0; + } else { + // upper left green pixel + b = 0; g = t; r = 0; + } + } + + /* only paint the image if the source is within the destination */ + if (xd < dst_w) { + /* set the pixel */ + *(dst++) = r; + *(dst++) = g; + *(dst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places */ + if (xd < dst_w) + dst += 3 * (dst_w - xd); + } +} + +// macros to make code better readable +#define CE (*(src)) +#define UP (*(src-src_w)) +#define DN (*(src+src_w)) +#define LE (*(src-1)) +#define RI (*(src+1)) +#define UPLE (*(src-src_w-1)) +#define UPRI (*(src-src_w+1)) +#define DNLE (*(src+src_w-1)) +#define DNRI (*(src+src_w+1)) + +#define BITCONV(d) ((d>>8) & 0xff) +#define STORE *(dst++) = BITCONV(r); *(dst++) = BITCONV(g); *(dst++) = BITCONV(b); src++; +#define STORE8 *(dst++) = (r & 0xff); *(dst++) = (g & 0xff); *(dst++) = (b & 0xff); src++; + + +/* + The function converts a 16bit GRBG 2x2 CFA coded image into an 8bit + RGB image by bilinear interpolation. + */ +void debayer_grbg16_bilinear (uint16_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h) { + + // GG RR GG RR + // BB GG BB GG + // GG RR GG RR + // BB GG BB GG + uint32_t r, g, b; + int xs, ys; + + // start with upper left pixel (green) + r = RI; + g = CE; + b = DN; + STORE; + + // upper first line, starts with RR GG RR ... + for (xs = 1; xs < src_w - 1; xs+=2) { + // red pixel + r = CE; + g = (LE + RI + DN) / 3; + b = (DNLE + DNRI) / 2; + STORE; + // green pixel + r = (LE + RI) / 2; + g = CE; + b = DN; + STORE; + } + + // upper right pixel (red) + r = CE; + g = (DN + LE) / 2; + b = DNLE; + STORE; + + // go through the "body" of the image + for (ys = 1; ys < src_h - 1; ys+=2) { + + // every second line with BB GG BB GG (start at 2nd line) + + // left hand pixel (blue) + r = (UPRI + DNRI) / 2; + g = (UP + DN + RI) / 3; + b = CE; + STORE; + for (xs = 1; xs < src_w - 1; xs+=2) { + // green pixel + r = (UP + DN) / 2; + g = CE; + b = (LE + RI) / 2; + STORE; + // blue pixel + r = (UPLE + UPRI + DNLE + DNRI) / 4; + g = (LE + RI + UP + DN) / 4; + b = CE; + STORE; + } + // last pixel in line (green) + r = (UP + DN) / 2; + g = CE; + b = LE; + STORE; + + // every second line with GG RR GG RR ... (start at 3rd line) + + // left hand pixel (green) + r = RI; + g = CE; + b = (UP + DN) / 2; + STORE; + for (xs = 1; xs < src_w - 1; xs+=2) { + // red pixel + r = CE; + g = (LE + RI + UP + DN) / 4; + b = (UPLE + UPRI + DNLE + DNRI) / 4; + STORE; + // green pixel + r = (LE + RI) / 2; + g = CE; + b = (UP + DN) / 2; + STORE; + } + // last pixel in line (red) + r = CE; + g = (UP + DN + LE) / 3; + b = (UPLE + DNLE) / 2; + STORE; + } + + // bottom left pixel + r = UPRI; + g = (UP + RI) / 2; + b = CE; + STORE; + + // last line starting with GG BB GG ... + for (xs = 1; xs < src_w - 1; xs+=2) { + // green pixel + r = UP; + g = CE; + b = (LE + RI) / 2; + STORE; + // blue pixel + r = (UPLE + UPRI) / 2; + g = (LE + UP + RI) / 2; + b = CE; + STORE; + } + + // bottom right pixel (green) + r = UP; + g = CE; + b = LE; + STORE; +} + +/* + The function converts a 8bit GRBG 2x2 CFA coded image into an 8bit + RGB image by setting the missing RGB values to zero. + */ +void debayer_grbg8_simple (uint8_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h) { + + // GG RR + // BB GG + uint8_t t; + uint8_t r, g, b; + int ys, yd, xs, xd; + + for (ys = 0, yd = 0; ys < src_h && yd < dst_h; ys++, yd++) { + for (xs = 0, xd = 0; xs < src_w; xs++) { + + /* read the pixel but only the higher 8bit, assuming data is little endian */ + t = *(src++); + + if (xs & 1) { + if (ys & 1) { + // lower right green pixel + b = 0; g = t; r = 0; + } else { + // upper right red pixel + b = 0; g = 0; r = t; + } + } else { + if (ys & 1) { + // lower left blue pixel + b = t; g = 0; r = 0; + } else { + // upper left green pixel + b = 0; g = t; r = 0; + } + } + + /* only paint the image if the source is within the destination */ + if (xd < dst_w) { + /* set the pixel */ + *(dst++) = r; + *(dst++) = g; + *(dst++) = b; + xd++; + } + } + + /* if the source image is too small ignore the other places */ + if (xd < dst_w) + dst += 3 * (dst_w - xd); + } +} + +/* + The function converts a 8bit GRBG 2x2 CFA coded image into an 8bit + RGB image by bilinear interpolation. + */ +void debayer_grbg8_bilinear (uint8_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h) { + + // GG RR GG RR + // BB GG BB GG + // GG RR GG RR + // BB GG BB GG + uint16_t r, g, b; + int xs, ys; + + // start with upper left pixel (green) + r = RI; + g = CE; + b = DN; + STORE8; + + // upper first line, starts with RR GG RR ... + for (xs = 1; xs < src_w - 1; xs+=2) { + // red pixel + r = CE; + g = (LE + RI + DN) / 3; + b = (DNLE + DNRI) / 2; + STORE8; + // green pixel + r = (LE + RI) / 2; + g = CE; + b = DN; + STORE8; + } + + // upper right pixel (red) + r = CE; + g = (DN + LE) / 2; + b = DNLE; + STORE8; + + // go through the "body" of the image + for (ys = 1; ys < src_h - 1; ys+=2) { + + // every second line with BB GG BB GG (start at 2nd line) + + // left hand pixel (blue) + r = (UPRI + DNRI) / 2; + g = (UP + DN + RI) / 3; + b = CE; + STORE8; + for (xs = 1; xs < src_w - 1; xs+=2) { + // green pixel + r = (UP + DN) / 2; + g = CE; + b = (LE + RI) / 2; + STORE8; + // blue pixel + r = (UPLE + UPRI + DNLE + DNRI) / 4; + g = (LE + RI + UP + DN) / 4; + b = CE; + STORE8; + } + // last pixel in line (green) + r = (UP + DN) / 2; + g = CE; + b = LE; + STORE8; + + // every second line with GG RR GG RR ... (start at 3rd line) + + // left hand pixel (green) + r = RI; + g = CE; + b = (UP + DN) / 2; + STORE8; + for (xs = 1; xs < src_w - 1; xs+=2) { + // red pixel + r = CE; + g = (LE + RI + UP + DN) / 4; + b = (UPLE + UPRI + DNLE + DNRI) / 4; + STORE8; + // green pixel + r = (LE + RI) / 2; + g = CE; + b = (UP + DN) / 2; + STORE8; + } + // last pixel in line (red) + r = CE; + g = (UP + DN + LE) / 3; + b = (UPLE + DNLE) / 2; + STORE8; + } + + // bottom left pixel + r = UPRI; + g = (UP + RI) / 2; + b = CE; + STORE8; + + // last line starting with GG BB GG ... + for (xs = 1; xs < src_w - 1; xs+=2) { + // green pixel + r = UP; + g = CE; + b = (LE + RI) / 2; + STORE8; + // blue pixel + r = (UPLE + UPRI) / 2; + g = (LE + UP + RI) / 2; + b = CE; + STORE8; + } + + // bottom right pixel (green) + r = UP; + g = CE; + b = LE; + STORE8; +} diff --git a/debayer.h b/debayer.h new file mode 100644 index 0000000..2621019 --- /dev/null +++ b/debayer.h @@ -0,0 +1,16 @@ +#ifndef _DEBAYER_H_ +#define _DEBAYER_H_ + +#include + +void debayer_grbg16_simple (uint16_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h); +void debayer_grbg8_simple (uint8_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h); + +void debayer_grbg16_bilinear (uint16_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h); +void debayer_grbg8_bilinear (uint8_t * src, int src_w, int src_h, + uint8_t * dst, int dst_w, int dst_h); + +#endif diff --git a/json.cc b/json.cc deleted file mode 100644 index fad7399..0000000 --- a/json.cc +++ /dev/null @@ -1,534 +0,0 @@ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "json.h" - -/*********************************************************************** - *********************************************************************** - * - * JSONParse - * - *********************************************************************** - *********************************************************************** - */ - -enum { - STEP_NONE = 0, - STEP_STARTNAME, - STEP_NAME, - STEP_STARTVALUE, - STEP_VALUE, - STEP_END -}; - - -/* - * clear out all data - */ -void JSONParse::Clear() { - jsondata = ""; - names.clear(); -} - -/* - * read every element and keep only this in memory. - */ -int JSONParse::Set(string json) { - int i; - int step; - int level; - bool ignorenext; - - JSONElement jelement; - - Clear(); - - // find start and read until end - for (step = STEP_NONE, i = 0, ignorenext = false; (unsigned int)i < json.length(); i++) { - // need to copy next character - if (ignorenext) { - ignorenext = false; - if (step == STEP_NAME) jelement.name += json[i]; - if (step == STEP_VALUE) jelement.value += json[i]; - } - - // searching for startname - else if (step == STEP_NONE) { - if (json[i] == '{') { - step = STEP_STARTNAME; - continue; - } - } - - // searching for startname - else if (step == STEP_STARTNAME) { - if (json[i] == '"') { - step = STEP_NAME; - continue; - } - } - - // copy name - else if (step == STEP_NAME) { - if (json[i] == '"') { - step = STEP_STARTVALUE; - continue; - } - else { - jelement.name += json[i]; - continue; - } - } - - // searching for startvalue - else if (step == STEP_STARTVALUE) { - if (json[i] == '"') { - step = STEP_VALUE; - jelement.type = JSON_T_STRING; - continue; - } - if (json[i] == '{') { - step = STEP_VALUE; - level = 0; - jelement.type = JSON_T_OBJECT; - jelement.value = "{"; - continue; - } - if (json[i] == '[') { - step = STEP_VALUE; - level = 0; - jelement.type = JSON_T_ARRAY; - jelement.value = "["; - continue; - } - if ((json[i] >= '0' && json[i] <= '9') || - (json[i] == '+' || json[i] == '-')) { - step = STEP_VALUE; - level = 0; - jelement.type = JSON_T_NUMBER; - jelement.value = json[i]; - continue; - } - } - - // copy value - else if (step == STEP_VALUE) { - if (jelement.type == JSON_T_STRING) { - if (json[i] == '"') step = STEP_END; - else jelement.value += json[i]; - continue; - } - else if (jelement.type == JSON_T_OBJECT) { - if (json[i] == '}' && level == 0) { - jelement.value += json[i]; - step = STEP_END; - } - else { - if (json[i] == '{') level++; // increase level - if (json[i] == '}') level--; // decrease level - jelement.value += json[i]; - } - continue; - } - else if (jelement.type == JSON_T_ARRAY) { - if (json[i] == ']' && level == 0) { - jelement.value += json[i]; - step = STEP_END; - } - else { - if (json[i] == '[') level++; // increase level - if (json[i] == ']') level--; // decrease level - jelement.value += json[i]; - } - continue; - } - else if (jelement.type == JSON_T_NUMBER) { - if ((json[i] < '0' || json[i] > '9') && json[i] != '.' && - json[i] != '+' && json[i] != 'e' && json[i] != 'E') step = STEP_END; - else { - jelement.value += json[i]; - continue; - } - } - } - - // another element? - if (step == STEP_END) { - if (json[i] == ',') { - if (jelement.type != JSON_T_NONE) { - names.push_back (jelement); - } - jelement.Clear(); - step = STEP_STARTNAME; - } - continue; - } - } - if (jelement.type != JSON_T_NONE) { - names.push_back (jelement); - } - - return 0; -}; - - -int JSONParse::GetValue(string varname, string *dest) { - list::iterator iter; - - if (dest == NULL) return 0; - *dest = ""; - - for (iter = names.begin(); iter != names.end(); iter++) { - if (varname.compare(iter->name) == 0) { - *dest = iter->value; - return 1; - } - } - - return 0; -}; - - -int JSONParse::GetValueInt(string varname, int *dest) { - string s; - int res = GetValue(varname, &s); - if (res) { - *dest = atoi (s.c_str()); - return 1; - } - return 0; -}; - - -int JSONParse::GetValueDouble(string varname, double *dest) { - string s; - int res = GetValue(varname, &s); - if (res) { - *dest = atof (s.c_str()); - return 1; - } - return 0; -}; - - -int JSONParse::GetValueInt64(string varname, int64_t *dest) { - string s; - int res = GetValue(varname, &s); - if (res) { - *dest = atol (s.c_str()); - return 1; - } - return 0; -}; - - -int JSONParse::GetObjectJson(string varname, JSONParse *dest) { - list::iterator iter; - - if (dest == NULL) return 0; - - for (iter = names.begin(); iter != names.end(); iter++) { - if (varname.compare(iter->name) == 0) { - dest->Set(iter->value); - return 1; - } - } - - return 0; -}; - - -#define MAXRECURSIVE 255 -int JSONParse::GetIdx(string src, int idx, string *dest) { - char recursive[MAXRECURSIVE]; - int i = 0, rcnt = 0, cnt = 0; - - (*dest) = ""; - - for (i = 0; i < MAXRECURSIVE; i++) recursive[i] = 0; - for (i = 0; (unsigned int) i < src.length() && rcnt < MAXRECURSIVE && cnt <= idx; i++) { - if (src[i] == '[') { - recursive[rcnt++] = src[i]; - continue; - } - else if (src[i] == '{' && recursive[rcnt] != '"') recursive[++rcnt] = src[i]; - else if (src[i] == '}' && recursive[rcnt] == '{') rcnt--; - else if (src[i] == '"' && recursive[rcnt] == '"') rcnt--; - else if (src[i] == '"') recursive[++rcnt] = src[i]; - else if (src[i] == ',' && rcnt == 1) { - cnt++; - continue; - } - else if (src[i] == ']' && rcnt == 1) { - continue; - } - - if (rcnt > 0 && cnt == idx) { - (*dest) += src[i]; - if (src[i] == '\\') (*dest) += src[i]; - } - else { - if (src[i] == '\\')i++; - } - } - - // - // final checks - if (cnt == 0 && idx == 0 && // empty source/array? - dest->size() == 0) return 0; // - if (cnt >= idx) return 1; // found the right element - return 0; // element not found -} -#undef MAXRECURSIVE - -int JSONParse::GetValueIdx(string varname, int idx, string *dest) { - list::iterator iter; - - if (dest == NULL) return 0; - - for (iter = names.begin(); iter != names.end(); iter++) { - if (varname.compare(iter->name) == 0) { - return GetIdx(iter->value, idx, dest); - } - } - - return 0; -}; - -int JSONParse::GetObjectIdx(string varname, int idx, JSONParse *dest) { - list::iterator iter; - string deststr; - int ret = 0; - - if (dest == NULL) return 0; - - for (iter = names.begin(); iter != names.end(); iter++) { - if (varname.compare(iter->name) == 0) { - ret = GetIdx(iter->value, idx, &deststr); - if (ret == 1) dest->Set(deststr); - return ret; - } - } - - return 0; -}; - -list JSONParse::GetElements() { - list l; - list::iterator iter; - - l.clear(); - for (iter = names.begin(); iter != names.end(); iter++) { - l.push_back(*iter); - } - - return l; -}; - - -void JSONParse::AddObject (JSONElement element) { - names.push_back (element); -}; - - -void JSONParse::AddObject (string name, JSONParse jp) { - JSONElement je; - je.SetObject(name, jp.ToString()); - names.push_back(je); -}; - - -void JSONParse::AddObject (string name, int val) { - JSONElement je; - je.Set(name, val); - names.push_back(je); -}; - - -void JSONParse::AddObject (string name, int64_t val) { - JSONElement je; - je.Set(name, val); - names.push_back(je); -}; - - -void JSONParse::AddObject (string name, string val) { - JSONElement je; - je.Set(name, val); - names.push_back(je); -}; - - -void JSONParse::AddObject (string name, double val) { - JSONElement je; - je.Set(name, val); - names.push_back(je); -}; - - -string JSONParse::ToString() { - list::iterator iter; - string output; - int level, i; - - output = "{"; - - for (level = 1, iter = names.begin(); iter != names.end(); iter++) { - if (iter != names.begin()) output += ","; - output += "\n"; - for (i = 0; i < 4*level; i++) output += " "; - output += iter->GetString(); - } - - output += "\n}\n"; - - return output; -}; - - - -/* - * Load/Save elements to a file. - * Return Value: -1 .. on Error, errno will be set - * 0 .. on Success - */ -int JSONParse::LoadFromFile(string filename) { - int fd; - struct stat fs; - char *buffer; - - if (stat(filename.c_str(), &fs) != 0) return -1; - buffer = (char *) malloc (fs.st_size+1); - memset (buffer, 0x0, fs.st_size+1); - - fd = open(filename.c_str(), O_RDONLY); - if (fd < 0) return -1; - read (fd, buffer, fs.st_size); - close (fd); - - Set(buffer); - free (buffer); - - return 0; -}; - -int JSONParse::SaveToFile(string filename) { - ofstream out(filename); - - if (!out) return -1; - - out << ToString(); - out.close(); - - return 0; -}; - - - - -/*********************************************************************** - *********************************************************************** - * - * JSONElement - * - *********************************************************************** - *********************************************************************** - */ - - -void JSONElement::Set (string n, double v) { - name = n; - value = to_string(v); - type = JSON_T_NUMBER; -}; - - -void JSONElement::Set (string n, int v) { - name = n; - value = to_string(v); - type = JSON_T_NUMBER; -}; - - -void JSONElement::Set (string n, int64_t v) { - name = n; - value = to_string(v); - type = JSON_T_NUMBER; -}; - - -void JSONElement::Set (string n, string v) { - name = n; - value = v; - type = JSON_T_STRING; -}; - - -void JSONElement::SetArray (string n, list *l) { - list::iterator iter; - - name = n; - value = "["; - type = JSON_T_STRING; - - for (iter = l->begin(); iter != l->end(); iter++) { - if (iter != l->begin()) value += ","; - value += iter->GetString(); - } - value += "]"; -}; - - -void JSONElement::SetObject (string n, string s) { - name = n; - value = s; - type = JSON_T_OBJECT; -}; - - -string JSONElement::GetString () { - string output = ""; - string filename = __FILE__; - - switch (type) { - case(JSON_T_NUMBER): - output += "\"" + name + "\" : " + value; - break; - case(JSON_T_STRING): - if (value.length()==0) { - output += "\"" + name + "\" : \"\""; - } - else if (value[0] != '"') { - output += "\"" + name + "\" : \"" + value + "\""; - } - else output += "\"" + name + "\" : " + value; - break; - case(JSON_T_OBJECT): - output += "\"" + name + "\" : " + value; - break; - case(JSON_T_ARRAY): - if (value.length()==0) { - output += "\"" + name + "\" : []"; - } - else if (value[0] != '[') { - output += "\"" + name + "\" : [" + value + "]"; - } - else output += "\"" + name + "\" : " + value; - break; - default: - output += "\"error\" : \""+ filename + ":" + to_string(__LINE__) + " JSONElement unknown type error\"("+to_string(type)+")"; - break; - } - - return output; -}; diff --git a/json.h b/json.h deleted file mode 100644 index 69080fd..0000000 --- a/json.h +++ /dev/null @@ -1,80 +0,0 @@ - -// -// -// - -#ifndef _JSON_H_ -#define _JSON_H_ - -#include -#include -#include - -using namespace std; - -enum { - JSON_T_NONE, - JSON_T_STRING, - JSON_T_NUMBER, - JSON_T_OBJECT, - JSON_T_ARRAY -}; - -class JSONElement { -public: - int type; - string name; - string value; - - JSONElement() { Clear(); }; - ~JSONElement() {}; - - void Clear() { type = JSON_T_NONE; name = ""; value = ""; }; - void Set (string n, double v); - void Set (string n, int v); - void Set (string n, int64_t v); - void Set (string n, string v); - void SetArray (string n, list *l); - void SetObject (string n, string s); - string GetString(); -}; - -class JSONParse { -private: - string jsondata; - list names; - -public: - JSONParse() { Set("{}"); }; - JSONParse(string json) { Set(json); }; - ~JSONParse() {}; - - void Clear(); - int Set(string json); - - int GetValue(string varname, string *dest); - int GetValueInt(string varname, int *dest); - int GetValueDouble(string varname, double *dest); - int GetValueInt64(string varname, int64_t *dest); - int GetObjectJson(string varname, JSONParse *dest); - - int GetIdx(string src, int idx, string *dest); - int GetValueIdx(string varname, int idx, string *dest); - int GetObjectIdx(string varname, int idx, JSONParse *dest); - - list GetElements(); - - void AddObject (JSONElement element); - void AddObject (string name, int val); - void AddObject (string name, int64_t val); - void AddObject (string name, string val); - void AddObject (string name, double val); - void AddObject (string name, JSONParse jp); - - int LoadFromFile(string filename); - int SaveToFile(string filename); - - string ToString(); -}; - -#endif // _JSON_H_ diff --git a/main.cc b/main.cc index 6df6fc0..f8c60f6 100644 --- a/main.cc +++ b/main.cc @@ -10,7 +10,7 @@ void ErrorExit(std::string text, int errorcode) { printf ("Error: %s\n", text.c_str()); exit (errorcode); -} +}; int main(int argc, char **argv) { config.LoadArgs (argc, argv); @@ -26,6 +26,14 @@ int main(int argc, char **argv) { } printf ("MiniWebCam:\n"); + VideoFrame v; + VideoFrameFloat vf; + + printf ("VideoFrame Size:\n"); + v.SetSize(100,100); + + printf ("VideoFrameFloat Size:\n"); + vf.SetSize(100,100); return 0; }; diff --git a/meson.build b/meson.build deleted file mode 100644 index 60e0e79..0000000 --- a/meson.build +++ /dev/null @@ -1,25 +0,0 @@ - -project('miniwebcam', 'cpp', default_options: [ - 'cpp_std=gnu++14' -]) - -miniwebcam_src = [ - 'main.cc', - 'configuration.cc', - 'webserver.cc', - 'json.cc' -] - -miniwebcam_headers = [ - 'configuration.h', - 'miniwebcam.h', - 'json.h' -] - - -executable('miniwebcam', - install : true, - sources: miniwebcam_src, - extra_files: miniwebcam_headers -) - diff --git a/miniwebcam.h b/miniwebcam.h index da1f19e..e39a42f 100644 --- a/miniwebcam.h +++ b/miniwebcam.h @@ -1,9 +1,16 @@ + #ifndef _MINIWEBCAM_H_ #define _MINIWEBCAM_H_ +#include #include +#include "configuration.h" +#include "miniwebcam.h" +#include "video.h" + + void ErrorExit(std::string text, int errorcode); #endif diff --git a/video.cc b/video.cc new file mode 100644 index 0000000..64626e3 --- /dev/null +++ b/video.cc @@ -0,0 +1,108 @@ + +#include "miniwebcam.h" +#include "video.h" +#include +#include + +VideoFrame::VideoFrame() { + mem = NULL; + width = 0; + height = 0; + mem_allocated = 0; +}; + +VideoFrame::~VideoFrame() { + FreeFrame(); +}; + + +void VideoFrame::FreeFrame() { + if (mem != NULL) { + free (mem); + mem = NULL; + width = 0; + height = 0; + mem_allocated = 0; + } +}; + + +void VideoFrame::AllocateFrame() { + printf ("VideoFrame::AllocateFrame()\n"); + int memnewsize = width * height * 3; + if (memnewsize >= mem_allocated) return; + else if (memnewsize == 0) FreeFrame(); + + mem = (unsigned char *) realloc (mem, memnewsize); + mem_allocated = memnewsize; + + if (mem == NULL) { + debug ("Error on allocation new frame\n"); + exit (1); + } +}; + + +int VideoFrame::SetSize(int w, int h) { + if (w < 0 && h < 0) return 0; + + width = w; + height = h; + + AllocateFrame(); + + return 1; +}; + + +/*********************************************************************/ + + + +void VideoFrameFloat::AllocateFrame() { + printf ("VideoFrameFloat::AllocateFrame()\n"); + int memnewsize = width * height * 3 * sizeof(float); + if (memnewsize >= mem_allocated) return; + else if (memnewsize == 0) FreeFrame(); + + mem = (unsigned char *) realloc (mem, memnewsize); + mem_allocated = memnewsize; + + if (mem == NULL) { + debug ("Error on allocation new frame\n"); + exit (1); + } +}; + + +/*********************************************************************/ + +VideoDevice::VideoDevice() { + videofd = 0; +}; + + +VideoDevice::~VideoDevice() { + +}; + + +int VideoDevice::SetDevice() { + return 0; +}; + + +int VideoDevice::Start(int w, int h) { + return 0; +}; + + +int VideoDevice::Stop() { + return 0; +}; + + +int VideoDevice::GetFrame(VideoFrame *destframe) { + return 0; +}; + diff --git a/video.h b/video.h new file mode 100644 index 0000000..caa8792 --- /dev/null +++ b/video.h @@ -0,0 +1,90 @@ + +#ifndef _VIDEO_H_ +#define _VIDEO_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +enum { + IOMODE_READ, + IOMODE_MMAP +}; + +// +// 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; + + +// +// only contain 24bit each color 8Bit +class VideoFrame { + private: + virtual void AllocateFrame(); + protected: + void FreeFrame(); + int mem_allocated; + unsigned char *mem; + int height; + int width; + public: + VideoFrame(); + ~VideoFrame(); + + int GetHeight() { return height; }; + int GetWidth() { return width; }; + unsigned char *GetPixBuf() { return mem; }; + + int SetSize(int w, int h); +}; + + +class VideoFrameFloat : public VideoFrame { + private: + void AllocateFrame(); + protected: + public: +}; + + +class VideoDevice { + private: + int videofd; + protected: + public: + VideoDevice(); + ~VideoDevice(); + + int SetDevice(); + int Start(int w, int h); + int Stop(); + int GetFrame(VideoFrame *destframe); +}; + + +#endif diff --git a/webserver.cc b/webserver.cc index e69de29..4e41dde 100644 --- a/webserver.cc +++ b/webserver.cc @@ -0,0 +1,2 @@ + +#include "webserver.h" diff --git a/webserver.h b/webserver.h new file mode 100644 index 0000000..e4472cb --- /dev/null +++ b/webserver.h @@ -0,0 +1,6 @@ +#ifndef _WEBSERVER_H_ +#define _WEBSERVER_H_ + +#include "miniwebcam.h" + +#endif \ No newline at end of file