include: Move InterlockedCompareExchange128 next to the other InterlockedCompareExcha...
[wine.git] / dlls / qcap / v4l.c
blobccc06194b9957a77509f4c56c8651d3f50a4c283
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 #if 0
23 #pragma makedep unix
24 #endif
26 #define BIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD /* work around ioctl breakage on Android */
28 #include "config.h"
29 #include "wine/port.h"
31 #include <stdarg.h>
32 #include <stdio.h>
33 #include <fcntl.h>
34 #ifdef HAVE_SYS_IOCTL_H
35 #include <sys/ioctl.h>
36 #endif
37 #ifdef HAVE_SYS_MMAN_H
38 #include <sys/mman.h>
39 #endif
40 #include <errno.h>
41 #ifdef HAVE_SYS_TIME_H
42 #include <sys/time.h>
43 #endif
44 #ifdef HAVE_ASM_TYPES_H
45 #include <asm/types.h>
46 #endif
47 #ifdef HAVE_LINUX_VIDEODEV2_H
48 #include <linux/videodev2.h>
49 #endif
50 #ifdef HAVE_UNISTD_H
51 #include <unistd.h>
52 #endif
54 #include "ntstatus.h"
55 #define WIN32_NO_STATUS
56 #include "initguid.h"
57 #include "qcap_private.h"
58 #include "winternl.h"
60 #ifdef HAVE_LINUX_VIDEODEV2_H
62 WINE_DEFAULT_DEBUG_CHANNEL(qcap);
63 WINE_DECLARE_DEBUG_CHANNEL(winediag);
65 static typeof(open) *video_open = open;
66 static typeof(close) *video_close = close;
67 static typeof(ioctl) *video_ioctl = ioctl;
68 static typeof(read) *video_read = read;
70 static BOOL video_init(void)
72 #ifdef SONAME_LIBV4L2
73 static void *video_lib;
75 if (video_lib)
76 return TRUE;
77 if (!(video_lib = dlopen(SONAME_LIBV4L2, RTLD_NOW)))
78 return FALSE;
79 video_open = dlsym(video_lib, "v4l2_open");
80 video_close = dlsym(video_lib, "v4l2_close");
81 video_ioctl = dlsym(video_lib, "v4l2_ioctl");
82 video_read = dlsym(video_lib, "v4l2_read");
84 return TRUE;
85 #else
86 return FALSE;
87 #endif
90 struct caps
92 __u32 pixelformat;
93 AM_MEDIA_TYPE media_type;
94 VIDEOINFOHEADER video_info;
95 VIDEO_STREAM_CONFIG_CAPS config;
98 struct video_capture_device
100 const struct caps *current_caps;
101 struct caps *caps;
102 LONG caps_count;
104 int image_size, image_pitch;
105 BYTE *image_data;
107 int fd, mmap;
110 static int xioctl(int fd, int request, void * arg)
112 int r;
114 do {
115 r = video_ioctl (fd, request, arg);
116 } while (-1 == r && EINTR == errno);
118 return r;
121 static void CDECL v4l_device_destroy(struct video_capture_device *device)
123 if (device->fd != -1)
124 video_close(device->fd);
125 if (device->caps_count)
126 free(device->caps);
127 free(device->image_data);
128 free(device);
131 static const struct caps *find_caps(struct video_capture_device *device, const AM_MEDIA_TYPE *mt)
133 const VIDEOINFOHEADER *video_info = (VIDEOINFOHEADER *)mt->pbFormat;
134 LONG index;
136 if (mt->cbFormat < sizeof(VIDEOINFOHEADER) || !video_info)
137 return NULL;
139 for (index = 0; index < device->caps_count; index++)
141 struct caps *caps = &device->caps[index];
143 if (IsEqualGUID(&mt->formattype, &caps->media_type.formattype)
144 && video_info->bmiHeader.biWidth == caps->video_info.bmiHeader.biWidth
145 && video_info->bmiHeader.biHeight == caps->video_info.bmiHeader.biHeight)
146 return caps;
148 return NULL;
151 static HRESULT CDECL v4l_device_check_format(struct video_capture_device *device, const AM_MEDIA_TYPE *mt)
153 TRACE("device %p, mt %p.\n", device, mt);
155 if (!mt)
156 return E_POINTER;
158 if (!IsEqualGUID(&mt->majortype, &MEDIATYPE_Video))
159 return E_FAIL;
161 if (find_caps(device, mt))
162 return S_OK;
164 return E_FAIL;
167 static HRESULT set_caps(struct video_capture_device *device, const struct caps *caps)
169 struct v4l2_format format = {0};
170 LONG width, height, image_size;
171 BYTE *image_data;
173 width = caps->video_info.bmiHeader.biWidth;
174 height = caps->video_info.bmiHeader.biHeight;
175 image_size = width * height * caps->video_info.bmiHeader.biBitCount / 8;
177 if (!(image_data = malloc(image_size)))
179 ERR("Failed to allocate memory.\n");
180 return E_OUTOFMEMORY;
183 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
184 format.fmt.pix.pixelformat = caps->pixelformat;
185 format.fmt.pix.width = width;
186 format.fmt.pix.height = height;
187 if (xioctl(device->fd, VIDIOC_S_FMT, &format) == -1
188 || format.fmt.pix.pixelformat != caps->pixelformat
189 || format.fmt.pix.width != width
190 || format.fmt.pix.height != height)
192 ERR("Failed to set pixel format: %s.\n", strerror(errno));
193 free(image_data);
194 return VFW_E_TYPE_NOT_ACCEPTED;
197 device->current_caps = caps;
198 device->image_size = image_size;
199 device->image_pitch = width * caps->video_info.bmiHeader.biBitCount / 8;
200 free(device->image_data);
201 device->image_data = image_data;
202 return S_OK;
205 static HRESULT CDECL v4l_device_set_format(struct video_capture_device *device, const AM_MEDIA_TYPE *mt)
207 const struct caps *caps;
209 caps = find_caps(device, mt);
210 if (!caps)
211 return E_FAIL;
213 if (device->current_caps == caps)
214 return S_OK;
216 return set_caps(device, caps);
219 static void CDECL v4l_device_get_format(struct video_capture_device *device, AM_MEDIA_TYPE *mt, VIDEOINFOHEADER *format)
221 *mt = device->current_caps->media_type;
222 *format = device->current_caps->video_info;
225 static HRESULT CDECL v4l_device_get_media_type(struct video_capture_device *device,
226 unsigned int index, AM_MEDIA_TYPE *mt, VIDEOINFOHEADER *format)
228 unsigned int caps_count = (device->current_caps) ? 1 : device->caps_count;
230 if (index >= caps_count)
231 return VFW_S_NO_MORE_ITEMS;
233 if (device->current_caps)
235 *mt = device->current_caps->media_type;
236 *format = device->current_caps->video_info;
238 else
240 *mt = device->caps[index].media_type;
241 *format = device->caps[index].video_info;
243 return S_OK;
246 static __u32 v4l2_cid_from_qcap_property(VideoProcAmpProperty property)
248 switch (property)
250 case VideoProcAmp_Brightness:
251 return V4L2_CID_BRIGHTNESS;
252 case VideoProcAmp_Contrast:
253 return V4L2_CID_CONTRAST;
254 case VideoProcAmp_Hue:
255 return V4L2_CID_HUE;
256 case VideoProcAmp_Saturation:
257 return V4L2_CID_SATURATION;
258 default:
259 FIXME("Unhandled property %d.\n", property);
260 return 0;
264 static HRESULT CDECL v4l_device_get_prop_range(struct video_capture_device *device, VideoProcAmpProperty property,
265 LONG *min, LONG *max, LONG *step, LONG *default_value, LONG *flags)
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 CDECL v4l_device_get_prop(struct video_capture_device *device,
286 VideoProcAmpProperty property, LONG *value, LONG *flags)
288 struct v4l2_control ctrl;
290 ctrl.id = v4l2_cid_from_qcap_property(property);
292 if (xioctl(device->fd, VIDIOC_G_CTRL, &ctrl) == -1)
294 WARN("Failed to get property: %s\n", strerror(errno));
295 return E_FAIL;
298 *value = ctrl.value;
299 *flags = VideoProcAmp_Flags_Manual;
301 return S_OK;
304 static HRESULT CDECL v4l_device_set_prop(struct video_capture_device *device,
305 VideoProcAmpProperty property, LONG value, LONG flags)
307 struct v4l2_control ctrl;
309 ctrl.id = v4l2_cid_from_qcap_property(property);
310 ctrl.value = value;
312 if (xioctl(device->fd, VIDIOC_S_CTRL, &ctrl) == -1)
314 WARN("Failed to set property: %s\n", strerror(errno));
315 return E_FAIL;
318 return S_OK;
321 static void reverse_image(struct video_capture_device *device, LPBYTE output, const BYTE *input)
323 int inoffset, outoffset, pitch;
325 /* the whole image needs to be reversed,
326 because the dibs are messed up in windows */
327 outoffset = device->image_size;
328 pitch = device->image_pitch;
329 inoffset = 0;
330 while (outoffset > 0)
332 int x;
333 outoffset -= pitch;
334 for (x = 0; x < pitch; x++)
335 output[outoffset + x] = input[inoffset + x];
336 inoffset += pitch;
340 static BOOL CDECL v4l_device_read_frame(struct video_capture_device *device, BYTE *data)
342 while (video_read(device->fd, device->image_data, device->image_size) < 0)
344 if (errno != EAGAIN)
346 ERR("Failed to read frame: %s\n", strerror(errno));
347 return FALSE;
351 reverse_image(device, data, device->image_data);
352 return TRUE;
355 static void fill_caps(__u32 pixelformat, __u32 width, __u32 height,
356 __u32 max_fps, __u32 min_fps, struct caps *caps)
358 LONG depth = 24;
360 memset(caps, 0, sizeof(*caps));
361 caps->video_info.dwBitRate = width * height * depth * max_fps;
362 caps->video_info.bmiHeader.biSize = sizeof(caps->video_info.bmiHeader);
363 caps->video_info.bmiHeader.biWidth = width;
364 caps->video_info.bmiHeader.biHeight = height;
365 caps->video_info.bmiHeader.biPlanes = 1;
366 caps->video_info.bmiHeader.biBitCount = depth;
367 caps->video_info.bmiHeader.biCompression = BI_RGB;
368 caps->video_info.bmiHeader.biSizeImage = width * height * depth / 8;
369 caps->media_type.majortype = MEDIATYPE_Video;
370 caps->media_type.subtype = MEDIASUBTYPE_RGB24;
371 caps->media_type.bFixedSizeSamples = TRUE;
372 caps->media_type.bTemporalCompression = FALSE;
373 caps->media_type.lSampleSize = width * height * depth / 8;
374 caps->media_type.formattype = FORMAT_VideoInfo;
375 caps->media_type.pUnk = NULL;
376 caps->media_type.cbFormat = sizeof(VIDEOINFOHEADER);
377 /* We reallocate the caps array, so pbFormat has to be set after all caps
378 * have been enumerated. */
379 caps->config.MaxFrameInterval = 10000000 * max_fps;
380 caps->config.MinFrameInterval = 10000000 * min_fps;
381 caps->config.MaxOutputSize.cx = width;
382 caps->config.MaxOutputSize.cy = height;
383 caps->config.MinOutputSize.cx = width;
384 caps->config.MinOutputSize.cy = height;
385 caps->config.guid = FORMAT_VideoInfo;
386 caps->config.MinBitsPerSecond = width * height * depth * min_fps;
387 caps->config.MaxBitsPerSecond = width * height * depth * max_fps;
388 caps->pixelformat = pixelformat;
391 static void CDECL v4l_device_get_caps(struct video_capture_device *device, LONG index,
392 AM_MEDIA_TYPE *type, VIDEOINFOHEADER *format, VIDEO_STREAM_CONFIG_CAPS *vscc)
394 *vscc = device->caps[index].config;
395 *type = device->caps[index].media_type;
396 *format = device->caps[index].video_info;
399 static LONG CDECL v4l_device_get_caps_count(struct video_capture_device *device)
401 return device->caps_count;
404 struct video_capture_device * CDECL v4l_device_create(USHORT index)
406 struct v4l2_frmsizeenum frmsize = {0};
407 struct video_capture_device *device;
408 struct v4l2_capability caps = {{0}};
409 struct v4l2_format format = {0};
410 BOOL have_libv4l2;
411 char path[20];
412 HRESULT hr;
413 int fd, i;
415 have_libv4l2 = video_init();
417 if (!(device = calloc(1, sizeof(*device))))
418 return NULL;
420 sprintf(path, "/dev/video%i", index);
421 TRACE("Opening device %s.\n", path);
422 #ifdef O_CLOEXEC
423 if ((fd = video_open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC)) == -1 && errno == EINVAL)
424 #endif
425 fd = video_open(path, O_RDWR | O_NONBLOCK);
426 if (fd == -1)
428 WARN("Failed to open video device: %s\n", strerror(errno));
429 goto error;
431 fcntl(fd, F_SETFD, FD_CLOEXEC); /* in case O_CLOEXEC isn't supported */
432 device->fd = fd;
434 if (xioctl(fd, VIDIOC_QUERYCAP, &caps) == -1)
436 WARN("Failed to query device capabilities: %s\n", strerror(errno));
437 goto error;
440 #ifdef V4L2_CAP_DEVICE_CAPS
441 if (caps.capabilities & V4L2_CAP_DEVICE_CAPS)
442 caps.capabilities = caps.device_caps;
443 #endif
445 if (!(caps.capabilities & V4L2_CAP_VIDEO_CAPTURE))
447 WARN("Device does not support single-planar video capture.\n");
448 goto error;
451 if (!(caps.capabilities & V4L2_CAP_READWRITE))
453 WARN("Device does not support read().\n");
454 if (!have_libv4l2)
455 #ifdef SONAME_LIBV4L2
456 ERR_(winediag)("Reading from %s requires libv4l2, but it could not be loaded.\n", path);
457 #else
458 ERR_(winediag)("Reading from %s requires libv4l2, but Wine was compiled without libv4l2 support.\n", path);
459 #endif
460 goto error;
463 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
464 if (xioctl(fd, VIDIOC_G_FMT, &format) == -1)
466 ERR("Failed to get device format: %s\n", strerror(errno));
467 goto error;
470 format.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24;
471 if (xioctl(fd, VIDIOC_TRY_FMT, &format) == -1
472 || format.fmt.pix.pixelformat != V4L2_PIX_FMT_BGR24)
474 ERR("This device doesn't support V4L2_PIX_FMT_BGR24 format.\n");
475 goto error;
478 frmsize.pixel_format = V4L2_PIX_FMT_BGR24;
479 while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) != -1)
481 struct v4l2_frmivalenum frmival = {0};
482 __u32 max_fps = 30, min_fps = 30;
483 struct caps *new_caps;
485 frmival.pixel_format = format.fmt.pix.pixelformat;
486 if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE)
488 frmival.width = frmsize.discrete.width;
489 frmival.height = frmsize.discrete.height;
491 else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE)
493 frmival.width = frmsize.stepwise.max_width;
494 frmival.height = frmsize.stepwise.min_height;
496 else
498 FIXME("Unhandled frame size type: %d.\n", frmsize.type);
499 continue;
502 if (xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1)
504 if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
506 max_fps = frmival.discrete.denominator / frmival.discrete.numerator;
507 min_fps = max_fps;
509 else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE
510 || frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)
512 max_fps = frmival.stepwise.max.denominator / frmival.stepwise.max.numerator;
513 min_fps = frmival.stepwise.min.denominator / frmival.stepwise.min.numerator;
516 else
517 ERR("Failed to get fps: %s.\n", strerror(errno));
519 new_caps = realloc(device->caps, (device->caps_count + 1) * sizeof(*device->caps));
520 if (!new_caps)
521 goto error;
522 device->caps = new_caps;
523 fill_caps(format.fmt.pix.pixelformat, frmsize.discrete.width, frmsize.discrete.height,
524 max_fps, min_fps, &device->caps[device->caps_count]);
525 device->caps_count++;
527 frmsize.index++;
530 /* We reallocate the caps array, so we have to delay setting pbFormat. */
531 for (i = 0; i < device->caps_count; ++i)
532 device->caps[i].media_type.pbFormat = (BYTE *)&device->caps[i].video_info;
534 if (FAILED(hr = set_caps(device, &device->caps[0])))
536 if (hr == VFW_E_TYPE_NOT_ACCEPTED && !have_libv4l2)
537 ERR_(winediag)("You may need libv4l2 to use this device.\n");
538 goto error;
541 TRACE("Format: %d bpp - %dx%d.\n", device->current_caps->video_info.bmiHeader.biBitCount,
542 device->current_caps->video_info.bmiHeader.biWidth,
543 device->current_caps->video_info.bmiHeader.biHeight);
545 return device;
547 error:
548 v4l_device_destroy(device);
549 return NULL;
552 const struct video_capture_funcs v4l_funcs =
554 .create = v4l_device_create,
555 .destroy = v4l_device_destroy,
556 .check_format = v4l_device_check_format,
557 .set_format = v4l_device_set_format,
558 .get_format = v4l_device_get_format,
559 .get_media_type = v4l_device_get_media_type,
560 .get_caps = v4l_device_get_caps,
561 .get_caps_count = v4l_device_get_caps_count,
562 .get_prop_range = v4l_device_get_prop_range,
563 .get_prop = v4l_device_get_prop,
564 .set_prop = v4l_device_set_prop,
565 .read_frame = v4l_device_read_frame,
568 NTSTATUS CDECL __wine_init_unix_lib(HMODULE module, DWORD reason, const void *ptr_in, void *ptr_out)
570 if (reason != DLL_PROCESS_ATTACH) return STATUS_SUCCESS;
572 *(const struct video_capture_funcs **)ptr_out = &v4l_funcs;
573 return STATUS_SUCCESS;
576 #endif /* HAVE_LINUX_VIDEODEV2_H */