mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05: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
							
								
									bc9eb76a6e
								
							
						
					
					
						commit
						7114e9a31a
					
				
					 2 changed files with 100 additions and 57 deletions
				
			
		| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -965,7 +995,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);
 | 
			
		||||
| 
						 | 
				
			
			@ -1003,6 +1033,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