static void gst_kms_sink_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { GstKMSSink *sink; sink = GST_KMS_SINK (object); switch (prop_id) { case PROP_DRIVER_NAME: g_value_take_string (value, sink->devname); break; case PROP_CONNECTOR_ID: g_value_set_int (value, sink->conn_id); break; case PROP_PLANE_ID: g_value_set_int (value, sink->plane_id); break; case PROP_FORCE_MODESETTING: g_value_set_boolean (value, sink->modesetting_enabled); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_kms_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query) { GstKMSSink *self; GstCaps *caps; gboolean need_pool; GstVideoInfo vinfo; GstBufferPool *pool; gsize size; self = GST_KMS_SINK (bsink); gst_query_parse_allocation (query, &caps, &need_pool); if (!caps) goto no_caps; if (!gst_video_info_from_caps (&vinfo, caps)) goto invalid_caps; size = GST_VIDEO_INFO_SIZE (&vinfo); pool = NULL; if (need_pool) { pool = gst_kms_sink_create_pool (self, caps, size, 0); if (!pool) goto no_pool; } if (pool) { /* we need at least 2 buffer because we hold on to the last one */ gst_query_add_allocation_pool (query, pool, size, 2, 0); gst_object_unref (pool); } gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL); return TRUE; /* ERRORS */ no_caps: { GST_DEBUG_OBJECT (bsink, "no caps specified"); return FALSE; } invalid_caps: { GST_DEBUG_OBJECT (bsink, "invalid caps specified"); return FALSE; } no_pool: { /* Already warned in create_pool */ return FALSE; } }
static void gst_kms_sink_finalize (GObject * object) { GstKMSSink *sink; sink = GST_KMS_SINK (object); g_clear_pointer (&sink->devname, g_free); gst_poll_free (sink->poll); G_OBJECT_CLASS (parent_class)->finalize (object); }
static GstCaps * gst_kms_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) { GstKMSSink *self; GstCaps *caps, *out_caps; self = GST_KMS_SINK (bsink); caps = gst_kms_sink_get_allowed_caps (self); if (caps && filter) { out_caps = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST); gst_caps_unref (caps); } else { out_caps = caps; } return out_caps; }
static void gst_kms_sink_get_times (GstBaseSink * bsink, GstBuffer * buf, GstClockTime * start, GstClockTime * end) { GstKMSSink *self; self = GST_KMS_SINK (bsink); if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) { *start = GST_BUFFER_TIMESTAMP (buf); if (GST_BUFFER_DURATION_IS_VALID (buf)) *end = *start + GST_BUFFER_DURATION (buf); else { if (GST_VIDEO_INFO_FPS_N (&self->vinfo) > 0) { *end = *start + gst_util_uint64_scale_int (GST_SECOND, GST_VIDEO_INFO_FPS_D (&self->vinfo), GST_VIDEO_INFO_FPS_N (&self->vinfo)); } } } }
static gboolean gst_kms_sink_stop (GstBaseSink * bsink) { GstKMSSink *self; self = GST_KMS_SINK (bsink); gst_buffer_replace (&self->last_buffer, NULL); gst_caps_replace (&self->allowed_caps, NULL); gst_object_replace ((GstObject **) & self->pool, NULL); gst_object_replace ((GstObject **) & self->allocator, NULL); gst_poll_remove_fd (self->poll, &self->pollfd); gst_poll_restart (self->poll); gst_poll_fd_init (&self->pollfd); if (self->fd >= 0) { drmClose (self->fd); self->fd = -1; } return TRUE; }
static void gst_kms_sink_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { GstKMSSink *sink; sink = GST_KMS_SINK (object); switch (prop_id) { case PROP_DRIVER_NAME: sink->devname = g_value_dup_string (value); break; case PROP_CONNECTOR_ID: sink->conn_id = g_value_get_int (value); break; case PROP_PLANE_ID: sink->plane_id = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } }
static gboolean gst_kms_sink_set_caps (GstBaseSink * bsink, GstCaps * caps) { GstKMSSink *self; GstVideoInfo vinfo; GstBufferPool *newpool, *oldpool; self = GST_KMS_SINK (bsink); if (!gst_video_info_from_caps (&vinfo, caps)) goto invalid_format; if (!gst_kms_sink_calculate_display_ratio (self, &vinfo)) goto no_disp_ratio; if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0) goto invalid_size; /* create a new pool for the new configuration */ newpool = gst_kms_sink_create_pool (self, caps, GST_VIDEO_INFO_SIZE (&vinfo), 2); if (!newpool) goto no_pool; /* we don't activate the internal pool yet as it may not be needed */ oldpool = self->pool; self->pool = newpool; if (oldpool) { gst_buffer_pool_set_active (oldpool, FALSE); gst_object_unref (oldpool); } self->vinfo = vinfo; GST_DEBUG_OBJECT (self, "negotiated caps = %" GST_PTR_FORMAT, caps); return TRUE; /* ERRORS */ invalid_format: { GST_ERROR_OBJECT (self, "caps invalid"); return FALSE; } invalid_size: { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL), ("Invalid image size.")); return FALSE; } no_disp_ratio: { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL), ("Error calculating the output display ratio of the video.")); return FALSE; } no_pool: { /* Already warned in create_pool */ return FALSE; } }
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->fd = drmOpen (self->devname, NULL); else self->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; if (self->conn_id == -1) conn = find_main_monitor (self->fd, res); else conn = drmModeGetConnector (self->fd, self->conn_id); if (!conn) goto connector_failed; crtc = find_crtc_for_connector (self->fd, res, conn, &self->pipe); if (!crtc) goto crtc_failed; retry_find_plane: if (universal_planes && drmSetClientCap (self->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) goto set_cap_failed; pres = 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); else plane = drmModeGetPlane (self->fd, self->plane_id); if (!plane) goto plane_failed; /* let's get the available color formats in plane */ if (!ensure_allowed_caps (self, plane, res)) goto bail; 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); self->hdisplay = crtc->mode.hdisplay; self->vdisplay = crtc->mode.vdisplay; 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); ret = TRUE; bail: if (plane) drmModeFreePlane (plane); if (pres) drmModeFreePlaneResources (pres); if (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_ERROR_OBJECT (self, "Could not open DRM module %s: %s", GST_STR_NULL (self->devname), strerror (errno)); return FALSE; } resources_failed: { GST_ERROR_OBJECT (self, "drmModeGetResources failed: %s (%d)", strerror (errno), errno); goto bail; } connector_failed: { GST_ERROR_OBJECT (self, "Could not find a valid monitor connector"); goto bail; } crtc_failed: { GST_ERROR_OBJECT (self, "Could not find a crtc for connector"); goto bail; } set_cap_failed: { GST_ERROR_OBJECT (self, "Could not set universal planes capability bit"); goto bail; } plane_resources_failed: { GST_ERROR_OBJECT (self, "drmModeGetPlaneResources failed: %s (%d)", strerror (errno), errno); goto bail; } plane_failed: { if (universal_planes) { GST_ERROR_OBJECT (self, "Could not find a plane for crtc"); goto bail; } else { universal_planes = TRUE; goto retry_find_plane; } } }
static GstFlowReturn gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) { gint ret; GstBuffer *buffer; guint32 fb_id; GstKMSSink *self; GstVideoCropMeta *crop; GstVideoRectangle src = { 0, }; GstVideoRectangle dst = { 0, }; GstVideoRectangle result; GstFlowReturn res; self = GST_KMS_SINK (vsink); res = GST_FLOW_ERROR; buffer = gst_kms_sink_get_input_buffer (self, buf); 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); { if ((crop = gst_buffer_get_video_crop_meta (buffer))) { src.x = crop->x; src.y = crop->y; src.w = crop->width; src.h = crop->height; } else { src.w = GST_VIDEO_SINK_WIDTH (self); src.h = GST_VIDEO_SINK_HEIGHT (self); } } dst.w = self->hdisplay; dst.h = self->vdisplay; gst_video_sink_center_rect (src, dst, &result, FALSE); /* if the frame size is bigger than the display size, the source * must be the display size */ src.w = MIN (src.w, self->hdisplay); src.h = MIN (src.h, self->vdisplay); 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) goto set_plane_failed; /* Wait for the previous frame to complete redraw */ if (!gst_kms_sink_sync (self)) goto bail; gst_buffer_replace (&self->last_buffer, buffer); 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_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)", strerror (-ret), ret)); goto bail; } }
static GstFlowReturn gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf) { gint ret; GstBuffer *buffer; guint32 fb_id; GstKMSSink *self; GstVideoCropMeta *crop; GstVideoRectangle src = { 0, }; GstVideoRectangle dst = { 0, }; GstVideoRectangle result; GstFlowReturn res; self = GST_KMS_SINK (vsink); res = GST_FLOW_ERROR; buffer = gst_kms_sink_get_input_buffer (self, buf); 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); if (self->modesetting_enabled) { self->buffer_id = fb_id; goto sync_frame; } if ((crop = gst_buffer_get_video_crop_meta (buffer))) { GstVideoInfo vinfo = self->vinfo; vinfo.width = crop->width; vinfo.height = crop->height; if (!gst_kms_sink_calculate_display_ratio (self, &vinfo)) goto no_disp_ratio; src.x = crop->x; src.y = crop->y; } src.w = GST_VIDEO_SINK_WIDTH (self); src.h = GST_VIDEO_SINK_HEIGHT (self); dst.w = self->hdisplay; dst.h = self->vdisplay; gst_video_sink_center_rect (src, dst, &result, TRUE); if (crop) { src.w = crop->width; src.h = crop->height; } else { src.w = GST_VIDEO_INFO_WIDTH (&self->vinfo); src.h = GST_VIDEO_INFO_HEIGHT (&self->vinfo); } 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) goto set_plane_failed; sync_frame: /* Wait for the previous frame to complete redraw */ if (!gst_kms_sink_sync (self)) goto bail; gst_buffer_replace (&self->last_buffer, buffer); g_clear_pointer (&self->tmp_kmsmem, gst_memory_unref); 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_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)", strerror (-ret), ret)); goto bail; } no_disp_ratio: { GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL), ("Error calculating the output display ratio of the video.")); goto bail; } }