Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

CameraDriverV4L1.cc

Go to the documentation of this file.
00001 #if 0
00002 #ifdef __linux__
00003 
00004 #include "CameraDriverV4L1.h"
00005 #include "Shared/debuget.h"
00006 //#include "Shared/TimeET.h"
00007 #include "Shared/MarkScope.h"
00008 
00009 #include <fcntl.h>
00010 #include <sys/ioctl.h>
00011 
00012 using namespace std; 
00013 
00014 const std::string CameraDriverV4L1::autoRegisterCameraDriverV4L1 = DeviceDriver::getRegistry().registerType<CameraDriverV4L1>("CameraV4L1");
00015 const size_t CameraDriverV4L1::HEADER_SIZE = LoadSave::getSerializedSize<unsigned int>()*4;
00016 
00017 unsigned int CameraDriverV4L1::getData(const char *& payload, unsigned int& payloadSize, unsigned int& timestamp, std::string& name) {
00018   //cout << '.' << flush;
00019   payload=NULL;
00020   payloadSize=0;
00021   if(camfd<0)
00022     return frameCount;
00023 
00024   curBuf.resize(getBufferSize());
00025   
00026   // reset non-blocking in case thread was canceled while we were
00027   // were in a blocking call...
00028   if(blocking) {
00029     if( fcntl(camfd,F_SETFL,O_NONBLOCK) != 0) {
00030       perror("ioctl set non-blocking mode");
00031       blocking = fcntl(camfd,F_GETFL) & O_NONBLOCK;
00032     } else
00033       blocking=false;
00034   }
00035 
00036   unsigned int t=get_time();
00037   unsigned int dropped=0;
00038   int nbytes;
00039   size_t lastread;
00040   while(timestamp>t) {
00041     //clear any backlog
00042     while( (nbytes=read(camfd, &curBuf[HEADER_SIZE], curBuf.size()-HEADER_SIZE)) >= 0) {
00043       lastread=nbytes;
00044       ++dropped;
00045       //cout << "clear " << TimeET() << endl;
00046     }
00047     usleep(static_cast<unsigned int>((timestamp-t)*1000/(getTimeScale()>0?getTimeScale():1.f)));
00048     t=get_time();
00049   }
00050   timestamp = t;
00051   // we might've been asleep for a while, so need to double check a few things...
00052   if(camfd<0) // in case we shutdown while asleep!
00053     return frameCount;
00054 
00055   unsigned int width, height, components=3;
00056   {
00057     MarkScope l(lock);
00058     width=getWidth();
00059     height=getHeight();
00060     if(curBuf.size()!=getBufferSize()) { // check in case user changed resolution
00061       curBuf.resize(getBufferSize());
00062       dropped=0;
00063     }
00064   }
00065 
00066   //get most recent image
00067   while( (nbytes=read(camfd, &curBuf[HEADER_SIZE], curBuf.size()-HEADER_SIZE)) >= 0) {
00068     lastread=nbytes;
00069     ++dropped;
00070     //cout << "early " << TimeET() << endl;
00071   }
00072   timestamp = get_time();
00073 
00074   char * buf=reinterpret_cast<char*>(&curBuf[0]);
00075   unsigned int remain=curBuf.size();
00076   LoadSave::encodeIncT(*layer,buf,remain);
00077   LoadSave::encodeIncT(width,buf,remain);
00078   LoadSave::encodeIncT(height,buf,remain);
00079   LoadSave::encodeIncT(components,buf,remain);
00080 
00081   if(remain<img_size) {
00082     // user has changed resolution... skip frame
00083     //cerr << "Not enough space in buffer for image! " << remain << " vs " << img_size << endl;
00084     return frameCount;
00085   }
00086   if(dropped==0 || lastread!=img_size) {
00087     ASSERTRETVAL(static_cast<size_t>(remain)>=img_size, "Read more than remains in buffer!", frameCount);
00088     // disable non-blocking io, we want to wait for the next frame
00089     if( fcntl(camfd,F_SETFL,0) != 0) {
00090       perror("ioctl set blocking mode");
00091       blocking = fcntl(camfd,F_GETFL) & O_NONBLOCK;
00092     } else
00093       blocking=true;
00094     //TimeET bt;
00095     //cout << "block " << bt << ' ';
00096     nbytes = read(camfd, buf, remain);
00097     timestamp = get_time();
00098     //cout << bt.Age() << endl;
00099     if( fcntl(camfd,F_SETFL,O_NONBLOCK) != 0) {
00100       perror("ioctl set non-blocking mode");
00101       blocking = fcntl(camfd,F_GETFL) & O_NONBLOCK;
00102     } else
00103       blocking=false;
00104     if ( nbytes<0 ) {
00105       perror("Error reading from camera");
00106       return frameCount;
00107     }
00108     lastread=nbytes;
00109     ++dropped;
00110   }
00111   
00112   MarkScope l(lock);
00113   // check if we had a short read or if the desired image size changed while we were blocked
00114   if ( lastread!=img_size // short read
00115        || (resolution!=0 && img_size!=width*height*6) // size changed (downsample)
00116        || (resolution==0 && img_size!=width*height*3/2) // size changed (upsample)
00117      )
00118   {
00119     // short read is relatively normal -- always get this on the first frame for
00120     // some reason, and also might have just changed resolution setting and hasn't
00121     // taken effect yet.
00122     //cerr << "CameraDriverV4L1 got " << nbytes << " bytes from camera, expected " << img_size << endl;
00123     return frameCount;
00124   }
00125 
00126   if(resolution==0)
00127     interleave_yuv_up();
00128   else
00129     interleave_yuv_down();
00130   
00131   if ( autobright )
00132     auto_bright();
00133   
00134   curBuf.swap(lastBuf);
00135   payload = reinterpret_cast<char*>(&lastBuf[0]);
00136   payloadSize = lastBuf.size();
00137   name = nextName();
00138   return frameCount+=dropped;
00139 }
00140 
00141 
00142 void CameraDriverV4L1::setDataSourceThread(LoadDataThread* th) {
00143   DataSource::setDataSourceThread(th);
00144   if(th != NULL) {
00145     open_cam();
00146     path.addPrimitiveListener(this);
00147     resolution.addPrimitiveListener(this);
00148   } else {
00149     resolution.removePrimitiveListener(this);
00150     path.removePrimitiveListener(this);
00151     close_cam();
00152   }
00153 }
00154 
00155 void CameraDriverV4L1::plistValueChanged(const plist::PrimitiveBase& pl) {
00156   if(&pl == &path) {
00157     MarkScope l(lock);
00158     open_cam();
00159   } else if(&pl == &resolution) {
00160     MarkScope l(lock);
00161     set_resolution();
00162   }
00163 }
00164 
00165 void CameraDriverV4L1::close_cam() {
00166   if(camfd >= 0) {
00167     ::close(camfd);
00168     camfd=-1;
00169   }
00170 }
00171 
00172 void CameraDriverV4L1::open_cam() {
00173   close_cam();
00174   if(path.size() == 0)
00175     return;
00176   
00177   camfd = ::open(path.c_str(), O_RDWR|O_NONBLOCK, 0);
00178   if ( camfd < 0 ) {
00179     perror("Error on open()");
00180     cerr << "Could not open camera device '" << path << "'" << endl;
00181     return;
00182   }
00183   blocking=false;
00184   
00185   set_resolution();
00186 
00187   if ( ioctl(camfd, VIDIOCGPICT, &vid_pic) == -1 )
00188     perror ("ioctl (VIDIOCGPICT) brightness");
00189   vid_pic.brightness=128*256; // initialize to middle brightness
00190   if ( ioctl(camfd, VIDIOCSPICT, &vid_pic) == -1 )
00191     perror ("ioctl (VIDIOCSPICT) brightness");
00192 
00193   // try to switch to YUV mode so we don't have to do color conversion
00194   // this is only trying for 4-2-0 planar mode...
00195   isYUVMode=false;
00196   ioctl(camfd, VIDIOCGPICT, &vid_pic);
00197   vid_pic.depth=16;
00198   vid_pic.palette=VIDEO_PALETTE_YUV420P;
00199   if ( ioctl(camfd, VIDIOCSPICT, &vid_pic) == -1 )
00200     perror ("ioctl (VIDIOCSPICT) palette");
00201   else {
00202     img_size = vid_win.width * vid_win.height + vid_win.width*vid_win.height/2;
00203     isYUVMode=true;
00204   }
00205   
00206   /*{
00207     struct v4l2_control ctrl;
00208     ctrl.id=V4L2_CID_AUTO_WHITE_BALANCE;
00209     ctrl.value=0;
00210     if ( ioctl(camera->fd,VIDIOC_S_CTRL,&ctrl) == -1 )
00211       perror ("ioctl (VIDIOC_S_CTRL) disable auto white balance");
00212   } */ 
00213 }
00214 
00215 void CameraDriverV4L1::get_cam_info() {
00216   ioctl(camfd, VIDIOCGCAP, &vid_caps);
00217   ioctl(camfd, VIDIOCGWIN, &vid_win);
00218   ioctl(camfd, VIDIOCGPICT, &vid_pic);
00219 }
00220 
00221 void CameraDriverV4L1::set_cam_info() {
00222   if ( ioctl(camfd, VIDIOCSPICT, &vid_pic) == -1 ) {
00223     perror ("ioctl (VIDIOCSPICT)");
00224     cout << "refreshing settings..." << endl;
00225     if ( ioctl(camfd, VIDIOCGPICT, &vid_pic) == -1 )
00226       perror ("ioctl (VIDIOCGPICT)");
00227   }
00228   if ( ioctl(camfd, VIDIOCSWIN, &vid_win) == -1 ) {
00229     perror ("ioctl (VIDIOCSWIN)");
00230     cout << "refreshing settings..." << endl;
00231     if ( ioctl(camfd, VIDIOCGWIN, &vid_win) == -1 )
00232       perror ("ioctl (VIDIOCGWIN)");
00233   }
00234 }
00235 
00236 void CameraDriverV4L1::set_resolution() {
00237   get_cam_info();
00238   int width = vid_caps.maxwidth >> (resolution==0 ? 0 : resolution-1);
00239   int height = vid_caps.maxheight >> (resolution==0 ? 0 : resolution-1);
00240   if ( width < vid_caps.minwidth || height < vid_caps.minheight ) {
00241     cout << "Warning: requested image " << width<<'x'<<height<<" smaller than camera's minimum size, trying ";
00242     width = vid_caps.minwidth;
00243     height = vid_caps.minheight;
00244     cout << width<<'x'<<height << endl;
00245   }
00246 
00247   vid_win.width = width;
00248   vid_win.height = height;
00249   set_cam_info();
00250   width = vid_win.width; // restore in case of error or adjustment of requested values
00251   height = vid_win.height; // (camera might only support certain explicit resolutions)
00252   
00253   if(isYUVMode) {
00254     img_size = width * height + width * height / 2;
00255   } else {
00256     img_size = width * height * 3;
00257   }
00258     
00259   cout << "Camera image size is " << getWidth() << 'x' << getHeight() << endl;
00260 }
00261 
00262 /*! this is run @em after interleaving, so we go through every 3rd byte of the
00263  *  entire image to sum the y channel */
00264 void CameraDriverV4L1::auto_bright() {
00265   size_t total = 0;
00266   unsigned char *pix = &curBuf[HEADER_SIZE];
00267   const size_t skip = getWidth() * getHeight() / 719; // actually, only take a subsample
00268   const size_t npix = getWidth() * getHeight() / skip; // we want a regular sample stride, so will actually be a bit less
00269   const unsigned char *endp = pix + npix*skip*3;
00270   for (; pix!=endp; pix+=skip*3) // *3 because of interleaving
00271     total += *pix;
00272   int average = total / npix;
00273   if((average <= 120 || average >= 136)) {
00274     int bright = vid_pic.brightness + (128 - average)*256/4;
00275     const typeof(vid_pic.brightness) maxBright = numeric_limits<typeof(vid_pic.brightness)>::max();
00276     const typeof(vid_pic.brightness) minBright = numeric_limits<typeof(vid_pic.brightness)>::min();
00277     if(bright>maxBright)
00278       vid_pic.brightness = maxBright;
00279     else if(bright<minBright)
00280       vid_pic.brightness = minBright;
00281     else
00282       vid_pic.brightness = bright;
00283     //cout << "Autobrightness " << average << " " << vid_pic.brightness << endl;
00284     set_cam_info();
00285   }
00286 }
00287 
00288 void CameraDriverV4L1::interleave_yuv_down() {
00289   unsigned char * buffer = &curBuf[HEADER_SIZE];
00290   int width = vid_win.width;
00291   int height = vid_win.height;
00292   
00293   //first downsample y channel
00294   char tmp[width/2];
00295   int x,y=0;
00296   for(x=0; x<width; x+=2) {
00297     short t=buffer[x+y*width];
00298     t+=buffer[x+y*width+1];
00299     t+=buffer[x+y*width+width];
00300     t+=buffer[x+y*width+width+1];
00301     tmp[x/2] = t/4;
00302   }
00303   for(x=0; x<width/2; x++) {
00304     buffer[x*3] = tmp[x];
00305   }  
00306   for(y=2; y<height; y+=2) {
00307     for(x=0; x<width; x+=2) {
00308       short t=buffer[x+y*width];
00309       t+=buffer[x+y*width+1];
00310       t+=buffer[x+y*width+width];
00311       t+=buffer[x+y*width+width+1];
00312       buffer[y*width/4*3+x/2*3] = t/4;
00313     }
00314   }
00315   // now fill in color components
00316   unsigned char * c = &buffer[width*height];
00317   buffer = &curBuf[HEADER_SIZE]+1;
00318   unsigned char * endp = buffer + width*height/4*3;
00319   while(buffer!=endp) { // u/Cr channel
00320     *buffer = *c++;
00321     buffer+=3;
00322   }
00323   buffer = &curBuf[HEADER_SIZE]+2;
00324   endp = buffer + width*height/4*3;
00325   while(buffer!=endp) { // v/Cb channel
00326     *buffer = *c++;
00327     buffer+=3;
00328   }
00329 }
00330 
00331 void CameraDriverV4L1::interleave_yuv_up() {
00332   unsigned char * buffer = &curBuf[HEADER_SIZE];
00333   size_t width = vid_win.width;
00334   size_t height = vid_win.height;
00335   tmpBuf.resize(getBufferSize());
00336   memcpy(&tmpBuf[0],&curBuf[0],HEADER_SIZE);
00337   unsigned char * out = &tmpBuf[HEADER_SIZE];
00338 
00339   //first copy over y channel
00340   unsigned char * i = buffer;
00341   unsigned char * o = out;
00342   unsigned char * e = i + width*height;
00343   while(i!=e) {
00344     *o=*i++;
00345     o+=3;
00346   }
00347   
00348   //now u channel
00349   o = out+1;
00350   buffer+=width*height;
00351   for(size_t y=0; y<height/2; ++y) {
00352     i = buffer+width/2*y;
00353     e = i + width/2;
00354     while(i!=e) {
00355       *o=*(o+3)=*(o+width*3)=*(o+width*3+3)=*i++;
00356       o+=6;
00357     }
00358     o+=width*3;
00359   }
00360   
00361   //and now v channel
00362   o = out+2;
00363   buffer+=width*height/4;
00364   for(size_t y=0; y<height/2; ++y) {
00365     i = buffer+width/2*y;
00366     e = i + width/2;
00367     while(i!=e) {
00368       *o=*(o+3)=*(o+width*3)=*(o+width*3+3)=*i++;
00369       o+=6;
00370     }
00371     o+=width*3;
00372   }
00373   
00374   curBuf.swap(tmpBuf);
00375 }
00376  
00377 
00378 /*! @file
00379  * @brief 
00380  * @author Ethan Tira-Thompson (ejt) (Creator)
00381  */
00382 
00383 #endif
00384 #endif

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:37 2016 by Doxygen 1.6.3