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 */
35 #include <sys/ioctl.h>
39 #ifdef HAVE_ASM_TYPES_H
40 #include <asm/types.h>
42 #ifdef HAVE_LINUX_VIDEODEV2_H
43 #include <linux/videodev2.h>
48 #define WIN32_NO_STATUS
50 #include "qcap_private.h"
53 #ifdef HAVE_LINUX_VIDEODEV2_H
55 WINE_DEFAULT_DEBUG_CHANNEL(quartz
);
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)
66 static void *video_lib
;
70 if (!(video_lib
= dlopen(SONAME_LIBV4L2
, RTLD_NOW
)))
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");
86 AM_MEDIA_TYPE media_type
;
87 VIDEOINFOHEADER video_info
;
88 VIDEO_STREAM_CONFIG_CAPS config
;
91 struct video_capture_device
93 const struct caps
*current_caps
;
97 int image_size
, image_pitch
;
103 static int xioctl(int fd
, int request
, void * arg
)
108 r
= video_ioctl (fd
, request
, arg
);
109 } while (-1 == r
&& EINTR
== errno
);
114 static struct video_capture_device
*get_device( video_capture_device_t dev
)
116 return (struct video_capture_device
*)(ULONG_PTR
)dev
;
119 static void device_destroy(struct video_capture_device
*device
)
121 if (device
->fd
!= -1)
122 video_close(device
->fd
);
123 if (device
->caps_count
)
125 free(device
->image_data
);
129 static const struct caps
*find_caps(struct video_capture_device
*device
, const AM_MEDIA_TYPE
*mt
)
131 const VIDEOINFOHEADER
*video_info
= (VIDEOINFOHEADER
*)mt
->pbFormat
;
134 if (mt
->cbFormat
< sizeof(VIDEOINFOHEADER
) || !video_info
)
137 for (index
= 0; index
< device
->caps_count
; index
++)
139 struct caps
*caps
= &device
->caps
[index
];
141 if (IsEqualGUID(&mt
->formattype
, &caps
->media_type
.formattype
)
142 && video_info
->bmiHeader
.biWidth
== caps
->video_info
.bmiHeader
.biWidth
143 && video_info
->bmiHeader
.biHeight
== caps
->video_info
.bmiHeader
.biHeight
)
149 static NTSTATUS
v4l_device_check_format( void *args
)
151 const struct check_format_params
*params
= args
;
152 struct video_capture_device
*device
= get_device(params
->device
);
154 TRACE("device %p, mt %p.\n", device
, params
->mt
);
156 if (!IsEqualGUID(¶ms
->mt
->majortype
, &MEDIATYPE_Video
))
159 if (find_caps(device
, params
->mt
))
165 static HRESULT
set_caps(struct video_capture_device
*device
, const struct caps
*caps
)
167 struct v4l2_format format
= {0};
168 LONG width
, height
, image_size
;
171 width
= caps
->video_info
.bmiHeader
.biWidth
;
172 height
= caps
->video_info
.bmiHeader
.biHeight
;
173 image_size
= width
* height
* caps
->video_info
.bmiHeader
.biBitCount
/ 8;
175 if (!(image_data
= malloc(image_size
)))
177 ERR("Failed to allocate memory.\n");
178 return E_OUTOFMEMORY
;
181 format
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
182 format
.fmt
.pix
.pixelformat
= caps
->pixelformat
;
183 format
.fmt
.pix
.width
= width
;
184 format
.fmt
.pix
.height
= height
;
185 if (xioctl(device
->fd
, VIDIOC_S_FMT
, &format
) == -1
186 || format
.fmt
.pix
.pixelformat
!= caps
->pixelformat
187 || format
.fmt
.pix
.width
!= width
188 || format
.fmt
.pix
.height
!= height
)
190 ERR("Failed to set pixel format: %s.\n", strerror(errno
));
192 return VFW_E_TYPE_NOT_ACCEPTED
;
195 device
->current_caps
= caps
;
196 device
->image_size
= image_size
;
197 device
->image_pitch
= width
* caps
->video_info
.bmiHeader
.biBitCount
/ 8;
198 free(device
->image_data
);
199 device
->image_data
= image_data
;
203 static NTSTATUS
v4l_device_set_format( void *args
)
205 const struct set_format_params
*params
= args
;
206 struct video_capture_device
*device
= get_device(params
->device
);
207 const struct caps
*caps
;
209 caps
= find_caps(device
, params
->mt
);
213 if (device
->current_caps
== caps
)
216 return set_caps(device
, caps
);
219 static NTSTATUS
v4l_device_get_format( void *args
)
221 const struct get_format_params
*params
= args
;
222 struct video_capture_device
*device
= get_device(params
->device
);
224 *params
->mt
= device
->current_caps
->media_type
;
225 *params
->format
= device
->current_caps
->video_info
;
229 static NTSTATUS
v4l_device_get_media_type( void *args
)
231 const struct get_media_type_params
*params
= args
;
232 struct video_capture_device
*device
= get_device(params
->device
);
233 unsigned int caps_count
= (device
->current_caps
) ? 1 : device
->caps_count
;
235 if (params
->index
>= caps_count
)
236 return VFW_S_NO_MORE_ITEMS
;
238 if (device
->current_caps
)
240 *params
->mt
= device
->current_caps
->media_type
;
241 *params
->format
= device
->current_caps
->video_info
;
245 *params
->mt
= device
->caps
[params
->index
].media_type
;
246 *params
->format
= device
->caps
[params
->index
].video_info
;
251 static __u32
v4l2_cid_from_qcap_property(VideoProcAmpProperty property
)
255 case VideoProcAmp_Brightness
:
256 return V4L2_CID_BRIGHTNESS
;
257 case VideoProcAmp_Contrast
:
258 return V4L2_CID_CONTRAST
;
259 case VideoProcAmp_Hue
:
261 case VideoProcAmp_Saturation
:
262 return V4L2_CID_SATURATION
;
264 FIXME("Unhandled property %d.\n", property
);
269 static NTSTATUS
v4l_device_get_prop_range( void *args
)
271 const struct get_prop_range_params
*params
= args
;
272 struct video_capture_device
*device
= get_device(params
->device
);
273 struct v4l2_queryctrl ctrl
;
275 ctrl
.id
= v4l2_cid_from_qcap_property(params
->property
);
277 if (xioctl(device
->fd
, VIDIOC_QUERYCTRL
, &ctrl
) == -1)
279 WARN("Failed to query control: %s\n", strerror(errno
));
280 return E_PROP_ID_UNSUPPORTED
;
283 *params
->min
= ctrl
.minimum
;
284 *params
->max
= ctrl
.maximum
;
285 *params
->step
= ctrl
.step
;
286 *params
->default_value
= ctrl
.default_value
;
287 *params
->flags
= VideoProcAmp_Flags_Manual
;
291 static NTSTATUS
v4l_device_get_prop( void *args
)
293 const struct get_prop_params
*params
= args
;
294 struct video_capture_device
*device
= get_device(params
->device
);
295 struct v4l2_control ctrl
;
297 ctrl
.id
= v4l2_cid_from_qcap_property(params
->property
);
299 if (xioctl(device
->fd
, VIDIOC_G_CTRL
, &ctrl
) == -1)
301 WARN("Failed to get property: %s\n", strerror(errno
));
305 *params
->value
= ctrl
.value
;
306 *params
->flags
= VideoProcAmp_Flags_Manual
;
311 static NTSTATUS
v4l_device_set_prop( void *args
)
313 const struct set_prop_params
*params
= args
;
314 struct video_capture_device
*device
= get_device(params
->device
);
315 struct v4l2_control ctrl
;
317 ctrl
.id
= v4l2_cid_from_qcap_property(params
->property
);
318 ctrl
.value
= params
->value
;
320 if (xioctl(device
->fd
, VIDIOC_S_CTRL
, &ctrl
) == -1)
322 WARN("Failed to set property: %s\n", strerror(errno
));
329 static void reverse_image(struct video_capture_device
*device
, LPBYTE output
, const BYTE
*input
)
331 int inoffset
, outoffset
, pitch
;
333 /* the whole image needs to be reversed,
334 because the dibs are messed up in windows */
335 outoffset
= device
->image_size
;
336 pitch
= device
->image_pitch
;
338 while (outoffset
> 0)
342 for (x
= 0; x
< pitch
; x
++)
343 output
[outoffset
+ x
] = input
[inoffset
+ x
];
348 static NTSTATUS
v4l_device_read_frame( void *args
)
350 const struct read_frame_params
*params
= args
;
351 struct video_capture_device
*device
= get_device(params
->device
);
353 while (video_read(device
->fd
, device
->image_data
, device
->image_size
) < 0)
357 ERR("Failed to read frame: %s\n", strerror(errno
));
362 reverse_image(device
, params
->data
, device
->image_data
);
366 static void fill_caps(__u32 pixelformat
, __u32 width
, __u32 height
,
367 __u32 max_fps
, __u32 min_fps
, struct caps
*caps
)
371 memset(caps
, 0, sizeof(*caps
));
372 caps
->video_info
.dwBitRate
= width
* height
* depth
* max_fps
;
373 caps
->video_info
.bmiHeader
.biSize
= sizeof(caps
->video_info
.bmiHeader
);
374 caps
->video_info
.bmiHeader
.biWidth
= width
;
375 caps
->video_info
.bmiHeader
.biHeight
= height
;
376 caps
->video_info
.bmiHeader
.biPlanes
= 1;
377 caps
->video_info
.bmiHeader
.biBitCount
= depth
;
378 caps
->video_info
.bmiHeader
.biCompression
= BI_RGB
;
379 caps
->video_info
.bmiHeader
.biSizeImage
= width
* height
* depth
/ 8;
380 caps
->media_type
.majortype
= MEDIATYPE_Video
;
381 caps
->media_type
.subtype
= MEDIASUBTYPE_RGB24
;
382 caps
->media_type
.bFixedSizeSamples
= TRUE
;
383 caps
->media_type
.bTemporalCompression
= FALSE
;
384 caps
->media_type
.lSampleSize
= width
* height
* depth
/ 8;
385 caps
->media_type
.formattype
= FORMAT_VideoInfo
;
386 caps
->media_type
.pUnk
= NULL
;
387 caps
->media_type
.cbFormat
= sizeof(VIDEOINFOHEADER
);
388 /* We reallocate the caps array, so pbFormat has to be set after all caps
389 * have been enumerated. */
390 caps
->config
.MaxFrameInterval
= 10000000 / max_fps
;
391 caps
->config
.MinFrameInterval
= 10000000 / min_fps
;
392 caps
->config
.MaxOutputSize
.cx
= width
;
393 caps
->config
.MaxOutputSize
.cy
= height
;
394 caps
->config
.MinOutputSize
.cx
= width
;
395 caps
->config
.MinOutputSize
.cy
= height
;
396 caps
->config
.guid
= FORMAT_VideoInfo
;
397 caps
->config
.MinBitsPerSecond
= width
* height
* depth
* min_fps
;
398 caps
->config
.MaxBitsPerSecond
= width
* height
* depth
* max_fps
;
399 caps
->pixelformat
= pixelformat
;
402 static NTSTATUS
v4l_device_get_caps( void *args
)
404 const struct get_caps_params
*params
= args
;
405 struct video_capture_device
*device
= get_device(params
->device
);
407 *params
->caps
= device
->caps
[params
->index
].config
;
408 *params
->mt
= device
->caps
[params
->index
].media_type
;
409 *params
->format
= device
->caps
[params
->index
].video_info
;
413 static NTSTATUS
v4l_device_get_caps_count( void *args
)
415 const struct get_caps_count_params
*params
= args
;
416 struct video_capture_device
*device
= get_device(params
->device
);
418 *params
->count
= device
->caps_count
;
422 static NTSTATUS
v4l_device_create( void *args
)
424 const struct create_params
*params
= args
;
425 struct v4l2_frmsizeenum frmsize
= {0};
426 struct video_capture_device
*device
;
427 struct v4l2_capability caps
= {{0}};
428 struct v4l2_format format
= {0};
434 have_libv4l2
= video_init();
436 if (!(device
= calloc(1, sizeof(*device
))))
437 return E_OUTOFMEMORY
;
439 sprintf(path
, "/dev/video%i", params
->index
);
440 TRACE("Opening device %s.\n", path
);
442 if ((fd
= video_open(path
, O_RDWR
| O_NONBLOCK
| O_CLOEXEC
)) == -1 && errno
== EINVAL
)
444 fd
= video_open(path
, O_RDWR
| O_NONBLOCK
);
447 WARN("Failed to open video device: %s\n", strerror(errno
));
450 fcntl(fd
, F_SETFD
, FD_CLOEXEC
); /* in case O_CLOEXEC isn't supported */
453 if (xioctl(fd
, VIDIOC_QUERYCAP
, &caps
) == -1)
455 WARN("Failed to query device capabilities: %s\n", strerror(errno
));
459 #ifdef V4L2_CAP_DEVICE_CAPS
460 if (caps
.capabilities
& V4L2_CAP_DEVICE_CAPS
)
461 caps
.capabilities
= caps
.device_caps
;
464 if (!(caps
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
))
466 WARN("Device does not support single-planar video capture.\n");
470 if (!(caps
.capabilities
& V4L2_CAP_READWRITE
))
472 WARN("Device does not support read().\n");
474 #ifdef SONAME_LIBV4L2
475 ERR_(winediag
)("Reading from %s requires libv4l2, but it could not be loaded.\n", path
);
477 ERR_(winediag
)("Reading from %s requires libv4l2, but Wine was compiled without libv4l2 support.\n", path
);
482 format
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
483 if (xioctl(fd
, VIDIOC_G_FMT
, &format
) == -1)
485 ERR("Failed to get device format: %s\n", strerror(errno
));
489 format
.fmt
.pix
.pixelformat
= V4L2_PIX_FMT_BGR24
;
490 if (xioctl(fd
, VIDIOC_TRY_FMT
, &format
) == -1
491 || format
.fmt
.pix
.pixelformat
!= V4L2_PIX_FMT_BGR24
)
493 ERR("This device doesn't support V4L2_PIX_FMT_BGR24 format.\n");
497 frmsize
.pixel_format
= V4L2_PIX_FMT_BGR24
;
498 while (xioctl(fd
, VIDIOC_ENUM_FRAMESIZES
, &frmsize
) != -1)
500 struct v4l2_frmivalenum frmival
= {0};
501 __u32 max_fps
= 30, min_fps
= 30;
502 struct caps
*new_caps
;
504 frmival
.pixel_format
= format
.fmt
.pix
.pixelformat
;
505 if (frmsize
.type
== V4L2_FRMSIZE_TYPE_DISCRETE
)
507 frmival
.width
= frmsize
.discrete
.width
;
508 frmival
.height
= frmsize
.discrete
.height
;
510 else if (frmsize
.type
== V4L2_FRMSIZE_TYPE_STEPWISE
)
512 frmival
.width
= frmsize
.stepwise
.max_width
;
513 frmival
.height
= frmsize
.stepwise
.min_height
;
517 FIXME("Unhandled frame size type: %d.\n", frmsize
.type
);
521 if (xioctl(fd
, VIDIOC_ENUM_FRAMEINTERVALS
, &frmival
) != -1)
523 if (frmival
.type
== V4L2_FRMIVAL_TYPE_DISCRETE
)
525 max_fps
= frmival
.discrete
.denominator
/ frmival
.discrete
.numerator
;
528 else if (frmival
.type
== V4L2_FRMIVAL_TYPE_STEPWISE
529 || frmival
.type
== V4L2_FRMIVAL_TYPE_CONTINUOUS
)
531 min_fps
= frmival
.stepwise
.max
.denominator
/ frmival
.stepwise
.max
.numerator
;
532 max_fps
= frmival
.stepwise
.min
.denominator
/ frmival
.stepwise
.min
.numerator
;
536 ERR("Failed to get fps: %s.\n", strerror(errno
));
538 new_caps
= realloc(device
->caps
, (device
->caps_count
+ 1) * sizeof(*device
->caps
));
541 device
->caps
= new_caps
;
542 fill_caps(format
.fmt
.pix
.pixelformat
, frmsize
.discrete
.width
, frmsize
.discrete
.height
,
543 max_fps
, min_fps
, &device
->caps
[device
->caps_count
]);
544 device
->caps_count
++;
549 /* We reallocate the caps array, so we have to delay setting pbFormat. */
550 for (i
= 0; i
< device
->caps_count
; ++i
)
551 device
->caps
[i
].media_type
.pbFormat
= (BYTE
*)&device
->caps
[i
].video_info
;
553 if (FAILED(hr
= set_caps(device
, &device
->caps
[0])))
555 if (hr
== VFW_E_TYPE_NOT_ACCEPTED
&& !have_libv4l2
)
556 ERR_(winediag
)("You may need libv4l2 to use this device.\n");
560 TRACE("Format: %d bpp - %dx%d.\n", device
->current_caps
->video_info
.bmiHeader
.biBitCount
,
561 (int)device
->current_caps
->video_info
.bmiHeader
.biWidth
,
562 (int)device
->current_caps
->video_info
.bmiHeader
.biHeight
);
564 *params
->device
= (ULONG_PTR
)device
;
568 device_destroy(device
);
572 static NTSTATUS
v4l_device_destroy( void *args
)
574 const struct destroy_params
*params
= args
;
576 device_destroy( get_device(params
->device
) );
580 const unixlib_entry_t __wine_unix_call_funcs
[] =
584 v4l_device_check_format
,
585 v4l_device_set_format
,
586 v4l_device_get_format
,
587 v4l_device_get_media_type
,
589 v4l_device_get_caps_count
,
590 v4l_device_get_prop_range
,
593 v4l_device_read_frame
,
600 struct am_media_type32
604 BOOL bFixedSizeSamples
;
605 BOOL bTemporalCompression
;
613 static AM_MEDIA_TYPE
*get_media_type( const struct am_media_type32
*mt32
, AM_MEDIA_TYPE
*mt
)
615 mt
->majortype
= mt32
->majortype
;
616 mt
->subtype
= mt32
->subtype
;
617 mt
->bFixedSizeSamples
= mt32
->bFixedSizeSamples
;
618 mt
->bTemporalCompression
= mt32
->bTemporalCompression
;
619 mt
->lSampleSize
= mt32
->lSampleSize
;
620 mt
->formattype
= mt32
->formattype
;
622 mt
->cbFormat
= mt32
->cbFormat
;
623 mt
->pbFormat
= ULongToPtr(mt32
->pbFormat
);
627 static void put_media_type( const AM_MEDIA_TYPE
*mt
, struct am_media_type32
*mt32
)
629 mt32
->majortype
= mt
->majortype
;
630 mt32
->subtype
= mt
->subtype
;
631 mt32
->bFixedSizeSamples
= mt
->bFixedSizeSamples
;
632 mt32
->bTemporalCompression
= mt
->bTemporalCompression
;
633 mt32
->lSampleSize
= mt
->lSampleSize
;
634 mt32
->formattype
= mt
->formattype
;
637 static NTSTATUS
wow64_v4l_device_create( void *args
)
643 } const *params32
= args
;
645 struct create_params params
=
648 ULongToPtr(params32
->device
)
651 return v4l_device_create( ¶ms
);
654 static NTSTATUS
wow64_v4l_device_check_format( void *args
)
658 video_capture_device_t device
;
660 } const *params32
= args
;
663 struct check_format_params params
=
666 get_media_type( ULongToPtr(params32
->mt
), &mt
)
669 return v4l_device_check_format( ¶ms
);
672 static NTSTATUS
wow64_v4l_device_set_format( void *args
)
676 video_capture_device_t device
;
678 } const *params32
= args
;
681 struct set_format_params params
=
684 get_media_type( ULongToPtr(params32
->mt
), &mt
)
687 return v4l_device_set_format( ¶ms
);
690 static NTSTATUS
wow64_v4l_device_get_format( void *args
)
694 video_capture_device_t device
;
697 } const *params32
= args
;
700 struct get_format_params params
=
704 ULongToPtr(params32
->format
)
707 NTSTATUS status
= v4l_device_get_format( ¶ms
);
708 if (!status
) put_media_type( &mt
, ULongToPtr(params32
->mt
) );
712 static NTSTATUS
wow64_v4l_device_get_media_type( void *args
)
716 video_capture_device_t device
;
720 } const *params32
= args
;
723 struct get_media_type_params params
=
728 ULongToPtr(params32
->format
)
731 NTSTATUS status
= v4l_device_get_media_type( ¶ms
);
732 if (!status
) put_media_type( &mt
, ULongToPtr(params32
->mt
) );
736 static NTSTATUS
wow64_v4l_device_get_caps( void *args
)
740 video_capture_device_t device
;
745 } const *params32
= args
;
748 struct get_caps_params params
=
753 ULongToPtr(params32
->format
),
754 ULongToPtr(params32
->caps
)
757 NTSTATUS status
= v4l_device_get_caps( ¶ms
);
758 if (!status
) put_media_type( &mt
, ULongToPtr(params32
->mt
) );
762 static NTSTATUS
wow64_v4l_device_get_caps_count( void *args
)
766 video_capture_device_t device
;
768 } const *params32
= args
;
770 struct get_caps_count_params params
=
773 ULongToPtr(params32
->count
)
776 return v4l_device_get_caps_count( ¶ms
);
779 static NTSTATUS
wow64_v4l_device_get_prop_range( void *args
)
783 video_capture_device_t device
;
784 VideoProcAmpProperty property
;
790 } const *params32
= args
;
792 struct get_prop_range_params params
=
796 ULongToPtr(params32
->min
),
797 ULongToPtr(params32
->max
),
798 ULongToPtr(params32
->step
),
799 ULongToPtr(params32
->default_value
),
800 ULongToPtr(params32
->flags
)
803 return v4l_device_get_prop_range( ¶ms
);
806 static NTSTATUS
wow64_v4l_device_get_prop( void *args
)
810 video_capture_device_t device
;
811 VideoProcAmpProperty property
;
814 } const *params32
= args
;
816 struct get_prop_params params
=
820 ULongToPtr(params32
->value
),
821 ULongToPtr(params32
->flags
)
824 return v4l_device_get_prop( ¶ms
);
827 static NTSTATUS
wow64_v4l_device_read_frame( void *args
)
831 video_capture_device_t device
;
833 } const *params32
= args
;
835 struct read_frame_params params
=
838 ULongToPtr(params32
->data
)
841 return v4l_device_read_frame( ¶ms
);
844 const unixlib_entry_t __wine_unix_call_wow64_funcs
[] =
846 wow64_v4l_device_create
,
848 wow64_v4l_device_check_format
,
849 wow64_v4l_device_set_format
,
850 wow64_v4l_device_get_format
,
851 wow64_v4l_device_get_media_type
,
852 wow64_v4l_device_get_caps
,
853 wow64_v4l_device_get_caps_count
,
854 wow64_v4l_device_get_prop_range
,
855 wow64_v4l_device_get_prop
,
857 wow64_v4l_device_read_frame
,
862 #endif /* HAVE_LINUX_VIDEODEV2_H */