00001
00002
00003
00004
00005
00006
00007
00008
00009
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051 #ifndef _V4L_UTILS
00052 #define _V4L_UTILS
00053
00054 #include "ofConstants.h"
00055
00056 #include <stdio.h>
00057 #include <stdlib.h>
00058 #include <string.h>
00059 #include <errno.h>
00060 #include <fcntl.h>
00061 #include <unistd.h>
00062 #include <sys/ioctl.h>
00063 #include <sys/stat.h>
00064 #include <sys/mman.h>
00065 #include <sys/types.h>
00066 #include <time.h>
00067
00068 #include <linux/videodev.h>
00069
00070 #define HAVE_V4L 1
00071
00072
00073 static struct video_capability capability;
00074 static struct video_window window;
00075 static struct video_picture imageProperties;
00076 static struct video_mbuf mbuf;
00077 static struct video_mmap vmmap;
00078
00079 static int deviceHandle;
00080 static int frameIndex;
00081 static bool bFirstFrame;
00082 static unsigned char *bigbuf;
00083
00084
00085 bool openV4L_device(const char *device_name);
00086 void initialiseV4L_device(const char *device_name);
00087 bool initV4L(int width, int height,const char *devname);
00088 bool queryV4L_imageProperties(void);
00089 bool setV4L_palette(int palette, int depth);
00090 bool setV4L_imageProperties(void);
00091 bool setV4L_videoSize(int width, int height);
00092 bool getFrameV4L(unsigned char * pixels);
00093 void closeV4L(void);
00094 bool mmapV4L(void);
00095 int getV4L_Height();
00096 int getV4L_Width();
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 #define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
00122
00123 static inline void
00124 move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, int rowPixels, unsigned char * rgb)
00125 {
00126 const int rvScale = 91881;
00127 const int guScale = -22553;
00128 const int gvScale = -46801;
00129 const int buScale = 116129;
00130 const int yScale = 65536;
00131 int r, g, b;
00132
00133 g = guScale * u + gvScale * v;
00134 r = rvScale * v;
00135 b = buScale * u;
00136
00137 yTL *= yScale; yTR *= yScale;
00138 yBL *= yScale; yBR *= yScale;
00139
00140
00141 rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
00142 rgb[2] = LIMIT(r+yTL);
00143
00144 rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
00145 rgb[5] = LIMIT(r+yTR);
00146
00147
00148 rgb += 3 * rowPixels;
00149 rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
00150 rgb[2] = LIMIT(r+yBL);
00151
00152 rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
00153 rgb[5] = LIMIT(r+yBR);
00154 }
00155
00156 static inline void
00157 move_411_block(int yTL, int yTR, int yBL, int yBR, int u, int v,
00158 int rowPixels, unsigned char * rgb)
00159 {
00160 const int rvScale = 91881;
00161 const int guScale = -22553;
00162 const int gvScale = -46801;
00163 const int buScale = 116129;
00164 const int yScale = 65536;
00165 int r, g, b;
00166
00167 g = guScale * u + gvScale * v;
00168 r = rvScale * v;
00169 b = buScale * u;
00170
00171 yTL *= yScale; yTR *= yScale;
00172 yBL *= yScale; yBR *= yScale;
00173
00174
00175 rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL);
00176 rgb[2] = LIMIT(r+yTL);
00177
00178 rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR);
00179 rgb[5] = LIMIT(r+yTR);
00180
00181
00182 rgb += 6;
00183 rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL);
00184 rgb[2] = LIMIT(r+yBL);
00185
00186 rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR);
00187 rgb[5] = LIMIT(r+yBR);
00188 }
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202 static void
00203 yuv420p_to_rgb24(int width, int height, unsigned char *pIn0, unsigned char *pOut0)
00204 {
00205 const int numpix = width * height;
00206 const int bytes = 24 >> 3;
00207 int i, j, y00, y01, y10, y11, u, v;
00208 unsigned char *pY = pIn0;
00209 unsigned char *pU = pY + numpix;
00210 unsigned char *pV = pU + numpix / 4;
00211 unsigned char *pOut = pOut0;
00212
00213 for (j = 0; j <= height - 2; j += 2) {
00214 for (i = 0; i <= width - 2; i += 2) {
00215 y00 = *pY;
00216 y01 = *(pY + 1);
00217 y10 = *(pY + width);
00218 y11 = *(pY + width + 1);
00219 u = (*pU++) - 128;
00220 v = (*pV++) - 128;
00221
00222 move_420_block(y00, y01, y10, y11, u, v,
00223 width, pOut);
00224
00225 pY += 2;
00226 pOut += 2 * bytes;
00227
00228 }
00229 pY += width;
00230 pOut += width * bytes;
00231 }
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249 static void
00250 yuv411p_to_rgb24(int width, int height,
00251 unsigned char *pIn0, unsigned char *pOut0)
00252 {
00253 const int numpix = width * height;
00254 const int bytes = 24 >> 3;
00255 int i, j, y00, y01, y10, y11, u, v;
00256 unsigned char *pY = pIn0;
00257 unsigned char *pU = pY + numpix;
00258 unsigned char *pV = pU + numpix / 4;
00259 unsigned char *pOut = pOut0;
00260
00261 for (j = 0; j <= height; j++) {
00262 for (i = 0; i <= width - 4; i += 4) {
00263 y00 = *pY;
00264 y01 = *(pY + 1);
00265 y10 = *(pY + 2);
00266 y11 = *(pY + 3);
00267 u = (*pU++) - 128;
00268 v = (*pV++) - 128;
00269
00270 move_411_block(y00, y01, y10, y11, u, v,
00271 width, pOut);
00272
00273 pY += 4;
00274 pOut += 4 * bytes;
00275
00276 }
00277 }
00278 }
00279
00280
00281 static void errno_exit (const char *s)
00282 {
00283 printf ("%s error %d, %s\n",s, errno, strerror (errno));
00284 exit (EXIT_FAILURE);
00285 }
00286
00287 static int xioctl (int deviceHandle, int request, void *arg)
00288 {
00289 int r;
00290
00291 do r = ioctl (deviceHandle, request, arg);
00292 while (-1 == r && EINTR == errno);
00293
00294 return r;
00295 }
00296
00297 bool openV4L_device(const char *device_name)
00298 {
00299 struct stat st;
00300
00301 if (stat (device_name, &st) == -1) {
00302 printf("V4L : Cannot identify '%s': %d, %s\n", device_name, errno, strerror (errno));
00303 exit (EXIT_FAILURE);
00304 }
00305
00306 if (!S_ISCHR (st.st_mode)) {
00307 printf("V4L : %s is no device\n", device_name);
00308 exit (EXIT_FAILURE);
00309 }
00310
00311 deviceHandle = open(device_name, O_RDWR | O_NONBLOCK, 0);
00312
00313 if (deviceHandle == -1) {
00314 printf("V4L : Cannot open '%s': %d, %s\n", device_name, errno, strerror (errno));
00315 close(deviceHandle);
00316 exit (EXIT_FAILURE);
00317 }
00318
00319 return true;
00320 }
00321
00322 bool queryV4L_imageProperties(void) {
00323
00324 memset(&imageProperties, 0, sizeof(struct video_picture));
00325
00326 if (xioctl(deviceHandle, VIDIOCGPICT, &imageProperties) == -1) {
00327 printf("V4L : Unable to determine image properties\n");
00328 errno_exit ("VIDIOCGPICT");
00329 }
00330
00331 printf("V4L : Brightness\t= %u\n",imageProperties.brightness);
00332 printf("V4L : Hue\t\t= %u\n",imageProperties.hue);
00333 printf("V4L : Colour\t\t= %u\n",imageProperties.colour);
00334 printf("V4L : Depth\t\t= %u bits\n",imageProperties.depth);
00335 printf("V4L : Palette\t\t= ");
00336
00337 switch(imageProperties.palette) {
00338 case VIDEO_PALETTE_GREY:
00339 printf("VIDEO_PALETTE_GREY\n");
00340 break;
00341 case VIDEO_PALETTE_HI240:
00342 printf("VIDEO_PALETTE_HI240\n");
00343 break;
00344 case VIDEO_PALETTE_RGB565:
00345 printf("VIDEO_PALETTE_RGB565\n");
00346 break;
00347 case VIDEO_PALETTE_RGB24:
00348 printf("VIDEO_PALETTE_RGB24\n");
00349 break;
00350 case VIDEO_PALETTE_RGB32:
00351 printf("VIDEO_PALETTE_RGB32\n");
00352 break;
00353 case VIDEO_PALETTE_RGB555:
00354 printf("VIDEO_PALETTE_RGB555\n");
00355 break;
00356 case VIDEO_PALETTE_YUV422:
00357 printf("VIDEO_PALETTE_YUV422\n");
00358 break;
00359 case VIDEO_PALETTE_YUYV:
00360 printf("VIDEO_PALETTE_YUYV\n");
00361 break;
00362 case VIDEO_PALETTE_UYVY:
00363 printf("VIDEO_PALETTE_UYVY\n");
00364 break;
00365 case VIDEO_PALETTE_YUV420:
00366 printf("VIDEO_PALETTE_YUV420\n");
00367 break;
00368 case VIDEO_PALETTE_YUV411:
00369 printf("VIDEO_PALETTE_YUV411\n");
00370 break;
00371 case VIDEO_PALETTE_RAW:
00372 printf("VIDEO_PALETTE_RAW\n");
00373 break;
00374 case VIDEO_PALETTE_YUV422P:
00375 printf("VIDEO_PALETTE_YUV422P\n");
00376 break;
00377 case VIDEO_PALETTE_YUV411P:
00378 printf("VIDEO_PALETTE_YUV411P\n");
00379 break;
00380 case VIDEO_PALETTE_YUV420P:
00381 printf("VIDEO_PALETTE_YUV420P\n");
00382 break;
00383 case VIDEO_PALETTE_YUV410P:
00384 printf("VIDEO_PALETTE_YUV410P\n");
00385 break;
00386 default:
00387 printf(" Couldn't read palette\n");
00388 break;
00389 }
00390 return true;
00391 }
00392
00393 bool setV4L_palette(int palette, int depth) {
00394 imageProperties.palette = palette;
00395 imageProperties.depth = depth;
00396
00397 if (xioctl(deviceHandle, VIDIOCSPICT, &imageProperties) < 0) {
00398 printf("V4L : Failed to set image properties : %d, %s\n", errno, strerror (errno));
00399 return false;
00400 }
00401 if ((imageProperties.palette == palette) && (imageProperties.depth == depth)) {
00402 return true;
00403 }
00404 return false;
00405 }
00406
00407 bool setV4L_imageProperties(void) {
00408
00409 if (setV4L_palette(VIDEO_PALETTE_RGB24, 24)) {
00410 printf("V4L : Changed current palette to VIDEO_PALETTE_RGB24\n");
00411 }
00412 else
00413 if (setV4L_palette(VIDEO_PALETTE_YUV420, 16)) {
00414 printf("V4L : Changed current palette to VIDEO_PALETTE_YUV420\n");
00415 }
00416 else
00417 if (setV4L_palette(VIDEO_PALETTE_YUV420P, 16)) {
00418 printf("V4L : Changed current palette to VIDEO_PALETTE_YUV420P\n");
00419 }
00420 else
00421 if (setV4L_palette(VIDEO_PALETTE_YUV411P, 16)) {
00422 printf("V4L : Changed current palette to VIDEO_PALETTE_YUV411P\n");
00423 }
00424 else {
00425 printf("V4L : ERROR : Unable to change to a suitable palette\n");
00426 return false;
00427 }
00428 return true;
00429 }
00430
00431 void initialiseV4L_device(const char *device_name) {
00432
00433 if (xioctl(deviceHandle, VIDIOCGCAP, &capability) == -1) {
00434 if (errno == EINVAL) {
00435 printf("V4L : %s is no V4L device\n",device_name);
00436 exit (EXIT_FAILURE);
00437 } else {
00438 errno_exit ("VIDIOC_QUERYCAP");
00439 }
00440 }
00441
00442 if (!(capability.type & VID_TYPE_CAPTURE)) {
00443 printf("V4L : %s is no video capture device\n", device_name);
00444 exit (EXIT_FAILURE);
00445 } else {
00446 printf("V4L : Name = '%s'\n",capability.name);
00447 printf("V4L : Dimensions (%i x %i) - (%i x %i)\n", capability.minwidth, capability.minheight, capability.maxwidth,capability.maxheight);
00448 printf("V4L : Capability :\n");
00449 if (capability.type & VID_TYPE_CAPTURE ) printf(" - CAPTURE\n");
00450 if (capability.type & VID_TYPE_TUNER ) printf(" - TUNER\n");
00451 if (capability.type & VID_TYPE_TELETEXT ) printf(" - TELETEXT\n");
00452 if (capability.type & VID_TYPE_OVERLAY ) printf(" - OVERLAY\n");
00453 if (capability.type & VID_TYPE_CHROMAKEY ) printf(" - CHROMAKEY\n");
00454 if (capability.type & VID_TYPE_CLIPPING ) printf(" - CLIPPING\n");
00455 if (capability.type & VID_TYPE_FRAMERAM ) printf(" - FRAMERAM\n");
00456 if (capability.type & VID_TYPE_SCALES ) printf(" - SCALES\n");
00457 if (capability.type & VID_TYPE_MONOCHROME ) printf(" - MONOCHROME\n");
00458 if (capability.type & VID_TYPE_SUBCAPTURE ) printf(" - SUBCAPTURE\n");
00459 }
00460
00461 queryV4L_imageProperties();
00462 setV4L_imageProperties();
00463 }
00464
00465 void writePPM(unsigned char *data, char *filename, int width, int height) {
00466 int num;
00467 int size = width * height * 3;
00468 FILE *fp = fopen(filename, "w");
00469
00470 if (!fp) {printf("V4L : cannot open file for writing!\n");}
00471
00472 fprintf(fp, "P6\n%d %d\n%d\n", width, height, 255);
00473 num = fwrite((void *) data, 1, (size_t) size, fp);
00474
00475 if (num != size) {printf("V4L : cannot write image data to file\n");}
00476
00477 fclose(fp);
00478 }
00479
00480 void swap_rgb24(unsigned char *mem, int n)
00481 {
00482 unsigned char c;
00483 unsigned char *p = mem;
00484 int i = n;
00485 while(--i) {
00486 c = p[0];
00487 p[0] = p[2];
00488 p[2] = c;
00489 p += 3;
00490 }
00491 }
00492
00493 bool getFrameV4L(unsigned char * pixels) {
00494
00495
00496 if (pixels == NULL) {
00497 printf("V4L : ERROR : pixel data not not allocated.\n");
00498 return false;
00499 }
00500
00501 if (bFirstFrame) {
00502 bFirstFrame = false;
00503
00504 for (frameIndex = 0;frameIndex < (mbuf.frames);++frameIndex) {
00505 vmmap.frame = frameIndex;
00506 vmmap.width = window.width;
00507 vmmap.height = window.height;
00508 vmmap.format = imageProperties.palette;
00509
00510 if (xioctl(deviceHandle, VIDIOCMCAPTURE, &vmmap) == -1) {
00511 printf("V4L : ERROR: Could not make initial capture\n");
00512 return false;
00513 }
00514 }
00515
00516 frameIndex = 0;
00517 }
00518
00519
00520 if (xioctl(deviceHandle, VIDIOCSYNC, &frameIndex) == -1) {
00521 printf("V4L : ERROR: VIDIOCSYNC failed. %s\n", strerror(errno));
00522 }
00523
00524
00525 switch(imageProperties.palette) {
00526 case VIDEO_PALETTE_RGB24:
00527 {
00528 int imagesize = window.width*window.height*(imageProperties.depth/8);
00529 memcpy((unsigned char*)pixels, (unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),imagesize);
00530 swap_rgb24(pixels, window.width * window.height);
00531 }
00532 break;
00533 case VIDEO_PALETTE_YUV420P:
00534 {
00535
00536 yuv420p_to_rgb24(window.width,window.height,(unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),(unsigned char*) pixels);
00537 swap_rgb24(pixels, window.width * window.height);
00538 }
00539 break;
00540 case VIDEO_PALETTE_YUV420:
00541 {
00542
00543 yuv420p_to_rgb24(window.width,window.height,(unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),(unsigned char*) pixels);
00544 swap_rgb24(pixels, window.width * window.height);
00545 }
00546 break;
00547 case VIDEO_PALETTE_YUV411P:
00548 {
00549
00550 yuv411p_to_rgb24(window.width,window.height,(unsigned char*)(bigbuf + mbuf.offsets[frameIndex]),(unsigned char*) pixels);
00551 swap_rgb24(pixels, window.width * window.height);
00552 }
00553 break;
00554 default:
00555 printf("V4L : ERROR: Cannot convert from palette %d to RGB\n",imageProperties.palette);
00556 return false;
00557 }
00558
00559 vmmap.frame = frameIndex;
00560 vmmap.width = window.width;
00561 vmmap.height = window.height;
00562 vmmap.format = imageProperties.palette;
00563
00564
00565 if (xioctl (deviceHandle, VIDIOCMCAPTURE, &vmmap) == -1) {
00566 printf("V4L : ERROR: Could not capture...\n");
00567 return false;
00568 }
00569
00570 ++frameIndex;
00571 if (frameIndex == mbuf.frames) {
00572 frameIndex = 0;
00573 }
00574
00575 return true;
00576 }
00577
00578 bool setV4L_videoSize(int w, int h) {
00579
00580 if (!(capability.type & VID_TYPE_CAPTURE)) {
00581 printf("V4L : Not a capture device! Exiting...\n");
00582 return false;
00583 }
00584
00585 if (xioctl(deviceHandle, VIDIOCGWIN, &window) < 0) {
00586 printf("V4L : Could not get video size : %d, %s\n", errno, strerror (errno));
00587 return false;
00588 }
00589
00590 if (w > capability.maxwidth) w = capability.maxwidth;
00591 if (h > capability.maxheight) h = capability.maxheight;
00592
00593 window.width = w;
00594 window.height= h;
00595 window.x = window.y = window.chromakey = window.flags = 0;
00596
00597 if (xioctl(deviceHandle, VIDIOCSWIN, &window) < 0) {
00598 printf("V4L : ERROR : Could not set video size : %d, %s\n", errno, strerror (errno));
00599 return false;
00600 }
00601
00602 if (xioctl(deviceHandle, VIDIOCGWIN, &window) < 0) {
00603 printf("V4L : Could not get video size : %d, %s\n", errno, strerror (errno));
00604 return false;
00605 }
00606 printf("V4L : Video size changed to width=%i height=%i\n",window.width,window.height);
00607
00608 return true;
00609 }
00610
00611 bool mmapV4L(void) {
00612
00613 if (xioctl(deviceHandle, VIDIOCGMBUF, &mbuf) < 0) {
00614 printf("V4L : Could not use mmap : %d, %s\n", errno, strerror (errno));
00615 }
00616
00617
00618
00619 bigbuf = (unsigned char*) mmap(0, mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, deviceHandle, 0);
00620
00621 if (bigbuf == MAP_FAILED) {
00622 printf("V4L : Could not use mmap buffer.\n");
00623 closeV4L();
00624 }
00625
00626 return true;
00627 }
00628
00629 void closeV4L(void) {
00630 printf("V4L : Shutting down....\n");
00631 close(deviceHandle);
00632 munmap(bigbuf, mbuf.size);
00633 }
00634
00635 bool initV4L(int width, int height, const char *devname) {
00636 bFirstFrame = true;
00637 bigbuf = NULL;
00638
00639 openV4L_device(devname);
00640 initialiseV4L_device(devname);
00641 if(!setV4L_videoSize(width,height))
00642 exit(1);
00643
00644 mmapV4L();
00645
00646 return true;
00647 }
00648
00649 int getV4L_Height(void) {
00650 if(window.height)
00651 return window.height;
00652 }
00653
00654 int getV4L_Width(void) {
00655 if(window.width)
00656 return window.width;
00657 }
00658
00659 #endif
00660