/* * pwcview - application to view video, create jpeg snapshots and alter * settings of a webcam controlled by the pwc driver * * Copyright (C) 2006 Raaf * * This program is free software; you can redistribute it and/or modify * it 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., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NOGUI #include #endif #ifdef LOCAL_VIDEODEV #include "videodev.h" #else #include #endif #include "pwc-ioctl.h" #ifndef NOGUI int default_handler(int fd, int dir, char *buf) { if(dir != 0) return -1; snprintf(buf,80,"pwcview"); return 0; } int framerate_handler(int fd, int dir, char *buf) { int fps; struct video_window vw; if(ioctl(fd,VIDIOCGWIN,&vw) == -1) { perror("Failed to get current framerate"); return -1; } fps = vw.flags >> PWC_FPS_SHIFT; if((dir == -1 && fps >= 9) ||(dir == 1 && fps <= 25)) { fps += dir == -1 ? -5 : 5; vw.flags = fps << PWC_FPS_SHIFT; if(ioctl(fd,VIDIOCSWIN,&vw) == -1) fprintf(stderr,"Failed to set framerate to %d fps: %s\n",fps,strerror(errno)); if(ioctl(fd,VIDIOCGWIN,&vw) == -1) { perror("Failed to get new framerate"); return -1; } fps = vw.flags >> PWC_FPS_SHIFT; } snprintf(buf,80,"framerate: %d fps",fps); return 0; } int compression_handler(int fd, int dir, char *buf) { int qual; if(ioctl(fd,VIDIOCPWCGCQUAL,&qual) == -1) { perror("Failed to get current compression"); return -1; } if((dir == -1 && qual > 0) || (dir == 1 && qual < 3)) { qual += dir == -1 ? -1 : 1; if(ioctl(fd,VIDIOCPWCSCQUAL,&qual) == -1) perror("Failed to set compression"); if(ioctl(fd,VIDIOCPWCGCQUAL,&qual) == -1) { perror("Failed to get new compression"); return -1; } } snprintf(buf,80,"compression: %d",qual); return 0; } int brightness_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current brightness"); return -1; } if((dir == -1) || (dir == 1)) { pict.brightness += dir == -1 ? -512 : 512; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set brightness"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new brightness"); return -1; } } snprintf(buf,80,"brightness: %d",pict.brightness >> 9); return 0; } int contrast_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current contrast"); return -1; } if((dir == -1) || (dir == 1)) { pict.contrast += dir == -1 ? -1024 : 1024; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set contrast"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new contrast"); return -1; } } snprintf(buf,80,"contrast: %d",pict.contrast >> 10); return 0; } int saturation_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current saturation"); return -1; } if((dir == -1) || (dir == 1)) { pict.colour += dir == -1 ? -327 : 327; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set saturation"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new saturation"); return -1; } } snprintf(buf,80,"saturation: %d",(pict.colour - 32768) / 327); return 0; } int gamma_handler(int fd, int dir, char *buf) { struct video_picture pict; if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get current gamma"); return -1; } if((dir == -1) ||(dir == 1)) { pict.whiteness += dir == -1 ? -2048 : 2048; if(ioctl(fd,VIDIOCSPICT,&pict) == -1) perror("Failed to set gamma"); if(ioctl(fd,VIDIOCGPICT,&pict) == -1) { perror("Failed to get new gamma"); return -1; } } snprintf(buf,80,"gamma: %d",pict.whiteness >> 11); return 0; } int agc_handler(int fd, int dir, char *buf) { static u_int16_t agc = 32768; static int agcmode = 1; int val; if(dir == 2) { if(++agcmode == 2) agcmode = 0; } else if(dir == -1 && agcmode == 0) agc -= 1024; else if(dir == 1 && agcmode == 0) agc += 1024; if(agcmode == 1) { val = -1; snprintf(buf,80,"gain control: auto"); } else { val = agc; snprintf(buf,80,"gain control: %d",agc >> 10); } ioctl(fd,VIDIOCPWCSAGC,&val); return 0; } int shutter_handler(int fd, int dir, char *buf) { static u_int16_t shutter = 32768; static int shuttermode = 1; int val; if(dir == 2) { if(++shuttermode == 2) shuttermode = 0; } else if(dir == -1 && shuttermode == 0) shutter -= 256; else if(dir == 1 && shuttermode == 0) shutter += 256; if(shuttermode == 1) { val = -1; snprintf(buf,80,"shutter speed: auto"); } else { val = shutter; snprintf(buf,80,"shutter speed: %d",shutter >> 8); } ioctl(fd,VIDIOCPWCSSHUTTER,&val); return 0; } int whitebalance_handler(int fd, int dir, char *buf) { static int skip = 0; struct pwc_whitebalance wb; char *names[] = { "indoor", "outdoor", "fluorescent","manual","auto" }; int *val = NULL; if(ioctl(fd,VIDIOCPWCGAWB,&wb) == -1) { perror("Failed to get whitebalance"); return -1; } if(dir == 2 && !skip) { if(--wb.mode < PWC_WB_INDOOR) wb.mode = PWC_WB_AUTO; } if(wb.mode == PWC_WB_MANUAL) { if(dir == 2) { skip = !skip; if(skip) { wb.manual_red = wb.read_red; wb.manual_blue = wb.read_blue; } } val = skip ? &wb.manual_red : &wb.manual_blue; if(dir == -1) *val -= 256; else if(dir == 1) *val += 256; } if(ioctl(fd,VIDIOCPWCSAWB,&wb) == -1) perror("Failed to set whitebalance"); if(ioctl(fd,VIDIOCPWCGAWB,&wb) == -1) { perror("Failed to get whitebalance"); return -1; } if(wb.mode == PWC_WB_MANUAL) snprintf(buf,80,"whitebalance %s gain: %d",skip ? "red" : "blue",*val >> 8); else snprintf(buf,80,"whitebalance: %s",names[wb.mode]); return 0; } int whitebalancespeed_handler(int fd, int dir, char *buf) { struct pwc_wb_speed speed; if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get current awb speed"); return -1; } if((dir == -1) || (dir == 1)) { speed.control_speed += dir == -1 ? -2032 : 2032; if(ioctl(fd,VIDIOCPWCSAWBSPEED,&speed) == -1) perror("Failed to set awb speed"); if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get new awb speed"); return -1; } } snprintf(buf,80,"whitebalance speed: %d",speed.control_speed / 2032); return 0; } int whitebalancedelay_handler(int fd, int dir, char *buf) { struct pwc_wb_speed speed; if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get current awb delay"); return -1; } if((dir == -1) || (dir == 1)) { speed.control_delay += dir == -1 ? -1024 : 1024; if(ioctl(fd,VIDIOCPWCSAWBSPEED,&speed) == -1) perror("Failed to set awb delay"); if(ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) { perror("Failed to get new awb delay"); return -1; } } snprintf(buf,80,"whitebalance delay: %d",speed.control_delay >> 10); return 0; } int contour_handler(int fd, int dir,char *buf) { static u_int16_t contour = 32768; static int contourmode = 1; int val; if(dir == 2) { if(++contourmode == 2) contourmode = 0; } else if(dir == -1 && contourmode == 0) contour -= 1024; else if(dir == 1 && contourmode == 0) contour += 1024; if(contourmode == 1) val = -1; else val = contour; if(ioctl(fd,VIDIOCPWCSCONTOUR,&val) == -1) perror("Failed to set contour"); if(contourmode == 1) snprintf(buf,80,"contour: auto"); else { if(ioctl(fd,VIDIOCPWCGCONTOUR,&contour) == -1) { perror("Failed to get contour"); return -1; } snprintf(buf,80,"contour: %d",contour >> 10); } return 0; } int dynamicnoise_handler(int fd, int dir, char *buf) { int dynnoise; if(ioctl(fd,VIDIOCPWCGDYNNOISE,&dynnoise) == -1) { perror("Failed to get current dynamic noise reduction mode"); return -1; } if(dir == 2) { if(++dynnoise == 4) dynnoise = 0; if(ioctl(fd,VIDIOCPWCSDYNNOISE,&dynnoise) == -1) perror("Failed to set dynamic noise reduction mode"); if(ioctl(fd,VIDIOCPWCGDYNNOISE,&dynnoise) == -1) { perror("Failed to get new dynamic noise reduction mode"); return -1; } } snprintf(buf,80,"dnr mode: %d",dynnoise); return 0; } int backlight_handler(int fd, int dir, char *buf) { int backlight; if(ioctl(fd,VIDIOCPWCGBACKLIGHT,&backlight) == -1) { perror("Failed to get backlight mode"); return -1; } if(dir == 2) { backlight = !backlight; if(ioctl(fd,VIDIOCPWCSBACKLIGHT,&backlight) == -1) perror("Failed to set backlight mode"); if(ioctl(fd,VIDIOCPWCGBACKLIGHT,&backlight) == -1) { perror("Failed to get new backlight mode"); return -1; } } snprintf(buf,80,"backlight compensation: %s",backlight ? "on" : "off"); return 0; } int flicker_handler(int fd, int dir, char *buf) { int flicker; if(ioctl(fd,VIDIOCPWCGFLICKER,&flicker) == -1) { perror("Failed to get flicker mode"); return -1; } if(dir == 2) { flicker = !flicker; if(ioctl(fd,VIDIOCPWCSFLICKER,&flicker) == -1) perror("Failed to set flicker mode"); if(ioctl(fd,VIDIOCPWCGFLICKER,&flicker) == -1) { perror("Failed to get new flicker mode"); return -1; } } snprintf(buf,80,"anti flicker mode: %s",flicker ? "on" : "off"); return 0; } int colour_handler(int fd, int dir, char *buf) { int colour; if(ioctl(fd,VIDIOCPWCGCOLOUR,&colour) == -1) { perror("Failed to get colour mode"); return -1; } if(dir == 2) { colour = !colour; if(ioctl(fd,VIDIOCPWCSCOLOUR,&colour) == -1) perror("Failed to set colour mode"); if(ioctl(fd,VIDIOCPWCGCOLOUR,&colour) == -1) { perror("Failed to get new colour mode"); return -1; } } snprintf(buf,80,"colour mode: %s",colour ? "color" : "black & white"); return 0; } int saveuser_handler(int fd, int dir, char *buf) { if(dir == 0) { snprintf(buf,80,"save user settings"); } else if(dir == 2) { if(ioctl(fd,VIDIOCPWCSUSER) == -1) snprintf(buf,80,"Error: %s",strerror(errno)); else snprintf(buf,80,"User settings saved"); return 0; } else { return -1; } return 0; } int restoreuser_handler(int fd, int dir, char *buf) { if(dir == 0) { snprintf(buf,80,"restore user settings"); } else if(dir == 2) { if(ioctl(fd,VIDIOCPWCRUSER) == -1) snprintf(buf,80,"Error: %s",strerror(errno)); else snprintf(buf,80,"User settings restored"); } else { return -1; } return 0; } int restorefactory_handler(int fd, int dir, char *buf) { if(dir == 0) { snprintf(buf,80,"restore factory settings"); } else if(dir == 2) { if(ioctl(fd,VIDIOCPWCFACTORY) == -1) snprintf(buf,80,"Error: %s",strerror(errno)); else snprintf(buf,80,"Factory settings restored"); } else { return -1; } return 0; } int (*handler[])(int fd, int direction, char *buf) = { /* direction: -1 = down, 0 = init, 1 = up, 2 = special */ default_handler, framerate_handler, brightness_handler, contrast_handler, saturation_handler, gamma_handler, agc_handler, shutter_handler, whitebalance_handler, whitebalancespeed_handler, whitebalancedelay_handler, contour_handler, dynamicnoise_handler, backlight_handler, flicker_handler, colour_handler, compression_handler, saveuser_handler, restoreuser_handler, restorefactory_handler }; Uint32 cbtimer(Uint32 interval, void *param) { SDL_Event event; SDL_UserEvent userevent; userevent.type = SDL_USEREVENT; userevent.code = 0; userevent.data1 = NULL; userevent.data2 = NULL; event.type = SDL_USEREVENT; event.user = userevent; SDL_PushEvent(&event); return interval; } #endif void sig_chld(int signo) { int stat; while(waitpid(-1, &stat, WNOHANG) > 0) ; } void jpeg_init(int width, int height, int quality, struct jpeg_compress_struct *cinfo, struct jpeg_error_mgr *jerr, JSAMPIMAGE jimage, JSAMPROW y) { int i; JSAMPROW u,v; cinfo->err = jpeg_std_error(jerr); jpeg_create_compress(cinfo); cinfo->image_width = width; cinfo->image_height = height; cinfo->input_components = 3; cinfo->in_color_space = JCS_YCbCr; jpeg_set_defaults(cinfo); /* cinfo->dct_method = JDCT_FLOAT; */ cinfo->raw_data_in = TRUE; cinfo->comp_info[0].h_samp_factor = 2; cinfo->comp_info[0].v_samp_factor = 2; cinfo->comp_info[1].h_samp_factor = 1; cinfo->comp_info[1].v_samp_factor = 1; cinfo->comp_info[2].h_samp_factor = 1; cinfo->comp_info[2].v_samp_factor = 1; jimage[0] = malloc(height * 2 * sizeof(JSAMPROW)); if(jimage[0] == NULL) { fprintf(stderr,"Error: out of memory\n"); exit(1); } jimage[1] = jimage[0] + height; jimage[2] = jimage[1] + (height/2); u = y + width * height; v = u + width * height / 4; for(i = 0; i < height; ++i, y+=width) { jimage[0][i] = y; } for(i = 0; i < height/2; ++i, u+=width/2, v+=width/2) { jimage[1][i] = u; jimage[2][i] = v; } jpeg_set_quality(cinfo, quality, TRUE); } void jpeg_write(int height, JSAMPIMAGE jimage, struct jpeg_compress_struct *cinfo, const char *fmt, const char *cmd) { JSAMPARRAY jdata[3]; char filename[1024]; FILE *outfile; time_t tt; struct tm *tm; int i; tt = time(NULL); if(tt == (time_t)-1) { perror("Failed to get time"); return; } tm = localtime(&tt); if(strftime(filename,1024,fmt,tm) == 0) { fprintf(stderr,"Error: resulting filename to long\n"); return; } if ((outfile = fopen(filename, "wb")) == NULL) { perror("Error opening output file"); return; } jdata[0] = jimage[0]; jdata[1] = jimage[1]; jdata[2] = jimage[2]; jpeg_stdio_dest(cinfo, outfile); jpeg_start_compress(cinfo, TRUE); for (i = 0;i < height;i += 2*DCTSIZE) { jpeg_write_raw_data(cinfo, jdata, 2*DCTSIZE); jdata[0] += 2*DCTSIZE; jdata[1] += DCTSIZE; jdata[2] += DCTSIZE; } jpeg_finish_compress(cinfo); fclose(outfile); if(cmd != NULL) { switch(fork()) { case 0: execlp(cmd,cmd,filename,NULL); fprintf(stderr,"Failed to execute %s: %s\n",cmd,strerror(errno)); _exit(1); case -1: perror("fork failed"); /* Fall through */ default: break; } } } #define PSZ_MAX 6 struct { char *name; int width; int height; } sizes[PSZ_MAX] = { { "sqcif", 128, 96 }, { "qsif", 160, 120 }, { "qcif", 176, 144 }, { "sif", 320, 240 }, { "cif", 352, 288 }, { "vga", 640, 480 } }; int usage() { fprintf(stderr, #ifndef NOGUI "Usage: pwcview [ options ]\n\n" #else "Usage: pwcsnap [ options ]\n\n" #endif "Options:\n" " -? Display this help message\n" #ifndef NOGUI " -h Run in headless mode\n" " -x Create window without frame\n" " -y Use IYUV overlay instead of YV12 overlay\n" " -m Create the video surface in system memory (SDL_SWSURFACE)\n" " -a Always use video surface (SDL_ANYFORMAT)\n" " -b Bits per pixel to setup SDL video surface with (default: 0)\n" #endif " -c Number of jpeg snapshots to take (default: 0 (-1=unlimited))\n" " -i Jpeg snapshot interval in milliseconds (default: 3000)\n" " -q Quality of jpeg output image (range: 0 -100, default: 75)\n" " -o Filename for jpeg output (default: /tmp/%%Y%%m%%d%%H%%M%%S.jpg)\n" " -e Command to execute after each snaphot (default: none)\n" " -d Video device to open (default: /dev/video0)\n" " -s Video size to use (default: sif)\n" " -f Video framerate to use (default: 5)\n\n" " See the pwcview(1) manpage for details\n\n"); return 1; } int main(int argc, char **argv) { #ifndef NOGUI Uint8 *keylist; SDL_Surface *screen; SDL_Overlay *overlay; SDL_Event event; SDL_TimerID timerid; SDL_Rect rect = { 0 }; int initflags = SDL_INIT_VIDEO; int sdlflags = SDL_RESIZABLE; int format = SDL_YV12_OVERLAY; int swsurface = 0; int bpp = 0; int ysize, uvsize, rv; unsigned char *u, *v; int fullscreen = 0, mode = 0, failed = 0; char buf[80]; Uint32 interval = 3000; int headless = 0; #else unsigned int interval = 3000; int headless = 1; #endif struct timespec tv; struct video_window vw = { 0 }; const char *device = "/dev/video0"; unsigned int fps = 5; int snapcnt = 0; int frozen = 0; int i = 3; /* sizeidx (sif) */ JSAMPARRAY jdata[3]; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; int quality = 75; const char *outfile = "/tmp/%Y%m%d%H%M%S.jpg"; const char *command = NULL; int fd; int imgsize; int ch, size; unsigned char *y; while((ch = getopt(argc,argv,"yaxhmb:q:o:d:e:f:i:c:s:?")) != -1) { switch(ch) { #ifndef NOGUI case 'y': format = SDL_IYUV_OVERLAY; break; case 'a': sdlflags |= SDL_ANYFORMAT; break; case 'x': sdlflags |= SDL_NOFRAME; break; case 'h': headless = 1; break; case 'm': swsurface = 1; break; case 'b': bpp = atoi(optarg); break; #endif case 'q': quality = atoi(optarg); break; case 'o': outfile = optarg; break; case 'd': device = optarg; break; case 'e': command = optarg; break; case 'f': fps = strtoul(optarg,NULL,10); break; case 'i': interval = strtoul(optarg,NULL,10); break; case 'c': snapcnt = atoi(optarg); if(snapcnt < -1) { fprintf(stderr,"Invalid snapshot count: %d\n",snapcnt); return 1; } break; case 's': for(i = 0; i < PSZ_MAX; ++i) if(strcmp(sizes[i].name,optarg) == 0) break; if(i == PSZ_MAX) { fprintf(stderr,"Invalid size, valid sizes: sqcif, qsif, qcif, sif, cif, vga\n"); return 1; } break; case '?': default: return usage(); } } if(fps < 5 || fps > 30) { fprintf(stderr,"Invalid framerate, framerate must be in the range 5-30\n"); return 1; } if(!headless && interval < (((1000 / fps)/10)*10)) interval = (((1000 / fps)/10)*10); vw.width = sizes[i].width; vw.height= sizes[i].height; vw.flags = fps << PWC_FPS_SHIFT; imgsize = (vw.width * vw.height * 3)/2; if((fd = open(device, O_RDONLY)) < 0) { perror("Failed to open webcam"); exit(1); } fcntl(fd,F_SETFD,FD_CLOEXEC); if(ioctl(fd,VIDIOCSWIN,&vw) == -1) { fprintf(stderr,"Failed to set webcam to: %dx%d (%s) at %d fps (%s)\n", vw.width,vw.height,sizes[i].name,fps,strerror(errno)); exit(1); } fprintf(stderr,"Webcam set to: %dx%d (%s) at %d fps\n",vw.width,vw.height,sizes[i].name,fps); if(headless && snapcnt == 0) { /* Done */ close(fd); exit(0); } y = malloc(imgsize); if(y == NULL) { perror("Out of memory"); exit(1); } jpeg_init(vw.width,vw.height,quality,&cinfo,&jerr,jdata,(JSAMPROW)y); if(command != NULL) signal(SIGCHLD,sig_chld); #ifndef NOGUI if(!headless) { rect.w = vw.width; rect.h = vw.height; ysize = rect.w * rect.h; uvsize = ysize / 4; if(format == SDL_IYUV_OVERLAY) { u = y + ysize; v = u + uvsize; } else { v = y + ysize; u = v + uvsize; } if(snapcnt != 0) initflags |= SDL_INIT_TIMER; if (SDL_Init(initflags) < 0) { fprintf(stderr,"Failed to init sdl: %s\n", SDL_GetError()); exit(1); } atexit(SDL_Quit); sdlflags |= swsurface ? SDL_SWSURFACE : SDL_HWSURFACE; if((screen = SDL_SetVideoMode(rect.w, rect.h, bpp, sdlflags)) == NULL) { fprintf(stderr,"SDL Failed to set videomode: %s\n", SDL_GetError()); exit(1); } if((overlay = SDL_CreateYUVOverlay(rect.w, rect.h, format, screen)) == NULL) { fprintf(stderr,"Failed to create yuvoverlay: %s\n", SDL_GetError()); exit(1); } SDL_DisplayYUVOverlay(overlay, &rect); snprintf(buf,80,"pwcview"); keylist = SDL_GetKeyState(NULL); if(snapcnt != 0) timerid = SDL_AddTimer(interval,cbtimer,NULL); } #endif while (frozen || ((size = read(fd,y,imgsize)) > 0) || (size == -1 && errno == EINTR)) { if(!frozen && size != imgsize) { if(size != -1) { fprintf(stderr,"Warning short read, got only %d of %d bytes\n",size,imgsize); } continue; } if(headless) { jpeg_write(vw.height,jdata,&cinfo,outfile,command); if(snapcnt > 0) snapcnt--; if(snapcnt == 0) exit(0); tv.tv_sec = interval / 1000; tv.tv_nsec = (interval % 1000) * 1000000; while(nanosleep(&tv,&tv) == -1 && errno == EINTR) ; continue; } #ifndef NOGUI SDL_LockYUVOverlay(overlay); memcpy(overlay->pixels[0],y,ysize); memcpy(overlay->pixels[1],u,uvsize); memcpy(overlay->pixels[2],v,uvsize); SDL_UnlockYUVOverlay(overlay); SDL_DisplayYUVOverlay(overlay, &rect); SDL_PumpEvents(); if(failed == 0) SDL_WM_SetCaption(buf,buf); failed = 1; if(keylist[SDLK_RIGHT]) { failed = handler[mode](fd,1,buf); continue; } else if(keylist[SDLK_LEFT]) { failed = handler[mode](fd,-1,buf); continue; } if(frozen) rv = SDL_WaitEvent(&event); else rv= SDL_PollEvent(&event); if(rv) { do { if(event.type == SDL_KEYDOWN) { switch(event.key.keysym.sym) { case SDLK_DOWN: if(mode != sizeof(handler)/sizeof(handler[0]) - 1) failed = handler[++mode](fd,0,buf); break; case SDLK_UP: if(mode != 0) failed = handler[--mode](fd,0,buf); break; case SDLK_RETURN: failed = handler[mode](fd,2,buf); break; case SDLK_f: SDL_WM_ToggleFullScreen(screen); fullscreen = !fullscreen; SDL_WM_GrabInput(fullscreen ? SDL_GRAB_ON : SDL_GRAB_OFF); break; case SDLK_p: jpeg_write(vw.height,jdata,&cinfo,outfile,command); break; case SDLK_SPACE: frozen = !frozen; break; case SDLK_q: exit(0); default: break; } } else if(event.type == SDL_VIDEORESIZE) { rect.w = event.resize.w; rect.h = event.resize.h; if((screen = SDL_SetVideoMode(rect.w, rect.h, bpp, sdlflags)) == NULL) { fprintf(stderr,"SDL Failed to set videomode: %s\n", SDL_GetError()); exit(1); } } else if(event.type == SDL_USEREVENT) { jpeg_write(vw.height,jdata,&cinfo,outfile,command); if(snapcnt > 0) snapcnt--; if(snapcnt == 0) SDL_RemoveTimer(timerid); } else if(event.type == SDL_QUIT) { exit(0); } }while(!frozen && SDL_PollEvent(&event)); } #endif } if(size != 0) perror("Error reading from webcam"); close(fd); jpeg_destroy_compress(&cinfo); return 0; }