xref: /OK3568_Linux_fs/external/gstreamer-rockchip/gst/rkximage/ximagesink.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 #ifdef HAVE_CONFIG_H
2 #include "config.h"
3 #endif
4 
5 /* Our interfaces */
6 #include <gst/video/video.h>
7 #include <gst/allocators/gstdmabuf.h>
8 #include <gst/video/navigation.h>
9 #include <gst/video/videooverlay.h>
10 
11 #include <gst/video/gstvideometa.h>
12 
13 /* Debugging category */
14 #include <gst/gstinfo.h>
15 
16 /* for XkbKeycodeToKeysym */
17 #include <X11/XKBlib.h>
18 
19 #include <drm.h>
20 #include <xf86drm.h>
21 #include <xf86drmMode.h>
22 #include <drm_fourcc.h>
23 #include <fcntl.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 /* Object header */
28 #include "ximagesink.h"
29 #include "gstkmsutils.h"
30 #include "gstkmsbufferpool.h"
31 #include "gstkmsallocator.h"
32 
33 /* A random dark color */
34 #define RKXIMAGE_COLOR_KEY 0x010203
35 #define RK_COLOR_KEY_EN (1UL << 31)
36 
37 GST_DEBUG_CATEGORY (gst_debug_x_image_sink);
38 #define GST_CAT_DEFAULT gst_debug_x_image_sink
39 
40 #define EMPTY_RECT(r) !((r).x || (r).y || (r).w || (r).h)
41 #define RECT_EQUAL(r1, r2) \
42     ((r1).x == (r2).x && (r1).y == (r2).y && \
43      (r1).w == (r2.w) && (r1).h == (r2).h)
44 
45 typedef struct
46 {
47   unsigned long flags;
48   unsigned long functions;
49   unsigned long decorations;
50   long input_mode;
51   unsigned long status;
52 }
53 MotifWmHints, MwmHints;
54 
55 #define MWM_HINTS_DECORATIONS   (1L << 1)
56 
57 static void gst_x_image_sink_reset (GstRkXImageSink * ximagesink);
58 static void gst_x_image_sink_xwindow_update_geometry (GstRkXImageSink *
59     ximagesink);
60 static void gst_x_image_sink_expose (GstVideoOverlay * overlay);
61 static void gst_x_image_sink_xwindow_clear (GstRkXImageSink * ximagesink,
62     GstXWindow * xwindow);
63 static GstFlowReturn
64 gst_x_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf);
65 
66 static GstStaticPadTemplate gst_x_image_sink_sink_template_factory =
67 GST_STATIC_PAD_TEMPLATE ("sink",
68     GST_PAD_SINK,
69     GST_PAD_ALWAYS,
70     GST_STATIC_CAPS ("video/x-raw, "
71         "framerate = (fraction) [ 0, MAX ], "
72         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
73     );
74 
75 enum
76 {
77   PROP_0,
78   PROP_DISPLAY,
79   PROP_SYNCHRONOUS,
80   PROP_HANDLE_EVENTS,
81   PROP_HANDLE_EXPOSE,
82   PROP_WINDOW_WIDTH,
83   PROP_WINDOW_HEIGHT,
84   PROP_DRIVER_NAME,
85   PROP_BUS_ID,
86   PROP_CONNECTOR_ID,
87   PROP_PLANE_ID,
88   PROP_FORCE_ASPECT_RATIO,
89 };
90 
91 /* ============================================================= */
92 /*                                                               */
93 /*                       Public Methods                          */
94 /*                                                               */
95 /* ============================================================= */
96 
97 /* =========================================== */
98 /*                                             */
99 /*          Object typing & Creation           */
100 /*                                             */
101 /* =========================================== */
102 static void gst_x_image_sink_navigation_init (GstNavigationInterface * iface);
103 static void gst_x_image_sink_video_overlay_init (GstVideoOverlayInterface *
104     iface);
105 #define gst_x_image_sink_parent_class parent_class
106 G_DEFINE_TYPE_WITH_CODE (GstRkXImageSink, gst_x_image_sink, GST_TYPE_VIDEO_SINK,
107     G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
108         gst_x_image_sink_navigation_init);
109     G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
110         gst_x_image_sink_video_overlay_init));
111 
112 /* ============================================================= */
113 /*                                                               */
114 /*                       Private Methods                         */
115 /*                                                               */
116 /* ============================================================= */
117 /*drm*/
118 
119 static int
drm_plane_get_type(int fd,drmModePlane * plane)120 drm_plane_get_type (int fd, drmModePlane * plane)
121 {
122   drmModeObjectPropertiesPtr props;
123   drmModePropertyPtr prop;
124   int i, type = -1;
125 
126   props = drmModeObjectGetProperties (fd, plane->plane_id,
127       DRM_MODE_OBJECT_PLANE);
128   if (!props)
129     return -1;
130 
131   for (i = 0; i < props->count_props; i++) {
132     prop = drmModeGetProperty (fd, props->props[i]);
133     if (prop && !strcmp (prop->name, "type"))
134       type = props->prop_values[i];
135     drmModeFreeProperty (prop);
136   }
137 
138   drmModeFreeObjectProperties (props);
139   return type;
140 }
141 
142 static drmModePlane *
drm_find_plane_for_crtc_by_type(int fd,drmModeRes * res,drmModePlaneRes * pres,int crtc_id,int type)143 drm_find_plane_for_crtc_by_type (int fd, drmModeRes * res,
144     drmModePlaneRes * pres, int crtc_id, int type)
145 {
146   int i, pipe = -1, num_primary = 0;
147 
148   for (i = 0; i < res->count_crtcs; i++) {
149     if (crtc_id == res->crtcs[i]) {
150       pipe = i;
151       break;
152     }
153   }
154 
155   if (pipe == -1)
156     return NULL;
157 
158   for (i = 0; i < pres->count_planes; i++) {
159     drmModePlane *plane = drmModeGetPlane (fd, pres->planes[i]);
160     int plane_type = drm_plane_get_type (fd, plane);
161     int primary = plane_type == DRM_PLANE_TYPE_PRIMARY;
162 
163     num_primary += primary;
164     if ((plane->possible_crtcs & (1 << pipe)) && plane_type == type) {
165       if (!primary || pipe == num_primary - 1)
166         return plane;
167     }
168     drmModeFreePlane (plane);
169   }
170 
171   return NULL;
172 }
173 
174 static drmModeCrtc *
drm_find_crtc_for_connector(int fd,drmModeRes * res,drmModeConnector * conn,guint * pipe)175 drm_find_crtc_for_connector (int fd, drmModeRes * res, drmModeConnector * conn,
176     guint * pipe)
177 {
178   int i;
179   int crtc_id;
180   drmModeEncoder *enc;
181   drmModeCrtc *crtc;
182 
183   crtc_id = -1;
184   for (i = 0; i < res->count_encoders; i++) {
185     enc = drmModeGetEncoder (fd, res->encoders[i]);
186     if (enc) {
187       if (enc->encoder_id == conn->encoder_id) {
188         crtc_id = enc->crtc_id;
189         drmModeFreeEncoder (enc);
190         break;
191       }
192       drmModeFreeEncoder (enc);
193     }
194   }
195 
196   if (crtc_id == -1)
197     return NULL;
198 
199   for (i = 0; i < res->count_crtcs; i++) {
200     crtc = drmModeGetCrtc (fd, res->crtcs[i]);
201     if (crtc) {
202       if (crtc_id == crtc->crtc_id) {
203         if (pipe)
204           *pipe = i;
205         return crtc;
206       }
207       drmModeFreeCrtc (crtc);
208     }
209   }
210 
211   return NULL;
212 }
213 
214 static gboolean
drm_connector_is_used(int fd,drmModeRes * res,drmModeConnector * conn)215 drm_connector_is_used (int fd, drmModeRes * res, drmModeConnector * conn)
216 {
217   gboolean result;
218   drmModeCrtc *crtc;
219 
220   result = FALSE;
221   crtc = drm_find_crtc_for_connector (fd, res, conn, NULL);
222   if (crtc) {
223     result = crtc->buffer_id != 0;
224     drmModeFreeCrtc (crtc);
225   }
226 
227   return result;
228 }
229 
230 static drmModeConnector *
drm_find_used_connector_by_type(int fd,drmModeRes * res,int type)231 drm_find_used_connector_by_type (int fd, drmModeRes * res, int type)
232 {
233   int i;
234   drmModeConnector *conn;
235 
236   conn = NULL;
237   for (i = 0; i < res->count_connectors; i++) {
238     conn = drmModeGetConnector (fd, res->connectors[i]);
239     if (conn) {
240       if ((conn->connector_type == type)
241           && drm_connector_is_used (fd, res, conn))
242         return conn;
243       drmModeFreeConnector (conn);
244     }
245   }
246 
247   return NULL;
248 }
249 
250 static drmModeConnector *
drm_find_first_used_connector(int fd,drmModeRes * res)251 drm_find_first_used_connector (int fd, drmModeRes * res)
252 {
253   int i;
254   drmModeConnector *conn;
255 
256   conn = NULL;
257   for (i = 0; i < res->count_connectors; i++) {
258     conn = drmModeGetConnector (fd, res->connectors[i]);
259     if (conn) {
260       if (drm_connector_is_used (fd, res, conn))
261         return conn;
262       drmModeFreeConnector (conn);
263     }
264   }
265 
266   return NULL;
267 }
268 
269 static drmModeConnector *
drm_find_main_monitor(int fd,drmModeRes * res)270 drm_find_main_monitor (int fd, drmModeRes * res)
271 {
272   /* Find the LVDS and eDP connectors: those are the main screens. */
273   static const int priority[] = { DRM_MODE_CONNECTOR_LVDS,
274     DRM_MODE_CONNECTOR_eDP
275   };
276   int i;
277   drmModeConnector *conn;
278 
279   conn = NULL;
280   for (i = 0; !conn && i < G_N_ELEMENTS (priority); i++)
281     conn = drm_find_used_connector_by_type (fd, res, priority[i]);
282 
283   /* if we didn't find a connector, grab the first one in use */
284   if (!conn)
285     conn = drm_find_first_used_connector (fd, res);
286 
287   return conn;
288 }
289 
290 static void
drm_log_version(GstRkXImageSink * self)291 drm_log_version (GstRkXImageSink * self)
292 {
293 #ifndef GST_DISABLE_GST_DEBUG
294   drmVersion *v;
295 
296   v = drmGetVersion (self->fd);
297   if (v) {
298     GST_INFO_OBJECT (self, "DRM v%d.%d.%d [%s — %s — %s]", v->version_major,
299         v->version_minor, v->version_patchlevel, GST_STR_NULL (v->name),
300         GST_STR_NULL (v->desc), GST_STR_NULL (v->date));
301     drmFreeVersion (v);
302   } else {
303     GST_WARNING_OBJECT (self, "could not get driver information: %s",
304         GST_STR_NULL (self->devname));
305   }
306 #endif
307   return;
308 }
309 
310 static gboolean
drm_get_caps(GstRkXImageSink * self)311 drm_get_caps (GstRkXImageSink * self)
312 {
313   gint ret;
314   guint64 has_dumb_buffer;
315   guint64 has_prime;
316   guint64 has_async_page_flip;
317 
318   has_dumb_buffer = 0;
319   ret = drmGetCap (self->fd, DRM_CAP_DUMB_BUFFER, &has_dumb_buffer);
320   if (ret)
321     GST_WARNING_OBJECT (self, "could not get dumb buffer capability");
322   if (has_dumb_buffer == 0) {
323     GST_ERROR_OBJECT (self, "driver cannot handle dumb buffers");
324     return FALSE;
325   }
326 
327   has_prime = 0;
328   ret = drmGetCap (self->fd, DRM_CAP_PRIME, &has_prime);
329   if (ret)
330     GST_WARNING_OBJECT (self, "could not get prime capability");
331   else {
332     self->has_prime_import = (gboolean) (has_prime & DRM_PRIME_CAP_IMPORT);
333     self->has_prime_export = (gboolean) (has_prime & DRM_PRIME_CAP_EXPORT);
334   }
335 
336   has_async_page_flip = 0;
337   ret = drmGetCap (self->fd, DRM_CAP_ASYNC_PAGE_FLIP, &has_async_page_flip);
338   if (ret)
339     GST_WARNING_OBJECT (self, "could not get async page flip capability");
340   else
341     self->has_async_page_flip = (gboolean) has_async_page_flip;
342 
343   GST_INFO_OBJECT (self,
344       "prime import (%s) / prime export (%s) / async page flip (%s)",
345       self->has_prime_import ? "✓" : "✗",
346       self->has_prime_export ? "✓" : "✗",
347       self->has_async_page_flip ? "✓" : "✗");
348 
349   return TRUE;
350 }
351 
352 static void
check_afbc(GstRkXImageSink * self,drmModePlane * plane,guint32 drmfmt,gboolean * linear,gboolean * afbc)353 check_afbc (GstRkXImageSink * self, drmModePlane * plane, guint32 drmfmt,
354     gboolean * linear, gboolean * afbc)
355 {
356   drmModeObjectPropertiesPtr props;
357   drmModePropertyBlobPtr blob;
358   drmModePropertyPtr prop;
359   drmModeResPtr res;
360   struct drm_format_modifier_blob *header;
361   struct drm_format_modifier *modifiers;
362   guint32 *formats;
363   guint64 value = 0;
364   gint i, j;
365 
366   *linear = *afbc = FALSE;
367 
368   res = drmModeGetResources (self->fd);
369   if (!res)
370     return;
371 
372   props = drmModeObjectGetProperties (self->fd, plane->plane_id,
373       DRM_MODE_OBJECT_PLANE);
374   if (!props) {
375     drmModeFreeResources (res);
376     return;
377   }
378 
379   for (i = 0; i < props->count_props && !value; i++) {
380     prop = drmModeGetProperty (self->fd, props->props[i]);
381     if (!prop)
382       continue;
383 
384     if (!strcmp (prop->name, "IN_FORMATS"))
385       value = props->prop_values[i];
386 
387     drmModeFreeProperty (prop);
388   }
389 
390   drmModeFreeObjectProperties (props);
391   drmModeFreeResources (res);
392 
393   /* No modifiers */
394   if (!value) {
395     *linear = TRUE;
396     return;
397   }
398 
399   blob = drmModeGetPropertyBlob (self->fd, value);
400   if (!blob)
401     return;
402 
403   header = blob->data;
404   modifiers = (struct drm_format_modifier *)
405     ((gchar *) header + header->modifiers_offset);
406   formats = (guint32 *) ((gchar *) header + header->formats_offset);
407 
408   for (i = 0; i < header->count_formats; i++) {
409     if (formats[i] != drmfmt)
410       continue;
411 
412     for (j = 0; j < header->count_modifiers; j++) {
413       struct drm_format_modifier *mod = &modifiers[j];
414 
415       if ((i < mod->offset) || (i > mod->offset + 63))
416         continue;
417       if (!(mod->formats & (1 << (i - mod->offset))))
418         continue;
419 
420       if (mod->modifier == DRM_AFBC_MODIFIER)
421         *afbc = TRUE;
422       else if (mod->modifier == DRM_FORMAT_MOD_LINEAR)
423         *linear = TRUE;
424     }
425   }
426 
427   drmModeFreePropertyBlob(blob);
428 }
429 
430 static gboolean
drm_ensure_allowed_caps(GstRkXImageSink * self,drmModePlane * plane,drmModeRes * res)431 drm_ensure_allowed_caps (GstRkXImageSink * self, drmModePlane * plane,
432     drmModeRes * res)
433 {
434   GstCaps *out_caps, *caps;
435   int i;
436   GstVideoFormat fmt;
437   const gchar *format;
438 
439   if (self->allowed_caps)
440     return TRUE;
441 
442   out_caps = gst_caps_new_empty ();
443   if (!out_caps)
444     return FALSE;
445 
446   for (i = 0; i < plane->count_formats; i++) {
447     gboolean linear = FALSE, afbc = FALSE;
448 
449     check_afbc (self, plane, plane->formats[i], &linear, &afbc);
450 
451     if (plane->formats[i] == DRM_FORMAT_YUV420_8BIT)
452       fmt = GST_VIDEO_FORMAT_NV12;
453     else if (plane->formats[i] == DRM_FORMAT_YUV420_10BIT)
454       fmt = GST_VIDEO_FORMAT_NV12_10LE40;
455     else if (afbc && plane->formats[i] == DRM_FORMAT_YUYV)
456       fmt = GST_VIDEO_FORMAT_NV16;
457       else
458         fmt = gst_video_format_from_drm (plane->formats[i]);
459 
460     if (fmt == GST_VIDEO_FORMAT_UNKNOWN) {
461       GST_INFO_OBJECT (self, "ignoring format %" GST_FOURCC_FORMAT,
462           GST_FOURCC_ARGS (plane->formats[i]));
463       continue;
464     }
465 
466     format = gst_video_format_to_string (fmt);
467     caps = gst_caps_new_simple ("video/x-raw", "format", G_TYPE_STRING, format,
468         "width", GST_TYPE_INT_RANGE, res->min_width, res->max_width,
469         "height", GST_TYPE_INT_RANGE, res->min_height, res->max_height,
470         "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
471     if (!caps)
472       continue;
473 
474     if (afbc) {
475       GstCaps *afbc_caps = gst_caps_copy (caps);
476       gst_caps_set_simple (afbc_caps, "arm-afbc", G_TYPE_INT, 1, NULL);
477 
478       if (linear) {
479         gst_caps_append (caps, afbc_caps);
480       } else {
481         gst_caps_replace (&caps, afbc_caps);
482         gst_caps_unref (afbc_caps);
483       }
484     }
485 
486     out_caps = gst_caps_merge (out_caps, caps);
487   }
488 
489   self->allowed_caps = gst_caps_simplify (out_caps);
490 
491   GST_DEBUG_OBJECT (self, "allowed caps = %" GST_PTR_FORMAT,
492       self->allowed_caps);
493 
494   return TRUE;
495 }
496 
497 static gboolean
drm_plane_set_property(GstRkXImageSink * self,drmModePlane * plane,const char * prop_name,uint64_t prop_value)498 drm_plane_set_property (GstRkXImageSink * self, drmModePlane * plane,
499     const char *prop_name, uint64_t prop_value)
500 {
501   drmModeObjectPropertiesPtr props;
502   drmModePropertyPtr prop;
503   int i, ret = -1;
504 
505   props = drmModeObjectGetProperties (self->fd, plane->plane_id,
506       DRM_MODE_OBJECT_PLANE);
507   if (!props)
508     return FALSE;
509 
510   for (i = 0; i < props->count_props; i++) {
511     prop = drmModeGetProperty (self->fd, props->props[i]);
512     if (prop && !strcmp (prop->name, prop_name)) {
513       ret = drmModeObjectSetProperty (self->fd, plane->plane_id,
514           DRM_MODE_OBJECT_PLANE, props->props[i], prop_value);
515     }
516     drmModeFreeProperty (prop);
517   }
518 
519   drmModeFreeObjectProperties (props);
520   return ret < 0 ? FALSE : TRUE;
521 }
522 
523 static gboolean
drm_prepare_planes(GstRkXImageSink * self,drmModeRes * res,drmModePlaneRes * pres)524 drm_prepare_planes (GstRkXImageSink * self, drmModeRes * res,
525     drmModePlaneRes * pres)
526 {
527   drmModePlane *plane;
528   gboolean ret = FALSE;
529 
530   if (drmSetClientCap (self->fd, DRM_CLIENT_CAP_ATOMIC, 1))
531     return FALSE;
532 
533   /* Apply colorkey to primary plane */
534   plane = drm_find_plane_for_crtc_by_type (self->fd, res, pres,
535       self->crtc_id, DRM_PLANE_TYPE_PRIMARY);
536   if (!plane)
537     return FALSE;
538 
539   if (!drm_plane_set_property (self, plane, "colorkey",
540           RKXIMAGE_COLOR_KEY | RK_COLOR_KEY_EN))
541     goto out;
542 
543   GST_DEBUG_OBJECT (self, "applied colorkey = 0x%x to plane %d",
544       RKXIMAGE_COLOR_KEY, plane->plane_id);
545 
546   /* Uper primary plane */
547   if (!drm_plane_set_property (self, plane, "ZPOS", 1) &&
548       !drm_plane_set_property (self, plane, "zpos", 1))
549     goto out;
550 
551   drmModeFreePlane (plane);
552 
553   /* Lower overlay plane */
554   plane = drmModeGetPlane (self->fd, self->plane_id);
555   if (!plane)
556     goto out;
557 
558   if (!drm_plane_set_property (self, plane, "ZPOS", 0) &&
559       !drm_plane_set_property (self, plane, "zpos", 0))
560     goto out;
561 
562   GST_DEBUG_OBJECT (self, "set plane %d zpos to 0", plane->plane_id);
563 
564   ret = TRUE;
565 
566 out:
567   drmModeFreePlane (plane);
568   return ret;
569 }
570 
571 /*kms*/
572 static void
ensure_kms_allocator(GstRkXImageSink * self)573 ensure_kms_allocator (GstRkXImageSink * self)
574 {
575   if (self->allocator)
576     return;
577   self->allocator = gst_kms_allocator_new (self->fd);
578 }
579 
580 static GstBufferPool *
gst_kms_sink_create_pool(GstRkXImageSink * self,GstCaps * caps,gsize size,gint min)581 gst_kms_sink_create_pool (GstRkXImageSink * self, GstCaps * caps, gsize size,
582     gint min)
583 {
584   GstBufferPool *pool;
585   GstStructure *config;
586 
587   pool = gst_kms_buffer_pool_new ();
588   if (!pool)
589     goto pool_failed;
590 
591   config = gst_buffer_pool_get_config (pool);
592   gst_buffer_pool_config_set_params (config, caps, size, min, 0);
593   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
594 
595   ensure_kms_allocator (self);
596   gst_buffer_pool_config_set_allocator (config, self->allocator, NULL);
597 
598   if (!gst_buffer_pool_set_config (pool, config))
599     goto config_failed;
600 
601   return pool;
602 
603   /* ERRORS */
604 pool_failed:
605   {
606     GST_ERROR_OBJECT (self, "failed to create buffer pool");
607     return NULL;
608   }
609 config_failed:
610   {
611     GST_ERROR_OBJECT (self, "failed to set config");
612     gst_object_unref (pool);
613     return NULL;
614   }
615 }
616 
617 static gboolean
gst_kms_sink_propose_allocation(GstBaseSink * bsink,GstQuery * query)618 gst_kms_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
619 {
620   GstRkXImageSink *self;
621   GstCaps *caps;
622   gboolean need_pool;
623   GstVideoInfo vinfo;
624   GstBufferPool *pool;
625   GstStructure *s;
626   gsize size;
627   gint value;
628 
629   self = GST_X_IMAGE_SINK (bsink);
630 
631   gst_query_parse_allocation (query, &caps, &need_pool);
632   if (!caps)
633     goto no_caps;
634   if (!gst_video_info_from_caps (&vinfo, caps))
635     goto invalid_caps;
636 
637   s = gst_caps_get_structure (caps, 0);
638   if (gst_structure_get_int (s, "arm-afbc", &value) && value)
639     goto afbc_caps;
640 
641   size = GST_VIDEO_INFO_SIZE (&vinfo);
642 
643   pool = NULL;
644   if (need_pool) {
645     pool = gst_kms_sink_create_pool (self, caps, size, 0);
646     if (!pool)
647       goto no_pool;
648 
649     /* Only export for pool used upstream */
650     if (self->has_prime_export) {
651       GstStructure *config = gst_buffer_pool_get_config (pool);
652       gst_buffer_pool_config_add_option (config,
653           GST_BUFFER_POOL_OPTION_KMS_PRIME_EXPORT);
654       gst_buffer_pool_set_config (pool, config);
655     }
656   }
657 
658   /* we need at least 2 buffer because we hold on to the last one */
659   gst_query_add_allocation_pool (query, pool, size, 2, 0);
660   if (pool)
661     gst_object_unref (pool);
662 
663   gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
664   gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
665 
666   return TRUE;
667 
668   /* ERRORS */
669 no_caps:
670   {
671     GST_DEBUG_OBJECT (bsink, "no caps specified");
672     return FALSE;
673   }
674 invalid_caps:
675   {
676     GST_DEBUG_OBJECT (bsink, "invalid caps specified");
677     return FALSE;
678   }
679 afbc_caps:
680   {
681     GST_DEBUG_OBJECT (bsink, "no allocation for AFBC");
682     return FALSE;
683   }
684 no_pool:
685   {
686     /* Already warned in create_pool */
687     return FALSE;
688   }
689 }
690 
691 static gboolean
gst_kms_sink_import_dmabuf(GstRkXImageSink * self,GstBuffer * inbuf,GstBuffer ** outbuf)692 gst_kms_sink_import_dmabuf (GstRkXImageSink * self, GstBuffer * inbuf,
693     GstBuffer ** outbuf)
694 {
695   gint prime_fds[GST_VIDEO_MAX_PLANES] = { 0, };
696   GstVideoMeta *meta;
697   guint i, n_mem, n_planes;
698   GstKMSMemory *kmsmem;
699   guint mems_idx[GST_VIDEO_MAX_PLANES];
700   gsize mems_skip[GST_VIDEO_MAX_PLANES];
701   GstMemory *mems[GST_VIDEO_MAX_PLANES];
702 
703   if (!self->has_prime_import)
704     return FALSE;
705 
706   /* This will eliminate most non-dmabuf out there */
707   if (!gst_is_dmabuf_memory (gst_buffer_peek_memory (inbuf, 0)))
708     return FALSE;
709 
710   n_planes = GST_VIDEO_INFO_N_PLANES (&self->vinfo);
711   n_mem = gst_buffer_n_memory (inbuf);
712   meta = gst_buffer_get_video_meta (inbuf);
713 
714   GST_TRACE_OBJECT (self, "Found a dmabuf with %u planes and %u memories",
715       n_planes, n_mem);
716 
717   /* We cannot have multiple dmabuf per plane */
718   if (n_mem > n_planes)
719     return FALSE;
720   g_assert (n_planes != 0);
721 
722   /* Update video info based on video meta */
723   if (meta) {
724     GST_VIDEO_INFO_WIDTH (&self->vinfo) = meta->width;
725     GST_VIDEO_INFO_HEIGHT (&self->vinfo) = meta->height;
726 
727     for (i = 0; i < meta->n_planes; i++) {
728       GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i) = meta->offset[i];
729       GST_VIDEO_INFO_PLANE_STRIDE (&self->vinfo, i) = meta->stride[i];
730     }
731   }
732 
733   /* Find and validate all memories */
734   for (i = 0; i < n_planes; i++) {
735     guint length;
736 
737     if (!gst_buffer_find_memory (inbuf,
738             GST_VIDEO_INFO_PLANE_OFFSET (&self->vinfo, i), 1,
739             &mems_idx[i], &length, &mems_skip[i]))
740       return FALSE;
741 
742     mems[i] = gst_buffer_peek_memory (inbuf, mems_idx[i]);
743 
744     /* adjust for memory offset, in case data does not
745      * start from byte 0 in the dmabuf fd */
746     mems_skip[i] += mems[i]->offset;
747 
748     /* And all memory found must be dmabuf */
749     if (!gst_is_dmabuf_memory (mems[i]))
750       return FALSE;
751   }
752 
753   kmsmem = (GstKMSMemory *) gst_kms_allocator_get_cached (mems[0]);
754   if (kmsmem) {
755     GST_LOG_OBJECT (self, "found KMS mem %p in DMABuf mem %p with fb id = %d",
756         kmsmem, mems[0], kmsmem->fb_id);
757     goto wrap_mem;
758   }
759 
760   for (i = 0; i < n_planes; i++)
761     prime_fds[i] = gst_dmabuf_memory_get_fd (mems[i]);
762 
763   GST_LOG_OBJECT (self, "found these prime ids: %d, %d, %d, %d", prime_fds[0],
764       prime_fds[1], prime_fds[2], prime_fds[3]);
765 
766   kmsmem = gst_kms_allocator_dmabuf_import (self->allocator,
767       prime_fds, n_planes, mems_skip, &self->vinfo);
768   if (!kmsmem)
769     return FALSE;
770 
771   GST_LOG_OBJECT (self, "setting KMS mem %p to DMABuf mem %p with fb id = %d",
772       kmsmem, mems[0], kmsmem->fb_id);
773   gst_kms_allocator_cache (self->allocator, mems[0], GST_MEMORY_CAST (kmsmem));
774 
775 wrap_mem:
776   *outbuf = gst_buffer_new ();
777   if (!*outbuf)
778     return FALSE;
779   gst_buffer_append_memory (*outbuf, gst_memory_ref (GST_MEMORY_CAST (kmsmem)));
780   gst_buffer_add_parent_buffer_meta (*outbuf, inbuf);
781 
782   return TRUE;
783 }
784 
785 static GstBuffer *
gst_kms_sink_copy_to_dumb_buffer(GstRkXImageSink * self,GstBuffer * inbuf)786 gst_kms_sink_copy_to_dumb_buffer (GstRkXImageSink * self, GstBuffer * inbuf)
787 {
788   GstFlowReturn ret;
789   GstVideoFrame inframe, outframe;
790   gboolean success;
791   GstBuffer *buf = NULL;
792 
793   if (GST_VIDEO_INFO_IS_AFBC (&self->vinfo)) {
794     GST_ERROR_OBJECT (self, "unable to copy AFBC");
795     return NULL;
796   }
797 
798   if (!gst_buffer_pool_set_active (self->pool, TRUE))
799     goto activate_pool_failed;
800 
801   ret = gst_buffer_pool_acquire_buffer (self->pool, &buf, NULL);
802   if (ret != GST_FLOW_OK)
803     goto create_buffer_failed;
804 
805   if (!gst_video_frame_map (&inframe, &self->vinfo, inbuf, GST_MAP_READ))
806     goto error_map_src_buffer;
807 
808   if (!gst_video_frame_map (&outframe, &self->vinfo, buf, GST_MAP_WRITE))
809     goto error_map_dst_buffer;
810 
811   success = gst_video_frame_copy (&outframe, &inframe);
812   gst_video_frame_unmap (&outframe);
813   gst_video_frame_unmap (&inframe);
814   if (!success)
815     goto error_copy_buffer;
816 
817   return buf;
818 
819 bail:
820   {
821     if (buf)
822       gst_buffer_unref (buf);
823     return NULL;
824   }
825 
826   /* ERRORS */
827 activate_pool_failed:
828   {
829     GST_ELEMENT_ERROR (self, STREAM, FAILED, ("failed to activate buffer pool"),
830         ("failed to activate buffer pool"));
831     return NULL;
832   }
833 create_buffer_failed:
834   {
835     GST_ELEMENT_ERROR (self, STREAM, FAILED, ("allocation failed"),
836         ("failed to create buffer"));
837     return NULL;
838   }
839 error_copy_buffer:
840   {
841     GST_WARNING_OBJECT (self, "failed to upload buffer");
842     goto bail;
843   }
844 error_map_dst_buffer:
845   {
846     gst_video_frame_unmap (&inframe);
847     /* fall-through */
848   }
849 error_map_src_buffer:
850   {
851     GST_WARNING_OBJECT (self, "failed to map buffer");
852     goto bail;
853   }
854 }
855 
856 static GstBuffer *
gst_kms_sink_get_input_buffer(GstRkXImageSink * self,GstBuffer * inbuf)857 gst_kms_sink_get_input_buffer (GstRkXImageSink * self, GstBuffer * inbuf)
858 {
859   GstMemory *mem;
860   GstBuffer *buf = NULL;
861 
862   mem = gst_buffer_peek_memory (inbuf, 0);
863   if (!mem)
864     return NULL;
865 
866   if (gst_is_kms_memory (mem))
867     return gst_buffer_ref (inbuf);
868 
869   if (gst_kms_sink_import_dmabuf (self, inbuf, &buf))
870     goto done;
871 
872   buf = gst_kms_sink_copy_to_dumb_buffer (self, inbuf);
873 
874 done:
875   /* Copy all the non-memory related metas, this way CropMeta will be
876    * available upon GstVideoOverlay::expose calls. */
877   if (buf)
878     gst_buffer_copy_into (buf, inbuf, GST_BUFFER_COPY_METADATA, 0, -1);
879 
880   return buf;
881 }
882 
883 static void
sync_handler(gint fd,guint frame,guint sec,guint usec,gpointer data)884 sync_handler (gint fd, guint frame, guint sec, guint usec, gpointer data)
885 {
886   gboolean *waiting;
887 
888   waiting = data;
889   *waiting = FALSE;
890 }
891 
892 static gboolean
gst_kms_sink_sync(GstRkXImageSink * self)893 gst_kms_sink_sync (GstRkXImageSink * self)
894 {
895   gint ret;
896   gboolean waiting;
897   drmEventContext evctxt = {
898     .version = DRM_EVENT_CONTEXT_VERSION,
899     .vblank_handler = sync_handler,
900   };
901   drmVBlank vbl = {
902     .request = {
903           .type = DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT,
904           .sequence = 1,
905           .signal = (gulong) & waiting,
906         },
907   };
908 
909   if (self->pipe == 1)
910     vbl.request.type |= DRM_VBLANK_SECONDARY;
911   else if (self->pipe > 1)
912     vbl.request.type |= self->pipe << DRM_VBLANK_HIGH_CRTC_SHIFT;
913 
914   waiting = TRUE;
915   ret = drmWaitVBlank (self->fd, &vbl);
916   if (ret)
917     goto vblank_failed;
918 
919   while (waiting) {
920     do {
921       ret = gst_poll_wait (self->poll, 3 * GST_SECOND);
922     } while (ret == -1 && (errno == EAGAIN || errno == EINTR));
923 
924     ret = drmHandleEvent (self->fd, &evctxt);
925     if (ret)
926       goto event_failed;
927   }
928 
929   return TRUE;
930 
931   /* ERRORS */
932 vblank_failed:
933   {
934     GST_WARNING_OBJECT (self, "drmWaitVBlank failed: %s (%d)",
935         g_strerror (errno), errno);
936     return FALSE;
937   }
938 event_failed:
939   {
940     GST_ERROR_OBJECT (self, "drmHandleEvent failed: %s (%d)",
941         g_strerror (errno), errno);
942     return FALSE;
943   }
944 }
945 
946 static void
gst_kms_sink_drain(GstRkXImageSink * self)947 gst_kms_sink_drain (GstRkXImageSink * self)
948 {
949   GstParentBufferMeta *parent_meta;
950 
951   GST_DEBUG_OBJECT (self, "draining");
952 
953   if (!self->last_buffer)
954     return;
955 
956   /* We only need to return the last_buffer if it depends on upstream buffer.
957    * In this case, the last_buffer will have a GstParentBufferMeta set. */
958   parent_meta = gst_buffer_get_parent_buffer_meta (self->last_buffer);
959   if (parent_meta) {
960     GstBuffer *dumb_buf;
961     dumb_buf = gst_kms_sink_copy_to_dumb_buffer (self, parent_meta->buffer);
962     if (!dumb_buf)
963       dumb_buf = gst_buffer_ref (self->last_buffer);
964 
965     gst_kms_allocator_clear_cache (self->allocator);
966     gst_x_image_sink_show_frame (GST_VIDEO_SINK (self), dumb_buf);
967     gst_buffer_unref (dumb_buf);
968   }
969 }
970 
971 static gboolean
gst_kms_sink_query(GstBaseSink * bsink,GstQuery * query)972 gst_kms_sink_query (GstBaseSink * bsink, GstQuery * query)
973 {
974   GstRkXImageSink *self = GST_X_IMAGE_SINK (bsink);
975 
976   switch (GST_QUERY_TYPE (query)) {
977     case GST_QUERY_ALLOCATION:
978     case GST_QUERY_DRAIN:
979     {
980       gst_kms_sink_drain (self);
981       break;
982     }
983     default:
984       break;
985   }
986 
987   return GST_BASE_SINK_CLASS (parent_class)->query (bsink, query);
988 }
989 
990 /*ximagesink*/
991 static gboolean
xwindow_calculate_display_ratio(GstRkXImageSink * self,int * x,int * y,gint * window_width,gint * window_height)992 xwindow_calculate_display_ratio (GstRkXImageSink * self, int *x, int *y,
993     gint * window_width, gint * window_height)
994 {
995   guint dar_n, dar_d;
996   guint video_par_n, video_par_d;
997   guint dpy_par_n, dpy_par_d;
998   gint video_width, video_height;
999 
1000   video_width = GST_VIDEO_INFO_WIDTH (&self->vinfo);
1001   video_height = GST_VIDEO_INFO_HEIGHT (&self->vinfo);
1002 
1003   video_par_n = self->par_n;
1004   video_par_d = self->par_d;
1005 
1006   if (self->keep_aspect) {
1007     *window_width = video_width;
1008     *window_height = video_height;
1009     goto out;
1010   }
1011 
1012   gst_video_calculate_device_ratio (self->hdisplay, self->vdisplay,
1013       self->mm_width, self->mm_height, &dpy_par_n, &dpy_par_d);
1014 
1015   if (!gst_video_calculate_display_ratio (&dar_n, &dar_d, video_width,
1016           video_height, video_par_n, video_par_d, dpy_par_n, dpy_par_d))
1017     return FALSE;
1018 
1019   GST_DEBUG_OBJECT (self, "video calculated display ratio: %d/%d", dar_n,
1020       dar_d);
1021 
1022   /* now find a width x height that respects this display ratio.
1023    * prefer those that have one of w/h the same as the incoming video
1024    * using wd / hd = dar_n / dar_d */
1025 
1026   /* start with same height, because of interlaced video */
1027   /* check hd / dar_d is an integer scale factor, and scale wd with the PAR */
1028   video_width = gst_util_uint64_scale_int (self->xwindow->height, dar_n, dar_d);
1029   video_height = gst_util_uint64_scale_int (self->xwindow->width, dar_d, dar_n);
1030   if (video_width < *window_width) {
1031     *x += (self->xwindow->width - video_width) / 2;
1032     *window_width = video_width;
1033     *window_height = self->xwindow->height;
1034   } else {
1035     *y += (self->xwindow->height - video_height) / 2;
1036     *window_height = video_height;
1037     *window_width = self->xwindow->width;
1038   }
1039 
1040 out:
1041   GST_DEBUG_OBJECT (self, "scaling to %dx%d", *window_width, *window_height);
1042 
1043   return TRUE;
1044 }
1045 
1046 /* X11 stuff */
1047 
1048 static gboolean
xwindow_get_window_position(GstRkXImageSink * ximagesink,int * x,int * y)1049 xwindow_get_window_position (GstRkXImageSink * ximagesink, int *x, int *y)
1050 {
1051   XWindowAttributes attr;
1052   Window child;
1053   int tmp_x, tmp_y;
1054   static int last_x = 0, last_y = 0;
1055 
1056   if (x == NULL || y == NULL) {
1057     x = &tmp_x;
1058     y = &tmp_y;
1059   }
1060 
1061   XGetWindowAttributes (ximagesink->xcontext->disp,
1062       ximagesink->xwindow->win, &attr);
1063 
1064   XTranslateCoordinates (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1065       ximagesink->xcontext->root, 0, 0, x, y, &child);
1066 
1067   if (last_x != *x || last_y != *y) {
1068     last_x = *x;
1069     last_y = *y;
1070     /* moved */
1071     return TRUE;
1072   }
1073 
1074   return FALSE;
1075 }
1076 
1077 static void
xwindow_get_render_rectangle(GstRkXImageSink * ximagesink,gint * x,gint * y,gint * width,gint * height)1078 xwindow_get_render_rectangle (GstRkXImageSink * ximagesink,
1079     gint * x, gint * y, gint * width, gint * height)
1080 {
1081   if (ximagesink->save_rect.w != 0 && ximagesink->save_rect.h != 0) {
1082     *width = ximagesink->save_rect.w;
1083     *height = ximagesink->save_rect.h;
1084     *x += ximagesink->save_rect.x;
1085     *y += ximagesink->save_rect.y;
1086   }
1087 }
1088 
1089 static void
gst_x_image_sink_xwindow_fill_key(GstRkXImageSink * ximagesink,GstXWindow * xwindow,guint32 color)1090 gst_x_image_sink_xwindow_fill_key (GstRkXImageSink * ximagesink,
1091     GstXWindow * xwindow, guint32 color)
1092 {
1093   XSetForeground (ximagesink->xcontext->disp, xwindow->gc, color);
1094   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
1095       ximagesink->clip_rect.x, ximagesink->clip_rect.y,
1096       ximagesink->clip_rect.x + ximagesink->clip_rect.w,
1097       ximagesink->clip_rect.y + ximagesink->clip_rect.h);
1098 }
1099 
1100 /* We are called with the x_lock taken */
1101 static void
gst_x_image_sink_xwindow_draw_borders(GstRkXImageSink * ximagesink,GstXWindow * xwindow,GstVideoRectangle rect)1102 gst_x_image_sink_xwindow_draw_borders (GstRkXImageSink * ximagesink,
1103     GstXWindow * xwindow, GstVideoRectangle rect)
1104 {
1105   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1106   g_return_if_fail (xwindow != NULL);
1107 
1108   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
1109       ximagesink->xcontext->black);
1110 
1111   /* Left border */
1112   if (rect.x > 0) {
1113     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
1114         0, 0, rect.x, xwindow->height);
1115   }
1116 
1117   /* Right border */
1118   if ((rect.x + rect.w) < xwindow->width) {
1119     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
1120         rect.x + rect.w, 0, xwindow->width, xwindow->height);
1121   }
1122 
1123   /* Top border */
1124   if (rect.y > 0) {
1125     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
1126         0, 0, xwindow->width, rect.y);
1127   }
1128 
1129   /* Bottom border */
1130   if ((rect.y + rect.h) < xwindow->height) {
1131     XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
1132         0, rect.y + rect.h, xwindow->width, xwindow->height);
1133   }
1134 }
1135 
1136 /* This function puts a GstXImageBuffer on a GstRkXImageSink's window */
1137 static gboolean
gst_x_image_sink_ximage_put(GstRkXImageSink * ximagesink,GstBuffer * buf)1138 gst_x_image_sink_ximage_put (GstRkXImageSink * ximagesink, GstBuffer * buf)
1139 {
1140   GstVideoCropMeta *crop;
1141   GstVideoRectangle src = { 0, };
1142   GstVideoRectangle result = { 0, };
1143   GstBuffer *buffer = NULL;
1144   gboolean draw_border = FALSE;
1145   gboolean res = FALSE;
1146   gboolean expose = buf == NULL;
1147   guint32 fb_id;
1148   gint ret;
1149 
1150   /* We take the flow_lock. If expose is in there we don't want to run
1151      concurrently from the data flow thread */
1152   g_mutex_lock (&ximagesink->flow_lock);
1153 
1154   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
1155     g_mutex_unlock (&ximagesink->flow_lock);
1156     return FALSE;
1157   }
1158 
1159   /* Draw borders when displaying the first frame. After this
1160      draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
1161   if (expose || !ximagesink->last_buffer || ximagesink->draw_border)
1162     draw_border = TRUE;
1163 
1164   if (buf)
1165     buffer = gst_kms_sink_get_input_buffer (ximagesink, buf);
1166   else if (ximagesink->last_buffer)
1167     buffer = gst_buffer_ref (ximagesink->last_buffer);
1168 
1169   /* Make sure buf is not used accidentally */
1170   buf = NULL;
1171 
1172   if (!buffer)
1173     goto buffer_invalid;
1174   fb_id = gst_kms_memory_get_fb_id (gst_buffer_peek_memory (buffer, 0));
1175   if (fb_id == 0)
1176     goto buffer_invalid;
1177 
1178   GST_TRACE_OBJECT (ximagesink, "displaying fb %d", fb_id);
1179 
1180   crop = gst_buffer_get_video_crop_meta (buffer);
1181   if (crop) {
1182     src.x = crop->x;
1183     src.y = crop->y;
1184     src.w = crop->width;
1185     src.h = crop->height;
1186     GST_LOG_OBJECT (ximagesink,
1187         "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
1188   } else {
1189     src.w = GST_VIDEO_SINK_WIDTH (ximagesink);
1190     src.h = GST_VIDEO_SINK_HEIGHT (ximagesink);
1191   }
1192   result.w = ximagesink->xwindow->width;
1193   result.h = ximagesink->xwindow->height;
1194 
1195   g_mutex_lock (&ximagesink->x_lock);
1196 
1197   /* Fill color key when clip rect changed */
1198   if (draw_border || !RECT_EQUAL (ximagesink->clip_rect, result)) {
1199     ximagesink->clip_rect = result;
1200     gst_x_image_sink_xwindow_fill_key (ximagesink,
1201         ximagesink->xwindow, RKXIMAGE_COLOR_KEY);
1202 
1203     /* Do an extra repaint when handing expose */
1204     if (expose)
1205       memset (&ximagesink->clip_rect, 0, sizeof (GstVideoRectangle));
1206   }
1207 
1208   if (draw_border) {
1209     gst_x_image_sink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
1210         result);
1211     ximagesink->draw_border = FALSE;
1212   }
1213 
1214   xwindow_get_window_position (ximagesink, &result.x, &result.y);
1215 
1216   xwindow_get_render_rectangle (ximagesink, &result.x, &result.y, &result.w,
1217       &result.h);
1218   xwindow_calculate_display_ratio (ximagesink, &result.x, &result.y, &result.w,
1219       &result.h);
1220 
1221   if (GST_VIDEO_INFO_IS_AFBC (&ximagesink->vinfo))
1222     /* The AFBC's width should align to 4 */
1223     src.w &= ~3;
1224 
1225   GST_TRACE_OBJECT (ximagesink,
1226       "drmModeSetPlane at (%i,%i) %ix%i sourcing at (%i,%i) %ix%i",
1227       result.x, result.y, result.w, result.h, src.x, src.y, src.w, src.h);
1228 
1229   ret = drmModeSetPlane (ximagesink->fd, ximagesink->plane_id,
1230       ximagesink->crtc_id, fb_id, 0, result.x, result.y, result.w, result.h,
1231       /* source/cropping coordinates are given in Q16 */
1232       src.x << 16, src.y << 16, src.w << 16, src.h << 16);
1233 
1234   if (ret) {
1235     GST_ERROR_OBJECT (ximagesink, "drmModesetplane failed: %d", ret);
1236     goto out;
1237   }
1238 
1239 
1240   /* HACK: Disable vsync might cause tearing */
1241   if (!g_getenv ("KMSSINK_DISABLE_VSYNC"))
1242   /* Wait for the previous frame to complete redraw */
1243   if (!gst_kms_sink_sync (ximagesink))
1244     goto out;
1245 
1246   if (buffer != ximagesink->last_buffer)
1247     gst_buffer_replace (&ximagesink->last_buffer, buffer);
1248 
1249   res = TRUE;
1250 
1251 out:
1252   if (buffer)
1253     gst_buffer_unref (buffer);
1254   g_mutex_unlock (&ximagesink->x_lock);
1255   g_mutex_unlock (&ximagesink->flow_lock);
1256   return res;
1257 
1258 buffer_invalid:
1259   gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
1260   if (buffer)
1261     gst_buffer_unref (buffer);
1262   g_mutex_unlock (&ximagesink->flow_lock);
1263 
1264   return TRUE;
1265 }
1266 
1267 static gboolean
gst_x_image_sink_xwindow_decorate(GstRkXImageSink * ximagesink,GstXWindow * window)1268 gst_x_image_sink_xwindow_decorate (GstRkXImageSink * ximagesink,
1269     GstXWindow * window)
1270 {
1271   Atom hints_atom = None;
1272   MotifWmHints *hints;
1273 
1274   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), FALSE);
1275   g_return_val_if_fail (window != NULL, FALSE);
1276 
1277   g_mutex_lock (&ximagesink->x_lock);
1278 
1279   hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
1280       True);
1281   if (hints_atom == None) {
1282     g_mutex_unlock (&ximagesink->x_lock);
1283     return FALSE;
1284   }
1285 
1286   hints = g_malloc0 (sizeof (MotifWmHints));
1287 
1288   hints->flags |= MWM_HINTS_DECORATIONS;
1289   hints->decorations = 1 << 0;
1290 
1291   XChangeProperty (ximagesink->xcontext->disp, window->win,
1292       hints_atom, hints_atom, 32, PropModeReplace,
1293       (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
1294 
1295   XSync (ximagesink->xcontext->disp, FALSE);
1296 
1297   g_mutex_unlock (&ximagesink->x_lock);
1298 
1299   g_free (hints);
1300 
1301   return TRUE;
1302 }
1303 
1304 static void
gst_x_image_sink_xwindow_set_title(GstRkXImageSink * ximagesink,GstXWindow * xwindow,const gchar * media_title)1305 gst_x_image_sink_xwindow_set_title (GstRkXImageSink * ximagesink,
1306     GstXWindow * xwindow, const gchar * media_title)
1307 {
1308   if (media_title) {
1309     g_free (ximagesink->media_title);
1310     ximagesink->media_title = g_strdup (media_title);
1311   }
1312   if (xwindow) {
1313     /* we have a window */
1314     if (xwindow->internal) {
1315       XTextProperty xproperty;
1316       XClassHint *hint = XAllocClassHint ();
1317       const gchar *app_name;
1318       const gchar *title = NULL;
1319       gchar *title_mem = NULL;
1320 
1321       /* set application name as a title */
1322       app_name = g_get_application_name ();
1323 
1324       if (app_name && ximagesink->media_title) {
1325         title = title_mem = g_strconcat (ximagesink->media_title, " : ",
1326             app_name, NULL);
1327       } else if (app_name) {
1328         title = app_name;
1329       } else if (ximagesink->media_title) {
1330         title = ximagesink->media_title;
1331       }
1332 
1333       if (title) {
1334         if ((XStringListToTextProperty (((char **) &title), 1,
1335                     &xproperty)) != 0) {
1336           XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
1337           XFree (xproperty.value);
1338         }
1339 
1340         g_free (title_mem);
1341       }
1342 
1343       if (hint) {
1344         hint->res_name = (char *) app_name;
1345         hint->res_class = (char *) "GStreamer";
1346         XSetClassHint (ximagesink->xcontext->disp, xwindow->win, hint);
1347       }
1348       XFree (hint);
1349     }
1350   }
1351 }
1352 
1353 /* This function handles a GstXWindow creation */
1354 static GstXWindow *
gst_x_image_sink_xwindow_new(GstRkXImageSink * ximagesink,gint width,gint height)1355 gst_x_image_sink_xwindow_new (GstRkXImageSink * ximagesink, gint width,
1356     gint height)
1357 {
1358   GstXWindow *xwindow = NULL;
1359   XGCValues values;
1360 
1361   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
1362 
1363   xwindow = g_new0 (GstXWindow, 1);
1364 
1365   xwindow->width = width;
1366   xwindow->height = height;
1367   xwindow->internal = TRUE;
1368 
1369   g_mutex_lock (&ximagesink->x_lock);
1370 
1371   xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
1372       ximagesink->xcontext->root,
1373       0, 0, width, height, 0, 0, ximagesink->xcontext->black);
1374 
1375   /* We have to do that to prevent X from redrawing the background on
1376      ConfigureNotify. This takes away flickering of video when resizing. */
1377   XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
1378 
1379   /* set application name as a title */
1380   gst_x_image_sink_xwindow_set_title (ximagesink, xwindow, NULL);
1381 
1382   if (ximagesink->handle_events) {
1383     Atom wm_delete;
1384 
1385     XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1386         StructureNotifyMask | PointerMotionMask | KeyPressMask |
1387         KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1388 
1389     /* Tell the window manager we'd like delete client messages instead of
1390      * being killed */
1391     wm_delete = XInternAtom (ximagesink->xcontext->disp,
1392         "WM_DELETE_WINDOW", False);
1393     (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
1394         &wm_delete, 1);
1395   }
1396 
1397   xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
1398       0, &values);
1399 
1400   XMapRaised (ximagesink->xcontext->disp, xwindow->win);
1401 
1402   XSync (ximagesink->xcontext->disp, FALSE);
1403 
1404   g_mutex_unlock (&ximagesink->x_lock);
1405 
1406   gst_x_image_sink_xwindow_decorate (ximagesink, xwindow);
1407 
1408   gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
1409       xwindow->win);
1410 
1411   return xwindow;
1412 }
1413 
1414 /* This function destroys a GstXWindow */
1415 static void
gst_x_image_sink_xwindow_destroy(GstRkXImageSink * ximagesink,GstXWindow * xwindow)1416 gst_x_image_sink_xwindow_destroy (GstRkXImageSink * ximagesink,
1417     GstXWindow * xwindow)
1418 {
1419   g_return_if_fail (xwindow != NULL);
1420   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1421 
1422   g_mutex_lock (&ximagesink->x_lock);
1423 
1424   /* clean screen */
1425   if (ximagesink->last_fb_id) {
1426     drmModeRmFB (ximagesink->fd, ximagesink->last_fb_id);
1427     ximagesink->last_fb_id = 0;
1428   }
1429 
1430   /* If we did not create that window we just free the GC and let it live */
1431   if (xwindow->internal)
1432     XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
1433   else
1434     XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
1435 
1436   XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
1437 
1438   XSync (ximagesink->xcontext->disp, FALSE);
1439 
1440   g_mutex_unlock (&ximagesink->x_lock);
1441 
1442   g_free (xwindow);
1443 }
1444 
1445 static void
gst_x_image_sink_xwindow_update_geometry(GstRkXImageSink * ximagesink)1446 gst_x_image_sink_xwindow_update_geometry (GstRkXImageSink * ximagesink)
1447 {
1448   XWindowAttributes attr;
1449   gboolean reconfigure;
1450 
1451   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1452 
1453   /* Update the window geometry */
1454   g_mutex_lock (&ximagesink->x_lock);
1455   if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
1456     g_mutex_unlock (&ximagesink->x_lock);
1457     return;
1458   }
1459 
1460   XGetWindowAttributes (ximagesink->xcontext->disp,
1461       ximagesink->xwindow->win, &attr);
1462 
1463   /* Check if we would suggest a different width/height now */
1464   reconfigure = (ximagesink->xwindow->width != attr.width)
1465       || (ximagesink->xwindow->height != attr.height);
1466   ximagesink->xwindow->width = attr.width;
1467   ximagesink->xwindow->height = attr.height;
1468 
1469   g_mutex_unlock (&ximagesink->x_lock);
1470 
1471   if (reconfigure)
1472     gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
1473         gst_event_new_reconfigure ());
1474 }
1475 
1476 static void
gst_x_image_sink_xwindow_clear(GstRkXImageSink * ximagesink,GstXWindow * xwindow)1477 gst_x_image_sink_xwindow_clear (GstRkXImageSink * ximagesink,
1478     GstXWindow * xwindow)
1479 {
1480   g_return_if_fail (xwindow != NULL);
1481   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1482 
1483   g_mutex_lock (&ximagesink->x_lock);
1484 
1485   XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
1486       ximagesink->xcontext->black);
1487 
1488   XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
1489       0, 0, xwindow->width, xwindow->height);
1490 
1491   /* clean screen */
1492   if (ximagesink->last_fb_id) {
1493     drmModeRmFB (ximagesink->fd, ximagesink->last_fb_id);
1494     ximagesink->last_fb_id = 0;
1495   }
1496 
1497   g_mutex_unlock (&ximagesink->x_lock);
1498 }
1499 
1500 /* This function handles XEvents that might be in the queue. It generates
1501    GstEvent that will be sent upstream in the pipeline to handle interactivity
1502    and navigation.*/
1503 static void
gst_x_image_sink_handle_xevents(GstRkXImageSink * ximagesink)1504 gst_x_image_sink_handle_xevents (GstRkXImageSink * ximagesink)
1505 {
1506   XEvent e;
1507   guint pointer_x = 0, pointer_y = 0;
1508   gboolean pointer_moved = FALSE;
1509   gboolean exposed = FALSE, configured = FALSE;
1510 
1511   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1512 
1513   /* Then we get all pointer motion events, only the last position is
1514      interesting. */
1515   g_mutex_lock (&ximagesink->flow_lock);
1516   g_mutex_lock (&ximagesink->x_lock);
1517   while (XCheckWindowEvent (ximagesink->xcontext->disp,
1518           ximagesink->xwindow->win, PointerMotionMask, &e)) {
1519     g_mutex_unlock (&ximagesink->x_lock);
1520     g_mutex_unlock (&ximagesink->flow_lock);
1521 
1522     switch (e.type) {
1523       case MotionNotify:
1524         pointer_x = e.xmotion.x;
1525         pointer_y = e.xmotion.y;
1526         pointer_moved = TRUE;
1527         break;
1528       default:
1529         break;
1530     }
1531     g_mutex_lock (&ximagesink->flow_lock);
1532     g_mutex_lock (&ximagesink->x_lock);
1533   }
1534 
1535   if (pointer_moved) {
1536     g_mutex_unlock (&ximagesink->x_lock);
1537     g_mutex_unlock (&ximagesink->flow_lock);
1538 
1539     GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
1540         pointer_x, pointer_y);
1541     gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1542         "mouse-move", 0, pointer_x, pointer_y);
1543 
1544     g_mutex_lock (&ximagesink->flow_lock);
1545     g_mutex_lock (&ximagesink->x_lock);
1546   }
1547 
1548   /* We get all remaining events on our window to throw them upstream */
1549   while (XCheckWindowEvent (ximagesink->xcontext->disp,
1550           ximagesink->xwindow->win,
1551           KeyPressMask | KeyReleaseMask |
1552           ButtonPressMask | ButtonReleaseMask, &e)) {
1553     KeySym keysym;
1554     const char *key_str = NULL;
1555 
1556     /* We lock only for the X function call */
1557     g_mutex_unlock (&ximagesink->x_lock);
1558     g_mutex_unlock (&ximagesink->flow_lock);
1559 
1560     switch (e.type) {
1561       case ButtonPress:
1562         /* Mouse button pressed/released over our window. We send upstream
1563            events for interactivity/navigation */
1564         GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
1565             e.xbutton.button, e.xbutton.x, e.xbutton.x);
1566         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1567             "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1568         break;
1569       case ButtonRelease:
1570         GST_DEBUG ("ximagesink button %d release over window at %d,%d",
1571             e.xbutton.button, e.xbutton.x, e.xbutton.x);
1572         gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1573             "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1574         break;
1575       case KeyPress:
1576       case KeyRelease:
1577         /* Key pressed/released over our window. We send upstream
1578            events for interactivity/navigation */
1579         g_mutex_lock (&ximagesink->x_lock);
1580         keysym = XkbKeycodeToKeysym (ximagesink->xcontext->disp,
1581             e.xkey.keycode, 0, 0);
1582         if (keysym != NoSymbol) {
1583           key_str = XKeysymToString (keysym);
1584         } else {
1585           key_str = "unknown";
1586         }
1587         g_mutex_unlock (&ximagesink->x_lock);
1588         GST_DEBUG_OBJECT (ximagesink,
1589             "key %d pressed over window at %d,%d (%s)",
1590             e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
1591         gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1592             e.type == KeyPress ? "key-press" : "key-release", key_str);
1593         break;
1594       default:
1595         GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1596             e.type);
1597     }
1598     g_mutex_lock (&ximagesink->flow_lock);
1599     g_mutex_lock (&ximagesink->x_lock);
1600   }
1601 
1602   /* Handle Expose */
1603   while (XCheckWindowEvent (ximagesink->xcontext->disp,
1604           ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1605     switch (e.type) {
1606       case Expose:
1607         exposed = TRUE;
1608         break;
1609       case ConfigureNotify:
1610         g_mutex_unlock (&ximagesink->x_lock);
1611         gst_x_image_sink_xwindow_update_geometry (ximagesink);
1612         g_mutex_lock (&ximagesink->x_lock);
1613         configured = TRUE;
1614         break;
1615       default:
1616         break;
1617     }
1618 
1619   }
1620 
1621   if (ximagesink->handle_expose && (exposed || configured)) {
1622     g_mutex_unlock (&ximagesink->x_lock);
1623     g_mutex_unlock (&ximagesink->flow_lock);
1624 
1625     gst_x_image_sink_expose (GST_VIDEO_OVERLAY (ximagesink));
1626 
1627     g_mutex_lock (&ximagesink->flow_lock);
1628     g_mutex_lock (&ximagesink->x_lock);
1629   }
1630 
1631   /* Handle Display events */
1632   while (XPending (ximagesink->xcontext->disp)) {
1633     XNextEvent (ximagesink->xcontext->disp, &e);
1634 
1635     switch (e.type) {
1636       case ClientMessage:{
1637         Atom wm_delete;
1638 
1639         wm_delete = XInternAtom (ximagesink->xcontext->disp,
1640             "WM_DELETE_WINDOW", False);
1641         if (wm_delete == (Atom) e.xclient.data.l[0]) {
1642           /* Handle window deletion by posting an error on the bus */
1643           GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1644               ("Output window was closed"), (NULL));
1645 
1646           g_mutex_unlock (&ximagesink->x_lock);
1647           gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1648           ximagesink->xwindow = NULL;
1649           g_mutex_lock (&ximagesink->x_lock);
1650         }
1651         break;
1652       }
1653       default:
1654         break;
1655     }
1656   }
1657 
1658   /* Handle DRM display */
1659   if (ximagesink->paused
1660       && xwindow_get_window_position (ximagesink, NULL, NULL)) {
1661     /* if window stop moving, redraw display */
1662     g_mutex_unlock (&ximagesink->x_lock);
1663     g_mutex_unlock (&ximagesink->flow_lock);
1664 
1665     gst_x_image_sink_expose (GST_VIDEO_OVERLAY (ximagesink));
1666     g_mutex_lock (&ximagesink->flow_lock);
1667     g_mutex_lock (&ximagesink->x_lock);
1668   }
1669 
1670   g_mutex_unlock (&ximagesink->x_lock);
1671   g_mutex_unlock (&ximagesink->flow_lock);
1672 }
1673 
1674 static gpointer
gst_x_image_sink_event_thread(GstRkXImageSink * ximagesink)1675 gst_x_image_sink_event_thread (GstRkXImageSink * ximagesink)
1676 {
1677   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
1678 
1679   GST_OBJECT_LOCK (ximagesink);
1680   while (ximagesink->running) {
1681     GST_OBJECT_UNLOCK (ximagesink);
1682 
1683     if (ximagesink->xwindow) {
1684       gst_x_image_sink_handle_xevents (ximagesink);
1685     }
1686     /* FIXME: do we want to align this with the framerate or anything else? */
1687     g_usleep (G_USEC_PER_SEC / 100);
1688 
1689     GST_OBJECT_LOCK (ximagesink);
1690   }
1691   GST_OBJECT_UNLOCK (ximagesink);
1692 
1693   return NULL;
1694 }
1695 
1696 static void
gst_x_image_sink_manage_event_thread(GstRkXImageSink * ximagesink)1697 gst_x_image_sink_manage_event_thread (GstRkXImageSink * ximagesink)
1698 {
1699   GThread *thread = NULL;
1700 
1701   /* don't start the thread too early */
1702   if (ximagesink->xcontext == NULL) {
1703     return;
1704   }
1705 
1706   GST_OBJECT_LOCK (ximagesink);
1707   if (ximagesink->handle_expose || ximagesink->handle_events) {
1708     if (!ximagesink->event_thread) {
1709       /* Setup our event listening thread */
1710       GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
1711           ximagesink->handle_expose, ximagesink->handle_events);
1712       ximagesink->running = TRUE;
1713       ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
1714           (GThreadFunc) gst_x_image_sink_event_thread, ximagesink, NULL);
1715     }
1716   } else {
1717     if (ximagesink->event_thread) {
1718       GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
1719           ximagesink->handle_expose, ximagesink->handle_events);
1720       ximagesink->running = FALSE;
1721       /* grab thread and mark it as NULL */
1722       thread = ximagesink->event_thread;
1723       ximagesink->event_thread = NULL;
1724     }
1725   }
1726   GST_OBJECT_UNLOCK (ximagesink);
1727 
1728   /* Wait for our event thread to finish */
1729   if (thread)
1730     g_thread_join (thread);
1731 
1732 }
1733 
1734 /* This function gets the X Display and global info about it. Everything is
1735    stored in our object and will be cleaned when the object is disposed. Note
1736    here that caps for supported format are generated without any window or
1737    image creation */
1738 static GstXContext *
gst_x_image_sink_xcontext_get(GstRkXImageSink * ximagesink)1739 gst_x_image_sink_xcontext_get (GstRkXImageSink * ximagesink)
1740 {
1741   GstXContext *xcontext = NULL;
1742   XPixmapFormatValues *px_formats = NULL;
1743   gint nb_formats = 0, i;
1744   gint endianness;
1745   GstVideoFormat vformat;
1746   guint32 alpha_mask;
1747 
1748   g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
1749 
1750   xcontext = g_new0 (GstXContext, 1);
1751 
1752   g_mutex_lock (&ximagesink->x_lock);
1753 
1754   xcontext->disp = XOpenDisplay (ximagesink->display_name);
1755 
1756   if (!xcontext->disp) {
1757     g_mutex_unlock (&ximagesink->x_lock);
1758     g_free (xcontext);
1759     GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1760         ("Could not initialise X output"), ("Could not open display"));
1761     return NULL;
1762   }
1763 
1764   xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1765   xcontext->screen_num = DefaultScreen (xcontext->disp);
1766   xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1767   xcontext->root = DefaultRootWindow (xcontext->disp);
1768   xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1769   xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1770   xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1771 
1772   xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1773   xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1774   xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1775   xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1776 
1777   GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1778       xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1779 
1780   /* We get supported pixmap formats at supported depth */
1781   px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1782 
1783   if (!px_formats) {
1784     XCloseDisplay (xcontext->disp);
1785     g_mutex_unlock (&ximagesink->x_lock);
1786     g_free (xcontext);
1787     GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
1788         ("Could not get supported pixmap formats"), (NULL));
1789     return NULL;
1790   }
1791 
1792   /* We get bpp value corresponding to our running depth */
1793   for (i = 0; i < nb_formats; i++) {
1794     if (px_formats[i].depth == xcontext->depth)
1795       xcontext->bpp = px_formats[i].bits_per_pixel;
1796   }
1797 
1798   XFree (px_formats);
1799 
1800   endianness = (ImageByteOrder (xcontext->disp) ==
1801       LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1802 
1803   /* extrapolate alpha mask */
1804   if (xcontext->depth == 32) {
1805     alpha_mask = ~(xcontext->visual->red_mask
1806         | xcontext->visual->green_mask | xcontext->visual->blue_mask);
1807   } else {
1808     alpha_mask = 0;
1809   }
1810 
1811   vformat =
1812       gst_video_format_from_masks (xcontext->depth, xcontext->bpp, endianness,
1813       xcontext->visual->red_mask, xcontext->visual->green_mask,
1814       xcontext->visual->blue_mask, alpha_mask);
1815 
1816   if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1817     goto unknown_format;
1818 
1819   g_mutex_unlock (&ximagesink->x_lock);
1820 
1821   return xcontext;
1822 
1823   /* ERRORS */
1824 unknown_format:
1825   {
1826     GST_ERROR_OBJECT (ximagesink, "unknown format");
1827     return NULL;
1828   }
1829 }
1830 
1831 /* This function cleans the X context. Closing the Display and unrefing the
1832    caps for supported formats. */
1833 static void
gst_x_image_sink_xcontext_clear(GstRkXImageSink * ximagesink)1834 gst_x_image_sink_xcontext_clear (GstRkXImageSink * ximagesink)
1835 {
1836   GstXContext *xcontext;
1837 
1838   g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1839 
1840   GST_OBJECT_LOCK (ximagesink);
1841   if (ximagesink->xcontext == NULL) {
1842     GST_OBJECT_UNLOCK (ximagesink);
1843     return;
1844   }
1845 
1846   /* Take the xcontext reference and NULL it while we
1847    * clean it up, so that any buffer-alloced buffers
1848    * arriving after this will be freed correctly */
1849   xcontext = ximagesink->xcontext;
1850   ximagesink->xcontext = NULL;
1851 
1852   GST_OBJECT_UNLOCK (ximagesink);
1853 
1854   g_mutex_lock (&ximagesink->x_lock);
1855 
1856   XCloseDisplay (xcontext->disp);
1857 
1858   g_mutex_unlock (&ximagesink->x_lock);
1859 
1860   g_free (xcontext);
1861 }
1862 
1863 /* Element stuff */
1864 
1865 static GstCaps *
gst_x_image_sink_get_allowed_caps(GstRkXImageSink * self)1866 gst_x_image_sink_get_allowed_caps (GstRkXImageSink * self)
1867 {
1868   if (!self->allowed_caps)
1869     return NULL;                /* base class will return the template caps */
1870   return gst_caps_ref (self->allowed_caps);
1871 }
1872 
1873 static GstCaps *
gst_x_image_sink_getcaps(GstBaseSink * bsink,GstCaps * filter)1874 gst_x_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1875 {
1876   GstRkXImageSink *ximagesink;
1877   GstCaps *caps, *out_caps;
1878 
1879   ximagesink = GST_X_IMAGE_SINK (bsink);
1880 
1881   caps = gst_x_image_sink_get_allowed_caps (ximagesink);
1882   if (caps && filter) {
1883     out_caps = gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1884     gst_caps_unref (caps);
1885   } else {
1886     out_caps = caps;
1887   }
1888 
1889   return out_caps;
1890 }
1891 
1892 static gboolean
gst_x_image_sink_setcaps(GstBaseSink * bsink,GstCaps * caps)1893 gst_x_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1894 {
1895   GstRkXImageSink *ximagesink;
1896   GstVideoInfo info;
1897   GstBufferPool *newpool, *oldpool;
1898   GstStructure *s;
1899   gint value;
1900 
1901   ximagesink = GST_X_IMAGE_SINK (bsink);
1902 
1903   if (!ximagesink->xcontext)
1904     return FALSE;
1905 
1906   /* We are going to change the internal buffer pool, which means it will no
1907    * longer be compatbile with the last_buffer size. Drain now, as we won't be
1908    * able to do that later on. */
1909   gst_kms_sink_drain (ximagesink);
1910 
1911   GST_DEBUG_OBJECT (ximagesink, "given caps %" GST_PTR_FORMAT, caps);
1912 
1913   if (!gst_video_info_from_caps (&info, caps))
1914     goto invalid_format;
1915 
1916   /* parse AFBC from caps */
1917   s = gst_caps_get_structure (caps, 0);
1918   if (gst_structure_get_int (s, "arm-afbc", &value)) {
1919     if (value)
1920       GST_VIDEO_INFO_SET_AFBC (&info);
1921     else
1922       GST_VIDEO_INFO_UNSET_AFBC (&info);
1923   }
1924 
1925   GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1926   GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1927   ximagesink->fps_n = info.fps_n;
1928   ximagesink->fps_d = info.fps_d;
1929   ximagesink->par_n = info.par_n;
1930   ximagesink->par_d = info.par_d;
1931 
1932   /* Notify application to set xwindow id now */
1933   g_mutex_lock (&ximagesink->flow_lock);
1934   if (!ximagesink->xwindow) {
1935     g_mutex_unlock (&ximagesink->flow_lock);
1936     gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1937   } else {
1938     g_mutex_unlock (&ximagesink->flow_lock);
1939   }
1940 
1941   /* Creating our window and our image */
1942   if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1943       GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1944     goto invalid_size;
1945 
1946   /* create a new pool for the new configuration */
1947   newpool = gst_kms_sink_create_pool (ximagesink, caps,
1948       GST_VIDEO_INFO_SIZE (&info), 2);
1949   if (!newpool)
1950     goto no_pool;
1951 
1952   /* we don't activate the internal pool yet as it may not be needed */
1953   oldpool = ximagesink->pool;
1954   ximagesink->pool = newpool;
1955 
1956   if (oldpool) {
1957     gst_buffer_pool_set_active (oldpool, FALSE);
1958     gst_object_unref (oldpool);
1959   }
1960 
1961   g_mutex_lock (&ximagesink->flow_lock);
1962   if (!ximagesink->xwindow) {
1963     ximagesink->xwindow = gst_x_image_sink_xwindow_new (ximagesink,
1964         GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1965   }
1966 
1967   ximagesink->vinfo = info;
1968 
1969   /* Remember to draw borders for next frame */
1970   ximagesink->draw_border = TRUE;
1971 
1972   g_mutex_unlock (&ximagesink->flow_lock);
1973 
1974   return TRUE;
1975 
1976   /* ERRORS */
1977 invalid_format:
1978   {
1979     GST_ERROR_OBJECT (ximagesink, "caps invalid");
1980     return FALSE;
1981   }
1982 invalid_size:
1983   {
1984     GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1985         ("Invalid image size."));
1986     return FALSE;
1987   }
1988 no_pool:
1989   {
1990     /* Already warned in create_pool */
1991     return FALSE;
1992   }
1993 }
1994 
1995 static GstStateChangeReturn
gst_x_image_sink_change_state(GstElement * element,GstStateChange transition)1996 gst_x_image_sink_change_state (GstElement * element, GstStateChange transition)
1997 {
1998   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1999   GstRkXImageSink *ximagesink;
2000   GstXContext *xcontext = NULL;
2001 
2002   ximagesink = GST_X_IMAGE_SINK (element);
2003 
2004   switch (transition) {
2005     case GST_STATE_CHANGE_NULL_TO_READY:
2006       /* Initializing the XContext */
2007       if (ximagesink->xcontext == NULL) {
2008         xcontext = gst_x_image_sink_xcontext_get (ximagesink);
2009         if (xcontext == NULL) {
2010           ret = GST_STATE_CHANGE_FAILURE;
2011           goto beach;
2012         }
2013         GST_OBJECT_LOCK (ximagesink);
2014         if (xcontext)
2015           ximagesink->xcontext = xcontext;
2016         GST_OBJECT_UNLOCK (ximagesink);
2017       }
2018 
2019       /* call XSynchronize with the current value of synchronous */
2020       GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2021           ximagesink->synchronous ? "TRUE" : "FALSE");
2022       g_mutex_lock (&ximagesink->x_lock);
2023       XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2024       g_mutex_unlock (&ximagesink->x_lock);
2025       gst_x_image_sink_manage_event_thread (ximagesink);
2026       break;
2027     case GST_STATE_CHANGE_READY_TO_PAUSED:
2028       ximagesink->paused = TRUE;
2029       g_mutex_lock (&ximagesink->flow_lock);
2030       if (ximagesink->xwindow)
2031         gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
2032       g_mutex_unlock (&ximagesink->flow_lock);
2033       break;
2034     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2035       ximagesink->paused = FALSE;
2036       break;
2037     default:
2038       break;
2039   }
2040 
2041   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2042 
2043   switch (transition) {
2044     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2045       ximagesink->paused = TRUE;
2046       break;
2047     case GST_STATE_CHANGE_PAUSED_TO_READY:
2048       ximagesink->paused = FALSE;
2049       ximagesink->fps_n = 0;
2050       ximagesink->fps_d = 1;
2051       GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
2052       GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
2053       break;
2054     case GST_STATE_CHANGE_READY_TO_NULL:
2055       gst_x_image_sink_reset (ximagesink);
2056       break;
2057     default:
2058       break;
2059   }
2060 
2061 beach:
2062   return ret;
2063 }
2064 
2065 static void
gst_x_image_sink_get_times(GstBaseSink * bsink,GstBuffer * buf,GstClockTime * start,GstClockTime * end)2066 gst_x_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2067     GstClockTime * start, GstClockTime * end)
2068 {
2069   GstRkXImageSink *ximagesink;
2070 
2071   ximagesink = GST_X_IMAGE_SINK (bsink);
2072 
2073   if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2074     *start = GST_BUFFER_TIMESTAMP (buf);
2075     if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2076       *end = *start + GST_BUFFER_DURATION (buf);
2077     } else {
2078       if (ximagesink->fps_n > 0) {
2079         *end = *start +
2080             gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
2081             ximagesink->fps_n);
2082       }
2083     }
2084   }
2085 }
2086 
2087 static GstFlowReturn
gst_x_image_sink_show_frame(GstVideoSink * vsink,GstBuffer * buf)2088 gst_x_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2089 {
2090   GstFlowReturn res = GST_FLOW_OK;
2091   GstRkXImageSink *ximagesink;
2092 
2093   ximagesink = GST_X_IMAGE_SINK (vsink);
2094 
2095   if (!gst_x_image_sink_ximage_put (ximagesink, buf)) {
2096     /* No Window available to put our image into */
2097     GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
2098     res = GST_FLOW_ERROR;
2099   }
2100 
2101   return res;
2102 }
2103 
2104 static gboolean
gst_x_image_sink_event(GstBaseSink * sink,GstEvent * event)2105 gst_x_image_sink_event (GstBaseSink * sink, GstEvent * event)
2106 {
2107   GstRkXImageSink *ximagesink = GST_X_IMAGE_SINK (sink);
2108 
2109   switch (GST_EVENT_TYPE (event)) {
2110     case GST_EVENT_TAG:{
2111       GstTagList *l;
2112       gchar *title = NULL;
2113 
2114       gst_event_parse_tag (event, &l);
2115       gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
2116 
2117       if (title) {
2118         GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
2119         gst_x_image_sink_xwindow_set_title (ximagesink, ximagesink->xwindow,
2120             title);
2121 
2122         g_free (title);
2123       }
2124       break;
2125     }
2126     default:
2127       break;
2128   }
2129 
2130   return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
2131 }
2132 
2133 /* Interfaces stuff */
2134 static void
gst_x_image_sink_navigation_send_event(GstNavigation * navigation,GstStructure * structure)2135 gst_x_image_sink_navigation_send_event (GstNavigation * navigation,
2136     GstStructure * structure)
2137 {
2138   GstRkXImageSink *ximagesink = GST_X_IMAGE_SINK (navigation);
2139   GstEvent *event = NULL;
2140   gint x_offset, y_offset;
2141   gdouble x, y;
2142   gboolean handled = FALSE;
2143 
2144   /* We are not converting the pointer coordinates as there's no hardware
2145      scaling done here. The only possible scaling is done by videoscale and
2146      videoscale will have to catch those events and tranform the coordinates
2147      to match the applied scaling. So here we just add the offset if the image
2148      is centered in the window.  */
2149 
2150   /* We take the flow_lock while we look at the window */
2151   g_mutex_lock (&ximagesink->flow_lock);
2152 
2153   if (!ximagesink->xwindow) {
2154     g_mutex_unlock (&ximagesink->flow_lock);
2155     gst_structure_free (structure);
2156     return;
2157   }
2158 
2159   x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
2160   y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
2161 
2162   g_mutex_unlock (&ximagesink->flow_lock);
2163 
2164   if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
2165     x -= x_offset / 2;
2166     gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
2167   }
2168   if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
2169     y -= y_offset / 2;
2170     gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
2171   }
2172 
2173   event = gst_event_new_navigation (structure);
2174   if (event) {
2175     gst_event_ref (event);
2176     handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (ximagesink), event);
2177 
2178     if (!handled)
2179       gst_element_post_message (GST_ELEMENT_CAST (ximagesink),
2180           gst_navigation_message_new_event (GST_OBJECT_CAST (ximagesink),
2181               event));
2182 
2183     gst_event_unref (event);
2184   }
2185 }
2186 
2187 static void
gst_x_image_sink_navigation_init(GstNavigationInterface * iface)2188 gst_x_image_sink_navigation_init (GstNavigationInterface * iface)
2189 {
2190   iface->send_event = gst_x_image_sink_navigation_send_event;
2191 }
2192 
2193 static void
gst_x_image_sink_set_window_handle(GstVideoOverlay * overlay,guintptr id)2194 gst_x_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2195 {
2196   XID xwindow_id = id;
2197   GstRkXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
2198   GstXWindow *xwindow = NULL;
2199 
2200   /* We acquire the stream lock while setting this window in the element.
2201      We are basically cleaning tons of stuff replacing the old window, putting
2202      images while we do that would surely crash */
2203   g_mutex_lock (&ximagesink->flow_lock);
2204 
2205   /* If we already use that window return */
2206   if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
2207     g_mutex_unlock (&ximagesink->flow_lock);
2208     return;
2209   }
2210 
2211   /* If the element has not initialized the X11 context try to do so */
2212   if (!ximagesink->xcontext &&
2213       !(ximagesink->xcontext = gst_x_image_sink_xcontext_get (ximagesink))) {
2214     g_mutex_unlock (&ximagesink->flow_lock);
2215     /* we have thrown a GST_ELEMENT_ERROR now */
2216     return;
2217   }
2218 
2219   /* If a window is there already we destroy it */
2220   if (ximagesink->xwindow) {
2221     gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2222     ximagesink->xwindow = NULL;
2223   }
2224 
2225   /* If the xid is 0 we go back to an internal window */
2226   if (xwindow_id == 0) {
2227     /* If no width/height caps nego did not happen window will be created
2228        during caps nego then */
2229     if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
2230       xwindow = gst_x_image_sink_xwindow_new (ximagesink,
2231           GST_VIDEO_SINK_WIDTH (ximagesink),
2232           GST_VIDEO_SINK_HEIGHT (ximagesink));
2233     }
2234   } else {
2235     xwindow = g_new0 (GstXWindow, 1);
2236 
2237     xwindow->win = xwindow_id;
2238 
2239     /* We set the events we want to receive and create a GC. */
2240     g_mutex_lock (&ximagesink->x_lock);
2241     xwindow->internal = FALSE;
2242     if (ximagesink->handle_events) {
2243       XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
2244           StructureNotifyMask | PointerMotionMask | KeyPressMask |
2245           KeyReleaseMask);
2246     }
2247 
2248     xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
2249     g_mutex_unlock (&ximagesink->x_lock);
2250   }
2251 
2252   if (xwindow) {
2253     ximagesink->xwindow = xwindow;
2254     /* Update the window geometry, possibly generating a reconfigure event. */
2255     gst_x_image_sink_xwindow_update_geometry (ximagesink);
2256   }
2257 
2258   g_mutex_unlock (&ximagesink->flow_lock);
2259 }
2260 
2261 static void
gst_x_image_sink_expose(GstVideoOverlay * overlay)2262 gst_x_image_sink_expose (GstVideoOverlay * overlay)
2263 {
2264   GstRkXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
2265 
2266   gst_x_image_sink_xwindow_update_geometry (ximagesink);
2267   gst_x_image_sink_ximage_put (ximagesink, NULL);
2268 }
2269 
2270 static void
gst_x_image_sink_set_event_handling(GstVideoOverlay * overlay,gboolean handle_events)2271 gst_x_image_sink_set_event_handling (GstVideoOverlay * overlay,
2272     gboolean handle_events)
2273 {
2274   GstRkXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
2275 
2276   ximagesink->handle_events = handle_events;
2277 
2278   g_mutex_lock (&ximagesink->flow_lock);
2279 
2280   if (G_UNLIKELY (!ximagesink->xwindow)) {
2281     g_mutex_unlock (&ximagesink->flow_lock);
2282     return;
2283   }
2284 
2285   g_mutex_lock (&ximagesink->x_lock);
2286 
2287   if (handle_events) {
2288     if (ximagesink->xwindow->internal) {
2289       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2290           ExposureMask | StructureNotifyMask | PointerMotionMask |
2291           KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2292     } else {
2293       XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2294           ExposureMask | StructureNotifyMask | PointerMotionMask |
2295           KeyPressMask | KeyReleaseMask);
2296     }
2297   } else {
2298     XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
2299   }
2300 
2301   g_mutex_unlock (&ximagesink->x_lock);
2302 
2303   g_mutex_unlock (&ximagesink->flow_lock);
2304 }
2305 
2306 static void
gst_x_image_sink_set_render_rectangle(GstVideoOverlay * overlay,gint x,gint y,gint width,gint height)2307 gst_x_image_sink_set_render_rectangle (GstVideoOverlay * overlay,
2308     gint x, gint y, gint width, gint height)
2309 {
2310   GstRkXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
2311   GST_DEBUG_OBJECT (ximagesink, "Set Render Rectangle"
2312       "x %d y %d width %d height %d", x, y, width, height);
2313 
2314   ximagesink->save_rect.w = width;
2315   ximagesink->save_rect.h = height;
2316   ximagesink->save_rect.x = x;
2317   ximagesink->save_rect.y = y;
2318 
2319   gst_x_image_sink_expose (GST_VIDEO_OVERLAY (ximagesink));
2320 }
2321 
2322 static void
gst_x_image_sink_video_overlay_init(GstVideoOverlayInterface * iface)2323 gst_x_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
2324 {
2325   iface->set_window_handle = gst_x_image_sink_set_window_handle;
2326   iface->expose = gst_x_image_sink_expose;
2327   iface->set_render_rectangle = gst_x_image_sink_set_render_rectangle;
2328   iface->handle_events = gst_x_image_sink_set_event_handling;
2329 }
2330 
2331 /* =========================================== */
2332 /*                                             */
2333 /*              Init & Class init              */
2334 /*                                             */
2335 /* =========================================== */
2336 
2337 static void
gst_x_image_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2338 gst_x_image_sink_set_property (GObject * object, guint prop_id,
2339     const GValue * value, GParamSpec * pspec)
2340 {
2341   GstRkXImageSink *ximagesink;
2342 
2343   g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
2344 
2345   ximagesink = GST_X_IMAGE_SINK (object);
2346 
2347   switch (prop_id) {
2348     case PROP_DISPLAY:
2349       ximagesink->display_name = g_strdup (g_value_get_string (value));
2350       break;
2351     case PROP_SYNCHRONOUS:
2352       ximagesink->synchronous = g_value_get_boolean (value);
2353       if (ximagesink->xcontext) {
2354         GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2355             ximagesink->synchronous ? "TRUE" : "FALSE");
2356         g_mutex_lock (&ximagesink->x_lock);
2357         XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2358         g_mutex_unlock (&ximagesink->x_lock);
2359       }
2360       break;
2361     case PROP_HANDLE_EVENTS:
2362       gst_x_image_sink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
2363           g_value_get_boolean (value));
2364       gst_x_image_sink_manage_event_thread (ximagesink);
2365       break;
2366     case PROP_HANDLE_EXPOSE:
2367       ximagesink->handle_expose = g_value_get_boolean (value);
2368       gst_x_image_sink_manage_event_thread (ximagesink);
2369       break;
2370     case PROP_DRIVER_NAME:
2371       g_free (ximagesink->devname);
2372       ximagesink->devname = g_value_dup_string (value);
2373       break;
2374     case PROP_BUS_ID:
2375       g_free (ximagesink->bus_id);
2376       ximagesink->bus_id = g_value_dup_string (value);
2377       break;
2378     case PROP_CONNECTOR_ID:
2379       ximagesink->conn_id = g_value_get_int (value);
2380       break;
2381     case PROP_PLANE_ID:
2382       ximagesink->plane_id = g_value_get_int (value);
2383       break;
2384     case PROP_FORCE_ASPECT_RATIO:
2385       ximagesink->keep_aspect = g_value_get_boolean (value);
2386       break;
2387     default:
2388       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2389       break;
2390   }
2391 }
2392 
2393 static void
gst_x_image_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2394 gst_x_image_sink_get_property (GObject * object, guint prop_id,
2395     GValue * value, GParamSpec * pspec)
2396 {
2397   GstRkXImageSink *ximagesink;
2398 
2399   g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
2400 
2401   ximagesink = GST_X_IMAGE_SINK (object);
2402 
2403   switch (prop_id) {
2404     case PROP_DISPLAY:
2405       g_value_set_string (value, ximagesink->display_name);
2406       break;
2407     case PROP_SYNCHRONOUS:
2408       g_value_set_boolean (value, ximagesink->synchronous);
2409       break;
2410     case PROP_HANDLE_EVENTS:
2411       g_value_set_boolean (value, ximagesink->handle_events);
2412       break;
2413     case PROP_HANDLE_EXPOSE:
2414       g_value_set_boolean (value, ximagesink->handle_expose);
2415       break;
2416     case PROP_WINDOW_WIDTH:
2417       if (ximagesink->xwindow)
2418         g_value_set_uint64 (value, ximagesink->xwindow->width);
2419       else
2420         g_value_set_uint64 (value, 0);
2421       break;
2422     case PROP_WINDOW_HEIGHT:
2423       if (ximagesink->xwindow)
2424         g_value_set_uint64 (value, ximagesink->xwindow->height);
2425       else
2426         g_value_set_uint64 (value, 0);
2427       break;
2428     case PROP_DRIVER_NAME:
2429       g_value_set_string (value, ximagesink->devname);
2430       break;
2431     case PROP_BUS_ID:
2432       g_value_set_string (value, ximagesink->bus_id);
2433       break;
2434     case PROP_CONNECTOR_ID:
2435       g_value_set_int (value, ximagesink->conn_id);
2436       break;
2437     case PROP_PLANE_ID:
2438       g_value_set_int (value, ximagesink->plane_id);
2439       break;
2440     case PROP_FORCE_ASPECT_RATIO:
2441       g_value_set_boolean (value, ximagesink->keep_aspect);
2442       break;
2443     default:
2444       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2445       break;
2446   }
2447 }
2448 
2449 static void
gst_x_image_sink_reset(GstRkXImageSink * ximagesink)2450 gst_x_image_sink_reset (GstRkXImageSink * ximagesink)
2451 {
2452   GThread *thread;
2453 
2454   GST_OBJECT_LOCK (ximagesink);
2455   ximagesink->running = FALSE;
2456   /* grab thread and mark it as NULL */
2457   thread = ximagesink->event_thread;
2458   ximagesink->event_thread = NULL;
2459   GST_OBJECT_UNLOCK (ximagesink);
2460 
2461   /* Wait for our event thread to finish before we clean up our stuff. */
2462   if (thread)
2463     g_thread_join (thread);
2464 
2465   gst_buffer_replace (&ximagesink->last_buffer, NULL);
2466 
2467   g_mutex_lock (&ximagesink->flow_lock);
2468 
2469   if (ximagesink->xwindow) {
2470     gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
2471     gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2472     ximagesink->xwindow = NULL;
2473   }
2474   g_mutex_unlock (&ximagesink->flow_lock);
2475 
2476   gst_x_image_sink_xcontext_clear (ximagesink);
2477 }
2478 
2479 static void
gst_x_image_sink_finalize(GObject * object)2480 gst_x_image_sink_finalize (GObject * object)
2481 {
2482   GstRkXImageSink *ximagesink;
2483 
2484   ximagesink = GST_X_IMAGE_SINK (object);
2485 
2486   gst_x_image_sink_reset (ximagesink);
2487 
2488   if (ximagesink->display_name) {
2489     g_free (ximagesink->display_name);
2490     ximagesink->display_name = NULL;
2491   }
2492   g_mutex_clear (&ximagesink->x_lock);
2493   g_mutex_clear (&ximagesink->flow_lock);
2494 
2495   g_free (ximagesink->media_title);
2496 
2497   gst_poll_free (ximagesink->poll);
2498 
2499   g_clear_pointer (&ximagesink->devname, g_free);
2500   g_clear_pointer (&ximagesink->bus_id, g_free);
2501 
2502   G_OBJECT_CLASS (parent_class)->finalize (object);
2503 }
2504 
2505 static void
gst_x_image_sink_init(GstRkXImageSink * ximagesink)2506 gst_x_image_sink_init (GstRkXImageSink * ximagesink)
2507 {
2508   ximagesink->display_name = NULL;
2509   ximagesink->xcontext = NULL;
2510   ximagesink->xwindow = NULL;
2511   ximagesink->last_buffer = NULL;
2512 
2513   ximagesink->event_thread = NULL;
2514   ximagesink->running = FALSE;
2515 
2516   ximagesink->fps_n = 0;
2517   ximagesink->fps_d = 1;
2518 
2519   ximagesink->keep_aspect = TRUE;
2520 
2521   g_mutex_init (&ximagesink->x_lock);
2522   g_mutex_init (&ximagesink->flow_lock);
2523 
2524   ximagesink->synchronous = FALSE;
2525   ximagesink->handle_events = TRUE;
2526   ximagesink->handle_expose = TRUE;
2527 
2528   ximagesink->fd = -1;
2529   ximagesink->conn_id = -1;
2530   ximagesink->plane_id = -1;
2531   gst_poll_fd_init (&ximagesink->pollfd);
2532   ximagesink->poll = gst_poll_new (TRUE);
2533   gst_video_info_init (&ximagesink->vinfo);
2534 
2535   ximagesink->save_rect.x = 0;
2536   ximagesink->save_rect.y = 0;
2537   ximagesink->save_rect.w = 0;
2538   ximagesink->save_rect.h = 0;
2539 
2540   ximagesink->paused = FALSE;
2541 
2542   ximagesink->devname = g_strdup ("rockchip");
2543 }
2544 
2545 static gboolean
gst_x_image_sink_start(GstBaseSink * bsink)2546 gst_x_image_sink_start (GstBaseSink * bsink)
2547 {
2548   GstRkXImageSink *self;
2549   drmModeRes *res;
2550   drmModeConnector *conn;
2551   drmModeCrtc *crtc;
2552   drmModePlaneRes *pres;
2553   drmModePlane *plane;
2554   gboolean ret;
2555 
2556   self = GST_X_IMAGE_SINK (bsink);
2557   ret = FALSE;
2558   res = NULL;
2559   conn = NULL;
2560   crtc = NULL;
2561   pres = NULL;
2562   plane = NULL;
2563 
2564   if (self->devname || self->bus_id)
2565     self->fd = drmOpen (self->devname, self->bus_id);
2566 
2567   if (self->fd < 0)
2568     self->fd = open ("/dev/dri/card0", O_RDWR | O_CLOEXEC);
2569 
2570   if (self->fd < 0)
2571     goto open_failed;
2572 
2573   drm_log_version (self);
2574   if (!drm_get_caps (self))
2575     goto bail;
2576 
2577   res = drmModeGetResources (self->fd);
2578   if (!res)
2579     goto resources_failed;
2580 
2581   if (self->conn_id == -1)
2582     conn = drm_find_main_monitor (self->fd, res);
2583   else
2584     conn = drmModeGetConnector (self->fd, self->conn_id);
2585   if (!conn)
2586     goto connector_failed;
2587 
2588   crtc = drm_find_crtc_for_connector (self->fd, res, conn, &self->pipe);
2589   if (!crtc)
2590     goto crtc_failed;
2591 
2592   if (drmSetClientCap (self->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1))
2593     goto set_cap_failed;
2594 
2595   pres = drmModeGetPlaneResources (self->fd);
2596   if (!pres)
2597     goto plane_resources_failed;
2598 
2599   if (self->plane_id == -1)
2600     plane = drm_find_plane_for_crtc_by_type (self->fd, res, pres,
2601         crtc->crtc_id, DRM_PLANE_TYPE_OVERLAY);
2602   else {
2603     plane = drmModeGetPlane (self->fd, self->plane_id);
2604     if (drm_plane_get_type (self->fd, plane) == DRM_PLANE_TYPE_PRIMARY) {
2605       GST_ERROR_OBJECT (self, "Not support using primary plane");
2606       goto bail;
2607     }
2608   }
2609   if (!plane)
2610     goto plane_failed;
2611 
2612   /* let's get the available color formats in plane */
2613   if (!drm_ensure_allowed_caps (self, plane, res))
2614     goto bail;
2615 
2616   self->conn_id = conn->connector_id;
2617   self->crtc_id = crtc->crtc_id;
2618   self->plane_id = plane->plane_id;
2619 
2620   GST_INFO_OBJECT (self, "connector id = %d / crtc id = %d / plane id = %d",
2621       self->conn_id, self->crtc_id, self->plane_id);
2622 
2623   if (g_getenv ("GST_RKXIMAGE_USE_COLORKEY"))
2624     drm_prepare_planes (self, res, pres);
2625 
2626   self->hdisplay = crtc->mode.hdisplay;
2627   self->vdisplay = crtc->mode.vdisplay;
2628   self->buffer_id = crtc->buffer_id;
2629 
2630   self->mm_width = conn->mmWidth;
2631   self->mm_height = conn->mmHeight;
2632 
2633   GST_INFO_OBJECT (self, "display size: pixels = %dx%d / millimeters = %dx%d",
2634       self->hdisplay, self->vdisplay, self->mm_width, self->mm_height);
2635 
2636   self->pollfd.fd = self->fd;
2637   gst_poll_add_fd (self->poll, &self->pollfd);
2638   gst_poll_fd_ctl_read (self->poll, &self->pollfd, TRUE);
2639 
2640   ret = TRUE;
2641 
2642 bail:
2643   if (plane)
2644     drmModeFreePlane (plane);
2645   if (pres)
2646     drmModeFreePlaneResources (pres);
2647   if (crtc)
2648     drmModeFreeCrtc (crtc);
2649   if (conn)
2650     drmModeFreeConnector (conn);
2651   if (res)
2652     drmModeFreeResources (res);
2653 
2654   if (!ret && self->fd >= 0) {
2655     drmClose (self->fd);
2656     self->fd = -1;
2657   }
2658 
2659   return ret;
2660 
2661   /* ERRORS */
2662 open_failed:
2663   {
2664     GST_ERROR_OBJECT (self, "Could not open DRM module %s: %s",
2665         GST_STR_NULL (self->devname), strerror (errno));
2666     return FALSE;
2667   }
2668 resources_failed:
2669   {
2670     GST_ERROR_OBJECT (self, "drmModeGetResources failed: %s (%d)",
2671         strerror (errno), errno);
2672     goto bail;
2673   }
2674 
2675 connector_failed:
2676   {
2677     GST_ERROR_OBJECT (self, "Could not find a valid monitor connector");
2678     goto bail;
2679   }
2680 
2681 crtc_failed:
2682   {
2683     GST_ERROR_OBJECT (self, "Could not find a crtc for connector");
2684     goto bail;
2685   }
2686 
2687 set_cap_failed:
2688   {
2689     GST_ERROR_OBJECT (self, "Could not set universal planes capability bit");
2690     goto bail;
2691   }
2692 
2693 plane_resources_failed:
2694   {
2695     GST_ERROR_OBJECT (self, "drmModeGetPlaneResources failed: %s (%d)",
2696         strerror (errno), errno);
2697     goto bail;
2698   }
2699 
2700 plane_failed:
2701   {
2702     GST_ERROR_OBJECT (self, "Could not find a plane for crtc");
2703     goto bail;
2704   }
2705 }
2706 
2707 static gboolean
gst_x_image_sink_stop(GstBaseSink * bsink)2708 gst_x_image_sink_stop (GstBaseSink * bsink)
2709 {
2710   GstRkXImageSink *self;
2711 
2712   self = GST_X_IMAGE_SINK (bsink);
2713 
2714   if (!EMPTY_RECT (self->clip_rect))
2715     gst_x_image_sink_xwindow_fill_key (self, self->xwindow, 0);
2716 
2717   gst_buffer_replace (&self->last_buffer, NULL);
2718   gst_caps_replace (&self->allowed_caps, NULL);
2719   gst_object_replace ((GstObject **) & self->pool, NULL);
2720   gst_object_replace ((GstObject **) & self->allocator, NULL);
2721 
2722   gst_poll_remove_fd (self->poll, &self->pollfd);
2723   gst_poll_restart (self->poll);
2724   gst_poll_fd_init (&self->pollfd);
2725 
2726   if (self->fd >= 0) {
2727     close (self->fd);
2728     self->fd = -1;
2729   }
2730 
2731   return TRUE;
2732 }
2733 
2734 
2735 static void
gst_x_image_sink_class_init(GstRkXImageSinkClass * klass)2736 gst_x_image_sink_class_init (GstRkXImageSinkClass * klass)
2737 {
2738   GObjectClass *gobject_class;
2739   GstElementClass *gstelement_class;
2740   GstBaseSinkClass *gstbasesink_class;
2741   GstVideoSinkClass *videosink_class;
2742 
2743   gobject_class = (GObjectClass *) klass;
2744   gstelement_class = (GstElementClass *) klass;
2745   gstbasesink_class = (GstBaseSinkClass *) klass;
2746   videosink_class = (GstVideoSinkClass *) klass;
2747 
2748   gobject_class->finalize = gst_x_image_sink_finalize;
2749   gobject_class->set_property = gst_x_image_sink_set_property;
2750   gobject_class->get_property = gst_x_image_sink_get_property;
2751 
2752   g_object_class_install_property (gobject_class, PROP_DISPLAY,
2753       g_param_spec_string ("display", "Display", "X Display name",
2754           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2755   g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2756       g_param_spec_boolean ("synchronous", "Synchronous",
2757           "When enabled, runs the X display in synchronous mode. "
2758           "(unrelated to A/V sync, used only for debugging)", FALSE,
2759           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2760   g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2761       g_param_spec_boolean ("handle-events", "Handle XEvents",
2762           "When enabled, XEvents will be selected and handled", TRUE,
2763           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2764   g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2765       g_param_spec_boolean ("handle-expose", "Handle expose",
2766           "When enabled, "
2767           "the current frame will always be drawn in response to X Expose "
2768           "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2769 
2770   /**
2771    * GstRkXImageSink:window-width
2772    *
2773    * Actual width of the video window.
2774    */
2775   g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2776       g_param_spec_uint64 ("window-width", "window-width",
2777           "Width of the window", 0, G_MAXUINT64, 0,
2778           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2779 
2780   /**
2781    * GstRkXImageSink:window-height
2782    *
2783    * Actual height of the video window.
2784    */
2785   g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2786       g_param_spec_uint64 ("window-height", "window-height",
2787           "Height of the window", 0, G_MAXUINT64, 0,
2788           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2789 
2790   /**
2791    * kmssink:driver-name:
2792    *
2793    * If you have a system with multiple GPUs, you can choose which GPU
2794    * to use setting the DRM device driver name. Otherwise, the first
2795    * one from an internal list is used.
2796    */
2797   g_object_class_install_property (gobject_class, PROP_DRIVER_NAME,
2798       g_param_spec_string ("driver-name", "device name",
2799           "DRM device driver name", NULL,
2800           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
2801 
2802   /**
2803    * kmssink:bus-id:
2804    *
2805    * If you have a system with multiple displays for the same driver-name,
2806    * you can choose which display to use by setting the DRM bus ID. Otherwise,
2807    * the driver decides which one.
2808    */
2809   g_object_class_install_property (gobject_class, PROP_BUS_ID,
2810       g_param_spec_string ("bus-id", "Bus ID", "DRM bus ID", NULL,
2811       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
2812 
2813   /**
2814    * kmssink:connector-id:
2815    *
2816    * A GPU has several output connectors, for example: LVDS, VGA,
2817    * HDMI, etc. By default the first LVDS is tried, then the first
2818    * eDP, and at the end, the first connected one.
2819    */
2820   g_object_class_install_property (gobject_class, PROP_CONNECTOR_ID,
2821       g_param_spec_int ("connector-id", "Connector ID", "DRM connector id", -1,
2822           G_MAXINT32, -1,
2823           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
2824 
2825    /**
2826    * kmssink:plane-id:
2827    *
2828    * There could be several planes associated with a CRTC.
2829    * By default the first plane that's possible to use with a given
2830    * CRTC is tried.
2831    */
2832   g_object_class_install_property (gobject_class, PROP_PLANE_ID,
2833       g_param_spec_int ("plane-id", "Plane ID", "DRM plane id", -1, G_MAXINT32,
2834           -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT));
2835 
2836   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2837       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2838       "When enabled, scaling will respect original aspect ratio", TRUE,
2839       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2840 
2841   gst_element_class_set_static_metadata (gstelement_class,
2842       "Video sink", "Sink/Video",
2843       "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
2844 
2845   gst_element_class_add_static_pad_template (gstelement_class,
2846       &gst_x_image_sink_sink_template_factory);
2847 
2848   gstelement_class->change_state = gst_x_image_sink_change_state;
2849 
2850   gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_x_image_sink_start);
2851   gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_x_image_sink_stop);
2852   gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_getcaps);
2853   gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_setcaps);
2854   gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_x_image_sink_get_times);
2855   gstbasesink_class->propose_allocation =
2856       GST_DEBUG_FUNCPTR (gst_kms_sink_propose_allocation);
2857   gstbasesink_class->query = GST_DEBUG_FUNCPTR (gst_kms_sink_query);
2858   gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_x_image_sink_event);
2859 
2860   videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_x_image_sink_show_frame);
2861 }
2862 
2863 static gboolean
plugin_init(GstPlugin * plugin)2864 plugin_init (GstPlugin * plugin)
2865 {
2866   if (!gst_element_register (plugin, "rkximagesink",
2867           GST_RANK_SECONDARY, GST_TYPE_X_IMAGE_SINK))
2868     return FALSE;
2869 
2870   GST_DEBUG_CATEGORY_INIT (gst_debug_x_image_sink, "rkximagesink", 0,
2871       "rkximagesink element");
2872 
2873   return TRUE;
2874 }
2875 
2876 
2877 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
2878     GST_VERSION_MINOR,
2879     rkximage,
2880     "Rockchip X/DRM Video Sink",
2881     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
2882