Signed-off-by: Jactry Zeng jzeng@codeweavers.com --- dlls/qcap/v4l.c | 158 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 3 deletions(-)
diff --git a/dlls/qcap/v4l.c b/dlls/qcap/v4l.c index 6fe92d2d0a..26eccf6ee3 100644 --- a/dlls/qcap/v4l.c +++ b/dlls/qcap/v4l.c @@ -93,9 +93,20 @@ static BOOL video_init(void) #endif }
+struct capabilitie +{ + __u32 pixelformat; + UINT width, height, depth; + AM_MEDIA_TYPE media_type; + VIDEO_STREAM_CONFIG_CAPS config; +}; + struct _Capture { UINT width, height, bitDepth, fps, outputwidth, outputheight; + struct capabilitie *current_cap; + struct capabilitie **caps; + LONG caps_count; BOOL swresize;
struct strmbase_source *pin; @@ -116,13 +127,22 @@ static int xioctl(int fd, int request, void * arg) return r; }
+static void free_capabilitie(struct capabilitie *cap) +{ + heap_free(cap->media_type.pbFormat); + heap_free(cap); +} + HRESULT qcap_driver_destroy(Capture *capBox) { TRACE("%p\n", capBox);
- if( capBox->fd != -1 ) + if (capBox->fd != -1) video_close(capBox->fd); - CoTaskMemFree(capBox); + while (--capBox->caps_count >= 0) + free_capabilitie(capBox->caps[capBox->caps_count]); + heap_free(capBox); + return S_OK; }
@@ -495,9 +515,83 @@ void qcap_driver_cleanup_stream(Capture *device) ERR("Failed to decommit allocator, hr %#x.\n", hr); }
+ +static HRESULT create_capabilitie(__u32 pixelformat, __u32 width, __u32 height, + __u32 max_fps, __u32 min_fps, struct capabilitie **out) +{ + VIDEOINFOHEADER *video_info; + struct capabilitie *cap; + DWORD compression; + BOOL compressed; + GUID subtype; + LONG depth; + + *out = NULL; + switch (pixelformat) + { + case V4L2_PIX_FMT_BGR24: + subtype = MEDIASUBTYPE_RGB24; + compression = BI_RGB; + compressed = FALSE; + depth = 24; + break; + default: + FIXME("Unknown format: %#x (%dx%d).\n", pixelformat, width, height); + return E_FAIL; + } + + cap = heap_alloc_zero(sizeof(*cap)); + if (!cap) + return E_OUTOFMEMORY; + + video_info = heap_alloc_zero(sizeof(*video_info)); + if (!video_info) + { + heap_free(cap); + return E_OUTOFMEMORY; + } + + video_info->rcSource.right = width; + video_info->rcSource.bottom = height; + video_info->rcTarget.right = width; + video_info->rcTarget.bottom = height; + video_info->dwBitRate = max_fps * width * height * depth; + video_info->bmiHeader.biSize = sizeof(video_info->bmiHeader); + video_info->bmiHeader.biWidth = width; + video_info->bmiHeader.biHeight = height; + video_info->bmiHeader.biPlanes = 1; + video_info->bmiHeader.biBitCount = depth; + video_info->bmiHeader.biCompression = compression; + video_info->bmiHeader.biSizeImage = width * height * depth / 8; + cap->media_type.majortype = MEDIATYPE_Video; + cap->media_type.subtype = subtype; + cap->media_type.bFixedSizeSamples = TRUE; + cap->media_type.bTemporalCompression = compressed; + cap->media_type.lSampleSize = width * height * depth / 8; + cap->media_type.formattype = FORMAT_VideoInfo; + cap->media_type.pUnk = NULL; + cap->media_type.cbFormat = sizeof(VIDEOINFOHEADER); + cap->media_type.pbFormat = (void *)video_info; + cap->config.MaxFrameInterval = 10000000 * max_fps; + cap->config.MinFrameInterval = 10000000 * min_fps;; + cap->config.MaxOutputSize.cx = width; + cap->config.MaxOutputSize.cy = height; + cap->config.MinOutputSize.cx = width; + cap->config.MinOutputSize.cy = height; + cap->config.guid = FORMAT_VideoInfo; + cap->pixelformat = pixelformat; + cap->width = width; + cap->height = height; + cap->depth = depth; + + *out = cap; + return S_OK; +} + Capture *qcap_driver_init(struct strmbase_source *pin, USHORT card) { struct v4l2_capability caps = {{0}}; + struct v4l2_fmtdesc fmtdesc = {0}; struct v4l2_format format = {0}; Capture *device = NULL; BOOL have_libv4l2; @@ -506,7 +600,7 @@ Capture *qcap_driver_init(struct strmbase_source *pin, USHORT card)
have_libv4l2 = video_init();
- if (!(device = CoTaskMemAlloc(sizeof(*device)))) + if (!(device = heap_alloc_zero(sizeof(*device)))) return NULL;
sprintf(path, "/dev/video%i", card); @@ -559,6 +653,64 @@ Capture *qcap_driver_init(struct strmbase_source *pin, USHORT card) goto error; }
+ /* Feeding a RGB format to VIDIOC_TRY_FMT is helpful and necessary for + making VIDIOC_ENUM_FMT return more supproted RGB formats. */ + format.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + if (xioctl(fd, VIDIOC_TRY_FMT, &format) == -1 + || format.fmt.pix.pixelformat != V4L2_PIX_FMT_BGR24) + WARN("This device doesn't support V4L2_PIX_FMT_BGR24.\n"); + + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + while(xioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) + { + struct v4l2_frmsizeenum frmsize = {0}; + + frmsize.pixel_format = fmtdesc.pixelformat; + while(xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) != -1) + { + struct v4l2_frmivalenum frmival = {0}; + struct capabilitie *cap, **new_caps; + __u32 max_fps = 30, min_fps = 30; + + frmival.pixel_format = fmtdesc.pixelformat; + frmival.width = frmsize.discrete.width; + frmival.height = frmsize.discrete.height; + if (xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1) + { + if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) + { + max_fps = frmival.discrete.denominator / frmival.discrete.numerator; + min_fps = max_fps; + } + else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) + { + max_fps = frmival.stepwise.max.denominator / frmival.stepwise.max.numerator; + min_fps = frmival.stepwise.min.denominator / frmival.stepwise.min.numerator; + } + } + else + ERR("Failed to get fps: %s.\n", strerror(errno)); + + if (SUCCEEDED(create_capabilitie(fmtdesc.pixelformat, frmsize.discrete.width, frmsize.discrete.height, + max_fps, min_fps, &cap))) + { + new_caps = heap_realloc(device->caps, (device->caps_count + 1) * sizeof(*device->caps)); + if (!new_caps) + { + free_capabilitie(cap); + goto error; + } + device->caps = new_caps; + device->caps[device->caps_count] = cap; + device->caps_count++; + } + frmsize.index++; + } + fmtdesc.index++; + } + + device->current_cap = device->caps[0]; + format.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; if (xioctl(fd, VIDIOC_S_FMT, &format) == -1 || format.fmt.pix.pixelformat != V4L2_PIX_FMT_BGR24)