streaming.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. package v4l2
  2. // #include <linux/videodev2.h>
  3. import "C"
  4. import (
  5. "fmt"
  6. "unsafe"
  7. sys "golang.org/x/sys/unix"
  8. )
  9. // Streaming with Buffers
  10. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html
  11. // BufType (v4l2_buf_type)
  12. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html?highlight=v4l2_buf_type#c.V4L.v4l2_buf_type
  13. // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L141
  14. type BufType = uint32
  15. const (
  16. BufTypeVideoCapture BufType = C.V4L2_BUF_TYPE_VIDEO_CAPTURE
  17. BufTypeVideoOutput BufType = C.V4L2_BUF_TYPE_VIDEO_OUTPUT
  18. BufTypeOverlay BufType = C.V4L2_BUF_TYPE_VIDEO_OVERLAY
  19. )
  20. // IOType (v4l2_memory)
  21. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/mmap.html?highlight=v4l2_memory_mmap
  22. // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L188
  23. type IOType = uint32
  24. const (
  25. IOTypeMMAP IOType = C.V4L2_MEMORY_MMAP
  26. IOTypeUserPtr IOType = C.V4L2_MEMORY_USERPTR
  27. IOTypeOverlay IOType = C.V4L2_MEMORY_OVERLAY
  28. IOTypeDMABuf IOType = C.V4L2_MEMORY_DMABUF
  29. )
  30. type BufFlag = uint32
  31. const (
  32. BufFlagMapped BufFlag = C.V4L2_BUF_FLAG_MAPPED
  33. BufFlagQueued BufFlag = C.V4L2_BUF_FLAG_QUEUED
  34. BufFlagDone BufFlag = C.V4L2_BUF_FLAG_DONE
  35. BufFlagKeyFrame BufFlag = C.V4L2_BUF_FLAG_KEYFRAME
  36. BufFlagPFrame BufFlag = C.V4L2_BUF_FLAG_PFRAME
  37. BufFlagBFrame BufFlag = C.V4L2_BUF_FLAG_BFRAME
  38. BufFlagError BufFlag = C.V4L2_BUF_FLAG_ERROR
  39. BufFlagInRequest BufFlag = C.V4L2_BUF_FLAG_IN_REQUEST
  40. BufFlagTimeCode BufFlag = C.V4L2_BUF_FLAG_TIMECODE
  41. BufFlagM2MHoldCaptureBuf BufFlag = C.V4L2_BUF_FLAG_M2M_HOLD_CAPTURE_BUF
  42. BufFlagPrepared BufFlag = C.V4L2_BUF_FLAG_PREPARED
  43. BufFlagNoCacheInvalidate BufFlag = C.V4L2_BUF_FLAG_NO_CACHE_INVALIDATE
  44. BufFlagNoCacheClean BufFlag = C.V4L2_BUF_FLAG_NO_CACHE_CLEAN
  45. BufFlagTimestampMask BufFlag = C.V4L2_BUF_FLAG_TIMESTAMP_MASK
  46. BufFlagTimestampUnknown BufFlag = C.V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN
  47. BufFlagTimestampMonotonic BufFlag = C.V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
  48. BufFlagTimestampCopy BufFlag = C.V4L2_BUF_FLAG_TIMESTAMP_COPY
  49. BufFlagTimestampSourceMask BufFlag = C.V4L2_BUF_FLAG_TSTAMP_SRC_MASK
  50. BufFlagTimestampSourceEOF BufFlag = C.V4L2_BUF_FLAG_TSTAMP_SRC_EOF
  51. BufFlagTimestampSourceSOE BufFlag = C.V4L2_BUF_FLAG_TSTAMP_SRC_SOE
  52. BufFlagLast BufFlag = C.V4L2_BUF_FLAG_LAST
  53. BufFlagRequestFD BufFlag = C.V4L2_BUF_FLAG_REQUEST_FD
  54. )
  55. // TODO implement vl42_create_buffers
  56. // RequestBuffers (v4l2_requestbuffers) is used to request buffer allocation initializing
  57. // streaming for memory mapped, user pointer, or DMA buffer access.
  58. // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L949
  59. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-reqbufs.html?highlight=v4l2_requestbuffers#c.V4L.v4l2_requestbuffers
  60. type RequestBuffers struct {
  61. Count uint32
  62. StreamType uint32
  63. Memory uint32
  64. Capabilities uint32
  65. _ [1]uint32
  66. }
  67. // Buffer (v4l2_buffer) is used to send buffers info between application and driver
  68. // after streaming IO has been initialized.
  69. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html#c.V4L.v4l2_buffer
  70. // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L1037
  71. type Buffer struct {
  72. Index uint32
  73. Type uint32
  74. BytesUsed uint32
  75. Flags uint32
  76. Field uint32
  77. Timestamp sys.Timeval
  78. Timecode Timecode
  79. Sequence uint32
  80. Memory uint32
  81. Info BufferInfo // union m
  82. Length uint32
  83. Reserved2 uint32
  84. RequestFD int32
  85. }
  86. // makeBuffer makes a Buffer value from C.struct_v4l2_buffer
  87. func makeBuffer(v4l2Buf C.struct_v4l2_buffer) Buffer {
  88. return Buffer{
  89. Index: uint32(v4l2Buf.index),
  90. Type: uint32(v4l2Buf._type),
  91. BytesUsed: uint32(v4l2Buf.bytesused),
  92. Flags: uint32(v4l2Buf.flags),
  93. Field: uint32(v4l2Buf.field),
  94. Timestamp: *(*sys.Timeval)(unsafe.Pointer(&v4l2Buf.timestamp)),
  95. Timecode: *(*Timecode)(unsafe.Pointer(&v4l2Buf.timecode)),
  96. Sequence: uint32(v4l2Buf.sequence),
  97. Memory: uint32(v4l2Buf.memory),
  98. Info: *(*BufferInfo)(unsafe.Pointer(&v4l2Buf.m[0])),
  99. Length: uint32(v4l2Buf.length),
  100. Reserved2: uint32(v4l2Buf.reserved2),
  101. RequestFD: *(*int32)(unsafe.Pointer(&v4l2Buf.anon0[0])),
  102. }
  103. }
  104. // BufferInfo represents Union of several values in type Buffer
  105. // that are used to service the stream depending on the type of streaming
  106. // selected (MMap, User pointer, planar, file descriptor for DMA)
  107. type BufferInfo struct {
  108. Offset uint32
  109. UserPtr uintptr
  110. Planes *Plane
  111. FD int32
  112. }
  113. // Plane (see struct v4l2_plane) represents a plane in a multi-planar buffers
  114. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/buffer.html#c.V4L.v4l2_plane
  115. // https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/videodev2.h#L990
  116. type Plane struct {
  117. BytesUsed uint32
  118. Length uint32
  119. Info PlaneInfo // union m
  120. DataOffset uint32
  121. }
  122. // PlaneInfo representes the combination of type
  123. // of type of memory stream that can be serviced for the
  124. // associated plane.
  125. type PlaneInfo struct {
  126. MemOffset uint32
  127. UserPtr uintptr
  128. FD int32
  129. }
  130. // StreamOn requests streaming to be turned on for
  131. // capture (or output) that uses memory map, user ptr, or DMA buffers.
  132. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-streamon.html
  133. func StreamOn(dev StreamingDevice) error {
  134. bufType := dev.BufferType()
  135. if err := send(dev.Fd(), C.VIDIOC_STREAMON, uintptr(unsafe.Pointer(&bufType))); err != nil {
  136. return fmt.Errorf("stream on: %w", err)
  137. }
  138. return nil
  139. }
  140. // StreamOff requests streaming to be turned off for
  141. // capture (or output) that uses memory map, user ptr, or DMA buffers.
  142. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-streamon.html
  143. func StreamOff(dev StreamingDevice) error {
  144. bufType := dev.BufferType()
  145. if err := send(dev.Fd(), C.VIDIOC_STREAMOFF, uintptr(unsafe.Pointer(&bufType))); err != nil {
  146. return fmt.Errorf("stream off: %w", err)
  147. }
  148. return nil
  149. }
  150. // InitBuffers sends buffer allocation request (VIDIOC_REQBUFS) to initialize buffer IO
  151. // for video capture or video output when using either mem map, user pointer, or DMA buffers.
  152. // See https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-reqbufs.html#vidioc-reqbufs
  153. func InitBuffers(dev StreamingDevice) (RequestBuffers, error) {
  154. if dev.MemIOType() != IOTypeMMAP && dev.MemIOType() != IOTypeDMABuf {
  155. return RequestBuffers{}, fmt.Errorf("request buffers: %w", ErrorUnsupported)
  156. }
  157. var req C.struct_v4l2_requestbuffers
  158. req.count = C.uint(dev.BufferCount())
  159. req._type = C.uint(dev.BufferType())
  160. req.memory = C.uint(dev.MemIOType())
  161. if err := send(dev.Fd(), C.VIDIOC_REQBUFS, uintptr(unsafe.Pointer(&req))); err != nil {
  162. return RequestBuffers{}, fmt.Errorf("request buffers: %w: type not supported", err)
  163. }
  164. return *(*RequestBuffers)(unsafe.Pointer(&req)), nil
  165. }
  166. // ResetBuffers allocates a buffer of size 0 VIDIOC_REQBUFS(0) to free (or orphan) all
  167. // buffers. Useful when shuttingdown the stream.
  168. // See https://linuxtv.org/downloads/v4l-dvb-apis-new/userspace-api/v4l/vidioc-reqbufs.html
  169. func ResetBuffers(dev StreamingDevice) (RequestBuffers, error) {
  170. if dev.MemIOType() != IOTypeMMAP && dev.MemIOType() != IOTypeDMABuf {
  171. return RequestBuffers{}, fmt.Errorf("reset buffers: %w", ErrorUnsupported)
  172. }
  173. var req C.struct_v4l2_requestbuffers
  174. req.count = C.uint(0)
  175. req._type = C.uint(dev.BufferType())
  176. req.memory = C.uint(dev.MemIOType())
  177. if err := send(dev.Fd(), C.VIDIOC_REQBUFS, uintptr(unsafe.Pointer(&req))); err != nil {
  178. return RequestBuffers{}, fmt.Errorf("reset buffers VIDIOC_REQBUFS(0): %w", err)
  179. }
  180. return *(*RequestBuffers)(unsafe.Pointer(&req)), nil
  181. }
  182. // GetBuffer retrieves buffer info for allocated buffers at provided index.
  183. // This call should take place after buffers are allocated with RequestBuffers (for mmap for instance).
  184. func GetBuffer(dev StreamingDevice, index uint32) (Buffer, error) {
  185. var v4l2Buf C.struct_v4l2_buffer
  186. v4l2Buf._type = C.uint(dev.BufferType())
  187. v4l2Buf.memory = C.uint(dev.MemIOType())
  188. v4l2Buf.index = C.uint(index)
  189. if err := send(dev.Fd(), C.VIDIOC_QUERYBUF, uintptr(unsafe.Pointer(&v4l2Buf))); err != nil {
  190. return Buffer{}, fmt.Errorf("query buffer: type not supported: %w", err)
  191. }
  192. return makeBuffer(v4l2Buf), nil
  193. }
  194. // mapMemoryBuffer creates a local buffer mapped to the address space of the device specified by fd.
  195. func mapMemoryBuffer(fd uintptr, offset int64, len int) ([]byte, error) {
  196. data, err := sys.Mmap(int(fd), offset, len, sys.PROT_READ|sys.PROT_WRITE, sys.MAP_SHARED)
  197. if err != nil {
  198. return nil, fmt.Errorf("map memory buffer: %w", err)
  199. }
  200. return data, nil
  201. }
  202. // MapMemoryBuffers creates mapped memory buffers for specified buffer count of device.
  203. func MapMemoryBuffers(dev StreamingDevice) ([][]byte, error) {
  204. bufCount := int(dev.BufferCount())
  205. buffers := make([][]byte, bufCount)
  206. for i := 0; i < bufCount; i++ {
  207. buffer, err := GetBuffer(dev, uint32(i))
  208. if err != nil {
  209. return nil, fmt.Errorf("mapped buffers: %w", err)
  210. }
  211. // TODO check buffer flags for errors etc
  212. offset := buffer.Info.Offset
  213. length := buffer.Length
  214. mappedBuf, err := mapMemoryBuffer(dev.Fd(), int64(offset), int(length))
  215. if err != nil {
  216. return nil, fmt.Errorf("mapped buffers: %w", err)
  217. }
  218. buffers[i] = mappedBuf
  219. }
  220. return buffers, nil
  221. }
  222. // unmapMemoryBuffer removes the buffer that was previously mapped.
  223. func unmapMemoryBuffer(buf []byte) error {
  224. if err := sys.Munmap(buf); err != nil {
  225. return fmt.Errorf("unmap memory buffer: %w", err)
  226. }
  227. return nil
  228. }
  229. // UnmapMemoryBuffers unmaps all mapped memory buffer for device
  230. func UnmapMemoryBuffers(dev StreamingDevice) error {
  231. if dev.Buffers() == nil {
  232. return fmt.Errorf("unmap buffers: uninitialized buffers")
  233. }
  234. for i := 0; i < len(dev.Buffers()); i++ {
  235. if err := unmapMemoryBuffer(dev.Buffers()[i]); err != nil {
  236. return fmt.Errorf("unmap buffers: %w", err)
  237. }
  238. }
  239. return nil
  240. }
  241. // QueueBuffer enqueues a buffer in the device driver (as empty for capturing, or filled for video output)
  242. // when using either memory map, user pointer, or DMA buffers. Buffer is returned with
  243. // additional information about the queued buffer.
  244. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-qbuf.html#vidioc-qbuf
  245. func QueueBuffer(fd uintptr, ioType IOType, bufType BufType, index uint32) (Buffer, error) {
  246. var v4l2Buf C.struct_v4l2_buffer
  247. v4l2Buf._type = C.uint(bufType)
  248. v4l2Buf.memory = C.uint(ioType)
  249. v4l2Buf.index = C.uint(index)
  250. if err := send(fd, C.VIDIOC_QBUF, uintptr(unsafe.Pointer(&v4l2Buf))); err != nil {
  251. return Buffer{}, fmt.Errorf("buffer queue: %w", err)
  252. }
  253. return makeBuffer(v4l2Buf), nil
  254. }
  255. // DequeueBuffer dequeues a buffer in the device driver, marking it as consumed by the application,
  256. // when using either memory map, user pointer, or DMA buffers. Buffer is returned with
  257. // additional information about the dequeued buffer.
  258. // https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/vidioc-qbuf.html#vidioc-qbuf
  259. func DequeueBuffer(fd uintptr, ioType IOType, bufType BufType) (Buffer, error) {
  260. var v4l2Buf C.struct_v4l2_buffer
  261. v4l2Buf._type = C.uint(bufType)
  262. v4l2Buf.memory = C.uint(ioType)
  263. err := send(fd, C.VIDIOC_DQBUF, uintptr(unsafe.Pointer(&v4l2Buf)))
  264. if err != nil {
  265. return Buffer{}, fmt.Errorf("buffer dequeue: %w", err)
  266. }
  267. return makeBuffer(v4l2Buf), nil
  268. }