[PATCH 0/3] MR9689: qcap: Implement GetFrameRateList().
From: Elizabeth Figura <zfigura(a)codeweavers.com> --- dlls/qcap/v4l.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/dlls/qcap/v4l.c b/dlls/qcap/v4l.c index 5efb4b7e505..1a9e2428b41 100644 --- a/dlls/qcap/v4l.c +++ b/dlls/qcap/v4l.c @@ -368,13 +368,15 @@ static NTSTATUS v4l_device_read_frame( void *args ) return TRUE; } +#define TICKSPERSEC ((LONGLONG)10000000) + static void fill_caps(__u32 pixelformat, __u32 width, __u32 height, - __u32 max_fps, __u32 min_fps, struct caps *caps) + LONGLONG max_frame_interval, LONGLONG min_frame_interval, struct caps *caps) { LONG depth = 24; memset(caps, 0, sizeof(*caps)); - caps->video_info.dwBitRate = width * height * depth * max_fps; + caps->video_info.dwBitRate = width * height * depth * TICKSPERSEC / min_frame_interval; caps->video_info.bmiHeader.biSize = sizeof(caps->video_info.bmiHeader); caps->video_info.bmiHeader.biWidth = width; caps->video_info.bmiHeader.biHeight = height; @@ -392,15 +394,16 @@ static void fill_caps(__u32 pixelformat, __u32 width, __u32 height, caps->media_type.cbFormat = sizeof(VIDEOINFOHEADER); /* We reallocate the caps array, so pbFormat has to be set after all caps * have been enumerated. */ - caps->config.MaxFrameInterval = 10000000 / max_fps; - caps->config.MinFrameInterval = 10000000 / min_fps; + caps->config.MaxFrameInterval = max_frame_interval; + caps->config.MinFrameInterval = min_frame_interval; caps->config.MaxOutputSize.cx = width; caps->config.MaxOutputSize.cy = height; caps->config.MinOutputSize.cx = width; caps->config.MinOutputSize.cy = height; caps->config.guid = FORMAT_VideoInfo; - caps->config.MinBitsPerSecond = width * height * depth * min_fps; - caps->config.MaxBitsPerSecond = width * height * depth * max_fps; + /* Dividing swaps max and min. */ + caps->config.MinBitsPerSecond = width * height * depth * TICKSPERSEC / max_frame_interval; + caps->config.MaxBitsPerSecond = width * height * depth * TICKSPERSEC / min_frame_interval; caps->pixelformat = pixelformat; } @@ -502,8 +505,8 @@ static NTSTATUS v4l_device_create( void *args ) frmsize.pixel_format = V4L2_PIX_FMT_BGR24; while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) != -1) { + LONGLONG max_frame_interval = (TICKSPERSEC / 30), min_frame_interval = (TICKSPERSEC / 30); struct v4l2_frmivalenum frmival = {0}; - __u32 max_fps = 30, min_fps = 30; struct caps *new_caps; frmival.pixel_format = format.fmt.pix.pixelformat; @@ -527,14 +530,14 @@ static NTSTATUS v4l_device_create( void *args ) { if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { - max_fps = frmival.discrete.denominator / frmival.discrete.numerator; - min_fps = max_fps; + max_frame_interval = TICKSPERSEC * frmival.discrete.numerator / frmival.discrete.denominator; + min_frame_interval = max_frame_interval; } else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE || frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) { - min_fps = frmival.stepwise.max.denominator / frmival.stepwise.max.numerator; - max_fps = frmival.stepwise.min.denominator / frmival.stepwise.min.numerator; + max_frame_interval = TICKSPERSEC * frmival.stepwise.max.numerator / frmival.stepwise.max.denominator; + min_frame_interval = TICKSPERSEC * frmival.stepwise.min.numerator / frmival.stepwise.min.denominator; } } else @@ -545,7 +548,7 @@ static NTSTATUS v4l_device_create( void *args ) goto error; device->caps = new_caps; fill_caps(format.fmt.pix.pixelformat, frmsize.discrete.width, frmsize.discrete.height, - max_fps, min_fps, &device->caps[device->caps_count]); + max_frame_interval, min_frame_interval, &device->caps[device->caps_count]); device->caps_count++; frmsize.index++; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9689
From: Elizabeth Figura <zfigura(a)codeweavers.com> --- dlls/qcap/v4l.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/dlls/qcap/v4l.c b/dlls/qcap/v4l.c index 1a9e2428b41..482a0f1d803 100644 --- a/dlls/qcap/v4l.c +++ b/dlls/qcap/v4l.c @@ -27,6 +27,7 @@ #include "config.h" +#include <limits.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -505,7 +506,7 @@ static NTSTATUS v4l_device_create( void *args ) frmsize.pixel_format = V4L2_PIX_FMT_BGR24; while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) != -1) { - LONGLONG max_frame_interval = (TICKSPERSEC / 30), min_frame_interval = (TICKSPERSEC / 30); + LONGLONG max_frame_interval = 0, min_frame_interval = LLONG_MAX; struct v4l2_frmivalenum frmival = {0}; struct caps *new_caps; @@ -526,21 +527,27 @@ static NTSTATUS v4l_device_create( void *args ) continue; } - if (xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1) + for (frmival.index = 0; xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1; ++frmival.index) { if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) { - max_frame_interval = TICKSPERSEC * frmival.discrete.numerator / frmival.discrete.denominator; - min_frame_interval = max_frame_interval; + LONGLONG interval = TICKSPERSEC * frmival.discrete.numerator / frmival.discrete.denominator; + + max_frame_interval = max(max_frame_interval, interval); + min_frame_interval = min(min_frame_interval, interval); } else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE || frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) { - max_frame_interval = TICKSPERSEC * frmival.stepwise.max.numerator / frmival.stepwise.max.denominator; - min_frame_interval = TICKSPERSEC * frmival.stepwise.min.numerator / frmival.stepwise.min.denominator; + LONGLONG max_interval = TICKSPERSEC * frmival.stepwise.max.numerator / frmival.stepwise.max.denominator; + LONGLONG min_interval = TICKSPERSEC * frmival.stepwise.min.numerator / frmival.stepwise.min.denominator; + + max_frame_interval = max(max_frame_interval, max_interval); + min_frame_interval = min(min_frame_interval, min_interval); } } - else + + if (max_frame_interval < min_frame_interval) ERR("Failed to get fps: %s.\n", strerror(errno)); new_caps = realloc(device->caps, (device->caps_count + 1) * sizeof(*device->caps)); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9689
From: Elizabeth Figura <zfigura(a)codeweavers.com> Based on a patch by Jiangyi Chen. --- dlls/qcap/qcap_private.h | 11 +++++++ dlls/qcap/tests/videocapture.c | 50 ++++++++++++++++++++++++++++ dlls/qcap/v4l.c | 59 ++++++++++++++++++++++++++++++++++ dlls/qcap/vfwcapture.c | 28 +++++++++++++--- 4 files changed, 143 insertions(+), 5 deletions(-) diff --git a/dlls/qcap/qcap_private.h b/dlls/qcap/qcap_private.h index c1fb7877781..9f771a7fac4 100644 --- a/dlls/qcap/qcap_private.h +++ b/dlls/qcap/qcap_private.h @@ -97,6 +97,16 @@ struct get_caps_count_params int *count; }; +struct get_frame_intervals_params +{ + video_capture_device_t device; + unsigned int caps_index; + unsigned int width; + unsigned int height; + unsigned int *count; + LONGLONG *intervals; +}; + struct get_prop_range_params { video_capture_device_t device; @@ -141,6 +151,7 @@ enum unix_funcs unix_get_media_type, unix_get_caps, unix_get_caps_count, + unix_get_frame_intervals, unix_get_prop_range, unix_get_prop, unix_set_prop, diff --git a/dlls/qcap/tests/videocapture.c b/dlls/qcap/tests/videocapture.c index f60ecee0180..13fd45b618c 100644 --- a/dlls/qcap/tests/videocapture.c +++ b/dlls/qcap/tests/videocapture.c @@ -311,6 +311,55 @@ static void test_pin_interfaces(IPin *pin) check_interface(pin, &IID_IStreamBuilder, FALSE); } +static void test_frame_rate_list(IBaseFilter *filter, IPin *pin) +{ + IAMStreamConfig *stream_config; + VIDEO_STREAM_CONFIG_CAPS vscc; + IAMVideoControl *control; + AM_MEDIA_TYPE *format; + LONGLONG *rates; + LONG rate_count; + SIZE dimensions; + int count, size; + HRESULT hr; + + IPin_QueryInterface(pin, &IID_IAMStreamConfig, (void **)&stream_config); + IBaseFilter_QueryInterface(filter, &IID_IAMVideoControl, (void **)&control); + + hr = IAMStreamConfig_GetNumberOfCapabilities(stream_config, &count, &size); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + ok(count != 0xdeadbeef, "Got wrong count: %d.\n", count); + ok(size == sizeof(VIDEO_STREAM_CONFIG_CAPS), "Got wrong size: %d.\n", size); + + dimensions.cx = dimensions.cy = 0; + + for (int i = 0; i < count; ++i) + { + hr = IAMStreamConfig_GetStreamCaps(stream_config, i, &format, (BYTE *)&vscc); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + + if (0) + { + /* The documentation says the rates can be NULL, but it crashes. */ + IAMVideoControl_GetFrameRateList(control, pin, i, vscc.MinOutputSize, &rate_count, NULL); + } + + rate_count = 0; + hr = IAMVideoControl_GetFrameRateList(control, pin, i, vscc.MinOutputSize, &rate_count, &rates); + ok(hr == S_OK, "Got hr %#lx.\n", hr); + for (LONG j = 0; j < rate_count; ++j) + ok(rates[j] >= vscc.MinFrameInterval && rates[j] <= vscc.MaxFrameInterval, + "Got unexpected rates[%ld] %I64d, min %I64d, max %I64d.\n", + j, rates[j], vscc.MinFrameInterval, vscc.MaxFrameInterval); + CoTaskMemFree(rates); + + DeleteMediaType(format); + } + + IAMStreamConfig_Release(stream_config); + IAMVideoControl_Release(control); +} + static void test_pins(IBaseFilter *filter) { IEnumPins *enum_pins; @@ -329,6 +378,7 @@ static void test_pins(IBaseFilter *filter) test_pin_interfaces(pin); test_media_types(pin); test_stream_config(pin); + test_frame_rate_list(filter, pin); } IPin_Release(pin); } diff --git a/dlls/qcap/v4l.c b/dlls/qcap/v4l.c index 482a0f1d803..44f643d68ab 100644 --- a/dlls/qcap/v4l.c +++ b/dlls/qcap/v4l.c @@ -29,6 +29,7 @@ #include <limits.h> #include <stdarg.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> @@ -428,6 +429,37 @@ static NTSTATUS v4l_device_get_caps_count( void *args ) return S_OK; } +static NTSTATUS v4l_device_get_frame_intervals( void *args ) +{ + const struct get_frame_intervals_params *params = args; + struct video_capture_device *device = get_device(params->device); + + struct v4l2_frmivalenum frmival = + { + .pixel_format = device->caps[params->caps_index].pixelformat, + .width = params->width, + .height = params->height, + }; + __u32 count = 0; + + for (frmival.index = 0; xioctl(device->fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1; ++frmival.index) + { + if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) + { + if (params->intervals) + params->intervals[count] = TICKSPERSEC * frmival.discrete.numerator / frmival.discrete.denominator; + ++count; + } + else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE || frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) + { + FIXME("Unhandled type %u.\n", frmival.type); + } + } + + *params->count = count; + return S_OK; +} + static NTSTATUS v4l_device_create( void *args ) { const struct create_params *params = args; @@ -619,6 +651,7 @@ const unixlib_entry_t __wine_unix_call_funcs[] = v4l_device_get_media_type, v4l_device_get_caps, v4l_device_get_caps_count, + v4l_device_get_frame_intervals, v4l_device_get_prop_range, v4l_device_get_prop, v4l_device_set_prop, @@ -810,6 +843,31 @@ static NTSTATUS wow64_v4l_device_get_caps_count( void *args ) return v4l_device_get_caps_count( ¶ms ); } +static NTSTATUS wow64_v4l_device_get_frame_intervals( void *args ) +{ + struct + { + video_capture_device_t device; + unsigned int caps_index; + unsigned int width; + unsigned int height; + PTR32 count; + PTR32 intervals; + } const *params32 = args; + + struct get_frame_intervals_params params = + { + params32->device, + params32->caps_index, + params32->width, + params32->height, + ULongToPtr(params32->count), + ULongToPtr(params32->intervals), + }; + + return v4l_device_get_frame_intervals( ¶ms ); +} + static NTSTATUS wow64_v4l_device_get_prop_range( void *args ) { struct @@ -886,6 +944,7 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] = wow64_v4l_device_get_media_type, wow64_v4l_device_get_caps, wow64_v4l_device_get_caps_count, + wow64_v4l_device_get_frame_intervals, wow64_v4l_device_get_prop_range, wow64_v4l_device_get_prop, v4l_device_set_prop, diff --git a/dlls/qcap/vfwcapture.c b/dlls/qcap/vfwcapture.c index bf3ded9223c..97f79c9f521 100644 --- a/dlls/qcap/vfwcapture.c +++ b/dlls/qcap/vfwcapture.c @@ -857,15 +857,33 @@ static HRESULT WINAPI video_control_GetMaxAvailableFrameRate(IAMVideoControl *if return E_NOTIMPL; } -static HRESULT WINAPI video_control_GetFrameRateList(IAMVideoControl *iface, IPin *pin, LONG index, - SIZE dimensions, LONG *list_size, LONGLONG **frame_rate) +/* Despite the name this function actually returns a list of frame intervals. */ +static HRESULT WINAPI video_control_GetFrameRateList(IAMVideoControl *iface, IPin *pin, + LONG index, SIZE dimensions, LONG *ret_count, LONGLONG **ret_intervals) { struct vfw_capture *filter = impl_from_IAMVideoControl(iface); + struct get_frame_intervals_params params; + unsigned int count; - FIXME("filter %p, pin %p, index %ld, dimensions (%ldx%ld), list size %p, frame rate %p, stub.\n", - filter, pin, index, dimensions.cx, dimensions.cy, list_size, frame_rate); + TRACE("filter %p, pin %p, index %ld, dimensions (%ldx%ld), ret_count %p, ret_intervals %p.\n", + filter, pin, index, dimensions.cx, dimensions.cy, ret_count, ret_intervals); - return E_NOTIMPL; + params.device = filter->device; + params.caps_index = index; + params.width = dimensions.cx; + params.height = dimensions.cy; + params.intervals = NULL; + params.count = &count; + V4L_CALL( get_frame_intervals, ¶ms ); + + if (!(params.intervals = CoTaskMemAlloc(count * sizeof(LONGLONG)))) + return E_OUTOFMEMORY; + + V4L_CALL( get_frame_intervals, ¶ms ); + + *ret_intervals = params.intervals; + *ret_count = count; + return S_OK; } static const IAMVideoControlVtbl IAMVideoControl_VTable = -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9689
participants (2)
-
Elizabeth Figura -
Elizabeth Figura (@zfigura)