|
@@ -0,0 +1,524 @@
|
|
1
|
+/*
|
|
2
|
+ * Author: Jon Trulson <jtrulson@ics.com>
|
|
3
|
+ * Copyright (c) 2016 Intel Corporation.
|
|
4
|
+ *
|
|
5
|
+ * Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+ * a copy of this software and associated documentation files (the
|
|
7
|
+ * "Software"), to deal in the Software without restriction, including
|
|
8
|
+ * without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+ * distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+ * permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+ * the following conditions:
|
|
12
|
+ *
|
|
13
|
+ * The above copyright notice and this permission notice shall be
|
|
14
|
+ * included in all copies or substantial portions of the Software.
|
|
15
|
+ *
|
|
16
|
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+ */
|
|
24
|
+
|
|
25
|
+#include <iostream>
|
|
26
|
+#include <stdexcept>
|
|
27
|
+#include <unistd.h>
|
|
28
|
+#include <stdio.h>
|
|
29
|
+#include <math.h>
|
|
30
|
+#include <string.h>
|
|
31
|
+
|
|
32
|
+#include "vcap.hpp"
|
|
33
|
+
|
|
34
|
+using namespace upm;
|
|
35
|
+using namespace std;
|
|
36
|
+
|
|
37
|
+#define CLAMP(_val, _min, _max) \
|
|
38
|
+ (((_val) < (_min)) ? (_min) : (((_val) > (_max)) ? (_max) : (_val)))
|
|
39
|
+
|
|
40
|
+VCAP::VCAP(string videoDev) :
|
|
41
|
+ m_buffer(0), m_fd(-1)
|
|
42
|
+{
|
|
43
|
+ memset(&m_caps, 0, sizeof(struct v4l2_capability));
|
|
44
|
+ memset(&m_format, 0, sizeof(struct v4l2_format));
|
|
45
|
+
|
|
46
|
+ m_debugging = false;
|
|
47
|
+ m_bufferLen = 0;
|
|
48
|
+ m_videoDevice = videoDev;
|
|
49
|
+ setJPGQuality(VCAP_DEFAULT_JPEG_QUALITY);
|
|
50
|
+
|
|
51
|
+ // try to open the video device, and set a default format.
|
|
52
|
+ if (!initVideoDevice())
|
|
53
|
+ throw std::runtime_error(std::string(__FUNCTION__) +
|
|
54
|
+ ": initVideoDevice() failed");
|
|
55
|
+
|
|
56
|
+ m_height = 0;
|
|
57
|
+ m_width = 0;
|
|
58
|
+ m_imageCaptured = false;
|
|
59
|
+}
|
|
60
|
+
|
|
61
|
+VCAP::~VCAP()
|
|
62
|
+{
|
|
63
|
+ releaseBuffer();
|
|
64
|
+
|
|
65
|
+ if (m_fd >= 0)
|
|
66
|
+ close(m_fd);
|
|
67
|
+
|
|
68
|
+ m_fd = -1;
|
|
69
|
+}
|
|
70
|
+
|
|
71
|
+bool VCAP::initVideoDevice()
|
|
72
|
+{
|
|
73
|
+ if (m_videoDevice.empty())
|
|
74
|
+ return false;
|
|
75
|
+
|
|
76
|
+ if ((m_fd = open(m_videoDevice.c_str(), O_RDWR)) < 0)
|
|
77
|
+ {
|
|
78
|
+ cerr << __FUNCTION__ << ": open failed: " << strerror(errno) << endl;
|
|
79
|
+ return false;
|
|
80
|
+ }
|
|
81
|
+
|
|
82
|
+ if (!checkCapabilities())
|
|
83
|
+ {
|
|
84
|
+ close(m_fd);
|
|
85
|
+ m_fd = -1;
|
|
86
|
+ return false;
|
|
87
|
+ }
|
|
88
|
+
|
|
89
|
+ return true;
|
|
90
|
+}
|
|
91
|
+
|
|
92
|
+// This seems... odd, but appears to be necessary.
|
|
93
|
+// Ignore error and retry if the ioctl fails due to EINTR
|
|
94
|
+int VCAP::xioctl(int fd, int request, void* argp)
|
|
95
|
+{
|
|
96
|
+ int r;
|
|
97
|
+
|
|
98
|
+ do {
|
|
99
|
+ r = ioctl(fd, request, argp);
|
|
100
|
+ }
|
|
101
|
+ while (r == -1 && errno == EINTR);
|
|
102
|
+
|
|
103
|
+ return r;
|
|
104
|
+}
|
|
105
|
+
|
|
106
|
+bool VCAP::checkCapabilities()
|
|
107
|
+{
|
|
108
|
+ if (xioctl(m_fd, VIDIOC_QUERYCAP, &m_caps) < 0)
|
|
109
|
+ {
|
|
110
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_QUERYCAP) failed: "
|
|
111
|
+ << strerror(errno) << endl;
|
|
112
|
+ return false;
|
|
113
|
+ }
|
|
114
|
+
|
|
115
|
+ if (m_debugging)
|
|
116
|
+ {
|
|
117
|
+ cerr << "Driver: " << m_caps.driver << endl;
|
|
118
|
+ cerr << "Device: " << m_caps.card << endl;
|
|
119
|
+ cerr << "Caps : 0x" << std::hex << m_caps.capabilities << std::dec
|
|
120
|
+ << endl;
|
|
121
|
+ }
|
|
122
|
+
|
|
123
|
+ // see if capturing is supported
|
|
124
|
+ if (!(m_caps.capabilities & V4L2_CAP_VIDEO_CAPTURE))
|
|
125
|
+ {
|
|
126
|
+ cerr << __FUNCTION__ << ": Device does not support video capture"
|
|
127
|
+ << endl;
|
|
128
|
+ return false;
|
|
129
|
+ }
|
|
130
|
+
|
|
131
|
+ if (!(m_caps.capabilities & V4L2_CAP_STREAMING))
|
|
132
|
+ {
|
|
133
|
+ cerr << __FUNCTION__ << ": Device does not support streaming I/O"
|
|
134
|
+ << endl;
|
|
135
|
+ return false;
|
|
136
|
+ }
|
|
137
|
+
|
|
138
|
+ return true;
|
|
139
|
+}
|
|
140
|
+
|
|
141
|
+bool VCAP::setResolution(int width, int height)
|
|
142
|
+{
|
|
143
|
+ // in case we already created one
|
|
144
|
+ releaseBuffer();
|
|
145
|
+
|
|
146
|
+ m_width = width;
|
|
147
|
+ m_height = height;
|
|
148
|
+
|
|
149
|
+ m_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
150
|
+ // initialize with the current format
|
|
151
|
+ if (xioctl(m_fd, VIDIOC_G_FMT, &m_format) < 0)
|
|
152
|
+ {
|
|
153
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_G_FMT) failed: "
|
|
154
|
+ << strerror(errno) << endl;
|
|
155
|
+ return false;
|
|
156
|
+ }
|
|
157
|
+
|
|
158
|
+ // make our changes...
|
|
159
|
+ m_format.fmt.pix.width = m_width;
|
|
160
|
+ m_format.fmt.pix.height = m_height;
|
|
161
|
+ m_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
|
162
|
+ m_format.fmt.pix.field = V4L2_FIELD_ANY;
|
|
163
|
+
|
|
164
|
+ if (xioctl(m_fd, VIDIOC_S_FMT, &m_format) < 0)
|
|
165
|
+ {
|
|
166
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_S_FMT) failed: "
|
|
167
|
+ << strerror(errno) << endl;
|
|
168
|
+
|
|
169
|
+ // If it's just busy, then this still might work, so don't fail here
|
|
170
|
+ if (errno != EBUSY)
|
|
171
|
+ return false;
|
|
172
|
+ }
|
|
173
|
+
|
|
174
|
+ // Now retrieve the driver's selected format and check it -
|
|
175
|
+ // specifically, the width and height might change, causing
|
|
176
|
+ // coredumps if we don't adjust them accordingly.
|
|
177
|
+
|
|
178
|
+ if (xioctl(m_fd, VIDIOC_G_FMT, &m_format) < 0)
|
|
179
|
+ {
|
|
180
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_G_FMT) failed: "
|
|
181
|
+ << strerror(errno) << endl;
|
|
182
|
+ return false;
|
|
183
|
+ }
|
|
184
|
+
|
|
185
|
+ // G_FMT will have adjusted these if neccessary, so verify
|
|
186
|
+ if (m_format.fmt.pix.width != m_width)
|
|
187
|
+ {
|
|
188
|
+ if (m_debugging)
|
|
189
|
+ cerr << __FUNCTION__ << ": Warning: Selected width "
|
|
190
|
+ << std::to_string(m_width)
|
|
191
|
+ << " adjusted by driver to "
|
|
192
|
+ << std::to_string(m_format.fmt.pix.width)
|
|
193
|
+ << endl;
|
|
194
|
+
|
|
195
|
+ m_width = m_format.fmt.pix.width;
|
|
196
|
+ }
|
|
197
|
+
|
|
198
|
+ if (m_format.fmt.pix.height != m_height)
|
|
199
|
+ {
|
|
200
|
+ if (m_debugging)
|
|
201
|
+ cerr << __FUNCTION__ << ": Warning: Selected height "
|
|
202
|
+ << std::to_string(m_height)
|
|
203
|
+ << " adjusted by driver to "
|
|
204
|
+ << std::to_string(m_format.fmt.pix.height)
|
|
205
|
+ << endl;
|
|
206
|
+
|
|
207
|
+ m_height = m_format.fmt.pix.height;
|
|
208
|
+ }
|
|
209
|
+
|
|
210
|
+ // now alloc the buffers here
|
|
211
|
+ if (!allocBuffer())
|
|
212
|
+ return false;
|
|
213
|
+
|
|
214
|
+ return true;
|
|
215
|
+}
|
|
216
|
+
|
|
217
|
+bool VCAP::allocBuffer()
|
|
218
|
+{
|
|
219
|
+ struct v4l2_requestbuffers rb;
|
|
220
|
+ memset(&rb, 0, sizeof(rb));
|
|
221
|
+
|
|
222
|
+ // we just want one buffer, and we only support mmap().
|
|
223
|
+ rb.count = 1;
|
|
224
|
+ rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
225
|
+ rb.memory = V4L2_MEMORY_MMAP;
|
|
226
|
+
|
|
227
|
+ if (xioctl(m_fd, VIDIOC_REQBUFS, &rb) < 0)
|
|
228
|
+ {
|
|
229
|
+ if (errno == EINVAL)
|
|
230
|
+ {
|
|
231
|
+ cerr << __FUNCTION__ << ": Capture device does not support mmapped "
|
|
232
|
+ << "buffers"
|
|
233
|
+ << endl;
|
|
234
|
+ }
|
|
235
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_REQBUFS) failed: "
|
|
236
|
+ << strerror(errno) << endl;
|
|
237
|
+
|
|
238
|
+ return false;
|
|
239
|
+ }
|
|
240
|
+
|
|
241
|
+ // get the buffer and mmap it
|
|
242
|
+ struct v4l2_buffer mbuf;
|
|
243
|
+ memset(&mbuf, 0, sizeof(mbuf));
|
|
244
|
+
|
|
245
|
+ mbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
246
|
+ mbuf.memory = V4L2_MEMORY_MMAP;
|
|
247
|
+ mbuf.index = 0;
|
|
248
|
+
|
|
249
|
+ if (xioctl(m_fd, VIDIOC_QUERYBUF, &mbuf) < 0)
|
|
250
|
+ {
|
|
251
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_QUERYBUF) failed: "
|
|
252
|
+ << strerror(errno) << endl;
|
|
253
|
+ return false;
|
|
254
|
+ }
|
|
255
|
+
|
|
256
|
+ // map it
|
|
257
|
+ m_buffer = (unsigned char *)mmap(NULL, mbuf.length,
|
|
258
|
+ PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
259
|
+ m_fd, mbuf.m.offset);
|
|
260
|
+
|
|
261
|
+ if (m_buffer == MAP_FAILED)
|
|
262
|
+ {
|
|
263
|
+ cerr << __FUNCTION__ << ": mmap() failed: "
|
|
264
|
+ << strerror(errno) << endl;
|
|
265
|
+ return false;
|
|
266
|
+ }
|
|
267
|
+
|
|
268
|
+ // we'll need this when unmapping
|
|
269
|
+ m_bufferLen = mbuf.length;
|
|
270
|
+
|
|
271
|
+ return true;
|
|
272
|
+}
|
|
273
|
+
|
|
274
|
+void VCAP::releaseBuffer()
|
|
275
|
+{
|
|
276
|
+ // first unmap any buffers
|
|
277
|
+ if (m_buffer)
|
|
278
|
+ munmap(m_buffer, m_bufferLen);
|
|
279
|
+
|
|
280
|
+ m_buffer = 0;
|
|
281
|
+ m_bufferLen = 0;
|
|
282
|
+
|
|
283
|
+ // then, tell the kernel driver to free any allocated buffer(s)...
|
|
284
|
+ struct v4l2_requestbuffers rb;
|
|
285
|
+ memset(&rb, 0, sizeof(rb));
|
|
286
|
+
|
|
287
|
+ rb.count = 0;
|
|
288
|
+ rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
289
|
+ rb.memory = V4L2_MEMORY_MMAP;
|
|
290
|
+
|
|
291
|
+ if (xioctl(m_fd, VIDIOC_REQBUFS, &rb) < 0)
|
|
292
|
+ {
|
|
293
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_REQBUFS) failed while freeing: "
|
|
294
|
+ << strerror(errno) << endl;
|
|
295
|
+ }
|
|
296
|
+
|
|
297
|
+ // reset captured flag
|
|
298
|
+ m_imageCaptured = false;
|
|
299
|
+}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+bool VCAP::YUYV2JPEG(FILE *file)
|
|
303
|
+{
|
|
304
|
+ struct jpeg_compress_struct jpgInfo;
|
|
305
|
+ struct jpeg_error_mgr jerr;
|
|
306
|
+ JSAMPROW row_pointer[1];
|
|
307
|
+ unsigned char *row_buffer = NULL;
|
|
308
|
+ unsigned char *yuyv = NULL;
|
|
309
|
+ int z;
|
|
310
|
+
|
|
311
|
+ row_buffer = (unsigned char *)calloc(m_width * 3, 1);
|
|
312
|
+ if (!row_buffer)
|
|
313
|
+ {
|
|
314
|
+ cerr << __FUNCTION__ << ": allocation of line buffer failed."
|
|
315
|
+ << endl;
|
|
316
|
+ return false;
|
|
317
|
+ }
|
|
318
|
+
|
|
319
|
+ yuyv = m_buffer;
|
|
320
|
+
|
|
321
|
+ jpgInfo.err = jpeg_std_error(&jerr);
|
|
322
|
+ jpeg_create_compress(&jpgInfo);
|
|
323
|
+ jpeg_stdio_dest(&jpgInfo, file);
|
|
324
|
+
|
|
325
|
+ jpgInfo.image_width = m_width;
|
|
326
|
+ jpgInfo.image_height = m_height;
|
|
327
|
+
|
|
328
|
+ // components R, G, B
|
|
329
|
+ jpgInfo.input_components = 3;
|
|
330
|
+ jpgInfo.in_color_space = JCS_RGB;
|
|
331
|
+
|
|
332
|
+ jpeg_set_defaults(&jpgInfo);
|
|
333
|
+ jpeg_set_quality(&jpgInfo, m_jpgQuality, TRUE);
|
|
334
|
+
|
|
335
|
+ jpeg_start_compress(&jpgInfo, TRUE);
|
|
336
|
+
|
|
337
|
+ z = 0;
|
|
338
|
+
|
|
339
|
+ while (jpgInfo.next_scanline < jpgInfo.image_height)
|
|
340
|
+ {
|
|
341
|
+ int x;
|
|
342
|
+ unsigned char *ptr = row_buffer;
|
|
343
|
+
|
|
344
|
+ for (x = 0; x < m_width; x++)
|
|
345
|
+ {
|
|
346
|
+ int r, g, b;
|
|
347
|
+ int y, u, v;
|
|
348
|
+
|
|
349
|
+ if (!z)
|
|
350
|
+ y = yuyv[0] << 8;
|
|
351
|
+ else
|
|
352
|
+ y = yuyv[2] << 8;
|
|
353
|
+ u = yuyv[1] - 128;
|
|
354
|
+ v = yuyv[3] - 128;
|
|
355
|
+
|
|
356
|
+ r = (y + (359 * v)) >> 8;
|
|
357
|
+ g = (y - (88 * u) - (183 * v)) >> 8;
|
|
358
|
+ b = (y + (454 * u)) >> 8;
|
|
359
|
+
|
|
360
|
+ *(ptr++) = CLAMP(r, 0, 255);
|
|
361
|
+ *(ptr++) = CLAMP(g, 0, 255);
|
|
362
|
+ *(ptr++) = CLAMP(b, 0, 255);
|
|
363
|
+
|
|
364
|
+ if (z++)
|
|
365
|
+ {
|
|
366
|
+ z = 0;
|
|
367
|
+ yuyv += 4;
|
|
368
|
+ }
|
|
369
|
+ }
|
|
370
|
+
|
|
371
|
+ row_pointer[0] = row_buffer;
|
|
372
|
+ jpeg_write_scanlines(&jpgInfo, row_pointer, 1);
|
|
373
|
+ }
|
|
374
|
+
|
|
375
|
+ jpeg_finish_compress(&jpgInfo);
|
|
376
|
+ jpeg_destroy_compress(&jpgInfo);
|
|
377
|
+
|
|
378
|
+ free(row_buffer);
|
|
379
|
+
|
|
380
|
+ return true;
|
|
381
|
+}
|
|
382
|
+
|
|
383
|
+bool VCAP::saveImage(string filename)
|
|
384
|
+{
|
|
385
|
+ // check m_buffer to make sure we have an actual buffer... If not,
|
|
386
|
+ // we throw here.
|
|
387
|
+ if (!m_buffer)
|
|
388
|
+ {
|
|
389
|
+ throw std::runtime_error(std::string(__FUNCTION__) +
|
|
390
|
+ ": no buffer. Call setResolution() first");
|
|
391
|
+ }
|
|
392
|
+
|
|
393
|
+ // if we haven't done at least one capture yet...
|
|
394
|
+ if (!m_imageCaptured)
|
|
395
|
+ {
|
|
396
|
+ throw std::runtime_error(std::string(__FUNCTION__) +
|
|
397
|
+ ": No data, call captureImage() first");
|
|
398
|
+ }
|
|
399
|
+
|
|
400
|
+ FILE *file;
|
|
401
|
+ if ((file = fopen(filename.c_str(), "wb")) == NULL)
|
|
402
|
+ {
|
|
403
|
+ cerr << __FUNCTION__ << ": fopen() failed: "
|
|
404
|
+ << strerror(errno) << endl;
|
|
405
|
+ return false;
|
|
406
|
+ }
|
|
407
|
+
|
|
408
|
+ YUYV2JPEG(file);
|
|
409
|
+ fclose(file);
|
|
410
|
+
|
|
411
|
+ if (m_debugging)
|
|
412
|
+ cerr << __FUNCTION__ << ": Saved image to " << filename << endl;
|
|
413
|
+
|
|
414
|
+ return true;
|
|
415
|
+}
|
|
416
|
+
|
|
417
|
+bool VCAP::captureImage()
|
|
418
|
+{
|
|
419
|
+ // first, make sure a resolution was specified. If not, set the
|
|
420
|
+ // default
|
|
421
|
+ if (m_width == 0 || m_height == 0)
|
|
422
|
+ {
|
|
423
|
+ if (!setResolution(VCAP_DEFAULT_WIDTH, VCAP_DEFAULT_HEIGHT))
|
|
424
|
+ throw std::runtime_error(std::string(__FUNCTION__) +
|
|
425
|
+ ": setResolution() failed");
|
|
426
|
+ }
|
|
427
|
+
|
|
428
|
+ // we basically just call doCaptureImage() twice - once to grab and
|
|
429
|
+ // discard the first frame (which is usually a remnent of a previous
|
|
430
|
+ // capture), and another to grab the real frame we are interesed in.
|
|
431
|
+
|
|
432
|
+ if (!doCaptureImage())
|
|
433
|
+ {
|
|
434
|
+ cerr << __FUNCTION__ << ": capture of first frame failed"
|
|
435
|
+ << endl;
|
|
436
|
+ }
|
|
437
|
+
|
|
438
|
+ return doCaptureImage();
|
|
439
|
+}
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+// the real workhorse
|
|
443
|
+bool VCAP::doCaptureImage()
|
|
444
|
+{
|
|
445
|
+ struct v4l2_buffer buf = {0};
|
|
446
|
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
447
|
+ buf.memory = V4L2_MEMORY_MMAP;
|
|
448
|
+ buf.index = 0;
|
|
449
|
+
|
|
450
|
+ // queue our buffer
|
|
451
|
+ if (xioctl(m_fd, VIDIOC_QBUF, &buf) < 0)
|
|
452
|
+ {
|
|
453
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_QBUF) failed: "
|
|
454
|
+ << strerror(errno) << endl;
|
|
455
|
+
|
|
456
|
+ return false;
|
|
457
|
+ }
|
|
458
|
+
|
|
459
|
+ // enable streaming
|
|
460
|
+ if (xioctl(m_fd, VIDIOC_STREAMON, &buf.type) < 0)
|
|
461
|
+ {
|
|
462
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_STREAMON) failed: "
|
|
463
|
+ << strerror(errno) << endl;
|
|
464
|
+
|
|
465
|
+ return false;
|
|
466
|
+ }
|
|
467
|
+
|
|
468
|
+ // use select to wait for a complete frame.
|
|
469
|
+ fd_set fds;
|
|
470
|
+
|
|
471
|
+ FD_ZERO(&fds);
|
|
472
|
+ FD_SET(m_fd, &fds);
|
|
473
|
+
|
|
474
|
+ struct timeval tv;
|
|
475
|
+ memset(&tv, 0, sizeof(tv));
|
|
476
|
+
|
|
477
|
+ // 5 seconds should be more than enough
|
|
478
|
+ tv.tv_sec = 5;
|
|
479
|
+
|
|
480
|
+ int rv;
|
|
481
|
+ if ((rv = select(m_fd + 1, &fds, NULL, NULL, &tv)) < 0)
|
|
482
|
+ {
|
|
483
|
+ cerr << __FUNCTION__ << ": select() failed: "
|
|
484
|
+ << strerror(errno) << endl;
|
|
485
|
+ return false;
|
|
486
|
+ }
|
|
487
|
+
|
|
488
|
+ if (!rv)
|
|
489
|
+ {
|
|
490
|
+ // timed out
|
|
491
|
+ cerr << __FUNCTION__ << ": select() timed out waiting for frame"
|
|
492
|
+ << endl;
|
|
493
|
+
|
|
494
|
+ return false;
|
|
495
|
+ }
|
|
496
|
+
|
|
497
|
+ // de-queue the buffer, we're now free to access it via the mmapped
|
|
498
|
+ // ptr (m_buffer)
|
|
499
|
+ if (xioctl(m_fd, VIDIOC_DQBUF, &buf) < 0)
|
|
500
|
+ {
|
|
501
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_DQBUF) failed: "
|
|
502
|
+ << strerror(errno) << endl;
|
|
503
|
+
|
|
504
|
+ return false;
|
|
505
|
+ }
|
|
506
|
+
|
|
507
|
+ // turn off streaming
|
|
508
|
+ if (xioctl(m_fd, VIDIOC_STREAMOFF, &buf.type) < 0)
|
|
509
|
+ {
|
|
510
|
+ cerr << __FUNCTION__ << ": ioctl(VIDIOC_STREAMOFF) failed: "
|
|
511
|
+ << strerror(errno) << endl;
|
|
512
|
+
|
|
513
|
+ return false;
|
|
514
|
+ }
|
|
515
|
+
|
|
516
|
+ m_imageCaptured = true;
|
|
517
|
+
|
|
518
|
+ return true;
|
|
519
|
+}
|
|
520
|
+
|
|
521
|
+ void VCAP::setJPGQuality(unsigned int qual)
|
|
522
|
+ {
|
|
523
|
+ m_jpgQuality = CLAMP(qual, 0, 100);
|
|
524
|
+ }
|