diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index ac956c695..20ae2fc95 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -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); diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index be2752617..cb48e9f69 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -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,