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
26 #define BIONIC_IOCTL_NO_SIGNEDNESS_OVERLOAD /* work around ioctl breakage on Android */
29 #include "wine/port.h"
34 #ifdef HAVE_SYS_IOCTL_H
35 #include <sys/ioctl.h>
37 #ifdef HAVE_SYS_MMAN_H
41 #ifdef HAVE_SYS_TIME_H
44 #ifdef HAVE_ASM_TYPES_H
45 #include <asm/types.h>
47 #ifdef HAVE_LINUX_VIDEODEV2_H
48 #include <linux/videodev2.h>
55 #define WIN32_NO_STATUS
57 #include "qcap_private.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)
73 static void *video_lib
;
77 if (!(video_lib
= dlopen(SONAME_LIBV4L2
, RTLD_NOW
)))
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");
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
;
104 int image_size
, image_pitch
;
110 static int xioctl(int fd
, int request
, void * arg
)
115 r
= video_ioctl (fd
, request
, arg
);
116 } while (-1 == r
&& EINTR
== errno
);
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
)
127 free(device
->image_data
);
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
;
136 if (mt
->cbFormat
< sizeof(VIDEOINFOHEADER
) || !video_info
)
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
)
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
);
158 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Video
))
161 if (find_caps(device
, mt
))
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
;
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
));
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
;
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
);
213 if (device
->current_caps
== caps
)
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
;
240 *mt
= device
->caps
[index
].media_type
;
241 *format
= device
->caps
[index
].video_info
;
246 static __u32
v4l2_cid_from_qcap_property(VideoProcAmpProperty property
)
250 case VideoProcAmp_Brightness
:
251 return V4L2_CID_BRIGHTNESS
;
252 case VideoProcAmp_Contrast
:
253 return V4L2_CID_CONTRAST
;
254 case VideoProcAmp_Hue
:
256 case VideoProcAmp_Saturation
:
257 return V4L2_CID_SATURATION
;
259 FIXME("Unhandled property %d.\n", property
);
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
;
280 *default_value
= ctrl
.default_value
;
281 *flags
= VideoProcAmp_Flags_Manual
;
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
));
299 *flags
= VideoProcAmp_Flags_Manual
;
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
);
312 if (xioctl(device
->fd
, VIDIOC_S_CTRL
, &ctrl
) == -1)
314 WARN("Failed to set property: %s\n", strerror(errno
));
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
;
330 while (outoffset
> 0)
334 for (x
= 0; x
< pitch
; x
++)
335 output
[outoffset
+ x
] = input
[inoffset
+ x
];
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)
346 ERR("Failed to read frame: %s\n", strerror(errno
));
351 reverse_image(device
, data
, device
->image_data
);
355 static void fill_caps(__u32 pixelformat
, __u32 width
, __u32 height
,
356 __u32 max_fps
, __u32 min_fps
, struct caps
*caps
)
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};
415 have_libv4l2
= video_init();
417 if (!(device
= calloc(1, sizeof(*device
))))
420 sprintf(path
, "/dev/video%i", index
);
421 TRACE("Opening device %s.\n", path
);
423 if ((fd
= video_open(path
, O_RDWR
| O_NONBLOCK
| O_CLOEXEC
)) == -1 && errno
== EINVAL
)
425 fd
= video_open(path
, O_RDWR
| O_NONBLOCK
);
428 WARN("Failed to open video device: %s\n", strerror(errno
));
431 fcntl(fd
, F_SETFD
, FD_CLOEXEC
); /* in case O_CLOEXEC isn't supported */
434 if (xioctl(fd
, VIDIOC_QUERYCAP
, &caps
) == -1)
436 WARN("Failed to query device capabilities: %s\n", strerror(errno
));
440 #ifdef V4L2_CAP_DEVICE_CAPS
441 if (caps
.capabilities
& V4L2_CAP_DEVICE_CAPS
)
442 caps
.capabilities
= caps
.device_caps
;
445 if (!(caps
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
))
447 WARN("Device does not support single-planar video capture.\n");
451 if (!(caps
.capabilities
& V4L2_CAP_READWRITE
))
453 WARN("Device does not support read().\n");
455 #ifdef SONAME_LIBV4L2
456 ERR_(winediag
)("Reading from %s requires libv4l2, but it could not be loaded.\n", path
);
458 ERR_(winediag
)("Reading from %s requires libv4l2, but Wine was compiled without libv4l2 support.\n", path
);
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
));
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");
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
;
498 FIXME("Unhandled frame size type: %d.\n", frmsize
.type
);
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
;
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
;
517 ERR("Failed to get fps: %s.\n", strerror(errno
));
519 new_caps
= realloc(device
->caps
, (device
->caps_count
+ 1) * sizeof(*device
->caps
));
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
++;
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");
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
);
548 v4l_device_destroy(device
);
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 */