From 8ea0da109c1e8134b123f618b5e7df5274183e87 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Fri, 19 Sep 2014 17:44:44 +0200 Subject: YUYV -> JPEG conversion (untested). --- src/v4l.cc | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/src/v4l.cc b/src/v4l.cc index 7d8ed3f..c83da06 100644 --- a/src/v4l.cc +++ b/src/v4l.cc @@ -44,6 +44,13 @@ #include +extern "C" { +#include +} + +#define JPEG_HEADER_PAD 500 + +static bool isYUYV = false; static V4L *v4l = NULL; #define CLEAR(x) memset(&(x), 0, sizeof(x)) @@ -85,9 +92,86 @@ static int xioctl(int fh, int request, void *arg) return r; } +/* + * In typical computer APIs, "YUV" actually means YCbCr, and "YUYV" means + * "YCbCr 4:2:2" stored as Y0, Cb01, Y1, Cr01, Y2 ... + */ +static void toJpeg(int quality, unsigned char *yuyv, + int image_width, int image_height, + char **out, size_t *size) +{ + JSAMPLE *image_buffer = (JSAMPLE*)yuyv; + + long unsigned int buffersize = + (image_width * image_height * 3) + JPEG_HEADER_PAD; + unsigned char *jpeg_output_buffer = (unsigned char *)malloc(buffersize); + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + + JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s] + int row_stride; // physical row width in image buffer + + // Allocate and initialize JPEG compression object + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + // Specify data destination (see jpeg_mem_dest) + jpeg_mem_dest(&cinfo, &jpeg_output_buffer, &buffersize); + + // Set compression parameters + cinfo.image_width = image_width; // image width and height, in pixels + cinfo.image_height = image_height; + cinfo.input_components = 3; // # of color components per pixel + cinfo.in_color_space = JCS_RGB; // colorspace of input image + + jpeg_set_defaults(&cinfo); + + jpeg_set_quality(&cinfo, quality, TRUE); // limit to baseline-JPEG values + + // Start compressor + jpeg_start_compress(&cinfo, TRUE); + + // While (scan lines remain to be written) + row_stride = image_width * 3; // JSAMPLEs per row in image_buffer + + unsigned char *yuv = new unsigned char[image_width * 3]; + while (cinfo.next_scanline < cinfo.image_height) { + for (int i = 0; i < cinfo.image_width; i += 2) { + yuv[i*3] = yuyv[i*2]; + yuv[i*3+1] = yuyv[i*2+1]; + yuv[i*3+2] = yuyv[i*2+3]; + yuv[i*3+3] = yuyv[i*2+2]; + yuv[i*3+4] = yuyv[i*2+1]; + yuv[i*3+5] = yuyv[i*2+3]; + } + row_pointer[0] = yuv; + yuyv += image_width * 2; + jpeg_write_scanlines(&cinfo, row_pointer, 1); + + // row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride]; + // (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + delete[] yuv; + + // Finish compression + jpeg_finish_compress(&cinfo); + + // Release JPEG compression object + jpeg_destroy_compress(&cinfo); + + *size = buffersize; + *out = (char*)jpeg_output_buffer; +} + static void process_image(const void *p, int size) { - if(v4l) v4l->processImage(p, size); + const void *img = p; + int img_size = size; + + if(isYUYV) { + toJpeg(70, (unsigned char*)p, 640, 480, (char**)&img, (size_t*)&img_size); + } + if(v4l) v4l->processImage(img, img_size); /* if (out_buf) fwrite(p, size, 1, stdout); @@ -528,10 +612,15 @@ static void init_device(void) format[4] = '\0'; printf("format: %s\n", format); if(strcmp(format, "JPEG")) { - printf("Webcam does not support JPEG...!\n"); - exit(1); + if(strcmp(format, "YUYV")) { + printf("Webcam neither supports JPEG nor YUYV...!\n"); + exit(1); + } else { + isYUYV = true; // Activate libJPEG encoding on each frame. + } } + /* Note VIDIOC_S_FMT may change width and height. */ } else { /* Preserve original settings as set by v4l2-ctl for example */ -- cgit v1.2.3