kmssink: add fd property
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / sys / kms / gstkmssink.c
1 /* GStreamer
2  *
3  * Copyright (C) 2016 Igalia
4  *
5  * Authors:
6  *  Víctor Manuel Jáquez Leal <vjaquez@igalia.com>
7  *  Javier Martin <javiermartin@by.com.es>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25
26 /**
27  * SECTION:element-kmssink
28  * @title: kmssink
29  * @short_description: A KMS/DRM based video sink
30  *
31  * kmssink is a simple video sink that renders video frames directly
32  * in a plane of a DRM device.
33  *
34  * In advance usage, the behaviour of kmssink can be change using the
35  * supported properties. Note that plane and connectors IDs and properties can
36  * be enumerated using the modetest command line tool.
37  *
38  * ## Example launch line
39  * |[
40  * gst-launch-1.0 videotestsrc ! kmssink
41  * gst-launch-1.0 videotestsrc ! kmssink plane-properties=s,rotation=4
42  * ]|
43  *
44  */
45
46 #ifdef HAVE_CONFIG_H
47 #include "config.h"
48 #endif
49
50 #include <gst/video/video.h>
51 #include <gst/video/videooverlay.h>
52 #include <gst/allocators/gstdmabuf.h>
53
54 #include <drm.h>
55 #include <xf86drm.h>
56 #include <xf86drmMode.h>
57 #include <drm_fourcc.h>
58 #include <string.h>
59
60 #include "gstkmssink.h"
61 #include "gstkmsutils.h"
62 #include "gstkmsbufferpool.h"
63 #include "gstkmsallocator.h"
64
65 #define GST_PLUGIN_NAME "kmssink"
66 #define GST_PLUGIN_DESC "Video sink using the Linux kernel mode setting API"
67
68 GST_DEBUG_CATEGORY_STATIC (gst_kms_sink_debug);
69 GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE);
70 #define GST_CAT_DEFAULT gst_kms_sink_debug
71
72 static GstFlowReturn gst_kms_sink_show_frame (GstVideoSink * vsink,
73     GstBuffer * buf);
74 static void gst_kms_sink_video_overlay_init (GstVideoOverlayInterface * iface);
75 static void gst_kms_sink_drain (GstKMSSink * self);
76
77 #define parent_class gst_kms_sink_parent_class
78 G_DEFINE_TYPE_WITH_CODE (GstKMSSink, gst_kms_sink, GST_TYPE_VIDEO_SINK,
79     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_PLUGIN_NAME, 0,
80         GST_PLUGIN_DESC);
81     GST_DEBUG_CATEGORY_GET (CAT_PERFORMANCE, "GST_PERFORMANCE");
82     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
83         gst_kms_sink_video_overlay_init));
84 GST_ELEMENT_REGISTER_DEFINE (kmssink, GST_PLUGIN_NAME, GST_RANK_SECONDARY,
85     GST_TYPE_KMS_SINK);
86
87 enum
88 {
89   PROP_DRIVER_NAME = 1,
90   PROP_BUS_ID,
91   PROP_CONNECTOR_ID,
92   PROP_PLANE_ID,
93   PROP_FORCE_MODESETTING,
94   PROP_RESTORE_CRTC,
95   PROP_CAN_SCALE,
96   PROP_DISPLAY_WIDTH,
97   PROP_DISPLAY_HEIGHT,
98   PROP_CONNECTOR_PROPS,
99   PROP_PLANE_PROPS,
100   PROP_FD,
101   PROP_N,
102 };
103
104 static GParamSpec *g_properties[PROP_N] = { NULL, };
105
106 static void
107 gst_kms_sink_set_render_rectangle (GstVideoOverlay * overlay,
108     gint x, gint y, gint width, gint height)
109 {
110   GstKMSSink *self = GST_KMS_SINK (overlay);
111
112   GST_DEBUG_OBJECT (self, "Setting render rectangle to (%d,%d) %dx%d", x, y,
113       width, height);
114
115   GST_OBJECT_LOCK (self);
116
117   if (width == -1 && height == -1) {
118     x = 0;
119     y = 0;
120     width = self->hdisplay;
121     height = self->vdisplay;
122   }
123
124   if (width <= 0 || height <= 0)
125     goto done;
126
127   self->pending_rect.x = x;
128   self->pending_rect.y = y;
129   self->pending_rect.w = width;
130   self->pending_rect.h = height;
131
132   if (self->can_scale ||
133       (self->render_rect.w == width && self->render_rect.h == height)) {
134     self->render_rect = self->pending_rect;
135   } else {
136     self->reconfigure = TRUE;
137     GST_DEBUG_OBJECT (self, "Waiting for new caps to apply render rectangle");
138   }
139
140 done:
141   GST_OBJECT_UNLOCK (self);
142 }
143
144 static void
145 gst_kms_sink_expose (GstVideoOverlay * overlay)
146 {
147   GstKMSSink *self = GST_KMS_SINK (overlay);
148
149   GST_DEBUG_OBJECT (overlay, "Expose called by application");
150
151   if (!self->can_scale) {
152     GST_OBJECT_LOCK (self);
153     if (self->reconfigure) {
154       GST_OBJECT_UNLOCK (self);
155       GST_DEBUG_OBJECT (overlay, "Sending a reconfigure event");
156       gst_pad_push_event (GST_BASE_SINK_PAD (self),
157           gst_event_new_reconfigure ());
158     } else {
159       GST_DEBUG_OBJECT (overlay, "Applying new render rectangle");
160       /* size of the rectangle does not change, only the (x,y) position changes */
161       self->render_rect = self->pending_rect;
162       GST_OBJECT_UNLOCK (self);
163     }
164   }
165
166   gst_kms_sink_show_frame (GST_VIDEO_SINK (self), NULL);
167 }
168
169 static void
170 gst_kms_sink_video_overlay_init (GstVideoOverlayInterface * iface)
171 {
172   iface->expose = gst_kms_sink_expose;
173   iface->set_render_rectangle = gst_kms_sink_set_render_rectangle;
174 }
175
176 static int
177 kms_open (gchar ** driver)
178 {
179   static const char *drivers[] = { "i915", "radeon", "nouveau", "vmwgfx",
180     "exynos", "amdgpu", "imx-drm", "rockchip", "atmel-hlcdc", "msm",
181     "xlnx", "vc4", "meson", "sun4i-drm", "mxsfb-drm", "tegra",
182     "xilinx_drm",               /* DEPRECATED. Replaced by xlnx */
183   };
184   int i, fd = -1;
185
186   for (i = 0; i < G_N_ELEMENTS (drivers); i++) {
187     fd = drmOpen (drivers[i], NULL);
188     if (fd >= 0) {
189       if (driver)
190         *driver = g_strdup (drivers[i]);
191       break;
192     }
193   }
194
195   return fd;
196 }
197
198 static drmModePlane *
199 find_plane_for_crtc (int fd, drmModeRes * res, drmModePlaneRes * pres,
200     int crtc_id)
201 {
202   drmModePlane *plane;
203   int i, pipe;
204
205   plane = NULL;
206   pipe = -1;
207   for (i = 0; i < res->count_crtcs; i++) {
208     if (crtc_id == res->crtcs[i]) {
209       pipe = i;
210       break;
211     }
212   }
213
214   if (pipe == -1)
215     return NULL;
216
217   for (i = 0; i < pres->count_planes; i++) {
218     plane = drmModeGetPlane (fd, pres->planes[i]);
219     if (plane->possible_crtcs & (1 << pipe))
220       return plane;
221     drmModeFreePlane (plane);
222   }
223
224   return NULL;
225 }
226
227 static drmModeCrtc *
228 find_crtc_for_connector (int fd, drmModeRes * res, drmModeConnector * conn,
229     guint * pipe)
230 {
231   int i;
232   int crtc_id;
233   drmModeEncoder *enc;
234   drmModeCrtc *crtc;
235   guint32 crtcs_for_connector = 0;
236
237   crtc_id = -1;
238   for (i = 0; i < res->count_encoders; i++) {
239     enc = drmModeGetEncoder (fd, res->encoders[i]);
240     if (enc) {
241       if (enc->encoder_id == conn->encoder_id) {
242         crtc_id = enc->crtc_id;
243         drmModeFreeEncoder (enc);
244         break;
245       }
246       drmModeFreeEncoder (enc);
247     }
248   }
249
250   /* If no active crtc was found, pick the first possible crtc */
251   if (crtc_id == -1) {
252     for (i = 0; i < conn->count_encoders; i++) {
253       enc = drmModeGetEncoder (fd, conn->encoders[i]);
254       crtcs_for_connector |= enc->possible_crtcs;
255       drmModeFreeEncoder (enc);
256     }
257
258     if (crtcs_for_connector != 0)
259       crtc_id = res->crtcs[ffs (crtcs_for_connector) - 1];
260   }
261
262   if (crtc_id == -1)
263     return NULL;
264
265   for (i = 0; i < res->count_crtcs; i++) {
266     crtc = drmModeGetCrtc (fd, res->crtcs[i]);
267     if (crtc) {
268       if (crtc_id == crtc->crtc_id) {
269         if (pipe)
270           *pipe = i;
271         return crtc;
272       }
273       drmModeFreeCrtc (crtc);
274     }
275   }
276
277   return NULL;
278 }
279
280 static gboolean
281 connector_is_used (int fd, drmModeRes * res, drmModeConnector * conn)
282 {
283   gboolean result;
284   drmModeCrtc *crtc;
285
286   result = FALSE;
287   crtc = find_crtc_for_connector (fd, res, conn, NULL);
288   if (crtc) {
289     result = crtc->buffer_id != 0;
290     drmModeFreeCrtc (crtc);
291   }
292
293   return result;
294 }
295
296 static drmModeConnector *
297 find_used_connector_by_type (int fd, drmModeRes * res, int type)
298 {
299   int i;
300   drmModeConnector *conn;
301
302   conn = NULL;
303   for (i = 0; i < res->count_connectors; i++) {
304     conn = drmModeGetConnector (fd, res->connectors[i]);
305     if (conn) {
306       if ((conn->connector_type == type) && connector_is_used (fd, res, conn))
307         return conn;
308       drmModeFreeConnector (conn);
309     }
310   }
311
312   return NULL;
313 }
314
315 static drmModeConnector *
316 find_first_used_connector (int fd, drmModeRes * res)
317 {
318   int i;
319   drmModeConnector *conn;
320
321   conn = NULL;
322   for (i = 0; i < res->count_connectors; i++) {
323     conn = drmModeGetConnector (fd, res->connectors[i]);
324     if (conn) {
325       if (connector_is_used (fd, res, conn))
326         return conn;
327       drmModeFreeConnector (conn);
328     }
329   }
330
331   return NULL;
332 }
333
334 static drmModeConnector *
335 find_main_monitor (int fd, drmModeRes * res)
336 {
337   /* Find the LVDS and eDP connectors: those are the main screens. */
338   static const int priority[] = { DRM_MODE_CONNECTOR_LVDS,
339     DRM_MODE_CONNECTOR_eDP
340   };
341   int i;
342   drmModeConnector *conn;
343
344   conn = NULL;
345   for (i = 0; !conn && i < G_N_ELEMENTS (priority); i++)
346     conn = find_used_connector_by_type (fd, res, priority[i]);
347
348   /* if we didn't find a connector, grab the first one in use */
349   if (!conn)
350     conn = find_first_used_connector (fd, res);
351
352   /* if no connector is used, grab the first one */
353   if (!conn)
354     conn = drmModeGetConnector (fd, res->connectors[0]);
355
356   return conn;
357 }
358
359 static void
360 log_drm_version (GstKMSSink * self)
361 {
362 #ifndef GST_DISABLE_GST_DEBUG
363   drmVersion *v;
364
365   v = drmGetVersion (self->fd);
366   if (v) {
367     GST_INFO_OBJECT (self, "DRM v%d.%d.%d [%s — %s — %s]", v->version_major,
368         v->version_minor, v->version_patchlevel, GST_STR_NULL (v->name),
369         GST_STR_NULL (v->desc), GST_STR_NULL (v->date));
370     drmFreeVersion (v);
371   } else {
372     GST_WARNING_OBJECT (self, "could not get driver information: %s",
373         GST_STR_NULL (self->devname));
374   }
375 #endif
376   return;
377 }
378
379 static gboolean
380 get_drm_caps (GstKMSSink * self)
381 {
382   gint ret;
383   guint64 has_dumb_buffer;
384   guint64 has_prime;
385   guint64 has_async_page_flip;
386
387   has_dumb_buffer = 0;
388   ret = drmGetCap (self->fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer);
389   if (ret)
390     GST_WARNING_OBJECT (self, "could not get dumb buffer capability");
391   if (has_dumb_buffer == 0) {
392     GST_ERROR_OBJECT (self, "driver cannot handle dumb buffers");
393     return FALSE;
394   }
395
396   has_prime = 0;
397   ret = drmGetCap (self->fd, DRM_CAP_PRIME, &has_prime);
398   if (ret)
399     GST_WARNING_OBJECT (self, "could not get prime capability");
400   else {
401     self->has_prime_import = (gboolean) (has_prime & DRM_PRIME_CAP_IMPORT);
402     self->has_prime_export = (gboolean) (has_prime & DRM_PRIME_CAP_EXPORT);
403   }
404
405   has_async_page_flip = 0;
406   ret = drmGetCap (self->fd, DRM_CAP_ASYNC_PAGE_FLIP, &has_async_page_flip);
407   if (ret)
408     GST_WARNING_OBJECT (self, "could not get async page flip capability");
409   else
410     self->has_async_page_flip = (gboolean) has_async_page_flip;
411
412   GST_INFO_OBJECT (self,
413       "prime import (%s) / prime export (%s) / async page flip (%s)",
414       self->has_prime_import ? "✓" : "✗",
415       self->has_prime_export ? "✓" : "✗",
416       self->has_async_page_flip ? "✓" : "✗");
417
418   return TRUE;
419 }
420
421 static void
422 ensure_kms_allocator (GstKMSSink * self)
423 {
424   if (self->allocator)
425     return;
426   self->allocator = gst_kms_allocator_new (self->fd);
427 }
428
429 static gboolean
430 configure_mode_setting (GstKMSSink * self, GstVideoInfo * vinfo)
431 {
432   gboolean ret;
433   drmModeConnector *conn;
434   int err;
435   gint i;
436   drmModeModeInfo *mode;
437   guint32 fb_id;
438   GstKMSMemory *kmsmem;
439
440   ret = FALSE;
441   conn = NULL;
442   mode = NULL;
443   kmsmem = NULL;
444
445   if (self->conn_id < 0)
446     goto bail;
447
448   GST_INFO_OBJECT (self, "configuring mode setting");
449
450   ensure_kms_allocator (self);
451   kmsmem = (GstKMSMemory *) gst_kms_allocator_bo_alloc (self->allocator, vinfo);
452   if (!kmsmem)
453     goto bo_failed;
454   fb_id = kmsmem->fb_id;
455
456   conn = drmModeGetConnector (self->fd, self->conn_id);
457   if (!conn)
458     goto connector_failed;
459
460   for (i = 0; i < conn->count_modes; i++) {
461     if (conn->modes[i].vdisplay == GST_VIDEO_INFO_HEIGHT (vinfo) &&
462         conn->modes[i].hdisplay == GST_VIDEO_INFO_WIDTH (vinfo)) {
463       mode = &conn->modes[i];
464       break;
465     }
466   }
467   if (!mode)
468     goto mode_failed;
469
470   err = drmModeSetCrtc (self->fd, self->crtc_id, fb_id, 0, 0,
471       (uint32_t *) & self->conn_id, 1, mode);
472   if (err)
473     goto modesetting_failed;
474
475   g_clear_pointer (&self->tmp_kmsmem, gst_memory_unref);
476   self->tmp_kmsmem = (GstMemory *) kmsmem;
477
478   ret = TRUE;
479
480 bail:
481   if (conn)
482     drmModeFreeConnector (conn);
483
484   return ret;
485
486   /* ERRORS */
487 bo_failed:
488   {
489     GST_ERROR_OBJECT (self,
490         "failed to allocate buffer object for mode setting");
491     goto bail;
492   }
493 connector_failed:
494   {
495     GST_ERROR_OBJECT (self, "Could not find a valid monitor connector");
496     goto bail;
497   }
498 mode_failed:
499   {
500     GST_ERROR_OBJECT (self, "cannot find appropriate mode");
501     goto bail;
502   }
503 modesetting_failed:
504   {
505     GST_ERROR_OBJECT (self, "Failed to set mode: %s", g_strerror (errno));
506     goto bail;
507   }
508 }
509
510 static gboolean
511 ensure_allowed_caps (GstKMSSink * self, drmModeConnector * conn,
512     drmModePlane * plane, drmModeRes * res)
513 {
514   GstCaps *out_caps, *tmp_caps, *caps;
515   int i, j;
516   GstVideoFormat fmt;
517   const gchar *format;
518   drmModeModeInfo *mode;
519   gint count_modes;
520
521   if (self->allowed_caps)
522     return TRUE;
523
524   out_caps = gst_caps_new_empty ();
525   if (!out_caps)
526     return FALSE;
527
528   if (conn && self->modesetting_enabled)
529     count_modes = conn->count_modes;
530   else
531     count_modes = 1;
532
533   for (i = 0; i < count_modes; i++) {
534     tmp_caps = gst_caps_new_empty ();
535     if (!tmp_caps)
536       return FALSE;
537
538     mode = NULL;
539     if (conn && self->modesetting_enabled)
540       mode = &conn->modes[i];
541
542     for (j = 0; j < plane->count_formats; j++) {
543       fmt = gst_video_format_from_drm (plane->formats[j]);
544       if (fmt == GST_VIDEO_FORMAT_UNKNOWN) {
545         GST_INFO_OBJECT (self, "ignoring format %" GST_FOURCC_FORMAT,
546             GST_FOURCC_ARGS (plane->formats[j]));
547         continue;
548       }
549
550       format = gst_video_format_to_string (fmt);
551
552       if (mode) {
553         caps = gst_caps_new_simple ("video/x-raw",
554             "format", G_TYPE_STRING, format,
555             "width", G_TYPE_INT, mode->hdisplay,
556             "height", G_TYPE_INT, mode->vdisplay,
557             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
558       } else {
559         caps = gst_caps_new_simple ("video/x-raw",
560             "format", G_TYPE_STRING, format,
561             "width", GST_TYPE_INT_RANGE, res->min_width, res->max_width,
562             "height", GST_TYPE_INT_RANGE, res->min_height, res->max_height,
563             "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
564       }
565       if (!caps)
566         continue;
567
568       tmp_caps = gst_caps_merge (tmp_caps, caps);
569     }
570
571     out_caps = gst_caps_merge (out_caps, gst_caps_simplify (tmp_caps));
572   }
573
574   if (gst_caps_is_empty (out_caps)) {
575     GST_DEBUG_OBJECT (self, "allowed caps is empty");
576     gst_caps_unref (out_caps);
577     return FALSE;
578   }
579
580   self->allowed_caps = gst_caps_simplify (out_caps);
581
582   GST_DEBUG_OBJECT (self, "allowed caps = %" GST_PTR_FORMAT,
583       self->allowed_caps);
584
585   return TRUE;
586 }
587
588 static gboolean
589 set_drm_property (gint fd, guint32 object, guint32 object_type,
590     drmModeObjectPropertiesPtr properties, const gchar * prop_name,
591     guint64 value)
592 {
593   guint i;
594   gboolean ret = FALSE;
595
596   for (i = 0; i < properties->count_props && !ret; i++) {
597     drmModePropertyPtr property;
598
599     property = drmModeGetProperty (fd, properties->props[i]);
600
601     /* GstStructure parser limits the set of supported character, so we
602      * replace the invalid characters with '-'. In DRM, this is generally
603      * replacing spaces into '-'. */
604     g_strcanon (property->name, G_CSET_a_2_z G_CSET_A_2_Z G_CSET_DIGITS "_",
605         '-');
606
607     GST_LOG ("found property %s (looking for %s)", property->name, prop_name);
608
609     if (!strcmp (property->name, prop_name)) {
610       drmModeObjectSetProperty (fd, object, object_type,
611           property->prop_id, value);
612       ret = TRUE;
613     }
614     drmModeFreeProperty (property);
615   }
616
617   return ret;
618 }
619
620 typedef struct
621 {
622   GstKMSSink *self;
623   drmModeObjectPropertiesPtr properties;
624   guint obj_id;
625   guint obj_type;
626   const gchar *obj_type_str;
627 } SetPropsIter;
628
629 static gboolean
630 set_obj_prop (GQuark field_id, const GValue * value, gpointer user_data)
631 {
632   SetPropsIter *iter = user_data;
633   GstKMSSink *self = iter->self;
634   const gchar *name;
635   guint64 v;
636
637   name = g_quark_to_string (field_id);
638
639   if (G_VALUE_HOLDS (value, G_TYPE_INT))
640     v = g_value_get_int (value);
641   else if (G_VALUE_HOLDS (value, G_TYPE_UINT))
642     v = g_value_get_uint (value);
643   else if (G_VALUE_HOLDS (value, G_TYPE_INT64))
644     v = g_value_get_int64 (value);
645   else if (G_VALUE_HOLDS (value, G_TYPE_UINT64))
646     v = g_value_get_uint64 (value);
647   else {
648     GST_WARNING_OBJECT (self,
649         "'uint64' value expected for control '%s'.", name);
650     return TRUE;
651   }
652
653   if (set_drm_property (self->fd, iter->obj_id, iter->obj_type,
654           iter->properties, name, v)) {
655     GST_DEBUG_OBJECT (self,
656         "Set %s property '%s' to %" G_GUINT64_FORMAT,
657         iter->obj_type_str, name, v);
658   } else {
659     GST_WARNING_OBJECT (self,
660         "Failed to set %s property '%s' to %" G_GUINT64_FORMAT,
661         iter->obj_type_str, name, v);
662   }
663
664   return TRUE;
665 }
666
667 static void
668 gst_kms_sink_update_properties (SetPropsIter * iter, GstStructure * props)
669 {
670   GstKMSSink *self = iter->self;
671
672   iter->properties = drmModeObjectGetProperties (self->fd, iter->obj_id,
673       iter->obj_type);
674
675   gst_structure_foreach (props, set_obj_prop, iter);
676
677   drmModeFreeObjectProperties (iter->properties);
678 }
679
680 static void
681 gst_kms_sink_update_connector_properties (GstKMSSink * self)
682 {
683   SetPropsIter iter;
684
685   if (!self->connector_props)
686     return;
687
688   iter.self = self;
689   iter.obj_id = self->conn_id;
690   iter.obj_type = DRM_MODE_OBJECT_CONNECTOR;
691   iter.obj_type_str = "connector";
692
693   gst_kms_sink_update_properties (&iter, self->connector_props);
694 }
695
696 static void
697 gst_kms_sink_update_plane_properties (GstKMSSink * self)
698 {
699   SetPropsIter iter;
700
701   if (!self->plane_props)
702     return;
703
704   iter.self = self;
705   iter.obj_id = self->plane_id;
706   iter.obj_type = DRM_MODE_OBJECT_PLANE;
707   iter.obj_type_str = "plane";
708
709   gst_kms_sink_update_properties (&iter, self->plane_props);
710 }
711
712 static gboolean
713 gst_kms_sink_start (GstBaseSink * bsink)
714 {
715   GstKMSSink *self;
716   drmModeRes *res;
717   drmModeConnector *conn;
718   drmModeCrtc *crtc;
719   drmModePlaneRes *pres;
720   drmModePlane *plane;
721   gboolean universal_planes;
722   gboolean ret;
723
724   self = GST_KMS_SINK (bsink);
725   universal_planes = FALSE;
726   ret = FALSE;
727   res = NULL;
728   conn = NULL;
729   crtc = NULL;
730   pres = NULL;
731   plane = NULL;
732
733   /* open our own internal device fd if application did not supply its own */
734   if (self->is_internal_fd) {
735     if (self->devname || self->bus_id)
736       self->fd = drmOpen (self->devname, self->bus_id);
737     else
738       self->fd = kms_open (&self->devname);
739   }
740
741   if (self->fd < 0)
742     goto open_failed;
743
744   log_drm_version (self);
745   if (!get_drm_caps (self))
746     goto bail;
747
748   res = drmModeGetResources (self->fd);
749   if (!res)
750     goto resources_failed;
751
752   if (self->conn_id == -1)
753     conn = find_main_monitor (self->fd, res);
754   else
755     conn = drmModeGetConnector (self->fd, self->conn_id);
756   if (!conn)
757     goto connector_failed;
758
759   crtc = find_crtc_for_connector (self->fd, res, conn, &self->pipe);
760   if (!crtc)
761     goto crtc_failed;
762
763   if (!crtc->mode_valid || self->modesetting_enabled) {
764     GST_DEBUG_OBJECT (self, "enabling modesetting");
765     self->modesetting_enabled = TRUE;
766     universal_planes = TRUE;
767   }
768
769   if (crtc->mode_valid && self->modesetting_enabled && self->restore_crtc) {
770     self->saved_crtc = (drmModeCrtc *) crtc;
771   }
772
773 retry_find_plane:
774   if (universal_planes &&
775       drmSetClientCap (self->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1))
776     goto set_cap_failed;
777
778   pres = drmModeGetPlaneResources (self->fd);
779   if (!pres)
780     goto plane_resources_failed;
781
782   if (self->plane_id == -1)
783     plane = find_plane_for_crtc (self->fd, res, pres, crtc->crtc_id);
784   else
785     plane = drmModeGetPlane (self->fd, self->plane_id);
786   if (!plane)
787     goto plane_failed;
788
789   if (!ensure_allowed_caps (self, conn, plane, res))
790     goto allowed_caps_failed;
791
792   self->conn_id = conn->connector_id;
793   self->crtc_id = crtc->crtc_id;
794   self->plane_id = plane->plane_id;
795
796   GST_INFO_OBJECT (self, "connector id = %d / crtc id = %d / plane id = %d",
797       self->conn_id, self->crtc_id, self->plane_id);
798
799   GST_OBJECT_LOCK (self);
800   self->hdisplay = crtc->mode.hdisplay;
801   self->vdisplay = crtc->mode.vdisplay;
802
803   if (self->render_rect.w == 0 || self->render_rect.h == 0) {
804     self->render_rect.x = 0;
805     self->render_rect.y = 0;
806     self->render_rect.w = self->hdisplay;
807     self->render_rect.h = self->vdisplay;
808   }
809
810   self->pending_rect = self->render_rect;
811   GST_OBJECT_UNLOCK (self);
812
813   self->buffer_id = crtc->buffer_id;
814
815   self->mm_width = conn->mmWidth;
816   self->mm_height = conn->mmHeight;
817
818   GST_INFO_OBJECT (self, "display size: pixels = %dx%d / millimeters = %dx%d",
819       self->hdisplay, self->vdisplay, self->mm_width, self->mm_height);
820
821   self->pollfd.fd = self->fd;
822   gst_poll_add_fd (self->poll, &self->pollfd);
823   gst_poll_fd_ctl_read (self->poll, &self->pollfd, TRUE);
824
825   g_object_notify_by_pspec (G_OBJECT (self), g_properties[PROP_DISPLAY_WIDTH]);
826   g_object_notify_by_pspec (G_OBJECT (self), g_properties[PROP_DISPLAY_HEIGHT]);
827
828   gst_kms_sink_update_connector_properties (self);
829   gst_kms_sink_update_plane_properties (self);
830
831   ret = TRUE;
832
833 bail:
834   if (plane)
835     drmModeFreePlane (plane);
836   if (pres)
837     drmModeFreePlaneResources (pres);
838   if (crtc != self->saved_crtc)
839     drmModeFreeCrtc (crtc);
840   if (conn)
841     drmModeFreeConnector (conn);
842   if (res)
843     drmModeFreeResources (res);
844
845   if (!ret && self->fd >= 0) {
846     if (self->is_internal_fd)
847       drmClose (self->fd);
848     self->fd = -1;
849   }
850
851   return ret;
852
853   /* ERRORS */
854 open_failed:
855   {
856     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ_WRITE,
857         ("Could not open DRM module %s", GST_STR_NULL (self->devname)),
858         ("reason: %s (%d)", g_strerror (errno), errno));
859     return FALSE;
860   }
861
862 resources_failed:
863   {
864     GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
865         ("drmModeGetResources failed"),
866         ("reason: %s (%d)", g_strerror (errno), errno));
867     goto bail;
868   }
869
870 connector_failed:
871   {
872     GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
873         ("Could not find a valid monitor connector"), (NULL));
874     goto bail;
875   }
876
877 crtc_failed:
878   {
879     GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
880         ("Could not find a crtc for connector"), (NULL));
881     goto bail;
882   }
883
884 set_cap_failed:
885   {
886     GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
887         ("Could not set universal planes capability bit"), (NULL));
888     goto bail;
889   }
890
891 plane_resources_failed:
892   {
893     GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
894         ("drmModeGetPlaneResources failed"),
895         ("reason: %s (%d)", g_strerror (errno), errno));
896     goto bail;
897   }
898
899 plane_failed:
900   {
901     if (universal_planes) {
902       GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
903           ("Could not find a plane for crtc"), (NULL));
904       goto bail;
905     } else {
906       universal_planes = TRUE;
907       goto retry_find_plane;
908     }
909   }
910
911 allowed_caps_failed:
912   {
913     GST_ELEMENT_ERROR (self, RESOURCE, SETTINGS,
914         ("Could not get allowed GstCaps of device"),
915         ("driver does not provide mode settings configuration"));
916     goto bail;
917   }
918 }
919
920 static gboolean
921 gst_kms_sink_stop (GstBaseSink * bsink)
922 {
923   GstKMSSink *self;
924   int err;
925
926   self = GST_KMS_SINK (bsink);
927
928   if (self->allocator)
929     gst_kms_allocator_clear_cache (self->allocator);
930
931   gst_buffer_replace (&self->last_buffer, NULL);
932   gst_caps_replace (&self->allowed_caps, NULL);
933   gst_object_replace ((GstObject **) & self->pool, NULL);
934   gst_object_replace ((GstObject **) & self->allocator, NULL);
935
936   gst_poll_remove_fd (self->poll, &self->pollfd);
937   gst_poll_restart (self->poll);
938   gst_poll_fd_init (&self->pollfd);
939
940   if (self->saved_crtc) {
941     drmModeCrtc *crtc = (drmModeCrtc *) self->saved_crtc;
942
943     err = drmModeSetCrtc (self->fd, crtc->crtc_id, crtc->buffer_id, crtc->x,
944         crtc->y, (uint32_t *) & self->conn_id, 1, &crtc->mode);
945     if (err)
946       GST_ERROR_OBJECT (self, "Failed to restore previous CRTC mode: %s",
947           g_strerror (errno));
948
949     drmModeFreeCrtc (crtc);
950     self->saved_crtc = NULL;
951   }
952
953   if (self->fd >= 0) {
954     if (self->is_internal_fd)
955       drmClose (self->fd);
956     self->fd = -1;
957   }
958
959   GST_OBJECT_LOCK (bsink);
960   self->hdisplay = 0;
961   self->vdisplay = 0;
962   self->pending_rect.x = 0;
963   self->pending_rect.y = 0;
964   self->pending_rect.w = 0;
965   self->pending_rect.h = 0;
966   self->render_rect = self->pending_rect;
967   GST_OBJECT_UNLOCK (bsink);
968
969   g_object_notify_by_pspec (G_OBJECT (self), g_properties[PROP_DISPLAY_WIDTH]);
970   g_object_notify_by_pspec (G_OBJECT (self), g_properties[PROP_DISPLAY_HEIGHT]);
971
972   return TRUE;
973 }
974
975 static GstCaps *
976 gst_kms_sink_get_allowed_caps (GstKMSSink * self)
977 {
978   if (!self->allowed_caps)
979     return NULL;                /* base class will return the template caps */
980   return gst_caps_ref (self->allowed_caps);
981 }
982
983 static GstCaps *
984 gst_kms_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
985 {
986   GstKMSSink *self;
987   GstCaps *caps, *out_caps;
988   GstStructure *s;
989   guint dpy_par_n, dpy_par_d;
990
991   self = GST_KMS_SINK (bsink);
992
993   caps = gst_kms_sink_get_allowed_caps (self);
994   if (!caps)
995     return NULL;
996
997   GST_OBJECT_LOCK (self);
998
999   if (!self->can_scale) {
1000     out_caps = gst_caps_new_empty ();
1001     gst_video_calculate_device_ratio (self->hdisplay, self->vdisplay,
1002         self->mm_width, self->mm_height, &dpy_par_n, &dpy_par_d);
1003
1004     s = gst_structure_copy (gst_caps_get_structure (caps, 0));
1005     gst_structure_set (s, "width", G_TYPE_INT, self->pending_rect.w,
1006         "height", G_TYPE_INT, self->pending_rect.h,
1007         "pixel-aspect-ratio", GST_TYPE_FRACTION, dpy_par_n, dpy_par_d, NULL);
1008
1009     gst_caps_append_structure (out_caps, s);
1010
1011     out_caps = gst_caps_merge (out_caps, caps);
1012     caps = NULL;
1013
1014     /* enforce our display aspect ratio */
1015     gst_caps_set_simple (out_caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
1016         dpy_par_n, dpy_par_d, NULL);
1017   } else {
1018     out_caps = gst_caps_make_writable (caps);
1019     caps = NULL;
1020   }
1021
1022   GST_OBJECT_UNLOCK (self);
1023
1024   GST_DEBUG_OBJECT (self, "Proposing caps %" GST_PTR_FORMAT, out_caps);
1025
1026   if (filter) {
1027     caps = out_caps;
1028     out_caps = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1029     gst_caps_unref (caps);
1030   }
1031
1032   return out_caps;
1033 }
1034
1035 static GstBufferPool *
1036 gst_kms_sink_create_pool (GstKMSSink * self, GstCaps * caps, gsize size,
1037     gint min)
1038 {
1039   GstBufferPool *pool;
1040   GstStructure *config;
1041
1042   pool = gst_kms_buffer_pool_new ();
1043   if (!pool)
1044     goto pool_failed;
1045
1046   config = gst_buffer_pool_get_config (pool);
1047   gst_buffer_pool_config_set_params (config, caps, size, min, 0);
1048   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
1049
1050   ensure_kms_allocator (self);
1051   gst_buffer_pool_config_set_allocator (config, self->allocator, NULL);
1052
1053   if (!gst_buffer_pool_set_config (pool, config))
1054     goto config_failed;
1055
1056   return pool;
1057
1058   /* ERRORS */
1059 pool_failed:
1060   {
1061     GST_ERROR_OBJECT (self, "failed to create buffer pool");
1062     return NULL;
1063   }
1064 config_failed:
1065   {
1066     GST_ERROR_OBJECT (self, "failed to set config");
1067     gst_object_unref (pool);
1068     return NULL;
1069   }
1070 }
1071
1072 static gboolean
1073 gst_kms_sink_calculate_display_ratio (GstKMSSink * self, GstVideoInfo * vinfo,
1074     gint * scaled_width, gint * scaled_height)
1075 {
1076   guint dar_n, dar_d;
1077   guint video_width, video_height;
1078   guint video_par_n, video_par_d;
1079   guint dpy_par_n, dpy_par_d;
1080
1081   video_width = GST_VIDEO_INFO_WIDTH (vinfo);
1082   video_height = GST_VIDEO_INFO_HEIGHT (vinfo);
1083   video_par_n = GST_VIDEO_INFO_PAR_N (vinfo);
1084   video_par_d = GST_VIDEO_INFO_PAR_D (vinfo);
1085
1086   if (self->can_scale) {
1087     gst_video_calculate_device_ratio (self->hdisplay, self->vdisplay,
1088         self->mm_width, self->mm_height, &dpy_par_n, &dpy_par_d);
1089   } else {
1090     *scaled_width = video_width;
1091     *scaled_height = video_height;
1092     goto out;
1093   }
1094
1095   if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, video_width,
1096           video_height, video_par_n, video_par_d, dpy_par_n, dpy_par_d))
1097     return FALSE;
1098
1099   GST_DEBUG_OBJECT (self, "video calculated display ratio: %d/%d", dar_n,
1100       dar_d);
1101
1102   /* now find a width x height that respects this display ratio.
1103    * prefer those that have one of w/h the same as the incoming video
1104    * using wd / hd = dar_n / dar_d */
1105
1106   /* start with same height, because of interlaced video */
1107   /* check hd / dar_d is an integer scale factor, and scale wd with the PAR */
1108   if (video_height % dar_d == 0) {
1109     GST_DEBUG_OBJECT (self, "keeping video height");
1110     *scaled_width = (guint)
1111         gst_util_uint64_scale_int (video_height, dar_n, dar_d);
1112     *scaled_height = video_height;
1113   } else if (video_width % dar_n == 0) {
1114     GST_DEBUG_OBJECT (self, "keeping video width");
1115     *scaled_width = video_width;
1116     *scaled_height = (guint)
1117         gst_util_uint64_scale_int (video_width, dar_d, dar_n);
1118   } else {
1119     GST_DEBUG_OBJECT (self, "approximating while keeping video height");
1120     *scaled_width = (guint)
1121         gst_util_uint64_scale_int (video_height, dar_n, dar_d);
1122     *scaled_height = video_height;
1123   }
1124
1125 out:
1126   GST_DEBUG_OBJECT (self, "scaling to %dx%d", *scaled_width, *scaled_height);
1127
1128   return TRUE;
1129 }
1130
1131 static gboolean
1132 gst_kms_sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
1133 {
1134   GstKMSSink *self;
1135   GstVideoInfo vinfo;
1136
1137   self = GST_KMS_SINK (bsink);
1138
1139   if (!gst_video_info_from_caps (&vinfo, caps))
1140     goto invalid_format;
1141   self->vinfo = vinfo;
1142
1143   if (!gst_kms_sink_calculate_display_ratio (self, &vinfo,
1144           &GST_VIDEO_SINK_WIDTH (self), &GST_VIDEO_SINK_HEIGHT (self)))
1145     goto no_disp_ratio;
1146
1147   if (GST_VIDEO_SINK_WIDTH (self) <= 0 || GST_VIDEO_SINK_HEIGHT (self) <= 0)
1148     goto invalid_size;
1149
1150   /* discard dumb buffer pool */
1151   if (self->pool) {
1152     gst_buffer_pool_set_active (self->pool, FALSE);
1153     gst_object_unref (self->pool);
1154     self->pool = NULL;
1155   }
1156
1157   if (self->modesetting_enabled && !configure_mode_setting (self, &vinfo))
1158     goto modesetting_failed;
1159
1160   GST_OBJECT_LOCK (self);
1161   if (self->reconfigure) {
1162     self->reconfigure = FALSE;
1163     self->render_rect = self->pending_rect;
1164   }
1165   GST_OBJECT_UNLOCK (self);
1166
1167   GST_DEBUG_OBJECT (self, "negotiated caps = %" GST_PTR_FORMAT, caps);
1168
1169   return TRUE;
1170
1171   /* ERRORS */
1172 invalid_format:
1173   {
1174     GST_ERROR_OBJECT (self, "caps invalid");
1175     return FALSE;
1176   }
1177
1178 invalid_size:
1179   {
1180     GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1181         ("Invalid image size."));
1182     return FALSE;
1183   }
1184
1185 no_disp_ratio:
1186   {
1187     GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1188         ("Error calculating the output display ratio of the video."));
1189     return FALSE;
1190   }
1191
1192 modesetting_failed:
1193   {
1194     GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1195         ("failed to configure video mode"));
1196     return FALSE;
1197   }
1198
1199 }
1200
1201 static gboolean
1202 gst_kms_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1203 {
1204   GstKMSSink *self;
1205   GstCaps *caps;
1206   gboolean need_pool;
1207   GstVideoInfo vinfo;
1208   GstBufferPool *pool;
1209   gsize size;
1210
1211   self = GST_KMS_SINK (bsink);
1212
1213   GST_DEBUG_OBJECT (self, "propose allocation");
1214
1215   gst_query_parse_allocation (query, &caps, &need_pool);
1216   if (!caps)
1217     goto no_caps;
1218   if (!gst_video_info_from_caps (&vinfo, caps))
1219     goto invalid_caps;
1220
1221   size = GST_VIDEO_INFO_SIZE (&vinfo);
1222
1223   pool = NULL;
1224   if (need_pool) {
1225     pool = gst_kms_sink_create_pool (self, caps, size, 0);
1226     if (!pool)
1227       goto no_pool;
1228
1229     /* Only export for pool used upstream */
1230     if (self->has_prime_export) {
1231       GstStructure *config = gst_buffer_pool_get_config (pool);
1232       gst_buffer_pool_config_add_option (config,
1233           GST_BUFFER_POOL_OPTION_KMS_PRIME_EXPORT);
1234       gst_buffer_pool_set_config (pool, config);
1235     }
1236   }
1237
1238   /* we need at least 2 buffer because we hold on to the last one */
1239   gst_query_add_allocation_pool (query, pool, size, 2, 0);
1240   if (pool)
1241     gst_object_unref (pool);
1242
1243   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1244   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1245
1246   return TRUE;
1247
1248   /* ERRORS */
1249 no_caps:
1250   {
1251     GST_DEBUG_OBJECT (bsink, "no caps specified");
1252     return FALSE;
1253   }
1254 invalid_caps:
1255   {
1256     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1257     return FALSE;
1258   }
1259 no_pool:
1260   {
1261     /* Already warned in create_pool */
1262     return FALSE;
1263   }
1264 }
1265
1266 static void
1267 sync_handler (gint fd, guint frame, guint sec, guint usec, gpointer data)
1268 {
1269   gboolean *waiting;
1270
1271   waiting = data;
1272   *waiting = FALSE;
1273 }
1274
1275 static gboolean
1276 gst_kms_sink_sync (GstKMSSink * self)
1277 {
1278   gint ret;
1279   gboolean waiting;
1280   drmEventContext evctxt = {
1281     .version = DRM_EVENT_CONTEXT_VERSION,
1282     .page_flip_handler = sync_handler,
1283     .vblank_handler = sync_handler,
1284   };
1285   drmVBlank vbl = {
1286     .request = {
1287           .type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
1288           .sequence = 1,
1289           .signal = (gulong) & waiting,
1290         },
1291   };
1292
1293   if (self->pipe == 1)
1294     vbl.request.type |= DRM_VBLANK_SECONDARY;
1295   else if (self->pipe > 1)
1296     vbl.request.type |= self->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
1297
1298   waiting = TRUE;
1299   if (!self->has_async_page_flip && !self->modesetting_enabled) {
1300     ret = drmWaitVBlank (self->fd, &vbl);
1301     if (ret)
1302       goto vblank_failed;
1303   } else {
1304     ret = drmModePageFlip (self->fd, self->crtc_id, self->buffer_id,
1305         DRM_MODE_PAGE_FLIP_EVENT, &waiting);
1306     if (ret)
1307       goto pageflip_failed;
1308   }
1309
1310   while (waiting) {
1311     do {
1312       ret = gst_poll_wait (self->poll, 3 * GST_SECOND);
1313     } while (ret == -1 && (errno == EAGAIN || errno == EINTR));
1314
1315     ret = drmHandleEvent (self->fd, &evctxt);
1316     if (ret)
1317       goto event_failed;
1318   }
1319
1320   return TRUE;
1321
1322   /* ERRORS */
1323 vblank_failed:
1324   {
1325     GST_WARNING_OBJECT (self, "drmWaitVBlank failed: %s (%d)",
1326         g_strerror (errno), errno);
1327     return FALSE;
1328   }
1329 pageflip_failed:
1330   {
1331     GST_WARNING_OBJECT (self, "drmModePageFlip failed: %s (%d)",
1332         g_strerror (errno), errno);
1333     return FALSE;
1334   }
1335 event_failed:
1336   {
1337     GST_ERROR_OBJECT (self, "drmHandleEvent failed: %s (%d)",
1338         g_strerror (errno), errno);
1339     return FALSE;
1340   }
1341 }
1342
1343 static gboolean
1344 gst_kms_sink_import_dmabuf (GstKMSSink * self, GstBuffer * inbuf,
1345     GstBuffer ** outbuf)
1346 {
1347   gint prime_fds[GST_VIDEO_MAX_PLANES] = { 0, };
1348   GstVideoMeta *meta;
1349   guint i, n_mem, n_planes;
1350   GstKMSMemory *kmsmem;
1351   guint mems_idx[GST_VIDEO_MAX_PLANES];
1352   gsize mems_skip[GST_VIDEO_MAX_PLANES];
1353   GstMemory *mems[GST_VIDEO_MAX_PLANES];
1354
1355   if (!self->has_prime_import)
1356     return FALSE;
1357
1358   /* This will eliminate most non-dmabuf out there */
1359   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
1360     return FALSE;
1361
1362   n_planes = GST_VIDEO_INFO_N_PLANES (&self->vinfo);
1363   n_mem = gst_buffer_n_memory (inbuf);
1364   meta = gst_buffer_get_video_meta (inbuf);
1365
1366   GST_TRACE_OBJECT (self, "Found a dmabuf with %u planes and %u memories",
1367       n_planes, n_mem);
1368
1369   /* We cannot have multiple dmabuf per plane */
1370   if (n_mem > n_planes)
1371     return FALSE;
1372   g_assert (n_planes != 0);
1373
1374   /* Update video info based on video meta */
1375   if (meta) {
1376     GST_VIDEO_INFO_WIDTH (&self->vinfo) = meta->width;
1377     GST_VIDEO_INFO_HEIGHT (&self->vinfo) = meta->height;
1378
1379     for (i = 0; i < meta->n_planes; i++) {
1380       GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i) = meta->offset[i];
1381       GST_VIDEO_INFO_PLANE_STRIDE (&self->vinfo, i) = meta->stride[i];
1382     }
1383   }
1384
1385   /* Find and validate all memories */
1386   for (i = 0; i < n_planes; i++) {
1387     guint length;
1388
1389     if (!gst_buffer_find_memory (inbuf,
1390             GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i), 1,
1391             &mems_idx[i], &length, &mems_skip[i]))
1392       return FALSE;
1393
1394     mems[i] = gst_buffer_peek_memory (inbuf, mems_idx[i]);
1395
1396     /* adjust for memory offset, in case data does not
1397      * start from byte 0 in the dmabuf fd */
1398     mems_skip[i] += mems[i]->offset;
1399
1400     /* And all memory found must be dmabuf */
1401     if (!gst_is_dmabuf_memory (mems[i]))
1402       return FALSE;
1403   }
1404
1405   ensure_kms_allocator (self);
1406
1407   kmsmem = (GstKMSMemory *) gst_kms_allocator_get_cached (mems[0]);
1408   if (kmsmem) {
1409     GST_LOG_OBJECT (self, "found KMS mem %p in DMABuf mem %p with fb id = %d",
1410         kmsmem, mems[0], kmsmem->fb_id);
1411     goto wrap_mem;
1412   }
1413
1414   for (i = 0; i < n_planes; i++)
1415     prime_fds[i] = gst_dmabuf_memory_get_fd (mems[i]);
1416
1417   GST_LOG_OBJECT (self, "found these prime ids: %d, %d, %d, %d", prime_fds[0],
1418       prime_fds[1], prime_fds[2], prime_fds[3]);
1419
1420   kmsmem = gst_kms_allocator_dmabuf_import (self->allocator,
1421       prime_fds, n_planes, mems_skip, &self->vinfo);
1422   if (!kmsmem)
1423     return FALSE;
1424
1425   GST_LOG_OBJECT (self, "setting KMS mem %p to DMABuf mem %p with fb id = %d",
1426       kmsmem, mems[0], kmsmem->fb_id);
1427   gst_kms_allocator_cache (self->allocator, mems[0], GST_MEMORY_CAST (kmsmem));
1428
1429 wrap_mem:
1430   *outbuf = gst_buffer_new ();
1431   if (!*outbuf)
1432     return FALSE;
1433   gst_buffer_append_memory (*outbuf, gst_memory_ref (GST_MEMORY_CAST (kmsmem)));
1434   gst_buffer_add_parent_buffer_meta (*outbuf, inbuf);
1435
1436   return TRUE;
1437 }
1438
1439 static gboolean
1440 ensure_internal_pool (GstKMSSink * self, GstVideoInfo * in_vinfo,
1441     GstBuffer * inbuf)
1442 {
1443   GstBufferPool *pool;
1444   GstVideoInfo vinfo = *in_vinfo;
1445   GstVideoMeta *vmeta;
1446   GstCaps *caps;
1447
1448   if (self->pool)
1449     return TRUE;
1450
1451   /* When cropping, the caps matches the cropped rectangle width/height, but
1452    * we can retrieve the padded width/height from the VideoMeta (which is kept
1453    * intact when adding crop meta */
1454   if ((vmeta = gst_buffer_get_video_meta (inbuf))) {
1455     vinfo.width = vmeta->width;
1456     vinfo.height = vmeta->height;
1457   }
1458
1459   caps = gst_video_info_to_caps (&vinfo);
1460   pool = gst_kms_sink_create_pool (self, caps, gst_buffer_get_size (inbuf), 2);
1461   gst_caps_unref (caps);
1462
1463   if (!pool)
1464     return FALSE;
1465
1466   if (!gst_buffer_pool_set_active (pool, TRUE))
1467     goto activate_pool_failed;
1468
1469   self->pool = pool;
1470   return TRUE;
1471
1472 activate_pool_failed:
1473   {
1474     GST_ELEMENT_ERROR (self, STREAM, FAILED, ("failed to activate buffer pool"),
1475         ("failed to activate buffer pool"));
1476     gst_object_unref (pool);
1477     return FALSE;
1478   }
1479
1480 }
1481
1482 static GstBuffer *
1483 gst_kms_sink_copy_to_dumb_buffer (GstKMSSink * self, GstVideoInfo * vinfo,
1484     GstBuffer * inbuf)
1485 {
1486   GstFlowReturn ret;
1487   GstVideoFrame inframe, outframe;
1488   gboolean success;
1489   GstBuffer *buf = NULL;
1490
1491   if (!ensure_internal_pool (self, vinfo, inbuf))
1492     goto bail;
1493
1494   ret = gst_buffer_pool_acquire_buffer (self->pool, &buf, NULL);
1495   if (ret != GST_FLOW_OK)
1496     goto create_buffer_failed;
1497
1498   if (!gst_video_frame_map (&inframe, vinfo, inbuf, GST_MAP_READ))
1499     goto error_map_src_buffer;
1500
1501   if (!gst_video_frame_map (&outframe, vinfo, buf, GST_MAP_WRITE))
1502     goto error_map_dst_buffer;
1503
1504   success = gst_video_frame_copy (&outframe, &inframe);
1505   gst_video_frame_unmap (&outframe);
1506   gst_video_frame_unmap (&inframe);
1507   if (!success)
1508     goto error_copy_buffer;
1509
1510   return buf;
1511
1512 bail:
1513   {
1514     if (buf)
1515       gst_buffer_unref (buf);
1516     return NULL;
1517   }
1518
1519   /* ERRORS */
1520 create_buffer_failed:
1521   {
1522     GST_ELEMENT_ERROR (self, STREAM, FAILED, ("allocation failed"),
1523         ("failed to create buffer"));
1524     return NULL;
1525   }
1526 error_copy_buffer:
1527   {
1528     GST_WARNING_OBJECT (self, "failed to upload buffer");
1529     goto bail;
1530   }
1531 error_map_dst_buffer:
1532   {
1533     gst_video_frame_unmap (&inframe);
1534     /* fall-through */
1535   }
1536 error_map_src_buffer:
1537   {
1538     GST_WARNING_OBJECT (self, "failed to map buffer");
1539     goto bail;
1540   }
1541 }
1542
1543 static GstBuffer *
1544 gst_kms_sink_get_input_buffer (GstKMSSink * self, GstBuffer * inbuf)
1545 {
1546   GstMemory *mem;
1547   GstBuffer *buf = NULL;
1548
1549   mem = gst_buffer_peek_memory (inbuf, 0);
1550   if (!mem)
1551     return NULL;
1552
1553   if (gst_is_kms_memory (mem))
1554     return gst_buffer_ref (inbuf);
1555
1556   if (gst_kms_sink_import_dmabuf (self, inbuf, &buf))
1557     goto done;
1558
1559   GST_CAT_INFO_OBJECT (CAT_PERFORMANCE, self, "frame copy");
1560   buf = gst_kms_sink_copy_to_dumb_buffer (self, &self->vinfo, inbuf);
1561
1562 done:
1563   /* Copy all the non-memory related metas, this way CropMeta will be
1564    * available upon GstVideoOverlay::expose calls. */
1565   if (buf)
1566     gst_buffer_copy_into (buf, inbuf, GST_BUFFER_COPY_METADATA, 0, -1);
1567
1568   return buf;
1569 }
1570
1571 static GstFlowReturn
1572 gst_kms_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1573 {
1574   gint ret;
1575   GstBuffer *buffer = NULL;
1576   guint32 fb_id;
1577   GstKMSSink *self;
1578   GstVideoInfo *vinfo;
1579   GstVideoCropMeta *crop;
1580   GstVideoRectangle src = { 0, };
1581   gint video_width, video_height;
1582   GstVideoRectangle dst = { 0, };
1583   GstVideoRectangle result;
1584   GstFlowReturn res;
1585
1586   self = GST_KMS_SINK (vsink);
1587
1588   res = GST_FLOW_ERROR;
1589
1590   if (buf) {
1591     buffer = gst_kms_sink_get_input_buffer (self, buf);
1592     vinfo = &self->vinfo;
1593     video_width = src.w = GST_VIDEO_SINK_WIDTH (self);
1594     video_height = src.h = GST_VIDEO_SINK_HEIGHT (self);
1595   } else if (self->last_buffer) {
1596     buffer = gst_buffer_ref (self->last_buffer);
1597     vinfo = &self->last_vinfo;
1598     video_width = src.w = self->last_width;
1599     video_height = src.h = self->last_height;
1600   }
1601
1602   /* Make sure buf is not used accidentally */
1603   buf = NULL;
1604
1605   if (!buffer)
1606     return GST_FLOW_ERROR;
1607   fb_id = gst_kms_memory_get_fb_id (gst_buffer_peek_memory (buffer, 0));
1608   if (fb_id == 0)
1609     goto buffer_invalid;
1610
1611   GST_TRACE_OBJECT (self, "displaying fb %d", fb_id);
1612
1613   GST_OBJECT_LOCK (self);
1614   if (self->modesetting_enabled) {
1615     self->buffer_id = fb_id;
1616     goto sync_frame;
1617   }
1618
1619   if ((crop = gst_buffer_get_video_crop_meta (buffer))) {
1620     GstVideoInfo cropped_vinfo = *vinfo;
1621
1622     cropped_vinfo.width = crop->width;
1623     cropped_vinfo.height = crop->height;
1624
1625     if (!gst_kms_sink_calculate_display_ratio (self, &cropped_vinfo, &src.w,
1626             &src.h))
1627       goto no_disp_ratio;
1628
1629     src.x = crop->x;
1630     src.y = crop->y;
1631   }
1632
1633   dst.w = self->render_rect.w;
1634   dst.h = self->render_rect.h;
1635
1636 retry_set_plane:
1637   gst_video_sink_center_rect (src, dst, &result, self->can_scale);
1638
1639   result.x += self->render_rect.x;
1640   result.y += self->render_rect.y;
1641
1642   if (crop) {
1643     src.w = crop->width;
1644     src.h = crop->height;
1645   } else {
1646     src.w = video_width;
1647     src.h = video_height;
1648   }
1649
1650   /* handle out of screen case */
1651   if ((result.x + result.w) > self->hdisplay)
1652     result.w = self->hdisplay - result.x;
1653
1654   if ((result.y + result.h) > self->vdisplay)
1655     result.h = self->vdisplay - result.y;
1656
1657   if (result.w <= 0 || result.h <= 0) {
1658     GST_WARNING_OBJECT (self, "video is out of display range");
1659     goto sync_frame;
1660   }
1661
1662   /* to make sure it can be show when driver don't support scale */
1663   if (!self->can_scale) {
1664     src.w = result.w;
1665     src.h = result.h;
1666   }
1667
1668   GST_TRACE_OBJECT (self,
1669       "drmModeSetPlane at (%i,%i) %ix%i sourcing at (%i,%i) %ix%i",
1670       result.x, result.y, result.w, result.h, src.x, src.y, src.w, src.h);
1671
1672   ret = drmModeSetPlane (self->fd, self->plane_id, self->crtc_id, fb_id, 0,
1673       result.x, result.y, result.w, result.h,
1674       /* source/cropping coordinates are given in Q16 */
1675       src.x << 16, src.y << 16, src.w << 16, src.h << 16);
1676   if (ret) {
1677     if (self->can_scale) {
1678       self->can_scale = FALSE;
1679       goto retry_set_plane;
1680     }
1681     goto set_plane_failed;
1682   }
1683
1684 sync_frame:
1685   /* Wait for the previous frame to complete redraw */
1686   if (!gst_kms_sink_sync (self)) {
1687     GST_OBJECT_UNLOCK (self);
1688     goto bail;
1689   }
1690
1691   /* Save the rendered buffer and its metadata in case a redraw is needed */
1692   if (buffer != self->last_buffer) {
1693     gst_buffer_replace (&self->last_buffer, buffer);
1694     self->last_width = GST_VIDEO_SINK_WIDTH (self);
1695     self->last_height = GST_VIDEO_SINK_HEIGHT (self);
1696     self->last_vinfo = self->vinfo;
1697   }
1698   g_clear_pointer (&self->tmp_kmsmem, gst_memory_unref);
1699
1700   GST_OBJECT_UNLOCK (self);
1701   res = GST_FLOW_OK;
1702
1703 bail:
1704   gst_buffer_unref (buffer);
1705   return res;
1706
1707   /* ERRORS */
1708 buffer_invalid:
1709   {
1710     GST_ERROR_OBJECT (self, "invalid buffer: it doesn't have a fb id");
1711     goto bail;
1712   }
1713 set_plane_failed:
1714   {
1715     GST_OBJECT_UNLOCK (self);
1716     GST_DEBUG_OBJECT (self, "result = { %d, %d, %d, %d} / "
1717         "src = { %d, %d, %d %d } / dst = { %d, %d, %d %d }", result.x, result.y,
1718         result.w, result.h, src.x, src.y, src.w, src.h, dst.x, dst.y, dst.w,
1719         dst.h);
1720     GST_ELEMENT_ERROR (self, RESOURCE, FAILED,
1721         (NULL), ("drmModeSetPlane failed: %s (%d)", g_strerror (errno), errno));
1722     goto bail;
1723   }
1724 no_disp_ratio:
1725   {
1726     GST_OBJECT_UNLOCK (self);
1727     GST_ELEMENT_ERROR (self, CORE, NEGOTIATION, (NULL),
1728         ("Error calculating the output display ratio of the video."));
1729     goto bail;
1730   }
1731 }
1732
1733 static void
1734 gst_kms_sink_drain (GstKMSSink * self)
1735 {
1736   GstParentBufferMeta *parent_meta;
1737
1738   if (!self->last_buffer)
1739     return;
1740
1741   /* We only need to return the last_buffer if it depends on upstream buffer.
1742    * In this case, the last_buffer will have a GstParentBufferMeta set. */
1743   parent_meta = gst_buffer_get_parent_buffer_meta (self->last_buffer);
1744   if (parent_meta) {
1745     GstBuffer *dumb_buf, *last_buf;
1746
1747     /* If this was imported from our dumb buffer pool we can safely skip the
1748      * drain */
1749     if (parent_meta->buffer->pool &&
1750         GST_IS_KMS_BUFFER_POOL (parent_meta->buffer->pool))
1751       return;
1752
1753     GST_DEBUG_OBJECT (self, "draining");
1754
1755     dumb_buf = gst_kms_sink_copy_to_dumb_buffer (self, &self->last_vinfo,
1756         parent_meta->buffer);
1757     last_buf = self->last_buffer;
1758     self->last_buffer = dumb_buf;
1759
1760     gst_kms_allocator_clear_cache (self->allocator);
1761     gst_kms_sink_show_frame (GST_VIDEO_SINK (self), NULL);
1762     gst_buffer_unref (last_buf);
1763   }
1764 }
1765
1766 static gboolean
1767 gst_kms_sink_query (GstBaseSink * bsink, GstQuery * query)
1768 {
1769   GstKMSSink *self = GST_KMS_SINK (bsink);
1770
1771   switch (GST_QUERY_TYPE (query)) {
1772     case GST_QUERY_ALLOCATION:
1773     case GST_QUERY_DRAIN:
1774     {
1775       gst_kms_sink_drain (self);
1776       break;
1777     }
1778     default:
1779       break;
1780   }
1781
1782   return GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
1783 }
1784
1785 static void
1786 _validate_and_set_external_fd (GstKMSSink * self, gint fd)
1787 {
1788   if (self->devname) {
1789     GST_WARNING_OBJECT (self, "Can't set fd... %s already set.",
1790         g_param_spec_get_name (g_properties[PROP_DRIVER_NAME]));
1791     return;
1792   }
1793
1794   if (self->bus_id) {
1795     GST_WARNING_OBJECT (self, "Can't set fd... %s already set.",
1796         g_param_spec_get_name (g_properties[PROP_BUS_ID]));
1797     return;
1798   }
1799
1800   if (self->fd >= 0) {
1801     GST_WARNING_OBJECT (self, "Can't set fd... it is already set.");
1802     return;
1803   }
1804
1805   if (fd >= 0) {
1806     self->devname = drmGetDeviceNameFromFd (fd);
1807     if (!self->devname) {
1808       GST_WARNING_OBJECT (self, "Failed to verify fd is a DRM fd.");
1809       return;
1810     }
1811
1812     self->fd = fd;
1813     self->is_internal_fd = FALSE;
1814   }
1815 }
1816
1817 static void
1818 _invalidate_external_fd (GstKMSSink * self, GParamSpec * pspec)
1819 {
1820   if (self->is_internal_fd)
1821     return;
1822
1823   GST_WARNING_OBJECT (self, "Unsetting fd... %s has priority.",
1824       g_param_spec_get_name (pspec));
1825
1826   self->fd = -1;
1827   self->is_internal_fd = TRUE;
1828 }
1829
1830 static void
1831 gst_kms_sink_set_property (GObject * object, guint prop_id,
1832     const GValue * value, GParamSpec * pspec)
1833 {
1834   GstKMSSink *sink;
1835
1836   sink = GST_KMS_SINK (object);
1837
1838   switch (prop_id) {
1839     case PROP_DRIVER_NAME:
1840       _invalidate_external_fd (sink, pspec);
1841       g_free (sink->devname);
1842       sink->devname = g_value_dup_string (value);
1843       break;
1844     case PROP_BUS_ID:
1845       _invalidate_external_fd (sink, pspec);
1846       g_free (sink->bus_id);
1847       sink->bus_id = g_value_dup_string (value);
1848       break;
1849     case PROP_CONNECTOR_ID:
1850       sink->conn_id = g_value_get_int (value);
1851       break;
1852     case PROP_PLANE_ID:
1853       sink->plane_id = g_value_get_int (value);
1854       break;
1855     case PROP_FORCE_MODESETTING:
1856       sink->modesetting_enabled = g_value_get_boolean (value);
1857       break;
1858     case PROP_RESTORE_CRTC:
1859       sink->restore_crtc = g_value_get_boolean (value);
1860       break;
1861     case PROP_CAN_SCALE:
1862       sink->can_scale = g_value_get_boolean (value);
1863       break;
1864     case PROP_CONNECTOR_PROPS:{
1865       const GstStructure *s = gst_value_get_structure (value);
1866
1867       g_clear_pointer (&sink->connector_props, gst_structure_free);
1868
1869       if (s)
1870         sink->connector_props = gst_structure_copy (s);
1871
1872       break;
1873     }
1874     case PROP_PLANE_PROPS:{
1875       const GstStructure *s = gst_value_get_structure (value);
1876
1877       g_clear_pointer (&sink->plane_props, gst_structure_free);
1878
1879       if (s)
1880         sink->plane_props = gst_structure_copy (s);
1881
1882       break;
1883     }
1884     case PROP_FD:
1885       _validate_and_set_external_fd (sink, g_value_get_int (value));
1886       break;
1887     default:
1888       if (!gst_video_overlay_set_property (object, PROP_N, prop_id, value))
1889         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1890       break;
1891   }
1892 }
1893
1894 static void
1895 gst_kms_sink_get_property (GObject * object, guint prop_id,
1896     GValue * value, GParamSpec * pspec)
1897 {
1898   GstKMSSink *sink;
1899
1900   sink = GST_KMS_SINK (object);
1901
1902   switch (prop_id) {
1903     case PROP_DRIVER_NAME:
1904       g_value_set_string (value, sink->devname);
1905       break;
1906     case PROP_BUS_ID:
1907       g_value_set_string (value, sink->bus_id);
1908       break;
1909     case PROP_CONNECTOR_ID:
1910       g_value_set_int (value, sink->conn_id);
1911       break;
1912     case PROP_PLANE_ID:
1913       g_value_set_int (value, sink->plane_id);
1914       break;
1915     case PROP_FORCE_MODESETTING:
1916       g_value_set_boolean (value, sink->modesetting_enabled);
1917       break;
1918     case PROP_RESTORE_CRTC:
1919       g_value_set_boolean (value, sink->restore_crtc);
1920       break;
1921     case PROP_CAN_SCALE:
1922       g_value_set_boolean (value, sink->can_scale);
1923       break;
1924     case PROP_DISPLAY_WIDTH:
1925       GST_OBJECT_LOCK (sink);
1926       g_value_set_int (value, sink->hdisplay);
1927       GST_OBJECT_UNLOCK (sink);
1928       break;
1929     case PROP_DISPLAY_HEIGHT:
1930       GST_OBJECT_LOCK (sink);
1931       g_value_set_int (value, sink->vdisplay);
1932       GST_OBJECT_UNLOCK (sink);
1933       break;
1934     case PROP_CONNECTOR_PROPS:
1935       gst_value_set_structure (value, sink->connector_props);
1936       break;
1937     case PROP_PLANE_PROPS:
1938       gst_value_set_structure (value, sink->plane_props);
1939       break;
1940     case PROP_FD:
1941       g_value_set_int (value, sink->fd);
1942       break;
1943     default:
1944       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1945       break;
1946   }
1947 }
1948
1949 static void
1950 gst_kms_sink_finalize (GObject * object)
1951 {
1952   GstKMSSink *sink;
1953
1954   sink = GST_KMS_SINK (object);
1955   g_clear_pointer (&sink->devname, g_free);
1956   g_clear_pointer (&sink->bus_id, g_free);
1957   gst_poll_free (sink->poll);
1958   g_clear_pointer (&sink->connector_props, gst_structure_free);
1959   g_clear_pointer (&sink->plane_props, gst_structure_free);
1960   g_clear_pointer (&sink->tmp_kmsmem, gst_memory_unref);
1961
1962   G_OBJECT_CLASS (parent_class)->finalize (object);
1963 }
1964
1965 static void
1966 gst_kms_sink_init (GstKMSSink * sink)
1967 {
1968   sink->fd = -1;
1969   sink->is_internal_fd = TRUE;
1970   sink->conn_id = -1;
1971   sink->plane_id = -1;
1972   sink->can_scale = TRUE;
1973   gst_poll_fd_init (&sink->pollfd);
1974   sink->poll = gst_poll_new (TRUE);
1975   gst_video_info_init (&sink->vinfo);
1976 }
1977
1978 static void
1979 gst_kms_sink_class_init (GstKMSSinkClass * klass)
1980 {
1981   GObjectClass *gobject_class;
1982   GstElementClass *element_class;
1983   GstBaseSinkClass *basesink_class;
1984   GstVideoSinkClass *videosink_class;
1985   GstCaps *caps;
1986
1987   gobject_class = G_OBJECT_CLASS (klass);
1988   element_class = GST_ELEMENT_CLASS (klass);
1989   basesink_class = GST_BASE_SINK_CLASS (klass);
1990   videosink_class = GST_VIDEO_SINK_CLASS (klass);
1991
1992   gst_element_class_set_static_metadata (element_class, "KMS video sink",
1993       "Sink/Video", GST_PLUGIN_DESC, "Víctor Jáquez <vjaquez@igalia.com>");
1994
1995   caps = gst_kms_sink_caps_template_fill ();
1996   gst_element_class_add_pad_template (element_class,
1997       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps));
1998   gst_caps_unref (caps);
1999
2000   basesink_class->start = GST_DEBUG_FUNCPTR (gst_kms_sink_start);
2001   basesink_class->stop = GST_DEBUG_FUNCPTR (gst_kms_sink_stop);
2002   basesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_kms_sink_set_caps);
2003   basesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_kms_sink_get_caps);
2004   basesink_class->propose_allocation = gst_kms_sink_propose_allocation;
2005   basesink_class->query = gst_kms_sink_query;
2006
2007   videosink_class->show_frame = gst_kms_sink_show_frame;
2008
2009   gobject_class->finalize = gst_kms_sink_finalize;
2010   gobject_class->set_property = gst_kms_sink_set_property;
2011   gobject_class->get_property = gst_kms_sink_get_property;
2012
2013   /**
2014    * kmssink:driver-name:
2015    *
2016    * If you have a system with multiple GPUs, you can choose which GPU
2017    * to use setting the DRM device driver name. Otherwise, the first
2018    * one from an internal list is used.
2019    */
2020   g_properties[PROP_DRIVER_NAME] = g_param_spec_string ("driver-name",
2021       "device name", "DRM device driver name", NULL,
2022       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2023
2024   /**
2025    * kmssink:bus-id:
2026    *
2027    * If you have a system with multiple displays for the same driver-name,
2028    * you can choose which display to use by setting the DRM bus ID. Otherwise,
2029    * the driver decides which one.
2030    */
2031   g_properties[PROP_BUS_ID] = g_param_spec_string ("bus-id",
2032       "Bus ID", "DRM bus ID", NULL,
2033       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2034
2035   /**
2036    * kmssink:connector-id:
2037    *
2038    * A GPU has several output connectors, for example: LVDS, VGA,
2039    * HDMI, etc. By default the first LVDS is tried, then the first
2040    * eDP, and at the end, the first connected one.
2041    */
2042   g_properties[PROP_CONNECTOR_ID] = g_param_spec_int ("connector-id",
2043       "Connector ID", "DRM connector id", -1, G_MAXINT32, -1,
2044       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2045
2046    /**
2047    * kmssink:plane-id:
2048    *
2049    * There could be several planes associated with a CRTC.
2050    * By default the first plane that's possible to use with a given
2051    * CRTC is tried.
2052    */
2053   g_properties[PROP_PLANE_ID] = g_param_spec_int ("plane-id",
2054       "Plane ID", "DRM plane id", -1, G_MAXINT32, -1,
2055       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2056
2057   /**
2058    * kmssink:force-modesetting:
2059    *
2060    * If the output connector is already active, the sink automatically uses an
2061    * overlay plane. Enforce mode setting in the kms sink and output to the
2062    * base plane to override the automatic behavior.
2063    */
2064   g_properties[PROP_FORCE_MODESETTING] =
2065       g_param_spec_boolean ("force-modesetting", "Force modesetting",
2066       "When enabled, the sink try to configure the display mode", FALSE,
2067       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2068
2069   /**
2070    * kmssink:restore-crtc:
2071    *
2072    * Restore previous CRTC setting if new CRTC mode was set forcefully.
2073    * By default this is enabled if user set CRTC with a new mode on an already
2074    * active CRTC wich was having a valid mode.
2075    */
2076   g_properties[PROP_RESTORE_CRTC] =
2077       g_param_spec_boolean ("restore-crtc", "Restore CRTC mode",
2078       "When enabled and CRTC was set with a new mode, previous CRTC mode will"
2079       "be restored when going to NULL state.", TRUE,
2080       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2081
2082   /**
2083    * kmssink:can-scale:
2084    *
2085    * User can tell kmssink if the driver can support scale.
2086    */
2087   g_properties[PROP_CAN_SCALE] =
2088       g_param_spec_boolean ("can-scale", "can scale",
2089       "User can tell kmssink if the driver can support scale", TRUE,
2090       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2091
2092   /**
2093    * kmssink:display-width
2094    *
2095    * Actual width of the display. This is read only and only available in
2096    * PAUSED and PLAYING state. It's meant to be used with
2097    * gst_video_overlay_set_render_rectangle() function.
2098    */
2099   g_properties[PROP_DISPLAY_WIDTH] =
2100       g_param_spec_int ("display-width", "Display Width",
2101       "Width of the display surface in pixels", 0, G_MAXINT, 0,
2102       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2103
2104   /**
2105    * kmssink:display-height
2106    *
2107    * Actual height of the display. This is read only and only available in
2108    * PAUSED and PLAYING state. It's meant to be used with
2109    * gst_video_overlay_set_render_rectangle() function.
2110    */
2111   g_properties[PROP_DISPLAY_HEIGHT] =
2112       g_param_spec_int ("display-height", "Display Height",
2113       "Height of the display surface in pixels", 0, G_MAXINT, 0,
2114       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
2115
2116   /**
2117    * kmssink:connector-properties:
2118    *
2119    * Additional properties for the connector. Keys are strings and values
2120    * unsigned 64 bits integers.
2121    *
2122    * Since: 1.16
2123    */
2124   g_properties[PROP_CONNECTOR_PROPS] =
2125       g_param_spec_boxed ("connector-properties", "Connector Properties",
2126       "Additional properties for the connector",
2127       GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2128
2129   /**
2130    * kmssink:plane-properties:
2131    *
2132    * Additional properties for the plane. Keys are strings and values
2133    * unsigned 64 bits integers.
2134    *
2135    * Since: 1.16
2136    */
2137   g_properties[PROP_PLANE_PROPS] =
2138       g_param_spec_boxed ("plane-properties", "Connector Plane",
2139       "Additional properties for the plane",
2140       GST_TYPE_STRUCTURE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
2141
2142   /**
2143    * kmssink:fd:
2144    *
2145    * You can supply your own DRM file descriptor.  By default, the sink will
2146    * open its own DRM file descriptor.
2147    *
2148    * Since: 1.22
2149    */
2150   g_properties[PROP_FD] =
2151       g_param_spec_int ("fd", "File Descriptor",
2152       "DRM file descriptor", -1, G_MAXINT, -1,
2153       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT);
2154
2155   g_object_class_install_properties (gobject_class, PROP_N, g_properties);
2156
2157   gst_video_overlay_install_properties (gobject_class, PROP_N);
2158 }
2159
2160 static gboolean
2161 plugin_init (GstPlugin * plugin)
2162 {
2163   return GST_ELEMENT_REGISTER (kmssink, plugin);
2164 }
2165
2166 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, kms,
2167     GST_PLUGIN_DESC, plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
2168     GST_PACKAGE_ORIGIN)