source: flair-src/tags/0.3/lib/FlairSensorActuator/src/V4LCamera_impl.cpp

Last change on this file was 352, checked in by Sanahuja Guillaume, 4 years ago

v4lcamera: use pimpl

File size: 14.2 KB
Line 
1// %flair:license{
2// This file is part of the Flair framework distributed under the
3// CECILL-C License, Version 1.0.
4// %flair:license}
5// created: 2014/07/17
6// filename: V4LCamera_impl.cpp
7//
8// author: Guillaume Sanahuja
9// Copyright Heudiasyc UMR UTC/CNRS 7253
10//
11// version: $Id: $
12//
13// purpose: base class for V4l camera
14//
15//
16/*********************************************************************/
17
18#include "V4LCamera_impl.h"
19#include "V4LCamera.h"
20#include <GroupBox.h>
21#include <DoubleSpinBox.h>
22#include <CheckBox.h>
23#include <Label.h>
24#include <Image.h>
25#include <FrameworkManager.h>
26#include <fcntl.h>
27//#include <linux/videodev2.h>
28#include <sys/ioctl.h>
29#include <unistd.h>
30#include <cstring>
31#include <sys/mman.h>
32#include <VisionFilter.h>
33
34using std::string;
35using namespace flair::core;
36using namespace flair::gui;
37using namespace flair::sensor;
38
39
40V4LCamera_impl::V4LCamera_impl(V4LCamera *self,string name,uint8_t camera_index, uint16_t width, uint16_t height,
41 Image::Type::Format format) {
42 this->self=self;
43
44 string deviceName="/dev/video"+std::to_string(camera_index);
45 device = open(deviceName.c_str(), O_RDWR | O_NONBLOCK);
46 if (device == -1) {
47 ((Thread*)self)->Err("Cannot open %s\n",deviceName.c_str());
48 } else {
49 Printf("V4LCamera %s, opened %s\n",name.c_str(),deviceName.c_str());
50 }
51
52 if(format == Image::Type::Format::UYVY) {
53 if(Init(width,height,V4L2_PIX_FMT_UYVY) == -1) {
54 ((Thread*)self)->Err("initialisation failed\n");
55 }
56 } else if (format == Image::Type::Format::YUYV) {
57 if(Init(width,height,V4L2_PIX_FMT_YUYV) == -1) {
58 ((Thread*)self)->Err("initialisation failed\n");
59 }
60 } else {
61 ((Thread*)self)->Err("format not supported\n");
62 }
63
64 //todo: better handling of detection, only neeeded for omap/dm
65 //also in run, copy from v4l to cmem is not necessary if not using cmem...
66#ifdef ARMV7A
67 useMemoryUsrPtr=true;
68#else
69 useMemoryUsrPtr=false;
70#endif
71 if(useMemoryUsrPtr) {
72 AllocUserBuffers();
73 } else {
74 AllocV4LBuffers();
75 }
76
77 for (int i=0;i < nbBuffers;i++) {
78 QueueBuffer(i);
79 }
80
81 /* enable the streaming */
82 v4l2_buf_type type;
83 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
84 if (-1 == xioctl (device, VIDIOC_STREAMON,&type)) {
85 ((Thread*)self)->Err("VIDIOC_STREAMON error\n");
86 }
87
88 // ground station
89 gain = new DoubleSpinBox(self->GetGroupBox()->NewRow(), "gain:", 0, 1, 0.1);
90 exposure = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "exposure:", 0,1, 0.1);
91 bright = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "bright:", 0, 1, 0.1);
92 contrast = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "contrast:", 0,1, 0.1);
93 hue = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "hue:", 0, 1, 0.1);
94 sharpness = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "sharpness:", 0, 1, 0.1);
95 sat = new DoubleSpinBox(self->GetGroupBox()->LastRowLastCol(), "saturation:", 0, 1,0.1);
96 autogain = new CheckBox(self->GetGroupBox()->NewRow(), "autogain:");
97 autoexposure = new CheckBox(self->GetGroupBox()->LastRowLastCol(), "autoexposure:");
98 awb = new CheckBox(self->GetGroupBox()->LastRowLastCol(), "awb:");
99 fps = new Label(self->GetGroupBox()->NewRow(), "fps");
100
101 hasProblems=false;
102}
103
104V4LCamera_impl::~V4LCamera_impl() {
105 if(useMemoryUsrPtr) {
106 for (int i = 0; i < nbBuffers; i++) {
107 FreeFunction((char*)buffers[i]);
108 }
109 } else {
110 FreeFunction(imageData);
111 }
112 close(device);
113}
114
115int V4LCamera_impl::Init(int width, int height,unsigned long colorspace) {
116 struct v4l2_capability cap;
117 memset(&cap, 0, sizeof (v4l2_capability));
118
119 if(-1 == xioctl(device, VIDIOC_QUERYCAP, &cap)) {
120 return -1;
121 }
122
123 if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) {
124 ((Thread*)self)->Err("device is unable to capture video memory.\n");
125 return -1;
126 }
127
128 struct v4l2_format form;
129 memset(&form, 0, sizeof (v4l2_format));
130 form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
131
132 /* read the current setting */
133 if(-1 == xioctl(device, VIDIOC_G_FMT, &form)) {
134 ((Thread*)self)->Err("Could not obtain specifics of capture window.\n");
135 return -1;
136 }
137
138 /* set the values we want to change */
139 form.fmt.pix.width = width;
140 form.fmt.pix.height = height;
141 form.fmt.win.chromakey = 0;
142 form.fmt.win.field = V4L2_FIELD_ANY;
143 form.fmt.win.clips = 0;
144 form.fmt.win.clipcount = 0;
145 form.fmt.pix.field = V4L2_FIELD_ANY;
146 form.fmt.pix.pixelformat = colorspace;
147
148 /* ask the device to change the size*/
149 if(-1 == xioctl (device, VIDIOC_S_FMT, &form)) {
150 ((Thread*)self)->Err("Could not set specifics of capture window.\n");
151 return -1;
152 }
153
154 return 0;
155}
156
157int V4LCamera_impl::AllocV4LBuffers() {
158 struct v4l2_requestbuffers req;
159 memset(&req, 0, sizeof (v4l2_requestbuffers));
160 nbBuffers=DEFAULT_V4L_BUFFERS;
161
162 try_again:
163
164 req.count = nbBuffers;
165 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
166 req.memory = V4L2_MEMORY_MMAP;
167
168 if(-1 == xioctl(device, VIDIOC_REQBUFS, &req)) {
169 if (EINVAL == errno) {
170 ((Thread*)self)->Err("camera does not support memory mapping\n");
171 } else {
172 ((Thread*)self)->Err("VIDIOC_REQBUFS failed\n");
173 }
174 return -1;
175 }
176
177 if(req.count < nbBuffers) {
178 if (nbBuffers == 1) {
179 ((Thread*)self)->Err("Insufficient buffer memory\n");
180 return -1;
181 } else {
182 nbBuffers--;
183 ((Thread*)self)->Warn("Insufficient buffer memory -- decreaseing buffers to %i\n",nbBuffers);
184 goto try_again;
185 }
186 }
187
188 for(int i=0; i<req.count; i++) {
189 struct v4l2_buffer buf;
190 memset(&buf, 0, sizeof (v4l2_buffer));
191
192 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
193 buf.memory = V4L2_MEMORY_MMAP;
194 buf.index = i;
195
196 if(-1 == xioctl(device, VIDIOC_QUERYBUF, &buf)) {
197 ((Thread*)self)->Err("VIDIOC_QUERYBUF error\n");
198 return -1;
199 }
200
201 if(self->output->GetDataType().GetSize()!=buf.length) {
202 ((Thread*)self)->Err("buf size is not as exepcted %i/%i\n",buf.length,self->output->GetDataType().GetSize());
203 return -1;
204 }
205
206 buffers[i]=mmap(NULL,buf.length,PROT_READ | PROT_WRITE,MAP_SHARED,device, buf.m.offset);
207
208 if(MAP_FAILED == buffers[i]) {
209 ((Thread*)self)->Err("mmap error\n");
210 return -1;
211 }
212 }
213
214 //allocate output data
215 imageData = AllocFunction(self->output->GetDataType().GetSize());
216 //Printf("cmem allocated %i at %x\n",output->GetDataType().GetSize(),imageData);
217
218 return 1;
219};
220
221int V4LCamera_impl::AllocUserBuffers(void) {
222 struct v4l2_requestbuffers req;
223 memset(&req, 0, sizeof (v4l2_requestbuffers));
224 nbBuffers=DEFAULT_V4L_BUFFERS;
225
226 req.count = nbBuffers;
227 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
228 req.memory = V4L2_MEMORY_USERPTR;
229
230 if (xioctl (device, VIDIOC_REQBUFS, &req)==-1) {
231 if (errno==EINVAL) {
232 ((Thread*)self)->Err("VIDIOC_REQBUFS user memory not supported\n");
233 } else {
234 ((Thread*)self)->Err ("VIDIOC_REQBUFS xioctl\n");
235 }
236 return -1;
237 }
238
239 for (int i=0; i<nbBuffers; i++) {
240 buffers[i] =AllocFunction(self->output->GetDataType().GetSize());
241 }
242
243 return 1;
244};
245
246int V4LCamera_impl::GrabFrame(void) {
247 fd_set fds;
248 struct timeval tv;
249 FD_ZERO (&fds);
250 FD_SET (device, &fds);
251
252 tv.tv_sec = 0;
253 tv.tv_usec = 100000;
254
255 int r=select(device+1, &fds, NULL, NULL, &tv);
256
257 if (r==-1) {
258 char errorMsg[256];
259 ((Thread*)self)->Err("select (%s)\n", strerror_r(-r, errorMsg, sizeof(errorMsg)));
260 return -1;
261 }
262
263 if (r==0) {
264 ((Thread*)self)->Err("select timeout\n");
265 return -1;
266 }
267
268 struct v4l2_buffer buf;
269 memset(&buf, 0, sizeof (v4l2_buffer));
270 buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
271 if(useMemoryUsrPtr) {
272 buf.memory=V4L2_MEMORY_USERPTR;
273 } else {
274 buf.memory=V4L2_MEMORY_MMAP;
275 }
276
277 //get last captured buffer
278 dQueuedBuffer.index=-1;
279 for(int i=0;i<nbBuffers;i++) {
280 if (xioctl (device, VIDIOC_DQBUF, &buf)==-1) {
281 if (errno==EAGAIN) {//when no new buffer is available for reading (non blocking)
282 //Printf("%s eagain\n",((Thread*)self)->ObjectName().c_str());
283 break;
284 } else {
285 ((Thread*)self)->Err("VIDIOC_DQBUF xioctl\n");
286 return -1;
287 }
288 } else {
289 //Printf("%s %i dqueued\n",((Thread*)self)->ObjectName().c_str(),buf.index);
290 //Printf("%i %x %i %i\n",buf.bytesused,buf.flags,buf.field,buf.sequence);
291 if(dQueuedBuffer.index!=-1) {
292 //Printf("%s %i queued\n",((Thread*)self)->ObjectName().c_str(),dQueuedBuffer.index);
293 QueueBuffer(&dQueuedBuffer);
294 }
295 dQueuedBuffer=buf;
296 }
297 }
298 //Printf("%s %i\n",((Thread*)self)->ObjectName().c_str(),dQueuedBuffer.index);
299
300 return 1;
301}
302
303void V4LCamera_impl::Run(void) {
304 Time cam_time, new_time, fpsNow, fpsPrev;
305 int fpsCounter = 0;
306
307 cam_time = GetTime();
308 fpsPrev = cam_time;
309
310 while (!self->ToBeStopped()) {
311 // fps counter
312 fpsCounter++;
313 if (fpsCounter == 100) {
314 fpsNow = GetTime();
315 fps->SetText("fps: %.1f",
316 fpsCounter / ((float)(fpsNow - fpsPrev) / 1000000000.));
317 fpsCounter = 0;
318 fpsPrev = fpsNow;
319 }
320
321 // cam properties
322 if (gain->ValueChanged() == true && autogain->Value() == false)
323 self->SetGain(gain->Value());
324 if (exposure->ValueChanged() == true && autoexposure->Value() == false)
325 self->SetExposure(exposure->Value());
326 if (bright->ValueChanged() == true)
327 self->SetBrightness(bright->Value());
328 if (sat->ValueChanged() == true)
329 self->SetSaturation(sat->Value());
330 if (contrast->ValueChanged() == true)
331 self->SetContrast(contrast->Value());
332 if (hue->ValueChanged() == true)
333 self->SetHue(hue->Value());
334 //if (sharpness->ValueChanged() == true)
335 // cvSetCaptureProperty(capture, CV_CAP_PROP_SHARPNESS, sharpness->Value());
336 if (autogain->ValueChanged() == true) {
337 self->SetAutoGain(autogain->Value());
338 if (autogain->Value() == true) {
339 gain->setEnabled(false);
340 } else {
341 gain->setEnabled(true);
342 self->SetGain(gain->Value());
343 }
344 }
345 if (autoexposure->ValueChanged() == true) {
346 self->SetAutoExposure(autoexposure->Value());
347 if (autoexposure->Value() == true) {
348 exposure->setEnabled(false);
349 } else {
350 exposure->setEnabled(true);
351 self->SetExposure(exposure->Value());
352 }
353 }
354 //if (awb->ValueChanged() == true)
355 // cvSetCaptureProperty(capture, CV_CAP_PROP_AWB, awb->Value());
356
357 // cam pictures
358 if (!GrabFrame()) {
359 Printf("Could not grab a frame\n");
360 }
361
362 //check for ps3eye deconnection in hds uav
363 new_time = GetTime();
364 if(new_time-cam_time>100*1000*1000) {
365 ((Thread*)self)->Warn("delta t trop grand\n");
366 hasProblems=true;
367 }/*
368 if(hasProblems==false) {
369 struct v4l2_format form;
370 form.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
371 xioctl(device, VIDIOC_G_FMT,&form);
372 if(xioctl (device, VIDIOC_G_FMT,&form)<0) {
373 ((Thread*)self)->Warn("camera disconnected\n");
374 hasProblems=true;
375 }
376 }*/
377
378 //select buffer
379 if(useMemoryUsrPtr) {//buffers are already in CMEM
380 imageData=(char*)buffers[dQueuedBuffer.index];
381 //dequeue it latter (buffer used by ProcessUpdate)
382 } else {//copy to CMEM allocated buffer
383 memcpy(imageData,(char *)buffers[dQueuedBuffer.index],self->output->GetDataType().GetSize());
384 QueueBuffer(&dQueuedBuffer);//we can do it right now
385 }
386
387 self->output->GetMutex();
388 self->output->buffer=imageData;
389 self->output->ReleaseMutex();
390
391 self->output->SetDataTime(cam_time);
392 self->ProcessUpdate(self->output);
393 cam_time = new_time;
394
395 if(useMemoryUsrPtr) {
396 QueueBuffer(&dQueuedBuffer);//now it is possible to dequeue
397 }
398 }
399}
400
401int V4LCamera_impl::QueueBuffer(struct v4l2_buffer *buf) {
402 if(buf->index>=0 && buf->index<nbBuffers) {
403 int ret=xioctl (device, VIDIOC_QBUF,buf);
404 if (ret==-1) {
405 ((Thread*)self)->Err("VIDIOC_QBUF xioctl %s\n",strerror(-ret));
406 return -1;
407 }
408 }
409 return 0;
410}
411
412int V4LCamera_impl::QueueBuffer(int index) {
413 struct v4l2_buffer buf;
414 memset(&buf, 0, sizeof (v4l2_buffer));
415
416 buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
417 buf.index=(unsigned long)index;
418 if(useMemoryUsrPtr) {
419 buf.memory=V4L2_MEMORY_USERPTR;
420 buf.m.userptr=(unsigned long)(buffers[index]);
421 buf.length=self->output->GetDataType().GetSize();
422 } else {
423 buf.memory=V4L2_MEMORY_MMAP;
424 }
425 return QueueBuffer(&buf);
426}
427
428void V4LCamera_impl::SetAutoGain(bool value) {
429 SetProperty(V4L2_CID_AUTOGAIN, value);
430}
431
432void V4LCamera_impl::SetAutoExposure(bool value) {
433 ((Thread*)self)->Warn("not implemented\n");
434}
435
436void V4LCamera_impl::SetGain(float value) {
437 SetProperty(V4L2_CID_GAIN, value);
438}
439
440void V4LCamera_impl::SetExposure(float value) {
441 SetProperty(V4L2_CID_EXPOSURE, value);
442}
443
444void V4LCamera_impl::SetBrightness(float value) {
445 SetProperty(V4L2_CID_BRIGHTNESS, value);
446}
447
448void V4LCamera_impl::SetSaturation(float value) {
449 SetProperty(V4L2_CID_SATURATION, value);
450}
451
452void V4LCamera_impl::SetHue(float value) {
453 SetProperty(V4L2_CID_HUE, value);
454}
455
456void V4LCamera_impl::SetContrast(float value) {
457 SetProperty(V4L2_CID_CONTRAST, value);
458}
459
460float V4LCamera_impl::GetProperty(int property) {
461 //get min and max value
462 struct v4l2_queryctrl queryctrl;
463 queryctrl.id = property;
464 if(xioctl (device, VIDIOC_QUERYCTRL,&queryctrl)==-1) return -1;
465 int min = queryctrl.minimum;
466 int max = queryctrl.maximum;
467
468 //set value
469 struct v4l2_control control;
470 memset (&control, 0, sizeof (v4l2_control));
471 control.id = property;
472 if(xioctl (device,VIDIOC_G_CTRL, &control)==-1) return -1;
473
474 return ((float)control.value - min + 1) / (max - min);
475}
476
477void V4LCamera_impl::SetProperty(int property,float value) {
478 //get min and max value
479 struct v4l2_queryctrl queryctrl;
480 queryctrl.id = property;
481 if(xioctl (device, VIDIOC_QUERYCTRL,&queryctrl)==-1) {
482 ((Thread*)self)->Warn("prop %x, VIDIOC_QUERYCTRL failed\n",property);
483 }
484 int min = queryctrl.minimum;
485 int max = queryctrl.maximum;
486
487 //set value
488 struct v4l2_control control;
489 memset (&control, 0, sizeof (v4l2_control));
490 control.id = property;
491 control.value = (int)(value * (max - min) + min);
492 if(xioctl (device,VIDIOC_S_CTRL, &control)==-1) {
493 ((Thread*)self)->Warn("prop %x, VIDIOC_S_CTRL failed\n",property);
494 }
495}
496
497int V4LCamera_impl::xioctl( int fd, int request, void *arg) {
498 int r;
499
500 do r = ioctl (fd, request, arg);
501 while (-1 == r && EINTR == errno);
502
503 return r;
504}
Note: See TracBrowser for help on using the repository browser.