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