1 /*
2 * GStreamer
3 * Copyright (C) 2013 Julien Isorce <julien.isorce@collabora.co.uk>
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
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #define GLIB_DISABLE_DEPRECATION_WARNINGS
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "../gstgl_fwd.h"
28 #include <gst/gl/gstglcontext.h>
29
30 #include "gstglwindow_dispmanx_egl.h"
31 #include "../gstglwindow_private.h"
32
33
34 #ifndef ELEMENT_CHANGE_LAYER
35 /* copied from interface/vmcs_host/vc_vchi_dispmanx.h of userland.git */
36 #define ELEMENT_CHANGE_LAYER (1<<0)
37 #define ELEMENT_CHANGE_OPACITY (1<<1)
38 #define ELEMENT_CHANGE_DEST_RECT (1<<2)
39 #define ELEMENT_CHANGE_SRC_RECT (1<<3)
40 #define ELEMENT_CHANGE_MASK_RESOURCE (1<<4)
41 #define ELEMENT_CHANGE_TRANSFORM (1<<5)
42 #endif
43
44 #define GST_CAT_DEFAULT gst_gl_window_debug
45
46 #define gst_gl_window_dispmanx_egl_parent_class parent_class
47 G_DEFINE_TYPE (GstGLWindowDispmanxEGL, gst_gl_window_dispmanx_egl,
48 GST_TYPE_GL_WINDOW);
49
50 static guintptr gst_gl_window_dispmanx_egl_get_window_handle (GstGLWindow *
51 window);
52 static void gst_gl_window_dispmanx_egl_set_window_handle (GstGLWindow * window,
53 guintptr handle);
54 static void gst_gl_window_dispmanx_egl_set_preferred_size (GstGLWindow * window,
55 gint width, gint height);
56 static void gst_gl_window_dispmanx_egl_show (GstGLWindow * window);
57 static void gst_gl_window_dispmanx_egl_close (GstGLWindow * window);
58 static gboolean gst_gl_window_dispmanx_egl_open (GstGLWindow * window,
59 GError ** error);
60 static guintptr gst_gl_window_dispmanx_egl_get_display (GstGLWindow * window);
61 static gboolean gst_gl_window_dispmanx_egl_set_render_rectangle (GstGLWindow *
62 window, gint x, gint y, gint width, gint height);
63
64 static void window_resize (GstGLWindowDispmanxEGL * window_egl, guint width,
65 guint height, gboolean visible);
66
67 static void
gst_gl_window_dispmanx_egl_class_init(GstGLWindowDispmanxEGLClass * klass)68 gst_gl_window_dispmanx_egl_class_init (GstGLWindowDispmanxEGLClass * klass)
69 {
70 GstGLWindowClass *window_class = (GstGLWindowClass *) klass;
71
72 window_class->get_window_handle =
73 GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_get_window_handle);
74 window_class->set_window_handle =
75 GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_set_window_handle);
76 window_class->show = GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_show);
77 window_class->close = GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_close);
78 window_class->open = GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_open);
79 window_class->get_display =
80 GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_get_display);
81 window_class->set_preferred_size =
82 GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_set_preferred_size);
83 window_class->set_render_rectangle =
84 GST_DEBUG_FUNCPTR (gst_gl_window_dispmanx_egl_set_render_rectangle);
85 }
86
87 static void
gst_gl_window_dispmanx_egl_init(GstGLWindowDispmanxEGL * window_egl)88 gst_gl_window_dispmanx_egl_init (GstGLWindowDispmanxEGL * window_egl)
89 {
90 window_egl->egldisplay = EGL_DEFAULT_DISPLAY;
91
92 window_egl->visible = FALSE;
93 window_egl->display = 0;
94 window_egl->dp_width = 0;
95 window_egl->dp_height = 0;
96 window_egl->native.element = 0;
97 window_egl->native.width = 0;
98 window_egl->native.height = 0;
99 window_egl->foreign.element = 0;
100 window_egl->foreign.width = 0;
101 window_egl->foreign.height = 0;
102 window_egl->render_rect.x = 0;
103 window_egl->render_rect.y = 0;
104 window_egl->render_rect.w = 0;
105 window_egl->render_rect.h = 0;
106 }
107
108 /* Must be called in the gl thread */
109 GstGLWindowDispmanxEGL *
gst_gl_window_dispmanx_egl_new(GstGLDisplay * display)110 gst_gl_window_dispmanx_egl_new (GstGLDisplay * display)
111 {
112 GstGLWindowDispmanxEGL *window;
113
114 if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_EGL) == 0)
115 /* we require an egl display to create dispmanx windows */
116 return NULL;
117
118 GST_DEBUG ("creating Dispmanx EGL window");
119
120 window = g_object_new (GST_TYPE_GL_WINDOW_DISPMANX_EGL, NULL);
121 gst_object_ref_sink (window);
122
123 return window;
124 }
125
126 static void
gst_gl_window_dispmanx_egl_close(GstGLWindow * window)127 gst_gl_window_dispmanx_egl_close (GstGLWindow * window)
128 {
129 GstGLWindowDispmanxEGL *window_egl;
130 DISPMANX_UPDATE_HANDLE_T dispman_update;
131
132 window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
133
134 if (window_egl->native.element
135 && window_egl->native.element != window_egl->foreign.element) {
136 dispman_update = vc_dispmanx_update_start (0);
137 vc_dispmanx_element_remove (dispman_update, window_egl->native.element);
138 vc_dispmanx_update_submit_sync (dispman_update);
139 }
140 vc_dispmanx_display_close (window_egl->display);
141
142 GST_GL_WINDOW_CLASS (parent_class)->close (window);
143 }
144
145 static gboolean
gst_gl_window_dispmanx_egl_open(GstGLWindow * window,GError ** error)146 gst_gl_window_dispmanx_egl_open (GstGLWindow * window, GError ** error)
147 {
148 GstGLWindowDispmanxEGL *window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
149 gint ret = graphics_get_display_size (0, &window_egl->dp_width,
150 &window_egl->dp_height);
151 if (ret < 0) {
152 g_set_error (error, GST_GL_WINDOW_ERROR,
153 GST_GL_WINDOW_ERROR_RESOURCE_UNAVAILABLE, "Can't open display");
154 return FALSE;
155 }
156 GST_DEBUG ("Got display size: %dx%d", window_egl->dp_width,
157 window_egl->dp_height);
158
159 window_egl->native.element = 0;
160
161 return GST_GL_WINDOW_CLASS (parent_class)->open (window, error);
162 }
163
164 gboolean
gst_gl_window_dispmanx_egl_create_window(GstGLWindowDispmanxEGL * window_egl)165 gst_gl_window_dispmanx_egl_create_window (GstGLWindowDispmanxEGL * window_egl)
166 {
167 window_egl->native.width = 0;
168 window_egl->native.height = 0;
169 window_egl->display = vc_dispmanx_display_open (0);
170 window_resize (window_egl, 16, 16, FALSE);
171 return TRUE;
172 }
173
174 static guintptr
gst_gl_window_dispmanx_egl_get_window_handle(GstGLWindow * window)175 gst_gl_window_dispmanx_egl_get_window_handle (GstGLWindow * window)
176 {
177 GstGLWindowDispmanxEGL *window_egl;
178 window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
179 if (window_egl->native.element)
180 return (guintptr) & window_egl->native;
181 return 0;
182 }
183
184 static void
gst_gl_window_dispmanx_egl_set_window_handle(GstGLWindow * window,guintptr handle)185 gst_gl_window_dispmanx_egl_set_window_handle (GstGLWindow * window,
186 guintptr handle)
187 {
188 GstGLWindowDispmanxEGL *window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
189 EGL_DISPMANX_WINDOW_T *foreign_window = (EGL_DISPMANX_WINDOW_T *) handle;
190 DISPMANX_UPDATE_HANDLE_T dispman_update;
191
192 GST_DEBUG_OBJECT (window, "set window handle with size %dx%d",
193 foreign_window->width, foreign_window->height);
194
195 if (window_egl->native.element
196 && window_egl->native.element != window_egl->foreign.element) {
197 dispman_update = vc_dispmanx_update_start (0);
198 vc_dispmanx_element_remove (dispman_update, window_egl->native.element);
199 vc_dispmanx_update_submit_sync (dispman_update);
200 }
201
202 window_egl->native.element = window_egl->foreign.element =
203 foreign_window->element;
204 window_egl->native.width = window_egl->foreign.width = foreign_window->width;
205 window_egl->native.height = window_egl->foreign.height =
206 foreign_window->height;
207 }
208
209 static void
gst_gl_window_dispmanx_egl_set_preferred_size(GstGLWindow * window,gint width,gint height)210 gst_gl_window_dispmanx_egl_set_preferred_size (GstGLWindow * window, gint width,
211 gint height)
212 {
213 GstGLWindowDispmanxEGL *window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
214
215 GST_DEBUG_OBJECT (window, "set preferred size to %dx%d", width, height);
216 window_egl->preferred_width = width;
217 window_egl->preferred_height = height;
218 }
219
220 static void
window_resize(GstGLWindowDispmanxEGL * window_egl,guint width,guint height,gboolean visible)221 window_resize (GstGLWindowDispmanxEGL * window_egl, guint width, guint height,
222 gboolean visible)
223 {
224 GstGLWindow *window = GST_GL_WINDOW (window_egl);
225
226 GST_DEBUG ("resizing %s window from %ux%u to %ux%u",
227 visible ? "visible" : "invisible", window_egl->native.width,
228 window_egl->native.height, width, height);
229
230 if (window_egl->display) {
231 VC_RECT_T dst_rect;
232 VC_RECT_T src_rect;
233 GstVideoRectangle src, res;
234 DISPMANX_UPDATE_HANDLE_T dispman_update;
235 uint32_t opacity = visible ? 255 : 0;
236 VC_DISPMANX_ALPHA_T alpha =
237 { DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS, opacity, 0 };
238
239 src.w = width;
240 src.h = height;
241 src.x = src.y = 0;
242
243 /* If there is no render rectangle, center the width*height frame
244 * inside dp_width*dp_height */
245 if (window_egl->render_rect.w <= 0 || window_egl->render_rect.h <= 0) {
246 GstVideoRectangle dst;
247 dst.w = window_egl->dp_width;
248 dst.h = window_egl->dp_height;
249 dst.x = dst.y = 0;
250 gst_video_sink_center_rect (src, dst, &res, FALSE);
251 } else {
252 gst_video_sink_center_rect (src, window_egl->render_rect, &res, FALSE);
253 }
254
255 dst_rect.x = res.x;
256 dst_rect.y = res.y;
257 dst_rect.width = res.w;
258 dst_rect.height = res.h;
259
260 src_rect.x = 0;
261 src_rect.y = 0;
262 src_rect.width = width << 16;
263 src_rect.height = height << 16;
264
265 dispman_update = vc_dispmanx_update_start (0);
266
267 if (window_egl->native.element) {
268 uint32_t change_flags =
269 ELEMENT_CHANGE_OPACITY | ELEMENT_CHANGE_DEST_RECT |
270 ELEMENT_CHANGE_SRC_RECT;
271 vc_dispmanx_element_change_attributes (dispman_update,
272 window_egl->native.element, change_flags, 0, opacity, &dst_rect,
273 &src_rect, 0, 0);
274 } else {
275 window_egl->native.element = vc_dispmanx_element_add (dispman_update,
276 window_egl->display, 0, &dst_rect, 0, &src_rect,
277 DISPMANX_PROTECTION_NONE, &alpha, 0, 0);
278 }
279
280 vc_dispmanx_update_submit_sync (dispman_update);
281
282 gst_gl_window_resize (window, width, height);
283 }
284
285 window_egl->native.width = width;
286 window_egl->native.height = height;
287 }
288
289 struct SetRenderRectangle
290 {
291 GstGLWindowDispmanxEGL *window_egl;
292 GstVideoRectangle rect;
293 };
294
295 static void
_free_set_render_rectangle(struct SetRenderRectangle * render)296 _free_set_render_rectangle (struct SetRenderRectangle *render)
297 {
298 if (render) {
299 if (render->window_egl)
300 gst_object_unref (render->window_egl);
301 g_free (render);
302 }
303 }
304
305 static void
_set_render_rectangle(gpointer data)306 _set_render_rectangle (gpointer data)
307 {
308 struct SetRenderRectangle *render = data;
309
310 GST_LOG_OBJECT (render->window_egl, "setting render rectangle %i,%i+%ix%i",
311 render->rect.x, render->rect.y, render->rect.w, render->rect.h);
312
313 render->window_egl->render_rect = render->rect;
314 window_resize (render->window_egl, render->rect.w, render->rect.h, TRUE);
315 }
316
317 static gboolean
gst_gl_window_dispmanx_egl_set_render_rectangle(GstGLWindow * window,gint x,gint y,gint width,gint height)318 gst_gl_window_dispmanx_egl_set_render_rectangle (GstGLWindow * window,
319 gint x, gint y, gint width, gint height)
320 {
321 GstGLWindowDispmanxEGL *window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
322 struct SetRenderRectangle *render;
323
324 render = g_new0 (struct SetRenderRectangle, 1);
325 render->window_egl = gst_object_ref (window_egl);
326 render->rect.x = x;
327 render->rect.y = y;
328 render->rect.w = width;
329 render->rect.h = height;
330
331 gst_gl_window_send_message_async (window,
332 (GstGLWindowCB) _set_render_rectangle, render,
333 (GDestroyNotify) _free_set_render_rectangle);
334
335 return TRUE;
336 }
337
338 static void
gst_gl_window_dispmanx_egl_show(GstGLWindow * window)339 gst_gl_window_dispmanx_egl_show (GstGLWindow * window)
340 {
341 GstGLWindowDispmanxEGL *window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
342
343 if (!window_egl->visible) {
344 if (window_egl->render_rect.w <= 0 || window_egl->render_rect.h <= 0) {
345 window_resize (window_egl, window_egl->preferred_width,
346 window_egl->preferred_height, TRUE);
347 }
348 window_egl->visible = TRUE;
349 }
350 }
351
352 static guintptr
gst_gl_window_dispmanx_egl_get_display(GstGLWindow * window)353 gst_gl_window_dispmanx_egl_get_display (GstGLWindow * window)
354 {
355 GstGLWindowDispmanxEGL *window_egl;
356
357 window_egl = GST_GL_WINDOW_DISPMANX_EGL (window);
358
359 return (guintptr) window_egl->egldisplay;
360 }
361