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