// %flair:license{ // This file is part of the Flair framework distributed under the // CECILL-C License, Version 1.0. // %flair:license} // created: 2014/07/17 // filename: V4LCamera_impl.cpp // // author: Guillaume Sanahuja // Copyright Heudiasyc UMR UTC/CNRS 7253 // // version: $Id: $ // // purpose: base class for V4l camera // // /*********************************************************************/ #include "V4LCamera_impl.h" #include "V4LCamera.h" #include #include #include #include #include #include #include //#include #include #include #include #include #include using std::string; using namespace flair::core; using namespace flair::gui; using namespace flair::sensor; V4LCamera_impl::V4LCamera_impl(V4LCamera *self,string name,uint8_t camera_index, uint16_t width, uint16_t height, Image::Type::Format format) { this->self=self; string deviceName="/dev/video"+std::to_string(camera_index); device = open(deviceName.c_str(), O_RDWR | O_NONBLOCK); if (device == -1) { ((Thread*)self)->Err("Cannot open %s\n",deviceName.c_str()); } else { Printf("V4LCamera %s, opened %s\n",name.c_str(),deviceName.c_str()); } if(format == Image::Type::Format::UYVY) { if(Init(width,height,V4L2_PIX_FMT_UYVY) == -1) { ((Thread*)self)->Err("initialisation failed\n"); } } else if (format == Image::Type::Format::YUYV) { if(Init(width,height,V4L2_PIX_FMT_YUYV) == -1) { ((Thread*)self)->Err("initialisation failed\n"); } } else { ((Thread*)self)->Err("format not supported\n"); } //todo: better handling of detection, only neeeded for omap/dm //also in run, copy from v4l to cmem is not necessary if not using cmem... #ifdef ARMV7A useMemoryUsrPtr=true; #else useMemoryUsrPtr=false; #endif if(useMemoryUsrPtr) { AllocUserBuffers(); } else { AllocV4LBuffers(); } for (int i=0;i < nbBuffers;i++) { QueueBuffer(i); } /* enable the streaming */ v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == xioctl (device, VIDIOC_STREAMON,&type)) { ((Thread*)self)->Err("VIDIOC_STREAMON error\n"); } // ground station gain = new DoubleSpinBox(self->GetGroupBox()->NewRow(), "gain:", 0, 1, 0.1); exposure = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "exposure:", 0,1, 0.1); bright = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "bright:", 0, 1, 0.1); contrast = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "contrast:", 0,1, 0.1); hue = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "hue:", 0, 1, 0.1); sharpness = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "sharpness:", 0, 1, 0.1); sat = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "saturation:", 0, 1,0.1); autogain = new CheckBox(self->GetGroupBox()->NewRow(), "autogain:"); autoexposure = new CheckBox(self->GetGroupBox()->LastRowLastCol(), "autoexposure:"); awb = new CheckBox(self->GetGroupBox()->LastRowLastCol(), "awb:"); fps = new Label(self->GetGroupBox()->NewRow(), "fps"); hasProblems=false; } V4LCamera_impl::~V4LCamera_impl() { if(useMemoryUsrPtr) { for (int i = 0; i < nbBuffers; i++) { FreeFunction((char*)buffers[i]); } } else { FreeFunction(imageData); } close(device); } int V4LCamera_impl::Init(int width, int height,unsigned long colorspace) { struct v4l2_capability cap; memset(&cap, 0, sizeof (v4l2_capability)); if(-1 == xioctl(device, VIDIOC_QUERYCAP, &cap)) { return -1; } if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) { ((Thread*)self)->Err("device is unable to capture video memory.\n"); return -1; } struct v4l2_format form; memset(&form, 0, sizeof (v4l2_format)); form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* read the current setting */ if(-1 == xioctl(device, VIDIOC_G_FMT, &form)) { ((Thread*)self)->Err("Could not obtain specifics of capture window.\n"); return -1; } /* set the values we want to change */ form.fmt.pix.width = width; form.fmt.pix.height = height; form.fmt.win.chromakey = 0; form.fmt.win.field = V4L2_FIELD_ANY; form.fmt.win.clips = 0; form.fmt.win.clipcount = 0; form.fmt.pix.field = V4L2_FIELD_ANY; form.fmt.pix.pixelformat = colorspace; /* ask the device to change the size*/ if(-1 == xioctl (device, VIDIOC_S_FMT, &form)) { ((Thread*)self)->Err("Could not set specifics of capture window.\n"); return -1; } return 0; } int V4LCamera_impl::AllocV4LBuffers() { struct v4l2_requestbuffers req; memset(&req, 0, sizeof (v4l2_requestbuffers)); nbBuffers=DEFAULT_V4L_BUFFERS; try_again: req.count = nbBuffers; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; if(-1 == xioctl(device, VIDIOC_REQBUFS, &req)) { if (EINVAL == errno) { ((Thread*)self)->Err("camera does not support memory mapping\n"); } else { ((Thread*)self)->Err("VIDIOC_REQBUFS failed\n"); } return -1; } if(req.count < nbBuffers) { if (nbBuffers == 1) { ((Thread*)self)->Err("Insufficient buffer memory\n"); return -1; } else { nbBuffers--; ((Thread*)self)->Warn("Insufficient buffer memory -- decreaseing buffers to %i\n",nbBuffers); goto try_again; } } for(int i=0; iErr("VIDIOC_QUERYBUF error\n"); return -1; } if(self->output->GetDataType().GetSize()!=buf.length) { ((Thread*)self)->Err("buf size is not as exepcted %i/%i\n",buf.length,self->output->GetDataType().GetSize()); return -1; } buffers[i]=mmap(NULL,buf.length,PROT_READ | PROT_WRITE,MAP_SHARED,device, buf.m.offset); if(MAP_FAILED == buffers[i]) { ((Thread*)self)->Err("mmap error\n"); return -1; } } //allocate output data imageData = AllocFunction(self->output->GetDataType().GetSize()); //Printf("cmem allocated %i at %x\n",output->GetDataType().GetSize(),imageData); return 1; }; int V4LCamera_impl::AllocUserBuffers(void) { struct v4l2_requestbuffers req; memset(&req, 0, sizeof (v4l2_requestbuffers)); nbBuffers=DEFAULT_V4L_BUFFERS; req.count = nbBuffers; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_USERPTR; if (xioctl (device, VIDIOC_REQBUFS, &req)==-1) { if (errno==EINVAL) { ((Thread*)self)->Err("VIDIOC_REQBUFS user memory not supported\n"); } else { ((Thread*)self)->Err ("VIDIOC_REQBUFS xioctl\n"); } return -1; } for (int i=0; ioutput->GetDataType().GetSize()); } return 1; }; int V4LCamera_impl::GrabFrame(void) { fd_set fds; struct timeval tv; FD_ZERO (&fds); FD_SET (device, &fds); tv.tv_sec = 0; tv.tv_usec = 100000; int r=select(device+1, &fds, NULL, NULL, &tv); if (r==-1) { char errorMsg[256]; ((Thread*)self)->Err("select (%s)\n", strerror_r(-r, errorMsg, sizeof(errorMsg))); return -1; } if (r==0) { ((Thread*)self)->Err("select timeout\n"); return -1; } struct v4l2_buffer buf; memset(&buf, 0, sizeof (v4l2_buffer)); buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; if(useMemoryUsrPtr) { buf.memory=V4L2_MEMORY_USERPTR; } else { buf.memory=V4L2_MEMORY_MMAP; } //get last captured buffer dQueuedBuffer.index=-1; for(int i=0;iObjectName().c_str()); break; } else { ((Thread*)self)->Err("VIDIOC_DQBUF xioctl\n"); return -1; } } else { //Printf("%s %i dqueued\n",((Thread*)self)->ObjectName().c_str(),buf.index); //Printf("%i %x %i %i\n",buf.bytesused,buf.flags,buf.field,buf.sequence); if(dQueuedBuffer.index!=-1) { //Printf("%s %i queued\n",((Thread*)self)->ObjectName().c_str(),dQueuedBuffer.index); QueueBuffer(&dQueuedBuffer); } dQueuedBuffer=buf; } } //Printf("%s %i\n",((Thread*)self)->ObjectName().c_str(),dQueuedBuffer.index); return 1; } void V4LCamera_impl::Run(void) { Time cam_time, new_time, fpsNow, fpsPrev; int fpsCounter = 0; cam_time = GetTime(); fpsPrev = cam_time; while (!self->ToBeStopped()) { // fps counter fpsCounter++; if (fpsCounter == 100) { fpsNow = GetTime(); fps->SetText("fps: %.1f", fpsCounter / ((float)(fpsNow - fpsPrev) / 1000000000.)); fpsCounter = 0; fpsPrev = fpsNow; } // cam properties if (gain->ValueChanged() == true && autogain->Value() == false) self->SetGain(gain->Value()); if (exposure->ValueChanged() == true && autoexposure->Value() == false) self->SetExposure(exposure->Value()); if (bright->ValueChanged() == true) self->SetBrightness(bright->Value()); if (sat->ValueChanged() == true) self->SetSaturation(sat->Value()); if (contrast->ValueChanged() == true) self->SetContrast(contrast->Value()); if (hue->ValueChanged() == true) self->SetHue(hue->Value()); //if (sharpness->ValueChanged() == true) // cvSetCaptureProperty(capture, CV_CAP_PROP_SHARPNESS, sharpness->Value()); if (autogain->ValueChanged() == true) { self->SetAutoGain(autogain->Value()); if (autogain->Value() == true) { gain->setEnabled(false); } else { gain->setEnabled(true); self->SetGain(gain->Value()); } } if (autoexposure->ValueChanged() == true) { self->SetAutoExposure(autoexposure->Value()); if (autoexposure->Value() == true) { exposure->setEnabled(false); } else { exposure->setEnabled(true); self->SetExposure(exposure->Value()); } } //if (awb->ValueChanged() == true) // cvSetCaptureProperty(capture, CV_CAP_PROP_AWB, awb->Value()); // cam pictures if (!GrabFrame()) { Printf("Could not grab a frame\n"); } //check for ps3eye deconnection in hds uav new_time = GetTime(); if(new_time-cam_time>100*1000*1000) { ((Thread*)self)->Warn("delta t trop grand\n"); hasProblems=true; }/* if(hasProblems==false) { struct v4l2_format form; form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; xioctl(device, VIDIOC_G_FMT,&form); if(xioctl (device, VIDIOC_G_FMT,&form)<0) { ((Thread*)self)->Warn("camera disconnected\n"); hasProblems=true; } }*/ //select buffer if(useMemoryUsrPtr) {//buffers are already in CMEM imageData=(char*)buffers[dQueuedBuffer.index]; //dequeue it latter (buffer used by ProcessUpdate) } else {//copy to CMEM allocated buffer memcpy(imageData,(char *)buffers[dQueuedBuffer.index],self->output->GetDataType().GetSize()); QueueBuffer(&dQueuedBuffer);//we can do it right now } self->output->GetMutex(); self->output->buffer=imageData; self->output->ReleaseMutex(); self->output->SetDataTime(cam_time); self->ProcessUpdate(self->output); cam_time = new_time; if(useMemoryUsrPtr) { QueueBuffer(&dQueuedBuffer);//now it is possible to dequeue } } } int V4LCamera_impl::QueueBuffer(struct v4l2_buffer *buf) { if(buf->index>=0 && buf->indexErr("VIDIOC_QBUF xioctl %s\n",strerror(-ret)); return -1; } } return 0; } int V4LCamera_impl::QueueBuffer(int index) { struct v4l2_buffer buf; memset(&buf, 0, sizeof (v4l2_buffer)); buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.index=(unsigned long)index; if(useMemoryUsrPtr) { buf.memory=V4L2_MEMORY_USERPTR; buf.m.userptr=(unsigned long)(buffers[index]); buf.length=self->output->GetDataType().GetSize(); } else { buf.memory=V4L2_MEMORY_MMAP; } return QueueBuffer(&buf); } void V4LCamera_impl::SetAutoGain(bool value) { SetProperty(V4L2_CID_AUTOGAIN, value); } void V4LCamera_impl::SetAutoExposure(bool value) { ((Thread*)self)->Warn("not implemented\n"); } void V4LCamera_impl::SetGain(float value) { SetProperty(V4L2_CID_GAIN, value); } void V4LCamera_impl::SetExposure(float value) { SetProperty(V4L2_CID_EXPOSURE, value); } void V4LCamera_impl::SetBrightness(float value) { SetProperty(V4L2_CID_BRIGHTNESS, value); } void V4LCamera_impl::SetSaturation(float value) { SetProperty(V4L2_CID_SATURATION, value); } void V4LCamera_impl::SetHue(float value) { SetProperty(V4L2_CID_HUE, value); } void V4LCamera_impl::SetContrast(float value) { SetProperty(V4L2_CID_CONTRAST, value); } float V4LCamera_impl::GetProperty(int property) { //get min and max value struct v4l2_queryctrl queryctrl; queryctrl.id = property; if(xioctl (device, VIDIOC_QUERYCTRL,&queryctrl)==-1) return -1; int min = queryctrl.minimum; int max = queryctrl.maximum; //set value struct v4l2_control control; memset (&control, 0, sizeof (v4l2_control)); control.id = property; if(xioctl (device,VIDIOC_G_CTRL, &control)==-1) return -1; return ((float)control.value - min + 1) / (max - min); } void V4LCamera_impl::SetProperty(int property,float value) { //get min and max value struct v4l2_queryctrl queryctrl; queryctrl.id = property; if(xioctl (device, VIDIOC_QUERYCTRL,&queryctrl)==-1) { ((Thread*)self)->Warn("prop %x, VIDIOC_QUERYCTRL failed\n",property); } int min = queryctrl.minimum; int max = queryctrl.maximum; //set value struct v4l2_control control; memset (&control, 0, sizeof (v4l2_control)); control.id = property; control.value = (int)(value * (max - min) + min); if(xioctl (device,VIDIOC_S_CTRL, &control)==-1) { ((Thread*)self)->Warn("prop %x, VIDIOC_S_CTRL failed\n",property); } } int V4LCamera_impl::xioctl( int fd, int request, void *arg) { int r; do r = ioctl (fd, request, arg); while (-1 == r && EINTR == errno); return r; }