directmanipulation: Return S_OK form viewport_SetViewportOptions stub.
[wine/zf.git] / dlls / qcap / v4l.c
blobb1d7e68d426fb9ebdac317bcdc0bf5e072f2819b
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 #include <stdarg.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #ifdef HAVE_SYS_IOCTL_H
31 #include <sys/ioctl.h>
32 #endif
33 #ifdef HAVE_SYS_MMAN_H
34 #include <sys/mman.h>
35 #endif
36 #include <errno.h>
37 #ifdef HAVE_SYS_TIME_H
38 #include <sys/time.h>
39 #endif
40 #ifdef HAVE_ASM_TYPES_H
41 #include <asm/types.h>
42 #endif
43 #ifdef HAVE_LINUX_VIDEODEV2_H
44 #include <linux/videodev2.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
50 #include "qcap_private.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
54 #ifdef HAVE_LINUX_VIDEODEV2_H
56 WINE_DECLARE_DEBUG_CHANNEL(winediag);
58 static typeof(open) *video_open = open;
59 static typeof(close) *video_close = close;
60 static typeof(ioctl) *video_ioctl = ioctl;
61 static typeof(read) *video_read = read;
63 static BOOL video_init(void)
65 #ifdef SONAME_LIBV4L2
66 static void *video_lib;
68 if (video_lib)
69 return TRUE;
70 if (!(video_lib = dlopen(SONAME_LIBV4L2, RTLD_NOW)))
71 return FALSE;
72 video_open = dlsym(video_lib, "v4l2_open");
73 video_close = dlsym(video_lib, "v4l2_close");
74 video_ioctl = dlsym(video_lib, "v4l2_ioctl");
75 video_read = dlsym(video_lib, "v4l2_read");
77 return TRUE;
78 #else
79 return FALSE;
80 #endif
83 struct caps
85 __u32 pixelformat;
86 AM_MEDIA_TYPE media_type;
87 VIDEOINFOHEADER video_info;
88 VIDEO_STREAM_CONFIG_CAPS config;
91 struct v4l_device
93 struct video_capture_device d;
95 const struct caps *current_caps;
96 struct caps *caps;
97 LONG caps_count;
99 int image_size, image_pitch;
101 struct strmbase_source *pin;
102 int fd, mmap;
104 HANDLE thread;
105 FILTER_STATE state;
106 CONDITION_VARIABLE state_cv;
107 CRITICAL_SECTION state_cs;
110 static inline struct v4l_device *v4l_device(struct video_capture_device *iface)
112 return CONTAINING_RECORD(iface, struct v4l_device, d);
115 static int xioctl(int fd, int request, void * arg)
117 int r;
119 do {
120 r = video_ioctl (fd, request, arg);
121 } while (-1 == r && EINTR == errno);
123 return r;
126 static void v4l_device_destroy(struct video_capture_device *iface)
128 struct v4l_device *device = v4l_device(iface);
130 device->state_cs.DebugInfo->Spare[0] = 0;
131 DeleteCriticalSection(&device->state_cs);
132 if (device->fd != -1)
133 video_close(device->fd);
134 if (device->caps_count)
135 heap_free(device->caps);
136 heap_free(device);
139 static const struct caps *find_caps(struct v4l_device *device, const AM_MEDIA_TYPE *mt)
141 const VIDEOINFOHEADER *video_info = (VIDEOINFOHEADER *)mt->pbFormat;
142 LONG index;
144 if (mt->cbFormat < sizeof(VIDEOINFOHEADER) || !video_info)
145 return NULL;
147 for (index = 0; index < device->caps_count; index++)
149 struct caps *caps = &device->caps[index];
151 if (IsEqualGUID(&mt->formattype, &caps->media_type.formattype)
152 && video_info->bmiHeader.biWidth == caps->video_info.bmiHeader.biWidth
153 && video_info->bmiHeader.biHeight == caps->video_info.bmiHeader.biHeight)
154 return caps;
156 return NULL;
159 static HRESULT v4l_device_check_format(struct video_capture_device *iface, const AM_MEDIA_TYPE *mt)
161 struct v4l_device *device = v4l_device(iface);
163 TRACE("device %p, mt %p.\n", device, mt);
165 if (!mt)
166 return E_POINTER;
168 if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video))
169 return E_FAIL;
171 if (find_caps(device, mt))
172 return S_OK;
174 return E_FAIL;
177 static BOOL set_caps(struct v4l_device *device, const struct caps *caps)
179 struct v4l2_format format = {0};
180 LONG width, height;
182 width = caps->video_info.bmiHeader.biWidth;
183 height = caps->video_info.bmiHeader.biHeight;
185 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
186 format.fmt.pix.pixelformat = caps->pixelformat;
187 format.fmt.pix.width = width;
188 format.fmt.pix.height = height;
189 if (xioctl(device->fd, VIDIOC_S_FMT, &format) == -1
190 || format.fmt.pix.pixelformat != caps->pixelformat
191 || format.fmt.pix.width != width
192 || format.fmt.pix.height != height)
194 ERR("Failed to set pixel format: %s.\n", strerror(errno));
195 return FALSE;
198 device->current_caps = caps;
199 device->image_size = width * height * caps->video_info.bmiHeader.biBitCount / 8;
200 device->image_pitch = width * caps->video_info.bmiHeader.biBitCount / 8;
202 return TRUE;
205 static HRESULT v4l_device_set_format(struct video_capture_device *iface, const AM_MEDIA_TYPE *mt)
207 struct v4l_device *device = v4l_device(iface);
208 const struct caps *caps;
210 caps = find_caps(device, mt);
211 if (!caps)
212 return E_FAIL;
214 if (device->current_caps == caps)
215 return S_OK;
217 if (!set_caps(device, caps))
218 return VFW_E_TYPE_NOT_ACCEPTED;
220 return S_OK;
223 static HRESULT v4l_device_get_format(struct video_capture_device *iface, AM_MEDIA_TYPE *mt)
225 struct v4l_device *device = v4l_device(iface);
227 return CopyMediaType(mt, &device->current_caps->media_type);
230 static HRESULT v4l_device_get_media_type(struct video_capture_device *iface,
231 unsigned int index, AM_MEDIA_TYPE *mt)
233 struct v4l_device *device = v4l_device(iface);
234 unsigned int caps_count = (device->current_caps) ? 1 : device->caps_count;
236 if (index >= caps_count)
237 return VFW_S_NO_MORE_ITEMS;
239 if (device->current_caps)
240 return CopyMediaType(mt, &device->current_caps->media_type);
242 return CopyMediaType(mt, &device->caps[index].media_type);
245 static __u32 v4l2_cid_from_qcap_property(VideoProcAmpProperty property)
247 switch (property)
249 case VideoProcAmp_Brightness:
250 return V4L2_CID_BRIGHTNESS;
251 case VideoProcAmp_Contrast:
252 return V4L2_CID_CONTRAST;
253 case VideoProcAmp_Hue:
254 return V4L2_CID_HUE;
255 case VideoProcAmp_Saturation:
256 return V4L2_CID_SATURATION;
257 default:
258 FIXME("Unhandled property %d.\n", property);
259 return 0;
263 static HRESULT v4l_device_get_prop_range(struct video_capture_device *iface, VideoProcAmpProperty property,
264 LONG *min, LONG *max, LONG *step, LONG *default_value, LONG *flags)
266 struct v4l_device *device = v4l_device(iface);
267 struct v4l2_queryctrl ctrl;
269 ctrl.id = v4l2_cid_from_qcap_property(property);
271 if (xioctl(device->fd, VIDIOC_QUERYCTRL, &ctrl) == -1)
273 WARN("Failed to query control: %s\n", strerror(errno));
274 return E_PROP_ID_UNSUPPORTED;
277 *min = ctrl.minimum;
278 *max = ctrl.maximum;
279 *step = ctrl.step;
280 *default_value = ctrl.default_value;
281 *flags = VideoProcAmp_Flags_Manual;
282 return S_OK;
285 static HRESULT v4l_device_get_prop(struct video_capture_device *iface,
286 VideoProcAmpProperty property, LONG *value, LONG *flags)
288 struct v4l_device *device = v4l_device(iface);
289 struct v4l2_control ctrl;
291 ctrl.id = v4l2_cid_from_qcap_property(property);
293 if (xioctl(device->fd, VIDIOC_G_CTRL, &ctrl) == -1)
295 WARN("Failed to get property: %s\n", strerror(errno));
296 return E_FAIL;
299 *value = ctrl.value;
300 *flags = VideoProcAmp_Flags_Manual;
302 return S_OK;
305 static HRESULT v4l_device_set_prop(struct video_capture_device *iface,
306 VideoProcAmpProperty property, LONG value, LONG flags)
308 struct v4l_device *device = v4l_device(iface);
309 struct v4l2_control ctrl;
311 ctrl.id = v4l2_cid_from_qcap_property(property);
312 ctrl.value = value;
314 if (xioctl(device->fd, VIDIOC_S_CTRL, &ctrl) == -1)
316 WARN("Failed to set property: %s\n", strerror(errno));
317 return E_FAIL;
320 return S_OK;
323 static void reverse_image(struct v4l_device *device, LPBYTE output, const BYTE *input)
325 int inoffset, outoffset, pitch;
327 /* the whole image needs to be reversed,
328 because the dibs are messed up in windows */
329 outoffset = device->image_size;
330 pitch = device->image_pitch;
331 inoffset = 0;
332 while (outoffset > 0)
334 int x;
335 outoffset -= pitch;
336 for (x = 0; x < pitch; x++)
337 output[outoffset + x] = input[inoffset + x];
338 inoffset += pitch;
342 static DWORD WINAPI ReadThread(void *arg)
344 struct v4l_device *device = arg;
345 HRESULT hr;
346 IMediaSample *pSample = NULL;
347 unsigned char *pTarget, *image_data;
349 if (!(image_data = heap_alloc(device->image_size)))
351 ERR("Failed to allocate memory.\n");
352 return 0;
355 for (;;)
357 EnterCriticalSection(&device->state_cs);
359 while (device->state == State_Paused)
360 SleepConditionVariableCS(&device->state_cv, &device->state_cs, INFINITE);
362 if (device->state == State_Stopped)
364 LeaveCriticalSection(&device->state_cs);
365 break;
368 LeaveCriticalSection(&device->state_cs);
370 hr = BaseOutputPinImpl_GetDeliveryBuffer(device->pin, &pSample, NULL, NULL, 0);
371 if (SUCCEEDED(hr))
373 int len;
375 IMediaSample_SetActualDataLength(pSample, device->image_size);
377 len = IMediaSample_GetActualDataLength(pSample);
378 TRACE("Data length: %d KB\n", len / 1024);
380 IMediaSample_GetPointer(pSample, &pTarget);
382 while (video_read(device->fd, image_data, device->image_size) == -1)
384 if (errno != EAGAIN)
386 ERR("Failed to read frame: %s\n", strerror(errno));
387 break;
391 reverse_image(device, pTarget, image_data);
392 hr = IMemInputPin_Receive(device->pin->pMemInputPin, pSample);
393 IMediaSample_Release(pSample);
396 if (FAILED(hr) && hr != VFW_E_NOT_CONNECTED)
398 TRACE("Return %x, stop IFilterGraph\n", hr);
399 break;
403 heap_free(image_data);
404 return 0;
407 static void v4l_device_init_stream(struct video_capture_device *iface)
409 struct v4l_device *device = v4l_device(iface);
410 ALLOCATOR_PROPERTIES req_props, ret_props;
411 HRESULT hr;
413 req_props.cBuffers = 3;
414 req_props.cbBuffer = device->image_size;
415 req_props.cbAlign = 1;
416 req_props.cbPrefix = 0;
418 hr = IMemAllocator_SetProperties(device->pin->pAllocator, &req_props, &ret_props);
419 if (FAILED(hr))
420 ERR("Failed to set allocator properties (buffer size %u), hr %#x.\n", req_props.cbBuffer, hr);
422 if (SUCCEEDED(hr))
424 if (FAILED(hr = IMemAllocator_Commit(device->pin->pAllocator)))
425 ERR("Failed to commit allocator, hr %#x.\n", hr);
428 device->state = State_Paused;
429 device->thread = CreateThread(NULL, 0, ReadThread, device, 0, NULL);
432 static void v4l_device_start_stream(struct video_capture_device *iface)
434 struct v4l_device *device = v4l_device(iface);
435 EnterCriticalSection(&device->state_cs);
436 device->state = State_Running;
437 LeaveCriticalSection(&device->state_cs);
440 static void v4l_device_stop_stream(struct video_capture_device *iface)
442 struct v4l_device *device = v4l_device(iface);
443 EnterCriticalSection(&device->state_cs);
444 device->state = State_Paused;
445 LeaveCriticalSection(&device->state_cs);
448 static void v4l_device_cleanup_stream(struct video_capture_device *iface)
450 struct v4l_device *device = v4l_device(iface);
451 HRESULT hr;
453 EnterCriticalSection(&device->state_cs);
454 device->state = State_Stopped;
455 LeaveCriticalSection(&device->state_cs);
456 WakeConditionVariable(&device->state_cv);
457 WaitForSingleObject(device->thread, INFINITE);
458 CloseHandle(device->thread);
459 device->thread = NULL;
461 hr = IMemAllocator_Decommit(device->pin->pAllocator);
462 if (hr != S_OK && hr != VFW_E_NOT_COMMITTED)
463 ERR("Failed to decommit allocator, hr %#x.\n", hr);
467 static void fill_caps(__u32 pixelformat, __u32 width, __u32 height,
468 __u32 max_fps, __u32 min_fps, struct caps *caps)
470 LONG depth = 24;
472 memset(caps, 0, sizeof(*caps));
473 caps->video_info.dwBitRate = width * height * depth * max_fps;
474 caps->video_info.bmiHeader.biSize = sizeof(caps->video_info.bmiHeader);
475 caps->video_info.bmiHeader.biWidth = width;
476 caps->video_info.bmiHeader.biHeight = height;
477 caps->video_info.bmiHeader.biPlanes = 1;
478 caps->video_info.bmiHeader.biBitCount = depth;
479 caps->video_info.bmiHeader.biCompression = BI_RGB;
480 caps->video_info.bmiHeader.biSizeImage = width * height * depth / 8;
481 caps->media_type.majortype = MEDIATYPE_Video;
482 caps->media_type.subtype = MEDIASUBTYPE_RGB24;
483 caps->media_type.bFixedSizeSamples = TRUE;
484 caps->media_type.bTemporalCompression = FALSE;
485 caps->media_type.lSampleSize = width * height * depth / 8;
486 caps->media_type.formattype = FORMAT_VideoInfo;
487 caps->media_type.pUnk = NULL;
488 caps->media_type.cbFormat = sizeof(VIDEOINFOHEADER);
489 /* We reallocate the caps array, so pbFormat has to be set after all caps
490 * have been enumerated. */
491 caps->config.MaxFrameInterval = 10000000 * max_fps;
492 caps->config.MinFrameInterval = 10000000 * min_fps;
493 caps->config.MaxOutputSize.cx = width;
494 caps->config.MaxOutputSize.cy = height;
495 caps->config.MinOutputSize.cx = width;
496 caps->config.MinOutputSize.cy = height;
497 caps->config.guid = FORMAT_VideoInfo;
498 caps->config.MinBitsPerSecond = width * height * depth * min_fps;
499 caps->config.MaxBitsPerSecond = width * height * depth * max_fps;
500 caps->pixelformat = pixelformat;
503 static HRESULT v4l_device_get_caps(struct video_capture_device *iface, LONG index,
504 AM_MEDIA_TYPE **type, VIDEO_STREAM_CONFIG_CAPS *vscc)
506 struct v4l_device *device = v4l_device(iface);
508 if (index >= device->caps_count)
509 return S_FALSE;
511 *type = CreateMediaType(&device->caps[index].media_type);
512 if (!*type)
513 return E_OUTOFMEMORY;
515 if (vscc)
516 memcpy(vscc, &device->caps[index].config, sizeof(VIDEO_STREAM_CONFIG_CAPS));
517 return S_OK;
520 static LONG v4l_device_get_caps_count(struct video_capture_device *iface)
522 struct v4l_device *device = v4l_device(iface);
524 return device->caps_count;
527 static const struct video_capture_device_ops v4l_device_ops =
529 .destroy = v4l_device_destroy,
530 .check_format = v4l_device_check_format,
531 .set_format = v4l_device_set_format,
532 .get_format = v4l_device_get_format,
533 .get_media_type = v4l_device_get_media_type,
534 .get_caps = v4l_device_get_caps,
535 .get_caps_count = v4l_device_get_caps_count,
536 .get_prop_range = v4l_device_get_prop_range,
537 .get_prop = v4l_device_get_prop,
538 .set_prop = v4l_device_set_prop,
539 .init_stream = v4l_device_init_stream,
540 .start_stream = v4l_device_start_stream,
541 .stop_stream = v4l_device_stop_stream,
542 .cleanup_stream = v4l_device_cleanup_stream,
545 struct video_capture_device *v4l_device_create(struct strmbase_source *pin, USHORT card)
547 struct v4l2_frmsizeenum frmsize = {0};
548 struct v4l2_capability caps = {{0}};
549 struct v4l2_format format = {0};
550 struct v4l_device *device;
551 BOOL have_libv4l2;
552 char path[20];
553 int fd, i;
555 have_libv4l2 = video_init();
557 if (!(device = heap_alloc_zero(sizeof(*device))))
558 return NULL;
560 sprintf(path, "/dev/video%i", card);
561 TRACE("Opening device %s.\n", path);
562 #ifdef O_CLOEXEC
563 if ((fd = video_open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC)) == -1 && errno == EINVAL)
564 #endif
565 fd = video_open(path, O_RDWR | O_NONBLOCK);
566 if (fd == -1)
568 WARN("Failed to open video device: %s\n", strerror(errno));
569 goto error;
571 fcntl(fd, F_SETFD, FD_CLOEXEC); /* in case O_CLOEXEC isn't supported */
572 device->fd = fd;
574 if (xioctl(fd, VIDIOC_QUERYCAP, &caps) == -1)
576 WARN("Failed to query device capabilities: %s\n", strerror(errno));
577 goto error;
580 #ifdef V4L2_CAP_DEVICE_CAPS
581 if (caps.capabilities & V4L2_CAP_DEVICE_CAPS)
582 caps.capabilities = caps.device_caps;
583 #endif
585 if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE))
587 WARN("Device does not support single-planar video capture.\n");
588 goto error;
591 if (!(caps.capabilities & V4L2_CAP_READWRITE))
593 WARN("Device does not support read().\n");
594 if (!have_libv4l2)
595 #ifdef SONAME_LIBV4L2
596 ERR_(winediag)("Reading from %s requires libv4l2, but it could not be loaded.\n", path);
597 #else
598 ERR_(winediag)("Reading from %s requires libv4l2, but Wine was compiled without libv4l2 support.\n", path);
599 #endif
600 goto error;
603 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
604 if (xioctl(fd, VIDIOC_G_FMT, &format) == -1)
606 ERR("Failed to get device format: %s\n", strerror(errno));
607 goto error;
610 format.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
611 if (xioctl(fd, VIDIOC_TRY_FMT, &format) == -1
612 || format.fmt.pix.pixelformat != V4L2_PIX_FMT_BGR24)
614 ERR("This device doesn't support V4L2_PIX_FMT_BGR24 format.\n");
615 goto error;
618 frmsize.pixel_format = V4L2_PIX_FMT_BGR24;
619 while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) != -1)
621 struct v4l2_frmivalenum frmival = {0};
622 __u32 max_fps = 30, min_fps = 30;
623 struct caps *new_caps;
625 frmival.pixel_format = format.fmt.pix.pixelformat;
626 if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE)
628 frmival.width = frmsize.discrete.width;
629 frmival.height = frmsize.discrete.height;
631 else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE)
633 frmival.width = frmsize.stepwise.max_width;
634 frmival.height = frmsize.stepwise.min_height;
636 else
638 FIXME("Unhandled frame size type: %d.\n", frmsize.type);
639 continue;
642 if (xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1)
644 if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
646 max_fps = frmival.discrete.denominator / frmival.discrete.numerator;
647 min_fps = max_fps;
649 else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE
650 || frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)
652 max_fps = frmival.stepwise.max.denominator / frmival.stepwise.max.numerator;
653 min_fps = frmival.stepwise.min.denominator / frmival.stepwise.min.numerator;
656 else
657 ERR("Failed to get fps: %s.\n", strerror(errno));
659 new_caps = heap_realloc(device->caps, (device->caps_count + 1) * sizeof(*device->caps));
660 if (!new_caps)
661 goto error;
662 device->caps = new_caps;
663 fill_caps(format.fmt.pix.pixelformat, frmsize.discrete.width, frmsize.discrete.height,
664 max_fps, min_fps, &device->caps[device->caps_count]);
665 device->caps_count++;
667 frmsize.index++;
670 /* We reallocate the caps array, so we have to delay setting pbFormat. */
671 for (i = 0; i < device->caps_count; ++i)
672 device->caps[i].media_type.pbFormat = (BYTE *)&device->caps[i].video_info;
674 if (!set_caps(device, &device->caps[0]))
676 ERR("Failed to set pixel format: %s\n", strerror(errno));
677 if (!have_libv4l2)
678 ERR_(winediag)("You may need libv4l2 to use this device.\n");
679 goto error;
682 device->d.ops = &v4l_device_ops;
683 device->pin = pin;
684 device->state = State_Stopped;
685 InitializeConditionVariable(&device->state_cv);
686 InitializeCriticalSection(&device->state_cs);
687 device->state_cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": v4l_device.state_cs");
689 TRACE("Format: %d bpp - %dx%d.\n", device->current_caps->video_info.bmiHeader.biBitCount,
690 device->current_caps->video_info.bmiHeader.biWidth,
691 device->current_caps->video_info.bmiHeader.biHeight);
693 return &device->d;
695 error:
696 v4l_device_destroy(&device->d);
697 return NULL;
700 #else
702 struct video_capture_device *v4l_device_create(struct strmbase_source *pin, USHORT card)
704 ERR("v4l2 was not present at compilation time.\n");
705 return NULL;
708 #endif /* defined(VIDIOCMCAPTURE) */