kmssink: wait for page flip or vblank

This patch requests for drmModePageFlip() for the used CRTC, if the kernel
module suppports async page flip. If it does not, the element requests for a
vblank event. A GstPoll waits for the event to happen.

https://bugzilla.gnome.org/show_bug.cgi?id=761059
This commit is contained in:
Víctor Manuel Jáquez Leal 2016-02-20 23:13:54 +01:00
parent c419d17dbf
commit b29f7d048c
2 changed files with 115 additions and 8 deletions

View file

@ -261,6 +261,7 @@ get_drm_caps (GstKMSSink * self)
gint ret;
guint64 has_dumb_buffer;
guint64 has_prime;
guint64 has_async_page_flip;
has_dumb_buffer = 0;
ret = drmGetCap (self->fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer);
@ -278,8 +279,16 @@ get_drm_caps (GstKMSSink * self)
else
self->has_prime_import = (gboolean) (has_prime & DRM_PRIME_CAP_IMPORT);
GST_INFO_OBJECT (self, "prime import (%s)",
self->has_prime_import ? "" : "");
has_async_page_flip = 0;
ret = drmGetCap (self->fd, DRM_CAP_ASYNC_PAGE_FLIP, &has_async_page_flip);
if (ret)
GST_WARNING_OBJECT (self, "could not get async page flip capability");
else
self->has_async_page_flip = (gboolean) has_async_page_flip;
GST_INFO_OBJECT (self, "prime import (%s) / async page flip (%s)",
self->has_prime_import ? "" : "",
self->has_async_page_flip ? "" : "");
return TRUE;
}
@ -392,6 +401,7 @@ gst_kms_sink_start (GstBaseSink * bsink)
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;
@ -399,6 +409,10 @@ gst_kms_sink_start (GstBaseSink * bsink)
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:
@ -472,6 +486,10 @@ gst_kms_sink_stop (GstBaseSink * bsink)
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;
@ -748,6 +766,78 @@ gst_kms_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
}
}
static void
sync_handler (gint fd, guint frame, guint sec, guint usec, gpointer data)
{
gboolean *waiting;
waiting = data;
*waiting = FALSE;
}
static gboolean
gst_kms_sink_sync (GstKMSSink * self)
{
gint ret;
gboolean waiting;
drmEventContext evctxt = {
.version = DRM_EVENT_CONTEXT_VERSION,
.page_flip_handler = sync_handler,
.vblank_handler = sync_handler,
};
drmVBlank vbl = {
.request = {
.type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
.sequence = 1,
.signal = (gulong) & waiting,
},
};
waiting = TRUE;
if (!self->has_async_page_flip) {
ret = drmWaitVBlank (self->fd, &vbl);
if (ret)
goto vblank_failed;
} else {
ret = drmModePageFlip (self->fd, self->crtc_id, self->buffer_id,
DRM_MODE_PAGE_FLIP_EVENT, &waiting);
if (ret)
goto pageflip_failed;
}
while (waiting) {
do {
ret = gst_poll_wait (self->poll, 3 * GST_SECOND);
} while (ret == -1 && (errno == EAGAIN || errno == EINTR));
ret = drmHandleEvent (self->fd, &evctxt);
if (ret)
goto event_failed;
}
return TRUE;
/* ERRORS */
vblank_failed:
{
GST_WARNING_OBJECT (self, "drmWaitVBlank failed: %s (%d)", strerror (-ret),
ret);
return FALSE;
}
pageflip_failed:
{
GST_WARNING_OBJECT (self, "drmModePageFlip failed: %s (%d)",
strerror (-ret), ret);
return FALSE;
}
event_failed:
{
GST_ERROR_OBJECT (self, "drmHandleEvent failed: %s (%d)", strerror (-ret),
ret);
return FALSE;
}
}
static GstMemory *
get_cached_kmsmem (GstMemory * mem)
{
@ -961,9 +1051,12 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
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;
@ -999,19 +1092,24 @@ gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
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);
gst_buffer_unref (buffer);
if (ret)
goto set_plane_failed;
return GST_FLOW_OK;
/* Wait for the previous frame to complete redraw */
if (!gst_kms_sink_sync (self))
goto bail;
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");
return GST_FLOW_ERROR;
goto bail;
}
set_plane_failed:
{
@ -1021,7 +1119,7 @@ set_plane_failed:
dst.h);
GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
(NULL), ("drmModeSetPlane failed: %s (%d)", strerror (-ret), ret));
return GST_FLOW_ERROR;
goto bail;
}
}
@ -1074,6 +1172,7 @@ gst_kms_sink_finalize (GObject * object)
sink = GST_KMS_SINK (object);
g_clear_pointer (&sink->devname, g_free);
gst_poll_free (sink->poll);
}
static void
@ -1081,6 +1180,8 @@ gst_kms_sink_init (GstKMSSink * sink)
{
sink->fd = -1;
sink->conn_id = -1;
gst_poll_fd_init (&sink->pollfd);
sink->poll = gst_poll_new (TRUE);
gst_video_info_init (&sink->vinfo);
}

View file

@ -53,10 +53,13 @@ struct _GstKMSSink {
gint crtc_id;
gint plane_id;
/* crtc data */
guint16 hdisplay, vdisplay;
guint32 buffer_id;
/* capabilities */
gboolean has_prime_import;
gboolean has_async_page_flip;
GstVideoInfo vinfo;
GstCaps *allowed_caps;
@ -66,6 +69,9 @@ struct _GstKMSSink {
gchar *devname;
guint32 mm_width, mm_height;
GstPoll *poll;
GstPollFD pollfd;
};
struct _GstKMSSinkClass {