/* * Philips webcam Image Capture Program for NetBSD/FreeBSD, version 0.0 * phpsshot - Simple image capture program. * Copyright (C) 2003 Takafumi Mizuno * * Portions of this program were modeled after or adapted from the * Philips webcam dirver for Linux by USB and Video4Linux interface part * (C) 1999-2003 Nemosoft Unv.; see * http://www.smcc.demon.nl/webcam/ * * This program is free software; you can redistribute it and/or modify * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include "linux_usbif.h" #include "pwc.h" #include "pwc-ioctl.h" #include "phpsshot.h" #define IFNUM 0 struct usb_device usbdev; struct pwc_device *pwcdev; char dev[FILENAME_MAX]; #define LOCAL_FPS 10 #define LOCAL_X 640 #define LOCAL_Y 480 #define LOCAL_SIZE_RGB (LOCAL_X * LOCAL_Y * 3) #define LOCAL_SIZE_YUV (LOCAL_SIZE_RGB / 2) #define MY_ENDP_SIZE 592 /* XXX 592... My camera's endpoint max packet size */ #define RAW_PKTSIZE (LOCAL_SIZE_YUV+TOUCAM_HEADER_SIZE+TOUCAM_TRAILER_SIZE) #define STORE_BUFSIZE (RAW_PKTSIZE*2+(MY_ENDP_SIZE*2)) /* (MY_ENDP_SIZE*2) is margin */ #define MIN(a,b) ((a)<(b) ? (a) : (b)) unsigned char *storebuf; /* raw isoc packets data */ unsigned char *grabbuf; /* native image format data from above */ unsigned char *yuv; /* YUV420P image data from above */ unsigned char *rgb; /* RGB image data from above */ static int pwc_grab(struct usb_device *udev); static void outputppm(unsigned char *); static void yuv420p2rgb( unsigned char *yuvbuf, unsigned char *rgbbuf ); /*#define DUMPONLY*/ int main(int argc, char *argv[]) { int i = 0; int ret, exflag; if (((storebuf = malloc(STORE_BUFSIZE)) == NULL) || ((grabbuf = malloc(RAW_PKTSIZE)) == NULL) || ((yuv = malloc(LOCAL_SIZE_YUV)) == NULL) || ((rgb = malloc(LOCAL_SIZE_RGB)) == NULL)){ perror("malloc"); exit(-1); } /* module initial */ usb_pwc_init(); /* * check option */ if (argc == 1) { /* search camera */ for ( i = 0; i < 15; ++i ) { #ifdef __FreeBSD__ sprintf(dev, "/dev/ugen%d", i); #else sprintf(dev, "/dev/ugen%d.00", i); #endif if ( (usbdev.fd = open(dev, O_RDWR)) < 0 ) { continue; } if ( (pwcdev = (struct pwc_device *)usb_pwc_probe(&usbdev, IFNUM)) == NULL ) { close(usbdev.fd); usbdev.fd = -1; continue; } else { break; } } } else { if ( argv[1][0] != '/' ) { fprintf(stderr, "usage: %s [device-name]\n", argv[0]); fprintf(stderr, " [device-name] must be started slash \'/\' character.\n"); exit(1); } strncpy(dev, argv[1], FILENAME_MAX-1); if ( (usbdev.fd = open(dev, O_RDWR)) < 0 ) { err(1, "%s", dev); } else { if ( (pwcdev = (struct pwc_device *)usb_pwc_probe(&usbdev, IFNUM)) == NULL ) { close(usbdev.fd); usbdev.fd = -1; } } } if ( usbdev.fd < 0 ) { fprintf(stderr, "Not found Philips Webcam, or Permission denied\n"); exit(1); } if ( pwc_video_open(pwcdev) < 0 ) { usb_pwc_disconnect(&usbdev, (void *)pwcdev); close(usbdev.fd); exit(1); } /* best quality is SIF(320x240) 5fps with uncompress mode. */ if ( pwc_try_video_mode(pwcdev, LOCAL_X, LOCAL_Y, LOCAL_FPS, 0, 0) != 0) { usb_pwc_disconnect(&usbdev, (void *)pwcdev); close(usbdev.fd); exit(1); } /* open endpoint */ #ifdef __FreeBSD__ sprintf(usbdev.epdevname, "%s.%d", dev, pwcdev->vendpoint); #else sprintf(usbdev.epdevname, "%s.%02d", strtok(dev,"."), pwcdev->vendpoint); #endif if ((usbdev.efd = open(usbdev.epdevname, O_RDONLY)) < 0) { perror(usbdev.epdevname); pwc_video_close(pwcdev); usb_pwc_disconnect(&usbdev, (void *)pwcdev); close(usbdev.fd); exit(1); } /* image clear */ memset((void *)grabbuf, 0, RAW_PKTSIZE); #define EXPOSURE_TIME 10 exflag = 0; for ( i = 0; i < EXPOSURE_TIME; i++ ) { ret = pwc_grab(&usbdev); if ( ret < 0 ) { break; } else if ( ret == 0 ) { if ( i > 7 ) exflag = 1; } /* when not found header, do nothing */ if ((i%2) == 0) fprintf(stderr, " Say cheese! after %d\r", 5-(i/2)); } if (exflag == 1) { outputppm(rgb); } else { fprintf(stderr, "Error: Not found Image header.\n"); } fprintf(stderr, "Closing %s.....",pwcdev->vdev_name); close(usbdev.efd); pwc_video_close(pwcdev); /* */ usb_pwc_disconnect(&usbdev, (void *)pwcdev); close(usbdev.fd); fprintf(stderr, "Done.\n"); free(storebuf); free(grabbuf); free(yuv); free(rgb); return 0; } static int pwc_grab(struct usb_device *udev) { int totalsize, overchk; int hdflag; unsigned char *isodat, *searchp; ssize_t readlen; #ifdef DUMPONLY int i; #endif isodat = storebuf; totalsize = 0; overchk = 0; readlen = 0; while ( totalsize < RAW_PKTSIZE*2 ) { readlen = read(udev->efd, (void *)isodat, MY_ENDP_SIZE); if ( readlen < 0 ) { break; } isodat += readlen; totalsize += readlen; if ( ++overchk > (STORE_BUFSIZE/MY_ENDP_SIZE+1) ) break; } if ( totalsize > STORE_BUFSIZE ) { fprintf(stderr, "overrun error\n"); return -1; } /* search header */ /* * header bit stream that I found ... (uncompressed mode) * 00 01 00 00 03 cb 04 bd * 00 01 00 14 03 bc 04 ab * 00 01 00 09 03 cc 04 7f * 00 01 00 0b 04 60 04 ac * 00 01 00 00 04 04 04 e2 * 00 01 00 00 04 04 04 e3 * 00 01 ff ff 04 4d 04 d3 * 00 01 ff ff 04 4d 04 c7 * 00 01 Xa Xb ** ** 04 ** <= check bytes (Xa == Xb) or (Xa == 00) */ searchp = storebuf; hdflag = 0; while ( searchp < isodat ) { if ( /* uncompressed 320x240x5*/ (*searchp == 0x00 && (*(searchp+1) == 0x01) && (*(searchp+6) == 0x04) && ((*(searchp+2) == *(searchp+3)) || (*(searchp+2) == 0x00))) || /* compressed 640x480x10 and few others, depending on camera */ /* 20 0x 00 xx 04 xx xx xx */ (*searchp == 0x20 && (*(searchp+1) <= 0x02) && (*(searchp+2) == 0x00) && (*(searchp+4) == 0x04)) ){ /* found header */ searchp += TOUCAM_HEADER_SIZE; hdflag = 1; break; } searchp++; } if ( hdflag == 1 ) { memcpy((void *)grabbuf, (void *)searchp, MIN(LOCAL_SIZE_YUV, totalsize - (searchp - storebuf))); pwc_decompress(pwcdev); yuv420p2rgb(yuv, rgb); } else { #ifdef DUMPONLY isodat = storebuf; for ( i = 0; i < totalsize; i++ ) { printf("%02x ", *isodat++); if (((i+1) % 16) == 0 ) printf("\n"); } return 0; #else fprintf(stderr, "header not found !\n"); return 1; #endif } return 0; } static void outputppm(unsigned char *target) { int i; if ( target == NULL ) return; #ifdef DUMPONLY return; #endif printf("P6\n"); printf("%d %d 255 ", LOCAL_X, LOCAL_Y); for ( i = 0; i < (LOCAL_SIZE_RGB); i+=3 ) { printf("%c%c%c", target[i], target[i+1], target[i+2]); } return; } static void yuv420p2rgb( unsigned char *yuvbuf, unsigned char *rgbbuf ) { int i, j; int halfoff; unsigned char *yp, *up, *vp; float y, u, v; float r, g, b; #ifdef DUMPONLY return; #endif yp = yuvbuf; up = yuvbuf + LOCAL_X * LOCAL_Y; vp = up + (LOCAL_X/2) * (LOCAL_Y/2); for ( i = 0; i < LOCAL_Y; i++ ) { for ( j = 0; j < LOCAL_X; j++ ) { y = (float)*(yp + ( i * LOCAL_X + j )); halfoff = (((i%2==1)?i-1:i)/2)*(LOCAL_X/2) + ((j%2==1)?j-1:j)/2; u = (float)*(up + halfoff); v = (float)*(vp + halfoff); /* convert to RGB from YUV */ u = u - 128.0; v = v - 128.0; r = 1.4*u + y; g = 1.02 * y - 0.750 * u - 0.336 * v; b = 1.77*v + y; if ( r > 255 ) r = 255; if ( r < 0 ) r = 0; if ( g > 255 ) g = 255; if ( g < 0 ) g = 0; if ( b > 255 ) b = 255; if ( b < 0 ) b = 0; /* print data */ *rgbbuf++ = (unsigned char)b; *rgbbuf++ = (unsigned char)g; *rgbbuf++ = (unsigned char)r; } } return; }