├── LICENSE ├── README.md ├── consts.go ├── consts_arm.go ├── consts_arm64.go ├── device.go ├── examples └── record.go ├── go.mod ├── go.sum ├── init.go ├── structs.go ├── structs_arm.go └── structs_arm64.go /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Chris Hiszpanski and Jordan Carlson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-v4l2 2 | 3 | A pure Go implementation of Video4Linux2 stream capture with a simple channel 4 | based interface: 5 | 6 | * No C code. No separate cross-compiler required. 7 | * Zero copy. Memory-mapped double-buffer scheme makes kernel memory reference 8 | available via Go channel. 9 | 10 | ## Quickstart 11 | 12 | See `examples/record.go` for a more detailed example. 13 | 14 | ``` 15 | device, err := v4l2.Open("/dev/video0") 16 | 17 | device.SetPixelFormat(1280, 720, v4l2.V4L2_PIX_FMT_H264) 18 | 19 | device.SetBitrate(1000000) 20 | 21 | device.Start() 22 | 23 | for { 24 | select { 25 | case sample := <-device.C: 26 | // Each read is a H.264 NAL unit 27 | fmt.Printf("Read %d byte sample\n", len(sample.Data)) 28 | sample.Release() 29 | } 30 | } 31 | ``` 32 | 33 | Build example: 34 | 35 | `GOARCH=arm GOOS=linux go build examples/record.go` 36 | -------------------------------------------------------------------------------- /consts.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package v4l2 5 | 6 | const ( 7 | V4L2_BUF_TYPE_VIDEO_CAPTURE = 1 8 | 9 | V4L2_FIELD_ANY = 0 10 | V4L2_FIELD_NONE = 1 11 | 12 | V4L2_PIX_FMT_JPEG = 'J' | 'P'<<8 | 'E'<<16 | 'G'<<24 13 | V4L2_PIX_FMT_H264 = 'H' | '2'<<8 | '6'<<16 | '4'<<24 14 | 15 | V4L2_MEMORY_MMAP = 1 16 | 17 | VIDIOC_QUERYCAP = 0x80685600 18 | VIDIOC_REQBUFS = 0xc0145608 19 | VIDIOC_STREAMON = 0x40045612 20 | VIDIOC_STREAMOFF = 0x40045613 21 | VIDIOC_S_CTRL = 0xc008561c 22 | ) 23 | 24 | // Controls (from linux/v4l2-controls.h) 25 | const ( 26 | // Control classes 27 | V4L2_CTRL_CLASS_USER = 0x00980000 28 | V4L2_CTRL_CLASS_MPEG = 0x00990000 29 | 30 | // User-class control IDs 31 | V4L2_CID_BASE = V4L2_CTRL_CLASS_USER | 0x900 32 | V4L2_CID_USER_BASE = V4L2_CID_BASE 33 | V4L2_CID_USER_CLASS = V4L2_CTRL_CLASS_USER | 1 34 | V4L2_CID_BRIGHTNESS = V4L2_CID_BASE + 0 35 | V4L2_CID_CONTRAST = V4L2_CID_BASE + 1 36 | V4L2_CID_SATURATION = V4L2_CID_BASE + 2 37 | V4L2_CID_HUE = V4L2_CID_BASE + 3 38 | V4L2_CID_AUTO_WHITE_BALANCE = V4L2_CID_BASE + 12 39 | V4L2_CID_DO_WHITE_BALANCE = V4L2_CID_BASE + 13 40 | V4L2_CID_RED_BALANCE = V4L2_CID_BASE + 14 41 | V4L2_CID_BLUE_BALANCE = V4L2_CID_BASE + 15 42 | V4L2_CID_GAMMA = V4L2_CID_BASE + 16 43 | V4L2_CID_EXPOSURE = V4L2_CID_BASE + 17 44 | V4L2_CID_AUTOGAIN = V4L2_CID_BASE + 18 45 | V4L2_CID_GAIN = V4L2_CID_BASE + 19 46 | V4L2_CID_HFLIP = V4L2_CID_BASE + 20 47 | V4L2_CID_VFLIP = V4L2_CID_BASE + 21 48 | 49 | // MPEG-class control IDs 50 | V4L2_CID_MPEG_BASE = V4L2_CTRL_CLASS_MPEG | 0x900 51 | V4L2_CID_MPEG_CLASS = V4L2_CTRL_CLASS_MPEG | 1 52 | V4L2_CID_MPEG_VIDEO_B_FRAMES = V4L2_CID_MPEG_BASE + 202 53 | V4L2_CID_MPEG_VIDEO_GOP_SIZE = V4L2_CID_MPEG_BASE + 203 54 | V4L2_CID_MPEG_VIDEO_BITRATE = V4L2_CID_MPEG_BASE + 207 55 | V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER = V4L2_CID_MPEG_BASE + 226 56 | V4L2_CID_MPEG_VIDEO_H264_I_PERIOD = V4L2_CID_MPEG_BASE + 358 57 | V4L2_CID_MPEG_VIDEO_H264_LEVEL = V4L2_CID_MPEG_BASE + 359 58 | V4L2_CID_MPEG_VIDEO_H264_PROFILE = V4L2_CID_MPEG_BASE + 363 59 | 60 | // H.264 Levels 61 | V4L2_MPEG_VIDEO_H264_LEVEL_1_0 = 0 62 | V4L2_MPEG_VIDEO_H264_LEVEL_1B = 1 63 | V4L2_MPEG_VIDEO_H264_LEVEL_1_1 = 2 64 | V4L2_MPEG_VIDEO_H264_LEVEL_1_2 = 3 65 | V4L2_MPEG_VIDEO_H264_LEVEL_1_3 = 4 66 | V4L2_MPEG_VIDEO_H264_LEVEL_2_0 = 5 67 | V4L2_MPEG_VIDEO_H264_LEVEL_2_1 = 6 68 | V4L2_MPEG_VIDEO_H264_LEVEL_2_2 = 7 69 | V4L2_MPEG_VIDEO_H264_LEVEL_3_0 = 8 70 | V4L2_MPEG_VIDEO_H264_LEVEL_3_1 = 9 71 | V4L2_MPEG_VIDEO_H264_LEVEL_3_2 = 10 72 | V4L2_MPEG_VIDEO_H264_LEVEL_4_0 = 11 73 | V4L2_MPEG_VIDEO_H264_LEVEL_4_1 = 12 74 | V4L2_MPEG_VIDEO_H264_LEVEL_4_2 = 13 75 | V4L2_MPEG_VIDEO_H264_LEVEL_5_0 = 14 76 | V4L2_MPEG_VIDEO_H264_LEVEL_5_1 = 15 77 | 78 | // H.264 Profiles 79 | V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE = 0 80 | V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE = 1 81 | V4L2_MPEG_VIDEO_H264_PROFILE_MAIN = 2 82 | V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED = 3 83 | V4L2_MPEG_VIDEO_H264_PROFILE_HIGH = 4 84 | V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10 = 5 85 | V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422 = 6 86 | V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE = 7 87 | V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA = 8 88 | V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA = 9 89 | V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA = 10 90 | V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA = 11 91 | V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE = 12 92 | V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH = 13 93 | V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA = 14 94 | ) 95 | -------------------------------------------------------------------------------- /consts_arm.go: -------------------------------------------------------------------------------- 1 | //go:build linux && !arm64 2 | // +build linux,!arm64 3 | 4 | package v4l2 5 | 6 | const ( 7 | VIDIOC_DQBUF = 0xc0445611 8 | VIDIOC_QBUF = 0xc044560f 9 | VIDIOC_QUERYBUF = 0xc0445609 10 | VIDIOC_G_EXT_CTRLS = 0xc0185647 11 | VIDIOC_S_EXT_CTRLS = 0xc0185648 12 | VIDIOC_S_FMT = 0xc0cc5605 13 | ) 14 | -------------------------------------------------------------------------------- /consts_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build linux && arm64 2 | // +build linux,arm64 3 | 4 | package v4l2 5 | 6 | const ( 7 | VIDIOC_DQBUF = 0xc0585611 8 | VIDIOC_QBUF = 0xc058560f 9 | VIDIOC_QUERYBUF = 0xc0585609 10 | VIDIOC_G_EXT_CTRLS = 0xc0205647 11 | VIDIOC_S_EXT_CTRLS = 0xc0205648 12 | VIDIOC_S_FMT = 0xc0d05605 13 | ) 14 | -------------------------------------------------------------------------------- /device.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package v4l2 5 | 6 | import ( 7 | "io" 8 | "syscall" 9 | "unsafe" 10 | 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | const ( 15 | defaultNumBuffers = 2 16 | ) 17 | 18 | type Buffer struct { 19 | Data []byte 20 | 21 | fd int 22 | index int 23 | } 24 | 25 | // Release buffer so that it may be re-enqueued into the device 26 | func (b *Buffer) Release() error { 27 | return enqueue(b.fd, b.index) 28 | } 29 | 30 | type Device struct { 31 | C chan Buffer 32 | buffers [][]byte 33 | fd int 34 | } 35 | 36 | // Open device 37 | func Open(path string) (*Device, error) { 38 | fd, err := unix.Open(path, unix.O_RDWR, 0666) 39 | if nil != err { 40 | return nil, err 41 | } 42 | 43 | return &Device{ 44 | C: make(chan Buffer, defaultNumBuffers), 45 | buffers: make([][]byte, defaultNumBuffers), 46 | fd: fd, 47 | }, nil 48 | } 49 | 50 | // Close device 51 | func (dev *Device) Close() error { 52 | return unix.Close(dev.fd) 53 | } 54 | 55 | // SetBitrate configures the target bitrate of encoder 56 | func (dev *Device) SetBitrate(bitrate int32) error { 57 | return setCodecControl(dev.fd, V4L2_CID_MPEG_VIDEO_BITRATE, bitrate) 58 | } 59 | 60 | // SetPixelFormat configures frame geometry and pixel format. The pixel 61 | // format may be a compressor supported by the device, such as MJPEG or 62 | // H.264. 63 | func (dev *Device) SetPixelFormat(width, height, format int) error { 64 | pfmt := v4l2_pix_format{ 65 | width: uint32(width), 66 | height: uint32(height), 67 | pixelformat: uint32(format), 68 | field: V4L2_FIELD_ANY, 69 | } 70 | fmt := v4l2_format{ 71 | typ: V4L2_BUF_TYPE_VIDEO_CAPTURE, 72 | fmt: pfmt.marshal(), 73 | } 74 | return ioctl(dev.fd, VIDIOC_S_FMT, unsafe.Pointer(&fmt)) 75 | } 76 | 77 | // SetRepeatSequenceHeader configures the device to output sequence 78 | // parameter sets (SPS) and picture parameter sets (PPS) before each 79 | // group-of-pictures (GoP). This is H.264 specific and not supported by 80 | // all devices. 81 | func (dev *Device) SetRepeatSequenceHeader(on bool) error { 82 | var value int32 83 | if on { 84 | value = 1 85 | } 86 | return setCodecControl(dev.fd, V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, value) 87 | } 88 | 89 | // Start video capture 90 | func (dev *Device) Start() error { 91 | // Request specified number of kernel-space buffers from device 92 | if err := requestBuffers(dev.fd, len(dev.buffers)); nil != err { 93 | return err 94 | } 95 | 96 | // For each buffer... 97 | for i := 0; i < len(dev.buffers); i++ { 98 | // Get length and offset of i-th buffer 99 | length, offset, err := queryBuffer(dev.fd, uint32(i)) 100 | if nil != err { 101 | return err 102 | } 103 | 104 | // Memory map i-th buffer to user-space 105 | if dev.buffers[i], err = unix.Mmap( 106 | dev.fd, 107 | int64(offset), 108 | int(length), 109 | unix.PROT_READ|unix.PROT_WRITE, 110 | unix.MAP_SHARED, 111 | ); nil != err { 112 | return err 113 | } 114 | 115 | // Enqueue to device for population 116 | if err := enqueue(dev.fd, i); nil != err { 117 | return err 118 | } 119 | } 120 | 121 | go func(dev *Device) { 122 | for { 123 | // Dequeue buffer 124 | i, n, err := dequeue(dev.fd) 125 | if nil != err { 126 | if err == syscall.EINVAL { 127 | err = io.EOF 128 | } 129 | return 130 | } 131 | 132 | // Write buffer to channel 133 | // Note: Zero-copy. Slice bounds are written, not contents. 134 | dev.C <- Buffer{ 135 | Data: dev.buffers[i][:n], 136 | 137 | fd: dev.fd, 138 | index: i, 139 | } 140 | } 141 | }(dev) 142 | 143 | // Enable stream 144 | typ := V4L2_BUF_TYPE_VIDEO_CAPTURE 145 | return ioctl(dev.fd, VIDIOC_STREAMON, unsafe.Pointer(&typ)) 146 | } 147 | 148 | // Stop video capture 149 | func (dev *Device) Stop() error { 150 | // Disable stream (dequeues any outstanding buffers as well). 151 | typ := V4L2_BUF_TYPE_VIDEO_CAPTURE 152 | if err := ioctl(dev.fd, VIDIOC_STREAMOFF, unsafe.Pointer(&typ)); nil != err { 153 | return nil 154 | } 155 | 156 | // Unmap each buffer from user space 157 | for i := 0; i < len(dev.buffers); i++ { 158 | if dev.buffers[i] != nil { 159 | if err := unix.Munmap(dev.buffers[i]); err != nil { 160 | return err 161 | } 162 | dev.buffers[i] = nil 163 | } 164 | } 165 | 166 | // Count of zero frees all buffers, after aborting or finishing any 167 | // DMA in progress. 168 | return requestBuffers(dev.fd, 0) 169 | } 170 | 171 | // setCodecControl configures the value of a codec-specific control id 172 | func setCodecControl(fd int, id uint32, value int32) error { 173 | return setControl(fd, V4L2_CTRL_CLASS_MPEG, id, value) 174 | } 175 | 176 | // setControl configures the value of a control id 177 | func setControl(fd int, class, id uint32, value int32) error { 178 | const numControls = 1 179 | 180 | ctrls := [numControls]v4l2_ext_control{ 181 | v4l2_ext_control{ 182 | id: id, 183 | size: 0, 184 | }, 185 | } 186 | nativeEndian.PutUint32(ctrls[0].value[:], uint32(value)) 187 | 188 | extctrls := v4l2_ext_controls{ 189 | ctrl_class: class, 190 | count: numControls, 191 | controls: unsafe.Pointer(&ctrls), 192 | } 193 | 194 | return ioctl(fd, VIDIOC_S_EXT_CTRLS, unsafe.Pointer(&extctrls)) 195 | } 196 | 197 | // ioctl system call for device control 198 | func ioctl(fd int, req uint, arg unsafe.Pointer) error { 199 | if _, _, errno := unix.Syscall( 200 | unix.SYS_IOCTL, 201 | uintptr(fd), 202 | uintptr(req), 203 | uintptr(arg), 204 | ); errno != 0 { 205 | return errno 206 | } 207 | return nil 208 | } 209 | 210 | // Query buffer parameters. 211 | func queryBuffer(fd int, n uint32) (length, offset uint32, err error) { 212 | qb := v4l2_buffer{ 213 | index: n, 214 | typ: V4L2_BUF_TYPE_VIDEO_CAPTURE, 215 | memory: V4L2_MEMORY_MMAP, 216 | } 217 | if err = ioctl(fd, VIDIOC_QUERYBUF, unsafe.Pointer(&qb)); err != nil { 218 | return 219 | } 220 | 221 | length = qb.length 222 | offset = nativeEndian.Uint32(qb.m[0:4]) 223 | return 224 | } 225 | 226 | // Request specified number of kernel-space buffers from device 227 | func requestBuffers(fd int, n int) error { 228 | rb := v4l2_requestbuffers{ 229 | count: uint32(n), 230 | typ: V4L2_BUF_TYPE_VIDEO_CAPTURE, 231 | memory: V4L2_MEMORY_MMAP, 232 | } 233 | return ioctl(fd, VIDIOC_REQBUFS, unsafe.Pointer(&rb)) 234 | } 235 | 236 | // enqueue buffer to device 237 | func enqueue(fd int, index int) error { 238 | qbuf := v4l2_buffer{ 239 | typ: V4L2_BUF_TYPE_VIDEO_CAPTURE, 240 | memory: V4L2_MEMORY_MMAP, 241 | index: uint32(index), 242 | } 243 | return ioctl(fd, VIDIOC_QBUF, unsafe.Pointer(&qbuf)) 244 | } 245 | 246 | // dequeue next buffer from device 247 | func dequeue(fd int) (int, int, error) { 248 | dqbuf := v4l2_buffer{ 249 | typ: V4L2_BUF_TYPE_VIDEO_CAPTURE, 250 | } 251 | err := ioctl(fd, VIDIOC_DQBUF, unsafe.Pointer(&dqbuf)) 252 | return int(dqbuf.index), int(dqbuf.bytesused), err 253 | } 254 | -------------------------------------------------------------------------------- /examples/record.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Chris Hiszpanski. All rights reserved. 2 | 3 | package main 4 | 5 | import ( 6 | "flag" 7 | "log" 8 | "os" 9 | "time" 10 | 11 | "github.com/thinkski/go-v4l2" 12 | ) 13 | 14 | var flagBitrate int 15 | var flagHeight int 16 | var flagWidth int 17 | var flagOutput string 18 | 19 | func init() { 20 | const ( 21 | defaultBitrate = 3000000 22 | defaultHeight = 720 23 | defaultWidth = 1280 24 | defaultOutput = "" 25 | ) 26 | flag.IntVar(&flagBitrate, "b", defaultBitrate, "Bitrate") 27 | flag.IntVar(&flagHeight, "h", defaultHeight, "Height") 28 | flag.IntVar(&flagWidth, "w", defaultWidth, "Width") 29 | flag.StringVar(&flagOutput, "o", defaultOutput, "Output file") 30 | } 31 | 32 | func main() { 33 | flag.Parse() 34 | 35 | // Open device 36 | dev, err := v4l2.Open("/dev/video0") 37 | if nil != err { 38 | log.Fatal(err) 39 | } 40 | defer dev.Close() 41 | 42 | // Set pixel format 43 | if err := dev.SetPixelFormat( 44 | flagWidth, 45 | flagHeight, 46 | v4l2.V4L2_PIX_FMT_H264, 47 | ); nil != err { 48 | log.Fatal(err) 49 | } 50 | 51 | // Set bitrate 52 | if err := dev.SetBitrate(int32(flagBitrate)); nil != err { 53 | log.Fatal(err) 54 | } 55 | 56 | // Set timer to stop stream after ten seconds 57 | timer := time.NewTimer(10 * time.Second) 58 | 59 | // Start stream 60 | if err := dev.Start(); nil != err { 61 | log.Fatal(err) 62 | } 63 | 64 | // Open file for writing 65 | f := os.Stdout 66 | if flagOutput != "" { 67 | var err error 68 | if f, err = os.Create(flagOutput); nil != err { 69 | log.Fatal(err) 70 | } 71 | } 72 | defer f.Close() 73 | 74 | ReadLoop: 75 | for { 76 | select { 77 | case b := <-dev.C: 78 | f.Write(b.Data) 79 | b.Release() 80 | case <-timer.C: 81 | break ReadLoop 82 | } 83 | } 84 | 85 | // Stop stream 86 | if err := dev.Stop(); nil != err { 87 | log.Fatal() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/thinkski/go-v4l2 2 | 3 | go 1.13 4 | 5 | require golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 6 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1 h1:sIky/MyNRSHTrdxfsiUSS4WIAMvInbeXljJz+jDjeYE= 2 | golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 3 | -------------------------------------------------------------------------------- /init.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 The TensorFlow Authors. All Rights Reserved. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v4l2 18 | 19 | import ( 20 | "encoding/binary" 21 | "unsafe" 22 | ) 23 | 24 | // nativeEndian is the byte order for the local platform. Used to send back and 25 | // V4L2 structs with the C API. We test for endianness at runtime because 26 | // some architectures can be booted into different endian modes. 27 | var nativeEndian binary.ByteOrder 28 | 29 | func init() { 30 | buf := [2]byte{} 31 | *(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD) 32 | 33 | switch buf { 34 | case [2]byte{0xCD, 0xAB}: 35 | nativeEndian = binary.LittleEndian 36 | case [2]byte{0xAB, 0xCD}: 37 | nativeEndian = binary.BigEndian 38 | default: 39 | panic("Could not determine native endianness.") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /structs.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package v4l2 5 | 6 | import "unsafe" 7 | 8 | const ( 9 | maxSizeExtControlDotValue = 8 10 | maxSizeFormatDotFmt = 200 11 | sizePixFormat = 48 12 | ) 13 | 14 | type v4l2_capability struct { 15 | driver [16]uint8 16 | card [32]uint8 17 | bus_info [32]uint8 18 | version uint32 19 | capabilities uint32 20 | device_caps uint32 21 | reserved [3]uint32 22 | } 23 | 24 | type v4l2_pix_format struct { 25 | width uint32 26 | height uint32 27 | pixelformat uint32 28 | field uint32 29 | bytesperline uint32 30 | sizeimage uint32 31 | colorspace uint32 32 | priv uint32 33 | flags uint32 34 | xx_enc uint32 35 | quantization uint32 36 | xfer_func uint32 37 | } 38 | 39 | type v4l2_control struct { 40 | id uint32 41 | value int32 42 | } 43 | 44 | type v4l2_timecode struct { 45 | typ uint32 46 | flags uint32 47 | frames uint8 48 | seconds uint8 49 | minutes uint8 50 | hours uint8 51 | userbits [4]uint8 52 | } 53 | 54 | type v4l2_ext_control struct { 55 | id uint32 56 | size uint32 57 | reserved2 [1]uint32 58 | value [maxSizeExtControlDotValue]byte // union 59 | } 60 | 61 | type v4l2_ext_controls struct { 62 | ctrl_class uint32 63 | count uint32 64 | error_idx uint32 65 | reserved [2]uint32 66 | controls unsafe.Pointer 67 | } 68 | 69 | // marshals v4l2_pix_format struct into v4l2_format.fmt union 70 | func (pfmt *v4l2_pix_format) marshal() [maxSizeFormatDotFmt]byte { 71 | var b [maxSizeFormatDotFmt]byte 72 | 73 | copy(b[0:sizePixFormat], (*[sizePixFormat]byte)(unsafe.Pointer(pfmt))[:]) 74 | 75 | return b 76 | } 77 | -------------------------------------------------------------------------------- /structs_arm.go: -------------------------------------------------------------------------------- 1 | //go:build linux && !arm64 2 | // +build linux,!arm64 3 | 4 | package v4l2 5 | 6 | const maxSizeBufferDotM = 4 7 | 8 | type v4l2_format struct { 9 | typ uint32 10 | fmt [maxSizeFormatDotFmt]byte // union 11 | } 12 | 13 | type v4l2_requestbuffers struct { 14 | count uint32 15 | typ uint32 16 | memory uint32 17 | reserved [2]uint32 18 | } 19 | 20 | type timeval struct { 21 | tv_sec uint32 22 | tv_usec uint32 23 | } 24 | 25 | type v4l2_buffer struct { 26 | index uint32 27 | typ uint32 28 | bytesused uint32 29 | flags uint32 30 | field uint32 31 | timestamp timeval 32 | timecode v4l2_timecode 33 | sequence uint32 34 | memory uint32 35 | m [maxSizeBufferDotM]byte // union 36 | length uint32 37 | reserved2 uint32 38 | reserved uint32 39 | } 40 | -------------------------------------------------------------------------------- /structs_arm64.go: -------------------------------------------------------------------------------- 1 | //go:build linux && arm64 2 | // +build linux,arm64 3 | 4 | package v4l2 5 | 6 | const maxSizeBufferDotM = 8 7 | 8 | type v4l2_format struct { 9 | typ uint64 10 | fmt [maxSizeFormatDotFmt]byte // union 11 | } 12 | 13 | type v4l2_requestbuffers struct { 14 | count uint32 15 | typ uint32 16 | memory uint32 17 | capabilities uint32 18 | flags uint8 19 | reserved [3]uint8 20 | } 21 | 22 | type timeval struct { 23 | tv_sec uint64 24 | tv_usec uint64 25 | } 26 | 27 | type v4l2_buffer struct { 28 | index uint32 29 | typ uint32 30 | bytesused uint32 31 | flags uint32 32 | field uint32 33 | timestamp timeval 34 | timecode v4l2_timecode 35 | sequence uint32 36 | memory uint32 37 | m [maxSizeBufferDotM]byte // union 38 | length uint32 39 | reserved2 uint32 40 | reserved uint32 41 | } 42 | --------------------------------------------------------------------------------