1/*
2 * Copyright © 2018 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20#include "config.h"
21
22#include "gtkgstpaintableprivate.h"
23#include "gtkgstsinkprivate.h"
24
25#include <gtk/gtk.h>
26#include <gst/player/gstplayer-video-renderer.h>
27
28#include <math.h>
29
30struct _GtkGstPaintable
31{
32 GObject parent_instance;
33
34 GdkPaintable *image;
35 double pixel_aspect_ratio;
36
37 GdkGLContext *context;
38};
39
40struct _GtkGstPaintableClass
41{
42 GObjectClass parent_class;
43};
44
45static void
46gtk_gst_paintable_paintable_snapshot (GdkPaintable *paintable,
47 GdkSnapshot *snapshot,
48 double width,
49 double height)
50{
51 GtkGstPaintable *self = GTK_GST_PAINTABLE (ptr: paintable);
52
53 if (self->image)
54 gdk_paintable_snapshot (paintable: self->image, snapshot, width, height);
55}
56
57static GdkPaintable *
58gtk_gst_paintable_paintable_get_current_image (GdkPaintable *paintable)
59{
60 GtkGstPaintable *self = GTK_GST_PAINTABLE (ptr: paintable);
61
62 if (self->image)
63 return GDK_PAINTABLE (g_object_ref (self->image));
64
65 return gdk_paintable_new_empty (intrinsic_width: 0, intrinsic_height: 0);
66}
67
68static int
69gtk_gst_paintable_paintable_get_intrinsic_width (GdkPaintable *paintable)
70{
71 GtkGstPaintable *self = GTK_GST_PAINTABLE (ptr: paintable);
72
73 if (self->image)
74 return round (x: self->pixel_aspect_ratio *
75 gdk_paintable_get_intrinsic_width (paintable: self->image));
76
77 return 0;
78}
79
80static int
81gtk_gst_paintable_paintable_get_intrinsic_height (GdkPaintable *paintable)
82{
83 GtkGstPaintable *self = GTK_GST_PAINTABLE (ptr: paintable);
84
85 if (self->image)
86 return gdk_paintable_get_intrinsic_height (paintable: self->image);
87
88 return 0;
89}
90
91static double
92gtk_gst_paintable_paintable_get_intrinsic_aspect_ratio (GdkPaintable *paintable)
93{
94 GtkGstPaintable *self = GTK_GST_PAINTABLE (ptr: paintable);
95
96 if (self->image)
97 return self->pixel_aspect_ratio *
98 gdk_paintable_get_intrinsic_aspect_ratio (paintable: self->image);
99
100 return 0.0;
101};
102
103static void
104gtk_gst_paintable_paintable_init (GdkPaintableInterface *iface)
105{
106 iface->snapshot = gtk_gst_paintable_paintable_snapshot;
107 iface->get_current_image = gtk_gst_paintable_paintable_get_current_image;
108 iface->get_intrinsic_width = gtk_gst_paintable_paintable_get_intrinsic_width;
109 iface->get_intrinsic_height = gtk_gst_paintable_paintable_get_intrinsic_height;
110 iface->get_intrinsic_aspect_ratio = gtk_gst_paintable_paintable_get_intrinsic_aspect_ratio;
111}
112
113static GstElement *
114gtk_gst_paintable_video_renderer_create_video_sink (GstPlayerVideoRenderer *renderer,
115 GstPlayer *player)
116{
117 GtkGstPaintable *self = GTK_GST_PAINTABLE (ptr: renderer);
118 GstElement *sink;
119 GdkGLContext *ctx;
120
121 sink = g_object_new (GTK_TYPE_GST_SINK,
122 first_property_name: "paintable", self,
123 "gl-context", self->context,
124 NULL);
125
126 if (self->context != NULL)
127 g_object_get (GTK_GST_SINK (sink), first_property_name: "gl-context", &ctx, NULL);
128
129 if (self->context != NULL && ctx != NULL)
130 {
131 GstElement *glsinkbin = gst_element_factory_make (factoryname: "glsinkbin", NULL);
132
133 if (!glsinkbin)
134 return NULL;
135
136 g_object_set (object: glsinkbin, first_property_name: "sink", sink, NULL);
137 g_object_unref (object: ctx);
138
139 return glsinkbin;
140 }
141 else
142 {
143 if (self->context != NULL)
144 {
145 g_warning ("GstGL context creation failed, falling back to non-GL playback");
146
147 g_object_unref (object: sink);
148 sink = g_object_new (GTK_TYPE_GST_SINK,
149 first_property_name: "paintable", self,
150 NULL);
151 }
152
153 return sink;
154 }
155}
156
157static void
158gtk_gst_paintable_video_renderer_init (GstPlayerVideoRendererInterface *iface)
159{
160 iface->create_video_sink = gtk_gst_paintable_video_renderer_create_video_sink;
161}
162
163G_DEFINE_TYPE_WITH_CODE (GtkGstPaintable, gtk_gst_paintable, G_TYPE_OBJECT,
164 G_IMPLEMENT_INTERFACE (GDK_TYPE_PAINTABLE,
165 gtk_gst_paintable_paintable_init)
166 G_IMPLEMENT_INTERFACE (GST_TYPE_PLAYER_VIDEO_RENDERER,
167 gtk_gst_paintable_video_renderer_init));
168
169static void
170gtk_gst_paintable_dispose (GObject *object)
171{
172 GtkGstPaintable *self = GTK_GST_PAINTABLE (ptr: object);
173
174 g_clear_object (&self->image);
175
176 G_OBJECT_CLASS (gtk_gst_paintable_parent_class)->dispose (object);
177}
178
179static void
180gtk_gst_paintable_class_init (GtkGstPaintableClass *klass)
181{
182 GObjectClass *object_class = G_OBJECT_CLASS (klass);
183
184 object_class->dispose = gtk_gst_paintable_dispose;
185}
186
187static void
188gtk_gst_paintable_init (GtkGstPaintable *self)
189{
190}
191
192GdkPaintable *
193gtk_gst_paintable_new (void)
194{
195 return g_object_new (GTK_TYPE_GST_PAINTABLE, NULL);
196}
197
198void
199gtk_gst_paintable_realize (GtkGstPaintable *self,
200 GdkSurface *surface)
201{
202 GError *error = NULL;
203
204 if (self->context)
205 return;
206
207 self->context = gdk_surface_create_gl_context (surface, error: &error);
208 if (self->context == NULL)
209 {
210 GST_INFO ("failed to create GDK GL context: %s", error->message);
211 g_error_free (error);
212 return;
213 }
214
215 if (!gdk_gl_context_realize (context: self->context, error: &error))
216 {
217 GST_INFO ("failed to realize GDK GL context: %s", error->message);
218 g_clear_object (&self->context);
219 g_error_free (error);
220 return;
221 }
222}
223
224void
225gtk_gst_paintable_unrealize (GtkGstPaintable *self,
226 GdkSurface *surface)
227{
228 /* XXX: We could be smarter here and:
229 * - track how often we were realized with that surface
230 * - track alternate surfaces
231 */
232 if (self->context == NULL)
233 return;
234
235 if (gdk_gl_context_get_surface (context: self->context) == surface)
236 g_clear_object (&self->context);
237}
238
239static void
240gtk_gst_paintable_set_paintable (GtkGstPaintable *self,
241 GdkPaintable *paintable,
242 double pixel_aspect_ratio)
243{
244 gboolean size_changed;
245
246 if (self->image == paintable)
247 return;
248
249 if (self->image == NULL ||
250 self->pixel_aspect_ratio * gdk_paintable_get_intrinsic_width (paintable: self->image) !=
251 pixel_aspect_ratio * gdk_paintable_get_intrinsic_width (paintable) ||
252 gdk_paintable_get_intrinsic_height (paintable: self->image) != gdk_paintable_get_intrinsic_height (paintable) ||
253 gdk_paintable_get_intrinsic_aspect_ratio (paintable: self->image) != gdk_paintable_get_intrinsic_aspect_ratio (paintable))
254 size_changed = TRUE;
255 else
256 size_changed = FALSE;
257
258 g_set_object (&self->image, paintable);
259 self->pixel_aspect_ratio = pixel_aspect_ratio;
260
261 if (size_changed)
262 gdk_paintable_invalidate_size (paintable: GDK_PAINTABLE (ptr: self));
263
264 gdk_paintable_invalidate_contents (paintable: GDK_PAINTABLE (ptr: self));
265}
266
267typedef struct _SetTextureInvocation SetTextureInvocation;
268
269struct _SetTextureInvocation {
270 GtkGstPaintable *paintable;
271 GdkTexture *texture;
272 double pixel_aspect_ratio;
273};
274
275static void
276set_texture_invocation_free (SetTextureInvocation *invoke)
277{
278 g_object_unref (object: invoke->paintable);
279 g_object_unref (object: invoke->texture);
280
281 g_slice_free (SetTextureInvocation, invoke);
282}
283
284static gboolean
285gtk_gst_paintable_set_texture_invoke (gpointer data)
286{
287 SetTextureInvocation *invoke = data;
288
289 gtk_gst_paintable_set_paintable (self: invoke->paintable,
290 paintable: GDK_PAINTABLE (ptr: invoke->texture),
291 pixel_aspect_ratio: invoke->pixel_aspect_ratio);
292
293 return G_SOURCE_REMOVE;
294}
295
296void
297gtk_gst_paintable_queue_set_texture (GtkGstPaintable *self,
298 GdkTexture *texture,
299 double pixel_aspect_ratio)
300{
301 SetTextureInvocation *invoke;
302
303 invoke = g_slice_new0 (SetTextureInvocation);
304 invoke->paintable = g_object_ref (self);
305 invoke->texture = g_object_ref (texture);
306 invoke->pixel_aspect_ratio = pixel_aspect_ratio;
307
308 g_main_context_invoke_full (NULL,
309 G_PRIORITY_DEFAULT,
310 function: gtk_gst_paintable_set_texture_invoke,
311 data: invoke,
312 notify: (GDestroyNotify) set_texture_invocation_free);
313}
314
315

source code of gtk/modules/media/gtkgstpaintable.c