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  }