00001
00002 #include <linux/videodev2.h>
00003 #warning make sure to point isp_user to the right place before long!
00004 #include "/elphel/git/linux-omap-2.6/drivers/media/video/isp/isp_user.h"
00005
00006
00007 #include <asm/types.h>
00008 #include <sys/types.h>
00009 #include <sys/syscall.h>
00010 #include <sys/prctl.h>
00011 #include <linux/capability.h>
00012
00013 #include <pthread.h>
00014 #include <poll.h>
00015
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include <string.h>
00019 #include <unistd.h>
00020 #include <math.h>
00021
00022 #include <sys/fcntl.h>
00023 #include <sys/ioctl.h>
00024 #include <sys/mman.h>
00025 #include <sys/time.h>
00026 #include <time.h>
00027
00028 #include <errno.h>
00029 #include <malloc.h>
00030
00031 #include "V4L2Sensor.h"
00032
00033 namespace FCam { namespace F2 {
00034
00035 V4L2Sensor *V4L2Sensor::instance(std::string fname) {
00036 std::map<std::string, V4L2Sensor *>::iterator i;
00037 i = instances_.find(fname);
00038 if (i == instances_.end()) {
00039 instances_[fname] = new V4L2Sensor(fname);
00040 }
00041
00042 return instances_[fname];
00043 };
00044
00045 V4L2Sensor::V4L2Sensor(std::string fname) : state(CLOSED), filename(fname) {
00046
00047 }
00048
00049 std::map<std::string, V4L2Sensor *> V4L2Sensor::instances_;
00050
00051 void V4L2Sensor::open() {
00052 if (state != CLOSED) {
00053 printf("Sensor is already open!\n");
00054 return;
00055 }
00056
00057 fd = ::open(filename.c_str(), O_RDWR | O_NONBLOCK, 0);
00058
00059 if (fd < 0) {
00060 perror("opening device");
00061 return;
00062 }
00063
00064 state = IDLE;
00065 }
00066
00067 void V4L2Sensor::close() {
00068 if (state != CLOSED) {
00069 ::close(fd);
00070 }
00071 state = CLOSED;
00072 }
00073
00074 int V4L2Sensor::getFD() {
00075 if (state == CLOSED) {
00076 return -1;
00077 }
00078 return fd;
00079 }
00080
00081 void V4L2Sensor::startStreaming(Mode m,
00082 HistogramConfig *histogram,
00083 SharpnessMapConfig *sharpness) {
00084
00085 if (state != IDLE) {
00086 printf("Can only initiate streaming if sensor is idle\n");
00087 return;
00088 }
00089
00090 struct v4l2_format fmt;
00091
00092 memset(&fmt, 0, sizeof(struct v4l2_format));
00093
00094 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00095 fmt.fmt.pix.width = m.width;
00096 fmt.fmt.pix.height = m.height;
00097 if (m.type == UYVY) {
00098 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
00099 } else if (m.type == RAW) {
00100 fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SGRBG10;
00101 } else {
00102 perror("Unknown image format requested");
00103 return;
00104 }
00105 fmt.fmt.pix.field = V4L2_FIELD_NONE;
00106
00107
00108 if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
00109 perror("VIDIOC_S_FMT");
00110 return;
00111 }
00112
00113 currentMode.width = fmt.fmt.pix.width;
00114 currentMode.height = fmt.fmt.pix.height;
00115 currentMode.type = (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_UYVY) ? UYVY : RAW;
00116
00117 struct v4l2_requestbuffers req;
00118 memset(&req, 0, sizeof(req));
00119 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00120 req.memory = V4L2_MEMORY_MMAP;
00121 req.count = 8;
00122
00123 if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
00124 perror("VIDIOC_REQBUFS");
00125 return;
00126 }
00127
00128 buffers.resize(req.count);
00129
00130 for (size_t i = 0; i < buffers.size(); i++) {
00131 v4l2_buffer buf;
00132 memset(&buf, 0, sizeof(v4l2_buffer));
00133 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00134 buf.memory = V4L2_MEMORY_MMAP;
00135 buf.index = i;
00136
00137 if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
00138 perror("VIDIOC_QUERYBUF");
00139 return;
00140 }
00141
00142 buffers[i].index = i;
00143 buffers[i].length = buf.length;
00144 buffers[i].data =
00145 (unsigned char *)mmap(NULL, buffers[i].length, PROT_READ | PROT_WRITE,
00146 MAP_SHARED, fd, buf.m.offset);
00147
00148 if (buffers[i].data == MAP_FAILED) {
00149 perror("mmap");
00150 return;
00151 }
00152 }
00153
00154 for (size_t i = 0; i < buffers.size(); i++) {
00155 printf("Queueing up buffer %d\n", i);
00156 releaseFrame(&buffers[i]);
00157 }
00158
00159
00160 setHistogramConfig(histogram);
00161 setSharpnessMapConfig(sharpness);
00162
00163 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00164 if (ioctl(fd, VIDIOC_STREAMON, &type) < 0) {
00165 perror("VIDIOC_STREAMON");
00166 return;
00167 }
00168
00169 printf("Sensor now streaming\n");
00170 state = STREAMING;
00171 }
00172
00173
00174 void V4L2Sensor::stopStreaming() {
00175 if (state != STREAMING) {
00176 printf("Camera is already not streaming!\n");
00177 return;
00178 }
00179
00180 enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00181 if (ioctl(fd, VIDIOC_STREAMOFF, &type) < 0) {
00182 perror("VIDIOC_STREAMOFF");
00183 return;
00184 }
00185
00186 for (size_t i = 0; i < buffers.size(); i++) {
00187 if (munmap(buffers[i].data, buffers[i].length)) {
00188 perror("MUNMAP");
00189 }
00190 }
00191
00192 state = IDLE;
00193 }
00194
00195
00196
00197 V4L2Sensor::V4L2Frame *V4L2Sensor::acquireFrame(bool blocking) {
00198 if (state != STREAMING) {
00199 printf("Can't acquire a frame when not streaming!\n");
00200 return NULL;
00201 }
00202
00203
00204
00205 v4l2_buffer buf;
00206 memset(&buf, 0, sizeof(v4l2_buffer));
00207 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00208 buf.memory = V4L2_MEMORY_MMAP;
00209
00210 if (blocking) {
00211 struct pollfd p = {fd, POLLIN, 0};
00212 poll(&p, 1, -1);
00213 if (!(p.revents & POLLIN)) {
00214 perror("Poll returned without data being available");
00215 return NULL;
00216 }
00217 }
00218
00219 if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
00220 if (errno == EAGAIN && !blocking) {
00221 printf("VIDIOC_DQBUF: EAGAIN\n"); fflush(stdout);
00222 return NULL;
00223 }
00224
00225 perror("VIDIOC_DQBUF");
00226 return NULL;
00227 }
00228
00229
00230
00231
00232 buffers[buf.index].processingDoneTime = Time(buf.timestamp);
00233 return &(buffers[buf.index]);
00234 }
00235
00236
00237
00238 void V4L2Sensor::getHistogram(Time t, Histogram *histogram, HistogramConfig *conf) {
00239
00240
00241 if (!histogram || !conf) return;
00242
00243 if (!conf->regions) {
00244 histogram->data = NULL;
00245 wipeHistogram(histogram);
00246 return;
00247 }
00248
00249
00250
00251 struct isp_hist_data hist_data;
00252 histogram->data = new unsigned[currentHistogram.regions * currentHistogram.buckets * 4];
00253 hist_data.hist_statistics_buf = histogram->data;
00254 hist_data.update = REQUEST_STATISTICS;
00255
00256
00257
00258 hist_data.frame_number = NEWEST_FRAME;
00259 hist_data.curr_frame = 0;
00260 hist_data.config_counter = 0;
00261 hist_data.ts.tv_sec = 0;
00262 hist_data.ts.tv_usec = 0;
00263
00264 if (ioctl(fd, VIDIOC_PRIVATE_ISP_HIST_REQ, &hist_data)) {
00265 perror("VIDIOC_PRIVATE_ISP_HIST_REQ");
00266 wipeHistogram(histogram); return;
00267 }
00268
00269
00270
00271
00272
00273 Time h(hist_data.ts);
00274
00275 if ((t - h) > 2000) {
00276 printf(" WARNING: Missing histogram (%d)\n", t-h);
00277 wipeHistogram(histogram); return;
00278 }
00279
00280 while ((t-h) < -2000) {
00281
00282
00283
00284 if (hist_data.frame_number == 0) hist_data.frame_number = 4095;
00285 else hist_data.frame_number--;
00286
00287
00288 if (ioctl(fd, VIDIOC_PRIVATE_ISP_HIST_REQ, &hist_data)) {
00289 perror("VIDIOC_PRIVATE_ISP_HIST_REQ");
00290 wipeHistogram(histogram); return;
00291 }
00292
00293 h = Time(hist_data.ts);
00294 }
00295
00296 histogram->valid = true;
00297 histogram->channels = 4;
00298 histogram->buckets = currentHistogram.buckets;
00299 histogram->regions = currentHistogram.regions;
00300 for (int i = 0; i < 4; i++) {
00301 histogram->region[i] = currentHistogram.region[i];
00302 }
00303 }
00304
00305 void V4L2Sensor::wipeHistogram(Histogram *histogram) {
00306 if (histogram->data) delete[] histogram->data;
00307 histogram->valid = false;
00308 histogram->data = NULL;
00309 histogram->regions = 0;
00310 histogram->channels = 0;
00311 histogram->buckets = 0;
00312 }
00313
00314 void V4L2Sensor::getSharpnessMap(Time t, SharpnessMap *sharpness, SharpnessMapConfig *conf) {
00315 if (!sharpness || !conf) return;
00316
00317 if (!conf->enabled) {
00318 sharpness->data = NULL;
00319 wipeSharpnessMap(sharpness);
00320 return;
00321 }
00322
00323
00324 struct isp_af_data af_data;
00325 af_data.frame_number = NEWEST_FRAME;
00326 af_data.update = REQUEST_STATISTICS;
00327 af_data.curr_frame = 0;
00328 af_data.config_counter = 0;
00329 af_data.xtrastats.ts.tv_sec = 0;
00330 af_data.xtrastats.ts.tv_usec = 0;
00331 sharpness->data = new unsigned[12*currentSharpness.size.width*currentSharpness.size.height];
00332 af_data.af_statistics_buf = sharpness->data;
00333
00334 if (ioctl(fd, VIDIOC_PRIVATE_ISP_AF_REQ, &af_data)) {
00335 perror("VIDIOC_PRIVATE_ISP_AF_REQ");
00336 wipeSharpnessMap(sharpness); return;
00337 }
00338
00339 Time s(af_data.xtrastats.ts);
00340 if ((t - s) > 2000) {
00341 printf("WARNING: Missing sharpness (%d)\n", t-s);
00342 wipeSharpnessMap(sharpness); return;
00343 }
00344
00345 while ((t-s) < -2000) {
00346
00347
00348 if (af_data.frame_number == 0) af_data.frame_number = 4095;
00349 else af_data.frame_number--;
00350
00351
00352 if (ioctl(fd, VIDIOC_PRIVATE_ISP_AF_REQ, &af_data)) {
00353 perror("VIDIOC_PRIVATE_ISP_AF_REQ");
00354 wipeSharpnessMap(sharpness); return;
00355 }
00356 s = Time(af_data.xtrastats.ts);
00357 }
00358
00359 sharpness->valid = true;
00360 sharpness->channels = 3;
00361 sharpness->size = currentSharpness.size;
00362 }
00363
00364 void V4L2Sensor::wipeSharpnessMap(SharpnessMap *sharpness) {
00365 if (sharpness->data) delete[] sharpness->data;
00366 sharpness->valid = false;
00367 sharpness->data = NULL;
00368 sharpness->size = FCam::Size(0, 0);
00369 }
00370
00371 void V4L2Sensor::setHistogramConfig(HistogramConfig *histogram) {
00372 if (!histogram) return;
00373 if (!histogram->regions) return;
00374
00375
00376 isp_pipeline_stats pstats;
00377 if (ioctl(fd, VIDIOC_PRIVATE_ISP_PIPELINE_STATS_REQ, &pstats) < 0) {
00378 perror("VIDIOC_PRIVATE_ISP_PIPELINE_STATS_REQ");
00379 return;
00380 }
00381
00382
00383 printf("CCDC output: %d x %d\n", pstats.ccdc_out_w, pstats.ccdc_out_h);
00384 printf("PRV output: %d x %d\n", pstats.prv_out_w, pstats.prv_out_h);
00385 printf("RSZ input: %d x %d + %d, %d\n",
00386 pstats.rsz_in_w, pstats.rsz_in_h,
00387 pstats.rsz_in_x, pstats.rsz_in_y);
00388 printf("RSZ output: %d x %d\n", pstats.rsz_out_w, pstats.rsz_out_h);
00389
00390 struct isp_hist_config hist_cfg;
00391 hist_cfg.enable = 1;
00392 hist_cfg.source = HIST_SOURCE_CCDC;
00393 hist_cfg.input_bit_width = 10;
00394 hist_cfg.num_acc_frames = 1;
00395 hist_cfg.hist_bins = HIST_BINS_64;
00396 hist_cfg.cfa = HIST_CFA_BAYER;
00397
00398
00399
00400
00401 hist_cfg.wg[0] = 35;
00402 hist_cfg.wg[1] = 35;
00403 hist_cfg.wg[2] = 35;
00404 hist_cfg.wg[3] = 35;
00405 hist_cfg.num_regions = histogram->regions;
00406
00407 for (int i = 0; i < histogram->regions; i++) {
00408
00409 unsigned x = ((unsigned)histogram->region[i].x * pstats.ccdc_out_w) / currentMode.width;
00410 unsigned y = ((unsigned)histogram->region[i].y * pstats.ccdc_out_h) / currentMode.height;
00411 unsigned w = ((unsigned)histogram->region[i].width * pstats.ccdc_out_w) / currentMode.width;
00412 unsigned h = ((unsigned)histogram->region[i].height * pstats.ccdc_out_h) / currentMode.height;
00413 if (x > pstats.ccdc_out_w) x = pstats.ccdc_out_w-1;
00414 if (y > pstats.ccdc_out_h) y = pstats.ccdc_out_h-1;
00415 if (w > pstats.ccdc_out_w) w = pstats.ccdc_out_w-x;
00416 if (h > pstats.ccdc_out_h) h = pstats.ccdc_out_h-y;
00417 hist_cfg.reg_hor[i] = (x << 16) | w;
00418 hist_cfg.reg_ver[i] = (y << 16) | h;
00419 printf("Histogram size: %d x %d + %d, %d\n", w, h, x, y);
00420 }
00421
00422 printf("Enabling histogram generator\n");
00423
00424 if (ioctl(fd, VIDIOC_PRIVATE_ISP_HIST_CFG, &hist_cfg)) {
00425 perror("VIDIOC_PRIVATE_ISP_HIST_CFG");
00426 return;
00427 }
00428
00429 currentHistogram = *histogram;
00430 currentHistogram.buckets = 64;
00431 }
00432
00433 void V4L2Sensor::setSharpnessMapConfig(SharpnessMapConfig *sharpness) {
00434 if (!sharpness) return;
00435 if (!sharpness->enabled) return;
00436
00437
00438 isp_pipeline_stats pstats;
00439 if (ioctl(fd, VIDIOC_PRIVATE_ISP_PIPELINE_STATS_REQ, &pstats) < 0) {
00440 perror("VIDIOC_PRIVATE_ISP_PIPELINE_STATS_REQ");
00441 return;
00442 }
00443
00444 printf("CCDC output: %d x %d\n", pstats.ccdc_out_w, pstats.ccdc_out_h);
00445 printf("PRV output: %d x %d\n", pstats.prv_out_w, pstats.prv_out_h);
00446 printf("RSZ input: %d x %d + %d, %d\n",
00447 pstats.rsz_in_w, pstats.rsz_in_h,
00448 pstats.rsz_in_x, pstats.rsz_in_y);
00449 printf("RSZ output: %d x %d\n", pstats.rsz_out_w, pstats.rsz_out_h);
00450
00451 struct af_configuration af_config;
00452
00453 af_config.alaw_enable = H3A_AF_ALAW_DISABLE;
00454 af_config.hmf_config.enable = H3A_AF_HMF_ENABLE;
00455 af_config.hmf_config.threshold = 10;
00456 af_config.rgb_pos = RG_GB_BAYER;
00457 af_config.iir_config.hz_start_pos = 0;
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472 af_config.iir_config.coeff_set0[0] = 32;
00473
00474 af_config.iir_config.coeff_set0[1] = 4096-27;
00475 af_config.iir_config.coeff_set0[2] = 6;
00476
00477 af_config.iir_config.coeff_set0[3] = 16;
00478 af_config.iir_config.coeff_set0[4] = 4096-32;
00479 af_config.iir_config.coeff_set0[5] = 16;
00480
00481 af_config.iir_config.coeff_set0[6] = 0;
00482 af_config.iir_config.coeff_set0[7] = 0;
00483
00484 af_config.iir_config.coeff_set0[8] = 32;
00485 af_config.iir_config.coeff_set0[9] = 0;
00486 af_config.iir_config.coeff_set0[10] = 0;
00487
00488
00489
00490 af_config.iir_config.coeff_set1[0] = 32;
00491
00492 af_config.iir_config.coeff_set1[1] = 0;
00493 af_config.iir_config.coeff_set1[2] = 0;
00494
00495 af_config.iir_config.coeff_set1[3] = 16;
00496 af_config.iir_config.coeff_set1[4] = 4096-32;
00497 af_config.iir_config.coeff_set1[5] = 16;
00498
00499 af_config.iir_config.coeff_set1[6] = 0;
00500 af_config.iir_config.coeff_set1[7] = 0;
00501
00502 af_config.iir_config.coeff_set1[8] = 32;
00503 af_config.iir_config.coeff_set1[9] = 0;
00504 af_config.iir_config.coeff_set1[10] = 0;
00505
00506 af_config.mode = ACCUMULATOR_SUMMED;
00507 af_config.af_config = H3A_AF_CFG_ENABLE;
00508 int paxWidth = ((pstats.ccdc_out_w-4) / (2*sharpness->size.width))*2;
00509 int paxHeight = ((pstats.ccdc_out_h-4) / (2*sharpness->size.height))*2;
00510 if (paxWidth > 256) {
00511 printf("ERROR: AF paxels are too wide. Use a higher resolution sharpness map\n");
00512 return;
00513 }
00514 if (paxHeight > 256) {
00515 printf("ERROR: AF paxels are too tall. Use a higher resolution sharpness map\n");
00516 return;
00517 }
00518 if (paxWidth < 16) {
00519 printf("ERROR: AF paxels are too narrow. Use a lower resolution sharpness map\n");
00520 return;
00521 }
00522 if (paxHeight < 2) {
00523 printf("ERROR: AF paxels are too short. Use a lower resolution sharpness map\n");
00524 return;
00525 }
00526
00527
00528 af_config.paxel_config.width = (paxWidth-2)/2;
00529 af_config.paxel_config.height = (paxHeight-2)/2;
00530 af_config.paxel_config.hz_start = (pstats.ccdc_out_w - sharpness->size.width * paxWidth)/2;
00531 af_config.paxel_config.vt_start = (pstats.ccdc_out_h - sharpness->size.height * paxHeight)/2;
00532 af_config.paxel_config.hz_cnt = sharpness->size.width-1;
00533 af_config.paxel_config.vt_cnt = sharpness->size.height-1;
00534 af_config.paxel_config.line_incr = 0;
00535
00536 printf("Enabling sharpness detector\n");
00537 if (ioctl(fd, VIDIOC_PRIVATE_ISP_AF_CFG, &af_config)) {
00538 perror("VIDIOC_PRIVATE_ISP_AF_CFG");
00539 return;
00540 }
00541
00542 currentSharpness = *sharpness;
00543 }
00544
00545 void V4L2Sensor::releaseFrame(V4L2Frame *frame) {
00546
00547
00548
00549
00550
00551 v4l2_buffer buf;
00552 memset(&buf, 0, sizeof(v4l2_buffer));
00553 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00554 buf.memory = V4L2_MEMORY_MMAP;
00555 buf.index = frame->index;
00556
00557 if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
00558 perror("VIDIOC_QBUF");
00559 return;
00560 }
00561 }
00562
00563 void V4L2Sensor::setControl(unsigned int id, int value) {
00564 if (state == CLOSED) return;
00565 v4l2_control ctrl;
00566 ctrl.id = id;
00567 ctrl.value = value;
00568 if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) < 0) {
00569
00570 perror("VIDIOC_S_CTRL");
00571 return;
00572 }
00573 }
00574
00575 int V4L2Sensor::getControl(unsigned int id) {
00576 if (state == CLOSED) return -1;
00577 v4l2_control ctrl;
00578 ctrl.id = id;
00579 if (ioctl(fd, VIDIOC_G_CTRL, &ctrl) < 0) {
00580 perror("VIDIOC_G_CTRL");
00581 return -1;
00582 }
00583
00584 return ctrl.value;
00585 }
00586
00587 void V4L2Sensor::setExposure(int e) {
00588 if (state == CLOSED) return;
00589
00590 struct v4l2_control ctrl;
00591 ctrl.id = V4L2_CID_EXPOSURE;
00592 ctrl.value = e;
00593 if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) < 0) {
00594 perror("VIDIOC_S_CTRL");
00595 return;
00596 }
00597
00598 }
00599
00600 int V4L2Sensor::getExposure() {
00601 if (state == CLOSED) return -1;
00602
00603 struct v4l2_control ctrl;
00604 ctrl.id = V4L2_CID_EXPOSURE;
00605
00606 if (ioctl(fd, VIDIOC_G_CTRL, &ctrl) < 0) {
00607 perror("VIDIOC_G_CTRL");
00608 return -1;
00609 }
00610
00611 return ctrl.value;
00612 }
00613
00614 #define V4L2_CID_FRAME_TIME (V4L2_CTRL_CLASS_CAMERA | 0x10ff)
00615
00616 void V4L2Sensor::setFrameTime(int e) {
00617 if (state == CLOSED) return;
00618
00619 struct v4l2_control ctrl;
00620 ctrl.id = V4L2_CID_FRAME_TIME;
00621 ctrl.value = e;
00622 if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) < 0) {
00623 perror("VIDIOC_S_CTRL");
00624 return;
00625 }
00626 }
00627
00628 int V4L2Sensor::getFrameTime() {
00629 if (state == CLOSED) return -1;
00630
00631 struct v4l2_control ctrl;
00632 ctrl.id = V4L2_CID_FRAME_TIME;
00633
00634 if (ioctl(fd, VIDIOC_G_CTRL, &ctrl) < 0) {
00635 perror("VIDIOC_G_CTRL");
00636 return -1;
00637 }
00638
00639 return ctrl.value;
00640 }
00641
00642 void V4L2Sensor::setGain(float g) {
00643 if (state == CLOSED) return;
00644
00645 unsigned int gain;
00646 struct v4l2_control ctrl;
00647
00648 gain = (int)(g * 32.0 + 0.5);
00649
00650 ctrl.id = V4L2_CID_GAIN;
00651 ctrl.value = gain;
00652 if (ioctl(fd, VIDIOC_S_CTRL, &ctrl) < 0) {
00653 perror("VIDIOC_S_CTRL");
00654 return;
00655 }
00656 }
00657
00658 float V4L2Sensor::getGain() {
00659 if (state == CLOSED) return -1.0f;
00660
00661 struct v4l2_control ctrl;
00662
00663 ctrl.id = V4L2_CID_GAIN;
00664 if (ioctl(fd, VIDIOC_G_CTRL, &ctrl) < 0) {
00665 perror("VIDIOC_G_CTRL");
00666 return -1.0f;
00667 }
00668
00669 return ctrl.value / 32.0f;
00670 }
00671 }}
00672
00673