qcap: Use BasePinImpl_QueryInterface().
[wine.git] / dlls / qcap / v4l.c
blobc9102accb8004368e58577c75804c55eb6e862b0
1 /*
2 * v4l2 backend to the VFW Capture filter
4 * Copyright 2005 Maarten Lankhorst
5 * Copyright 2019 Zebediah Figura
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define BIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD /* work around ioctl breakage on Android */
24 #include "config.h"
25 #include "wine/port.h"
27 #define COBJMACROS
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #ifdef HAVE_SYS_IOCTL_H
33 #include <sys/ioctl.h>
34 #endif
35 #ifdef HAVE_SYS_MMAN_H
36 #include <sys/mman.h>
37 #endif
38 #include <errno.h>
39 #ifdef HAVE_SYS_TIME_H
40 #include <sys/time.h>
41 #endif
42 #ifdef HAVE_ASM_TYPES_H
43 #include <asm/types.h>
44 #endif
45 #ifdef HAVE_LINUX_VIDEODEV2_H
46 #include <linux/videodev2.h>
47 #endif
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif
52 #include "windef.h"
53 #include "winbase.h"
54 #include "wtypes.h"
55 #include "wingdi.h"
56 #include "winuser.h"
57 #include "dshow.h"
58 #include "vfwmsgs.h"
59 #include "amvideo.h"
60 #include "wine/debug.h"
61 #include "wine/library.h"
63 #include "qcap_main.h"
64 #include "capture.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
68 #ifdef HAVE_LINUX_VIDEODEV2_H
70 WINE_DECLARE_DEBUG_CHANNEL(winediag);
72 static typeof(open) *video_open = open;
73 static typeof(close) *video_close = close;
74 static typeof(ioctl) *video_ioctl = ioctl;
75 static typeof(read) *video_read = read;
77 static BOOL video_init(void)
79 #ifdef SONAME_LIBV4L2
80 static void *video_lib;
82 if (video_lib)
83 return TRUE;
84 if (!(video_lib = wine_dlopen(SONAME_LIBV4L2, RTLD_NOW, NULL, 0)))
85 return FALSE;
86 video_open = wine_dlsym(video_lib, "v4l2_open", NULL, 0);
87 video_close = wine_dlsym(video_lib, "v4l2_close", NULL, 0);
88 video_ioctl = wine_dlsym(video_lib, "v4l2_ioctl", NULL, 0);
89 video_read = wine_dlsym(video_lib, "v4l2_read", NULL, 0);
91 return TRUE;
92 #else
93 return FALSE;
94 #endif
97 struct _Capture
99 UINT width, height, bitDepth, fps, outputwidth, outputheight;
100 BOOL swresize;
102 CRITICAL_SECTION CritSect;
104 struct strmbase_source *pin;
105 int fd, mmap;
106 BOOL iscommitted, stopped;
108 HANDLE thread;
111 static int xioctl(int fd, int request, void * arg)
113 int r;
115 do {
116 r = video_ioctl (fd, request, arg);
117 } while (-1 == r && EINTR == errno);
119 return r;
122 HRESULT qcap_driver_destroy(Capture *capBox)
124 TRACE("%p\n", capBox);
126 if( capBox->fd != -1 )
127 video_close(capBox->fd);
128 capBox->CritSect.DebugInfo->Spare[0] = 0;
129 DeleteCriticalSection(&capBox->CritSect);
130 CoTaskMemFree(capBox);
131 return S_OK;
134 HRESULT qcap_driver_check_format(Capture *device, const AM_MEDIA_TYPE *mt)
136 HRESULT hr;
137 TRACE("device %p, mt %p.\n", device, mt);
138 dump_AM_MEDIA_TYPE(mt);
140 if (!mt)
141 return E_POINTER;
143 if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video))
144 return S_FALSE;
146 if (IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo) && mt->pbFormat
147 && mt->cbFormat >= sizeof(VIDEOINFOHEADER))
149 VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)mt->pbFormat;
150 if (vih->bmiHeader.biBitCount == 24 && vih->bmiHeader.biCompression == BI_RGB)
151 hr = S_OK;
152 else
154 FIXME("Unsupported compression %#x, bpp %u.\n", vih->bmiHeader.biCompression,
155 vih->bmiHeader.biBitCount);
156 hr = S_FALSE;
159 else
160 hr = VFW_E_INVALIDMEDIATYPE;
162 return hr;
165 HRESULT qcap_driver_set_format(Capture *device, AM_MEDIA_TYPE *mt)
167 struct v4l2_format format = {0};
168 int newheight, newwidth;
169 VIDEOINFOHEADER *vih;
170 int fd = device->fd;
171 HRESULT hr;
173 if (FAILED(hr = qcap_driver_check_format(device, mt)))
174 return hr;
175 vih = (VIDEOINFOHEADER *)mt->pbFormat;
177 newwidth = vih->bmiHeader.biWidth;
178 newheight = vih->bmiHeader.biHeight;
180 if (device->height == newheight && device->width == newwidth)
181 return S_OK;
183 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
184 if (xioctl(fd, VIDIOC_G_FMT, &format) == -1)
186 ERR("Failed to get current format: %s\n", strerror(errno));
187 return VFW_E_TYPE_NOT_ACCEPTED;
190 format.fmt.pix.width = newwidth;
191 format.fmt.pix.height = newheight;
193 if (!xioctl(fd, VIDIOC_S_FMT, &format)
194 && format.fmt.pix.width == newwidth
195 && format.fmt.pix.height == newheight)
197 device->width = newwidth;
198 device->height = newheight;
199 device->swresize = FALSE;
201 else
203 TRACE("Using software resize: %dx%d -> %dx%d.\n",
204 format.fmt.pix.width, format.fmt.pix.height, device->width, device->height);
205 device->swresize = TRUE;
207 device->outputwidth = format.fmt.pix.width;
208 device->outputheight = format.fmt.pix.height;
209 return S_OK;
212 HRESULT qcap_driver_get_format(const Capture *capBox, AM_MEDIA_TYPE ** mT)
214 VIDEOINFOHEADER *vi;
216 mT[0] = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
217 if (!mT[0])
218 return E_OUTOFMEMORY;
219 vi = CoTaskMemAlloc(sizeof(VIDEOINFOHEADER));
220 mT[0]->cbFormat = sizeof(VIDEOINFOHEADER);
221 if (!vi)
223 CoTaskMemFree(mT[0]);
224 mT[0] = NULL;
225 return E_OUTOFMEMORY;
227 mT[0]->majortype = MEDIATYPE_Video;
228 mT[0]->subtype = MEDIASUBTYPE_RGB24;
229 mT[0]->formattype = FORMAT_VideoInfo;
230 mT[0]->bFixedSizeSamples = TRUE;
231 mT[0]->bTemporalCompression = FALSE;
232 mT[0]->pUnk = NULL;
233 mT[0]->lSampleSize = capBox->outputwidth * capBox->outputheight * capBox->bitDepth / 8;
234 TRACE("Output format: %dx%d - %d bits = %u KB\n", capBox->outputwidth,
235 capBox->outputheight, capBox->bitDepth, mT[0]->lSampleSize/1024);
236 vi->rcSource.left = 0; vi->rcSource.top = 0;
237 vi->rcTarget.left = 0; vi->rcTarget.top = 0;
238 vi->rcSource.right = capBox->width; vi->rcSource.bottom = capBox->height;
239 vi->rcTarget.right = capBox->outputwidth; vi->rcTarget.bottom = capBox->outputheight;
240 vi->dwBitRate = capBox->fps * mT[0]->lSampleSize;
241 vi->dwBitErrorRate = 0;
242 vi->AvgTimePerFrame = (LONGLONG)10000000.0 / (LONGLONG)capBox->fps;
243 vi->bmiHeader.biSize = 40;
244 vi->bmiHeader.biWidth = capBox->outputwidth;
245 vi->bmiHeader.biHeight = capBox->outputheight;
246 vi->bmiHeader.biPlanes = 1;
247 vi->bmiHeader.biBitCount = 24;
248 vi->bmiHeader.biCompression = BI_RGB;
249 vi->bmiHeader.biSizeImage = mT[0]->lSampleSize;
250 vi->bmiHeader.biClrUsed = vi->bmiHeader.biClrImportant = 0;
251 vi->bmiHeader.biXPelsPerMeter = 100;
252 vi->bmiHeader.biYPelsPerMeter = 100;
253 mT[0]->pbFormat = (void *)vi;
254 dump_AM_MEDIA_TYPE(mT[0]);
255 return S_OK;
258 static __u32 v4l2_cid_from_qcap_property(VideoProcAmpProperty property)
260 switch (property)
262 case VideoProcAmp_Brightness:
263 return V4L2_CID_BRIGHTNESS;
264 case VideoProcAmp_Contrast:
265 return V4L2_CID_CONTRAST;
266 case VideoProcAmp_Hue:
267 return V4L2_CID_HUE;
268 case VideoProcAmp_Saturation:
269 return V4L2_CID_SATURATION;
270 default:
271 FIXME("Unhandled property %d.\n", property);
272 return 0;
276 HRESULT qcap_driver_get_prop_range(Capture *device, VideoProcAmpProperty property,
277 LONG *min, LONG *max, LONG *step, LONG *default_value, LONG *flags)
279 struct v4l2_queryctrl ctrl;
281 ctrl.id = v4l2_cid_from_qcap_property(property);
283 if (xioctl(device->fd, VIDIOC_QUERYCTRL, &ctrl) == -1)
285 WARN("Failed to query control: %s\n", strerror(errno));
286 return E_PROP_ID_UNSUPPORTED;
289 *min = ctrl.minimum;
290 *max = ctrl.maximum;
291 *step = ctrl.step;
292 *default_value = ctrl.default_value;
293 *flags = VideoProcAmp_Flags_Manual;
294 return S_OK;
297 HRESULT qcap_driver_get_prop(Capture *device, VideoProcAmpProperty property,
298 LONG *value, LONG *flags)
300 struct v4l2_control ctrl;
302 ctrl.id = v4l2_cid_from_qcap_property(property);
304 if (xioctl(device->fd, VIDIOC_G_CTRL, &ctrl) == -1)
306 WARN("Failed to get property: %s\n", strerror(errno));
307 return E_FAIL;
310 *value = ctrl.value;
311 *flags = VideoProcAmp_Flags_Manual;
313 return S_OK;
316 HRESULT qcap_driver_set_prop(Capture *device, VideoProcAmpProperty property,
317 LONG value, LONG flags)
319 struct v4l2_control ctrl;
321 ctrl.id = v4l2_cid_from_qcap_property(property);
322 ctrl.value = value;
324 if (xioctl(device->fd, VIDIOC_S_CTRL, &ctrl) == -1)
326 WARN("Failed to set property: %s\n", strerror(errno));
327 return E_FAIL;
330 return S_OK;
333 static void Resize(const Capture * capBox, LPBYTE output, const BYTE *input)
335 /* the whole image needs to be reversed,
336 because the dibs are messed up in windows */
337 if (!capBox->swresize)
339 int depth = capBox->bitDepth / 8;
340 int inoffset = 0, outoffset = capBox->height * capBox->width * depth;
341 int ow = capBox->width * depth;
342 while (outoffset > 0)
344 int x;
345 outoffset -= ow;
346 for (x = 0; x < ow; x++)
347 output[outoffset + x] = input[inoffset + x];
348 inoffset += ow;
351 else
353 HDC dc_s, dc_d;
354 HBITMAP bmp_s, bmp_d;
355 int depth = capBox->bitDepth / 8;
356 int inoffset = 0, outoffset = (capBox->outputheight) * capBox->outputwidth * depth;
357 int ow = capBox->outputwidth * depth;
358 LPBYTE myarray;
360 /* FIXME: Improve software resizing: add error checks and optimize */
362 myarray = CoTaskMemAlloc(capBox->outputwidth * capBox->outputheight * depth);
363 dc_s = CreateCompatibleDC(NULL);
364 dc_d = CreateCompatibleDC(NULL);
365 bmp_s = CreateBitmap(capBox->width, capBox->height, 1, capBox->bitDepth, input);
366 bmp_d = CreateBitmap(capBox->outputwidth, capBox->outputheight, 1, capBox->bitDepth, NULL);
367 SelectObject(dc_s, bmp_s);
368 SelectObject(dc_d, bmp_d);
369 StretchBlt(dc_d, 0, 0, capBox->outputwidth, capBox->outputheight,
370 dc_s, 0, 0, capBox->width, capBox->height, SRCCOPY);
371 GetBitmapBits(bmp_d, capBox->outputwidth * capBox->outputheight * depth, myarray);
372 while (outoffset > 0)
374 int i;
376 outoffset -= ow;
377 for (i = 0; i < ow; i++)
378 output[outoffset + i] = myarray[inoffset + i];
379 inoffset += ow;
381 CoTaskMemFree(myarray);
382 DeleteObject(dc_s);
383 DeleteObject(dc_d);
384 DeleteObject(bmp_s);
385 DeleteObject(bmp_d);
389 static DWORD WINAPI ReadThread(LPVOID lParam)
391 Capture * capBox = lParam;
392 HRESULT hr;
393 IMediaSample *pSample = NULL;
394 ULONG framecount = 0;
395 unsigned char *pTarget, *image_data;
396 unsigned int image_size;
398 image_size = capBox->height * capBox->width * 3;
399 if (!(image_data = heap_alloc(image_size)))
401 ERR("Failed to allocate memory.\n");
402 capBox->thread = 0;
403 capBox->stopped = TRUE;
404 return 0;
407 while (1)
409 EnterCriticalSection(&capBox->CritSect);
410 if (capBox->stopped)
411 break;
412 hr = BaseOutputPinImpl_GetDeliveryBuffer(capBox->pin, &pSample, NULL, NULL, 0);
413 if (SUCCEEDED(hr))
415 int len;
417 if (!capBox->swresize)
418 len = capBox->height * capBox->width * capBox->bitDepth / 8;
419 else
420 len = capBox->outputheight * capBox->outputwidth * capBox->bitDepth / 8;
421 IMediaSample_SetActualDataLength(pSample, len);
423 len = IMediaSample_GetActualDataLength(pSample);
424 TRACE("Data length: %d KB\n", len / 1024);
426 IMediaSample_GetPointer(pSample, &pTarget);
428 while (video_read(capBox->fd, image_data, image_size) == -1)
430 if (errno != EAGAIN)
432 ERR("Failed to read frame: %s\n", strerror(errno));
433 break;
437 Resize(capBox, pTarget, image_data);
438 hr = BaseOutputPinImpl_Deliver(capBox->pin, pSample);
439 TRACE("%p -> Frame %u: %x\n", capBox, ++framecount, hr);
440 IMediaSample_Release(pSample);
442 if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
444 TRACE("Return %x, stop IFilterGraph\n", hr);
445 capBox->thread = 0;
446 capBox->stopped = TRUE;
447 break;
449 LeaveCriticalSection(&capBox->CritSect);
452 LeaveCriticalSection(&capBox->CritSect);
453 heap_free(image_data);
454 return 0;
457 HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state)
459 HANDLE thread;
460 HRESULT hr;
462 TRACE("%p -> (%p)\n", capBox, state);
464 if (*state == State_Running) return S_OK;
466 EnterCriticalSection(&capBox->CritSect);
468 capBox->stopped = FALSE;
470 if (*state == State_Stopped)
472 *state = State_Running;
473 if (!capBox->iscommitted)
475 ALLOCATOR_PROPERTIES ap, actual;
477 capBox->iscommitted = TRUE;
479 ap.cBuffers = 3;
480 if (!capBox->swresize)
481 ap.cbBuffer = capBox->width * capBox->height;
482 else
483 ap.cbBuffer = capBox->outputwidth * capBox->outputheight;
484 ap.cbBuffer = (ap.cbBuffer * capBox->bitDepth) / 8;
485 ap.cbAlign = 1;
486 ap.cbPrefix = 0;
488 hr = IMemAllocator_SetProperties(capBox->pin->pAllocator, &ap, &actual);
490 if (SUCCEEDED(hr))
491 hr = IMemAllocator_Commit(capBox->pin->pAllocator);
493 TRACE("Committing allocator: %x\n", hr);
496 thread = CreateThread(NULL, 0, ReadThread, capBox, 0, NULL);
497 if (thread)
499 capBox->thread = thread;
500 SetThreadPriority(thread, THREAD_PRIORITY_LOWEST);
501 LeaveCriticalSection(&capBox->CritSect);
502 return S_OK;
504 ERR("Creating thread failed.. %u\n", GetLastError());
505 LeaveCriticalSection(&capBox->CritSect);
506 return E_FAIL;
509 ResumeThread(capBox->thread);
510 *state = State_Running;
511 LeaveCriticalSection(&capBox->CritSect);
512 return S_OK;
515 HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state)
517 TRACE("%p -> (%p)\n", capBox, state);
519 if (*state == State_Paused)
520 return S_OK;
521 if (*state == State_Stopped)
522 qcap_driver_run(capBox, state);
524 EnterCriticalSection(&capBox->CritSect);
525 *state = State_Paused;
526 SuspendThread(capBox->thread);
527 LeaveCriticalSection(&capBox->CritSect);
529 return S_OK;
532 HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state)
534 TRACE("%p -> (%p)\n", capBox, state);
536 if (*state == State_Stopped)
537 return S_OK;
539 EnterCriticalSection(&capBox->CritSect);
541 if (capBox->thread)
543 if (*state == State_Paused)
544 ResumeThread(capBox->thread);
545 capBox->stopped = TRUE;
546 capBox->thread = 0;
547 if (capBox->iscommitted)
549 HRESULT hr;
551 capBox->iscommitted = FALSE;
553 hr = IMemAllocator_Decommit(capBox->pin->pAllocator);
555 if (hr != S_OK && hr != VFW_E_NOT_COMMITTED)
556 WARN("Decommitting allocator: %x\n", hr);
560 *state = State_Stopped;
561 LeaveCriticalSection(&capBox->CritSect);
562 return S_OK;
565 Capture *qcap_driver_init(struct strmbase_source *pin, USHORT card)
567 struct v4l2_capability caps = {{0}};
568 struct v4l2_format format = {0};
569 Capture *device = NULL;
570 BOOL have_libv4l2;
571 char path[20];
572 int fd;
574 have_libv4l2 = video_init();
576 if (!(device = CoTaskMemAlloc(sizeof(*device))))
577 return NULL;
579 InitializeCriticalSection(&device->CritSect);
580 device->CritSect.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": Capture.CritSect");
582 sprintf(path, "/dev/video%i", card);
583 TRACE("Opening device %s.\n", path);
584 #ifdef O_CLOEXEC
585 if ((fd = video_open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC)) == -1 && errno == EINVAL)
586 #endif
587 fd = video_open(path, O_RDWR | O_NONBLOCK);
588 if (fd == -1)
590 WARN("Failed to open video device: %s\n", strerror(errno));
591 goto error;
593 fcntl(fd, F_SETFD, FD_CLOEXEC); /* in case O_CLOEXEC isn't supported */
594 device->fd = fd;
596 if (xioctl(fd, VIDIOC_QUERYCAP, &caps) == -1)
598 WARN("Failed to query device capabilities: %s\n", strerror(errno));
599 goto error;
602 #ifdef V4L2_CAP_DEVICE_CAPS
603 if (caps.capabilities & V4L2_CAP_DEVICE_CAPS)
604 caps.capabilities = caps.device_caps;
605 #endif
607 if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE))
609 WARN("Device does not support single-planar video capture.\n");
610 goto error;
613 if (!(caps.capabilities & V4L2_CAP_READWRITE))
615 WARN("Device does not support read().\n");
616 if (!have_libv4l2)
617 #ifdef SONAME_LIBV4L2
618 ERR_(winediag)("Reading from %s requires libv4l2, but it could not be loaded.\n", path);
619 #else
620 ERR_(winediag)("Reading from %s requires libv4l2, but Wine was compiled without libv4l2 support.\n", path);
621 #endif
622 goto error;
625 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
626 if (xioctl(fd, VIDIOC_G_FMT, &format) == -1)
628 ERR("Failed to get device format: %s\n", strerror(errno));
629 goto error;
632 format.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
633 if (xioctl(fd, VIDIOC_S_FMT, &format) == -1
634 || format.fmt.pix.pixelformat != V4L2_PIX_FMT_BGR24)
636 ERR("Failed to set pixel format: %s\n", strerror(errno));
637 if (!have_libv4l2)
638 ERR_(winediag)("You may need libv4l2 to use this device.\n");
639 goto error;
642 device->outputwidth = device->width = format.fmt.pix.width;
643 device->outputheight = device->height = format.fmt.pix.height;
644 device->swresize = FALSE;
645 device->bitDepth = 24;
646 device->pin = pin;
647 device->fps = 3;
648 device->stopped = FALSE;
649 device->iscommitted = FALSE;
651 TRACE("Format: %d bpp - %dx%d.\n", device->bitDepth, device->width, device->height);
653 return device;
655 error:
656 qcap_driver_destroy(device);
657 return NULL;
660 #else
662 Capture *qcap_driver_init(struct strmbase_source *pin, USHORT card)
664 static const char msg[] =
665 "The v4l headers were not available at compile time,\n"
666 "so video capture support is not available.\n";
667 MESSAGE(msg);
668 return NULL;
671 #define FAIL_WITH_ERR \
672 ERR("v4l absent: shouldn't be called\n"); \
673 return E_NOTIMPL
675 HRESULT qcap_driver_destroy(Capture *capBox)
677 FAIL_WITH_ERR;
680 HRESULT qcap_driver_check_format(Capture *device, const AM_MEDIA_TYPE *mt)
682 FAIL_WITH_ERR;
685 HRESULT qcap_driver_set_format(Capture *capBox, AM_MEDIA_TYPE * mT)
687 FAIL_WITH_ERR;
690 HRESULT qcap_driver_get_format(const Capture *capBox, AM_MEDIA_TYPE ** mT)
692 FAIL_WITH_ERR;
695 HRESULT qcap_driver_get_prop_range( Capture *capBox,
696 VideoProcAmpProperty Property, LONG *pMin, LONG *pMax,
697 LONG *pSteppingDelta, LONG *pDefault, LONG *pCapsFlags )
699 FAIL_WITH_ERR;
702 HRESULT qcap_driver_get_prop(Capture *capBox,
703 VideoProcAmpProperty Property, LONG *lValue, LONG *Flags)
705 FAIL_WITH_ERR;
708 HRESULT qcap_driver_set_prop(Capture *capBox, VideoProcAmpProperty Property,
709 LONG lValue, LONG Flags)
711 FAIL_WITH_ERR;
714 HRESULT qcap_driver_run(Capture *capBox, FILTER_STATE *state)
716 FAIL_WITH_ERR;
719 HRESULT qcap_driver_pause(Capture *capBox, FILTER_STATE *state)
721 FAIL_WITH_ERR;
724 HRESULT qcap_driver_stop(Capture *capBox, FILTER_STATE *state)
726 FAIL_WITH_ERR;
729 #endif /* defined(VIDIOCMCAPTURE) */