github.com/aamcrae/webcam@v0.0.0-20210915060337-934acc13bdc3/webcam.go (about) 1 // Library for working with webcams and other video capturing devices. 2 // It depends entirely on v4l2 framework, thus will compile and work 3 // only on Linux machine 4 package webcam 5 6 import ( 7 "errors" 8 "golang.org/x/sys/unix" 9 ) 10 11 // Webcam object 12 type Webcam struct { 13 fd uintptr 14 bufcount uint32 15 buffers [][]byte 16 streaming bool 17 } 18 19 type ControlID uint32 20 21 type Control struct { 22 Name string 23 Min int32 24 Max int32 25 } 26 27 // Open a webcam with a given path 28 // Checks if device is a v4l2 device and if it is 29 // capable to stream video 30 func Open(path string) (*Webcam, error) { 31 32 handle, err := unix.Open(path, unix.O_RDWR|unix.O_NONBLOCK, 0666) 33 fd := uintptr(handle) 34 35 if fd < 0 || err != nil { 36 return nil, err 37 } 38 39 supportsVideoCapture, supportsVideoStreaming, err := checkCapabilities(fd) 40 41 if err != nil { 42 return nil, err 43 } 44 45 if !supportsVideoCapture { 46 return nil, errors.New("Not a video capture device") 47 } 48 49 if !supportsVideoStreaming { 50 return nil, errors.New("Device does not support the streaming I/O method") 51 } 52 53 w := new(Webcam) 54 w.fd = uintptr(fd) 55 w.bufcount = 256 56 return w, nil 57 } 58 59 // Returns image formats supported by the device alongside with 60 // their text description 61 // Not that this function is somewhat experimental. Frames are not ordered in 62 // any meaning, also duplicates can occur so it's up to developer to clean it up. 63 // See http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-enum-framesizes.html 64 // for more information 65 func (w *Webcam) GetSupportedFormats() map[PixelFormat]string { 66 67 result := make(map[PixelFormat]string) 68 var err error 69 var code uint32 70 var desc string 71 var index uint32 72 73 for index = 0; err == nil; index++ { 74 code, desc, err = getPixelFormat(w.fd, index) 75 76 if err != nil { 77 break 78 } 79 80 result[PixelFormat(code)] = desc 81 } 82 83 return result 84 } 85 86 // Returns supported frame sizes for a given image format 87 func (w *Webcam) GetSupportedFrameSizes(f PixelFormat) []FrameSize { 88 result := make([]FrameSize, 0) 89 90 var index uint32 91 var err error 92 93 for index = 0; err == nil; index++ { 94 s, err := getFrameSize(w.fd, index, uint32(f)) 95 96 if err != nil { 97 break 98 } 99 100 result = append(result, s) 101 } 102 103 return result 104 } 105 106 // Sets desired image format and frame size 107 // Note, that device driver can change that values. 108 // Resulting values are returned by a function 109 // alongside with an error if any 110 func (w *Webcam) SetImageFormat(f PixelFormat, width, height uint32) (PixelFormat, uint32, uint32, uint32, uint32, error) { 111 112 code := uint32(f) 113 cw := width 114 ch := height 115 var stride uint32 116 var size uint32 117 118 err := setImageFormat(w.fd, &code, &width, &height, &stride, &size) 119 120 if err != nil { 121 return 0, 0, 0, 0, 0, err 122 } else { 123 return PixelFormat(code), cw, ch, stride, size, nil 124 } 125 } 126 127 // Set the number of frames to be buffered. 128 // Not allowed if streaming is already on. 129 func (w *Webcam) SetBufferCount(count uint32) error { 130 if w.streaming { 131 return errors.New("Cannot set buffer count when streaming") 132 } 133 w.bufcount = count 134 return nil 135 } 136 137 // Get a map of available controls. 138 func (w *Webcam) GetControls() map[ControlID]Control { 139 cmap := make(map[ControlID]Control) 140 for _, c := range queryControls(w.fd) { 141 cmap[ControlID(c.id)] = Control{c.name, c.min, c.max} 142 } 143 return cmap 144 } 145 146 // Get the value of a control. 147 func (w *Webcam) GetControl(id ControlID) (int32, error) { 148 return getControl(w.fd, uint32(id)) 149 } 150 151 // Set a control. 152 func (w *Webcam) SetControl(id ControlID, value int32) error { 153 return setControl(w.fd, uint32(id), value) 154 } 155 156 // Start streaming process 157 func (w *Webcam) StartStreaming() error { 158 if w.streaming { 159 return errors.New("Already streaming") 160 } 161 162 err := mmapRequestBuffers(w.fd, &w.bufcount) 163 164 if err != nil { 165 return errors.New("Failed to map request buffers: " + string(err.Error())) 166 } 167 168 w.buffers = make([][]byte, w.bufcount, w.bufcount) 169 for index, _ := range w.buffers { 170 var length uint32 171 172 buffer, err := mmapQueryBuffer(w.fd, uint32(index), &length) 173 174 if err != nil { 175 return errors.New("Failed to map memory: " + string(err.Error())) 176 } 177 178 w.buffers[index] = buffer 179 } 180 181 for index, _ := range w.buffers { 182 183 err := mmapEnqueueBuffer(w.fd, uint32(index)) 184 185 if err != nil { 186 return errors.New("Failed to enqueue buffer: " + string(err.Error())) 187 } 188 189 } 190 191 err = startStreaming(w.fd) 192 193 if err != nil { 194 return errors.New("Failed to start streaming: " + string(err.Error())) 195 } 196 w.streaming = true 197 198 return nil 199 } 200 201 // Read a single frame from the webcam 202 // If frame cannot be read at the moment 203 // function will return empty slice 204 func (w *Webcam) ReadFrame() ([]byte, error) { 205 result, index, err := w.GetFrame() 206 if err == nil { 207 w.ReleaseFrame(index) 208 } 209 return result, err 210 } 211 212 // Get a single frame from the webcam and return the frame and 213 // the buffer index. To return the buffer, ReleaseFrame must be called. 214 // If frame cannot be read at the moment 215 // function will return empty slice 216 func (w *Webcam) GetFrame() ([]byte, uint32, error) { 217 var index uint32 218 var length uint32 219 220 err := mmapDequeueBuffer(w.fd, &index, &length) 221 222 if err != nil { 223 return nil, 0, err 224 } 225 226 return w.buffers[int(index)][:length], index, nil 227 228 } 229 230 // Release the frame buffer that was obtained via GetFrame 231 func (w *Webcam) ReleaseFrame(index uint32) error { 232 return mmapEnqueueBuffer(w.fd, index) 233 } 234 235 // Wait until frame could be read 236 func (w *Webcam) WaitForFrame(timeout uint32) error { 237 238 count, err := waitForFrame(w.fd, timeout) 239 240 if count < 0 || err != nil { 241 return err 242 } else if count == 0 { 243 return new(Timeout) 244 } else { 245 return nil 246 } 247 } 248 249 func (w *Webcam) StopStreaming() error { 250 if !w.streaming { 251 return errors.New("Request to stop streaming when not streaming") 252 } 253 w.streaming = false 254 for _, buffer := range w.buffers { 255 err := mmapReleaseBuffer(buffer) 256 if err != nil { 257 return err 258 } 259 } 260 261 return stopStreaming(w.fd) 262 } 263 264 // Close the device 265 func (w *Webcam) Close() error { 266 if w.streaming { 267 w.StopStreaming() 268 } 269 270 err := unix.Close(int(w.fd)) 271 272 return err 273 } 274 275 // Sets automatic white balance correction 276 func (w *Webcam) SetAutoWhiteBalance(val bool) error { 277 v := int32(0) 278 if val { 279 v = 1 280 } 281 return setControl(w.fd, V4L2_CID_AUTO_WHITE_BALANCE, v) 282 }