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