mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
v4l2: Improve format and control enumeration
Use dynamic pod builder so that we can also build complex formats. Make sure we zero the format before we parse it or else we end up with potentially uninitialized values. When ENUM_FRAMESIZES or VIDIOC_ENUM_FRAMEINTERVALS return EINVAL for the first index, make a dummy result and continue with that. This will trigger an intersect withe filter so that we end up with something valid instead of nothing. Handle 0 framerates without crashing. See #4063
This commit is contained in:
parent
2c1ec7fa43
commit
853a46120e
2 changed files with 101 additions and 58 deletions
|
|
@ -243,7 +243,8 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
{
|
||||
struct impl *this = object;
|
||||
struct spa_pod *param;
|
||||
struct spa_pod_builder b = { 0 };
|
||||
spa_auto(spa_pod_dynamic_builder) b = { 0 };
|
||||
struct spa_pod_builder_state state;
|
||||
uint8_t buffer[1024];
|
||||
struct spa_result_node_params result;
|
||||
uint32_t count = 0;
|
||||
|
|
@ -252,12 +253,15 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||
spa_return_val_if_fail(num != 0, -EINVAL);
|
||||
|
||||
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
|
||||
spa_pod_builder_get_state(&b.b, &state);
|
||||
|
||||
result.id = id;
|
||||
result.next = start;
|
||||
next:
|
||||
result.index = result.next++;
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
spa_pod_builder_reset(&b.b, &state);
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_PropInfo:
|
||||
|
|
@ -266,21 +270,21 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
|
||||
switch (result.index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_device),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("The V4L2 device"),
|
||||
SPA_PROP_INFO_type, SPA_POD_String(p->device));
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_deviceName),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("The V4L2 device name"),
|
||||
SPA_PROP_INFO_type, SPA_POD_String(p->device_name));
|
||||
break;
|
||||
case 2:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_PropInfo, id,
|
||||
SPA_PROP_INFO_id, SPA_POD_Id(SPA_PROP_deviceFd),
|
||||
SPA_PROP_INFO_description, SPA_POD_String("The V4L2 fd"),
|
||||
|
|
@ -305,8 +309,8 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
|
||||
switch (result.index) {
|
||||
case 0:
|
||||
spa_pod_builder_push_object(&b, &f, SPA_TYPE_OBJECT_Props, id);
|
||||
spa_pod_builder_add(&b,
|
||||
spa_pod_builder_push_object(&b.b, &f, SPA_TYPE_OBJECT_Props, id);
|
||||
spa_pod_builder_add(&b.b,
|
||||
SPA_PROP_device, SPA_POD_String(p->device),
|
||||
SPA_PROP_deviceName, SPA_POD_String(p->device_name),
|
||||
SPA_PROP_deviceFd, SPA_POD_Int(p->device_fd),
|
||||
|
|
@ -314,20 +318,20 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
for (i = 0; i < port->n_controls; i++) {
|
||||
struct control *c = &port->controls[i];
|
||||
|
||||
spa_pod_builder_prop(&b, c->id, 0);
|
||||
spa_pod_builder_prop(&b.b, c->id, 0);
|
||||
switch (c->type) {
|
||||
case SPA_TYPE_Int:
|
||||
spa_pod_builder_int(&b, c->value);
|
||||
spa_pod_builder_int(&b.b, c->value);
|
||||
break;
|
||||
case SPA_TYPE_Bool:
|
||||
spa_pod_builder_bool(&b, c->value);
|
||||
spa_pod_builder_bool(&b.b, c->value);
|
||||
break;
|
||||
default:
|
||||
spa_pod_builder_int(&b, c->value);
|
||||
spa_pod_builder_int(&b.b, c->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
param = spa_pod_builder_pop(&b, &f);
|
||||
param = spa_pod_builder_pop(&b.b, &f);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -338,14 +342,14 @@ static int impl_node_enum_params(void *object, int seq,
|
|||
return spa_v4l2_enum_format(this, seq, start, num, filter);
|
||||
case SPA_PARAM_Format:
|
||||
if((res = port_get_format(GET_OUT_PORT(this, 0),
|
||||
result.index, filter, ¶m, &b)) <= 0)
|
||||
result.index, filter, ¶m, &b.b)) <= 0)
|
||||
return res;
|
||||
break;
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
|
||||
if (spa_pod_filter(&b.b, &result.param, param, filter) < 0)
|
||||
goto next;
|
||||
|
||||
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
||||
|
|
@ -535,7 +539,8 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
struct impl *this = object;
|
||||
struct port *port;
|
||||
struct spa_pod *param;
|
||||
struct spa_pod_builder b = { 0 };
|
||||
spa_auto(spa_pod_dynamic_builder) b = { 0 };
|
||||
struct spa_pod_builder_state state;
|
||||
uint8_t buffer[1024];
|
||||
struct spa_result_node_params result;
|
||||
uint32_t count = 0;
|
||||
|
|
@ -545,6 +550,9 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
spa_return_val_if_fail(num != 0, -EINVAL);
|
||||
spa_return_val_if_fail(CHECK_PORT(this, direction, port_id), -EINVAL);
|
||||
|
||||
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
|
||||
spa_pod_builder_get_state(&b.b, &state);
|
||||
|
||||
port = GET_PORT(this, direction, port_id);
|
||||
|
||||
result.id = id;
|
||||
|
|
@ -552,7 +560,7 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
next:
|
||||
result.index = result.next++;
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
spa_pod_builder_reset(&b.b, &state);
|
||||
|
||||
switch (id) {
|
||||
case SPA_PARAM_PropInfo:
|
||||
|
|
@ -562,7 +570,7 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
return spa_v4l2_enum_format(this, seq, start, num, filter);
|
||||
|
||||
case SPA_PARAM_Format:
|
||||
if((res = port_get_format(port, result.index, filter, ¶m, &b)) <= 0)
|
||||
if((res = port_get_format(port, result.index, filter, ¶m, &b.b)) <= 0)
|
||||
return res;
|
||||
break;
|
||||
case SPA_PARAM_Buffers:
|
||||
|
|
@ -571,7 +579,7 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
if (result.index > 0)
|
||||
return 0;
|
||||
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_ParamBuffers, id,
|
||||
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(4, 1, MAX_BUFFERS),
|
||||
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
|
||||
|
|
@ -582,13 +590,13 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
case SPA_PARAM_Meta:
|
||||
switch (result.index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_ParamMeta, id,
|
||||
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
|
||||
SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)));
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_ParamMeta, id,
|
||||
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoTransform),
|
||||
SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_videotransform)));
|
||||
|
|
@ -600,19 +608,19 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
case SPA_PARAM_IO:
|
||||
switch (result.index) {
|
||||
case 0:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_ParamIO, id,
|
||||
SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers),
|
||||
SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_ParamIO, id,
|
||||
SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Clock),
|
||||
SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_clock)));
|
||||
break;
|
||||
case 2:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
param = spa_pod_builder_add_object(&b.b,
|
||||
SPA_TYPE_OBJECT_ParamIO, id,
|
||||
SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Control),
|
||||
SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_sequence)));
|
||||
|
|
@ -624,7 +632,7 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
case SPA_PARAM_Latency:
|
||||
switch (result.index) {
|
||||
case 0: case 1:
|
||||
param = spa_latency_build(&b, id, &this->latency[result.index]);
|
||||
param = spa_latency_build(&b.b, id, &this->latency[result.index]);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
|
|
@ -634,7 +642,7 @@ static int impl_node_port_enum_params(void *object, int seq,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (spa_pod_filter(&b, &result.param, param, filter) < 0)
|
||||
if (spa_pod_filter(&b.b, &result.param, param, filter) < 0)
|
||||
goto next;
|
||||
|
||||
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
||||
|
|
@ -652,6 +660,8 @@ static int port_set_format(struct impl *this, struct port *port,
|
|||
struct spa_video_info info;
|
||||
int res;
|
||||
|
||||
spa_zero(info);
|
||||
|
||||
if (port->have_format) {
|
||||
spa_v4l2_stream_off(this);
|
||||
spa_v4l2_clear_buffers(this);
|
||||
|
|
|
|||
|
|
@ -507,7 +507,8 @@ spa_v4l2_enum_format(struct impl *this, int seq,
|
|||
uint32_t filter_media_type, filter_media_subtype;
|
||||
struct spa_v4l2_device *dev = &port->dev;
|
||||
uint8_t buffer[1024];
|
||||
struct spa_pod_builder b = { 0 };
|
||||
spa_auto(spa_pod_dynamic_builder) b = { 0 };
|
||||
struct spa_pod_builder_state state;
|
||||
struct spa_pod_frame f[2];
|
||||
struct spa_result_node_params result;
|
||||
uint32_t count = 0;
|
||||
|
|
@ -515,6 +516,9 @@ spa_v4l2_enum_format(struct impl *this, int seq,
|
|||
if ((res = spa_v4l2_open(dev, this->props.device)) < 0)
|
||||
return res;
|
||||
|
||||
spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096);
|
||||
spa_pod_builder_get_state(&b.b, &state);
|
||||
|
||||
result.id = SPA_PARAM_EnumFormat;
|
||||
result.next = start;
|
||||
|
||||
|
|
@ -632,14 +636,31 @@ do_enum_fmt:
|
|||
}
|
||||
do_frmsize:
|
||||
if ((res = xioctl(dev->fd, VIDIOC_ENUM_FRAMESIZES, &port->frmsize)) < 0) {
|
||||
if (errno == EINVAL || errno == ENOTTY)
|
||||
if (errno == ENOTTY)
|
||||
goto next_fmtdesc;
|
||||
if (errno == EINVAL) {
|
||||
if (port->frmsize.index == 0) {
|
||||
port->frmsize.type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
|
||||
port->frmsize.stepwise.min_width = 16;
|
||||
port->frmsize.stepwise.min_height = 16;
|
||||
port->frmsize.stepwise.max_width = 16384;
|
||||
port->frmsize.stepwise.max_height = 16384;
|
||||
port->frmsize.stepwise.step_width = 16;
|
||||
port->frmsize.stepwise.step_height = 16;
|
||||
port->fmtdesc.index++;
|
||||
port->next_fmtdesc = true;
|
||||
goto do_frmsize_filter;
|
||||
}
|
||||
else
|
||||
goto next_fmtdesc;
|
||||
}
|
||||
|
||||
res = -errno;
|
||||
spa_log_error(this->log, "'%s' VIDIOC_ENUM_FRAMESIZES: %m",
|
||||
this->props.device);
|
||||
goto exit;
|
||||
}
|
||||
do_frmsize_filter:
|
||||
if (filter) {
|
||||
static const struct spa_rectangle step = {1, 1};
|
||||
|
||||
|
|
@ -697,34 +718,35 @@ do_enum_fmt:
|
|||
}
|
||||
}
|
||||
|
||||
spa_pod_builder_init(&b, buffer, sizeof(buffer));
|
||||
spa_pod_builder_push_object(&b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
||||
spa_pod_builder_add(&b,
|
||||
spa_pod_builder_reset(&b.b, &state);
|
||||
spa_pod_builder_push_object(&b.b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat);
|
||||
spa_pod_builder_add(&b.b,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(info->media_type),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(info->media_subtype),
|
||||
0);
|
||||
|
||||
if (info->media_subtype == SPA_MEDIA_SUBTYPE_raw) {
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_format, 0);
|
||||
spa_pod_builder_id(&b, info->format);
|
||||
spa_pod_builder_prop(&b.b, SPA_FORMAT_VIDEO_format, 0);
|
||||
spa_pod_builder_id(&b.b, info->format);
|
||||
}
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_size, 0);
|
||||
|
||||
spa_pod_builder_prop(&b.b, SPA_FORMAT_VIDEO_size, 0);
|
||||
if (port->frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
|
||||
spa_pod_builder_rectangle(&b,
|
||||
spa_pod_builder_rectangle(&b.b,
|
||||
port->frmsize.discrete.width,
|
||||
port->frmsize.discrete.height);
|
||||
} else if (port->frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS ||
|
||||
port->frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
|
||||
spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
|
||||
spa_pod_builder_push_choice(&b.b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b.b, &f[1]);
|
||||
|
||||
spa_pod_builder_rectangle(&b,
|
||||
spa_pod_builder_rectangle(&b.b,
|
||||
port->frmsize.stepwise.min_width,
|
||||
port->frmsize.stepwise.min_height);
|
||||
spa_pod_builder_rectangle(&b,
|
||||
spa_pod_builder_rectangle(&b.b,
|
||||
port->frmsize.stepwise.min_width,
|
||||
port->frmsize.stepwise.min_height);
|
||||
spa_pod_builder_rectangle(&b,
|
||||
spa_pod_builder_rectangle(&b.b,
|
||||
port->frmsize.stepwise.max_width,
|
||||
port->frmsize.stepwise.max_height);
|
||||
|
||||
|
|
@ -732,35 +754,43 @@ do_enum_fmt:
|
|||
choice->body.type = SPA_CHOICE_Range;
|
||||
} else {
|
||||
choice->body.type = SPA_CHOICE_Step;
|
||||
spa_pod_builder_rectangle(&b,
|
||||
spa_pod_builder_rectangle(&b.b,
|
||||
port->frmsize.stepwise.max_width,
|
||||
port->frmsize.stepwise.max_height);
|
||||
}
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
spa_pod_builder_pop(&b.b, &f[1]);
|
||||
}
|
||||
|
||||
spa_pod_builder_prop(&b, SPA_FORMAT_VIDEO_framerate, 0);
|
||||
spa_pod_builder_prop(&b.b, SPA_FORMAT_VIDEO_framerate, 0);
|
||||
|
||||
n_fractions = 0;
|
||||
|
||||
spa_pod_builder_push_choice(&b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b, &f[1]);
|
||||
spa_pod_builder_push_choice(&b.b, &f[1], SPA_CHOICE_None, 0);
|
||||
choice = (struct spa_pod_choice*)spa_pod_builder_frame(&b.b, &f[1]);
|
||||
port->frmival.index = 0;
|
||||
|
||||
while (true) {
|
||||
if ((res = xioctl(dev->fd, VIDIOC_ENUM_FRAMEINTERVALS, &port->frmival)) < 0) {
|
||||
res = -errno;
|
||||
port->frmsize.index++;
|
||||
port->next_frmsize = true;
|
||||
if (errno == EINVAL || errno == ENOTTY) {
|
||||
port->frmsize.index++;
|
||||
port->next_frmsize = true;
|
||||
if (port->frmival.index == 0)
|
||||
goto next_frmsize;
|
||||
break;
|
||||
if (port->frmival.index == 0) {
|
||||
port->frmival.type = V4L2_FRMIVAL_TYPE_CONTINUOUS;
|
||||
port->frmival.stepwise.min.denominator = 1;
|
||||
port->frmival.stepwise.min.numerator = 1;
|
||||
port->frmival.stepwise.max.denominator = 120;
|
||||
port->frmival.stepwise.max.numerator = 1;
|
||||
goto do_frminterval_filter;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
spa_log_error(this->log, "'%s' VIDIOC_ENUM_FRAMEINTERVALS: %m",
|
||||
this->props.device);
|
||||
goto exit;
|
||||
}
|
||||
do_frminterval_filter:
|
||||
if (filter) {
|
||||
static const struct spa_fraction step = {1, 1};
|
||||
|
||||
|
|
@ -812,10 +842,10 @@ do_enum_fmt:
|
|||
if (port->frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
|
||||
choice->body.type = SPA_CHOICE_Enum;
|
||||
if (n_fractions == 0)
|
||||
spa_pod_builder_fraction(&b,
|
||||
spa_pod_builder_fraction(&b.b,
|
||||
port->frmival.discrete.denominator,
|
||||
port->frmival.discrete.numerator);
|
||||
spa_pod_builder_fraction(&b,
|
||||
spa_pod_builder_fraction(&b.b,
|
||||
port->frmival.discrete.denominator,
|
||||
port->frmival.discrete.numerator);
|
||||
port->frmival.index++;
|
||||
|
|
@ -823,11 +853,11 @@ do_enum_fmt:
|
|||
} else if (port->frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS ||
|
||||
port->frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
|
||||
if (n_fractions == 0)
|
||||
spa_pod_builder_fraction(&b, 25, 1);
|
||||
spa_pod_builder_fraction(&b,
|
||||
spa_pod_builder_fraction(&b.b, 25, 1);
|
||||
spa_pod_builder_fraction(&b.b,
|
||||
port->frmival.stepwise.min.denominator,
|
||||
port->frmival.stepwise.min.numerator);
|
||||
spa_pod_builder_fraction(&b,
|
||||
spa_pod_builder_fraction(&b.b,
|
||||
port->frmival.stepwise.max.denominator,
|
||||
port->frmival.stepwise.max.numerator);
|
||||
|
||||
|
|
@ -836,7 +866,7 @@ do_enum_fmt:
|
|||
n_fractions += 2;
|
||||
} else {
|
||||
choice->body.type = SPA_CHOICE_Step;
|
||||
spa_pod_builder_fraction(&b,
|
||||
spa_pod_builder_fraction(&b.b,
|
||||
port->frmival.stepwise.step.denominator,
|
||||
port->frmival.stepwise.step.numerator);
|
||||
n_fractions += 3;
|
||||
|
|
@ -851,9 +881,9 @@ do_enum_fmt:
|
|||
goto next_frmsize;
|
||||
if (n_fractions == 1)
|
||||
choice->body.type = SPA_CHOICE_None;
|
||||
spa_pod_builder_pop(&b.b, &f[1]);
|
||||
|
||||
spa_pod_builder_pop(&b, &f[1]);
|
||||
result.param = spa_pod_builder_pop(&b, &f[0]);
|
||||
result.param = spa_pod_builder_pop(&b.b, &f[0]);
|
||||
|
||||
spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result);
|
||||
|
||||
|
|
@ -966,7 +996,7 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
|
|||
streamparm.parm.capture.timeperframe.numerator = framerate->denom;
|
||||
streamparm.parm.capture.timeperframe.denominator = framerate->num;
|
||||
|
||||
spa_log_debug(this->log, "set %.4s %dx%d %d/%d", (char *)&fmt.fmt.pix.pixelformat,
|
||||
spa_log_info(this->log, "set %.4s %dx%d %d/%d", (char *)&fmt.fmt.pix.pixelformat,
|
||||
fmt.fmt.pix.width, fmt.fmt.pix.height,
|
||||
streamparm.parm.capture.timeperframe.denominator,
|
||||
streamparm.parm.capture.timeperframe.numerator);
|
||||
|
|
@ -1005,6 +1035,9 @@ static int spa_v4l2_set_format(struct impl *this, struct spa_video_info *format,
|
|||
if (flags & SPA_NODE_PARAM_FLAG_TEST_ONLY)
|
||||
return match ? 0 : 1;
|
||||
|
||||
if (streamparm.parm.capture.timeperframe.denominator == 0)
|
||||
streamparm.parm.capture.timeperframe.denominator = 1;
|
||||
|
||||
spa_log_info(this->log, "'%s' got %.4s %dx%d %d/%d",
|
||||
dev->path, (char *)&fmt.fmt.pix.pixelformat,
|
||||
fmt.fmt.pix.width, fmt.fmt.pix.height,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue