From a244a8f4ab162a8c24ea8cf71c74484e88b459a7 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Thu, 15 Sep 2022 18:01:12 +0800 Subject: [PATCH 38/48] kmssink: Support scaling in modesetting Signed-off-by: Jeffy Chen --- sys/kms/gstkmssink.c | 131 +++++++++++++++++++++++++++++-------------- sys/kms/gstkmssink.h | 1 + 2 files changed, 90 insertions(+), 42 deletions(-) diff --git a/sys/kms/gstkmssink.c b/sys/kms/gstkmssink.c index 88fc30d..c2a889d 100644 --- a/sys/kms/gstkmssink.c +++ b/sys/kms/gstkmssink.c @@ -79,6 +79,7 @@ static GstFlowReturn gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf); static void gst_kms_sink_video_overlay_init (GstVideoOverlayInterface * iface); static void gst_kms_sink_drain (GstKMSSink * self); +static gboolean gst_kms_sink_calculate_display_ratio (GstKMSSink * self, GstVideoInfo * vinfo, gint * scaled_width, gint * scaled_height); #define parent_class gst_kms_sink_parent_class G_DEFINE_TYPE_WITH_CODE (GstKMSSink, gst_kms_sink, GST_TYPE_VIDEO_SINK, @@ -581,12 +582,13 @@ find_plane_for_crtc (int fd, drmModeRes * res, drmModePlaneRes * pres, plane = drmModeGetPlane (fd, pres->planes[i]); plane_type = drm_plane_get_type (fd, plane); - /* Check unused possible planes */ - if (plane->possible_crtcs & (1 << pipe) && !plane->fb_id) { + /* Check possible planes */ + if (plane->possible_crtcs & (1 << pipe)) { if (plane->plane_id == preferred) return plane; - if (!fallback && plane_type == DRM_PLANE_TYPE_OVERLAY) { + if (!fallback && !plane->fb_id && + plane_type == DRM_PLANE_TYPE_OVERLAY) { /* Fallback to the first unused overlay plane */ fallback = plane->plane_id; } @@ -594,7 +596,7 @@ find_plane_for_crtc (int fd, drmModeRes * res, drmModePlaneRes * pres, drmModeFreePlane (plane); } - if (fallback) + if (!preferred && fallback) return drmModeGetPlane (fd, fallback); return NULL; @@ -832,13 +834,15 @@ configure_mode_setting (GstKMSSink * self, GstVideoInfo * vinfo) drmModeConnector *conn; int err; gint i; - drmModeModeInfo *mode; + drmModeModeInfo *mode, *preferred; guint32 fb_id; GstKMSMemory *kmsmem; + GstVideoInfo *info; ret = FALSE; conn = NULL; mode = NULL; + preferred = NULL; kmsmem = NULL; if (self->conn_id < 0) @@ -846,33 +850,55 @@ configure_mode_setting (GstKMSSink * self, GstVideoInfo * vinfo) GST_INFO_OBJECT (self, "configuring mode setting"); - ensure_kms_allocator (self); - kmsmem = (GstKMSMemory *) gst_kms_allocator_bo_alloc (self->allocator, vinfo); - if (!kmsmem) - goto bo_failed; - fb_id = kmsmem->fb_id; - conn = drmModeGetConnector (self->fd, self->conn_id); if (!conn) goto connector_failed; for (i = 0; i < conn->count_modes; i++) { - if (conn->modes[i].vdisplay == GST_VIDEO_INFO_HEIGHT (vinfo) && - conn->modes[i].hdisplay == GST_VIDEO_INFO_WIDTH (vinfo)) { + if (!preferred && (conn->modes[i].type & DRM_MODE_TYPE_PREFERRED)) + preferred = &conn->modes[i]; + + if (!mode && conn->modes[i].vdisplay == GST_VIDEO_INFO_HEIGHT (vinfo) && + conn->modes[i].hdisplay == GST_VIDEO_INFO_WIDTH (vinfo)) mode = &conn->modes[i]; - break; - } } + + if (preferred && (self->can_scale || !mode)) + mode = preferred; + + /* Fallback to the latest mode */ + if (!mode && conn->count_modes) + mode = &conn->modes[conn->count_modes - 1]; + if (!mode) goto mode_failed; + info = gst_video_info_new (); + gst_video_info_set_format (info, GST_VIDEO_FORMAT_BGRx, + mode->hdisplay, mode->vdisplay); + ensure_kms_allocator (self); + kmsmem = (GstKMSMemory *) gst_kms_allocator_bo_alloc (self->allocator, info); + gst_video_info_free (info); + + if (!kmsmem) + goto bo_failed; + fb_id = kmsmem->fb_id; + err = drmModeSetCrtc (self->fd, self->crtc_id, fb_id, 0, 0, (uint32_t *) & self->conn_id, 1, mode); if (err) goto modesetting_failed; + self->hdisplay = mode->hdisplay; + self->vdisplay = mode->vdisplay; + g_clear_pointer (&self->tmp_kmsmem, gst_memory_unref); self->tmp_kmsmem = (GstMemory *) kmsmem; + kmsmem = NULL; + + if (!gst_kms_sink_calculate_display_ratio (self, vinfo, + &GST_VIDEO_SINK_WIDTH (self), &GST_VIDEO_SINK_HEIGHT (self))) + goto no_disp_ratio; ret = TRUE; @@ -880,6 +906,10 @@ bail: if (conn) drmModeFreeConnector (conn); + if (kmsmem) + gst_memory_unref ((GstMemory *) kmsmem); + + self->mode_valid = ret; return ret; /* ERRORS */ @@ -904,6 +934,11 @@ modesetting_failed: GST_ERROR_OBJECT (self, "Failed to set mode: %s", g_strerror (errno)); goto bail; } +no_disp_ratio: + { + GST_ERROR_OBJECT (self, "Error calculating the output display ratio of the video."); + goto bail; + } } static void @@ -1040,7 +1075,7 @@ ensure_allowed_caps (GstKMSSink * self, drmModeConnector * conn, format = gst_video_format_to_string (fmt); - if (mode) { + if (mode && !self->can_scale) { caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, format, "width", G_TYPE_INT, mode->hdisplay, @@ -1338,6 +1373,8 @@ gst_kms_sink_start (GstBaseSink * bsink) self->modesetting_enabled = TRUE; } + self->mode_valid = !self->modesetting_enabled; + if (crtc->mode_valid && self->modesetting_enabled && self->restore_crtc) { self->saved_crtc = (drmModeCrtc *) crtc; } @@ -1376,13 +1413,6 @@ gst_kms_sink_start (GstBaseSink * bsink) self->hdisplay = crtc->mode.hdisplay; self->vdisplay = crtc->mode.vdisplay; - if (self->render_rect.w == 0 || self->render_rect.h == 0) { - self->render_rect.x = 0; - self->render_rect.y = 0; - self->render_rect.w = self->hdisplay; - self->render_rect.h = self->vdisplay; - } - self->pending_rect = self->render_rect; GST_OBJECT_UNLOCK (self); @@ -1744,9 +1774,6 @@ gst_kms_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) self->pool = NULL; } - if (self->modesetting_enabled && !configure_mode_setting (self, &vinfo)) - goto modesetting_failed; - GST_OBJECT_LOCK (self); if (self->reconfigure) { self->reconfigure = FALSE; @@ -1781,13 +1808,6 @@ no_disp_ratio: return FALSE; } -modesetting_failed: - { - GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL), - ("failed to configure video mode")); - return FALSE; - } - } static gboolean @@ -2249,6 +2269,16 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) GST_TRACE_OBJECT (self, "displaying fb %d", fb_id); GST_OBJECT_LOCK (self); + +retry_set_plane: + if (self->modesetting_enabled && !self->mode_valid) { + if (!configure_mode_setting (self, vinfo)) + goto modesetting_failed; + + src.w = GST_VIDEO_SINK_WIDTH (self); + src.h = GST_VIDEO_SINK_HEIGHT (self); + } + if ((crop = gst_buffer_get_video_crop_meta (buffer))) { GstVideoInfo cropped_vinfo = *vinfo; @@ -2263,10 +2293,9 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) src.y = crop->y; } - dst.w = self->render_rect.w; - dst.h = self->render_rect.h; + dst.w = self->render_rect.w ?: self->hdisplay; + dst.h = self->render_rect.h ?: self->vdisplay; -retry_set_plane: gst_video_sink_center_rect (src, dst, &result, self->can_scale); result.x += self->render_rect.x; @@ -2291,11 +2320,6 @@ retry_set_plane: goto sync_frame; } - /* to make sure it can be show when driver don't support scale */ - if (!self->can_scale) { - src.w = result.w; - src.h = result.h; - } #ifdef HAVE_DRM_HDR /* Send the HDR infoframes if appropriate */ gst_kms_push_hdr_infoframe (self, FALSE); @@ -2305,6 +2329,12 @@ retry_set_plane: /* The AFBC's width should align to 4 */ src.w &= ~3; + /* to make sure it can be show when driver don't support scale */ + if (!self->can_scale) { + src.w = result.w = MIN (src.w, result.w); + src.h = result.h = MIN (src.h, result.h); + } + GST_TRACE_OBJECT (self, "drmModeSetPlane at (%i,%i) %ix%i sourcing at (%i,%i) %ix%i", result.x, result.y, result.w, result.h, src.x, src.y, src.w, src.h); @@ -2315,7 +2345,17 @@ retry_set_plane: src.x << 16, src.y << 16, src.w << 16, src.h << 16); if (ret) { if (self->can_scale) { + GST_WARNING_OBJECT (self, "unable to scale on plane %d", self->plane_id); self->can_scale = FALSE; + + if (self->modesetting_enabled) { + if (!configure_mode_setting (self, vinfo)) + goto modesetting_failed; + + src.w = GST_VIDEO_SINK_WIDTH (self); + src.h = GST_VIDEO_SINK_HEIGHT (self); + } + goto retry_set_plane; } goto set_plane_failed; @@ -2335,7 +2375,9 @@ sync_frame: self->last_height = GST_VIDEO_SINK_HEIGHT (self); self->last_vinfo = self->vinfo; } - g_clear_pointer (&self->tmp_kmsmem, gst_memory_unref); + + if (self->tmp_kmsmem && self->plane_id == self->primary_plane_id) + g_clear_pointer (&self->tmp_kmsmem, gst_memory_unref); GST_OBJECT_UNLOCK (self); res = GST_FLOW_OK; @@ -2350,6 +2392,11 @@ buffer_invalid: GST_ERROR_OBJECT (self, "invalid buffer: it doesn't have a fb id"); goto bail; } +modesetting_failed: + { + GST_ERROR_OBJECT (self, "failed to configure video mode"); + goto bail; + } set_plane_failed: { GST_OBJECT_UNLOCK (self); diff --git a/sys/kms/gstkmssink.h b/sys/kms/gstkmssink.h index 5ce1e55..0d3374d 100644 --- a/sys/kms/gstkmssink.h +++ b/sys/kms/gstkmssink.h @@ -76,6 +76,7 @@ struct _GstKMSSink { gboolean can_scale; gboolean modesetting_enabled; + gboolean mode_valid; gboolean restore_crtc; GstStructure *connector_props; GstStructure *plane_props; -- 2.20.1