format_framesizes.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  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. type FrameSizeType = uint32
  13. const (
  14. FrameSizeTypeDiscrete FrameSizeType = C.V4L2_FRMSIZE_TYPE_DISCRETE
  15. FrameSizeTypeContinuous FrameSizeType = C.V4L2_FRMSIZE_TYPE_CONTINUOUS
  16. FrameSizeTypeStepwise FrameSizeType = C.V4L2_FRMSIZE_TYPE_STEPWISE
  17. )
  18. // FrameSizeEnum uses v4l2_frmsizeenum to get supporeted frame size for the driver based for the pixel format.
  19. // Use FrameSizeType to determine which sizes the driver support.
  20. // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L829
  21. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
  22. type FrameSizeEnum struct {
  23. Index uint32
  24. Type FrameSizeType
  25. PixelFormat FourCCType
  26. Size FrameSize
  27. }
  28. // FrameSizeDiscrete (v4l2_frmsize_discrete)
  29. // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L815
  30. type FrameSizeDiscrete struct {
  31. Width uint32 // width [pixel]
  32. Height uint32 // height [pixel]
  33. }
  34. // FrameSize stores all possible frame size information regardless of its type. It is mapped to v4l2_frmsize_stepwise.
  35. // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L820
  36. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
  37. type FrameSize struct {
  38. MinWidth uint32 // Minimum frame width [pixel]
  39. MaxWidth uint32 // Maximum frame width [pixel]
  40. StepWidth uint32 // Frame width step size [pixel]
  41. MinHeight uint32 // Minimum frame height [pixel]
  42. MaxHeight uint32 // Maximum frame height [pixel]
  43. StepHeight uint32 // Frame height step size [pixel]
  44. }
  45. // getFrameSize retrieves the supported frame size info from following union based on the type:
  46. // union {
  47. // struct v4l2_frmsize_discrete discrete;
  48. // struct v4l2_frmsize_stepwise stepwise;
  49. // }
  50. // See https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L829
  51. func getFrameSize(frmSizeEnum C.struct_v4l2_frmsizeenum) FrameSizeEnum {
  52. frameSize := FrameSizeEnum{Type: FrameSizeType(frmSizeEnum._type), PixelFormat: FourCCType(frmSizeEnum.pixel_format)}
  53. switch frameSize.Type {
  54. case FrameSizeTypeDiscrete:
  55. fsDiscrete := *(*FrameSizeDiscrete)(unsafe.Pointer(&frmSizeEnum.anon0[0]))
  56. frameSize.Size.MinWidth = fsDiscrete.Width
  57. frameSize.Size.MinHeight = fsDiscrete.Height
  58. frameSize.Size.MaxWidth = fsDiscrete.Width
  59. frameSize.Size.MaxHeight = fsDiscrete.Height
  60. case FrameSizeTypeStepwise, FrameSizeTypeContinuous:
  61. // Calculate pointer to access stepwise member
  62. frameSize.Size = *(*FrameSize)(unsafe.Pointer(uintptr(unsafe.Pointer(&frmSizeEnum.anon0[0])) + unsafe.Sizeof(FrameSizeDiscrete{})))
  63. default:
  64. }
  65. return frameSize
  66. }
  67. // GetFormatFrameSize returns a supported device frame size for a specified encoding at index
  68. func GetFormatFrameSize(fd uintptr, index uint32, encoding FourCCType) (FrameSizeEnum, error) {
  69. var frmSizeEnum C.struct_v4l2_frmsizeenum
  70. frmSizeEnum.index = C.uint(index)
  71. frmSizeEnum.pixel_format = C.uint(encoding)
  72. if err := send(fd, C.VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(&frmSizeEnum))); err != nil {
  73. return FrameSizeEnum{}, fmt.Errorf("frame size: index %d: %w", index, err)
  74. }
  75. return getFrameSize(frmSizeEnum), nil
  76. }
  77. // GetFormatFrameSizes returns all supported device frame sizes for a specified encoding
  78. func GetFormatFrameSizes(fd uintptr, encoding FourCCType) (result []FrameSizeEnum, err error) {
  79. index := uint32(0)
  80. for {
  81. var frmSizeEnum C.struct_v4l2_frmsizeenum
  82. frmSizeEnum.index = C.uint(index)
  83. frmSizeEnum.pixel_format = C.uint(encoding)
  84. if err = send(fd, C.VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(&frmSizeEnum))); err != nil {
  85. if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
  86. break
  87. }
  88. return result, fmt.Errorf("frame sizes: encoding %s: %w", PixelFormats[encoding], err)
  89. }
  90. // At index 0, check the frame type, if not discrete exit loop.
  91. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
  92. result = append(result, getFrameSize(frmSizeEnum))
  93. if index == 0 && uint32(frmSizeEnum._type) != FrameSizeTypeDiscrete {
  94. break
  95. }
  96. index++
  97. }
  98. return result, nil
  99. }
  100. // GetAllFormatFrameSizes returns all supported frame sizes for all supported formats.
  101. // It iterates from format at index 0 until it encounters and error and then stops. For
  102. // each supported format, it retrieves all supported frame sizes.
  103. func GetAllFormatFrameSizes(fd uintptr) (result []FrameSizeEnum, err error) {
  104. formats, err := GetAllFormatDescriptions(fd)
  105. if len(formats) == 0 && err != nil {
  106. return nil, fmt.Errorf("frame sizes: %w", err)
  107. }
  108. // for each supported format, grab frame size
  109. for _, format := range formats {
  110. index := uint32(0)
  111. for {
  112. var frmSizeEnum C.struct_v4l2_frmsizeenum
  113. frmSizeEnum.index = C.uint(index)
  114. frmSizeEnum.pixel_format = C.uint(format.PixelFormat)
  115. if err = send(fd, C.VIDIOC_ENUM_FRAMESIZES, uintptr(unsafe.Pointer(&frmSizeEnum))); err != nil {
  116. if errors.Is(err, ErrorBadArgument) && len(result) > 0 {
  117. break
  118. }
  119. return result, err
  120. }
  121. // At index 0, check the frame type, if not discrete exit loop.
  122. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-enum-framesizes.html
  123. result = append(result, getFrameSize(frmSizeEnum))
  124. if index == 0 && uint32(frmSizeEnum._type) != FrameSizeTypeDiscrete {
  125. break
  126. }
  127. index++
  128. }
  129. }
  130. return result, nil
  131. }