Waylandsink : 1. change video format name for SN12 and ST12
[platform/upstream/gstreamer.git] / ext / wayland / wldisplay.c
1 /* GStreamer Wayland video sink
2  *
3  * Copyright (C) 2014 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the Free
17  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301 USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include "wldisplay.h"
25 #include <errno.h>
26
27 #ifdef GST_WLSINK_ENHANCEMENT
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <xf86drm.h>
31
32 static void
33 handle_tizen_buffer_pool_device (void *data,
34     struct tizen_buffer_pool *tizen_buffer_pool, const char *device_name)
35 {
36   FUNCTION_ENTER ();
37   GstWlDisplay *self = data;
38
39   g_return_if_fail (self != NULL);
40   g_return_if_fail (device_name != NULL);
41
42   self->device_name = strdup (device_name);
43 }
44
45 static void
46 handle_tizen_buffer_pool_authenticated (void *data,
47     struct tizen_buffer_pool *tizen_buffer_pool)
48 {
49   FUNCTION_ENTER ();
50
51   GstWlDisplay *self = data;
52   g_return_if_fail (self != NULL);
53
54   /* authenticated */
55   self->authenticated = 1;
56 }
57
58 static void
59 handle_tizen_buffer_pool_capabilities (void *data,
60     struct tizen_buffer_pool *tizen_buffer_pool, uint32_t value)
61 {
62   FUNCTION_ENTER ();
63   GstWlDisplay *self = data;
64   g_return_if_fail (self != NULL);
65
66   drm_magic_t magic;
67
68   /* check if buffer_pool has video capability */
69   if (!(value & TIZEN_BUFFER_POOL_CAPABILITY_VIDEO))
70     return;
71
72   self->has_capability = 1;
73
74   /* do authenticate only if a pool has the video capability */
75 #ifdef O_CLOEXEC
76   self->drm_fd = open (self->device_name, O_RDWR | O_CLOEXEC);
77   if (self->drm_fd == -1 && errno == EINVAL)
78 #endif
79   {
80     self->drm_fd = open (self->device_name, O_RDWR);
81     if (self->drm_fd != -1)
82       fcntl (self->drm_fd, F_SETFD, fcntl (self->drm_fd, F_GETFD) | FD_CLOEXEC);
83   }
84
85   g_return_if_fail (self->drm_fd >= 0);
86
87   if (drmGetMagic (self->drm_fd, &magic) != 0) {
88     close (self->drm_fd);
89     self->drm_fd = -1;
90     return;
91   }
92
93   tizen_buffer_pool_authenticate (tizen_buffer_pool, magic);
94   wl_display_roundtrip (self->display);
95 }
96
97 static void
98 handle_tizen_buffer_pool_format (void *data,
99     struct tizen_buffer_pool *tizen_buffer_pool, uint32_t format)
100 {
101   FUNCTION_ENTER ();
102   GstWlDisplay *self = data;
103   g_return_if_fail (self != NULL);
104
105   GST_INFO ("format is %d", format);
106   g_array_append_val (self->formats, format);
107 }
108
109 static const struct tizen_buffer_pool_listener tz_buffer_pool_listener = {
110   handle_tizen_buffer_pool_device,
111   handle_tizen_buffer_pool_authenticated,
112   handle_tizen_buffer_pool_capabilities,
113   handle_tizen_buffer_pool_format
114 };
115 #endif
116
117 GST_DEBUG_CATEGORY_EXTERN (gstwayland_debug);
118 #define GST_CAT_DEFAULT gstwayland_debug
119
120 G_DEFINE_TYPE (GstWlDisplay, gst_wl_display, G_TYPE_OBJECT);
121
122 static void gst_wl_display_finalize (GObject * gobject);
123
124 static void
125 gst_wl_display_class_init (GstWlDisplayClass * klass)
126 {
127   FUNCTION_ENTER ();
128   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
129   gobject_class->finalize = gst_wl_display_finalize;
130 }
131
132 static void
133 gst_wl_display_init (GstWlDisplay * self)
134 {
135   FUNCTION_ENTER ();
136
137   self->formats = g_array_new (FALSE, FALSE, sizeof (uint32_t));
138   self->wl_fd_poll = gst_poll_new (TRUE);
139 }
140
141 static void
142 gst_wl_display_finalize (GObject * gobject)
143 {
144   FUNCTION_ENTER ();
145
146   GstWlDisplay *self = GST_WL_DISPLAY (gobject);
147
148   gst_poll_set_flushing (self->wl_fd_poll, TRUE);
149
150   if (self->thread)
151     g_thread_join (self->thread);
152
153 #ifdef GST_WLSINK_ENHANCEMENT
154   if (self->is_native_format == FALSE) {
155     /*in case of normal video format */
156     if (self->tbm_bo)
157       tbm_bo_unref (self->tbm_bo);
158     if (self->tbm_bufmgr)
159       tbm_bufmgr_deinit (self->tbm_bufmgr);
160     self->tbm_bo = NULL;
161     self->tbm_bufmgr = NULL;
162   }
163 #endif
164
165   g_array_unref (self->formats);
166   gst_poll_free (self->wl_fd_poll);
167
168 #ifndef GST_WLSINK_ENHANCEMENT
169   if (self->shm)
170     wl_shm_destroy (self->shm);
171 #endif
172
173   if (self->shell)
174     wl_shell_destroy (self->shell);
175
176   if (self->compositor)
177     wl_compositor_destroy (self->compositor);
178
179   if (self->subcompositor)
180     wl_subcompositor_destroy (self->subcompositor);
181
182   if (self->registry)
183     wl_registry_destroy (self->registry);
184
185   if (self->queue)
186     wl_event_queue_destroy (self->queue);
187
188   if (self->own_display) {
189     wl_display_flush (self->display);
190     wl_display_disconnect (self->display);
191   }
192 #ifdef GST_WLSINK_ENHANCEMENT
193   if (self->tizen_subsurface)
194     tizen_subsurface_destroy (self->tizen_subsurface);
195   if (self->tizen_buffer_pool) {
196     tizen_buffer_pool_destroy (self->tizen_buffer_pool);
197     self->tizen_buffer_pool = NULL;
198   }
199   if (self->device_name)
200     free (self->device_name);
201   if (self->drm_fd >= 0)
202     close (self->drm_fd);
203 #endif
204
205   G_OBJECT_CLASS (gst_wl_display_parent_class)->finalize (gobject);
206 }
207
208 static void
209 sync_callback (void *data, struct wl_callback *callback, uint32_t serial)
210 {
211   FUNCTION_ENTER ();
212
213   gboolean *done = data;
214   *done = TRUE;
215 }
216
217 static const struct wl_callback_listener sync_listener = {
218   sync_callback
219 };
220
221 static gint
222 gst_wl_display_roundtrip (GstWlDisplay * self)
223 {
224   FUNCTION_ENTER ();
225
226   struct wl_callback *callback;
227   gint ret = 0;
228   gboolean done = FALSE;
229
230   g_return_val_if_fail (self != NULL, -1);
231
232   /* We don't own the display, process only our queue */
233   callback = wl_display_sync (self->display);
234   wl_callback_add_listener (callback, &sync_listener, &done);
235   wl_proxy_set_queue ((struct wl_proxy *) callback, self->queue);
236   while (ret != -1 && !done)
237     ret = wl_display_dispatch_queue (self->display, self->queue);
238   wl_callback_destroy (callback);
239
240   return ret;
241 }
242
243 static void
244 shm_format (void *data, struct wl_shm *wl_shm, uint32_t format)
245 {
246   FUNCTION_ENTER ();
247
248   GstWlDisplay *self = data;
249
250   g_array_append_val (self->formats, format);
251 }
252
253 static const struct wl_shm_listener shm_listener = {
254   shm_format
255 };
256
257 static void
258 registry_handle_global (void *data, struct wl_registry *registry,
259     uint32_t id, const char *interface, uint32_t version)
260 {
261
262   FUNCTION_ENTER ();
263   GstWlDisplay *self = data;
264
265   if (g_strcmp0 (interface, "wl_compositor") == 0) {
266     self->compositor = wl_registry_bind (registry, id, &wl_compositor_interface,
267         MIN (version, 3));
268   } else if (g_strcmp0 (interface, "wl_subcompositor") == 0) {
269     self->subcompositor =
270         wl_registry_bind (registry, id, &wl_subcompositor_interface, 1);
271   } else if (g_strcmp0 (interface, "wl_shell") == 0) {
272     self->shell = wl_registry_bind (registry, id, &wl_shell_interface, 1);
273 #ifndef GST_WLSINK_ENHANCEMENT
274   } else if (g_strcmp0 (interface, "wl_shm") == 0) {
275     self->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1);
276     wl_shm_add_listener (self->shm, &shm_listener, self);
277 #endif
278   } else if (g_strcmp0 (interface, "wl_scaler") == 0) {
279     self->scaler = wl_registry_bind (registry, id, &wl_scaler_interface, 2);
280 #ifdef GST_WLSINK_ENHANCEMENT
281   } else if (g_strcmp0 (interface, "tizen_subsurface") == 0) {
282     self->tizen_subsurface =
283         wl_registry_bind (registry, id, &tizen_subsurface_interface, 1);
284   } else if (g_strcmp0 (interface, "tizen_buffer_pool") == 0) {
285
286     self->tizen_buffer_pool =
287         wl_registry_bind (registry, id, &tizen_buffer_pool_interface, 1);
288     g_return_if_fail (self->tizen_buffer_pool != NULL);
289
290     GST_INFO ("id(%d)", id);
291     self->name = id;
292     self->drm_fd = -1;
293
294     tizen_buffer_pool_add_listener (self->tizen_buffer_pool,
295         &tz_buffer_pool_listener, self);
296
297     /* make sure all tizen_buffer_pool's events are handled */
298     wl_display_roundtrip (self->display);
299   }
300 #endif
301 }
302
303 static const struct wl_registry_listener registry_listener = {
304   registry_handle_global
305 };
306
307 static gpointer
308 gst_wl_display_thread_run (gpointer data)
309 {
310   FUNCTION_ENTER ();
311
312   GstWlDisplay *self = data;
313   GstPollFD pollfd = GST_POLL_FD_INIT;
314
315   pollfd.fd = wl_display_get_fd (self->display);
316   gst_poll_add_fd (self->wl_fd_poll, &pollfd);
317   gst_poll_fd_ctl_read (self->wl_fd_poll, &pollfd, TRUE);
318
319   /* main loop */
320   while (1) {
321     while (wl_display_prepare_read_queue (self->display, self->queue) != 0)
322       wl_display_dispatch_queue_pending (self->display, self->queue);
323     wl_display_flush (self->display);
324
325     if (gst_poll_wait (self->wl_fd_poll, GST_CLOCK_TIME_NONE) < 0) {
326       gboolean normal = (errno == EBUSY);
327       wl_display_cancel_read (self->display);
328       if (normal)
329         break;
330       else
331         goto error;
332     } else {
333       wl_display_read_events (self->display);
334       wl_display_dispatch_queue_pending (self->display, self->queue);
335     }
336   }
337
338   return NULL;
339
340 error:
341   GST_ERROR ("Error communicating with the wayland server");
342   return NULL;
343 }
344
345 GstWlDisplay *
346 gst_wl_display_new (const gchar * name, GError ** error)
347 {
348   FUNCTION_ENTER ();
349
350   struct wl_display *display;
351
352   display = wl_display_connect (name);
353
354   if (!display) {
355     *error = g_error_new (g_quark_from_static_string ("GstWlDisplay"), 0,
356         "Failed to connect to the wayland display '%s'",
357         name ? name : "(default)");
358     return NULL;
359   } else {
360     return gst_wl_display_new_existing (display, TRUE, error);
361   }
362 }
363
364 GstWlDisplay *
365 gst_wl_display_new_existing (struct wl_display * display,
366     gboolean take_ownership, GError ** error)
367 {
368   FUNCTION_ENTER ();
369
370   GstWlDisplay *self;
371   GError *err = NULL;
372   gint i;
373
374   g_return_val_if_fail (display != NULL, NULL);
375
376   self = g_object_new (GST_TYPE_WL_DISPLAY, NULL);
377   self->display = display;
378   self->own_display = take_ownership;
379
380   self->queue = wl_display_create_queue (self->display);
381   self->registry = wl_display_get_registry (self->display);
382   wl_proxy_set_queue ((struct wl_proxy *) self->registry, self->queue);
383   wl_registry_add_listener (self->registry, &registry_listener, self);
384
385   /* we need exactly 2 roundtrips to discover global objects and their state */
386   for (i = 0; i < 2; i++) {
387     if (gst_wl_display_roundtrip (self) < 0) {
388       *error = g_error_new (g_quark_from_static_string ("GstWlDisplay"), 0,
389           "Error communicating with the wayland display");
390       g_object_unref (self);
391       return NULL;
392     }
393   }
394
395   /* verify we got all the required interfaces */
396 #define VERIFY_INTERFACE_EXISTS(var, interface) \
397   if (!self->var) { \
398     g_set_error (error, g_quark_from_static_string ("GstWlDisplay"), 0, \
399         "Could not bind to " interface ". Either it is not implemented in " \
400         "the compositor, or the implemented version doesn't match"); \
401     g_object_unref (self); \
402     return NULL; \
403   }
404
405   VERIFY_INTERFACE_EXISTS (compositor, "wl_compositor");
406   VERIFY_INTERFACE_EXISTS (subcompositor, "wl_subcompositor");
407   VERIFY_INTERFACE_EXISTS (shell, "wl_shell");
408 #ifdef GST_WLSINK_ENHANCEMENT
409   VERIFY_INTERFACE_EXISTS (tizen_buffer_pool, "tizen_buffer_pool");
410 #else
411   VERIFY_INTERFACE_EXISTS (shm, "wl_shm");
412 #endif
413   VERIFY_INTERFACE_EXISTS (scaler, "wl_scaler");
414
415 #undef VERIFY_INTERFACE_EXISTS
416
417   self->thread = g_thread_try_new ("GstWlDisplay", gst_wl_display_thread_run,
418       self, &err);
419   if (err) {
420     g_propagate_prefixed_error (error, err,
421         "Failed to start thread for the display's events");
422     g_object_unref (self);
423     return NULL;
424   }
425
426   return self;
427 }