/* * Philips webcam Sequential Image Capture Program for NetBSD, version 0.0 * phpsview - Sequential image capture program. * Copyright (C) 2003 Takafumi Mizuno * * Portions of this program were modeled after or adapted from the * Philips WebCam driver for Linux (C) Luc Saillard: * http://www.saillard.org/linux/pwc/ * * 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. * * Reference: Image Processing Program by Linux, author Iio Jun, ohmsha publishing * ISBN:4-274-94623-1 ( Japanese Only! ) */ #include #include #include #include #include #include #include #include #include #include #include "gui.h" #include "linux_usbif.h" #include "pwc.h" #include "pwc-ioctl.h" #include "phpsview.h" #define IFNUM 0 /* #define FIND_HEADER (this is not working) */ struct usb_device usbdev; struct pwc_device *pwcdev = NULL; char dev[FILENAME_MAX]; char dumpfile[FILENAME_MAX]; int dump_interval = 1000; #define LOCAL_FPS 10 #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 */ 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 */ GdkImlibImage *get_new_frame(GdkImlibImage *); static void yuv420p2rgb( unsigned char *yuvbuf, unsigned char *rgbbuf ); void dump_frame(); static void print_usage(char *argv); int errnum = 0; /* shared gui.c */ int LOCAL_X = 640, LOCAL_Y = 480; int LOCAL_SIZE_RGB; int LOCAL_SIZE_YUV; int no_display = 0; int main(int argc, char *argv[]) { int i = 0; //int ioctl_arg; gtk_init(&argc, &argv); gdk_imlib_init(); /* args parsing */ for (i=1; i '9') strncpy(dumpfile, argv[i], FILENAME_MAX-1); else { LOCAL_X = atoi(argv[i]); if (LOCAL_X == 0 || LOCAL_X > 640 || strlen(argv[i]) < 7 || argv[i][3] != 'x') print_usage(*argv); LOCAL_Y = atoi(argv[i]+4); if (LOCAL_Y == 0 || LOCAL_Y > 480) print_usage(*argv); } } else { if (!strncmp(argv[i], "/dev/", 5)) strncpy(dev, argv[i], FILENAME_MAX-1); else strncpy(dumpfile, argv[i], FILENAME_MAX-1); } } if (no_display && dumpfile[0] == '\0') print_usage(*argv); /* module initialisation */ usb_pwc_init(); /* opening camera device */ for ( i = -1; i < 15; ++i ) { if (i == -1 && dev[0] == '\0') continue; if (i >= 0){ #ifdef __FreeBSD__ sprintf(dev, "/dev/ugen%d", i); #elif defined __NetBSD__ sprintf(dev, "/dev/ugen%d.00", i); #else #error what device ? #endif } if ( (usbdev.fd = open(dev, O_RDWR)) < 0 ) { if (i == -1) break; continue; } if ( (pwcdev = (struct pwc_device *)usb_pwc_probe(&usbdev, IFNUM)) == NULL ) { close(usbdev.fd); usbdev.fd = -1; if (i == -1) break; continue; } else { break; } } 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); } 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); #elif defined __NetBSD__ sprintf(usbdev.epdevname, "%s.%02d", strtok(dev,"."), pwcdev->vendpoint); #else #error what device ? #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); } printf("Using resolution %dx%d\n", LOCAL_X, LOCAL_Y); if (dumpfile[0] != '\0') printf("frames will be dumped to %s every %d ms.\n", dumpfile, dump_interval); #if 0 ioctl_arg = 1; if (ioctl(usbdev.efd, USB_SET_SHORT_XFER, &ioctl_arg)) perror("ioctl"); ioctl_arg = 120; if (ioctl(usbdev.efd, USB_SET_TIMEOUT, &ioctl_arg)) perror("ioctl"); #endif LOCAL_SIZE_RGB = (LOCAL_X * LOCAL_Y * 3); LOCAL_SIZE_YUV = (LOCAL_SIZE_RGB / 2); 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); } /* image clear */ memset((void *)grabbuf, 0, RAW_PKTSIZE); if (no_display){ while (1) get_new_frame(NULL); } else { gui_config(); gtk_main(); } 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; } /* Read raw-data from camera and convert to RGB24 */ GdkImlibImage *get_new_frame(GdkImlibImage *current) { int totalsize, overchk; int hdflag; unsigned char *isodat, *searchp; struct usb_device *udev; ssize_t readlen; isodat = storebuf; totalsize = 0; overchk = 0; readlen = 0; udev = &usbdev; if ( errnum > ERRNUM_LIMITS ) { fprintf(stderr,"Can't found image header. Program aborted.\n"); errnum = (ERRNUM_LIMITS+1); return NULL; } while ( totalsize < RAW_PKTSIZE*2 ) { readlen = read(udev->efd, (void *)isodat, MY_ENDP_SIZE); if ( readlen < 0 ) { perror("read"); break; } isodat += readlen; totalsize += readlen; if ( ++overchk > (STORE_BUFSIZE/MY_ENDP_SIZE+1) ) break; } if ( totalsize > STORE_BUFSIZE ) { fprintf(stderr, "overrun error\n"); return NULL; } /* search header */ /* * header bit stream that I found ... * see phpsshot.c */ searchp = storebuf; hdflag = 0; while ( searchp < isodat ) { #ifdef FIND_HEADER int i; if (*searchp == 0x20){ printf("header:"); for (i=0; i<8; i++) printf(" %02x", *(searchp+i)); fputc('\n', stdout); } #endif if ( /* uncompressed */ (*searchp == 0x00 && (*(searchp+1) == 0x01) && (*(searchp+6) == 0x04) && ((*(searchp+2) == *(searchp+3)) || (*(searchp+2) == 0x00))) || /* compressed */ /* 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 ) { errnum = 0; memcpy((void *)grabbuf, (void *)searchp, MIN(LOCAL_SIZE_YUV, totalsize-(searchp-storebuf))); pwc_decompress(pwcdev); yuv420p2rgb(yuv, rgb); if (dumpfile[0] != '\0') dump_frame(); } else { errnum++; fprintf(stderr, "header not found !\n"); return NULL; } if (current) gdk_imlib_kill_image(current); if (no_display) return NULL; return gdk_imlib_create_image_from_data(rgb, NULL, LOCAL_X, LOCAL_Y); } 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; 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; } void dump_frame(){ int dump_fd; static struct timeval last = {0, 0}; struct timeval now; gettimeofday(&now, NULL); if ((now.tv_sec - last.tv_sec)*1000 + (now.tv_usec - last.tv_usec)/1000 < dump_interval) return; if ((dump_fd = open(dumpfile, O_WRONLY | O_CREAT | O_TRUNC, 0660)) == -1){ fprintf(stderr, "could not open dump file %s for write, cancelled.\n", dumpfile); dumpfile[0] = '\0'; } else { char header[16]; int len = sprintf(header, "P6 %d %d 255\n", LOCAL_X, LOCAL_Y); write(dump_fd, header, len); write(dump_fd, rgb, LOCAL_SIZE_RGB); close(dump_fd); } gettimeofday(&last, NULL); } void print_usage(char *argv){ fprintf(stderr, "\nusage: %s [-n] [device-name] [160x120 | 320x240 | 640x480] [filename] [-ddump_interval]\n", argv); fprintf(stderr, "\t[-n] no display. Don't open window, dumping must be active to use this.\n"); fprintf(stderr, "\t[device-name] the device used to grab pictures. Must begin with \"/dev/\" .\n"); fprintf(stderr, "\t[320x240] capture resolution, can be max 640x480 and min 160x120.\n"); fprintf(stderr, "\t[filename] capture file, can be any filename except /dev ones.\n"); fprintf(stderr, "\t[-ddump_interval] dump interval in ms, used if filename is specified.\n\n"); exit(1); }