├── build_script.sh ├── build_script_A64.sh ├── cap.c ├── install_deps.sh ├── readme.md ├── test_ov5640.sh ├── test_ov5640_image_mode.sh ├── test_ov8865.sh └── videodev2.h /build_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Building cap with local header...." 3 | g++ cap.c -o cap $(pkg-config --libs --cflags opencv4) -lm -O3 4 | echo "done!" 5 | echo "" 6 | echo "run: ./cap 1280 768 4 1 -999 -1 -1" 7 | echo "" 8 | -------------------------------------------------------------------------------- /build_script_A64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo "Building cap with kernel header version" $(uname -r) "...." 3 | echo "Searching for: /usr/src/linux-headers-"$(uname -r) "...." 4 | gcc -D_V4L2_KERNEL_ -I/usr/src/linux-headers-$(uname -r) cap.c -o cap $(pkg-config --libs --cflags opencv) -lm -O3 5 | echo "done!" 6 | -------------------------------------------------------------------------------- /cap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * OpenCV BSD3 License 3 | * videodev2.h BSD License (dual license) 4 | * If you raise some license issue here simply don't use it and let us know 5 | * 6 | * Copyright (C) 2016 the contributors 7 | * 8 | * Luiz Vitor Martinez Cardoso 9 | * Jules Thuillier 10 | * @lex (avafinger) 11 | * 12 | * gcc cap.c -o cap $(pkg-config --libs --cflags opencv) -lm 13 | * 14 | * gcc -I/usr/src/linux-headers-VERSION/ cap.c -o cap $(pkg-config --libs --cflags opencv) -lm -O3 15 | * 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | /* ----------------------------------------------------------------------------- 31 | * BananaPi M64 / Pine64+ (A64) or if you want to control Exposure,Hflip,Vflip 32 | * ----------------------------------------------------------------------------- 33 | * _V4L2_KERNEL_ should be defined and point to: /usr/src/linux-headers-version 34 | * 35 | * build with: gcc -I/usr/src/linux-headers-3.10.102/ cap.c -o cap $(pkg-config --libs --cflags opencv) -lm -O3 36 | * 37 | * 38 | * ----------------------------------------------------------------------------- 39 | * OrangePi / BananaPi / NanoPi (H3) / BPI-M3 (A83T - ov5640 & ov8865) 40 | * ----------------------------------------------------------------------------- 41 | * _V4L2_KERNEL_ should not be defined unless you want Exposure, Hflip and Vflip 42 | * 43 | * build with: gcc cap.c -o cap $(pkg-config --libs --cflags opencv) -lm 44 | * 45 | * 46 | */ 47 | //#define _V4L2_KERNEL_ // BananaPi M64 / Pine64+ only or for setting Exposure,Hflip,Vflip 48 | 49 | #ifdef _V4L2_KERNEL_ 50 | /* --- A64 --- */ 51 | #include 52 | #else 53 | /* --- H3 / A83T --- */ 54 | #include "videodev2.h" 55 | #endif 56 | 57 | // #include 58 | // #include 59 | 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | 68 | using namespace cv; 69 | using namespace std; 70 | 71 | #ifdef _V4L2_KERNEL_ 72 | #define V4L2_MODE_VIDEO 0x0002 /* video capture */ 73 | #define V4L2_MODE_IMAGE 0x0003 /* image capture */ 74 | #define V4L2_MODE_PREVIEW 0x0004 /* preview capture */ 75 | #endif 76 | 77 | #define N_BUFFERS 4 78 | #define CAP_OK 0 79 | #define CAP_ERROR -1 80 | #define CAP_ERROR_RET(s) { \ 81 | printf("v4l2: %s\n", s); \ 82 | return CAP_ERROR; \ 83 | } 84 | #define CAP_CLIP(val, min, max) (((val) > (max)) ? (max) : (((val) < (min)) ? (min) : (val))) 85 | 86 | #define CLEAR(x) memset (&(x), 0, sizeof (x)) 87 | #define ALIGN_4K(x) (((x) + (4095)) & ~(4095)) 88 | #define ALIGN_16B(x) (((x) + (15)) & ~(15)) 89 | 90 | 91 | typedef struct { 92 | void *start; 93 | size_t length; 94 | } v4l2_buffer_t; 95 | 96 | int width; 97 | int height; 98 | v4l2_buffer_t *buffers = NULL; 99 | int n_buffers = N_BUFFERS; 100 | int sensor_video_mode; 101 | int sensor_exposure; 102 | int sensor_hflip; 103 | int sensor_vflip; 104 | 105 | double get_wall_time() 106 | { 107 | struct timeval time; 108 | if (gettimeofday(&time, NULL)) 109 | return 0.; 110 | return (double) time.tv_sec + (double) time.tv_usec * .000001; 111 | } 112 | 113 | int yuv420p_to_bgr(void *in, int length, unsigned char *out) 114 | { 115 | uint8_t *yptr, *uptr, *vptr; 116 | uint32_t x, y, p; 117 | 118 | if (length < (width * height * 3) / 2) 119 | return CAP_ERROR; 120 | 121 | yptr = (uint8_t *) in; 122 | uptr = yptr + (width * height); 123 | vptr = uptr + (width * height / 4); 124 | p = 0; 125 | 126 | for (y = 0; y < height; y++) { 127 | for (x = 0; x < width; x++) { 128 | int r, g, b; 129 | int y, u, v; 130 | 131 | y = *(yptr++) << 8; 132 | u = uptr[p] - 128; 133 | v = vptr[p] - 128; 134 | 135 | r = (y + (359 * v)) >> 8; 136 | g = (y - (88 * u) - (183 * v)) >> 8; 137 | b = (y + (454 * u)) >> 8; 138 | 139 | *(out++) += CAP_CLIP(b, 0x00, 0xFF); 140 | *(out++) += CAP_CLIP(g, 0x00, 0xFF); 141 | *(out++) += CAP_CLIP(r, 0x00, 0xFF); 142 | 143 | if (x & 1) 144 | p++; 145 | } 146 | 147 | if (!(y & 1)) 148 | p -= width / 2; 149 | } 150 | 151 | return CAP_ERROR; 152 | } 153 | 154 | static int xioctl(int fd, int request, void *arg) 155 | { 156 | int r; 157 | int tries = 3; 158 | 159 | do { 160 | r = ioctl(fd, request, arg); 161 | } while (--tries > 0 && -1 == r && EINTR == errno); 162 | 163 | return r; 164 | } 165 | 166 | int v4l2_display_sizes_pix_format(int fd) 167 | { 168 | int ret = 0; 169 | int fsizeind = 0; /*index for supported sizes*/ 170 | struct v4l2_frmsizeenum fsize; 171 | 172 | printf("V4L2 pixel sizes:\n"); 173 | 174 | CLEAR(fsize); 175 | fsize.index = 0; 176 | fsize.pixel_format = V4L2_PIX_FMT_YUV420; 177 | 178 | while ((ret = xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &fsize)) == 0) { 179 | fsize.index++; 180 | if (fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { 181 | printf("( %u x %u ) Pixels\n", fsize.discrete.width, fsize.discrete.height); 182 | fsizeind++; 183 | } 184 | } 185 | return fsizeind; 186 | } 187 | 188 | int v4l2_display_pix_format(int fd) 189 | { 190 | struct v4l2_fmtdesc fmt; 191 | int index; 192 | 193 | printf("V4L2 pixel formats:\n"); 194 | 195 | index = 0; 196 | CLEAR(fmt); 197 | fmt.index = index; 198 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 199 | 200 | while (ioctl(fd, VIDIOC_ENUM_FMT, &fmt) != -1) { 201 | printf("%i: [0x%08X] '%c%c%c%c' (%s)\n", index, fmt.pixelformat, fmt.pixelformat >> 0, fmt.pixelformat >> 8, fmt.pixelformat >> 16, fmt.pixelformat >> 24, fmt.description); 202 | 203 | memset(&fmt, 0, sizeof(fmt)); 204 | fmt.index = ++index; 205 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 206 | } 207 | // printf("\n"); 208 | } 209 | 210 | #ifdef _V4L2_KERNEL_ 211 | int v4l2_set_exposure(int fd, int exposure) 212 | { 213 | struct v4l2_queryctrl queryctrl; 214 | struct v4l2_control control; 215 | int rc; 216 | 217 | printf("set Exposure: %d\n", exposure); 218 | rc = 0; 219 | memset(&control, 0, sizeof(control)); 220 | control.id = V4L2_CID_EXPOSURE; 221 | rc = xioctl(fd, VIDIOC_G_CTRL, &control); 222 | printf("rc: %d - get exposure: %d\n", rc, control.value); 223 | control.value = exposure; 224 | rc = xioctl(fd, VIDIOC_S_CTRL, &control); 225 | printf("rc: %d - new exposure: %d\n", rc, exposure); 226 | return rc; 227 | } 228 | 229 | int v4l2_set_hflip(int fd, int hflip) 230 | { 231 | struct v4l2_queryctrl queryctrl; 232 | struct v4l2_control control; 233 | int rc; 234 | 235 | printf("set Hflip: %d\n", hflip); 236 | rc = 0; 237 | memset(&control, 0, sizeof(control)); 238 | control.id = V4L2_CID_HFLIP; 239 | rc = xioctl(fd, VIDIOC_G_CTRL, &control); 240 | printf("rc: %d - get value: %d\n", rc, control.value); 241 | control.value = hflip; 242 | rc = xioctl(fd, VIDIOC_S_CTRL, &control); 243 | printf("rc: %d - new value: %d\n", rc, control.value); 244 | return rc; 245 | } 246 | 247 | int v4l2_set_vflip(int fd, int vflip) 248 | { 249 | struct v4l2_queryctrl queryctrl; 250 | struct v4l2_control control; 251 | int rc; 252 | 253 | printf("set Vflip: %d\n", vflip); 254 | rc = 0; 255 | memset(&control, 0, sizeof(control)); 256 | control.id = V4L2_CID_VFLIP; 257 | rc = xioctl(fd, VIDIOC_G_CTRL, &control); 258 | printf("rc: %d - get value: %d\n", rc, control.value); 259 | control.value = vflip; 260 | rc = xioctl(fd, VIDIOC_S_CTRL, &control); 261 | printf("rc: %d - new value: %d\n", rc, control.value); 262 | return rc; 263 | } 264 | #endif 265 | 266 | int v4l2_init_camera(int fd) 267 | { 268 | uint32_t i; 269 | uint32_t index; 270 | struct v4l2_streamparm parms; 271 | struct v4l2_format fmt; 272 | struct v4l2_input input; 273 | struct v4l2_capability caps; 274 | 275 | CLEAR(fmt); 276 | CLEAR(input); 277 | CLEAR(caps); 278 | 279 | 280 | if (xioctl(fd, VIDIOC_QUERYCAP, &caps) == -1) { 281 | CAP_ERROR_RET("unable to query capabilities."); 282 | } 283 | 284 | if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { 285 | CAP_ERROR_RET("doesn't support video capturing."); 286 | } 287 | 288 | printf("Driver: \"%s\"\n", caps.driver); 289 | printf("Card: \"%s\"\n", caps.card); 290 | printf("Bus: \"%s\"\n", caps.bus_info); 291 | printf("Version: %d.%d\n", (caps.version >> 16) && 0xff, (caps.version >> 24) && 0xff); 292 | printf("Capabilities: %08x\n", caps.capabilities); 293 | 294 | input.index = 0; 295 | if (xioctl(fd, VIDIOC_ENUMINPUT, &input) == -1) { 296 | CAP_ERROR_RET("unable to enumerate input."); 297 | } 298 | 299 | printf("Input: %d\n", input.index); 300 | if (xioctl(fd, VIDIOC_S_INPUT, &input.index) == -1) { 301 | CAP_ERROR_RET("unable to set input."); 302 | } 303 | 304 | parms.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 305 | parms.parm.capture.capturemode = sensor_video_mode ? V4L2_MODE_VIDEO : V4L2_MODE_IMAGE; 306 | // parms.parm.capture.capturemode =0; 307 | parms.parm.capture.timeperframe.numerator = 1; 308 | parms.parm.capture.timeperframe.denominator = sensor_video_mode ? 30 : 7; 309 | if (-1 == xioctl(fd, VIDIOC_S_PARM, &parms)) { 310 | CAP_ERROR_RET("unable to set stream parm."); 311 | } 312 | 313 | v4l2_display_pix_format(fd); 314 | v4l2_display_sizes_pix_format(fd); 315 | printf("\n"); 316 | 317 | fmt.fmt.pix.width = width; 318 | fmt.fmt.pix.height = height; 319 | fmt.fmt.pix.field = V4L2_FIELD_NONE; // V4L2_FIELD_ANY; 320 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 321 | fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420; 322 | //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; 323 | 324 | if (xioctl(fd, VIDIOC_TRY_FMT, &fmt) == -1) { 325 | CAP_ERROR_RET("failed trying to set pixel format."); 326 | } 327 | 328 | if (fmt.fmt.pix.width != width || fmt.fmt.pix.height != height) { 329 | width = fmt.fmt.pix.width; 330 | height = fmt.fmt.pix.height; 331 | printf("Sensor size adjusted to: %dx%d pixels\n", width, height); 332 | } else { 333 | printf("Sensor size: %dx%d pixels\n", width, height); 334 | } 335 | 336 | if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) { 337 | CAP_ERROR_RET("failed to set pixel format."); 338 | } 339 | 340 | switch (fmt.fmt.pix.pixelformat) { 341 | case V4L2_PIX_FMT_RGB24: 342 | printf("Pixel Format: V4L2_PIX_FMT_RGB24 [0x%08X]\n",fmt.fmt.pix.pixelformat); 343 | break; 344 | 345 | case V4L2_PIX_FMT_YUV420: 346 | printf("Pixel Format: V4L2_PIX_FMT_YUV420 [0x%08X]\n",fmt.fmt.pix.pixelformat); 347 | break; 348 | 349 | } 350 | 351 | return CAP_OK; 352 | } 353 | 354 | int v4l2_set_mmap(int fd, int *buffers_count) 355 | { 356 | int i; 357 | int nbf; 358 | enum v4l2_buf_type type; 359 | struct v4l2_requestbuffers req; 360 | struct v4l2_buffer buf; 361 | 362 | CLEAR(req); 363 | req.count = sensor_video_mode ? n_buffers : 1; 364 | req.memory = V4L2_MEMORY_MMAP; 365 | req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 366 | if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) { 367 | CAP_ERROR_RET("failed requesting buffers."); 368 | } 369 | nbf = req.count; 370 | if (n_buffers != nbf) { 371 | CAP_ERROR_RET("insufficient buffer memory."); 372 | } 373 | 374 | buffers = (v4l2_buffer_t *) calloc(nbf, sizeof(v4l2_buffer_t)); 375 | if (!buffers) { 376 | CAP_ERROR_RET("failed to allocated buffers memory."); 377 | } 378 | 379 | for (i = 0; i < nbf; i++) { 380 | CLEAR(buf); 381 | buf.index = i; 382 | buf.memory = V4L2_MEMORY_MMAP; 383 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 384 | if (xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) { 385 | CAP_ERROR_RET("failed to query buffer."); 386 | } 387 | buffers[i].length = buf.length; 388 | buffers[i].start = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); 389 | if (MAP_FAILED == buffers[i].start) { 390 | CAP_ERROR_RET("failed to mmap buffer."); 391 | } 392 | } 393 | 394 | for (i = 0; i < nbf; i++) { 395 | CLEAR(buf); 396 | buf.index = i; 397 | buf.memory = V4L2_MEMORY_MMAP; 398 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 399 | 400 | if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { 401 | CAP_ERROR_RET("failed to queue buffer."); 402 | } 403 | } 404 | 405 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 406 | if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) { 407 | CAP_ERROR_RET("failed to stream on."); 408 | } 409 | 410 | *buffers_count = nbf; 411 | 412 | return CAP_OK; 413 | } 414 | 415 | int v4l2_retrieve_frame(int fd, int buffers_count, int save_frame_jpg, int save_frame_yuv) 416 | { 417 | int sz; 418 | char frame_name[128]; 419 | FILE *f; 420 | fd_set fds; 421 | IplImage *frame_ipl; 422 | CvMat frame_bgr; 423 | unsigned char *frame_yuv; 424 | struct timeval tv; 425 | struct v4l2_buffer buf; 426 | int rc; 427 | 428 | CLEAR(tv); 429 | CLEAR(buf); 430 | 431 | rc = 1; 432 | while (rc > 0) { 433 | FD_ZERO(&fds); 434 | FD_SET(fd, &fds); 435 | 436 | tv.tv_sec = 2; 437 | tv.tv_usec = 0; 438 | 439 | rc = select(fd + 1, &fds, NULL, NULL, &tv); 440 | if (-1 == rc) { 441 | if (EINTR == errno) { 442 | rc = 1; // try again 443 | continue; 444 | } 445 | CAP_ERROR_RET("failed to select frame."); 446 | } 447 | /* we got something */ 448 | break; 449 | } 450 | if (rc <= 0) { 451 | sprintf(frame_name, "errno: %d - check sensor, something wrong.", errno); 452 | CAP_ERROR_RET(frame_name); 453 | } 454 | 455 | buf.memory = V4L2_MEMORY_MMAP; 456 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 457 | if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) { 458 | CAP_ERROR_RET("failed to retrieve frame."); 459 | } 460 | 461 | printf("Length: %d \tBytesused: %d \tAddress: %p\n", buf.length, buf.bytesused, &buffers[buf.index]); 462 | 463 | if (save_frame_yuv) { 464 | sprintf(frame_name, "frame_%dx%d.yuv", width, height); 465 | f = fopen(frame_name, "wb"); 466 | if (f != NULL) { 467 | sz = ALIGN_16B(width) * height * 3 / 2; 468 | fwrite(buffers[buf.index].start, sz, 1, f); 469 | fclose(f); 470 | } 471 | } 472 | 473 | if (save_frame_jpg) { 474 | unsigned char **frame_yuv = (unsigned char **)calloc(ALIGN_16B(width) * height * 3, sizeof(*frame_yuv)); 475 | yuv420p_to_bgr(buffers[buf.index].start, buf.length, *frame_yuv); 476 | cv::Mat frame_bgr = cv::Mat(height, width, CV_8UC3, frame_yuv); 477 | sprintf(frame_name, "frame_%dx%d.jpg", width, height); 478 | imwrite(frame_name, frame_bgr); 479 | free(frame_yuv); 480 | } 481 | 482 | if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) { 483 | CAP_ERROR_RET("failed to queue buffer."); 484 | } 485 | 486 | return CAP_OK; 487 | } 488 | 489 | int v4l2_close_camera(int fd, int buffers_count) 490 | { 491 | int i; 492 | enum v4l2_buf_type type; 493 | 494 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 495 | if (xioctl(fd, VIDIOC_STREAMOFF, &type) == -1) { 496 | CAP_ERROR_RET("failed to stream off."); 497 | } 498 | 499 | for (i = 0; i < buffers_count; i++) 500 | munmap(buffers[i].start, buffers[i].length); 501 | 502 | close(fd); 503 | } 504 | 505 | int main(int argc, char *argv[]) 506 | { 507 | int i, n, save_frame; 508 | int fd; 509 | double after; 510 | double before; 511 | double avg, fps; 512 | int buffers_count; 513 | 514 | if (argc != 8) { 515 | CAP_ERROR_RET("./cap