#include #include #include #include #include #ifdef BUILD_WINDOWS #include "windows.h" #else #include #endif #include "convert.h" #include "gui.h" #include "video.h" #include "configuration.h" #include "debayer.h" 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 }; /* * dump data into debug file * * file description: * * uint32_t width; * uint32_t height; * uint32_t pixelformat; * * uint32_t size; * uint32_t timestamp_ms; * * uint8_t data[size]; * * * .... */ int convert_debug_fd = -1; struct timeval convert_debug_tv; /* * 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]; if (config.debugpath == NULL) return -1; printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); // // 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.debugpath, 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.debugpath, 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.debugpath == NULL) 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; } } /* * 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 VDEV_STATUS_ERROR; if (pixelformat == V4L2_PIX_FMT_MJPEG) { jpeg_create_decompress(&cdata->cinfo); } return VDEV_STATUS_OK; }; int ConvertStop(ConvertData *cdata, uint32_t pixelformat) { convert_debug_close(); if (cdata == NULL) return VDEV_STATUS_ERROR; if (pixelformat == V4L2_PIX_FMT_MJPEG) jpeg_destroy_decompress(&cdata->cinfo); return VDEV_STATUS_OK; }; /* * 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; if (config.debugpath != NULL) convert_debug_dumpframe(ptrsrc, srcsize, pixelformat, srcw, srch); struct jpg_error_mgr jerr; if (cdata == NULL) return VDEV_STATUS_ERROR; // check if there is a destination and that the destination is large to keep // the full image if (dest == NULL || ptrsrc == NULL) return VDEV_STATUS_ERROR; if (dest->data != NULL && dest->w != srcw && dest->h != srch) { free (dest->data); dest->data = NULL; } if (dest->data == NULL) { dest->w = srcw; dest->h = srch; dest->size = srcw * srch * 3; dest->data = (unsigned char*) malloc (dest->size); } ptrdst = dest->data; 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 < dest->w) { /* set the pixel */ *(ptrdst++) = r; *(ptrdst++) = g; *(ptrdst++) = b; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dest->w) ptrdst += 3 * (dest->w - 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 < dest->w) { /* set the pixel */ *(ptrdst++) = r; *(ptrdst++) = g; *(ptrdst++) = b; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dest->w) ptrdst += 3 * (dest->w - 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 < dest->w) { /* set the pixel */ *(ptrdst++) = r; *(ptrdst++) = g; *(ptrdst++) = b; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dest->w) ptrdst += 3 * (dest->w - 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 < dest->w) { /* set the pixel */ *(ptrdst++) = r; *(ptrdst++) = g; *(ptrdst++) = b; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dest->w) ptrdst += 3 * (dest->w - xd); yd++; } break; case (V4L2_PIX_FMT_SGRBG16): if (config.debayer_mode == 0) debayer_grbg16_simple ((uint16_t *)ptrsrc, srcw, srch, ptrdst, dest->w, dest->h); else debayer_grbg16_bilinear ((uint16_t *)ptrsrc, srcw, srch, ptrdst, dest->w, dest->h); break; case (V4L2_PIX_FMT_SGRBG8): if (config.debayer_mode == 0) debayer_grbg8_simple ((uint8_t *)ptrsrc, srcw, srch, ptrdst, dest->w, dest->h); else debayer_grbg8_bilinear ((uint8_t *)ptrsrc, srcw, srch, ptrdst, dest->w, dest->h); 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 < dest->w) { /* set the pixel */ *(ptrdst++) = r; *(ptrdst++) = g; *(ptrdst++) = b; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dest->w) ptrdst += 3 * (dest->w - xd); yd++; } break; case (V4L2_PIX_FMT_YUYV): for (ys = 0, yd = 0; ys < (signed int)srch; ys++) { if (yd < dest->h) { 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 < dest->w) { /* set the pixel */ *(ptrdst++) = r; *(ptrdst++) = g; *(ptrdst++) = b; xd++; } } /* if the source image is too small ignore the other places.. */ if (xd < dest->w) ptrdst += 3 * (dest->w - 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 VDEV_STATUS_ERROR; } 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 VDEV_STATUS_OK; }; 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); *dstsize = dsize; printf ("%s:%d reallocate memory for destination raw image\n", __FILE__, __LINE__); } unsigned char *dptr, *sptr; int y, dy, x, dx; // 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); for (dx = (*dstw*bytesperpixel); dx > 0; dx--, dptr++, sptr++) *dptr = *sptr; } if (config.show_debugwin) debug_drawraw(*dstdataptr, srcpixfmt, *dstw, *dsth); return 1; }