Merge branch 'master2' into 'master'

qcap: Add implementation of IAMVideoControl_GetFrameRateList.

See merge request wine/wine!4636
This commit is contained in:
JiangYi Chen 2024-11-19 01:35:55 +00:00
commit 230b932795
3 changed files with 229 additions and 26 deletions

View file

@ -132,6 +132,22 @@ struct read_frame_params
void *data;
};
struct get_frame_rates_size_params
{
video_capture_device_t device;
unsigned int index;
SIZE *dimensions;
LONG *list_size;
};
struct get_frame_avg_time_params
{
video_capture_device_t device;
unsigned int index;
LONG list_size;
LONGLONG *frame_rate;
};
enum unix_funcs
{
unix_create,
@ -147,6 +163,8 @@ enum unix_funcs
unix_get_prop,
unix_set_prop,
unix_read_frame,
unix_get_frame_rates_size,
unix_get_frame_avg_time,
unix_funcs_count
};

View file

@ -60,6 +60,9 @@ static typeof(close) *video_close = close;
static typeof(ioctl) *video_ioctl = ioctl;
static typeof(read) *video_read = read;
#define SECOND_IN_100NS (LONGLONG)10000000
#define DEFAULT_FPS 25
static BOOL video_init(void)
{
#ifdef SONAME_LIBV4L2
@ -86,6 +89,8 @@ struct caps
AM_MEDIA_TYPE media_type;
VIDEOINFOHEADER video_info;
VIDEO_STREAM_CONFIG_CAPS config;
LONG fps_count;
__u32* fps_list;
};
struct video_capture_device
@ -122,6 +127,10 @@ static void device_destroy(struct video_capture_device *device)
{
if (device->fd != -1)
video_close(device->fd);
for (int index = 0; index < device->caps_count; index++)
{
free(device->caps[index].fps_list) ;
}
if (device->caps_count)
free(device->caps);
free(device->image_data);
@ -367,10 +376,72 @@ static NTSTATUS v4l_device_read_frame( void *args )
return TRUE;
}
static int get_fps_list(int fd, __u32 format, __u32 width, __u32 height, __u32 *fps_count, __u32 **fps_list)
{
struct v4l2_frmivalenum frmival = {.index=0, .pixel_format=format, .width=width, .height=height};
__u32 fps_index = 0, *fps_frame = NULL;
while (xioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmival) != -1)
{
if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
{
fps_frame = realloc(fps_frame, (fps_index + 1) * sizeof(__u32));
if (fps_frame && frmival.discrete.denominator && frmival.discrete.numerator)
{
fps_frame[fps_index] = frmival.discrete.denominator / frmival.discrete.numerator;
fps_index++;
}
}
else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE
|| frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)
{
__u32 fps_step = 0, fps_max = 0, fps_min = 0;
if(frmival.stepwise.step.denominator && frmival.stepwise.step.numerator)
fps_step = frmival.stepwise.step.denominator / frmival.stepwise.step.numerator;
/*The maximum FPS is the inverse of the minimum frame time, not the maximum frame time, and vice versa.*/
if(frmival.stepwise.min.denominator && frmival.stepwise.min.numerator)
fps_max = frmival.stepwise.min.denominator / frmival.stepwise.min.numerator;
if(frmival.stepwise.max.denominator && frmival.stepwise.max.numerator)
fps_min = frmival.stepwise.max.denominator / frmival.stepwise.max.numerator;
if(fps_step && fps_max && fps_min)
{
do
{
fps_frame = realloc(fps_frame, (fps_index + 1) * sizeof(__u32));
if (fps_frame)
{
fps_frame[fps_index] = fps_max;
fps_index++;
}
fps_max -= fps_step;
}while(fps_max >= fps_min);
}
break;
}
frmival.index++;
}
if(fps_count) *fps_count = fps_index;
if(fps_list) *fps_list = fps_frame;
return 0;
}
static void fill_caps(__u32 pixelformat, __u32 width, __u32 height,
__u32 max_fps, __u32 min_fps, struct caps *caps)
__u32 fps_count, __u32 *fps_list, struct caps *caps)
{
LONG depth = 24;
__u32 max_fps = DEFAULT_FPS, min_fps = DEFAULT_FPS;
for(int i = 0; i < fps_count; ++i)
{
if(max_fps < fps_list[i])
max_fps = fps_list[i];
if(min_fps > fps_list[i])
min_fps = fps_list[i];
}
memset(caps, 0, sizeof(*caps));
caps->video_info.dwBitRate = width * height * depth * max_fps;
@ -391,8 +462,8 @@ 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 = SECOND_IN_100NS / max_fps;
caps->config.MinFrameInterval = SECOND_IN_100NS / min_fps;
caps->config.MaxOutputSize.cx = width;
caps->config.MaxOutputSize.cy = height;
caps->config.MinOutputSize.cx = width;
@ -401,6 +472,8 @@ static void fill_caps(__u32 pixelformat, __u32 width, __u32 height,
caps->config.MinBitsPerSecond = width * height * depth * min_fps;
caps->config.MaxBitsPerSecond = width * height * depth * max_fps;
caps->pixelformat = pixelformat;
caps->fps_count = fps_count;
caps->fps_list = fps_list;
}
static NTSTATUS v4l_device_get_caps( void *args )
@ -502,10 +575,10 @@ static NTSTATUS v4l_device_create( void *args )
while (xioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) != -1)
{
struct v4l2_frmivalenum frmival = {0};
__u32 max_fps = 30, min_fps = 30;
__u32 fps_count, *fps_list;
struct caps *new_caps;
frmival.pixel_format = format.fmt.pix.pixelformat;
frmival.pixel_format = frmsize.pixel_format;
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE)
{
frmival.width = frmsize.discrete.width;
@ -522,29 +595,14 @@ static NTSTATUS v4l_device_create( void *args )
continue;
}
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
|| 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;
}
}
else
ERR("Failed to get fps: %s.\n", strerror(errno));
get_fps_list(fd, frmival.pixel_format, frmival.width, frmival.height, &fps_count, &fps_list);
new_caps = realloc(device->caps, (device->caps_count + 1) * sizeof(*device->caps));
if (!new_caps)
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]);
fill_caps(frmival.pixel_format, frmival.width, frmival.height,
fps_count, fps_list, &device->caps[device->caps_count]);
device->caps_count++;
frmsize.index++;
@ -597,6 +655,48 @@ static NTSTATUS v4l_device_destroy( void *args )
return S_OK;
}
static NTSTATUS v4l_device_get_frame_rates_size( void *args )
{
const struct get_frame_rates_size_params *params = args;
struct video_capture_device *device = get_device(params->device);
SIZE *dimensions = (SIZE *)params->dimensions;
LONG width, height;
unsigned int caps_count = device->caps_count;
if (params->index >= caps_count)
return E_INVALIDARG;
width = device->caps[params->index].video_info.bmiHeader.biWidth;
height = device->caps[params->index].video_info.bmiHeader.biHeight;
if((dimensions->cx != width) || (dimensions->cy != height))
{
WARN("requested demensions unsupport !!!\n");
return E_FAIL;
}
*params->list_size = device->caps[params->index].fps_count;
return S_OK;
}
static NTSTATUS v4l_device_get_frame_avg_time( void *args )
{
const struct get_frame_avg_time_params *params = args;
struct video_capture_device *device = get_device(params->device);
unsigned int caps_count = device->caps_count;
if (params->index >= caps_count)
return E_INVALIDARG ;
for(int i = 0; i < params->list_size; ++i)
{
__u32* fps = device->caps[params->index].fps_list;
if(fps[i]) (params->frame_rate)[i] = SECOND_IN_100NS / fps[i];
else (params->frame_rate)[i] = SECOND_IN_100NS / DEFAULT_FPS;
}
return S_OK;
}
const unixlib_entry_t __wine_unix_call_funcs[] =
{
v4l_device_create,
@ -612,6 +712,8 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
v4l_device_get_prop,
v4l_device_set_prop,
v4l_device_read_frame,
v4l_device_get_frame_rates_size,
v4l_device_get_frame_avg_time,
};
C_ASSERT( ARRAYSIZE(__wine_unix_call_funcs) == unix_funcs_count );
@ -864,6 +966,48 @@ static NTSTATUS wow64_v4l_device_read_frame( void *args )
return v4l_device_read_frame( &params );
}
static NTSTATUS wow64_v4l_device_get_frame_rates_size( void *args )
{
struct
{
video_capture_device_t device;
unsigned int index;
PTR32 dimensions;
PTR32 list_size;
} const *params32 = args;
struct get_frame_rates_size_params params =
{
params32->device,
params32->index,
ULongToPtr(params32->dimensions),
ULongToPtr(params32->list_size)
};
return v4l_device_get_frame_rates_size( &params );
}
static NTSTATUS wow64_v4l_device_get_frame_avg_time( void *args )
{
struct
{
video_capture_device_t device;
unsigned int index;
int list_size;
PTR32 frame_rate;
} const *params32 = args;
struct get_frame_avg_time_params params =
{
params32->device,
params32->index,
params32->list_size,
ULongToPtr(params32->frame_rate)
};
return v4l_device_get_frame_avg_time( &params );
}
const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
{
wow64_v4l_device_create,
@ -879,6 +1023,8 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
wow64_v4l_device_get_prop,
v4l_device_set_prop,
wow64_v4l_device_read_frame,
wow64_v4l_device_get_frame_rates_size,
wow64_v4l_device_get_frame_avg_time,
};
C_ASSERT( ARRAYSIZE(__wine_unix_call_wow64_funcs) == unix_funcs_count );

