github.com/blackjack/webcam@v0.0.0-20230509180125-87693b3f29dc/v4l2.go (about) 1 package webcam 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "fmt" 7 "unsafe" 8 9 "github.com/blackjack/webcam/ioctl" 10 "golang.org/x/sys/unix" 11 ) 12 13 /* 14 V4L2 constants, enums, and structs are derived from the header file 15 'videodev2.h'. See the following reference for more information: 16 https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/videodev.html#videodev2-h 17 */ 18 19 type controlType int 20 21 const ( 22 c_int controlType = iota 23 c_bool 24 c_menu 25 ) 26 27 type control struct { 28 id uint32 29 name string 30 c_type controlType 31 step int32 32 min int32 33 max int32 34 } 35 36 const ( 37 V4L2_CAP_VIDEO_CAPTURE uint32 = 0x00000001 38 V4L2_CAP_STREAMING uint32 = 0x04000000 39 V4L2_BUF_TYPE_VIDEO_CAPTURE uint32 = 1 40 V4L2_MEMORY_MMAP uint32 = 1 41 V4L2_FIELD_ANY uint32 = 0 42 ) 43 44 const ( 45 V4L2_FRMSIZE_TYPE_DISCRETE uint32 = 1 46 V4L2_FRMSIZE_TYPE_CONTINUOUS uint32 = 2 47 V4L2_FRMSIZE_TYPE_STEPWISE uint32 = 3 48 ) 49 50 const ( 51 V4L2_FRMIVAL_TYPE_DISCRETE uint32 = 1 52 V4L2_FRMIVAL_TYPE_CONTINUOUS uint32 = 2 53 V4L2_FRMIVAL_TYPE_STEPWISE uint32 = 3 54 ) 55 56 const ( 57 V4L2_CID_BASE uint32 = 0x00980900 58 V4L2_CID_AUTO_WHITE_BALANCE uint32 = V4L2_CID_BASE + 12 59 V4L2_CID_PRIVATE_BASE uint32 = 0x08000000 60 ) 61 62 const ( 63 V4L2_CTRL_TYPE_INTEGER uint32 = 1 64 V4L2_CTRL_TYPE_BOOLEAN uint32 = 2 65 V4L2_CTRL_TYPE_MENU uint32 = 3 66 V4L2_CTRL_TYPE_BUTTON uint32 = 4 67 V4L2_CTRL_TYPE_INTEGER64 uint32 = 5 68 V4L2_CTRL_TYPE_CTRL_CLASS uint32 = 6 69 V4L2_CTRL_TYPE_STRING uint32 = 7 70 V4L2_CTRL_TYPE_BITMASK uint32 = 8 71 V4L2_CTRL_TYPE_INTEGER_MENU uint32 = 9 72 73 V4L2_CTRL_COMPOUND_TYPES uint32 = 0x0100 74 V4L2_CTRL_TYPE_U8 uint32 = 0x0100 75 V4L2_CTRL_TYPE_U16 uint32 = 0x0101 76 V4L2_CTRL_TYPE_U32 uint32 = 0x0102 77 ) 78 79 const ( 80 V4L2_CTRL_FLAG_DISABLED uint32 = 0x00000001 81 V4L2_CTRL_FLAG_NEXT_CTRL uint32 = 0x80000000 82 ) 83 84 var ( 85 VIDIOC_QUERYCAP = ioctl.IoR(uintptr('V'), 0, unsafe.Sizeof(v4l2_capability{})) 86 VIDIOC_ENUM_FMT = ioctl.IoRW(uintptr('V'), 2, unsafe.Sizeof(v4l2_fmtdesc{})) 87 VIDIOC_S_FMT = ioctl.IoRW(uintptr('V'), 5, unsafe.Sizeof(v4l2_format{})) 88 VIDIOC_REQBUFS = ioctl.IoRW(uintptr('V'), 8, unsafe.Sizeof(v4l2_requestbuffers{})) 89 VIDIOC_QUERYBUF = ioctl.IoRW(uintptr('V'), 9, unsafe.Sizeof(v4l2_buffer{})) 90 VIDIOC_QBUF = ioctl.IoRW(uintptr('V'), 15, unsafe.Sizeof(v4l2_buffer{})) 91 VIDIOC_DQBUF = ioctl.IoRW(uintptr('V'), 17, unsafe.Sizeof(v4l2_buffer{})) 92 VIDIOC_G_PARM = ioctl.IoRW(uintptr('V'), 21, unsafe.Sizeof(v4l2_streamparm{})) 93 VIDIOC_S_PARM = ioctl.IoRW(uintptr('V'), 22, unsafe.Sizeof(v4l2_streamparm{})) 94 VIDIOC_G_CTRL = ioctl.IoRW(uintptr('V'), 27, unsafe.Sizeof(v4l2_control{})) 95 VIDIOC_S_CTRL = ioctl.IoRW(uintptr('V'), 28, unsafe.Sizeof(v4l2_control{})) 96 VIDIOC_QUERYCTRL = ioctl.IoRW(uintptr('V'), 36, unsafe.Sizeof(v4l2_queryctrl{})) 97 //sizeof int32 98 VIDIOC_STREAMON = ioctl.IoW(uintptr('V'), 18, 4) 99 VIDIOC_STREAMOFF = ioctl.IoW(uintptr('V'), 19, 4) 100 VIDIOC_G_INPUT = ioctl.IoR(uintptr('V'), 38, 4) 101 VIDIOC_S_INPUT = ioctl.IoRW(uintptr('V'), 39, 4) 102 VIDIOC_ENUM_FRAMESIZES = ioctl.IoRW(uintptr('V'), 74, unsafe.Sizeof(v4l2_frmsizeenum{})) 103 VIDIOC_ENUM_FRAMEINTERVALS = ioctl.IoRW(uintptr('V'), 75, unsafe.Sizeof(v4l2_frmivalenum{})) 104 __p = unsafe.Pointer(uintptr(0)) 105 NativeByteOrder = getNativeByteOrder() 106 ) 107 108 type v4l2_capability struct { 109 driver [16]uint8 110 card [32]uint8 111 bus_info [32]uint8 112 version uint32 113 capabilities uint32 114 device_caps uint32 115 reserved [3]uint32 116 } 117 118 type v4l2_fmtdesc struct { 119 index uint32 120 _type uint32 121 flags uint32 122 description [32]uint8 123 pixelformat uint32 124 reserved [4]uint32 125 } 126 127 type v4l2_frmsizeenum struct { 128 index uint32 129 pixel_format uint32 130 _type uint32 131 union [24]uint8 132 reserved [2]uint32 133 } 134 135 type v4l2_frmsize_discrete struct { 136 Width uint32 137 Height uint32 138 } 139 140 type v4l2_frmsize_stepwise struct { 141 Min_width uint32 142 Max_width uint32 143 Step_width uint32 144 Min_height uint32 145 Max_height uint32 146 Step_height uint32 147 } 148 149 // v4l2_frmivalenum that contains a pixel format and size and receives a 150 // frame interval (time duration between frames of a video stream). 151 type v4l2_frmivalenum struct { 152 index uint32 153 pixel_format uint32 154 width uint32 155 height uint32 156 _type uint32 157 union [24]uint8 158 reserved [2]uint32 159 } 160 161 // v4l2_frmival_stepwise represents the frame interval range 162 // as minimum, maximum, and step size intervals. 163 type v4l2_frmival_stepwise struct { 164 min v4l2_fract // minimum frame interval [s] 165 max v4l2_fract // maximum frame interval [s] 166 step v4l2_fract // frame interval step size [s] 167 } 168 169 // Hack to make go compiler properly align union 170 type v4l2_format_aligned_union struct { 171 data [200 - unsafe.Sizeof(__p)]byte 172 _ unsafe.Pointer 173 } 174 175 type v4l2_format struct { 176 _type uint32 177 union v4l2_format_aligned_union 178 } 179 180 type v4l2_pix_format struct { 181 Width uint32 182 Height uint32 183 Pixelformat uint32 184 Field uint32 185 Bytesperline uint32 186 Sizeimage uint32 187 Colorspace uint32 188 Priv uint32 189 Flags uint32 190 Ycbcr_enc uint32 191 Quantization uint32 192 Xfer_func uint32 193 } 194 195 type v4l2_requestbuffers struct { 196 count uint32 197 _type uint32 198 memory uint32 199 reserved [2]uint32 200 } 201 202 type v4l2_buffer struct { 203 index uint32 204 _type uint32 205 bytesused uint32 206 flags uint32 207 field uint32 208 timestamp unix.Timeval 209 timecode v4l2_timecode 210 sequence uint32 211 memory uint32 212 union [unsafe.Sizeof(__p)]uint8 213 length uint32 214 reserved2 uint32 215 reserved uint32 216 } 217 218 type v4l2_timecode struct { 219 _type uint32 220 flags uint32 221 frames uint8 222 seconds uint8 223 minutes uint8 224 hours uint8 225 userbits [4]uint8 226 } 227 228 type v4l2_queryctrl struct { 229 id uint32 230 _type uint32 231 name [32]uint8 232 minimum int32 233 maximum int32 234 step int32 235 default_value int32 236 flags uint32 237 reserved [2]uint32 238 } 239 240 type v4l2_control struct { 241 id uint32 242 value int32 243 } 244 245 type v4l2_fract struct { 246 Numerator uint32 247 Denominator uint32 248 } 249 250 type v4l2_streamparm_union struct { 251 capability uint32 252 output_mode uint32 253 time_per_frame v4l2_fract 254 extended_mode uint32 255 buffers uint32 256 reserved [4]uint32 257 data [200 - (10 * unsafe.Sizeof(uint32(0)))]byte 258 } 259 260 type v4l2_streamparm struct { 261 _type uint32 262 union v4l2_streamparm_union 263 } 264 265 func checkCapabilities(fd uintptr) (supportsVideoCapture bool, supportsVideoStreaming bool, err error) { 266 267 caps := &v4l2_capability{} 268 269 err = ioctl.Ioctl(fd, VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(caps))) 270 271 if err != nil { 272 return 273 } 274 275 supportsVideoCapture = (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0 276 supportsVideoStreaming = (caps.capabilities & V4L2_CAP_STREAMING) != 0 277 return 278 279 } 280 281 func getPixelFormat(fd uintptr, index uint32) (code uint32, description string, err error) { 282 283 fmtdesc := &v4l2_fmtdesc{} 284 285 fmtdesc.index = index 286 fmtdesc._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 287 288 err = ioctl.Ioctl(fd, VIDIOC_ENUM_FMT, uintptr(unsafe.Pointer(fmtdesc))) 289 290 if err != nil { 291 return 292 } 293 294 code = fmtdesc.pixelformat 295 description = CToGoString(fmtdesc.description[:]) 296 297 return 298 } 299 300 func getFrameSize(fd uintptr, index uint32, code uint32) (frameSize FrameSize, err error) { 301 302 frmsizeenum := &v4l2_frmsizeenum{} 303 frmsizeenum.index = index 304 frmsizeenum.pixel_format = code 305 306 err = ioctl.Ioctl(fd, VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(frmsizeenum))) 307 308 if err != nil { 309 return 310 } 311 312 switch frmsizeenum._type { 313 314 case V4L2_FRMSIZE_TYPE_DISCRETE: 315 discrete := &v4l2_frmsize_discrete{} 316 err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, discrete) 317 318 if err != nil { 319 return 320 } 321 322 frameSize.MinWidth = discrete.Width 323 frameSize.MaxWidth = discrete.Width 324 frameSize.StepWidth = 0 325 frameSize.MinHeight = discrete.Height 326 frameSize.MaxHeight = discrete.Height 327 frameSize.StepHeight = 0 328 329 case V4L2_FRMSIZE_TYPE_CONTINUOUS: 330 331 case V4L2_FRMSIZE_TYPE_STEPWISE: 332 stepwise := &v4l2_frmsize_stepwise{} 333 err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, stepwise) 334 335 if err != nil { 336 return 337 } 338 339 frameSize.MinWidth = stepwise.Min_width 340 frameSize.MaxWidth = stepwise.Max_width 341 frameSize.StepWidth = stepwise.Step_width 342 frameSize.MinHeight = stepwise.Min_height 343 frameSize.MaxHeight = stepwise.Max_height 344 frameSize.StepHeight = stepwise.Step_height 345 } 346 347 return 348 } 349 350 func getName(fd uintptr) (string, error) { 351 var caps v4l2_capability 352 if err := ioctl.Ioctl(fd, VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(&caps))); err != nil { 353 return "", err 354 } 355 356 return CToGoString(caps.card[:]), nil 357 } 358 359 func getFrameInterval(fd uintptr, index uint32, code uint32, width uint32, height uint32) (FrameRate, error) { 360 frmivalEnum := &v4l2_frmivalenum{ 361 index: index, 362 pixel_format: code, 363 width: width, 364 height: height, 365 } 366 367 if err := ioctl.Ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, uintptr(unsafe.Pointer(frmivalEnum))); err != nil { 368 return FrameRate{}, err 369 } 370 371 switch frmivalEnum._type { 372 373 case V4L2_FRMIVAL_TYPE_DISCRETE: 374 discrete := &v4l2_fract{} 375 if err := binary.Read(bytes.NewBuffer(frmivalEnum.union[:]), NativeByteOrder, discrete); err != nil { 376 return FrameRate{}, err 377 } 378 return FrameRate{ 379 MinDenominator: discrete.Denominator, 380 MaxDenominator: discrete.Denominator, 381 StepDenominator: 0, 382 MinNumerator: discrete.Numerator, 383 MaxNumerator: discrete.Numerator, 384 StepNumerator: 0, 385 }, nil 386 387 case V4L2_FRMIVAL_TYPE_CONTINUOUS: 388 return FrameRate{}, fmt.Errorf("V4L2_FRMIVAL_TYPE_CONTINUOUS not implemented") 389 390 case V4L2_FRMIVAL_TYPE_STEPWISE: 391 stepwise := &v4l2_frmival_stepwise{} 392 if err := binary.Read(bytes.NewBuffer(frmivalEnum.union[:]), NativeByteOrder, stepwise); err != nil { 393 return FrameRate{}, err 394 } 395 return FrameRate{ 396 MinDenominator: stepwise.min.Denominator, 397 MaxDenominator: stepwise.max.Denominator, 398 StepDenominator: stepwise.step.Denominator, 399 MinNumerator: stepwise.min.Numerator, 400 MaxNumerator: stepwise.max.Numerator, 401 StepNumerator: stepwise.step.Numerator, 402 }, nil 403 } 404 405 return FrameRate{}, fmt.Errorf("unknown frame interval type") 406 } 407 408 func getBusInfo(fd uintptr) (string, error) { 409 var caps v4l2_capability 410 if err := ioctl.Ioctl(fd, VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(&caps))); err != nil { 411 return "", err 412 } 413 414 return CToGoString(caps.bus_info[:]), nil 415 } 416 417 func setImageFormat(fd uintptr, formatcode *uint32, width *uint32, height *uint32) (err error) { 418 419 format := &v4l2_format{ 420 _type: V4L2_BUF_TYPE_VIDEO_CAPTURE, 421 } 422 423 pix := v4l2_pix_format{ 424 Width: *width, 425 Height: *height, 426 Pixelformat: *formatcode, 427 Field: V4L2_FIELD_ANY, 428 } 429 430 pixbytes := &bytes.Buffer{} 431 err = binary.Write(pixbytes, NativeByteOrder, pix) 432 433 if err != nil { 434 return 435 } 436 437 copy(format.union.data[:], pixbytes.Bytes()) 438 439 err = ioctl.Ioctl(fd, VIDIOC_S_FMT, uintptr(unsafe.Pointer(format))) 440 441 if err != nil { 442 return 443 } 444 445 pixReverse := &v4l2_pix_format{} 446 err = binary.Read(bytes.NewBuffer(format.union.data[:]), NativeByteOrder, pixReverse) 447 448 if err != nil { 449 return 450 } 451 452 *width = pixReverse.Width 453 *height = pixReverse.Height 454 *formatcode = pixReverse.Pixelformat 455 456 return 457 458 } 459 460 func mmapRequestBuffers(fd uintptr, buf_count *uint32) (err error) { 461 462 req := &v4l2_requestbuffers{} 463 req.count = *buf_count 464 req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 465 req.memory = V4L2_MEMORY_MMAP 466 467 err = ioctl.Ioctl(fd, VIDIOC_REQBUFS, uintptr(unsafe.Pointer(req))) 468 469 if err != nil { 470 return 471 } 472 473 *buf_count = req.count 474 475 return 476 477 } 478 479 func mmapQueryBuffer(fd uintptr, index uint32, length *uint32) (buffer []byte, err error) { 480 481 req := &v4l2_buffer{} 482 483 req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 484 req.memory = V4L2_MEMORY_MMAP 485 req.index = index 486 487 err = ioctl.Ioctl(fd, VIDIOC_QUERYBUF, uintptr(unsafe.Pointer(req))) 488 489 if err != nil { 490 return 491 } 492 493 var offset uint32 494 err = binary.Read(bytes.NewBuffer(req.union[:]), NativeByteOrder, &offset) 495 496 if err != nil { 497 return 498 } 499 500 *length = req.length 501 502 buffer, err = unix.Mmap(int(fd), int64(offset), int(req.length), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) 503 return 504 } 505 506 func mmapDequeueBuffer(fd uintptr, index *uint32, length *uint32) (err error) { 507 508 buffer := &v4l2_buffer{} 509 510 buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 511 buffer.memory = V4L2_MEMORY_MMAP 512 513 err = ioctl.Ioctl(fd, VIDIOC_DQBUF, uintptr(unsafe.Pointer(buffer))) 514 515 if err != nil { 516 return 517 } 518 519 *index = buffer.index 520 *length = buffer.bytesused 521 522 return 523 524 } 525 526 func mmapEnqueueBuffer(fd uintptr, index uint32) (err error) { 527 528 buffer := &v4l2_buffer{} 529 530 buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 531 buffer.memory = V4L2_MEMORY_MMAP 532 buffer.index = index 533 534 err = ioctl.Ioctl(fd, VIDIOC_QBUF, uintptr(unsafe.Pointer(buffer))) 535 return 536 537 } 538 539 func mmapReleaseBuffer(buffer []byte) (err error) { 540 err = unix.Munmap(buffer) 541 return 542 } 543 544 func startStreaming(fd uintptr) (err error) { 545 546 var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE 547 err = ioctl.Ioctl(fd, VIDIOC_STREAMON, uintptr(unsafe.Pointer(&uintPointer))) 548 return 549 550 } 551 552 func stopStreaming(fd uintptr) (err error) { 553 554 var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE 555 err = ioctl.Ioctl(fd, VIDIOC_STREAMOFF, uintptr(unsafe.Pointer(&uintPointer))) 556 return 557 558 } 559 560 func waitForFrame(fd uintptr, timeout uint32) (count int, err error) { 561 562 for { 563 fds := &unix.FdSet{} 564 fds.Set(int(fd)) 565 566 var oneSecInNsec int64 = 1e9 567 timeoutNsec := int64(timeout) * oneSecInNsec 568 nativeTimeVal := unix.NsecToTimeval(timeoutNsec) 569 tv := &nativeTimeVal 570 571 count, err = unix.Select(int(fd+1), fds, nil, nil, tv) 572 573 if count < 0 && err == unix.EINTR { 574 continue 575 } 576 return 577 } 578 579 } 580 581 func getControl(fd uintptr, id uint32) (int32, error) { 582 ctrl := &v4l2_control{} 583 ctrl.id = id 584 err := ioctl.Ioctl(fd, VIDIOC_G_CTRL, uintptr(unsafe.Pointer(ctrl))) 585 return ctrl.value, err 586 } 587 588 func setControl(fd uintptr, id uint32, val int32) error { 589 ctrl := &v4l2_control{} 590 ctrl.id = id 591 ctrl.value = val 592 return ioctl.Ioctl(fd, VIDIOC_S_CTRL, uintptr(unsafe.Pointer(ctrl))) 593 } 594 595 func getInput(fd uintptr) (index int32, err error) { 596 err = ioctl.Ioctl(fd, VIDIOC_G_INPUT, uintptr(unsafe.Pointer(&index))) 597 return 598 } 599 600 func selectInput(fd uintptr, index uint32) (err error) { 601 err = ioctl.Ioctl(fd, VIDIOC_S_INPUT, uintptr(unsafe.Pointer(&index))) 602 return 603 } 604 605 func getFramerate(fd uintptr) (float32, error) { 606 param := &v4l2_streamparm{} 607 param._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 608 609 err := ioctl.Ioctl(fd, VIDIOC_G_PARM, uintptr(unsafe.Pointer(param))) 610 if err != nil { 611 return 0, err 612 } 613 tf := param.union.time_per_frame 614 if tf.Denominator == 0 || tf.Numerator == 0 { 615 return 0, fmt.Errorf("Invalid framerate (%d/%d)", tf.Denominator, tf.Numerator) 616 } 617 return float32(tf.Denominator) / float32(tf.Numerator), nil 618 } 619 620 func setFramerate(fd uintptr, num, denom uint32) error { 621 param := &v4l2_streamparm{} 622 param._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 623 param.union.time_per_frame.Numerator = num 624 param.union.time_per_frame.Denominator = denom 625 return ioctl.Ioctl(fd, VIDIOC_S_PARM, uintptr(unsafe.Pointer(param))) 626 } 627 628 func queryControls(fd uintptr) []control { 629 controls := []control{} 630 var err error 631 // Don't use V42L_CID_BASE since it is the same as brightness. 632 var id uint32 633 for err == nil { 634 id |= V4L2_CTRL_FLAG_NEXT_CTRL 635 query := &v4l2_queryctrl{} 636 query.id = id 637 err = ioctl.Ioctl(fd, VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(query))) 638 id = query.id 639 if err == nil { 640 if (query.flags & V4L2_CTRL_FLAG_DISABLED) != 0 { 641 continue 642 } 643 var c control 644 switch query._type { 645 default: 646 continue 647 case V4L2_CTRL_TYPE_INTEGER, V4L2_CTRL_TYPE_INTEGER64: 648 c.c_type = c_int 649 case V4L2_CTRL_TYPE_BOOLEAN: 650 c.c_type = c_bool 651 case V4L2_CTRL_TYPE_MENU: 652 c.c_type = c_menu 653 } 654 c.id = id 655 c.name = CToGoString(query.name[:]) 656 c.min = query.minimum 657 c.max = query.maximum 658 c.step = query.step 659 controls = append(controls, c) 660 } 661 } 662 return controls 663 } 664 665 func getNativeByteOrder() binary.ByteOrder { 666 var i int32 = 0x01020304 667 u := unsafe.Pointer(&i) 668 pb := (*byte)(u) 669 b := *pb 670 if b == 0x04 { 671 return binary.LittleEndian 672 } else { 673 return binary.BigEndian 674 } 675 } 676 677 func CToGoString(c []byte) string { 678 n := -1 679 for i, b := range c { 680 if b == 0 { 681 break 682 } 683 n = i 684 } 685 return string(c[:n+1]) 686 }