diff --git a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c index f59ba1f285..6fc027d87d 100644 --- a/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c +++ b/subprojects/gst-plugins-good/sys/v4l2/gstv4l2object.c @@ -4606,6 +4606,61 @@ unsupported_format: } } +/** + * gst_v4l2_object_set_compose: + * @obj: the object + * @compose_rect: the region to compose + * + * Compose the video data to the regions specified in the @compose_rect. + * + * For capture devices, this compose the image sensor / video stream provided by + * the V4L2 device. The composing area specifies which part of the buffer is + * actually written to by the hardware. + * For output devices, this compose the memory buffer that GStreamer passed to + * the V4L2 device. The application may select the part of display where the + * image should be displayed. The size and position of such a window is + * controlled by the compose target. + * + * The compose_rect may be modified by the V4L2 device to a region that + * fulfills H/W requirements. + * + * Returns: %TRUE on success, %FALSE on failure. + */ +static gboolean +gst_v4l2_object_set_compose (GstV4l2Object * obj, + struct v4l2_rect *compose_rect) +{ + struct v4l2_selection sel = { 0 }; + + GST_V4L2_CHECK_OPEN (obj); + + sel.type = obj->type; + sel.target = V4L2_SEL_TGT_COMPOSE; + sel.flags = 0; + sel.r = *compose_rect; + + GST_DEBUG_OBJECT (obj->dbg_obj, + "Desired composing left %u, top %u, size %ux%u", sel.r.left, sel.r.top, + sel.r.width, sel.r.height); + + if (obj->ioctl (obj->video_fd, VIDIOC_S_SELECTION, &sel) < 0) { + if (errno != ENOTTY) { + GST_WARNING_OBJECT (obj->dbg_obj, + "Failed to set compose rectangle with VIDIOC_S_SELECTION: %s", + g_strerror (errno)); + return FALSE; + } + } + + GST_DEBUG_OBJECT (obj->dbg_obj, + "Got composing left %u, top %u, size %ux%u", sel.r.left, sel.r.top, + sel.r.width, sel.r.height); + + *compose_rect = sel.r; + + return TRUE; +} + /** * gst_v4l2_object_set_crop: * @obj: the object @@ -4689,20 +4744,24 @@ gboolean gst_v4l2_object_setup_padding (GstV4l2Object * obj) { GstVideoAlignment *align = &obj->align; - struct v4l2_rect crop; + struct v4l2_rect rect; if (align->padding_left + align->padding_top + align->padding_right + align->padding_bottom == 0) { - GST_DEBUG_OBJECT (obj->dbg_obj, "no cropping needed"); + GST_DEBUG_OBJECT (obj->dbg_obj, "no cropping/composing needed"); return TRUE; } - crop.left = align->padding_left; - crop.top = align->padding_top; - crop.width = obj->info.width; - crop.height = GST_VIDEO_INFO_FIELD_HEIGHT (&obj->info); + rect.left = align->padding_left; + rect.top = align->padding_top; + rect.width = obj->info.width; + rect.height = GST_VIDEO_INFO_FIELD_HEIGHT (&obj->info); - return gst_v4l2_object_set_crop (obj, &crop); + if (V4L2_TYPE_IS_OUTPUT (obj->type)) { + return gst_v4l2_object_set_crop (obj, &rect); + } else { + return gst_v4l2_object_set_compose (obj, &rect); + } } static gboolean @@ -5152,6 +5211,18 @@ gst_v4l2_object_match_buffer_layout (GstV4l2Object * obj, guint n_planes, wanted_stride[0] = plane_stride; } + GST_DEBUG_OBJECT (obj->dbg_obj, "Wanted format of %dx%d", + format.fmt.pix_mp.width, format.fmt.pix_mp.height); + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { + for (guint i = 0; i < format.fmt.pix_mp.num_planes; i++) + GST_DEBUG_OBJECT (obj->dbg_obj, " [%u] stride %d, sizeimage %d", i, + format.fmt.pix_mp.plane_fmt[i].bytesperline, + format.fmt.pix_mp.plane_fmt[i].sizeimage); + } else { + GST_DEBUG_OBJECT (obj->dbg_obj, " stride %d, sizeimage %d", + format.fmt.pix.bytesperline, format.fmt.pix.sizeimage); + } + if (obj->ioctl (obj->video_fd, VIDIOC_S_FMT, &format) < 0) { GST_WARNING_OBJECT (obj->dbg_obj, "Something went wrong trying to update current format: %s", @@ -5159,6 +5230,18 @@ gst_v4l2_object_match_buffer_layout (GstV4l2Object * obj, guint n_planes, return FALSE; } + GST_DEBUG_OBJECT (obj->dbg_obj, "Got format of %dx%d", + format.fmt.pix_mp.width, format.fmt.pix_mp.height); + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { + for (guint i = 0; i < format.fmt.pix_mp.num_planes; i++) + GST_DEBUG_OBJECT (obj->dbg_obj, " [%u] stride %d, sizeimage %d", i, + format.fmt.pix_mp.plane_fmt[i].bytesperline, + format.fmt.pix_mp.plane_fmt[i].sizeimage); + } else { + GST_DEBUG_OBJECT (obj->dbg_obj, " stride %d, sizeimage %d", + format.fmt.pix.bytesperline, format.fmt.pix.sizeimage); + } + gst_v4l2_object_save_format (obj, obj->fmtdesc, &format, &obj->info, &obj->align); @@ -5195,36 +5278,20 @@ gst_v4l2_object_match_buffer_layout (GstV4l2Object * obj, guint n_planes, } } - if (obj->align.padding_bottom) { - /* Crop because of vertical padding */ - GST_DEBUG_OBJECT (obj->dbg_obj, "crop because of bottom padding of %d", - obj->align.padding_bottom); + if (obj->align.padding_right || obj->align.padding_bottom) { + /* Setup padding */ + GST_DEBUG_OBJECT (obj->dbg_obj, + "setup padding (top: %u left: %u right: %u bottom: %u)", + obj->align.padding_top, + obj->align.padding_left, + obj->align.padding_right, obj->align.padding_bottom); + gst_v4l2_object_setup_padding (obj); } return TRUE; } -static gboolean -validate_video_meta_struct (GstV4l2Object * obj, const GstStructure * s) -{ - guint i; - - for (i = 0; i < gst_structure_n_fields (s); i++) { - const gchar *name = gst_structure_nth_field_name (s, i); - - if (!g_str_equal (name, "padding-top") - && !g_str_equal (name, "padding-bottom") - && !g_str_equal (name, "padding-left") - && !g_str_equal (name, "padding-right")) { - GST_WARNING_OBJECT (obj->dbg_obj, "Unknown video meta field: '%s'", name); - return FALSE; - } - } - - return TRUE; -} - static gboolean gst_v4l2_object_match_buffer_layout_from_struct (GstV4l2Object * obj, const GstStructure * s, GstCaps * caps, guint buffer_size) @@ -5233,20 +5300,13 @@ gst_v4l2_object_match_buffer_layout_from_struct (GstV4l2Object * obj, GstVideoAlignment align; gsize plane_size[GST_VIDEO_MAX_PLANES]; - if (!validate_video_meta_struct (obj, s)) - return FALSE; - - if (!gst_video_info_from_caps (&info, caps)) { - GST_WARNING_OBJECT (obj->dbg_obj, "Failed to create video info"); - return FALSE; - } - gst_video_alignment_reset (&align); - gst_structure_get_uint (s, "padding-top", &align.padding_top); - gst_structure_get_uint (s, "padding-bottom", &align.padding_bottom); - gst_structure_get_uint (s, "padding-left", &align.padding_left); - gst_structure_get_uint (s, "padding-right", &align.padding_right); + if (!gst_structure_has_name (s, "video-meta") || + !gst_buffer_pool_config_get_video_alignment (s, &align)) { + GST_WARNING_OBJECT (obj->dbg_obj, "Invalid params"); + return FALSE; + } if (align.padding_top || align.padding_bottom || align.padding_left || align.padding_right) { @@ -5256,6 +5316,11 @@ gst_v4l2_object_match_buffer_layout_from_struct (GstV4l2Object * obj, align.padding_right); } + if (!gst_video_info_from_caps (&info, caps)) { + GST_WARNING_OBJECT (obj->dbg_obj, "Failed to create video info"); + return FALSE; + } + if (!gst_video_info_align_full (&info, &align, plane_size)) { GST_WARNING_OBJECT (obj->dbg_obj, "Failed to align video info"); return FALSE; @@ -5582,6 +5647,7 @@ gst_v4l2_object_propose_allocation (GstV4l2Object * obj, GstQuery * query) guint size, min, max; GstCaps *caps; gboolean need_pool; + GstStructure *allocation_meta = NULL; /* Set defaults allocation parameters */ size = obj->info.size; @@ -5633,8 +5699,18 @@ gst_v4l2_object_propose_allocation (GstV4l2Object * obj, GstQuery * query) gst_query_add_allocation_pool (query, pool, size, min, max); + if (obj->align.padding_top || obj->align.padding_bottom || + obj->align.padding_left || obj->align.padding_right) { + allocation_meta = gst_structure_new_empty ("video-meta"); + gst_buffer_pool_config_set_video_alignment (allocation_meta, &obj->align); + } + /* we also support various metadata */ - gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, + allocation_meta); + + if (allocation_meta) + gst_structure_free (allocation_meta); if (pool) gst_object_unref (pool);