00001 #include "vidl_v4l2_device.h"
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "vidl_pixel_format.h"
00012 #include "vidl_v4l2_pixel_format.h"
00013
00014 extern "C" {
00015 #include <sys/types.h>
00016 #include <sys/stat.h>
00017 #include <sys/mman.h>
00018 #include <sys/ioctl.h>
00019 #include <vcl_cerrno.h>
00020 #include <fcntl.h>
00021 };
00022
00023 #include <vcl_cstring.h>
00024 #include <vcl_cstdlib.h>
00025 #include <vcl_sstream.h>
00026 #include <vcl_iostream.h>
00027
00028
00029 namespace {
00030 inline int xioctl(int fd, int request, void * arg)
00031 {
00032 int r;
00033 do { r = ioctl(fd, request, arg); }
00034 while (-1 == r && EINTR == errno);
00035 return r;
00036 }
00037
00038
00039 void double2fraction(double value, int& n, int& d) {
00040 if (value < 0.0) value = value;
00041 int a= n= (int)(value*10000+0.5);
00042 int b= d= 10000;
00043 int resto= a%b;
00044 while (resto!=0) {
00045 a=b;
00046 b=resto;
00047 resto= a% b;
00048 }
00049 n/=b;
00050 d/=b;
00051 }
00052 }
00053
00054
00055 void vidl_v4l2_device::update_controls()
00056 {
00057 for (unsigned int i=0;i<controls_.size();++i) delete controls_[i];
00058 controls_.clear();
00059
00060 struct v4l2_queryctrl ctrl;
00061 for (int indice = V4L2_CID_BASE;indice < V4L2_CID_LASTP1;indice++) {
00062 ctrl.id= indice;
00063 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) {
00064 vidl_v4l2_control *pc= vidl_v4l2_control::new_control(ctrl, fd);
00065 if (pc) controls_.push_back(pc);
00066 }
00067 }
00068
00069 for (int indice = V4L2_CID_PRIVATE_BASE;;indice++) {
00070 ctrl.id= indice;
00071 if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) {
00072 vidl_v4l2_control *pc= vidl_v4l2_control::new_control(ctrl, fd);
00073 if (pc) controls_.push_back(pc);
00074 } else break;
00075 }
00076
00077 #ifdef V4L2_CTRL_FLAG_NEXT_CTRL // apparently, not all versions of V4L2 support extended controls ... (PVr)
00078
00079 ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
00080 while (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) {
00081 if (!get_control_id(ctrl.id)) {
00082 vidl_v4l2_control *pc= vidl_v4l2_control::new_control(ctrl, fd);
00083 if (pc) controls_.push_back(pc);
00084 }
00085 ctrl.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
00086 }
00087 #endif // V4L2_CTRL_FLAG_NEXT_CTRL
00088 }
00089
00090 void vidl_v4l2_device::reset_controls()
00091 {
00092 if (is_open()) {
00093 if (n_controls()==0)
00094 update_controls();
00095 for (int i=0;i<n_controls();++i)
00096 get_control(i)->reset();
00097 }
00098 }
00099
00100 vidl_v4l2_device::vidl_v4l2_device(const char *file)
00101 {
00102 pre_nbuffers= 4;
00103 ref_count_ = 0;
00104 dev_name_= file;
00105 fd= -1;
00106 buffers= NULL;
00107 n_buffers= 0;
00108 capturing= false;
00109 last_error="";
00110
00111 if (!open()) {
00112 vcl_cerr << "Error creating device: " << last_error << vcl_endl;
00113 return;
00114 }
00115 if (!initialize_device()) {
00116 vcl_cerr << "Error initializing device: " << last_error << vcl_endl;
00117 close();
00118 return;
00119 }
00120
00121
00122 struct v4l2_input inp;
00123
00124 #ifdef DEBUG
00125 vcl_cerr << "Looking for inputs..." << fd << vcl_endl;
00126 #endif
00127 for (inp.index=0;-1!=xioctl(fd,VIDIOC_ENUMINPUT,&inp); inp.index++) {
00128 #ifdef DEBUG
00129 vcl_cerr << "Inserting input...\n";
00130 #endif
00131 inputs_.push_back(vidl_v4l2_input(inp));
00132 }
00133 #if 0
00134 fmt.fmt.pix.width = 0;
00135 fmt.fmt.pix.height =0;
00136 #endif
00137 try_formats();
00138 update_controls();
00139 }
00140
00141 vidl_v4l2_device::~vidl_v4l2_device()
00142 {
00143 if (is_open()) {
00144 if (capturing)
00145 stop_capturing();
00146 if (buffers)
00147 uninit_mmap();
00148 close();
00149 }
00150 for (unsigned int i=0;i<controls_.size();++i) delete controls_[i];
00151 }
00152
00153 void vidl_v4l2_device::reset()
00154 {
00155 if (is_open()) {
00156 if (capturing)
00157 stop_capturing();
00158 if (buffers)
00159 uninit_mmap();
00160 close();
00161 }
00162 last_error="";
00163 if (!open()) {
00164 vcl_cerr << "Error creating device: " << last_error << vcl_endl;
00165 return;
00166 }
00167 if (!initialize_device()) {
00168 vcl_cerr << "Error initializing device: " << last_error << vcl_endl;
00169 close();
00170 return;
00171 }
00172
00173 try_formats();
00174
00175 update_controls();
00176 for (int i=0;i<n_controls();++i)
00177 get_control(i)->reset();
00178
00179 }
00180
00181 bool vidl_v4l2_device::open()
00182 {
00183 if (is_open()) close();
00184
00185 struct stat st;
00186
00187 if (-1 == stat(dev_name_.c_str(), &st)) {
00188 vcl_ostringstream f;
00189 f << "Cannot identify " << dev_name_ << ": " << vcl_strerror(errno);
00190 last_error=f.str();
00191
00192 return false;
00193 }
00194
00195 if (!S_ISCHR(st.st_mode)) {
00196 vcl_ostringstream f;
00197 f << dev_name_ << "is not a valid video device";
00198 last_error=f.str();
00199 return false;
00200 }
00201
00202 fd = ::open(dev_name_.c_str(), O_RDWR | O_NONBLOCK, 0);
00203
00204 if (-1 == fd) {
00205 vcl_ostringstream f;
00206 f << "Cannot open " << dev_name_ << ": "<< vcl_strerror(errno);
00207 last_error=f.str();
00208 return false;
00209 }
00210 return true;
00211 }
00212
00213 bool vidl_v4l2_device::initialize_device()
00214 {
00215 struct v4l2_capability cap;
00216
00217 if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
00218 vcl_ostringstream f;
00219
00220 if (EINVAL == errno) {
00221 f << dev_name_ << " is not a valid V4L2 video device";
00222 }
00223 else
00224 f << "v4l2_device -> Error in VIDIOC_QUERYCAP";
00225 close();
00226 last_error=f.str();
00227 return false;
00228 }
00229
00230 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
00231 vcl_ostringstream f;
00232 f << dev_name_ << " is not a valid video capture device";
00233 close();
00234 last_error=f.str();
00235 return false;
00236 }
00237
00238 if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
00239 vcl_ostringstream f;
00240 f << dev_name_ << " does not support streaming i/o";
00241 close();
00242 last_error=f.str();
00243 return false;
00244 }
00245
00246 card_name_= (const char *)cap.card;
00247 return true;
00248 }
00249
00250 bool vidl_v4l2_device::try_formats()
00251 {
00252
00253
00254
00255 if (set_v4l2_format(V4L2_PIX_FMT_BGR24,640,480)) return true;
00256 if (set_v4l2_format(V4L2_PIX_FMT_BGR32,640,480)) return true;
00257 if (set_v4l2_format(V4L2_PIX_FMT_RGB565,640,480)) return true;
00258 if (set_v4l2_format(V4L2_PIX_FMT_RGB555,640,480)) return true;
00259
00260 if (set_v4l2_format(V4L2_PIX_FMT_YUYV,640,480)) return true;
00261 if (set_v4l2_format(V4L2_PIX_FMT_UYVY,640,480)) return true;
00262
00263 if (set_v4l2_format(V4L2_PIX_FMT_YUV422P,640,480)) return true;
00264 if (set_v4l2_format(V4L2_PIX_FMT_YVU420,640,480)) return true;
00265 if (set_v4l2_format(V4L2_PIX_FMT_YUV420,640,480)) return true;
00266 if (set_v4l2_format(V4L2_PIX_FMT_YUV411P,640,480)) return true;
00267 if (set_v4l2_format(V4L2_PIX_FMT_YVU410,640,480)) return true;
00268
00269 if (set_v4l2_format(V4L2_PIX_FMT_GREY,640,480)) return true;
00270
00271
00272
00273 fmt.fmt.pix.width = 0;
00274 fmt.fmt.pix.height =0;
00275
00276 return false;
00277 }
00278
00279
00280
00281 bool vidl_v4l2_device::set_v4l2_format(unsigned int fourcode, int width, int height,
00282 double fps)
00283 {
00284 fmt.fmt.pix.width = 0;
00285 fmt.fmt.pix.height= 0;
00286
00287 if (is_open()) {
00288 if (capturing)
00289 stop_capturing();
00290 if (buffers)
00291 uninit_mmap();
00292 vcl_memset(&fmt, 0, sizeof(fmt));
00293
00294 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00295 fmt.fmt.pix.width = width;
00296 fmt.fmt.pix.height = height;
00297 fmt.fmt.pix.pixelformat = fourcode;
00298 fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
00299
00300 if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) {
00301 fmt.fmt.pix.width = 0;
00302 fmt.fmt.pix.height =0;
00303 return false;
00304 }
00305
00306
00307 if (fps!=0.0) {
00308 struct v4l2_streamparm sfrate;
00309 vcl_memset(&sfrate, 0, sizeof(sfrate));
00310 sfrate.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00311
00312
00313 frame_rate=0;
00314 int n = 0, d = 0;
00315 double2fraction(fps,n,d);
00316 sfrate.parm.capture.timeperframe.numerator = d;
00317 sfrate.parm.capture.timeperframe.denominator = n;
00318
00319 if ( xioctl(fd, VIDIOC_S_PARM, &sfrate)== 0) {
00320 if ( xioctl(fd, VIDIOC_G_PARM, &sfrate)== 0) {
00321 frame_rate = (double)sfrate.parm.capture.timeperframe.denominator
00322 / (double)sfrate.parm.capture.timeperframe.numerator;
00323 }
00324 }
00325 }
00326
00327 if (init_mmap(pre_nbuffers))
00328 return true;
00329 else {
00330 fmt.fmt.pix.width = 0;
00331 fmt.fmt.pix.height =0;
00332 return false;
00333 }
00334 }
00335 else return false;
00336 }
00337
00338
00339 bool vidl_v4l2_device::init_mmap(int reqbuf)
00340 {
00341 if (!format_is_set())
00342 if (!try_formats()) return false;
00343
00344 struct v4l2_requestbuffers req;
00345
00346 vcl_memset(&req, 0, sizeof(req));
00347
00348 req.count = reqbuf;
00349 req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00350 req.memory = V4L2_MEMORY_MMAP;
00351
00352 if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
00353 if (EINVAL == errno) {
00354 vcl_ostringstream f;
00355 f << dev_name_ << " does not support memory mapping";
00356 last_error=f.str();
00357 return false;
00358 } else {
00359 last_error = "v4l2_device -> VIDEOC_REQBUFS";
00360 return false;
00361 }
00362 }
00363
00364 if (req.count < 1) {
00365 vcl_ostringstream f;
00366 f<< "Insufficient buffer memory on " << dev_name_ ;
00367 last_error=f.str();
00368 return false;
00369 }
00370
00371 buffers = (struct buffer*)vcl_calloc(req.count, sizeof(*buffers));
00372
00373 if (!buffers) {
00374 last_error= "Out of memory reserving buffers";
00375 return false;
00376 }
00377
00378 for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
00379 #if 0
00380 struct v4l2_buffer buf; vcl_memset(&buf, 0, sizeof(buf));
00381 #endif
00382 vcl_memset(&(buffers[n_buffers].buf), 0, sizeof(struct v4l2_buffer) );
00383
00384 buffers[n_buffers].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00385 buffers[n_buffers].buf.memory = V4L2_MEMORY_MMAP;
00386 buffers[n_buffers].buf.index = n_buffers;
00387
00388 if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buffers[n_buffers].buf)) {
00389 last_error= "v4l2_device -> VIDIOC_QUERYBUF";
00390 vcl_free(buffers); buffers=NULL;
00391 return false;
00392 }
00393
00394 #if 0
00395 buffers[n_buffers].length = buf.length;
00396 #endif
00397 buffers[n_buffers].start =
00398 mmap(NULL ,
00399 buffers[n_buffers].buf.length,
00400 PROT_READ | PROT_WRITE ,
00401 MAP_SHARED ,
00402 fd, buffers[n_buffers].buf.m.offset);
00403
00404 if (MAP_FAILED == buffers[n_buffers].start) {
00405 last_error= "v4l2_device -> mmap";
00406 vcl_free(buffers); buffers=NULL;
00407 return false;
00408 }
00409 }
00410 last_buffer= -1;
00411 return true;
00412 }
00413
00414
00415 bool vidl_v4l2_device::set_number_of_buffers(unsigned int nb){
00416 if (nb==0) return false;
00417 if (pre_nbuffers==nb) return true;
00418 pre_nbuffers= nb;
00419 if (capturing) stop_capturing();
00420 if (buffers) {
00421 uninit_mmap();
00422 return init_mmap(pre_nbuffers);
00423 }
00424 return true;
00425 }
00426
00427
00428 bool vidl_v4l2_device::start_capturing()
00429 {
00430 if (capturing) return true;
00431 if (!buffers)
00432 if (!init_mmap(pre_nbuffers))
00433 return false;
00434
00435 enum v4l2_buf_type type;
00436
00437 for (unsigned int i = 0; i < n_buffers; ++i) {
00438 struct v4l2_buffer buf;
00439 vcl_memset(&buf, 0, sizeof(buf));
00440
00441 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00442 buf.memory = V4L2_MEMORY_MMAP;
00443 buf.index = i;
00444
00445 if (-1 == xioctl(fd, VIDIOC_QBUF, &buf)){
00446 last_error= "v4l2_device -> VIDIOC_QBUF";
00447 return false;
00448 }
00449 }
00450
00451 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00452
00453 if (-1 == xioctl(fd, VIDIOC_STREAMON, &type)){
00454 last_error= "v4l2_device -> VIDIOC_STREAMON";
00455 return false;
00456 }
00457 capturing= true;
00458 last_buffer= -1;
00459 return true;
00460 }
00461
00462
00463 bool vidl_v4l2_device::read_frame()
00464 {
00465 if (!capturing) return false;
00466
00467 if (last_buffer!=-1)
00468 if (-1 == xioctl(fd, VIDIOC_QBUF, &(buffers[last_buffer].buf) ) ) {
00469 last_error= "read_frame: VIDIOC_QBUF";
00470 return false;
00471 }
00472
00473 struct v4l2_buffer buf;
00474 bool completed= false;
00475 do {
00476 fd_set fds;
00477 struct timeval tv;
00478 int r;
00479
00480 FD_ZERO(&fds);
00481 FD_SET(fd, &fds);
00482
00483
00484 tv.tv_sec = 5;
00485 tv.tv_usec = 0;
00486
00487 r = select(fd + 1, &fds, NULL, NULL, &tv);
00488 if (-1 == r) {
00489 if (EINTR == errno)
00490 continue;
00491
00492 last_error= "read_frame: error in select";
00493 return false;
00494 }
00495 if (0 == r) {
00496 last_error= "read_frame: select timeout";
00497 return false;
00498 }
00499
00500 vcl_memset(&buf, 0, sizeof(buf));
00501
00502 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00503 buf.memory = V4L2_MEMORY_MMAP;
00504
00505 if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
00506 if (errno!= EAGAIN) {
00507 last_error= "read_frame: VIDIOC_DQBUF";
00508 return false;
00509 }
00510 }
00511 else completed= true;
00512 }
00513 while (!completed);
00514
00515 buffers[buf.index].buf= buf;
00516 last_buffer=buf.index;
00517
00518 return true;
00519 }
00520
00521 bool vidl_v4l2_device::stop_capturing()
00522 {
00523 if (!capturing) return true;
00524
00525 enum v4l2_buf_type type;
00526
00527 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
00528
00529 if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type)){
00530 last_error= "v4l2_device -> VIDIOC_STREAMOFF";
00531 return false;
00532 }
00533 capturing= false;
00534 last_buffer= -1;
00535 return true;
00536 }
00537
00538
00539 bool vidl_v4l2_device::uninit_mmap()
00540 {
00541 for (unsigned int i = 0; i < n_buffers; ++i)
00542 if (-1 == munmap(buffers[i].start, buffers[i].buf.length)) {
00543 last_error= "v4l2_device -> munmap";
00544 return false;
00545 }
00546 vcl_free(buffers);
00547 buffers= NULL;
00548 n_buffers=0;
00549 return true;
00550 }
00551
00552 bool vidl_v4l2_device::close()
00553 {
00554 if (-1 == ::close(fd)) {
00555 last_error= "Error closing device";
00556 return false;
00557 }
00558 fd = -1;
00559 return true;
00560 }
00561
00562 unsigned int vidl_v4l2_device::current_input() const
00563 {
00564 if (!is_open())
00565 return n_inputs();
00566
00567 if (n_inputs()==0) return 0;
00568
00569 int index;
00570 if (-1==xioctl(fd,VIDIOC_G_INPUT,&index)) {
00571 last_error= "error getting current input (VIDIOC_G_INPUT)";
00572 return n_inputs();
00573 }
00574 return index;
00575 }
00576
00577 bool vidl_v4l2_device::set_input(unsigned int i)
00578 {
00579 if (current_input()==i)
00580 return true;
00581 if (!is_open() || i>=n_inputs())
00582 return false;
00583
00584 if (capturing)
00585 stop_capturing();
00586 if (buffers)
00587 uninit_mmap();
00588
00589 if (-1==xioctl(fd,VIDIOC_S_INPUT,&i))
00590 return false;
00591
00592 fmt.fmt.pix.width = 0;
00593 fmt.fmt.pix.height =0;
00594 #if 0
00595 try_formats();
00596 #endif
00597 update_controls();
00598
00599 return true;
00600 }
00601
00602
00603 vcl_ostream &
00604 operator<<(vcl_ostream &os, const vidl_v4l2_device & dev)
00605 {
00606 os << dev.device_file() << " -> " << dev.card_name()<< vcl_endl
00607 << " " << dev.n_inputs() << " inputs:"<< vcl_endl;
00608 for (unsigned int j=0;j<dev.n_inputs();++j){
00609 os << " " << j << ": " << dev.input(j).name();
00610 if (dev.input(j).is_tuner())
00611 os << " is tuner" << vcl_endl;
00612 else
00613 os << " is camera" << vcl_endl;
00614 }
00615 os << " Current input: " << dev.current_input() << vcl_endl
00616 << " Current format: " << v4l2_to_vidl(dev.get_v4l2_format())
00617 << " width: "<< dev.get_width()<< " height: " << dev.get_height() << vcl_endl;
00618 return os;
00619 }
00620