1 /*
2  * pwcview - application to view video, create jpeg snapshots and alter
3  * settings of a webcam controlled by the pwc driver
4  *
5  * Copyright (C) 2006-2007 Raaf
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
20  * MA  02110-1301  USA
21  */
22 
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <sys/ioctl.h>
26 #include <sys/time.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <signal.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <time.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <jpeglib.h>
37 #ifndef NOGUI
38 #include <SDL.h>
39 #endif
40 #include <libv4l1.h>
41 #include <linux/videodev.h>
42 #include "pwc-ioctl.h"
43 #include "pixels.h"
44 
45 const char *motioncmd;
46 long cmdinterval = 60;
47 int fullscreen = 0;
48 int motionrecord = 0;
49 int motionfs   = 0;
50 int motionbeep = 0;
51 int showmotion = 0;
52 int showtime   = 0;
53 int recmargin = 15;
54 uint32_t threshold = 1;
55 uint32_t sensitivity = 1000;
56 struct video_window vw;
57 
58 #define SCALE_NONE 0
59 #define SCALE_DOUBLE 1
60 #define SCALE_FULL 2
61 int scale = SCALE_FULL;
62 
63 #ifndef NOGUI
64 SDL_Surface *screen;
65 
default_handler(int fd,int dir,char * buf)66 int default_handler(int fd, int dir, char *buf)
67 {
68 	if(dir != 0)
69 		return -1;
70 
71 	snprintf(buf,80,"pwcview");
72 	return 0;
73 }
74 
framerate_handler(int fd,int dir,char * buf)75 int framerate_handler(int fd, int dir, char *buf)
76 {
77 	int fps;
78 	struct video_window vw;
79 
80 	if(v4l1_ioctl(fd,VIDIOCGWIN,&vw) == -1) {
81 		perror("Failed to get current framerate");
82 		return -1;
83 	}
84 
85 	fps = vw.flags  >> PWC_FPS_SHIFT;
86 
87 	if((dir == -1 && fps >= 9) ||(dir == 1 && fps <= 25)) {
88 		fps += dir == -1 ? -5 : 5;
89 		vw.flags = fps << PWC_FPS_SHIFT;
90 		if(v4l1_ioctl(fd,VIDIOCSWIN,&vw) == -1)
91 			fprintf(stderr,"Failed to set framerate to %d fps: %s\n",fps,strerror(errno));
92 		if(v4l1_ioctl(fd,VIDIOCGWIN,&vw) == -1) {
93 			perror("Failed to get new framerate");
94 			return -1;
95 		}
96 		fps = vw.flags  >> PWC_FPS_SHIFT;
97 	}
98 	snprintf(buf,80,"framerate: %d fps",fps);
99 	return 0;
100 }
101 
compression_handler(int fd,int dir,char * buf)102 int compression_handler(int fd, int dir, char *buf)
103 {
104 	int qual;
105 
106 	if(v4l1_ioctl(fd,VIDIOCPWCGCQUAL,&qual) == -1) {
107 		perror("Failed to get current compression");
108 		return -1;
109 	}
110 
111 	if((dir == -1 && qual > 0) || (dir == 1 && qual < 3)) {
112 		qual += dir == -1 ? -1 : 1;
113 		if(v4l1_ioctl(fd,VIDIOCPWCSCQUAL,&qual) == -1)
114 			perror("Failed to set compression");
115 		if(v4l1_ioctl(fd,VIDIOCPWCGCQUAL,&qual) == -1) {
116 			perror("Failed to get new compression");
117 			return -1;
118 		}
119 	}
120 	snprintf(buf,80,"compression: %d",qual);
121 	return 0;
122 }
123 
brightness_handler(int fd,int dir,char * buf)124 int brightness_handler(int fd, int dir, char *buf)
125 {
126 	struct video_picture pict;
127 
128 	if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
129 		perror("Failed to get current brightness");
130 		return -1;
131 	}
132 
133 	if((dir == -1) || (dir == 1)) {
134 		pict.brightness += dir == -1 ? -512 : 512;
135 		if(v4l1_ioctl(fd,VIDIOCSPICT,&pict) == -1)
136 			perror("Failed to set brightness");
137 		if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
138 			perror("Failed to get new brightness");
139 			return -1;
140 		}
141 	}
142 	snprintf(buf,80,"brightness: %d",pict.brightness >> 9);
143 	return 0;
144 }
145 
contrast_handler(int fd,int dir,char * buf)146 int contrast_handler(int fd, int dir, char *buf)
147 {
148 	struct video_picture pict;
149 
150 	if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
151 		perror("Failed to get current contrast");
152 		return -1;
153 	}
154 
155 	if((dir == -1) || (dir == 1)) {
156 		pict.contrast += dir == -1 ? -1024 : 1024;
157 		if(v4l1_ioctl(fd,VIDIOCSPICT,&pict) == -1)
158 			perror("Failed to set contrast");
159 		if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
160 			perror("Failed to get new contrast");
161 			return -1;
162 		}
163 	}
164 	snprintf(buf,80,"contrast: %d",pict.contrast >> 10);
165 	return 0;
166 }
167 
saturation_handler(int fd,int dir,char * buf)168 int saturation_handler(int fd, int dir, char *buf)
169 {
170 	struct video_picture pict;
171 
172 	if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
173 		perror("Failed to get current saturation");
174 		return -1;
175 	}
176 
177 	if((dir == -1) || (dir == 1)) {
178 		pict.colour += dir == -1 ? -327 : 327;
179 		if(v4l1_ioctl(fd,VIDIOCSPICT,&pict) == -1)
180 			perror("Failed to set saturation");
181 		if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
182 			perror("Failed to get new saturation");
183 			return -1;
184 		}
185 	}
186 	snprintf(buf,80,"saturation: %d",(pict.colour - 32768) / 327);
187 	return 0;
188 }
189 
gamma_handler(int fd,int dir,char * buf)190 int gamma_handler(int fd, int dir, char *buf)
191 {
192 	struct video_picture pict;
193 
194 	if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
195 		perror("Failed to get current gamma");
196 		return -1;
197 	}
198 
199 	if((dir == -1) ||(dir == 1)) {
200 		pict.whiteness += dir == -1 ? -2048 : 2048;
201 		if(v4l1_ioctl(fd,VIDIOCSPICT,&pict) == -1)
202 			perror("Failed to set gamma");
203 		if(v4l1_ioctl(fd,VIDIOCGPICT,&pict) == -1) {
204 			perror("Failed to get new gamma");
205 			return -1;
206 		}
207 	}
208 	snprintf(buf,80,"gamma: %d",pict.whiteness >> 11);
209 	return 0;
210 }
211 
agc_handler(int fd,int dir,char * buf)212 int agc_handler(int fd, int dir, char *buf)
213 {
214 	static u_int16_t agc = 32768;
215  	static int agcmode = 1;
216 	int val;
217 
218 	if(dir == 2) {
219 		if(++agcmode == 2)
220 			agcmode = 0;
221 	}
222 	else if(dir == -1 && agcmode == 0)
223 		agc -= 1024;
224 	else if(dir == 1 && agcmode == 0)
225 		agc += 1024;
226 
227 	if(agcmode == 1) {
228 		val = -1;
229 		snprintf(buf,80,"gain control: auto");
230 	}
231 	else {
232 		val = agc;
233 		snprintf(buf,80,"gain control: %d",agc >> 10);
234 	}
235 
236 	v4l1_ioctl(fd,VIDIOCPWCSAGC,&val);
237 	return 0;
238 }
239 
shutter_handler(int fd,int dir,char * buf)240 int shutter_handler(int fd, int dir, char *buf)
241 {
242 	static u_int16_t shutter = 32768;
243 	static int shuttermode = 1;
244 	int val;
245 
246 	if(dir == 2) {
247 		if(++shuttermode == 2)
248 			shuttermode = 0;
249 	}
250 	else if(dir == -1 && shuttermode == 0)
251 		shutter -= 256;
252 	else if(dir == 1 && shuttermode == 0)
253 		shutter += 256;
254 
255 	if(shuttermode == 1) {
256 		val = -1;
257 		snprintf(buf,80,"shutter speed: auto");
258 	}
259 	else {
260 		val = shutter;
261 		snprintf(buf,80,"shutter speed: %d",shutter >> 8);
262 	}
263 	v4l1_ioctl(fd,VIDIOCPWCSSHUTTER,&val);
264 	return 0;
265 }
266 
whitebalance_handler(int fd,int dir,char * buf)267 int whitebalance_handler(int fd, int dir, char *buf)
268 {
269 	static int skip = 0;
270 	struct pwc_whitebalance wb;
271 	char *names[] = { "indoor", "outdoor", "fluorescent","manual","auto" };
272 	int *val = NULL;
273 
274 	if(v4l1_ioctl(fd,VIDIOCPWCGAWB,&wb) == -1) {
275 		perror("Failed to get white balance");
276 		return -1;
277 	}
278 
279 	if(dir == 2 && !skip) {
280 		if(--wb.mode < PWC_WB_INDOOR)
281 			wb.mode = PWC_WB_AUTO;
282 	}
283 	if(wb.mode == PWC_WB_MANUAL) {
284 		if(dir == 2) {
285 			skip = !skip;
286 			if(skip) {
287 				wb.manual_red  = wb.read_red;
288 				wb.manual_blue  = wb.read_blue;
289 			}
290 		}
291 		val = skip ? &wb.manual_red : &wb.manual_blue;
292 		if(dir == -1)
293 			*val -= 256;
294 		else if(dir == 1)
295 			*val += 256;
296 	}
297 
298 	if(v4l1_ioctl(fd,VIDIOCPWCSAWB,&wb) == -1)
299 		perror("Failed to set white balance");
300 
301 	if(v4l1_ioctl(fd,VIDIOCPWCGAWB,&wb) == -1) {
302 		perror("Failed to get white balance");
303 		return -1;
304 	}
305 
306 	if(wb.mode == PWC_WB_MANUAL)
307 		snprintf(buf,80,"white balance %s gain: %d",skip ? "red" : "blue",*val >> 8);
308 	else
309 		snprintf(buf,80,"white balance: %s",names[wb.mode]);
310 	return 0;
311 }
312 
whitebalancespeed_handler(int fd,int dir,char * buf)313 int whitebalancespeed_handler(int fd, int dir, char *buf)
314 {
315 	struct pwc_wb_speed speed;
316 
317 	if(v4l1_ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) {
318 		perror("Failed to get current awb speed");
319 		return -1;
320 	}
321 
322 	if((dir == -1) || (dir == 1)) {
323 		speed.control_speed += dir == -1 ? -2032 : 2032;
324 		if(v4l1_ioctl(fd,VIDIOCPWCSAWBSPEED,&speed) == -1)
325 			perror("Failed to set awb speed");
326 		if(v4l1_ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) {
327 			perror("Failed to get new awb speed");
328 			return -1;
329 		}
330 	}
331 	snprintf(buf,80,"white balance speed: %d",speed.control_speed / 2032);
332 	return 0;
333 }
334 
whitebalancedelay_handler(int fd,int dir,char * buf)335 int whitebalancedelay_handler(int fd, int dir, char *buf)
336 {
337 	struct pwc_wb_speed speed;
338 
339 	if(v4l1_ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) {
340 		perror("Failed to get current awb delay");
341 		return -1;
342 	}
343 
344 	if((dir == -1) || (dir == 1)) {
345 		speed.control_delay += dir == -1 ? -1024 : 1024;
346 		if(v4l1_ioctl(fd,VIDIOCPWCSAWBSPEED,&speed) == -1)
347 			perror("Failed to set awb delay");
348 		if(v4l1_ioctl(fd,VIDIOCPWCGAWBSPEED,&speed) == -1) {
349 			perror("Failed to get new awb delay");
350 			return -1;
351 		}
352 	}
353 	snprintf(buf,80,"white balance delay: %d",speed.control_delay >> 10);
354 	return 0;
355 }
356 
contour_handler(int fd,int dir,char * buf)357 int contour_handler(int fd, int dir,char *buf)
358 {
359 	static u_int16_t contour = 32768;
360 	static int contourmode = 1;
361 	int val;
362 
363 	if(dir == 2) {
364 		if(++contourmode == 2)
365 			contourmode = 0;
366 	}
367 	else if(dir == -1 && contourmode == 0)
368 		contour -= 1024;
369 	else if(dir == 1 && contourmode == 0)
370 		contour += 1024;
371 
372 	if(contourmode == 1)
373 		val = -1;
374 	else
375 		val = contour;
376 
377 	if(v4l1_ioctl(fd,VIDIOCPWCSCONTOUR,&val) == -1)
378 		perror("Failed to set contour");
379 
380 	if(contourmode == 1)
381 		snprintf(buf,80,"contour: auto");
382 	else {
383 		if(v4l1_ioctl(fd,VIDIOCPWCGCONTOUR,&contour) == -1) {
384 			perror("Failed to get contour");
385 			return -1;
386 		}
387 		snprintf(buf,80,"contour: %d",contour >> 10);
388 	}
389 	return 0;
390 }
391 
dynamicnoise_handler(int fd,int dir,char * buf)392 int dynamicnoise_handler(int fd, int dir, char *buf)
393 {
394 	int dynnoise;
395 
396 	if(v4l1_ioctl(fd,VIDIOCPWCGDYNNOISE,&dynnoise) == -1) {
397 		perror("Failed to get current dynamic noise reduction mode");
398 		return -1;
399 	}
400 	if(dir == 2) {
401 		if(++dynnoise == 4)
402 			dynnoise = 0;
403 		if(v4l1_ioctl(fd,VIDIOCPWCSDYNNOISE,&dynnoise) == -1)
404 			perror("Failed to set dynamic noise reduction mode");
405 
406 		if(v4l1_ioctl(fd,VIDIOCPWCGDYNNOISE,&dynnoise) == -1) {
407 			perror("Failed to get new dynamic noise reduction mode");
408 			return -1;
409 		 }
410 	}
411 	snprintf(buf,80,"dnr mode: %d",dynnoise);
412 	return 0;
413 }
414 
backlight_handler(int fd,int dir,char * buf)415 int backlight_handler(int fd, int dir, char *buf)
416 {
417 	int backlight;
418 
419 	if(v4l1_ioctl(fd,VIDIOCPWCGBACKLIGHT,&backlight) == -1) {
420 		perror("Failed to get backlight mode");
421 		return -1;
422 	}
423 	if(dir == 2) {
424 		backlight = !backlight;
425 		if(v4l1_ioctl(fd,VIDIOCPWCSBACKLIGHT,&backlight) == -1)
426 			perror("Failed to set backlight mode");
427 
428 		if(v4l1_ioctl(fd,VIDIOCPWCGBACKLIGHT,&backlight) == -1) {
429 			perror("Failed to get new backlight mode");
430 			return -1;
431 		}
432 	}
433 	snprintf(buf,80,"backlight compensation: %s",backlight ? "on" : "off");
434 	return 0;
435 }
436 
flicker_handler(int fd,int dir,char * buf)437 int flicker_handler(int fd, int dir, char *buf)
438 {
439 	int flicker;
440 
441 	if(v4l1_ioctl(fd,VIDIOCPWCGFLICKER,&flicker) == -1) {
442 		perror("Failed to get flicker mode");
443 		return -1;
444 	}
445 	if(dir == 2) {
446 		flicker = !flicker;
447 		if(v4l1_ioctl(fd,VIDIOCPWCSFLICKER,&flicker) == -1)
448 			perror("Failed to set flicker mode");
449 
450 		if(v4l1_ioctl(fd,VIDIOCPWCGFLICKER,&flicker) == -1) {
451 			perror("Failed to get new flicker mode");
452 			return -1;
453 		}
454 	}
455 	snprintf(buf,80,"anti flicker mode: %s",flicker ? "on" : "off");
456 	return 0;
457 }
458 #ifdef __FreeBSD__
colour_handler(int fd,int dir,char * buf)459 int colour_handler(int fd, int dir, char *buf)
460 {
461 	int colour;
462 
463 	if(v4l1_ioctl(fd,VIDIOCPWCGCOLOUR,&colour) == -1) {
464 		perror("Failed to get colour mode");
465 		return -1;
466 	}
467 	if(dir == 2) {
468 		colour = !colour;
469 		if(v4l1_ioctl(fd,VIDIOCPWCSCOLOUR,&colour) == -1)
470 			perror("Failed to set colour mode");
471 
472 		if(v4l1_ioctl(fd,VIDIOCPWCGCOLOUR,&colour) == -1) {
473 			perror("Failed to get new colour mode");
474 			return -1;
475 		}
476 	}
477 	snprintf(buf,80,"colour mode: %s",colour ? "color" : "black & white");
478 	return 0;
479 }
480 #endif
saveuser_handler(int fd,int dir,char * buf)481 int saveuser_handler(int fd, int dir, char *buf)
482 {
483 	if(dir == 0) {
484 		snprintf(buf,80,"save user settings");
485 	}
486 	else if(dir == 2) {
487 		if(v4l1_ioctl(fd,VIDIOCPWCSUSER) == -1)
488 			snprintf(buf,80,"Error: %s",strerror(errno));
489 		else
490 			snprintf(buf,80,"User settings saved");
491 		return 0;
492 
493 	}
494 	else {
495 		return -1;
496 	}
497 	return 0;
498 }
499 
restoreuser_handler(int fd,int dir,char * buf)500 int restoreuser_handler(int fd, int dir, char *buf)
501 {
502 	if(dir == 0) {
503 		snprintf(buf,80,"restore user settings");
504 	}
505 	else if(dir == 2) {
506 		if(v4l1_ioctl(fd,VIDIOCPWCRUSER) == -1)
507 			snprintf(buf,80,"Error: %s",strerror(errno));
508 		else
509 			snprintf(buf,80,"User settings restored");
510 	}
511 	else {
512 		return -1;
513 	}
514 	return 0;
515 }
516 
restorefactory_handler(int fd,int dir,char * buf)517 int restorefactory_handler(int fd, int dir, char *buf)
518 {
519 	if(dir == 0) {
520 		snprintf(buf,80,"restore factory settings");
521 	}
522 	else if(dir == 2) {
523 		if(v4l1_ioctl(fd,VIDIOCPWCFACTORY) == -1)
524 			snprintf(buf,80,"Error: %s",strerror(errno));
525 		else
526 			snprintf(buf,80,"Factory settings restored");
527 	}
528 	else {
529 		return -1;
530 	}
531 	return 0;
532 }
533 
534 struct pwc_leds led;
ledon_handler(int fd,int dir,char * buf)535 int ledon_handler(int fd, int dir, char *buf)
536 {
537 	v4l1_ioctl(fd,VIDIOCPWCGLED,&led);
538 	if((dir == -1) || (dir == 1)) {
539 		led.led_on += (dir == -1) ? -100 : 100;
540 		if(led.led_on < 0)
541 			led.led_on = 0;
542 		if(v4l1_ioctl(fd,VIDIOCPWCSLED,&led) == -1)
543 			perror("Failed to set leds");
544 	}
545 	snprintf(buf,80,"led on: %d", led.led_on);
546 	return 0;
547 }
548 
ledoff_handler(int fd,int dir,char * buf)549 int ledoff_handler(int fd, int dir, char *buf)
550 {
551 	v4l1_ioctl(fd,VIDIOCPWCGLED,&led);
552 	if((dir == -1) || (dir == 1)) {
553 		led.led_off += (dir == -1) ? -100 : 100;
554 		if(led.led_off < 0)
555 			led.led_off = 0;
556 		if(v4l1_ioctl(fd,VIDIOCPWCSLED,&led) == -1)
557 			perror("Failed to set leds");
558 	}
559 	snprintf(buf,80,"led off: %d", led.led_off);
560 	return 0;
561 }
562 
563 int sdlflags = SDL_RESIZABLE;
564 int bpp = 0;
565 SDL_Rect rect, window;
566 
scale_handler(int fd,int dir,char * buf)567 int scale_handler(int fd, int dir, char *buf)
568 {
569 	const char *scalestr[] = { "none", "double", "full"};
570 	if(dir == 2) {
571 		if(++scale > SCALE_FULL)
572 			scale = SCALE_NONE;
573 		switch(scale) {
574 		case SCALE_NONE:
575 			rect.w = vw.width;
576 			rect.h = vw.height;
577 			break;
578 		case SCALE_DOUBLE:
579 			if(window.w >= (2 * vw.width) && window.h >= (2 * vw.height)) {
580 				rect.w = 2 * vw.width;
581 				rect.h = 2 * vw.height;
582 			}
583 			else {
584 				rect.w = vw.width;
585 				rect.h = vw.height;
586 			}
587 			break;
588 		case SCALE_FULL:
589 			rect.w = window.w;
590 			rect.h = window.h;
591 			break;
592 		}
593 		/* Force Redraw */
594 		if((screen = SDL_SetVideoMode(window.w, window.h, bpp, sdlflags)) == NULL) {
595 			fprintf(stderr,"SDL Failed to set videomode: %s\n", SDL_GetError());
596 			exit(1);
597 		}
598 	}
599 	snprintf(buf,80,"scaling: %s",scalestr[scale]);
600 	return 0;
601 }
602 
motionrecord_handler(int fd,int dir,char * buf)603 int motionrecord_handler(int fd, int dir, char *buf)
604 {
605 	if(dir == 2) {
606 		if(isatty(STDOUT_FILENO)) {
607 			fprintf(stderr,"Error: You must redirect stdout to a file or pipe stdout to another\n"
608 				       "       command when motion detection recording is enabled\n");
609 		}
610 		else
611 			motionrecord = !motionrecord;
612 	}
613 	snprintf(buf,80,"motion record: %s", motionrecord ? "yes" : "no");
614 	return 0;
615 }
616 
cmdinterval_handler(int fd,int dir,char * buf)617 int cmdinterval_handler(int fd, int dir, char *buf)
618 {
619 	if((dir == -1) || (dir == 1)) {
620 		cmdinterval += dir;
621 		if(cmdinterval < 0)
622 			cmdinterval = 0;
623 	}
624 	snprintf(buf,80,"motion command interval: %ld",cmdinterval);
625 	return 0;
626 }
627 
sensitivity_handler(int fd,int dir,char * buf)628 int sensitivity_handler(int fd, int dir, char *buf)
629 {
630 	if((dir == -1) || (dir == 1)) {
631 		sensitivity += (dir == -1) ? -10 : 10;
632 		if(sensitivity & 0x80000000)
633 			sensitivity = 0;
634 		if(sensitivity > 16320)
635 			sensitivity = 16320;
636 	}
637 	snprintf(buf,80,"motion sensitivity: %u",sensitivity);
638 	return 0;
639 }
640 
threshold_handler(int fd,int dir,char * buf)641 int threshold_handler(int fd, int dir, char *buf)
642 {
643 	if((dir == -1) || (dir == 1)) {
644 		threshold += (dir == -1) ? -1 : 1;
645 		if(threshold & 0x80000000)
646 			threshold = 0;
647 		if(threshold > 4800)
648 			threshold = 4800;
649 	}
650 	snprintf(buf,80,"motion threshold: %u",threshold);
651 	return 0;
652 }
653 
recmargin_handler(int fd,int dir,char * buf)654 int recmargin_handler(int fd, int dir, char *buf)
655 {
656 	if((dir == -1) || (dir == 1)) {
657 		recmargin += (dir == -1) ? -1 : 1;
658 		if(recmargin < 0)
659 			recmargin = 0;
660 	}
661 	snprintf(buf,80,"recording margin: %d frame(s)",recmargin);
662 	return 0;
663 }
664 
showmotion_handler(int fd,int dir,char * buf)665 int showmotion_handler(int fd, int dir, char *buf)
666 {
667 	if(dir == 2)
668 		showmotion = !showmotion;
669 	snprintf(buf,80,"motion show : %s", showmotion ? "yes" : "no");
670 	return 0;
671 }
672 
showtime_handler(int fd,int dir,char * buf)673 int showtime_handler(int fd, int dir, char *buf)
674 {
675 	if(dir == 2)
676 		showtime = !showtime;
677 	snprintf(buf,80,"time date string: %s", showtime ? "yes" : "no");
678 	return 0;
679 }
680 
fullscreen_handler(int fd,int dir,char * buf)681 int fullscreen_handler(int fd, int dir, char *buf)
682 {
683 	if(dir == 2)
684 		motionfs = !motionfs;
685 	snprintf(buf,80,"motion fullscreen: %s", motionfs ? "yes" : "no");
686 	return 0;
687 }
688 
beep_handler(int fd,int dir,char * buf)689 int beep_handler(int fd, int dir, char *buf)
690 {
691 	if(dir == 2)
692 		motionbeep = !motionbeep;
693 	snprintf(buf,80,"motion beep: %s", motionbeep ? "yes" : "no");
694 	return 0;
695 }
696 
697 int (*handler[])(int fd, int direction, char *buf) = {  /* direction: -1 = down, 0 = init, 1 = up, 2 = special */
698 	default_handler,
699 	framerate_handler,
700 	brightness_handler,
701 	contrast_handler,
702 	saturation_handler,
703 	gamma_handler,
704 	agc_handler,
705 	shutter_handler,
706 	whitebalance_handler,
707 	whitebalancespeed_handler,
708 	whitebalancedelay_handler,
709 	contour_handler,
710 	dynamicnoise_handler,
711 	backlight_handler,
712 	flicker_handler,
713 #ifdef __FreeBSD__
714 	colour_handler,
715 #endif
716 	compression_handler,
717 	ledon_handler,
718 	ledoff_handler,
719 	saveuser_handler,
720 	restoreuser_handler,
721 	restorefactory_handler,
722 	showtime_handler,
723 	scale_handler,
724 	motionrecord_handler,
725 	fullscreen_handler,
726 	beep_handler,
727 	showmotion_handler,
728 	cmdinterval_handler,
729 	sensitivity_handler,
730 	threshold_handler,
731 	recmargin_handler
732 };
733 
cbtimer(Uint32 interval,void * param)734 Uint32 cbtimer(Uint32 interval, void *param)
735 {
736 	SDL_Event event;
737 	SDL_UserEvent userevent;
738 
739 	userevent.type = SDL_USEREVENT;
740 	userevent.code = 0;
741 	userevent.data1 = NULL;
742 	userevent.data2 = NULL;
743 	event.type = SDL_USEREVENT;
744 	event.user = userevent;
745 	SDL_PushEvent(&event);
746 	return interval;
747 }
748 #endif
749 
sig_chld(int signo)750 void sig_chld(int signo)
751 {
752 	int stat;
753 	while(waitpid(-1, &stat, WNOHANG) > 0)
754 		;
755 }
756 
jpeg_init(int width,int height,int quality,struct jpeg_compress_struct * cinfo,struct jpeg_error_mgr * jerr,JSAMPIMAGE jimage,JSAMPROW y)757 void jpeg_init(int width, int height, int quality, struct jpeg_compress_struct *cinfo,
758 		struct jpeg_error_mgr *jerr, JSAMPIMAGE jimage, JSAMPROW y)
759 {
760 	int i;
761 	JSAMPROW u,v;
762 
763 	cinfo->err = jpeg_std_error(jerr);
764 	jpeg_create_compress(cinfo);
765 
766 	cinfo->image_width = width;
767 	cinfo->image_height = height;
768 	cinfo->input_components = 3;
769 	cinfo->in_color_space = JCS_YCbCr;
770 	jpeg_set_defaults(cinfo);
771 
772 	/* cinfo->dct_method = JDCT_FLOAT; */
773 	cinfo->raw_data_in = TRUE;
774 
775 	cinfo->comp_info[0].h_samp_factor = 2;
776 	cinfo->comp_info[0].v_samp_factor = 2;
777 	cinfo->comp_info[1].h_samp_factor = 1;
778 	cinfo->comp_info[1].v_samp_factor = 1;
779 	cinfo->comp_info[2].h_samp_factor = 1;
780 	cinfo->comp_info[2].v_samp_factor = 1;
781 
782 	jimage[0] = malloc(height * 2 * sizeof(JSAMPROW));
783 	if(jimage[0] == NULL) {
784 		fprintf(stderr,"Error: out of memory\n");
785 		exit(1);
786 	}
787 
788 	jimage[1] = jimage[0] + height;
789 	jimage[2] = jimage[1] + (height/2);
790 
791 	u = y + width * height;
792 	v = u + width * height / 4;
793 
794 	for(i = 0; i < height; ++i, y+=width) {
795 		jimage[0][i] = y;
796 	}
797 	for(i = 0; i < height/2; ++i, u+=width/2, v+=width/2) {
798 		jimage[1][i] = u;
799 		jimage[2][i] = v;
800 	}
801 
802 	jpeg_set_quality(cinfo, quality, TRUE);
803 }
804 
jpeg_write(int height,JSAMPIMAGE jimage,struct jpeg_compress_struct * cinfo,const char * fmt,const char * cmd)805 void jpeg_write(int height, JSAMPIMAGE jimage, struct jpeg_compress_struct *cinfo,
806 		const char *fmt, const char *cmd)
807 {
808         JSAMPARRAY jdata[3];
809 	char filename[1024];
810 	FILE *outfile;
811 	time_t tt;
812 	struct tm *tm;
813 	int i;
814 
815 	tt = time(NULL);
816 	if(tt == (time_t)-1) {
817 		perror("Failed to get time");
818 		return;
819 	}
820 
821 	tm = localtime(&tt);
822 
823 	if(strftime(filename,1024,fmt,tm) == 0) {
824 		fprintf(stderr,"Error: resulting filename to long\n");
825 		return;
826 	}
827 
828 	if ((outfile = fopen(filename, "wb")) == NULL) {
829 		perror("Error opening output file");
830 		return;
831 	}
832 
833 	jdata[0] = jimage[0];
834 	jdata[1] = jimage[1];
835 	jdata[2] = jimage[2];
836 
837 	cinfo->raw_data_in = TRUE;
838 	cinfo->do_fancy_downsampling = FALSE;
839 	jpeg_stdio_dest(cinfo, outfile);
840 	jpeg_start_compress(cinfo, TRUE);
841 
842 	for (i = 0;i < height;i += 2*DCTSIZE) {
843 		jpeg_write_raw_data(cinfo, jdata, 2*DCTSIZE);
844 		jdata[0] += 2*DCTSIZE;
845 		jdata[1] += DCTSIZE;
846 		jdata[2] += DCTSIZE;
847 	}
848 
849 	jpeg_finish_compress(cinfo);
850 	fclose(outfile);
851 
852 	if(cmd != NULL) {
853 		switch(fork()) {
854 		case 0:
855 			dup2(STDERR_FILENO,STDOUT_FILENO);
856 			execlp(cmd,cmd,filename,NULL);
857 			fprintf(stderr,"Failed to execute %s: %s\n",cmd,strerror(errno));
858 			_exit(1);
859 		case -1:
860 			perror("fork failed");
861 			/* Fall through */
862 		default:
863 			break;
864 		}
865 	}
866 }
867 
868 uint8_t motionmask[60][80];
detectmotion(unsigned char * buf,int width,int height)869 int detectmotion(unsigned char *buf, int width, int height)
870 {
871 	static int newbuf;
872 	static int skip = 5;
873 	static uint32_t motionbuf[2][150][200];
874 	static int rectime;
875 	uint32_t diff;
876 	int line, col, motiondetected = 0;
877 	unsigned char *dst, *src = buf;
878 	int oldbuf = !newbuf;
879 
880 	time_t tt;
881 	char *tp = NULL, *pt = NULL;
882 
883 	if(showtime) {
884 		if((tt = time(NULL)) != (time_t)-1) {
885 			pt = ctime(&tt);
886 			pt[25] = '\0';
887 		}
888 	}
889 
890 	memset(motionbuf[newbuf],0,150*200*sizeof(uint32_t));
891 
892 	for(line = 0; line < height; ++line) {
893 		int y = line / 8; tp = pt;
894 		if(line < 8)
895 			dst = buf + line * width;
896 		for(col = 0; col < width; col += 8) {
897 			int x = col / 8;
898 			motionbuf[newbuf][y][x] += *src++;
899 			motionbuf[newbuf][y][x] += *src++;
900 			motionbuf[newbuf][y][x] += *src++;
901 			motionbuf[newbuf][y][x] += *src++;
902 			motionbuf[newbuf][y][x] += *src++;
903 			motionbuf[newbuf][y][x] += *src++;
904 			motionbuf[newbuf][y][x] += *src++;
905 			motionbuf[newbuf][y][x] += *src++;
906 
907 			/* Add time string */
908 			if(tp != NULL && *tp != '\0' && line < 8) {
909 				int i;
910 				unsigned char c = (unsigned char)*tp++;
911 
912 				if(c >= '0' && c <= ':')
913 					c = c - '0' + 1;
914 				else if(c >= 'a' && c <= 'z')
915 					c = c - 'a' + 12;
916 				else  if(c >= 'A' && c <= 'Z')
917 					c = c - 'A' + 38;
918 				else
919 					c = 0;
920 
921 				for(i = 0; i < 7 && c != 0; ++i) {
922 					switch(pixels[c][line][i]) {
923 					case 1: dst[i] = 0;	break;
924 					case 2: dst[i] = 0xFF;	break;
925 					}
926 				}
927 				dst += (c == 0) ? 5 : 7;
928 			}
929 
930 			if((line % 8) != 7 || motionmask[y][x])
931 				continue;
932 
933 			diff = motionbuf[newbuf][y][x] - motionbuf[oldbuf][y][x];
934 			diff = diff & 0x80000000 ? 0xFFFFFFFF - diff : diff;
935 
936 			if(diff > sensitivity && !skip) {
937 				motiondetected++;
938 				if(showmotion) {
939 					uint32_t *p = (uint32_t*)(src - 8);
940 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p -= (width / sizeof(uint32_t));
941 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p -= (width / sizeof(uint32_t));
942 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p -= (width / sizeof(uint32_t));
943 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p -= (width / sizeof(uint32_t));
944 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p -= (width / sizeof(uint32_t));
945 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p -= (width / sizeof(uint32_t));
946 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p -= (width / sizeof(uint32_t));
947 					p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF;
948 				}
949 			}
950 		}
951 	}
952 	if(motiondetected >= threshold) {
953 		rectime = recmargin + 1;
954 #ifndef NOGUI
955 		if(motionfs && !fullscreen) {
956 			SDL_WM_ToggleFullScreen(screen);
957 			fullscreen = !fullscreen;
958 			SDL_WM_GrabInput(fullscreen ? SDL_GRAB_ON : SDL_GRAB_OFF);
959 		}
960 #endif
961 		if(motionbeep)
962 			fprintf(stderr,"\a");
963 
964 		if(motioncmd != NULL) {
965 			static struct timeval start;
966 			struct timeval cur, diff;
967 
968 			gettimeofday(&cur,NULL);
969 			timersub(&cur,&start,&diff);
970 
971 			if(diff.tv_sec > cmdinterval) {
972 				gettimeofday(&start,NULL);
973 
974 				switch(fork()) {
975 				case 0:
976 					dup2(STDERR_FILENO,STDOUT_FILENO);
977 					execlp(motioncmd,motioncmd,NULL);
978 					fprintf(stderr,"Failed to execute %s: %s\n",motioncmd,strerror(errno));
979 					_exit(1);
980 				case -1:
981 					perror("fork failed");
982 					/* Fall through */
983 				default:
984 					break;
985 				}
986 			}
987 		}
988 	}
989 	if(motionrecord && rectime > 0) {
990 		write(STDOUT_FILENO,buf,(width*height*3)/2);
991 		rectime--;
992 	}
993 	if(skip > 0)
994 		--skip;
995 	newbuf = !newbuf;
996 	return 0;
997 }
998 
displaymask(unsigned char * buf,int width,int height)999 void displaymask(unsigned char *buf, int width, int height)
1000 {
1001 	int x, y;
1002 	for(y = 0; y < (height/8); ++y) {
1003 		for(x = 0; x < (width/8); ++x) {
1004 			if(motionmask[y][x]) {
1005 				uint32_t *p = (uint32_t*)(buf + (x * 8) + (y * width * 8));
1006 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p += (width / sizeof(uint32_t));
1007 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p += (width / sizeof(uint32_t));
1008 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p += (width / sizeof(uint32_t));
1009 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p += (width / sizeof(uint32_t));
1010 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p += (width / sizeof(uint32_t));
1011 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p += (width / sizeof(uint32_t));
1012 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF; p += (width / sizeof(uint32_t));
1013 				p[0] ^= 0xFFFFFFFF; p[1] ^= 0xFFFFFFFF;
1014 			}
1015 		}
1016 	}
1017 }
1018 
1019 #ifndef NOGUI
createmask(SDL_Event * event,SDL_Rect * rect,int w,int h)1020 void createmask(SDL_Event *event, SDL_Rect *rect, int w, int h)
1021 {
1022 	static int xstart = -1, ystart = -1;
1023 	double divx = ((double)rect->w / w) * 8;
1024 	double divy = ((double)rect->h / h) * 8;
1025 
1026 	if(xstart == -1) {
1027 		xstart = (int)(event->button.x / divx);
1028 		ystart = (int)(event->button.y / divy);
1029 	}
1030 	else {
1031 		int xlast = (int)(event->button.x / divx);
1032 		int ylast = (int)(event->button.y / divy);
1033 		int x, y;
1034 
1035 		for(y = ystart ;y >= 0 && y < 60 && y <= ylast; y++)
1036 			for(x = xstart; x >= 0 && x < 80 && x <= xlast; x++)
1037 				motionmask[y][x] = (event->button.button == SDL_BUTTON_LEFT) ? 1 : 0;
1038 		xstart = ystart = -1;
1039 	}
1040 }
1041 #endif
1042 
1043 #ifdef PSZ_MAX
1044 #undef PSZ_MAX
1045 #endif
1046 #define PSZ_MAX 10
1047 struct {
1048 	char *name;
1049 	int width;
1050 	int height;
1051 } sizes[PSZ_MAX] = {
1052 	{ "sqcif", 128, 96 },
1053 	{ "qsif", 160, 120 },
1054 	{ "qcif", 176, 144 },
1055 	{ "sif", 320, 240 },
1056 	{ "cif", 352, 288 },
1057 	{ "vga", 640, 480 },
1058 	{ "svga", 800, 600 },
1059 	{ "xga", 1024, 768 },
1060 	{ "sxga", 1280, 1024 },
1061 	{ "uxga", 1600, 1200 }
1062 };
1063 
usage()1064 int usage()
1065 {
1066 	fprintf(stderr,
1067 #ifndef NOGUI
1068 		"Usage: pwcview [ options ]\n\n"
1069 #else
1070 		"Usage: pwcsnap [ options ]\n\n"
1071 #endif
1072 		"Options:\n"
1073 		" -?               Display this help message\n"
1074 #ifndef NOGUI
1075 		" -h               Run in headless mode\n"
1076 #ifdef __FreeBSD__
1077 		" -p               Enable webcam snapshot button\n"
1078 #endif
1079 		" -k               Add time date string to picture\n\n"
1080 		" -x               Create window without frame\n"
1081 		" -y               Use IYUV overlay instead of YV12 overlay\n"
1082 		" -z               Create the video surface in system memory (SDL_SWSURFACE)\n"
1083 		" -a               Always use video surface (SDL_ANYFORMAT)\n"
1084 		" -b <bpp>         Bits per pixel to setup SDL video surface with (default: 0)\n\n"
1085 #endif
1086 		" -r               Enable motion detection recording\n"
1087 		" -m               Show detected motion\n"
1088 		" -j <command>     Command to execute when motion is detected (default: none)\n"
1089 		" -g <interval>    Minimum time in seconds between motion commands (default: 60)\n"
1090 		" -u <sensitivity> Motion detection sensitivity (default: 1000)\n"
1091 		" -t <threshold>   Motion detection threshold (default: 1)\n"
1092 		" -l <frames>      Recording margin (default: 15)\n\n"
1093 		" -c <count>       Number of jpeg snapshots to take (default: 0 (-1=unlimited))\n"
1094 		" -i <interval>    Jpeg snapshot interval in milliseconds (default: 3000)\n"
1095 		" -q <quality>     Quality of jpeg output image (range: 0-100, default: 75)\n"
1096 		" -o <outfile>     Filename for jpeg output (default: /tmp/%%Y%%m%%d%%H%%M%%S.jpg)\n"
1097 		" -e <command>     Command to execute after each snaphot (default: none)\n\n"
1098 		" -d <device>      Video device to open (default: /dev/video0)\n"
1099 		" -s <size>        Video size to use (default: sif)\n"
1100 		" -f <fps>         Video framerate to use (default: 5)\n\n"
1101 		" See the pwcview(1) man page for details\n\n");
1102 
1103 	return 1;
1104 }
1105 
1106 int
main(int argc,char ** argv)1107 main(int argc, char **argv)
1108 {
1109 #ifndef NOGUI
1110 	Uint8 *keylist;
1111 	SDL_Overlay *overlay;
1112 	SDL_Event event;
1113 	SDL_TimerID timerid;
1114 	int initflags = SDL_INIT_VIDEO;
1115 	int format = SDL_YV12_OVERLAY;
1116 	int swsurface = 0;
1117 	int ysize, uvsize, rv;
1118 	unsigned char *u, *v;
1119 	int mode = 0, skipupdate = 0;
1120 	char buf[80];
1121 	Uint32 interval = 3000;
1122 	int headless = 0;
1123 #else
1124 	unsigned int interval = 3000;
1125 	int headless = 1;
1126 #endif
1127 	struct timespec tv;
1128 	struct video_picture vp;
1129 	struct pwc_probe probe;
1130 	const char *device = "/dev/video0";
1131 	unsigned int fps = 5;
1132 
1133 	int fdmask;
1134 	int showmask = 0;
1135 	int snapbtn = 0;
1136 	int snapcnt = 0;
1137 	int frozen = 0;
1138 	int i = 3; /* sizeidx (sif) */
1139 
1140         JSAMPARRAY jdata[3];
1141 	struct jpeg_compress_struct cinfo;
1142 	struct jpeg_error_mgr jerr;
1143 	int quality = 75;
1144 	const char *outfile = "/tmp/%Y%m%d%H%M%S.jpg";
1145 	const char *command = NULL;
1146 
1147 	int fd;
1148 	int imgsize;
1149 	int ch, size;
1150 	unsigned char *y;
1151 
1152 	while((ch = getopt(argc,argv,"yaxhrmkpzl:b:q:o:d:e:f:i:t:u:j:g:c:s:?")) != -1) {
1153 		switch(ch) {
1154 #ifndef NOGUI
1155 		case 'y': format = SDL_IYUV_OVERLAY;		break;
1156 		case 'a': sdlflags |= SDL_ANYFORMAT;		break;
1157 		case 'x': sdlflags |= SDL_NOFRAME;		break;
1158 		case 'h': headless = 1;				break;
1159 #ifdef __FreeBSD__
1160 		case 'p': snapbtn = 1;				break;
1161 #endif
1162 		case 'z': swsurface = 1;			break;
1163 		case 'b': bpp = atoi(optarg);			break;
1164 #endif
1165 		case 'q': quality = atoi(optarg);		break;
1166 		case 'o': outfile = optarg;			break;
1167 		case 'd': device = optarg;			break;
1168 		case 'e': command = optarg;			break;
1169 		case 'r': motionrecord = 1;			break;
1170 		case 'm': showmotion = 1;			break;
1171 		case 'k': showtime = 1;				break;
1172 		case 'l': recmargin = atoi(optarg);		break;
1173 		case 'f': fps = atoi(optarg);			break;
1174 		case 'i': interval = strtoul(optarg,NULL,10);	break;
1175 		case 't': threshold = strtoul(optarg,NULL,10);	break;
1176 		case 'u': sensitivity = strtoul(optarg,NULL,10);break;
1177 		case 'j': motioncmd = optarg;			break;
1178 		case 'g': cmdinterval = strtol(optarg,NULL,10);	break;
1179 		case 'c':
1180 			snapcnt  = atoi(optarg);
1181 			if(snapcnt < -1) {
1182 				fprintf(stderr,"Invalid snapshot count: %d\n",snapcnt);
1183 				return 1;
1184 			}
1185 			break;
1186 
1187 		case 's':
1188 			for(i = 0; i < PSZ_MAX; ++i)
1189 				if(strcmp(sizes[i].name,optarg) == 0)
1190 					  break;
1191 
1192 			if(i == PSZ_MAX) {
1193 				fprintf(stderr,"Invalid size, valid sizes: sqcif, qsif, qcif, sif, cif, vga, svga, xga, sxga, uxga\n");
1194 				return 1;
1195 			}
1196 			break;
1197 
1198 		case '?':
1199 		default:
1200 			return usage();
1201 		}
1202 	}
1203 	if(headless && motionrecord == 1 && snapcnt != 0) {
1204 		fprintf(stderr,"Error: You cannot use both motion detection recording\n"
1205 			       "       and jpeg snapshots in headless mode\n");
1206 		return 1;
1207 	}
1208 	if(motionrecord == 1 && isatty(STDOUT_FILENO)) {
1209 		fprintf(stderr,"Error: You must redirect stdout to a file or pipe stdout to another\n"
1210 			       "       command when motion detection recording is enabled\n");
1211 		return 1;
1212 	}
1213 
1214 	if(fps < 5 || fps > 30) {
1215 		fprintf(stderr,"Invalid framerate, framerate must be in the range 5-30\n");
1216 		return 1;
1217 	}
1218 
1219 	if(!headless && interval < (((1000 / fps)/10)*10))
1220 		interval = (((1000 / fps)/10)*10);
1221 
1222 	vw.width = sizes[i].width;
1223 	vw.height= sizes[i].height;
1224 	vw.flags = fps << PWC_FPS_SHIFT;
1225 
1226 	if((fd = v4l1_open(device, O_RDONLY)) < 0) {
1227 		if(errno == EBUSY)
1228 			fprintf(stderr,"Failed to access webcam: Device in use\n");
1229 		else {
1230 			perror("Failed to access webcam");
1231 			fprintf(stderr,"***********************************************************\n"
1232 #ifdef __FreeBSD__
1233 				       "Make sure you have connected your webcam to the root hub\n"
1234 				       "or to a USB 1.1 hub, also check your dmesg for any errors.\n"
1235 #else
1236 				       "Please check your dmesg for any errors.\n"
1237 #endif
1238 				       "***********************************************************\n");
1239 		}
1240 		exit(1);
1241     	}
1242 	fcntl(fd,F_SETFD,FD_CLOEXEC);
1243 
1244 	if(v4l1_ioctl(fd,VIDIOCGPICT,&vp) == -1) {
1245 		perror("Failed to get current picture info");
1246 		exit(1);
1247 	}
1248 	vp.palette = VIDEO_PALETTE_YUV420P;
1249 	if(v4l1_ioctl(fd,VIDIOCSPICT,&vp) == -1) {
1250 		perror("Failed to set palette to YUV420P");
1251 		exit(1);
1252 	}
1253 
1254 	if(v4l1_ioctl(fd,VIDIOCSWIN,&vw) == -1) {
1255 		fprintf(stderr,"Failed to set webcam to: %dx%d (%s) at %d fps (%s)\n",
1256 				vw.width,vw.height,sizes[i].name,fps,strerror(errno));
1257 		exit(1);
1258 	}
1259 	fprintf(stderr,"Webcam set to: %dx%d (%s) at %d fps\n",vw.width,vw.height,sizes[i].name,fps);
1260 	imgsize = (vw.width * vw.height * 3)/2;
1261 
1262 	if(headless && snapcnt == 0 && motionrecord == 0) { /* Done */
1263 		v4l1_close(fd);
1264 		exit(0);
1265 	}
1266 	if(snapbtn) {
1267 		snapbtn = 0;
1268 		if(v4l1_ioctl(fd,VIDIOCPWCPROBE,&probe) != -1 &&
1269 		   probe.type >= 720 && probe.type <= 740)
1270 				snapbtn = 1;
1271 	}
1272 
1273 	if((fdmask = open("pwcview.msk",O_RDONLY)) != -1) {
1274 		read(fdmask,motionmask,60*80*sizeof(uint8_t));
1275 		close(fdmask);
1276 		fprintf(stderr,"motion mask loaded\n");
1277 	}
1278 
1279 	y = malloc(imgsize);
1280 	if(y == NULL) {
1281 		perror("Out of memory");
1282 		exit(1);
1283 	}
1284 	jpeg_init(vw.width,vw.height,quality,&cinfo,&jerr,jdata,(JSAMPROW)y);
1285 
1286 	if(command != NULL ||motioncmd != NULL)
1287 		signal(SIGCHLD,sig_chld);
1288 
1289 #ifndef NOGUI
1290 	if(!headless) {
1291 
1292 		window.w = vw.width;
1293 		window.h = vw.height;
1294 		rect.w = vw.width;
1295 		rect.h = vw.height;
1296 		ysize = vw.width * vw.height;
1297 		uvsize = ysize / 4;
1298 
1299 		if(format == SDL_IYUV_OVERLAY) {
1300 			u = y + ysize;
1301 			v = u + uvsize;
1302 		}
1303 		else {
1304 			v = y + ysize;
1305 			u = v + uvsize;
1306 		}
1307 
1308 		if(snapcnt != 0)
1309 			initflags |= SDL_INIT_TIMER;
1310 
1311 		if (SDL_Init(initflags) < 0) {
1312 			fprintf(stderr,"Failed to init sdl: %s\n", SDL_GetError());
1313 			exit(1);
1314 		}
1315 
1316 		atexit(SDL_Quit);
1317 
1318 		sdlflags |= swsurface ? SDL_SWSURFACE : SDL_HWSURFACE;
1319 		if((screen = SDL_SetVideoMode(window.w, window.h, bpp, sdlflags)) == NULL) {
1320 			fprintf(stderr,"SDL Failed to set videomode: %s\n", SDL_GetError());
1321 			exit(1);
1322 		}
1323 		if((overlay = SDL_CreateYUVOverlay(vw.width, vw.height, format, screen)) == NULL) {
1324 			fprintf(stderr,"Failed to create yuvoverlay: %s\n", SDL_GetError());
1325 			exit(1);
1326 		}
1327 		SDL_DisplayYUVOverlay(overlay, &rect);
1328 
1329 		snprintf(buf,80,"pwcview");
1330 		keylist = SDL_GetKeyState(NULL);
1331 
1332 		if(snapcnt != 0)
1333 			timerid = SDL_AddTimer(interval,cbtimer,NULL);
1334 	}
1335 #endif
1336 	while (frozen || ((size = v4l1_read(fd,y,imgsize)) > 0) || (size == -1 && errno == EINTR)) {
1337 		int snap = y[0] & 0x01;
1338 		if(!frozen && size != imgsize) {
1339 			if(size != -1) {
1340 				fprintf(stderr,"Warning short read, got only %d of %d bytes\n",size,imgsize);
1341 			}
1342 			continue;
1343 		}
1344 		if(!frozen && (motionrecord||showmotion||showtime||motionfs||motionbeep||motioncmd))
1345 			detectmotion(y,vw.width,vw.height);
1346 		if(showmask)
1347 			displaymask(y, vw.width, vw.height);
1348 
1349 		if(headless) {
1350 			if(motionrecord)
1351 				continue;
1352 
1353 			jpeg_write(vw.height,jdata,&cinfo,outfile,command);
1354 			if(snapcnt > 0)
1355 				snapcnt--;
1356 			if(snapcnt == 0)
1357 				exit(0);
1358 
1359 			tv.tv_sec = interval / 1000;
1360 			tv.tv_nsec = (interval % 1000) * 1000000;
1361 
1362 			while(nanosleep(&tv,&tv) == -1 && errno == EINTR)
1363 				;
1364 
1365 			continue;
1366 		}
1367 		if(snapbtn && snap)
1368 			jpeg_write(vw.height,jdata,&cinfo,outfile,command);
1369 
1370 #ifndef NOGUI
1371 
1372 		SDL_LockYUVOverlay(overlay);
1373 		memcpy(overlay->pixels[0],y,ysize);
1374 		memcpy(overlay->pixels[1],u,uvsize);
1375 		memcpy(overlay->pixels[2],v,uvsize);
1376 		SDL_UnlockYUVOverlay(overlay);
1377 		SDL_DisplayYUVOverlay(overlay, &rect);
1378 
1379 		SDL_PumpEvents();
1380 
1381 		if(skipupdate == 0) {
1382 			if(showmask)
1383 				SDL_WM_SetCaption("edit motion mask:", "edit motion mask:");
1384 			else
1385 				SDL_WM_SetCaption(buf,buf);
1386 		}
1387 		skipupdate = 1;
1388 
1389 		if(keylist[SDLK_RIGHT]) {
1390 			skipupdate = handler[mode](fd,1,buf);
1391 			continue;
1392 		}
1393 		else if(keylist[SDLK_LEFT]) {
1394 			skipupdate = handler[mode](fd,-1,buf);
1395 			continue;
1396 		}
1397 
1398 		if(frozen)
1399 			rv = SDL_WaitEvent(&event);
1400 		else
1401 			rv = SDL_PollEvent(&event);
1402 
1403 		if(rv) {
1404 			do {
1405 				if(event.type == SDL_KEYDOWN) {
1406 					switch(event.key.keysym.sym) {
1407 					case SDLK_DOWN:
1408 						if(mode != sizeof(handler)/sizeof(handler[0]) - 1)
1409 							mode++;
1410 						else
1411 							mode = 0;
1412 						skipupdate = handler[mode](fd,0,buf);
1413 						break;
1414 					case SDLK_UP:
1415 						if(mode != 0)
1416 							mode--;
1417 						else
1418 							mode =  sizeof(handler)/sizeof(handler[0]) - 1;
1419 						skipupdate = handler[mode](fd,0,buf);
1420 						break;
1421 					case SDLK_RETURN:
1422 						skipupdate = handler[mode](fd,2,buf);
1423 						break;
1424 					case SDLK_f:
1425 						SDL_WM_ToggleFullScreen(screen);
1426 						fullscreen = !fullscreen;
1427 						SDL_WM_GrabInput(fullscreen ? SDL_GRAB_ON : SDL_GRAB_OFF);
1428 						break;
1429 					case SDLK_m:
1430 						showmask = !showmask;
1431 						skipupdate = 0;
1432 						break;
1433 					case SDLK_w:
1434 						if((fdmask = open("pwcview.msk",O_CREAT|O_TRUNC|O_WRONLY,0644)) != -1) {
1435 							write(fdmask,motionmask,60*80*sizeof(uint8_t));
1436 							close(fdmask);
1437 							fprintf(stderr,"motion mask saved\n");
1438 						}
1439 						break;
1440 					case SDLK_p:
1441 						jpeg_write(vw.height,jdata,&cinfo,outfile,command);
1442 						break;
1443 					case SDLK_SPACE:
1444 						frozen = !frozen;
1445 						break;
1446 					case SDLK_q:
1447 						exit(0);
1448 					default:
1449 						break;
1450 
1451 					}
1452 				}
1453 				else if(event.type == SDL_VIDEORESIZE) {
1454 					window.w = event.resize.w;
1455 					window.h = event.resize.h;
1456 					if(scale == SCALE_FULL) {
1457 						rect.w = window.w;
1458 						rect.h = window.h;
1459 					}
1460 					else if(scale == SCALE_DOUBLE) {
1461 						if(window.w >= (2 * vw.width) && window.h >= (2 * vw.height)) {
1462 							rect.w = 2 * vw.width;
1463 							rect.h = 2 * vw.height;
1464 						}
1465 						else {
1466 							rect.w = vw.width;
1467 							rect.h = vw.height;
1468 						}
1469 					}
1470 					if((screen = SDL_SetVideoMode(window.w, window.h, bpp, sdlflags)) == NULL) {
1471 						fprintf(stderr,"SDL Failed to set videomode: %s\n", SDL_GetError());
1472 						exit(1);
1473 					}
1474 				}
1475 				else if(event.type == SDL_USEREVENT) {
1476 					jpeg_write(vw.height,jdata,&cinfo,outfile,command);
1477 
1478 					if(snapcnt > 0)
1479 						snapcnt--;
1480 					if(snapcnt == 0)
1481 						SDL_RemoveTimer(timerid);
1482 				}
1483 				else if(event.type == SDL_MOUSEBUTTONDOWN) {
1484 					if(showmask) {
1485 						if(event.button.button == SDL_BUTTON_MIDDLE)
1486 							memset(motionmask,0,60*80*sizeof(uint8_t));
1487 						else
1488 							createmask(&event,&rect,vw.width,vw.height);
1489 					}
1490 					else {
1491 						SDL_WM_ToggleFullScreen(screen);
1492 						fullscreen = !fullscreen;
1493 						SDL_WM_GrabInput(fullscreen ? SDL_GRAB_ON : SDL_GRAB_OFF);
1494 					}
1495 				}
1496 				else if(event.type == SDL_QUIT) {
1497 					exit(0);
1498 				}
1499 			}while(!frozen && SDL_PollEvent(&event));
1500 		}
1501 #endif
1502 	}
1503 
1504 	if(size != 0)
1505 		perror("Error reading from webcam");
1506 
1507 	v4l1_close(fd);
1508 	jpeg_destroy_compress(&cinfo);
1509 	return 0;
1510 }
1511