/* Little deamon to take a picture every few seconds syntax vidcapd0 [-t time] [-q quality] [-j] [-m thresh] filename and more; see usage() */ #include #include #include #include #include #include #include #include //#include #include #include #include #include #include "vidcapd0.h" #include "videodev.h" /* these globals contain the app default values */ int repeat_rate = 0; int capture_width = 352; int capture_height = 240; char* filename = "/tmp/vidcap.jpg"; FILE* fout = stdout; int jpeg_Q = 0; int debug = 0; static int vga_initted = 0; int motion_threshold = 5; int nBits; unsigned char* pBits; int check_motion(int h_vidcap); void usage(void) { printf("VIDCAP2: capture an images and write it to a JPEG file\n" "syntax: vidcap2 [-h] [-r rate] [-w width] [-q Qfactor] [-d] \n" " [-b brighness] [-c contrast] filename\n" " -h help -d debug (ni)\n" " -r rate in secs -w width in pixels\n" " -b brightness[10-240] (ni)\n" " -c contrast [30-90] (ni)\n" " -m motion threshold -p soundfile (ni)\n" " (ni) not implemented\n"); } int main(int argc, char** argv) { int i; int r; /* return values */ if (argc==1) { usage(); exit(1); } while ((i = getopt(argc, argv, "dhr:w:q:m:")) != EOF) { switch (i) { case 'd': debug++; break; case 'r': repeat_rate = atoi(optarg); if (repeat_rate<0 || repeat_rate>10000) { printf("The -r parameter must be between 1 and 10000 seconds; \n" "use 0 to force a single image\n"); exit(1); } break; case 'w': capture_width = atoi(optarg); if (capture_width<10 || capture_width>748) { printf("The -w parameter must be between 10 and 748\n"); exit(1); } capture_height = capture_width * _HEIGHT / _WIDTH; break; case 'q': jpeg_Q = atoi(optarg); break; case 'h': default: usage(); exit(1); } } /* I need a temp file in order to pass the image to cjpeg so, I take the given filename, and add a tilde to make my temp file */ if (argv[optind] && argv[optind][0]) { filename = argv[optind]; char buf[200]; strcpy(buf, filename); strcat(buf, "~"); fout = fopen(buf,"w"); if (fout == 0) { fprintf(stderr, "problem opening the file %s; aborted\n",buf); exit(2); } } /* open the vidcap; this could also have O_NONBLOCK, if you wish */ int h_vidcap = open("/dev/video0",O_RDONLY); if (h_vidcap<0) { printf("error: cannot open the video capture device\n"); exit(1); } /* set up the capture session */ struct video_format vf; vf.width = capture_width; vf.height = vf.width * 7 / 10; /* this save the user from having to specify height */ vf.depth = 24; vf.pixelformat = PIX_FMT_RGB24; vf.flags = 0; r = ioctl(h_vidcap, VIDIOC_S_FMT, &vf); // printf("ioctl returned %d, width %d height %d\n",r,vf.width,vf.height); /* create a buffer to hold the bitmap */ bitmap bm(vf.width, vf.height, 3); /* just a quick read, which will tell us if we are in X the data is thrown away */ r = read(h_vidcap, bm.GetBits(), bm.Size()); if (r<0 && errno!=EAGAIN) { printf("error: this program must be executed from within a graphics environment\n" "such as Xwindows or svgalib\n"); exit(1); } do { /* loop reading until we get a full image */ do { r = read(h_vidcap, bm.GetBits(), bm.Size()); } while(r<0 && errno==EAGAIN); if (r<0) { printf("error reading capture file\n"); continue; } /* transfer from capture format (BGR) to ppm format (RGB) */ jpeg* jpg_current = bmp2jpg(&bm,jpeg_Q); /* save the .ppm file, and call cjpeg to make our jpeg file */ write_jpeg(jpg_current, fout); delete jpg_current; /* just a cutsie demo; delete this eventually */ check_motion(h_vidcap); sleep (repeat_rate); } while (repeat_rate); } /* save the RGB data in the temp file, then call cjpeg for a conversion */ void write_jpeg(jpeg* jp, FILE* fout) { fseek(fout, 0, SEEK_SET); fwrite(jp->Bits(),1,jp->Len(),fout); fflush(fout); char buf[200]; sprintf(buf,"cjpeg %s~ > %s",filename,filename); system(buf); } /* change the native RGB format (which is actually blue-green-red) to the Unix .ppm RGB format (which is actually red-green-blue) */ jpeg* bmp2jpg(bitmap* pbm, int jpeg_Q_factor) { /* a meg */ unsigned char* pBig = new unsigned char[pbm->Width() * pbm->Height() * 3 * 4]; unsigned char* pWalk = pBig; unsigned char* pSrc = (unsigned char*) pbm->GetBits(); if (pBig==0) { printf("error: ran out of memory\n"); exit(2); } sprintf(pWalk, "P6\n%d %d\n255\n",pbm->Width(),pbm->Height()); pWalk = pWalk + strlen(pWalk); int i,j; // printf("moving %d pixels\n",pbm->Width()*pbm->Height()); // printf("bytes source %p\n",pSrc); for (i=0; iHeight(); i++) { for (j=0; jWidth(); j++) { unsigned long p1, p2; // YUV2RGB(*pSrc++, &p1, &p2); // we have to reverse the rgb for Unix ppm *pWalk++ = pSrc[2]; *pWalk++ = pSrc[1]; *pWalk++ = pSrc[0]; pSrc += 3; } } pWalk = stpcpy(pWalk, "\n"); jpeg* jp = new jpeg(pBig, pWalk-pBig); // printf("writing %d %d bytes\n",pWalk-pBig,jp->Len()); return jp; } int check_motion(int h_vidcap) { struct video_format vf; static bitmap* old_bm = 0; /* take note of current capture format */ ioctl(h_vidcap, VIDIOC_G_FMT, &vf); /* temporarily change it to YUYV */ vf.pixelformat = PIX_FMT_YUYV; ioctl(h_vidcap, VIDIOC_S_FMT, &vf); int r; static int nQuiets = 5; /* sample the video */ bitmap* bm = new bitmap(vf.width, vf.height, 2); do { r = read(h_vidcap, bm->GetBits(), bm->Size()); } while (r<0 && errno==EAGAIN); /* compare the new video to the old video */ if (old_bm) { int i,j,acc; unsigned char* pBits = bm->GetBits(); unsigned char* pOldBits = old_bm->GetBits(); acc = 0; /* only compare the Y bytes, which are the even-number bytes */ for (i=0; imotion_threshold) acc++; } } delete old_bm; // make a bar chart for (i=0; i < acc * 120 / bm->Size(); i++) printf("*"); printf("\n"); /* coarse threshold */ if (acc > bm->Size()/12) { if (nQuiets>4) system("cat /etc/welcome.au >/dev/audio"); nQuiets = 0; } /* fine threshold */ else if (acc > bm->Size()/30) { if ((rand() & 15) == 15) system("bplay ./seeyou.wav &>/dev/null"); nQuiets = 0; } else { nQuiets++; if (nQuiets==3) system("bplay ./goaway.wav &>/dev/null"); } // printf("thresh %d, acc %d, nbits %d\n",motion_threshold, acc, bm->Size()/2); } old_bm = bm; /* and restore the capture format */ vf.pixelformat = PIX_FMT_RGB24; ioctl(h_vidcap, VIDIOC_S_FMT, &vf); }