From 17c755714d9be6b3a3ff2553c1a5a5570c4c6fe2 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 26 Nov 2024 16:58:57 +0100 Subject: [PATCH] v4l2: allow negotiation with modifier This assumes that the modifier is always 'linear'. That is not quite correct for all V4L2 formats. But PipeWire only uses input devices and other modifiers are very unlikely. This makes it possible to use DMABUFs with the GStreamer pipewiresrc. --- spa/plugins/v4l2/v4l2-source.c | 8 ++++++++ spa/plugins/v4l2/v4l2-utils.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/spa/plugins/v4l2/v4l2-source.c b/spa/plugins/v4l2/v4l2-source.c index b06dda994..6b6dcff0d 100644 --- a/spa/plugins/v4l2/v4l2-source.c +++ b/spa/plugins/v4l2/v4l2-source.c @@ -90,6 +90,7 @@ struct port { struct v4l2_frmivalenum frmival; bool have_format; + bool have_modifier; struct spa_video_info current_format; struct spa_v4l2_device dev; @@ -219,6 +220,12 @@ static int port_get_format(struct port *port, case SPA_MEDIA_SUBTYPE_raw: spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(port->current_format.info.raw.format), + 0); + if (port->have_modifier) + spa_pod_builder_add(builder, + SPA_FORMAT_VIDEO_modifier, SPA_POD_Long(0), + 0); + spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle(&port->current_format.info.raw.size), SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&port->current_format.info.raw.framerate), 0); @@ -706,6 +713,7 @@ static int port_set_format(struct impl *this, struct port *port, spa_log_error(this->log, "can't parse video raw"); return -EINVAL; } + port->have_modifier = info.info.raw.flags & SPA_VIDEO_FLAG_MODIFIER; break; case SPA_MEDIA_SUBTYPE_mjpg: if (spa_format_video_mjpg_parse(format, &info.info.mjpg) < 0) diff --git a/spa/plugins/v4l2/v4l2-utils.c b/spa/plugins/v4l2/v4l2-utils.c index 58758f758..7e972dc6e 100644 --- a/spa/plugins/v4l2/v4l2-utils.c +++ b/spa/plugins/v4l2/v4l2-utils.c @@ -513,11 +513,12 @@ spa_v4l2_enum_format(struct impl *this, int seq, struct spa_pod_frame f[2]; struct spa_result_node_params result; uint32_t count = 0; + bool with_modifier; if ((res = spa_v4l2_open(dev, this->props.device)) < 0) return res; - spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 4096); + spa_pod_dynamic_builder_init(&b, buffer, sizeof(buffer), 8192); spa_pod_builder_get_state(&b.b, &state); result.id = SPA_PARAM_EnumFormat; @@ -537,6 +538,7 @@ spa_v4l2_enum_format(struct impl *this, int seq, if ((res = spa_format_parse(filter, &filter_media_type, &filter_media_subtype)) < 0) return res; } + with_modifier = !filter || spa_pod_find_prop(filter, NULL, SPA_FORMAT_VIDEO_modifier); if (false) { next_fmtdesc: @@ -729,6 +731,10 @@ do_frmsize_filter: if (info->media_subtype == SPA_MEDIA_SUBTYPE_raw) { spa_pod_builder_prop(&b.b, SPA_FORMAT_VIDEO_format, 0); spa_pod_builder_id(&b.b, info->format); + if (with_modifier) { + spa_pod_builder_prop(&b.b, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY); + spa_pod_builder_long(&b.b, 0L); + } } spa_pod_builder_prop(&b.b, SPA_FORMAT_VIDEO_size, 0); @@ -899,6 +905,27 @@ do_frminterval_filter: spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + if (++count == num) + goto enum_end; + + if (with_modifier && info->media_subtype == SPA_MEDIA_SUBTYPE_raw) { + struct spa_pod_object *op = (struct spa_pod_object *) result.param; + const struct spa_pod_prop *p; + bool drop_next = false; + + spa_pod_builder_push_object(&b.b, &f[0], op->body.type, op->body.id); + + SPA_POD_OBJECT_FOREACH(op, p) { + if (p->key != SPA_FORMAT_VIDEO_modifier) + spa_pod_builder_raw_padded(&b.b, p, SPA_POD_PROP_SIZE(p)); + } + + result.index = result.next++; + result.param = spa_pod_builder_pop(&b.b, &f[0]); + + spa_node_emit_result(&this->hooks, seq, 0, SPA_RESULT_TYPE_NODE_PARAMS, &result); + } + if (++count != num) goto next;