control.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package v4l2
  2. /*
  3. #cgo linux CFLAGS: -I ${SRCDIR}/../include/
  4. #include <linux/videodev2.h>
  5. */
  6. import "C"
  7. import (
  8. "errors"
  9. "fmt"
  10. "unsafe"
  11. )
  12. // ControlValue represents the value for a user control.
  13. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html
  14. // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1740
  15. type CtrlValue = int32
  16. // Control (v4l2_control)
  17. //
  18. // This type is used to query/set/get user-specific controls.
  19. // For more information about user controls, see https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html.
  20. //
  21. // Also, see the followings:
  22. //
  23. // - https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1725
  24. // - https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-g-ctrl.html
  25. type Control struct {
  26. fd uintptr
  27. Type CtrlType
  28. ID CtrlID
  29. Value CtrlValue
  30. Name string
  31. Minimum int32
  32. Maximum int32
  33. Step int32
  34. Default int32
  35. flags uint32
  36. }
  37. type ControlMenuItem struct {
  38. ID uint32
  39. Index uint32
  40. Value uint32
  41. Name string
  42. }
  43. // IsMenu tests whether control Type == CtrlTypeMenu || Type == CtrlIntegerMenu
  44. func (c Control) IsMenu() bool {
  45. return c.Type == CtrlTypeMenu || c.Type == CtrlTypeIntegerMenu
  46. }
  47. // GetMenuItems returns control menu items if the associated control is a menu.
  48. func (c Control) GetMenuItems() (result []ControlMenuItem, err error) {
  49. if !c.IsMenu() {
  50. return result, fmt.Errorf("control is not a menu type")
  51. }
  52. for idx := c.Minimum; idx <= c.Maximum; idx++ {
  53. var qryMenu C.struct_v4l2_querymenu
  54. qryMenu.id = C.uint(c.ID)
  55. qryMenu.index = C.uint(idx)
  56. if err = send(c.fd, C.VIDIOC_QUERYMENU, uintptr(unsafe.Pointer(&qryMenu))); err != nil {
  57. continue
  58. }
  59. result = append(result, makeCtrlMenu(qryMenu))
  60. }
  61. return result, nil
  62. }
  63. // GetControlValue retrieves the value for a user control with the specified id.
  64. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html
  65. // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1740
  66. func GetControlValue(fd uintptr, id CtrlID) (CtrlValue, error) {
  67. var ctrl C.struct_v4l2_control
  68. ctrl.id = C.uint(id)
  69. if err := send(fd, C.VIDIOC_G_CTRL, uintptr(unsafe.Pointer(&ctrl))); err != nil {
  70. return 0, fmt.Errorf("get control value: VIDIOC_G_CTRL: id %d: %w", id, err)
  71. }
  72. return CtrlValue(ctrl.value), nil
  73. }
  74. // SetControlValue sets the value for a user control with the specified id.
  75. // This function applies range check based on the values supported by the control.
  76. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html
  77. // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1740
  78. func SetControlValue(fd uintptr, id CtrlID, val CtrlValue) error {
  79. ctrlInfo, err := QueryControlInfo(fd, id)
  80. if err != nil {
  81. return fmt.Errorf("set control value: id %s: %w", id, err)
  82. }
  83. if val < ctrlInfo.Minimum || val > ctrlInfo.Maximum {
  84. return fmt.Errorf("set control value: out-of-range failure: val %d: expected ctrl.Min %d, ctrl.Max %d", val, ctrlInfo.Minimum, ctrlInfo.Maximum)
  85. }
  86. var ctrl C.struct_v4l2_control
  87. ctrl.id = C.uint(id)
  88. ctrl.value = C.int(val)
  89. if err := send(fd, C.VIDIOC_S_CTRL, uintptr(unsafe.Pointer(&ctrl))); err != nil {
  90. return fmt.Errorf("set control value: id %d: %w", id, err)
  91. }
  92. return nil
  93. }
  94. // QueryControlInfo queries information about the specified control without the current value.
  95. func QueryControlInfo(fd uintptr, id CtrlID) (Control, error) {
  96. // query control information
  97. var qryCtrl C.struct_v4l2_queryctrl
  98. qryCtrl.id = C.uint(id)
  99. if err := send(fd, C.VIDIOC_QUERYCTRL, uintptr(unsafe.Pointer(&qryCtrl))); err != nil {
  100. return Control{}, fmt.Errorf("query control info: VIDIOC_QUERYCTRL: id %d: %w", id, err)
  101. }
  102. control := makeControl(qryCtrl)
  103. control.fd = fd
  104. return control, nil
  105. }
  106. // GetControl retrieves the value and information for the user control witht he specified id.
  107. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html
  108. func GetControl(fd uintptr, id CtrlID) (Control, error) {
  109. control, err := QueryControlInfo(fd, id)
  110. if err != nil {
  111. return Control{}, fmt.Errorf("get control: %w", err)
  112. }
  113. // retrieve control value
  114. ctrlValue, err := GetControlValue(fd, uint32(id))
  115. if err != nil {
  116. return Control{}, fmt.Errorf("get control: %w", id, err)
  117. }
  118. control.Value = ctrlValue
  119. control.fd = fd
  120. return control, nil
  121. }
  122. // QueryAllControls loop through all available user controls and returns information for
  123. // all controls without their current values (use GetControlValue to get current values).
  124. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/control.html
  125. func QueryAllControls(fd uintptr) (result []Control, err error) {
  126. cid := uint32(C.V4L2_CTRL_FLAG_NEXT_CTRL)
  127. for {
  128. control, err := QueryControlInfo(fd, cid)
  129. if err != nil {
  130. if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
  131. break
  132. }
  133. return result, fmt.Errorf("query all controls: %w", err)
  134. }
  135. result = append(result, control)
  136. // setup next id
  137. cid = control.ID | uint32(C.V4L2_CTRL_FLAG_NEXT_CTRL)
  138. }
  139. return result, nil
  140. }
  141. func makeControl(qryCtrl C.struct_v4l2_queryctrl) Control {
  142. return Control{
  143. Type: CtrlType(qryCtrl._type),
  144. ID: uint32(qryCtrl.id),
  145. Name: C.GoString((*C.char)(unsafe.Pointer(&qryCtrl.name[0]))),
  146. Maximum: int32(qryCtrl.maximum),
  147. Minimum: int32(qryCtrl.minimum),
  148. Step: int32(qryCtrl.step),
  149. Default: int32(qryCtrl.default_value),
  150. flags: uint32(qryCtrl.flags),
  151. }
  152. }
  153. func makeCtrlMenu(qryMenu C.struct_v4l2_querymenu) ControlMenuItem {
  154. return ControlMenuItem{
  155. ID: uint32(qryMenu.id),
  156. Index: uint32(qryMenu.index),
  157. Name: C.GoString((*C.char)(unsafe.Pointer(&qryMenu.anon0[0]))),
  158. }
  159. }