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 WINE_DEFAULT_DEBUG_CHANNEL(qcap
);
62 #ifdef HAVE_LINUX_VIDEODEV2_H
64 WINE_DECLARE_DEBUG_CHANNEL(winediag
);
66 static typeof(open
) *video_open
= open
;
67 static typeof(close
) *video_close
= close
;
68 static typeof(ioctl
) *video_ioctl
= ioctl
;
69 static typeof(read
) *video_read
= read
;
71 static BOOL
video_init(void)
74 static void *video_lib
;
78 if (!(video_lib
= dlopen(SONAME_LIBV4L2
, RTLD_NOW
)))
80 video_open
= dlsym(video_lib
, "v4l2_open");
81 video_close
= dlsym(video_lib
, "v4l2_close");
82 video_ioctl
= dlsym(video_lib
, "v4l2_ioctl");
83 video_read
= dlsym(video_lib
, "v4l2_read");
94 AM_MEDIA_TYPE media_type
;
95 VIDEOINFOHEADER video_info
;
96 VIDEO_STREAM_CONFIG_CAPS config
;
99 struct video_capture_device
101 const struct caps
*current_caps
;
105 int image_size
, image_pitch
;
111 static int xioctl(int fd
, int request
, void * arg
)
116 r
= video_ioctl (fd
, request
, arg
);
117 } while (-1 == r
&& EINTR
== errno
);
122 static void CDECL
v4l_device_destroy(struct video_capture_device
*device
)
124 if (device
->fd
!= -1)
125 video_close(device
->fd
);
126 if (device
->caps_count
)
128 free(device
->image_data
);
132 static const struct caps
*find_caps(struct video_capture_device
*device
, const AM_MEDIA_TYPE
*mt
)
134 const VIDEOINFOHEADER
*video_info
= (VIDEOINFOHEADER
*)mt
->pbFormat
;
137 if (mt
->cbFormat
< sizeof(VIDEOINFOHEADER
) || !video_info
)
140 for (index
= 0; index
< device
->caps_count
; index
++)
142 struct caps
*caps
= &device
->caps
[index
];
144 if (IsEqualGUID(&mt
->formattype
, &caps
->media_type
.formattype
)
145 && video_info
->bmiHeader
.biWidth
== caps
->video_info
.bmiHeader
.biWidth
146 && video_info
->bmiHeader
.biHeight
== caps
->video_info
.bmiHeader
.biHeight
)
152 static HRESULT CDECL
v4l_device_check_format(struct video_capture_device
*device
, const AM_MEDIA_TYPE
*mt
)
154 TRACE("device %p, mt %p.\n", device
, mt
);
159 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Video
))
162 if (find_caps(device
, mt
))
168 static HRESULT
set_caps(struct video_capture_device
*device
, const struct caps
*caps
)
170 struct v4l2_format format
= {0};
171 LONG width
, height
, image_size
;
174 width
= caps
->video_info
.bmiHeader
.biWidth
;
175 height
= caps
->video_info
.bmiHeader
.biHeight
;
176 image_size
= width
* height
* caps
->video_info
.bmiHeader
.biBitCount
/ 8;
178 if (!(image_data
= malloc(image_size
)))
180 ERR("Failed to allocate memory.\n");
181 return E_OUTOFMEMORY
;
184 format
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
185 format
.fmt
.pix
.pixelformat
= caps
->pixelformat
;
186 format
.fmt
.pix
.width
= width
;
187 format
.fmt
.pix
.height
= height
;
188 if (xioctl(device
->fd
, VIDIOC_S_FMT
, &format
) == -1
189 || format
.fmt
.pix
.pixelformat
!= caps
->pixelformat
190 || format
.fmt
.pix
.width
!= width
191 || format
.fmt
.pix
.height
!= height
)
193 ERR("Failed to set pixel format: %s.\n", strerror(errno
));
195 return VFW_E_TYPE_NOT_ACCEPTED
;
198 device
->current_caps
= caps
;
199 device
->image_size
= image_size
;
200 device
->image_pitch
= width
* caps
->video_info
.bmiHeader
.biBitCount
/ 8;
201 free(device
->image_data
);
202 device
->image_data
= image_data
;
206 static HRESULT CDECL
v4l_device_set_format(struct video_capture_device
*device
, const AM_MEDIA_TYPE
*mt
)
208 const struct caps
*caps
;
210 caps
= find_caps(device
, mt
);
214 if (device
->current_caps
== caps
)
217 return set_caps(device
, caps
);
220 static void CDECL
v4l_device_get_format(struct video_capture_device
*device
, AM_MEDIA_TYPE
*mt
, VIDEOINFOHEADER
*format
)
222 *mt
= device
->current_caps
->media_type
;
223 *format
= device
->current_caps
->video_info
;
226 static HRESULT CDECL
v4l_device_get_media_type(struct video_capture_device
*device
,
227 unsigned int index
, AM_MEDIA_TYPE
*mt
, VIDEOINFOHEADER
*format
)
229 unsigned int caps_count
= (device
->current_caps
) ? 1 : device
->caps_count
;
231 if (index
>= caps_count
)
232 return VFW_S_NO_MORE_ITEMS
;
234 if (device
->current_caps
)
236 *mt
= device
->current_caps
->media_type
;
237 *format
= device
->current_caps
->video_info
;
241 *mt
= device
->caps
[index
].media_type
;
242 *format
= device
->caps
[index
].video_info
;
247 static __u32
v4l2_cid_from_qcap_property(VideoProcAmpProperty property
)
251 case VideoProcAmp_Brightness
:
252 return V4L2_CID_BRIGHTNESS
;
253 case VideoProcAmp_Contrast
:
254 return V4L2_CID_CONTRAST
;
255 case VideoProcAmp_Hue
:
257 case VideoProcAmp_Saturation
:
258 return V4L2_CID_SATURATION
;
260 FIXME("Unhandled property %d.\n", property
);
265 static HRESULT CDECL
v4l_device_get_prop_range(struct video_capture_device
*device
, VideoProcAmpProperty property
,
266 LONG
*min
, LONG
*max
, LONG
*step
, LONG
*default_value
, LONG
*flags
)
268 struct v4l2_queryctrl ctrl
;
270 ctrl
.id
= v4l2_cid_from_qcap_property(property
);
272 if (xioctl(device
->fd
, VIDIOC_QUERYCTRL
, &ctrl
) == -1)
274 WARN("Failed to query control: %s\n", strerror(errno
));
275 return E_PROP_ID_UNSUPPORTED
;
281 *default_value
= ctrl
.default_value
;
282 *flags
= VideoProcAmp_Flags_Manual
;
286 static HRESULT CDECL
v4l_device_get_prop(struct video_capture_device
*device
,
287 VideoProcAmpProperty property
, LONG
*value
, LONG
*flags
)
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
));
300 *flags
= VideoProcAmp_Flags_Manual
;
305 static HRESULT CDECL
v4l_device_set_prop(struct video_capture_device
*device
,
306 VideoProcAmpProperty property
, LONG value
, LONG flags
)
308 struct v4l2_control ctrl
;
310 ctrl
.id
= v4l2_cid_from_qcap_property(property
);
313 if (xioctl(device
->fd
, VIDIOC_S_CTRL
, &ctrl
) == -1)
315 WARN("Failed to set property: %s\n", strerror(errno
));
322 static void reverse_image(struct video_capture_device
*device
, LPBYTE output
, const BYTE
*input
)
324 int inoffset
, outoffset
, pitch
;
326 /* the whole image needs to be reversed,
327 because the dibs are messed up in windows */
328 outoffset
= device
->image_size
;
329 pitch
= device
->image_pitch
;
331 while (outoffset
> 0)
335 for (x
= 0; x
< pitch
; x
++)
336 output
[outoffset
+ x
] = input
[inoffset
+ x
];
341 static BOOL CDECL
v4l_device_read_frame(struct video_capture_device
*device
, BYTE
*data
)
343 while (video_read(device
->fd
, device
->image_data
, device
->image_size
) < 0)
347 ERR("Failed to read frame: %s\n", strerror(errno
));
352 reverse_image(device
, data
, device
->image_data
);
356 static void fill_caps(__u32 pixelformat
, __u32 width
, __u32 height
,
357 __u32 max_fps
, __u32 min_fps
, struct caps
*caps
)
361 memset(caps
, 0, sizeof(*caps
));
362 caps
->video_info
.dwBitRate
= width
* height
* depth
* max_fps
;
363 caps
->video_info
.bmiHeader
.biSize
= sizeof(caps
->video_info
.bmiHeader
);
364 caps
->video_info
.bmiHeader
.biWidth
= width
;
365 caps
->video_info
.bmiHeader
.biHeight
= height
;
366 caps
->video_info
.bmiHeader
.biPlanes
= 1;
367 caps
->video_info
.bmiHeader
.biBitCount
= depth
;
368 caps
->video_info
.bmiHeader
.biCompression
= BI_RGB
;
369 caps
->video_info
.bmiHeader
.biSizeImage
= width
* height
* depth
/ 8;
370 caps
->media_type
.majortype
= MEDIATYPE_Video
;
371 caps
->media_type
.subtype
= MEDIASUBTYPE_RGB24
;
372 caps
->media_type
.bFixedSizeSamples
= TRUE
;
373 caps
->media_type
.bTemporalCompression
= FALSE
;
374 caps
->media_type
.lSampleSize
= width
* height
* depth
/ 8;
375 caps
->media_type
.formattype
= FORMAT_VideoInfo
;
376 caps
->media_type
.pUnk
= NULL
;
377 caps
->media_type
.cbFormat
= sizeof(VIDEOINFOHEADER
);
378 /* We reallocate the caps array, so pbFormat has to be set after all caps
379 * have been enumerated. */
380 caps
->config
.MaxFrameInterval
= 10000000 * max_fps
;
381 caps
->config
.MinFrameInterval
= 10000000 * min_fps
;
382 caps
->config
.MaxOutputSize
.cx
= width
;
383 caps
->config
.MaxOutputSize
.cy
= height
;
384 caps
->config
.MinOutputSize
.cx
= width
;
385 caps
->config
.MinOutputSize
.cy
= height
;
386 caps
->config
.guid
= FORMAT_VideoInfo
;
387 caps
->config
.MinBitsPerSecond
= width
* height
* depth
* min_fps
;
388 caps
->config
.MaxBitsPerSecond
= width
* height
* depth
* max_fps
;
389 caps
->pixelformat
= pixelformat
;
392 static void CDECL
v4l_device_get_caps(struct video_capture_device
*device
, LONG index
,
393 AM_MEDIA_TYPE
*type
, VIDEOINFOHEADER
*format
, VIDEO_STREAM_CONFIG_CAPS
*vscc
)
395 *vscc
= device
->caps
[index
].config
;
396 *type
= device
->caps
[index
].media_type
;
397 *format
= device
->caps
[index
].video_info
;
400 static LONG CDECL
v4l_device_get_caps_count(struct video_capture_device
*device
)
402 return device
->caps_count
;
405 struct video_capture_device
* CDECL
v4l_device_create(USHORT index
)
407 struct v4l2_frmsizeenum frmsize
= {0};
408 struct video_capture_device
*device
;
409 struct v4l2_capability caps
= {{0}};
410 struct v4l2_format format
= {0};
416 have_libv4l2
= video_init();
418 if (!(device
= calloc(1, sizeof(*device
))))
421 sprintf(path
, "/dev/video%i", index
);
422 TRACE("Opening device %s.\n", path
);
424 if ((fd
= video_open(path
, O_RDWR
| O_NONBLOCK
| O_CLOEXEC
)) == -1 && errno
== EINVAL
)
426 fd
= video_open(path
, O_RDWR
| O_NONBLOCK
);
429 WARN("Failed to open video device: %s\n", strerror(errno
));
432 fcntl(fd
, F_SETFD
, FD_CLOEXEC
); /* in case O_CLOEXEC isn't supported */
435 if (xioctl(fd
, VIDIOC_QUERYCAP
, &caps
) == -1)
437 WARN("Failed to query device capabilities: %s\n", strerror(errno
));
441 #ifdef V4L2_CAP_DEVICE_CAPS
442 if (caps
.capabilities
& V4L2_CAP_DEVICE_CAPS
)
443 caps
.capabilities
= caps
.device_caps
;
446 if (!(caps
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
))
448 WARN("Device does not support single-planar video capture.\n");
452 if (!(caps
.capabilities
& V4L2_CAP_READWRITE
))
454 WARN("Device does not support read().\n");
456 #ifdef SONAME_LIBV4L2
457 ERR_(winediag
)("Reading from %s requires libv4l2, but it could not be loaded.\n", path
);
459 ERR_(winediag
)("Reading from %s requires libv4l2, but Wine was compiled without libv4l2 support.\n", path
);
464 format
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
465 if (xioctl(fd
, VIDIOC_G_FMT
, &format
) == -1)
467 ERR("Failed to get device format: %s\n", strerror(errno
));
471 format
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_BGR24
;
472 if (xioctl(fd
, VIDIOC_TRY_FMT
, &format
) == -1
473 || format
.fmt
.pix
.pixelformat
!= V4L2_PIX_FMT_BGR24
)
475 ERR("This device doesn't support V4L2_PIX_FMT_BGR24 format.\n");
479 frmsize
.pixel_format
= V4L2_PIX_FMT_BGR24
;
480 while (xioctl(fd
, VIDIOC_ENUM_FRAMESIZES
, &frmsize
) != -1)
482 struct v4l2_frmivalenum frmival
= {0};
483 __u32 max_fps
= 30, min_fps
= 30;
484 struct caps
*new_caps
;
486 frmival
.pixel_format
= format
.fmt
.pix
.pixelformat
;
487 if (frmsize
.type
== V4L2_FRMSIZE_TYPE_DISCRETE
)
489 frmival
.width
= frmsize
.discrete
.width
;
490 frmival
.height
= frmsize
.discrete
.height
;
492 else if (frmsize
.type
== V4L2_FRMSIZE_TYPE_STEPWISE
)
494 frmival
.width
= frmsize
.stepwise
.max_width
;
495 frmival
.height
= frmsize
.stepwise
.min_height
;
499 FIXME("Unhandled frame size type: %d.\n", frmsize
.type
);
503 if (xioctl(fd
, VIDIOC_ENUM_FRAMEINTERVALS
, &frmival
) != -1)
505 if (frmival
.type
== V4L2_FRMIVAL_TYPE_DISCRETE
)
507 max_fps
= frmival
.discrete
.denominator
/ frmival
.discrete
.numerator
;
510 else if (frmival
.type
== V4L2_FRMIVAL_TYPE_STEPWISE
511 || frmival
.type
== V4L2_FRMIVAL_TYPE_CONTINUOUS
)
513 max_fps
= frmival
.stepwise
.max
.denominator
/ frmival
.stepwise
.max
.numerator
;
514 min_fps
= frmival
.stepwise
.min
.denominator
/ frmival
.stepwise
.min
.numerator
;
518 ERR("Failed to get fps: %s.\n", strerror(errno
));
520 new_caps
= realloc(device
->caps
, (device
->caps_count
+ 1) * sizeof(*device
->caps
));
523 device
->caps
= new_caps
;
524 fill_caps(format
.fmt
.pix
.pixelformat
, frmsize
.discrete
.width
, frmsize
.discrete
.height
,
525 max_fps
, min_fps
, &device
->caps
[device
->caps_count
]);
526 device
->caps_count
++;
531 /* We reallocate the caps array, so we have to delay setting pbFormat. */
532 for (i
= 0; i
< device
->caps_count
; ++i
)
533 device
->caps
[i
].media_type
.pbFormat
= (BYTE
*)&device
->caps
[i
].video_info
;
535 if (FAILED(hr
= set_caps(device
, &device
->caps
[0])))
537 if (hr
== VFW_E_TYPE_NOT_ACCEPTED
&& !have_libv4l2
)
538 ERR_(winediag
)("You may need libv4l2 to use this device.\n");
542 TRACE("Format: %d bpp - %dx%d.\n", device
->current_caps
->video_info
.bmiHeader
.biBitCount
,
543 device
->current_caps
->video_info
.bmiHeader
.biWidth
,
544 device
->current_caps
->video_info
.bmiHeader
.biHeight
);
549 v4l_device_destroy(device
);
553 const struct video_capture_funcs v4l_funcs
=
555 .create
= v4l_device_create
,
556 .destroy
= v4l_device_destroy
,
557 .check_format
= v4l_device_check_format
,
558 .set_format
= v4l_device_set_format
,
559 .get_format
= v4l_device_get_format
,
560 .get_media_type
= v4l_device_get_media_type
,
561 .get_caps
= v4l_device_get_caps
,
562 .get_caps_count
= v4l_device_get_caps_count
,
563 .get_prop_range
= v4l_device_get_prop_range
,
564 .get_prop
= v4l_device_get_prop
,
565 .set_prop
= v4l_device_set_prop
,
566 .read_frame
= v4l_device_read_frame
,
569 NTSTATUS CDECL
__wine_init_unix_lib(HMODULE module
, DWORD reason
, const void *ptr_in
, void *ptr_out
)
571 if (reason
!= DLL_PROCESS_ATTACH
) return STATUS_SUCCESS
;
573 *(const struct video_capture_funcs
**)ptr_out
= &v4l_funcs
;
574 return STATUS_SUCCESS
;
577 #endif /* HAVE_LINUX_VIDEODEV2_H */