github.com/blackjack/webcam@v0.0.0-20230509180125-87693b3f29dc/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 "reflect" 9 "unsafe" 10 11 "golang.org/x/sys/unix" 12 ) 13 14 // Webcam object 15 type Webcam struct { 16 fd uintptr 17 bufcount uint32 18 buffers [][]byte 19 streaming bool 20 } 21 22 type ControlID uint32 23 24 type Control struct { 25 Name string 26 Min int32 27 Max int32 28 Type int32 29 Step int32 30 } 31 32 // Open a webcam with a given path 33 // Checks if device is a v4l2 device and if it is 34 // capable to stream video 35 func Open(path string) (*Webcam, error) { 36 37 handle, err := unix.Open(path, unix.O_RDWR|unix.O_NONBLOCK, 0666) 38 fd := uintptr(handle) 39 40 if fd < 0 || err != nil { 41 return nil, err 42 } 43 44 supportsVideoCapture, supportsVideoStreaming, err := checkCapabilities(fd) 45 46 if err != nil { 47 return nil, err 48 } 49 50 if !supportsVideoCapture { 51 return nil, errors.New("Not a video capture device") 52 } 53 54 if !supportsVideoStreaming { 55 return nil, errors.New("Device does not support the streaming I/O method") 56 } 57 58 w := new(Webcam) 59 w.fd = fd 60 w.bufcount = 256 61 return w, nil 62 } 63 64 // Returns image formats supported by the device alongside with 65 // their text description 66 // Note that this function is somewhat experimental. Frames are not ordered in 67 // any meaning, also duplicates can occur so it's up to developer to clean it up. 68 // See http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-enum-framesizes.html 69 // for more information 70 func (w *Webcam) GetSupportedFormats() map[PixelFormat]string { 71 72 result := make(map[PixelFormat]string) 73 var err error 74 var code uint32 75 var desc string 76 var index uint32 77 78 for index = 0; err == nil; index++ { 79 code, desc, err = getPixelFormat(w.fd, index) 80 81 if err != nil { 82 break 83 } 84 85 result[PixelFormat(code)] = desc 86 } 87 88 return result 89 } 90 91 // GetName returns the human-readable name of the device 92 func (w *Webcam) GetName() (string, error) { 93 return getName(w.fd) 94 } 95 96 // GetBusInfo returns the location of the device in the system 97 func (w *Webcam) GetBusInfo() (string, error) { 98 return getBusInfo(w.fd) 99 } 100 101 // SelectInput selects the current video input. 102 func (w *Webcam) SelectInput(index uint32) error { 103 return selectInput(w.fd, index) 104 } 105 106 // GetInput queries the current video input. 107 func (w *Webcam) GetInput() (int32, error) { 108 return getInput(w.fd) 109 } 110 111 // Returns supported frame sizes for a given image format 112 func (w *Webcam) GetSupportedFrameSizes(f PixelFormat) []FrameSize { 113 result := make([]FrameSize, 0) 114 115 var index uint32 116 var err error 117 118 for index = 0; err == nil; index++ { 119 s, err := getFrameSize(w.fd, index, uint32(f)) 120 121 if err != nil { 122 break 123 } 124 125 result = append(result, s) 126 } 127 128 return result 129 } 130 131 // GetSupportedFramerates returns supported frame rates for a given image format and frame size. 132 func (w *Webcam) GetSupportedFramerates(fp PixelFormat, width uint32, height uint32) []FrameRate { 133 var result []FrameRate 134 var index uint32 135 var err error 136 137 // keep incrementing the index value until we get an EINVAL error 138 index = 0 139 for err == nil { 140 r, err := getFrameInterval(w.fd, index, uint32(fp), width, height) 141 if err != nil { 142 break 143 } 144 result = append(result, r) 145 index++ 146 } 147 148 return result 149 } 150 151 // Sets desired image format and frame size 152 // Note, that device driver can change that values. 153 // Resulting values are returned by a function 154 // alongside with an error if any 155 func (w *Webcam) SetImageFormat(f PixelFormat, width, height uint32) (PixelFormat, uint32, uint32, error) { 156 157 code := uint32(f) 158 cw := width 159 ch := height 160 161 err := setImageFormat(w.fd, &code, &width, &height) 162 163 if err != nil { 164 return 0, 0, 0, err 165 } else { 166 return PixelFormat(code), cw, ch, nil 167 } 168 } 169 170 // Set the number of frames to be buffered. 171 // Not allowed if streaming is already on. 172 func (w *Webcam) SetBufferCount(count uint32) error { 173 if w.streaming { 174 return errors.New("Cannot set buffer count when streaming") 175 } 176 w.bufcount = count 177 return nil 178 } 179 180 // Get a map of available controls. 181 func (w *Webcam) GetControls() map[ControlID]Control { 182 cmap := make(map[ControlID]Control) 183 for _, c := range queryControls(w.fd) { 184 cmap[ControlID(c.id)] = Control{ 185 Name: c.name, 186 Min: c.min, 187 Max: c.max, 188 Type: int32(c.c_type), 189 Step: c.step, 190 } 191 } 192 return cmap 193 } 194 195 // Get the value of a control. 196 func (w *Webcam) GetControl(id ControlID) (int32, error) { 197 return getControl(w.fd, uint32(id)) 198 } 199 200 // Set a control. 201 func (w *Webcam) SetControl(id ControlID, value int32) error { 202 return setControl(w.fd, uint32(id), value) 203 } 204 205 // Get the framerate. 206 func (w *Webcam) GetFramerate() (float32, error) { 207 return getFramerate(w.fd) 208 } 209 210 // Set FPS 211 func (w *Webcam) SetFramerate(fps float32) error { 212 return setFramerate(w.fd, 1000, uint32(1000*(fps))) 213 } 214 215 // Start streaming process 216 func (w *Webcam) StartStreaming() error { 217 if w.streaming { 218 return errors.New("Already streaming") 219 } 220 221 err := mmapRequestBuffers(w.fd, &w.bufcount) 222 223 if err != nil { 224 return errors.New("Failed to map request buffers: " + string(err.Error())) 225 } 226 227 w.buffers = make([][]byte, w.bufcount, w.bufcount) 228 for index, _ := range w.buffers { 229 var length uint32 230 231 buffer, err := mmapQueryBuffer(w.fd, uint32(index), &length) 232 233 if err != nil { 234 return errors.New("Failed to map memory: " + string(err.Error())) 235 } 236 237 w.buffers[index] = buffer 238 } 239 240 for index, _ := range w.buffers { 241 242 err := mmapEnqueueBuffer(w.fd, uint32(index)) 243 244 if err != nil { 245 return errors.New("Failed to enqueue buffer: " + string(err.Error())) 246 } 247 248 } 249 250 err = startStreaming(w.fd) 251 252 if err != nil { 253 return errors.New("Failed to start streaming: " + string(err.Error())) 254 } 255 w.streaming = true 256 257 return nil 258 } 259 260 // Read a single frame from the webcam 261 // If frame cannot be read at the moment 262 // function will return empty slice 263 func (w *Webcam) ReadFrame() ([]byte, error) { 264 result, index, err := w.GetFrame() 265 if err == nil { 266 w.ReleaseFrame(index) 267 } 268 return result, err 269 } 270 271 // Get a single frame from the webcam and return the frame and 272 // the buffer index. To return the buffer, ReleaseFrame must be called. 273 // If frame cannot be read at the moment 274 // function will return empty slice 275 func (w *Webcam) GetFrame() ([]byte, uint32, error) { 276 var index uint32 277 var length uint32 278 279 err := mmapDequeueBuffer(w.fd, &index, &length) 280 281 if err != nil { 282 return nil, 0, err 283 } 284 285 return w.buffers[int(index)][:length], index, nil 286 287 } 288 289 // Release the frame buffer that was obtained via GetFrame 290 func (w *Webcam) ReleaseFrame(index uint32) error { 291 return mmapEnqueueBuffer(w.fd, index) 292 } 293 294 // Wait until frame could be read 295 func (w *Webcam) WaitForFrame(timeout uint32) error { 296 297 count, err := waitForFrame(w.fd, timeout) 298 299 if count < 0 || err != nil { 300 return err 301 } else if count == 0 { 302 return new(Timeout) 303 } else { 304 return nil 305 } 306 } 307 308 func (w *Webcam) StopStreaming() error { 309 if !w.streaming { 310 return errors.New("Request to stop streaming when not streaming") 311 } 312 w.streaming = false 313 for _, buffer := range w.buffers { 314 err := mmapReleaseBuffer(buffer) 315 if err != nil { 316 return err 317 } 318 } 319 320 return stopStreaming(w.fd) 321 } 322 323 // Close the device 324 func (w *Webcam) Close() error { 325 if w.streaming { 326 w.StopStreaming() 327 } 328 329 err := unix.Close(int(w.fd)) 330 331 return err 332 } 333 334 // Sets automatic white balance correction 335 func (w *Webcam) SetAutoWhiteBalance(val bool) error { 336 v := int32(0) 337 if val { 338 v = 1 339 } 340 return setControl(w.fd, V4L2_CID_AUTO_WHITE_BALANCE, v) 341 } 342 343 func gobytes(p unsafe.Pointer, n int) []byte { 344 345 h := reflect.SliceHeader{uintptr(p), n, n} 346 s := *(*[]byte)(unsafe.Pointer(&h)) 347 348 return s 349 }