You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
510 lines
12 KiB
510 lines
12 KiB
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef BUILD_WINDOWS
|
|
#include "windows.h"
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#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:
|
|
* <HEDER>
|
|
* uint32_t width;
|
|
* uint32_t height;
|
|
* uint32_t pixelformat;
|
|
* <FRAMEHEADER>
|
|
* uint32_t size;
|
|
* uint32_t timestamp_ms;
|
|
* <FRAME>
|
|
* uint8_t data[size];
|
|
* <FRAMEHEADER>
|
|
* <FRAME>
|
|
* ....
|
|
*/
|
|
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() {
|
|
|
|
printf ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__);
|
|
|
|
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)
|
|
*/
|
|
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) return 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|