1/*
2 * GStreamer
3 * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
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#include "config.h"
22
23#include "gtkgstsinkprivate.h"
24
25#include "gtkgstpaintableprivate.h"
26#include "gtkintl.h"
27
28#if GST_GL_HAVE_WINDOW_X11 && (GST_GL_HAVE_PLATFORM_GLX || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_X11)
29#define HAVE_GST_X11_SUPPORT
30#include <gdk/x11/gdkx.h>
31#if GST_GL_HAVE_PLATFORM_GLX
32#include <gst/gl/x11/gstgldisplay_x11.h>
33#endif
34#endif
35
36#if GST_GL_HAVE_WINDOW_WAYLAND && GST_GL_HAVE_PLATFORM_EGL && defined (GDK_WINDOWING_WAYLAND)
37#define HAVE_GST_WAYLAND_SUPPORT
38#include <gdk/wayland/gdkwayland.h>
39#include <gst/gl/wayland/gstgldisplay_wayland.h>
40#endif
41
42#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
43#include <gdk/win32/gdkwin32.h>
44#include <epoxy/wgl.h>
45#endif
46
47#if GST_GL_HAVE_PLATFORM_EGL && (GST_GL_HAVE_WINDOW_WIN32 || GST_GL_HAVE_WINDOW_X11)
48#include <gst/gl/egl/gstgldisplay_egl.h>
49#endif
50
51#ifdef GDK_WINDOWING_MACOS
52#include <gdk/macos/gdkmacos.h>
53#endif
54
55#include <gst/gl/gstglfuncs.h>
56
57enum {
58 PROP_0,
59 PROP_PAINTABLE,
60 PROP_GL_CONTEXT,
61
62 N_PROPS,
63};
64
65GST_DEBUG_CATEGORY (gtk_debug_gst_sink);
66#define GST_CAT_DEFAULT gtk_debug_gst_sink
67
68#define FORMATS "{ BGRA, ARGB, RGBA, ABGR, RGB, BGR }"
69
70#define NOGL_CAPS GST_VIDEO_CAPS_MAKE (FORMATS)
71
72static GstStaticPadTemplate gtk_gst_sink_template =
73GST_STATIC_PAD_TEMPLATE ("sink",
74 GST_PAD_SINK,
75 GST_PAD_ALWAYS,
76 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_GL_MEMORY "), "
77 "format = (string) RGBA, "
78 "width = " GST_VIDEO_SIZE_RANGE ", "
79 "height = " GST_VIDEO_SIZE_RANGE ", "
80 "framerate = " GST_VIDEO_FPS_RANGE ", "
81 "texture-target = (string) 2D"
82 "; " NOGL_CAPS)
83 );
84
85G_DEFINE_TYPE_WITH_CODE (GtkGstSink, gtk_gst_sink,
86 GST_TYPE_VIDEO_SINK,
87 GST_DEBUG_CATEGORY_INIT (gtk_debug_gst_sink,
88 "gtkgstsink", 0, "GtkGstMediaFile Video Sink"));
89
90static GParamSpec *properties[N_PROPS] = { NULL, };
91
92
93static void
94gtk_gst_sink_get_times (GstBaseSink *bsink,
95 GstBuffer *buf,
96 GstClockTime *start,
97 GstClockTime *end)
98{
99 GtkGstSink *gtk_sink;
100
101 gtk_sink = GTK_GST_SINK (bsink);
102
103 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf))
104 {
105 *start = GST_BUFFER_TIMESTAMP (buf);
106 if (GST_BUFFER_DURATION_IS_VALID (buf))
107 *end = *start + GST_BUFFER_DURATION (buf);
108 else
109 {
110 if (GST_VIDEO_INFO_FPS_N (&gtk_sink->v_info) > 0)
111 {
112 *end = *start +
113 gst_util_uint64_scale_int (GST_SECOND,
114 GST_VIDEO_INFO_FPS_D (&gtk_sink->v_info),
115 GST_VIDEO_INFO_FPS_N (&gtk_sink->v_info));
116 }
117 }
118 }
119}
120
121static GstCaps *
122gtk_gst_sink_get_caps (GstBaseSink *bsink,
123 GstCaps *filter)
124{
125 GtkGstSink *self = GTK_GST_SINK (bsink);
126 GstCaps *tmp;
127 GstCaps *result;
128
129 if (self->gst_context)
130 {
131 tmp = gst_pad_get_pad_template_caps (GST_BASE_SINK_PAD (bsink));
132 }
133 else
134 {
135 tmp = gst_caps_from_string (NOGL_CAPS);
136 }
137 GST_DEBUG_OBJECT (self, "advertising own caps %" GST_PTR_FORMAT, tmp);
138
139 if (filter)
140 {
141 GST_DEBUG_OBJECT (self, "intersecting with filter caps %" GST_PTR_FORMAT, filter);
142
143 result = gst_caps_intersect_full (caps1: filter, caps2: tmp, mode: GST_CAPS_INTERSECT_FIRST);
144 gst_caps_unref (caps: tmp);
145 }
146 else
147 {
148 result = tmp;
149 }
150
151 GST_DEBUG_OBJECT (self, "returning caps: %" GST_PTR_FORMAT, result);
152
153 return result;
154}
155
156static gboolean
157gtk_gst_sink_set_caps (GstBaseSink *bsink,
158 GstCaps *caps)
159{
160 GtkGstSink *self = GTK_GST_SINK (bsink);
161
162 GST_DEBUG_OBJECT (self, "set caps with %" GST_PTR_FORMAT, caps);
163
164 if (!gst_video_info_from_caps (info: &self->v_info, caps))
165 return FALSE;
166
167 return TRUE;
168}
169
170static gboolean
171gtk_gst_sink_query (GstBaseSink *bsink,
172 GstQuery *query)
173{
174 GtkGstSink *self = GTK_GST_SINK (bsink);
175
176 if (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT &&
177 self->gst_display != NULL &&
178 gst_gl_handle_context_query (GST_ELEMENT (self), query, display: self->gst_display, context: self->gst_context, other_context: self->gst_app_context))
179 return TRUE;
180
181 return GST_BASE_SINK_CLASS (gtk_gst_sink_parent_class)->query (bsink, query);
182}
183
184static gboolean
185gtk_gst_sink_propose_allocation (GstBaseSink *bsink,
186 GstQuery *query)
187{
188 GtkGstSink *self = GTK_GST_SINK (bsink);
189 GstBufferPool *pool = NULL;
190 GstStructure *config;
191 GstCaps *caps;
192 guint size;
193 gboolean need_pool;
194 GstVideoInfo info;
195
196 if (!self->gst_context)
197 return FALSE;
198
199 gst_query_parse_allocation (query, caps: &caps, need_pool: &need_pool);
200
201 if (caps == NULL)
202 {
203 GST_DEBUG_OBJECT (bsink, "no caps specified");
204 return FALSE;
205 }
206
207 if (!gst_caps_features_contains (features: gst_caps_get_features (caps, index: 0), GST_CAPS_FEATURE_MEMORY_GL_MEMORY))
208 return FALSE;
209
210 if (!gst_video_info_from_caps (info: &info, caps))
211 {
212 GST_DEBUG_OBJECT (self, "invalid caps specified");
213 return FALSE;
214 }
215
216 /* the normal size of a frame */
217 size = info.size;
218
219 if (need_pool)
220 {
221 GST_DEBUG_OBJECT (self, "create new pool");
222 pool = gst_gl_buffer_pool_new (context: self->gst_context);
223
224 config = gst_buffer_pool_get_config (pool);
225 gst_buffer_pool_config_set_params (config, caps, size, min_buffers: 0, max_buffers: 0);
226 gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_GL_SYNC_META);
227
228 if (!gst_buffer_pool_set_config (pool, config))
229 {
230 GST_DEBUG_OBJECT (bsink, "failed setting config");
231 gst_object_unref (object: pool);
232 return FALSE;
233 }
234 }
235
236 /* we need at least 2 buffer because we hold on to the last one */
237 gst_query_add_allocation_pool (query, pool, size, min_buffers: 2, max_buffers: 0);
238 if (pool)
239 gst_object_unref (object: pool);
240
241 /* we also support various metadata */
242 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, params: 0);
243
244 if (self->gst_context->gl_vtable->FenceSync)
245 gst_query_add_allocation_meta (query, GST_GL_SYNC_META_API_TYPE, params: 0);
246
247 return TRUE;
248}
249
250static GdkMemoryFormat
251gtk_gst_memory_format_from_video (GstVideoFormat format)
252{
253 switch ((guint) format)
254 {
255 case GST_VIDEO_FORMAT_BGRA:
256 return GDK_MEMORY_B8G8R8A8;
257 case GST_VIDEO_FORMAT_ARGB:
258 return GDK_MEMORY_A8R8G8B8;
259 case GST_VIDEO_FORMAT_RGBA:
260 return GDK_MEMORY_R8G8B8A8;
261 case GST_VIDEO_FORMAT_ABGR:
262 return GDK_MEMORY_A8B8G8R8;
263 case GST_VIDEO_FORMAT_RGB:
264 return GDK_MEMORY_R8G8B8;
265 case GST_VIDEO_FORMAT_BGR:
266 return GDK_MEMORY_B8G8R8;
267 default:
268 g_assert_not_reached ();
269 return GDK_MEMORY_A8R8G8B8;
270 }
271}
272
273static void
274video_frame_free (GstVideoFrame *frame)
275{
276 gst_video_frame_unmap (frame);
277 g_free (mem: frame);
278}
279
280static GdkTexture *
281gtk_gst_sink_texture_from_buffer (GtkGstSink *self,
282 GstBuffer *buffer,
283 double *pixel_aspect_ratio)
284{
285 GstVideoFrame *frame = g_new (GstVideoFrame, 1);
286 GdkTexture *texture;
287
288 if (self->gdk_context &&
289 gst_video_frame_map (frame, info: &self->v_info, buffer, flags: GST_MAP_READ | GST_MAP_GL))
290 {
291 GstGLSyncMeta *sync_meta;
292
293 sync_meta = gst_buffer_get_gl_sync_meta (buffer);
294 if (sync_meta) {
295 gst_gl_sync_meta_set_sync_point (sync_meta, context: self->gst_context);
296 gst_gl_sync_meta_wait (sync_meta, context: self->gst_context);
297 }
298
299 texture = gdk_gl_texture_new (context: self->gdk_context,
300 id: *(guint *) frame->data[0],
301 width: frame->info.width,
302 height: frame->info.height,
303 destroy: (GDestroyNotify) video_frame_free,
304 data: frame);
305
306 *pixel_aspect_ratio = ((double) frame->info.par_n) / ((double) frame->info.par_d);
307 }
308 else if (gst_video_frame_map (frame, info: &self->v_info, buffer, flags: GST_MAP_READ))
309 {
310 GBytes *bytes;
311
312 bytes = g_bytes_new_with_free_func (data: frame->data[0],
313 size: frame->info.height * frame->info.stride[0],
314 free_func: (GDestroyNotify) video_frame_free,
315 user_data: frame);
316 texture = gdk_memory_texture_new (width: frame->info.width,
317 height: frame->info.height,
318 format: gtk_gst_memory_format_from_video (GST_VIDEO_FRAME_FORMAT (frame)),
319 bytes,
320 stride: frame->info.stride[0]);
321 g_bytes_unref (bytes);
322
323 *pixel_aspect_ratio = ((double) frame->info.par_n) / ((double) frame->info.par_d);
324 }
325 else
326 {
327 GST_ERROR_OBJECT (self, "Could not convert buffer to texture.");
328 texture = NULL;
329 g_free (mem: frame);
330 }
331
332 return texture;
333}
334
335static GstFlowReturn
336gtk_gst_sink_show_frame (GstVideoSink *vsink,
337 GstBuffer *buf)
338{
339 GtkGstSink *self;
340 GdkTexture *texture;
341 double pixel_aspect_ratio;
342
343 GST_TRACE ("rendering buffer:%p", buf);
344
345 self = GTK_GST_SINK (vsink);
346
347 GST_OBJECT_LOCK (self);
348
349 texture = gtk_gst_sink_texture_from_buffer (self, buffer: buf, pixel_aspect_ratio: &pixel_aspect_ratio);
350 if (texture)
351 {
352 gtk_gst_paintable_queue_set_texture (self: self->paintable, texture, pixel_aspect_ratio);
353 g_object_unref (object: texture);
354 }
355
356 GST_OBJECT_UNLOCK (self);
357
358 return GST_FLOW_OK;
359}
360
361#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
362#define HANDLE_EXTERNAL_WGL_MAKE_CURRENT(ctx) handle_wgl_makecurrent(ctx)
363#define DEACTIVATE_WGL_CONTEXT(ctx) deactivate_gdk_wgl_context(ctx)
364#define REACTIVATE_WGL_CONTEXT(ctx) reactivate_gdk_wgl_context(ctx)
365
366static void
367handle_wgl_makecurrent (GdkGLContext *ctx)
368{
369 if (!gdk_gl_context_get_use_es (ctx))
370 epoxy_handle_external_wglMakeCurrent();
371}
372
373static void
374deactivate_gdk_wgl_context (GdkGLContext *ctx)
375{
376 if (!gdk_gl_context_get_use_es (ctx))
377 {
378 HDC hdc = GetDC (GDK_SURFACE_HWND (gdk_gl_context_get_surface (ctx)));
379 wglMakeCurrent (hdc, NULL);
380 }
381}
382
383static void
384reactivate_gdk_wgl_context (GdkGLContext *ctx)
385{
386 if (!gdk_gl_context_get_use_es (ctx))
387 gdk_gl_context_make_current (ctx);
388}
389
390/*
391 * Unfortunately, libepoxy does not offer a way to allow us to safely call
392 * gst_gl_context_get_current_gl_api() on a WGL context that underlies a
393 * GdkGLContext after we notify libepoxy an external wglMakeCurrent() has
394 * been called (which is required for the first gdk_gl_context_make_current()
395 * call in gtk_gst_sink_initialize_gl(), for instance), so we can't do
396 * gst_gl_context_get_current_gl_api() directly on WGL contexts that underlies
397 * GdkGLContext's. So, we just ask GDK about our WGL context, since it already
398 * knows what kind of WGL context we have there...
399 */
400static gboolean
401check_win32_gst_gl_api (GdkGLContext *ctx,
402 GstGLPlatform *platform,
403 GstGLAPI *gl_api)
404{
405 gboolean is_gles = gdk_gl_context_get_use_es (ctx);
406
407 g_return_val_if_fail (*gl_api == GST_GL_API_NONE, FALSE);
408
409 *platform = is_gles ? GST_GL_PLATFORM_EGL : GST_GL_PLATFORM_WGL;
410
411 if (is_gles)
412 *gl_api = gst_gl_context_get_current_gl_api (*platform, NULL, NULL);
413 else
414 *gl_api = gdk_gl_context_is_legacy (ctx) ? GST_GL_API_OPENGL : GST_GL_API_OPENGL3;
415
416 return is_gles;
417}
418#else
419#define HANDLE_EXTERNAL_WGL_MAKE_CURRENT(ctx)
420#define DEACTIVATE_WGL_CONTEXT(ctx)
421#define REACTIVATE_WGL_CONTEXT(ctx)
422#endif
423
424static gboolean
425gtk_gst_sink_initialize_gl (GtkGstSink *self)
426{
427 GdkDisplay *display;
428 GError *error = NULL;
429 GstGLPlatform platform = GST_GL_PLATFORM_NONE;
430 GstGLAPI gl_api = GST_GL_API_NONE;
431 guintptr gl_handle = 0;
432 gboolean succeeded = FALSE;
433
434 display = gdk_gl_context_get_display (context: self->gdk_context);
435
436 HANDLE_EXTERNAL_WGL_MAKE_CURRENT (self->gdk_context);
437 gdk_gl_context_make_current (context: self->gdk_context);
438
439#ifdef HAVE_GST_X11_SUPPORT
440 if (GDK_IS_X11_DISPLAY (display))
441 {
442 gpointer display_ptr;
443
444#if GST_GL_HAVE_PLATFORM_EGL
445 display_ptr = gdk_x11_display_get_egl_display (display);
446 if (display_ptr)
447 {
448 GST_DEBUG_OBJECT (self, "got EGL on X11!");
449 platform = GST_GL_PLATFORM_EGL;
450 self->gst_display = GST_GL_DISPLAY (gst_gl_display_egl_new_with_egl_display (display_ptr));
451 }
452#endif
453#if GST_GL_HAVE_PLATFORM_GLX
454 if (!self->gst_display)
455 {
456 GST_DEBUG_OBJECT (self, "got GLX on X11!");
457 platform = GST_GL_PLATFORM_GLX;
458 display_ptr = gdk_x11_display_get_xdisplay (display);
459 self->gst_display = GST_GL_DISPLAY (gst_gl_display_x11_new_with_display (display_ptr));
460 }
461#endif
462
463 gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
464 gl_handle = gst_gl_context_get_current_gl_context (context_type: platform);
465
466 if (gl_handle)
467 {
468 self->gst_app_context = gst_gl_context_new_wrapped (display: self->gst_display, handle: gl_handle, context_type: platform, available_apis: gl_api);
469 }
470 else
471 {
472 GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext");
473 return FALSE;
474 }
475 }
476 else
477#endif
478#ifdef HAVE_GST_WAYLAND_SUPPORT
479 if (GDK_IS_WAYLAND_DISPLAY (display))
480 {
481 platform = GST_GL_PLATFORM_EGL;
482
483 GST_DEBUG_OBJECT (self, "got EGL on Wayland!");
484
485 gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
486 gl_handle = gst_gl_context_get_current_gl_context (context_type: platform);
487
488 if (gl_handle)
489 {
490 struct wl_display *wayland_display;
491
492 wayland_display = gdk_wayland_display_get_wl_display (display);
493 self->gst_display = GST_GL_DISPLAY (gst_gl_display_wayland_new_with_display (wayland_display));
494 self->gst_app_context = gst_gl_context_new_wrapped (display: self->gst_display, handle: gl_handle, context_type: platform, available_apis: gl_api);
495 }
496 else
497 {
498 GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using Wayland EGL");
499 return FALSE;
500 }
501 }
502 else
503#endif
504#if defined(GST_GL_HAVE_PLATFORM_CGL) && defined(GDK_WINDOWING_MACOS)
505 if (GDK_IS_MACOS_DISPLAY (display))
506 {
507 platform = GST_GL_PLATFORM_CGL;
508
509 GST_DEBUG_OBJECT (self, "got CGL on macOS!");
510
511 gl_api = gst_gl_context_get_current_gl_api (platform, NULL, NULL);
512 gl_handle = gst_gl_context_get_current_gl_context (platform);
513
514 if (gl_handle)
515 {
516 self->gst_display = gst_gl_display_new ();
517 self->gst_app_context = gst_gl_context_new_wrapped (self->gst_display, gl_handle, platform, gl_api);
518 }
519 else
520 {
521 GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using macOS CGL");
522 return FALSE;
523 }
524 }
525 else
526#endif
527#if GST_GL_HAVE_WINDOW_WIN32 && (GST_GL_HAVE_PLATFORM_WGL || GST_GL_HAVE_PLATFORM_EGL) && defined (GDK_WINDOWING_WIN32)
528 if (GDK_IS_WIN32_DISPLAY (display))
529 {
530 gboolean is_gles = check_win32_gst_gl_api (self->gdk_context, &platform, &gl_api);
531 const gchar *gl_type = is_gles ? "EGL" : "WGL";
532
533 GST_DEBUG_OBJECT (self, "got %s on Win32!", gl_type);
534
535 gl_handle = gst_gl_context_get_current_gl_context (platform);
536
537 if (gl_handle)
538 {
539 /*
540 * We must force a win32 GstGL display type and if using desktop GL, the GL_Platform to be WGL
541 * and an appropriate GstGL API depending on the gl_api we receive. We also ensure that we use
542 * an EGL GstGL API if we are using EGL in GDK. Envvars are required, unless
543 * gst_gl_display_new_with_type() is available, unfortunately, so that gst_gl_display_new() does
544 * things correctly if we have GstGL built with both EGL and WGL support for the WGL case,
545 * otherwise gst_gl_display_new() will assume an EGL display, which won't work for us
546 */
547
548 if (gl_api & (GST_GL_API_OPENGL3 | GST_GL_API_OPENGL))
549 {
550#ifdef HAVE_GST_GL_DISPLAY_NEW_WITH_TYPE
551 self->gst_display = gst_gl_display_new_with_type (GST_GL_DISPLAY_TYPE_WIN32);
552#else
553#if GST_GL_HAVE_PLATFORM_EGL
554 g_message ("If media fails to play, set the envvar `GST_DEBUG=1`, and if GstGL context creation fails");
555 g_message ("due to \"Couldn't create GL context: Cannot share context with non-EGL context\",");
556 g_message ("set in the environment `GST_GL_PLATFORM=wgl` and `GST_GL_WINDOW=win32`,");
557 g_message ("and restart the GTK application");
558#endif
559
560 self->gst_display = gst_gl_display_new ();
561#endif
562 }
563
564#if GST_GL_HAVE_PLATFORM_EGL
565 else
566 {
567 gpointer display_ptr = gdk_win32_display_get_egl_display (display);
568 self->gst_display = GST_GL_DISPLAY (gst_gl_display_egl_new_with_egl_display (display_ptr));
569 }
570#endif
571
572 gst_gl_display_filter_gl_api (self->gst_display, gl_api);
573 self->gst_app_context = gst_gl_context_new_wrapped (self->gst_display, gl_handle, platform, gl_api);
574 }
575 else
576 {
577 GST_ERROR_OBJECT (self, "Failed to get handle from GdkGLContext, not using %s", gl_type);
578 return FALSE;
579 }
580 }
581 else
582#endif
583 {
584 GST_INFO_OBJECT (self, "Unsupported GDK display %s for GL", G_OBJECT_TYPE_NAME (display));
585 return FALSE;
586 }
587
588 g_assert (self->gst_app_context != NULL);
589
590 gst_gl_context_activate (context: self->gst_app_context, TRUE);
591
592 if (!gst_gl_context_fill_info (context: self->gst_app_context, error: &error))
593 {
594 GST_ERROR_OBJECT (self, "failed to retrieve GDK context info: %s", error->message);
595 g_clear_error (err: &error);
596 g_clear_object (&self->gst_app_context);
597 g_clear_object (&self->gst_display);
598 HANDLE_EXTERNAL_WGL_MAKE_CURRENT (self->gdk_context);
599 return FALSE;
600 }
601 else
602 {
603 DEACTIVATE_WGL_CONTEXT (self->gdk_context);
604 gst_gl_context_activate (context: self->gst_app_context, FALSE);
605 }
606
607 succeeded = gst_gl_display_create_context (display: self->gst_display, other_context: self->gst_app_context, p_context: &self->gst_context, error: &error);
608
609 if (!succeeded)
610 {
611 GST_ERROR_OBJECT (self, "Couldn't create GL context: %s", error->message);
612 g_error_free (error);
613 g_clear_object (&self->gst_app_context);
614 g_clear_object (&self->gst_display);
615 }
616
617 HANDLE_EXTERNAL_WGL_MAKE_CURRENT (self->gdk_context);
618 REACTIVATE_WGL_CONTEXT (self->gdk_context);
619 return succeeded;
620}
621
622static void
623gtk_gst_sink_set_property (GObject *object,
624 guint prop_id,
625 const GValue *value,
626 GParamSpec *pspec)
627
628{
629 GtkGstSink *self = GTK_GST_SINK (object);
630
631 switch (prop_id)
632 {
633 case PROP_PAINTABLE:
634 self->paintable = g_value_dup_object (value);
635 if (self->paintable == NULL)
636 self->paintable = GTK_GST_PAINTABLE (ptr: gtk_gst_paintable_new ());
637 break;
638
639 case PROP_GL_CONTEXT:
640 self->gdk_context = g_value_dup_object (value);
641 if (self->gdk_context != NULL && !gtk_gst_sink_initialize_gl (self))
642 g_clear_object (&self->gdk_context);
643 break;
644
645 default:
646 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
647 break;
648 }
649}
650
651static void
652gtk_gst_sink_get_property (GObject *object,
653 guint prop_id,
654 GValue *value,
655 GParamSpec *pspec)
656{
657 GtkGstSink *self = GTK_GST_SINK (object);
658
659 switch (prop_id)
660 {
661 case PROP_PAINTABLE:
662 g_value_set_object (value, v_object: self->paintable);
663 break;
664 case PROP_GL_CONTEXT:
665 g_value_set_object (value, v_object: self->gdk_context);
666 break;
667
668 default:
669 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
670 break;
671 }
672}
673
674static void
675gtk_gst_sink_dispose (GObject *object)
676{
677 GtkGstSink *self = GTK_GST_SINK (object);
678
679 g_clear_object (&self->paintable);
680 g_clear_object (&self->gst_app_context);
681 g_clear_object (&self->gst_display);
682 g_clear_object (&self->gdk_context);
683
684 G_OBJECT_CLASS (gtk_gst_sink_parent_class)->dispose (object);
685}
686
687static void
688gtk_gst_sink_class_init (GtkGstSinkClass * klass)
689{
690 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
691 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
692 GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
693 GstVideoSinkClass *gstvideosink_class = GST_VIDEO_SINK_CLASS (klass);
694
695 gobject_class->set_property = gtk_gst_sink_set_property;
696 gobject_class->get_property = gtk_gst_sink_get_property;
697 gobject_class->dispose = gtk_gst_sink_dispose;
698
699 gstbasesink_class->set_caps = gtk_gst_sink_set_caps;
700 gstbasesink_class->get_times = gtk_gst_sink_get_times;
701 gstbasesink_class->query = gtk_gst_sink_query;
702 gstbasesink_class->propose_allocation = gtk_gst_sink_propose_allocation;
703 gstbasesink_class->get_caps = gtk_gst_sink_get_caps;
704
705 gstvideosink_class->show_frame = gtk_gst_sink_show_frame;
706
707 /**
708 * GtkGstSink:paintable:
709 *
710 * The paintable that provides the picture for this sink.
711 */
712 properties[PROP_PAINTABLE] =
713 g_param_spec_object (name: "paintable",
714 P_("paintable"),
715 P_("Paintable providing the picture"),
716 GTK_TYPE_GST_PAINTABLE,
717 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
718
719 /**
720 * GtkGstSink:gl-context:
721 *
722 * The #GdkGLContext to use for GL rendering.
723 */
724 properties[PROP_GL_CONTEXT] =
725 g_param_spec_object (name: "gl-context",
726 P_("GL context"),
727 P_("GL context to use for rendering"),
728 GDK_TYPE_GL_CONTEXT,
729 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
730
731 g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: properties);
732
733 gst_element_class_set_metadata (klass: gstelement_class,
734 longname: "GtkMediaStream Video Sink",
735 classification: "Sink/Video", description: "The video sink used by GtkMediaStream",
736 author: "Matthew Waters <matthew@centricular.com>, "
737 "Benjamin Otte <otte@gnome.org>");
738
739 gst_element_class_add_static_pad_template (klass: gstelement_class,
740 static_templ: &gtk_gst_sink_template);
741}
742
743static void
744gtk_gst_sink_init (GtkGstSink * gtk_sink)
745{
746}
747
748

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