bus-id :如果您的系统包含多个使用相同驱动程序名称的显示器,您可以通过设置 DRM 总线 ID 来选择使用哪个显示器。否则,驱动程序将决定使用哪个显示器。can-scale: 用户可以告诉 kmssink 驱动程序是否可以支持缩放。connector-id: GPU 有多个输出连接器,例如:LVDS、VGA、HDMI 等。默认情况下,先尝试第一个 LVDS,然后是第一个 eDP,最后是第一个连接的。connector-properties: 连接器的附加属性。键是字符串,值为无符号 64 位整数。display-height: 显示屏的实际高度。此值为只读,且仅在“暂停”和“播放”状态下可用。该值应与 gst_video_overlay_set_render_rectangle函数配合使用。display-width:显示屏的实际宽度。此参数为只读,且仅在 PAUSED 和 PLAYING 状态下可用。该参数旨在与 gst_video_overlay_set_render_rectangle函数配合使用。driver-name:如果您的系统包含多个 GPU,您可以通过设置 DRM 设备驱动程序名称来选择要使用的 GPU。否则,系统将使用内部列表中的第一个 GPU。fd:您可以提供自己的 DRM 文件描述符。默认情况下,接收器将打开其自己的 DRM 文件描述符。force-modesetting:如果输出连接器已处于活动状态,则接收器会自动使用覆盖平面。请在 kms 接收器和输出到基础平面中设置强制模式,以覆盖自动行为。plane-id:一个 CRTC 可能关联多个平面。默认情况下,系统会尝试第一个可能与给定 CRTC 配合使用的平面。plane-properties :平面的附加属性。键为字符串,值为无符号 64 位整数。render-rectangle:渲染矩形('<x, y, width, height>')restore-crtc:如果强制设置了新的 CRTC 模式,则恢复之前的 CRTC 设置。默认情况下,如果用户在已激活且具有有效模式的 CRTC 上设置了新模式,则启用此功能。skip-vsync:在某些情况下,为了抑制内部垂直同步(这会使帧速率降低一半),请将其设置为 1。
static gboolean gst_kms_sink_start(GstBaseSink *bsink)
{GstKMSSink *self;drmModeRes *res;drmModeConnector *conn;drmModeCrtc *crtc;drmModePlaneRes *pres;drmModePlane *plane;gboolean universal_planes;gboolean ret;self = GST_KMS_SINK(bsink);universal_planes = FALSE;ret = FALSE;res = NULL;conn = NULL;crtc = NULL;pres = NULL;plane = NULL;// 打开设备if (self->devname || self->bus_id)self->fd = drmOpen(self->devname, self->bus_id);elseself->fd = kms_open(&self->devname);if (self->fd < 0)goto open_failed;// 显示版本log_drm_version(self);if (!get_drm_caps(self))goto bail;// 获取资源信息res = drmModeGetResources(self->fd);if (!res)goto resources_failed;// 找到outputif (self->conn_id == -1)conn = find_main_monitor(self->fd, res);elseconn = drmModeGetConnector(self->fd, self->conn_id);if (!conn)goto connector_failed;// 找到CRTCcrtc = find_crtc_for_connector(self->fd, res, conn, &self->pipe);if (!crtc)goto crtc_failed;if (!crtc->mode_valid || self->modesetting_enabled) {GST_DEBUG_OBJECT(self, "enabling modesetting");self->modesetting_enabled = TRUE;universal_planes = TRUE;}if (crtc->mode_valid && self->modesetting_enabled && self->restore_crtc) {self->saved_crtc = (drmModeCrtc *)crtc;}retry_find_plane:if (universal_planes && drmSetClientCap(self->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1))goto set_cap_failed;// 获取所有的Planepres = drmModeGetPlaneResources(self->fd);if (!pres)goto plane_resources_failed;if (self->plane_id == -1)plane = find_plane_for_crtc(self->fd, res, pres, crtc->crtc_id);elseplane = drmModeGetPlane(self->fd, self->plane_id);if (!plane)goto plane_failed;if (!ensure_allowed_caps(self, conn, plane, res))goto allowed_caps_failed;self->conn_id = conn->connector_id;self->crtc_id = crtc->crtc_id;self->plane_id = plane->plane_id;GST_INFO_OBJECT(self, "connector id = %d / crtc id = %d / plane id = %d", self->conn_id, self->crtc_id, self->plane_id);GST_OBJECT_LOCK(self);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);self->buffer_id = crtc->buffer_id;self->mm_width = conn->mmWidth;self->mm_height = conn->mmHeight;GST_INFO_OBJECT(self, "display size: pixels = %dx%d / millimeters = %dx%d", self->hdisplay, self->vdisplay, self->mm_width, self->mm_height);self->pollfd.fd = self->fd;gst_poll_add_fd(self->poll, &self->pollfd);gst_poll_fd_ctl_read(self->poll, &self->pollfd, TRUE);g_object_notify_by_pspec(G_OBJECT(self), g_properties[PROP_DISPLAY_WIDTH]);g_object_notify_by_pspec(G_OBJECT(self), g_properties[PROP_DISPLAY_HEIGHT]);gst_kms_sink_update_connector_properties(self);gst_kms_sink_update_plane_properties(self);ret = TRUE;
bail:if (plane)drmModeFreePlane(plane);if (pres)drmModeFreePlaneResources(pres);if (crtc != self->saved_crtc)drmModeFreeCrtc(crtc);if (conn)drmModeFreeConnector(conn);if (res)drmModeFreeResources(res);if (!ret && self->fd >= 0) {drmClose(self->fd);self->fd = -1;}return ret;/* ERRORS */
open_failed: {GST_ELEMENT_ERROR(self, RESOURCE, OPEN_READ_WRITE, ("Could not open DRM module %s", GST_STR_NULL(self->devname)),("reason: %s (%d)", g_strerror(errno), errno));return FALSE;
}resources_failed: {GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, ("drmModeGetResources failed"), ("reason: %s (%d)", g_strerror(errno), errno));goto bail;
}connector_failed: {GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, ("Could not find a valid monitor connector"), (NULL));goto bail;
}crtc_failed: {GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, ("Could not find a crtc for connector"), (NULL));goto bail;
}set_cap_failed: {GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, ("Could not set universal planes capability bit"), (NULL));goto bail;
}plane_resources_failed: {GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, ("drmModeGetPlaneResources failed"),("reason: %s (%d)", g_strerror(errno), errno));goto bail;
}plane_failed: {if (universal_planes) {GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, ("Could not find a plane for crtc"), (NULL));goto bail;} else {universal_planes = TRUE;goto retry_find_plane;}
}allowed_caps_failed: {GST_ELEMENT_ERROR(self, RESOURCE, SETTINGS, ("Could not get allowed GstCaps of device"),("driver does not provide mode settings configuration"));goto bail;
}
} static GstFlowReturn gst_kms_sink_show_frame(GstVideoSink *vsink, GstBuffer *buf)
{gint ret;GstBuffer *buffer = NULL;guint32 fb_id;GstKMSSink *self;GstVideoInfo *vinfo;GstVideoCropMeta *crop;GstVideoRectangle src = {0,};gint video_width, video_height;GstVideoRectangle dst = {0,};GstVideoRectangle result;GstFlowReturn res;self = GST_KMS_SINK(vsink);res = GST_FLOW_ERROR;if (buf) {buffer = gst_kms_sink_get_input_buffer(self, buf);vinfo = &self->vinfo;video_width = src.w = GST_VIDEO_SINK_WIDTH(self);video_height = src.h = GST_VIDEO_SINK_HEIGHT(self);} else if (self->last_buffer) {buffer = gst_buffer_ref(self->last_buffer);vinfo = &self->last_vinfo;video_width = src.w = self->last_width;video_height = src.h = self->last_height;}// buf确定已经转换成了bufferbuf = NULL;if (!buffer)return GST_FLOW_ERROR;fb_id = gst_kms_memory_get_fb_id(gst_buffer_peek_memory(buffer, 0));if (fb_id == 0)goto buffer_invalid;GST_TRACE_OBJECT(self, "displaying fb %d", fb_id);GST_OBJECT_LOCK(self);if (self->modesetting_enabled) {self->buffer_id = fb_id;goto sync_frame;}// 缩放因子和位置if ((crop = gst_buffer_get_video_crop_meta(buffer))) {GstVideoInfo cropped_vinfo = *vinfo;cropped_vinfo.width = crop->width;cropped_vinfo.height = crop->height;if (!gst_kms_sink_calculate_display_ratio(self, &cropped_vinfo, &src.w, &src.h))goto no_disp_ratio;src.x = crop->x;src.y = crop->y;}dst.w = self->render_rect.w;dst.h = self->render_rect.h;retry_set_plane:// 转换gst_video_sink_center_rect(src, dst, &result, self->can_scale);result.x += self->render_rect.x;result.y += self->render_rect.y;if (crop) {src.w = crop->width;src.h = crop->height;} else {src.w = video_width;src.h = video_height;}// 处理超出屏幕情况if ((result.x + result.w) > self->hdisplay)result.w = self->hdisplay - result.x;if ((result.y + result.h) > self->vdisplay)result.h = self->vdisplay - result.y;if (result.w <= 0 || result.h <= 0) {GST_WARNING_OBJECT(self, "video is out of display range");goto sync_frame;}// 当驱动不支持缩放时不缩放if (!self->can_scale) {src.w = result.w;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);ret = drmModeSetPlane(self->fd, self->plane_id, self->crtc_id, fb_id, 0, result.x, result.y, result.w, result.h,/* source/cropping coordinates are given in Q16 */src.x << 16, src.y << 16, src.w << 16, src.h << 16);if (ret) {if (self->can_scale) {self->can_scale = FALSE;goto retry_set_plane;}goto set_plane_failed;}sync_frame:// 等待之前帧完成重绘if (!gst_kms_sink_sync(self)) {GST_OBJECT_UNLOCK(self);goto bail;}// 保存渲染buf到上一帧last_bufferif (buffer != self->last_buffer) {gst_buffer_replace(&self->last_buffer, buffer);self->last_width = GST_VIDEO_SINK_WIDTH(self);self->last_height = GST_VIDEO_SINK_HEIGHT(self);self->last_vinfo = self->vinfo;}g_clear_pointer(&self->tmp_kmsmem, gst_memory_unref);GST_OBJECT_UNLOCK(self);res = GST_FLOW_OK;bail:gst_buffer_unref(buffer);return res;/* ERRORS */
buffer_invalid: {GST_ERROR_OBJECT(self, "invalid buffer: it doesn't have a fb id");goto bail;
}
set_plane_failed: {GST_OBJECT_UNLOCK(self);GST_DEBUG_OBJECT(self,"result = { %d, %d, %d, %d} / ""src = { %d, %d, %d %d } / dst = { %d, %d, %d %d }",result.x, result.y, result.w, result.h, src.x, src.y, src.w, src.h, dst.x, dst.y, dst.w, dst.h);GST_ELEMENT_ERROR(self, RESOURCE, FAILED, (NULL), ("drmModeSetPlane failed: %s (%d)", g_strerror(errno), errno));goto bail;
}
no_disp_ratio: {GST_OBJECT_UNLOCK(self);GST_ELEMENT_ERROR(self, CORE, NEGOTIATION, (NULL), ("Error calculating the output display ratio of the video."));goto bail;
}
}