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  }