github.com/aamcrae/webcam@v0.0.0-20210915060337-934acc13bdc3/v4l2.go (about) 1 package webcam 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "unsafe" 7 8 "github.com/aamcrae/webcam/ioctl" 9 "golang.org/x/sys/unix" 10 ) 11 12 type controlType int 13 14 const ( 15 c_int controlType = iota 16 c_bool 17 c_menu 18 ) 19 20 type control struct { 21 id uint32 22 name string 23 c_type controlType 24 min int32 25 max int32 26 } 27 28 const ( 29 V4L2_CAP_VIDEO_CAPTURE uint32 = 0x00000001 30 V4L2_CAP_STREAMING uint32 = 0x04000000 31 V4L2_BUF_TYPE_VIDEO_CAPTURE uint32 = 1 32 V4L2_MEMORY_MMAP uint32 = 1 33 V4L2_FIELD_ANY uint32 = 0 34 ) 35 36 const ( 37 V4L2_FRMSIZE_TYPE_DISCRETE uint32 = 1 38 V4L2_FRMSIZE_TYPE_CONTINUOUS uint32 = 2 39 V4L2_FRMSIZE_TYPE_STEPWISE uint32 = 3 40 ) 41 42 const ( 43 V4L2_CID_BASE uint32 = 0x00980900 44 V4L2_CID_AUTO_WHITE_BALANCE uint32 = V4L2_CID_BASE + 12 45 V4L2_CID_PRIVATE_BASE uint32 = 0x08000000 46 ) 47 48 const ( 49 V4L2_CTRL_TYPE_INTEGER uint32 = 1 50 V4L2_CTRL_TYPE_BOOLEAN uint32 = 2 51 V4L2_CTRL_TYPE_MENU uint32 = 3 52 V4L2_CTRL_TYPE_BUTTON uint32 = 4 53 V4L2_CTRL_TYPE_INTEGER64 uint32 = 5 54 V4L2_CTRL_TYPE_CTRL_CLASS uint32 = 6 55 V4L2_CTRL_TYPE_STRING uint32 = 7 56 V4L2_CTRL_TYPE_BITMASK uint32 = 8 57 V4L2_CTRL_TYPE_INTEGER_MENU uint32 = 9 58 59 V4L2_CTRL_COMPOUND_TYPES uint32 = 0x0100 60 V4L2_CTRL_TYPE_U8 uint32 = 0x0100 61 V4L2_CTRL_TYPE_U16 uint32 = 0x0101 62 V4L2_CTRL_TYPE_U32 uint32 = 0x0102 63 ) 64 65 const ( 66 V4L2_CTRL_FLAG_DISABLED uint32 = 0x00000001 67 V4L2_CTRL_FLAG_NEXT_CTRL uint32 = 0x80000000 68 ) 69 70 var ( 71 VIDIOC_QUERYCAP = ioctl.IoR(uintptr('V'), 0, unsafe.Sizeof(v4l2_capability{})) 72 VIDIOC_ENUM_FMT = ioctl.IoRW(uintptr('V'), 2, unsafe.Sizeof(v4l2_fmtdesc{})) 73 VIDIOC_S_FMT = ioctl.IoRW(uintptr('V'), 5, unsafe.Sizeof(v4l2_format{})) 74 VIDIOC_REQBUFS = ioctl.IoRW(uintptr('V'), 8, unsafe.Sizeof(v4l2_requestbuffers{})) 75 VIDIOC_QUERYBUF = ioctl.IoRW(uintptr('V'), 9, unsafe.Sizeof(v4l2_buffer{})) 76 VIDIOC_QBUF = ioctl.IoRW(uintptr('V'), 15, unsafe.Sizeof(v4l2_buffer{})) 77 VIDIOC_DQBUF = ioctl.IoRW(uintptr('V'), 17, unsafe.Sizeof(v4l2_buffer{})) 78 VIDIOC_G_CTRL = ioctl.IoRW(uintptr('V'), 27, unsafe.Sizeof(v4l2_control{})) 79 VIDIOC_S_CTRL = ioctl.IoRW(uintptr('V'), 28, unsafe.Sizeof(v4l2_control{})) 80 VIDIOC_QUERYCTRL = ioctl.IoRW(uintptr('V'), 36, unsafe.Sizeof(v4l2_queryctrl{})) 81 //sizeof int32 82 VIDIOC_STREAMON = ioctl.IoW(uintptr('V'), 18, 4) 83 VIDIOC_STREAMOFF = ioctl.IoW(uintptr('V'), 19, 4) 84 VIDIOC_ENUM_FRAMESIZES = ioctl.IoRW(uintptr('V'), 74, unsafe.Sizeof(v4l2_frmsizeenum{})) 85 __p = unsafe.Pointer(uintptr(0)) 86 NativeByteOrder = getNativeByteOrder() 87 ) 88 89 type v4l2_capability struct { 90 driver [16]uint8 91 card [32]uint8 92 bus_info [32]uint8 93 version uint32 94 capabilities uint32 95 device_caps uint32 96 reserved [3]uint32 97 } 98 99 type v4l2_fmtdesc struct { 100 index uint32 101 _type uint32 102 flags uint32 103 description [32]uint8 104 pixelformat uint32 105 reserved [4]uint32 106 } 107 108 type v4l2_frmsizeenum struct { 109 index uint32 110 pixel_format uint32 111 _type uint32 112 union [24]uint8 113 reserved [2]uint32 114 } 115 116 type v4l2_frmsize_discrete struct { 117 Width uint32 118 Height uint32 119 } 120 121 type v4l2_frmsize_stepwise struct { 122 Min_width uint32 123 Max_width uint32 124 Step_width uint32 125 Min_height uint32 126 Max_height uint32 127 Step_height uint32 128 } 129 130 //Hack to make go compiler properly align union 131 type v4l2_format_aligned_union struct { 132 data [200 - unsafe.Sizeof(__p)]byte 133 _ unsafe.Pointer 134 } 135 136 type v4l2_format struct { 137 _type uint32 138 union v4l2_format_aligned_union 139 } 140 141 type v4l2_pix_format struct { 142 Width uint32 143 Height uint32 144 Pixelformat uint32 145 Field uint32 146 Bytesperline uint32 147 Sizeimage uint32 148 Colorspace uint32 149 Priv uint32 150 Flags uint32 151 Ycbcr_enc uint32 152 Quantization uint32 153 Xfer_func uint32 154 } 155 156 type v4l2_requestbuffers struct { 157 count uint32 158 _type uint32 159 memory uint32 160 reserved [2]uint32 161 } 162 163 type v4l2_buffer struct { 164 index uint32 165 _type uint32 166 bytesused uint32 167 flags uint32 168 field uint32 169 timestamp unix.Timeval 170 timecode v4l2_timecode 171 sequence uint32 172 memory uint32 173 union [unsafe.Sizeof(__p)]uint8 174 length uint32 175 reserved2 uint32 176 reserved uint32 177 } 178 179 type v4l2_timecode struct { 180 _type uint32 181 flags uint32 182 frames uint8 183 seconds uint8 184 minutes uint8 185 hours uint8 186 userbits [4]uint8 187 } 188 189 type v4l2_queryctrl struct { 190 id uint32 191 _type uint32 192 name [32]uint8 193 minimum int32 194 maximum int32 195 step int32 196 default_value int32 197 flags uint32 198 reserved [2]uint32 199 } 200 201 type v4l2_control struct { 202 id uint32 203 value int32 204 } 205 206 func checkCapabilities(fd uintptr) (supportsVideoCapture bool, supportsVideoStreaming bool, err error) { 207 208 caps := &v4l2_capability{} 209 210 err = ioctl.Ioctl(fd, VIDIOC_QUERYCAP, uintptr(unsafe.Pointer(caps))) 211 212 if err != nil { 213 return 214 } 215 216 supportsVideoCapture = (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) != 0 217 supportsVideoStreaming = (caps.capabilities & V4L2_CAP_STREAMING) != 0 218 return 219 220 } 221 222 func getPixelFormat(fd uintptr, index uint32) (code uint32, description string, err error) { 223 224 fmtdesc := &v4l2_fmtdesc{} 225 226 fmtdesc.index = index 227 fmtdesc._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 228 229 err = ioctl.Ioctl(fd, VIDIOC_ENUM_FMT, uintptr(unsafe.Pointer(fmtdesc))) 230 231 if err != nil { 232 return 233 } 234 235 code = fmtdesc.pixelformat 236 description = CToGoString(fmtdesc.description[:]) 237 238 return 239 } 240 241 func getFrameSize(fd uintptr, index uint32, code uint32) (frameSize FrameSize, err error) { 242 243 frmsizeenum := &v4l2_frmsizeenum{} 244 frmsizeenum.index = index 245 frmsizeenum.pixel_format = code 246 247 err = ioctl.Ioctl(fd, VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(frmsizeenum))) 248 249 if err != nil { 250 return 251 } 252 253 switch frmsizeenum._type { 254 255 case V4L2_FRMSIZE_TYPE_DISCRETE: 256 discrete := &v4l2_frmsize_discrete{} 257 err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, discrete) 258 259 if err != nil { 260 return 261 } 262 263 frameSize.MinWidth = discrete.Width 264 frameSize.MaxWidth = discrete.Width 265 frameSize.StepWidth = 0 266 frameSize.MinHeight = discrete.Height 267 frameSize.MaxHeight = discrete.Height 268 frameSize.StepHeight = 0 269 270 case V4L2_FRMSIZE_TYPE_CONTINUOUS: 271 272 case V4L2_FRMSIZE_TYPE_STEPWISE: 273 stepwise := &v4l2_frmsize_stepwise{} 274 err = binary.Read(bytes.NewBuffer(frmsizeenum.union[:]), NativeByteOrder, stepwise) 275 276 if err != nil { 277 return 278 } 279 280 frameSize.MinWidth = stepwise.Min_width 281 frameSize.MaxWidth = stepwise.Max_width 282 frameSize.StepWidth = stepwise.Step_width 283 frameSize.MinHeight = stepwise.Min_height 284 frameSize.MaxHeight = stepwise.Max_height 285 frameSize.StepHeight = stepwise.Step_height 286 } 287 288 return 289 } 290 291 func setImageFormat(fd uintptr, formatcode, width, height, stride, size *uint32) (err error) { 292 293 format := &v4l2_format{ 294 _type: V4L2_BUF_TYPE_VIDEO_CAPTURE, 295 } 296 297 pix := v4l2_pix_format{ 298 Width: *width, 299 Height: *height, 300 Pixelformat: *formatcode, 301 Field: V4L2_FIELD_ANY, 302 } 303 304 pixbytes := &bytes.Buffer{} 305 err = binary.Write(pixbytes, NativeByteOrder, pix) 306 307 if err != nil { 308 return 309 } 310 311 copy(format.union.data[:], pixbytes.Bytes()) 312 313 err = ioctl.Ioctl(fd, VIDIOC_S_FMT, uintptr(unsafe.Pointer(format))) 314 315 if err != nil { 316 return 317 } 318 319 pixReverse := &v4l2_pix_format{} 320 err = binary.Read(bytes.NewBuffer(format.union.data[:]), NativeByteOrder, pixReverse) 321 322 if err != nil { 323 return 324 } 325 326 *width = pixReverse.Width 327 *height = pixReverse.Height 328 *formatcode = pixReverse.Pixelformat 329 *stride = pixReverse.Bytesperline 330 *size = pixReverse.Sizeimage 331 332 return 333 334 } 335 336 func mmapRequestBuffers(fd uintptr, buf_count *uint32) (err error) { 337 338 req := &v4l2_requestbuffers{} 339 req.count = *buf_count 340 req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 341 req.memory = V4L2_MEMORY_MMAP 342 343 err = ioctl.Ioctl(fd, VIDIOC_REQBUFS, uintptr(unsafe.Pointer(req))) 344 345 if err != nil { 346 return 347 } 348 349 *buf_count = req.count 350 351 return 352 353 } 354 355 func mmapQueryBuffer(fd uintptr, index uint32, length *uint32) (buffer []byte, err error) { 356 357 req := &v4l2_buffer{} 358 359 req._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 360 req.memory = V4L2_MEMORY_MMAP 361 req.index = index 362 363 err = ioctl.Ioctl(fd, VIDIOC_QUERYBUF, uintptr(unsafe.Pointer(req))) 364 365 if err != nil { 366 return 367 } 368 369 var offset uint32 370 err = binary.Read(bytes.NewBuffer(req.union[:]), NativeByteOrder, &offset) 371 372 if err != nil { 373 return 374 } 375 376 *length = req.length 377 378 buffer, err = unix.Mmap(int(fd), int64(offset), int(req.length), unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED) 379 return 380 } 381 382 func mmapDequeueBuffer(fd uintptr, index *uint32, length *uint32) (err error) { 383 384 buffer := &v4l2_buffer{} 385 386 buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 387 buffer.memory = V4L2_MEMORY_MMAP 388 389 err = ioctl.Ioctl(fd, VIDIOC_DQBUF, uintptr(unsafe.Pointer(buffer))) 390 391 if err != nil { 392 return 393 } 394 395 *index = buffer.index 396 *length = buffer.bytesused 397 398 return 399 400 } 401 402 func mmapEnqueueBuffer(fd uintptr, index uint32) (err error) { 403 404 buffer := &v4l2_buffer{} 405 406 buffer._type = V4L2_BUF_TYPE_VIDEO_CAPTURE 407 buffer.memory = V4L2_MEMORY_MMAP 408 buffer.index = index 409 410 err = ioctl.Ioctl(fd, VIDIOC_QBUF, uintptr(unsafe.Pointer(buffer))) 411 return 412 413 } 414 415 func mmapReleaseBuffer(buffer []byte) (err error) { 416 err = unix.Munmap(buffer) 417 return 418 } 419 420 func startStreaming(fd uintptr) (err error) { 421 422 var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE 423 err = ioctl.Ioctl(fd, VIDIOC_STREAMON, uintptr(unsafe.Pointer(&uintPointer))) 424 return 425 426 } 427 428 func stopStreaming(fd uintptr) (err error) { 429 430 var uintPointer uint32 = V4L2_BUF_TYPE_VIDEO_CAPTURE 431 err = ioctl.Ioctl(fd, VIDIOC_STREAMOFF, uintptr(unsafe.Pointer(&uintPointer))) 432 return 433 434 } 435 436 func FD_SET(p *unix.FdSet, i int) { 437 var l int = int(len(p.Bits)) 438 p.Bits[i/l] |= 1 << uintptr(i%l) 439 } 440 441 func waitForFrame(fd uintptr, timeout uint32) (count int, err error) { 442 443 for { 444 fds := &unix.FdSet{} 445 FD_SET(fds, int(fd)) 446 447 var oneSecInNsec int64 = 1e9 448 timeoutNsec := int64(timeout) * oneSecInNsec 449 nativeTimeVal := unix.NsecToTimeval(timeoutNsec) 450 tv := &nativeTimeVal 451 452 count, err = unix.Select(int(fd+1), fds, nil, nil, tv) 453 454 if count < 0 && err == unix.EINTR { 455 continue 456 } 457 return 458 } 459 460 } 461 462 func getControl(fd uintptr, id uint32) (int32, error) { 463 ctrl := &v4l2_control{} 464 ctrl.id = id 465 err := ioctl.Ioctl(fd, VIDIOC_G_CTRL, uintptr(unsafe.Pointer(ctrl))) 466 return ctrl.value, err 467 } 468 469 func setControl(fd uintptr, id uint32, val int32) error { 470 ctrl := &v4l2_control{} 471 ctrl.id = id 472 ctrl.value = val 473 return ioctl.Ioctl(fd, VIDIOC_S_CTRL, uintptr(unsafe.Pointer(ctrl))) 474 } 475 476 func queryControls(fd uintptr) []control { 477 controls := []control{} 478 var err error 479 // Don't use V42L_CID_BASE since it is the same as brightness. 480 var id uint32 481 for err == nil { 482 id |= V4L2_CTRL_FLAG_NEXT_CTRL 483 query := &v4l2_queryctrl{} 484 query.id = id 485 err = ioctl.Ioctl(fd, VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(query))) 486 id = query.id 487 if err == nil { 488 if (query.flags & V4L2_CTRL_FLAG_DISABLED) != 0 { 489 continue 490 } 491 var c control 492 switch query._type { 493 default: 494 continue 495 case V4L2_CTRL_TYPE_INTEGER, V4L2_CTRL_TYPE_INTEGER64: 496 c.c_type = c_int 497 case V4L2_CTRL_TYPE_BOOLEAN: 498 c.c_type = c_bool 499 case V4L2_CTRL_TYPE_MENU: 500 c.c_type = c_menu 501 } 502 c.id = id 503 c.name = CToGoString(query.name[:]) 504 c.min = query.minimum 505 c.max = query.maximum 506 controls = append(controls, c) 507 } 508 } 509 return controls 510 } 511 512 func getNativeByteOrder() binary.ByteOrder { 513 var i int32 = 0x01020304 514 u := unsafe.Pointer(&i) 515 pb := (*byte)(u) 516 b := *pb 517 if b == 0x04 { 518 return binary.LittleEndian 519 } else { 520 return binary.BigEndian 521 } 522 } 523 524 func CToGoString(c []byte) string { 525 n := -1 526 for i, b := range c { 527 if b == 0 { 528 break 529 } 530 n = i 531 } 532 return string(c[:n+1]) 533 }