/* * USB Quickcam Video Camera Image Capture Program for NetBSD/FreeBSD, version 0.04 * qcamshot - Simple image capture program. * Copyright (C) 2003-2004 Takafumi Mizuno * * Portions of this program were modeled after or adapted from the * USB Quickcam Express dirver for Linux by many people; see * http://qce-ga.sourceforge.net/ for more information. * * 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 "quickcam.h" /* Control register of the STV0600 ASIC */ #include "helper.h" #include "pb0100.h" #include "hdcs.h" #include "vv6410.h" #include "qcamshot.h" struct usb_device usbdev; struct usb_quickcam *quickcam; char dev[FILENAME_MAX]; #define REQ_FRAMES 10 #define RAW_DAT_BUFSIZE (REQ_FRAMES*FRAMES_PER_DESC*FRAME_SIZE_PER_DESC) #define SEARCH_FRAMES 3 /* why 3? */ #define STORE_BUFSIZE (RAW_DAT_BUFSIZE*SEARCH_FRAMES) static unsigned char storebuf[STORE_BUFSIZE]; static unsigned char grabbuf[RAW_DAT_BUFSIZE]; static unsigned char rgb[BAYER_FRAME_SIZE*4]; /* 4= RGB and work area */ /* #define DEBUG_GRAB */ static int quickcam_grab(void); static void outputppm(unsigned char *, int, int); int main(int argc, char *argv[]) { int i = 0; /* * check option */ if (argc == 1) { /* search Quickcam */ 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 ( (quickcam = (struct usb_quickcam *)quickcam_probe(&usbdev)) == 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 ( (quickcam = (struct usb_quickcam *)quickcam_probe(&usbdev)) == NULL ) { close(usbdev.fd); usbdev.fd = -1; } } } if ( usbdev.fd < 0 ) { fprintf(stderr, "Not found Quickcam, or Permission denied\n"); exit(1); } quickcam_open(quickcam); /* open endpoint (endpoint 1) */ #ifdef __FreeBSD__ sprintf(usbdev.epdevname, "%s.1", dev); #else sprintf(usbdev.epdevname, "%s.01", strtok(dev,".")); #endif if ((usbdev.efd = open(usbdev.epdevname, O_RDONLY)) < 0) { perror(usbdev.epdevname); quickcam_close(quickcam); close(usbdev.fd); exit(1); } memset(storebuf, 0, STORE_BUFSIZE); memset(grabbuf, 0, RAW_DAT_BUFSIZE); memset(rgb, 0, BAYER_FRAME_SIZE*4); if ( usb_quickcam_start(quickcam) < 0) { fprintf(stderr, "can\'t quickcam start\n"); } else { #define EXPOSURE_TIME 20 for ( i = 0; i < EXPOSURE_TIME; i++ ) { if ( quickcam_grab() != 0 ) { break; } if ( (i%2) == 0) fprintf(stderr, " Say cheese! after %2d\r", (EXPOSURE_TIME-i)/2 ); } if ( i == EXPOSURE_TIME ) outputppm(rgb, QUICKCAM_WIDTH, QUICKCAM_HEIGHT); } fprintf(stderr, "Closing Qcam Express....."); close(usbdev.efd); quickcam_close(quickcam); /* do free(quickcam) */ close(usbdev.fd); fprintf(stderr, "Done.\n"); return 0; } /* Read raw-data from Quickcam and convert to RGB24 * reference qce-linux-driver * quickcam_parse_store() quickcam_parse_data() */ static int quickcam_grab(void) { int i, cur_len; unsigned char *isodat, *rawdat; ssize_t readlen; int totalsize; /* total iso-packet data */ int tot_len; /* total grabbing data */ unsigned char type1, type2; int frag_len; enum { SEARCH_HEAD, GRABBING_DAT, FOUND_TAIL} state; int framesize; long ii; unsigned long mid_value=0; unsigned long mid_value_line=0; int xx=0; int yy=0; int xd=0; int copylen=0; unsigned char *o; unsigned char *data; int skiph; int startw, endw; /* Use sensor information to get the framesize */ if (quickcam->sensor_ctrl.mode==1) framesize = qcmin(BAYER_FRAME_SIZE, (quickcam->sensor_ctrl.width* quickcam->sensor_ctrl.height)*2); else framesize = qcmin(BAYER_FRAME_SIZE, quickcam->sensor_ctrl.width* quickcam->sensor_ctrl.height); /* get iso packet */ totalsize = 0; isodat = storebuf; totalsize = 0; for ( i = 0; i < (REQ_FRAMES*FRAMES_PER_DESC*SEARCH_FRAMES); i++ ) { readlen = read(usbdev.efd, (void *)isodat, FRAME_SIZE_PER_DESC); isodat += readlen; totalsize += readlen; } /* fail safe */ if ( totalsize > STORE_BUFSIZE) { fprintf(stderr,"buffer overflow\n"); return -1; } /* grab raw RGB data from isopacket */ tot_len = 0; #ifdef DEBUG_GRAB fprintf(stderr, "total size = %d\n", totalsize); #endif isodat = storebuf; rawdat = grabbuf; state = SEARCH_HEAD; /* enum { SEARCH_HEAD, GRABBING_DAT, FOUND_TAIL} */ for ( cur_len = 0; cur_len < totalsize; cur_len++ ) { type1 = isodat[cur_len]; type2 = isodat[cur_len+1]; frag_len=isodat[cur_len+2]<<8 | isodat[cur_len+3]; if(type1==0x80 || type1==0xC0) { if(type2==1 || type2==5) { /* a frame begins - see if we can capture it. */ if ( state == SEARCH_HEAD ) { state = GRABBING_DAT; #ifdef DEBUG_GRAB fprintf(stderr, "found head\n"); #endif cur_len += 3; continue; } else if ( state == GRABBING_DAT ) { /* restart */ #ifdef DEBUG_GRAB fprintf(stderr, "found header again...restart\n"); #endif state = GRABBING_DAT; tot_len = 0; rawdat = grabbuf; cur_len += 3; continue; } else { /* cant find FOUND_TAIL, grabbing abort */ #ifdef DEBUG_GRAB fprintf(stderr,"not found head, grabbing abort\n"); #endif state = FOUND_TAIL; break; } } else if(type2==2 || type2==6) { /* a frame end */ if ( state == GRABBING_DAT ) { state = FOUND_TAIL; #ifdef DEBUG_GRAB fprintf(stderr, "found tail\n"); #endif break; } else { /* ignore */ cur_len += 3; continue; } } } else if(type1==0x02 || type1==0x42) { /* data chunk */ if ( state != GRABBING_DAT ) { cur_len += 3; continue; } if ( tot_len + frag_len > BAYER_FRAME_SIZE) { #ifdef DEBUG_GRAB fprintf(stderr, "buffer overflow, grabbing abort\n"); #endif state = FOUND_TAIL; break; } memcpy((void *)rawdat, (void *)&isodat[cur_len+4], frag_len); rawdat += frag_len; tot_len += frag_len; cur_len += (frag_len + 3); } /* other ignore */ } if ( state != FOUND_TAIL ) { fprintf(stderr, "grabbing error: grabbing size = %d\n", tot_len); return -1; } /* grabbing sucess ?: total size %d\n", tot_len); */ if ( tot_len != framesize ) { #ifdef DEBUG_GRAB fprintf(stderr,"warring some packet lost\n"); #endif tot_len = framesize; } #ifdef DEBUG_GRAB else { fprintf(stderr, "grabbing sucess total size %d\n", tot_len); } #endif #define addblue 0 #define addgreen 1 #define addred 2 /* need cleaning ... */ #define XH QUICKCAM_HEIGHT #define XW QUICKCAM_WIDTH #define SW quickcam->sensor_ctrl.width /* calculate data to skip for the VV6410 sensor */ if (mode) { skiph = (quickcam->sensor_ctrl.height-(XH/2))/2; startw = (quickcam->sensor_ctrl.width-(XW/2))/2; } else { skiph = (quickcam->sensor_ctrl.height-XH)/2; startw = (quickcam->sensor_ctrl.width-XW)/2; } if (skiph<0) skiph=0; if (startw<0) startw=0; endw = quickcam->sensor_ctrl.width-startw; /* difference from original linux driver as follow; */ /* frame->storedata ==> grabbuf */ /* frame->storelength ==> tot_len */ /* frame->data ==> rgb */ /* frame->scanlength ==> N/A */ /* skip fisrt and last lines if needed */ data = grabbuf+(skiph*quickcam->sensor_ctrl.width); for (ii=skiph*quickcam->sensor_ctrl.width; iisensor_ctrl.width); ii++) { /* * Skip line data when needed. */ if ((ii%quickcam->sensor_ctrl.widthsensor_ctrl.width>=endw && startw)) { data++; continue; } /* * For HDCS Sensors: * Camera is G1R1 G2R2 G3R3... * B4G4 B5G5 B6G6... * Video is B4G1R1 B4G1R1 * B4G4R1 B4G4R1 * For Photobit... */ if (yy>XH) yy=XH; if (mode) { o=((unsigned char*)rgb)+3*xd+(XW*3*yy); xd++; xd++; } else { o=((unsigned char*)rgb)+3*xx+(XW*3*yy); } mid_value_line+=*data; if (interpolation == 1) { /* bilinear interpolation */ if (!(yy&1)) { // even row if (xx&1) { // odd column, red *(o+addred)=*data; if (yy == 0) { if (xx == XW-1) { *(o+addblue)=*(data+SW-1); *(o+addgreen)=(*(data-1) + *(data+SW))/2; } else { *(o+addblue)=(*(data+SW-1) + *(data+SW+1))/2; *(o+addgreen)=(*(data-1) + *(data+1) + *(data+SW))/3; } } else { if (xx == XW-1) { *(o+addblue)=(*(data-SW-1) + *(data+SW-1))/2; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+SW))/3; } else { *(o+addblue)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4; } } if (mode) { } } else { // green if (yy == 0) { if (xx == 0) { *(o+addred)=*(data+1); *(o+addblue)=*(data+SW); } else { *(o+addred)=(*(data-1) + *(data+1))/2; *(o+addblue)=*(data+SW); } } else { if (xx == 0) { *(o+addred)=*(data+1); *(o+addblue)=(*(data-SW) + *(data+SW))/2; } else { *(o+addred)=(*(data-1) + *(data+1))/2; *(o+addblue)=(*(data-SW) + *(data+SW))/2; } } *(o+addgreen)=*data; if (mode) { } } } else { if (xx&1) { // odd column green if (yy == XH-1) { if (xx == XW-1) { *(o+addred)=*(data-SW); *(o+addblue)=*(data-1); } else { *(o+addred)=*(data-SW); *(o+addblue)=(*(data-1) + *(data+1))/2; } } else { if (xx == XW-1) { *(o+addred)=(*(data-SW) + *(data+SW))/2; *(o+addblue)=*(data-1); } else { *(o+addred)=(*(data-SW) + *(data+SW))/2; *(o+addblue)=(*(data-1) + *(data+1))/2; } } *(o+addgreen)=*data; if (mode) { } } else { //blue *(o+addblue)=*data; if (yy == XH-1) { if (xx == 0) { *(o+addred)=*(data-SW+1); *(o+addgreen)=(*(data-SW) + *(data+1))/2; } else { *(o+addred)=(*(data-SW-1) + *(data-SW+1))/2; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1))/3; } } else { if (xx == 0) { *(o+addred)=(*(data-SW+1) + *(data+SW+1))/2; *(o+addgreen)=(*(data-SW) + *(data+1) + *(data+SW))/3; } else { *(o+addred)=(*(data-SW-1) + *(data-SW+1) + *(data+SW-1) + *(data+SW+1))/4; *(o+addgreen)=(*(data-SW) + *(data-1) + *(data+1) + *(data+SW))/4; } } if (mode) { } } } } else { /* not bilinear interpolation */ if (!(yy&1)) { // even row if (xx&1) { // odd column, red *(o+addred)=*data; *(o+addred-3)=*data; *(o+addred+3*(XW))=*data; *(o+addred+3*(XW-1))=*data; if (mode) { *(o+addred+3)=*data; *(o+addred-6)=*data; *(o+addred+3*(XW+1))=*data; *(o+addred+3*(XW-2))=*data; } } else { // green *(o+1)=*data; *(o+1+3)=*data; if (mode) { *(o+1+6)=*data; if (xd>2) *(o+1-3)=*data; } } } else { if (xx&1) { // odd column green *(o+1)=*data; *(o+1-3)=*data; if (mode) { *(o+1+3)=*data; *(o+1-6)=*data; *(o+1+6)=*data; // last would be missing. } } else { //blue *(o+addblue)=*data; *(o+addblue+3)=*data; *(o+addblue+3*(XW))=*data; *(o+addblue+3*(XW-1))=*data; if (mode) { *(o+addblue+6)=*data; *(o+addblue+3*(XW+1))=*data; *(o+addblue-6)=*data; *(o+addblue+3*(XW-2))=*data; } } } } xx++; copylen ++; if (xx==XW || (xx==XW/2 && mode)) { if (mode) mid_value += (mid_value_line/(XW/2)); else mid_value += (mid_value_line/XW); mid_value_line = 0; xx=0; xd = 0; yy++; if (yy>=XH) { break; } } data++; } /* * the frame is done, the exposure should * be set here (We are the waked-up process) */ if (yy>=XH || (yy>=XH/2 && mode)) { if (quickcam->sensor_ctrl.mode==2) { mid_value = mid_value/(yy/2); } else { mid_value = mid_value/yy; } usb_quickcam_set_exposure(quickcam,mid_value); } /* if mode=2 we have only 1/2 of the enlargement */ if (quickcam->sensor_ctrl.mode==2) { data = rgb; for (i=XH/2;i>1;i--) { memcpy(&data[i*6*XW],&data[i*3*XW],XW*3); memcpy(&data[i*6*XW-3*XW],&data[i*3*XW],XW*3); } copylen = copylen *2; } if (mode) { copylen = copylen *2; } /* frame->scanlength = (copylen*6)/2; */// yes but how to compute partial pixels!. /* RGB -> BGR */ if(tobgr) { char c; char *p = rgb; int i = QUICKCAM_WIDTH * QUICKCAM_HEIGHT; while(--i) { c = p[0]; p[0] = p[2]; p[2] = c; p += 3; } } return 0; } static void outputppm(unsigned char *target, int width, int height) { int i; if ( target == NULL || width <= 0 || height <= 0 ) return; printf("P6\n"); printf("%d %d 255 ", width, height); for ( i = 0; i < (width*height*3); i+=3 ) { printf("%c%c%c", target[i], target[i+1], target[i+2]); } return; }