View file

@ -429,7 +429,7 @@ static HRESULT WINAPI AMStreamConfig_GetStreamCaps(IAMStreamConfig *iface,
TRACE("filter %p, index %d, pmt %p, vscc %p.\n", filter, index, pmt, vscc);
V4L_CALL( get_caps_count, &count_params );
if (index > count)
if (index >= count)
return S_FALSE;
if (!(mt = CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))))
@ -860,12 +860,51 @@ static HRESULT WINAPI video_control_GetMaxAvailableFrameRate(IAMVideoControl *if
static HRESULT WINAPI video_control_GetFrameRateList(IAMVideoControl *iface, IPin *pin, LONG index,
SIZE dimensions, LONG *list_size, LONGLONG **frame_rate)
{
LONG size;
LONGLONG *rate;
int count;
HRESULT hr;
struct get_caps_count_params count_params;
struct get_frame_rates_size_params size_params;
struct get_frame_avg_time_params rate_params;
struct vfw_capture *filter = impl_from_IAMVideoControl(iface);
FIXME("filter %p, pin %p, index %ld, dimensions (%ldx%ld), list size %p, frame rate %p, stub.\n",
TRACE("filter %p, pin %p, index %ld, dimensions (%ldx%ld), list size %p, frame rate: %p\n",
filter, pin, index, dimensions.cx, dimensions.cy, list_size, frame_rate);
return E_NOTIMPL;
if(!list_size) return S_FALSE;
count_params.device = filter->device;
count_params.count = &count;
V4L_CALL( get_caps_count, &count_params );
if (index >= count)
return S_FALSE;
size_params.device = filter->device;
size_params.index = index;
size_params.dimensions = &dimensions;
size_params.list_size = &size;
V4L_CALL( get_frame_rates_size, &size_params );
if(size < 0)
return E_FAIL;
*list_size=size;
if(!frame_rate) return S_OK;
if (!(rate = CoTaskMemAlloc(sizeof(LONGLONG)*size)))
return E_OUTOFMEMORY;
rate_params.device = filter->device;
rate_params.index = index;
rate_params.list_size = size;
rate_params.frame_rate = rate;
if ((hr = V4L_CALL( get_frame_avg_time, &rate_params )) != S_OK)
{
CoTaskMemFree(rate);
return hr;
}
*frame_rate=rate;
return S_OK;
}
static const IAMVideoControlVtbl IAMVideoControl_VTable =