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 */
25 #include "wine/port.h"
30 #ifdef HAVE_SYS_IOCTL_H
31 #include <sys/ioctl.h>
33 #ifdef HAVE_SYS_MMAN_H
37 #ifdef HAVE_SYS_TIME_H
40 #ifdef HAVE_ASM_TYPES_H
41 #include <asm/types.h>
43 #ifdef HAVE_LINUX_VIDEODEV2_H
44 #include <linux/videodev2.h>
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)
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
;
93 struct video_capture_device d
;
95 const struct caps
*current_caps
;
99 int image_size
, image_pitch
;
101 struct strmbase_source
*pin
;
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
)
120 r
= video_ioctl (fd
, request
, arg
);
121 } while (-1 == r
&& EINTR
== errno
);
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
);
139 static const struct caps
*find_caps(struct v4l_device
*device
, const AM_MEDIA_TYPE
*mt
)
141 const VIDEOINFOHEADER
*video_info
= (VIDEOINFOHEADER
*)mt
->pbFormat
;
144 if (mt
->cbFormat
< sizeof(VIDEOINFOHEADER
) || !video_info
)
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
)
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
);
168 if (!IsEqualGUID(&mt
->majortype
, &MEDIATYPE_Video
))
171 if (find_caps(device
, mt
))
177 static BOOL
set_caps(struct v4l_device
*device
, const struct caps
*caps
)
179 struct v4l2_format format
= {0};
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
));
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;
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
);
214 if (device
->current_caps
== caps
)
217 if (!set_caps(device
, caps
))
218 return VFW_E_TYPE_NOT_ACCEPTED
;
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
)
249 case VideoProcAmp_Brightness
:
250 return V4L2_CID_BRIGHTNESS
;
251 case VideoProcAmp_Contrast
:
252 return V4L2_CID_CONTRAST
;
253 case VideoProcAmp_Hue
:
255 case VideoProcAmp_Saturation
:
256 return V4L2_CID_SATURATION
;
258 FIXME("Unhandled property %d.\n", property
);
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
;
280 *default_value
= ctrl
.default_value
;
281 *flags
= VideoProcAmp_Flags_Manual
;
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
));
300 *flags
= VideoProcAmp_Flags_Manual
;
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
);
314 if (xioctl(device
->fd
, VIDIOC_S_CTRL
, &ctrl
) == -1)
316 WARN("Failed to set property: %s\n", strerror(errno
));
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
;
332 while (outoffset
> 0)
336 for (x
= 0; x
< pitch
; x
++)
337 output
[outoffset
+ x
] = input
[inoffset
+ x
];
342 static DWORD WINAPI
ReadThread(void *arg
)
344 struct v4l_device
*device
= arg
;
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");
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
);
368 LeaveCriticalSection(&device
->state_cs
);
370 hr
= BaseOutputPinImpl_GetDeliveryBuffer(device
->pin
, &pSample
, NULL
, NULL
, 0);
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)
386 ERR("Failed to read frame: %s\n", strerror(errno
));
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
);
403 heap_free(image_data
);
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
;
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
);
420 ERR("Failed to set allocator properties (buffer size %u), hr %#x.\n", req_props
.cbBuffer
, 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
);
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
)
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
)
511 *type
= CreateMediaType(&device
->caps
[index
].media_type
);
513 return E_OUTOFMEMORY
;
516 memcpy(vscc
, &device
->caps
[index
].config
, sizeof(VIDEO_STREAM_CONFIG_CAPS
));
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
;
555 have_libv4l2
= video_init();
557 if (!(device
= heap_alloc_zero(sizeof(*device
))))
560 sprintf(path
, "/dev/video%i", card
);
561 TRACE("Opening device %s.\n", path
);
563 if ((fd
= video_open(path
, O_RDWR
| O_NONBLOCK
| O_CLOEXEC
)) == -1 && errno
== EINVAL
)
565 fd
= video_open(path
, O_RDWR
| O_NONBLOCK
);
568 WARN("Failed to open video device: %s\n", strerror(errno
));
571 fcntl(fd
, F_SETFD
, FD_CLOEXEC
); /* in case O_CLOEXEC isn't supported */
574 if (xioctl(fd
, VIDIOC_QUERYCAP
, &caps
) == -1)
576 WARN("Failed to query device capabilities: %s\n", strerror(errno
));
580 #ifdef V4L2_CAP_DEVICE_CAPS
581 if (caps
.capabilities
& V4L2_CAP_DEVICE_CAPS
)
582 caps
.capabilities
= caps
.device_caps
;
585 if (!(caps
.capabilities
& V4L2_CAP_VIDEO_CAPTURE
))
587 WARN("Device does not support single-planar video capture.\n");
591 if (!(caps
.capabilities
& V4L2_CAP_READWRITE
))
593 WARN("Device does not support read().\n");
595 #ifdef SONAME_LIBV4L2
596 ERR_(winediag
)("Reading from %s requires libv4l2, but it could not be loaded.\n", path
);
598 ERR_(winediag
)("Reading from %s requires libv4l2, but Wine was compiled without libv4l2 support.\n", path
);
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
));
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");
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
;
638 FIXME("Unhandled frame size type: %d.\n", frmsize
.type
);
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
;
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
;
657 ERR("Failed to get fps: %s.\n", strerror(errno
));
659 new_caps
= heap_realloc(device
->caps
, (device
->caps_count
+ 1) * sizeof(*device
->caps
));
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
++;
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
));
678 ERR_(winediag
)("You may need libv4l2 to use this device.\n");
682 device
->d
.ops
= &v4l_device_ops
;
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
);
696 v4l_device_destroy(&device
->d
);
702 struct video_capture_device
*v4l_device_create(struct strmbase_source
*pin
, USHORT card
)
704 ERR("v4l2 was not present at compilation time.\n");
708 #endif /* defined(VIDIOCMCAPTURE) */