2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
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 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-ximagesink
25 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
26 * display. This element can receive a Window ID from the application through
27 * the XOverlay interface and will then render video frames in this drawable.
28 * If no Window ID was provided by the application, the element will create its
29 * own internal window and render into it.
31 * <title>Scaling</title>
33 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
34 * reverse caps negotiation to try to get scaled video frames for the drawable.
35 * This is accomplished by asking the peer pad if it accepts some different caps
36 * which in most cases implies that there is a scaling element in the pipeline,
37 * or that an element generating the video frames can generate them with a
38 * different geometry. This mechanism is handled during buffer allocations, for
39 * each allocation request the video sink will check the drawable geometry, look
41 * <link linkend="GstXImageSink--force-aspect-ratio">force-aspect-ratio</link>
42 * property, calculate the geometry of desired video frames and then check that
43 * the peer pad accept those new caps. If it does it will then allocate a buffer
44 * in video memory with this new geometry and return it with the new caps.
46 * <title>Events</title>
48 * XImageSink creates a thread to handle events coming from the drawable. There
49 * are several kind of events that can be grouped in 2 big categories: input
50 * events and window state related events. Input events will be translated to
51 * navigation events and pushed upstream for other elements to react on them.
52 * This includes events such as pointer moves, key press/release, clicks etc...
53 * Other events are used to handle the drawable appearance even when the data
54 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55 * paused, it will receive expose events from the drawable and draw the latest
56 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XImageSink will open a connection to
61 * the display specified in the
62 * <link linkend="GstXImageSink--display">display</link> property or the default
63 * display if nothing specified. Once this connection is open it will inspect
64 * the display configuration including the physical display geometry and
65 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
66 * video sink will set the calculated pixel aspect ratio on the caps to make
67 * sure that incoming video frames will have the correct pixel aspect ratio for
68 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
69 * then possible to enforce a specific pixel aspect ratio using the
70 * <link linkend="GstXImageSink--pixel-aspect-ratio">pixel-aspect-ratio</link>
73 * <title>Examples</title>
75 * Here is a simple pipeline to test reverse negotiation :
77 * gst-launch -v videotestsrc ! queue ! ximagesink
79 * When the test video signal appears you can resize the window and see that
80 * scaled buffers of the desired size are going to arrive with a short delay.
81 * This illustrates how buffers of desired size are allocated along the way.
82 * If you take away the queue, scaling will happen almost immediately.
85 * Here is a simple pipeline to test navigation events :
87 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
89 * While moving the mouse pointer over the test signal you will see a black box
90 * following the mouse pointer. If you press the mouse button somewhere on the
91 * video and release it somewhere else a green box will appear where you pressed
92 * the button and a red one where you released it. (The navigationtest element
93 * is part of gst-plugins-good.)
96 * Here is a simple pipeline to test pixel aspect ratio :
98 * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
100 * This is faking a 4/3 pixel aspect ratio caps on video frames produced by
101 * videotestsrc, in most cases the pixel aspect ratio of the display will be
102 * 1/1. This means that videoscale will have to do the scaling to convert
103 * incoming frames to a size that will match the display pixel aspect ratio
104 * (from 320x240 to 320x180 in this case). Note that you might have to escape
105 * some characters for your shell like '\(fraction\)'.
115 #include <gst/interfaces/navigation.h>
116 #include <gst/interfaces/xoverlay.h>
119 #include "ximagesink.h"
121 /* Debugging category */
122 #include <gst/gstinfo.h>
124 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
125 #define GST_CAT_DEFAULT gst_debug_ximagesink
130 unsigned long functions;
131 unsigned long decorations;
133 unsigned long status;
135 MotifWmHints, MwmHints;
137 #define MWM_HINTS_DECORATIONS (1L << 1)
139 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
140 GstXImageBuffer * ximage);
141 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
142 GstXWindow * xwindow);
143 static void gst_ximagesink_expose (GstXOverlay * overlay);
145 /* ElementFactory information */
146 static const GstElementDetails gst_ximagesink_details =
147 GST_ELEMENT_DETAILS ("Video sink",
149 "A standard X based videosink",
150 "Julien Moutte <julien@moutte.net>");
152 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
153 GST_STATIC_PAD_TEMPLATE ("sink",
156 GST_STATIC_CAPS ("video/x-raw-rgb, "
157 "framerate = (fraction) [ 0, MAX ], "
158 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
166 PROP_PIXEL_ASPECT_RATIO,
167 PROP_FORCE_ASPECT_RATIO
171 static GstVideoSinkClass *parent_class = NULL;
173 /* ============================================================= */
175 /* Private Methods */
177 /* ============================================================= */
181 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
183 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
184 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
185 #define GST_XIMAGE_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
187 /* So some words about GstMiniObject, this is pretty messy...
188 GstMiniObject does not use the standard finalizing of GObjects, you are
189 supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
190 which will handle its own refcount system and call gst_mini_object_free.
191 gst_mini_object_free will call the class finalize method which is not the
192 one from GObject, after calling this finalize method it will free the object
193 instance for you if the refcount is still 0 so you should not chain up */
195 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
197 GstXImageSink *ximagesink = NULL;
198 gboolean recycled = FALSE;
201 g_return_if_fail (ximage != NULL);
203 ximagesink = ximage->ximagesink;
205 GST_WARNING_OBJECT (ximagesink, "no sink found");
209 GST_OBJECT_LOCK (ximagesink);
210 running = ximagesink->running;
211 GST_OBJECT_UNLOCK (ximagesink);
213 if (running == FALSE) {
214 /* If the sink is shutting down, need to clear the image */
215 GST_DEBUG_OBJECT (ximagesink,
216 "destroy image %p because the sink is shutting down", ximage);
217 gst_ximagesink_ximage_destroy (ximagesink, ximage);
218 } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
219 (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
220 /* If our geometry changed we can't reuse that image. */
221 GST_DEBUG_OBJECT (ximagesink,
222 "destroy image %p as its size changed %dx%d vs current %dx%d",
223 ximage, ximage->width, ximage->height,
224 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
225 gst_ximagesink_ximage_destroy (ximagesink, ximage);
227 /* In that case we can reuse the image and add it to our image pool. */
228 GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
229 /* need to increment the refcount again to recycle */
230 gst_buffer_ref (GST_BUFFER_CAST (ximage));
231 g_mutex_lock (ximagesink->pool_lock);
232 ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
233 g_mutex_unlock (ximagesink->pool_lock);
242 gst_ximage_buffer_free (GstXImageBuffer * ximage)
244 /* make sure it is not recycled */
247 gst_buffer_unref (GST_BUFFER_CAST (ximage));
251 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
254 ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
255 ximage_buffer->SHMInfo.shmid = -1;
260 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
262 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
264 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
265 gst_ximage_buffer_finalize;
269 gst_ximage_buffer_get_type (void)
271 static GType _gst_ximage_buffer_type;
273 if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
274 static const GTypeInfo ximage_buffer_info = {
275 sizeof (GstBufferClass),
278 gst_ximage_buffer_class_init,
281 sizeof (GstXImageBuffer),
283 (GInstanceInitFunc) gst_ximage_buffer_init,
286 _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
287 "GstXImageBuffer", &ximage_buffer_info, 0);
289 return _gst_ximage_buffer_type;
294 #ifdef HAVE_XSHM /* Check that XShm calls actually work */
295 static gboolean error_caught = FALSE;
298 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
300 char error_msg[1024];
302 XGetErrorText (display, xevent->error_code, error_msg, 1024);
303 GST_DEBUG ("ximagesink failed to use XShm calls. error: %s", error_msg);
309 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
310 GstXContext * xcontext)
313 XShmSegmentInfo SHMInfo;
315 int (*handler) (Display *, XErrorEvent *);
316 gboolean result = FALSE;
317 gboolean did_attach = FALSE;
319 g_return_val_if_fail (xcontext != NULL, FALSE);
321 /* Sync to ensure any older errors are already processed */
322 XSync (xcontext->disp, FALSE);
324 /* Set defaults so we don't free these later unnecessarily */
325 SHMInfo.shmaddr = ((void *) -1);
328 /* Setting an error handler to catch failure */
329 error_caught = FALSE;
330 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
332 /* Trying to create a 1x1 ximage */
333 GST_DEBUG ("XShmCreateImage of 1x1");
335 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
336 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
338 /* Might cause an error, sync to ensure it is noticed */
339 XSync (xcontext->disp, FALSE);
340 if (!ximage || error_caught) {
341 GST_WARNING ("could not XShmCreateImage a 1x1 image");
344 size = ximage->height * ximage->bytes_per_line;
346 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
347 if (SHMInfo.shmid == -1) {
348 GST_WARNING ("could not get shared memory of %d bytes", size);
352 SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
353 if (SHMInfo.shmaddr == ((void *) -1)) {
354 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
355 /* Clean up shm seg */
356 shmctl (SHMInfo.shmid, IPC_RMID, 0);
360 /* Delete the shared memory segment as soon as we manage to attach.
361 * This way, it will be deleted as soon as we detach later, and not
362 * leaked if we crash. */
363 shmctl (SHMInfo.shmid, IPC_RMID, 0);
365 ximage->data = SHMInfo.shmaddr;
366 SHMInfo.readOnly = FALSE;
368 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
369 GST_WARNING ("Failed to XShmAttach");
373 /* Sync to ensure we see any errors we caused */
374 XSync (xcontext->disp, FALSE);
378 /* store whether we succeeded in result */
383 /* Sync to ensure we swallow any errors we caused and reset error_caught */
384 XSync (xcontext->disp, FALSE);
385 error_caught = FALSE;
386 XSetErrorHandler (handler);
389 XShmDetach (xcontext->disp, &SHMInfo);
390 XSync (xcontext->disp, FALSE);
392 if (SHMInfo.shmaddr != ((void *) -1))
393 shmdt (SHMInfo.shmaddr);
395 XDestroyImage (ximage);
398 #endif /* HAVE_XSHM */
400 /* This function handles GstXImageBuffer creation depending on XShm availability */
401 static GstXImageBuffer *
402 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
404 GstXImageBuffer *ximage = NULL;
405 GstStructure *structure = NULL;
406 gboolean succeeded = FALSE;
408 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
410 ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
412 structure = gst_caps_get_structure (caps, 0);
414 if (!gst_structure_get_int (structure, "width", &ximage->width) ||
415 !gst_structure_get_int (structure, "height", &ximage->height)) {
416 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
419 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
420 ximage->width, ximage->height);
422 g_mutex_lock (ximagesink->x_lock);
425 if (ximagesink->xcontext->use_xshm) {
426 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
427 ximagesink->xcontext->visual,
428 ximagesink->xcontext->depth,
429 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
430 if (!ximage->ximage) {
431 g_mutex_unlock (ximagesink->x_lock);
432 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
433 ("Failed to create output image buffer of %dx%d pixels",
434 ximage->width, ximage->height),
435 ("could not XShmCreateImage a %dx%d image",
436 ximage->width, ximage->height));
440 /* we have to use the returned bytes_per_line for our shm size */
441 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
442 GST_LOG_OBJECT (ximagesink, "XShm image size is %d, width %d, stride %d",
443 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
445 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
447 if (ximage->SHMInfo.shmid == -1) {
448 g_mutex_unlock (ximagesink->x_lock);
449 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
450 ("Failed to create output image buffer of %dx%d pixels",
451 ximage->width, ximage->height),
452 ("could not get shared memory of %d bytes", ximage->size));
456 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
457 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
458 g_mutex_unlock (ximagesink->x_lock);
459 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
460 ("Failed to create output image buffer of %dx%d pixels",
461 ximage->width, ximage->height),
462 ("Failed to shmat: %s", g_strerror (errno)));
463 /* Clean up the shared memory segment */
464 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
468 /* Now that we've attached, we can delete the shared memory segment.
469 * This way, it will be deleted as soon as we detach later, and not
470 * leaked if we crash. */
471 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
473 ximage->ximage->data = ximage->SHMInfo.shmaddr;
474 ximage->SHMInfo.readOnly = FALSE;
476 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
477 g_mutex_unlock (ximagesink->x_lock);
478 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
479 ("Failed to create output image buffer of %dx%d pixels",
480 ximage->width, ximage->height), ("Failed to XShmAttach"));
484 XSync (ximagesink->xcontext->disp, FALSE);
486 #endif /* HAVE_XSHM */
488 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
489 ximagesink->xcontext->visual,
490 ximagesink->xcontext->depth,
492 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
493 if (!ximage->ximage) {
494 g_mutex_unlock (ximagesink->x_lock);
495 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
496 ("Failed to create output image buffer of %dx%d pixels",
497 ximage->width, ximage->height),
498 ("could not XCreateImage a %dx%d image",
499 ximage->width, ximage->height));
503 /* we have to use the returned bytes_per_line for our image size */
504 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
505 ximage->ximage->data = g_malloc (ximage->size);
507 XSync (ximagesink->xcontext->disp, FALSE);
511 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
512 GST_BUFFER_SIZE (ximage) = ximage->size;
514 /* Keep a ref to our sink */
515 ximage->ximagesink = gst_object_ref (ximagesink);
517 g_mutex_unlock (ximagesink->x_lock);
520 gst_ximage_buffer_free (ximage);
527 /* This function destroys a GstXImageBuffer handling XShm availability */
529 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
530 GstXImageBuffer * ximage)
532 g_return_if_fail (ximage != NULL);
533 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
535 /* If the destroyed image is the current one we destroy our reference too */
536 if (ximagesink->cur_image == ximage) {
537 ximagesink->cur_image = NULL;
540 /* Hold the object lock to ensure the XContext doesn't disappear */
541 GST_OBJECT_LOCK (ximagesink);
543 /* We might have some buffers destroyed after changing state to NULL */
544 if (!ximagesink->xcontext) {
545 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
547 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
548 shmdt (ximage->SHMInfo.shmaddr);
554 g_mutex_lock (ximagesink->x_lock);
557 if (ximagesink->xcontext->use_xshm) {
558 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
559 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
560 XSync (ximagesink->xcontext->disp, 0);
561 shmdt (ximage->SHMInfo.shmaddr);
564 XDestroyImage (ximage->ximage);
567 #endif /* HAVE_XSHM */
569 if (ximage->ximage) {
570 XDestroyImage (ximage->ximage);
574 XSync (ximagesink->xcontext->disp, FALSE);
576 g_mutex_unlock (ximagesink->x_lock);
579 GST_OBJECT_UNLOCK (ximagesink);
581 if (ximage->ximagesink) {
582 /* Release the ref to our sink */
583 ximage->ximagesink = NULL;
584 gst_object_unref (ximagesink);
590 /* We are called with the x_lock taken */
592 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
593 GstXWindow * xwindow, GstVideoRectangle rect)
595 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
596 g_return_if_fail (xwindow != NULL);
598 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
599 ximagesink->xcontext->black);
603 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
604 0, 0, rect.x, xwindow->height);
608 if ((rect.x + rect.w) < xwindow->width) {
609 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
610 rect.x + rect.w, 0, xwindow->width, xwindow->height);
615 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
616 0, 0, xwindow->width, rect.y);
620 if ((rect.y + rect.h) < xwindow->height) {
621 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
622 0, rect.y + rect.h, xwindow->width, xwindow->height);
626 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
628 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
630 GstVideoRectangle src, dst, result;
631 gboolean draw_border = FALSE;
633 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
635 /* We take the flow_lock. If expose is in there we don't want to run
636 concurrently from the data flow thread */
637 g_mutex_lock (ximagesink->flow_lock);
639 /* Draw borders when displaying the first frame. After this
640 draw borders only on expose event. */
641 if (!ximagesink->cur_image) {
645 /* Store a reference to the last image we put, lose the previous one */
646 if (ximage && ximagesink->cur_image != ximage) {
647 if (ximagesink->cur_image) {
648 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
649 gst_buffer_unref (ximagesink->cur_image);
651 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
652 ximagesink->cur_image =
653 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
656 /* Expose sends a NULL image, we take the latest frame */
659 if (ximagesink->cur_image) {
660 ximage = ximagesink->cur_image;
662 g_mutex_unlock (ximagesink->flow_lock);
667 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
669 src.w = ximage->width;
670 src.h = ximage->height;
671 dst.w = ximagesink->xwindow->width;
672 dst.h = ximagesink->xwindow->height;
674 gst_video_sink_center_rect (src, dst, &result, FALSE);
676 g_mutex_lock (ximagesink->x_lock);
679 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
683 if (ximagesink->xcontext->use_xshm) {
684 GST_LOG_OBJECT (ximagesink,
685 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
686 ximage, 0, 0, result.x, result.y, result.w, result.h,
687 ximagesink->xwindow->width, ximagesink->xwindow->height);
688 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
689 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
690 result.w, result.h, FALSE);
692 #endif /* HAVE_XSHM */
694 GST_LOG_OBJECT (ximagesink,
695 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
696 ximage, 0, 0, result.x, result.y, result.w, result.h,
697 ximagesink->xwindow->width, ximagesink->xwindow->height);
698 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
699 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
703 XSync (ximagesink->xcontext->disp, FALSE);
705 g_mutex_unlock (ximagesink->x_lock);
707 g_mutex_unlock (ximagesink->flow_lock);
711 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
714 Atom hints_atom = None;
717 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
718 g_return_val_if_fail (window != NULL, FALSE);
720 g_mutex_lock (ximagesink->x_lock);
722 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
723 if (hints_atom == None) {
724 g_mutex_unlock (ximagesink->x_lock);
728 hints = g_malloc0 (sizeof (MotifWmHints));
730 hints->flags |= MWM_HINTS_DECORATIONS;
731 hints->decorations = 1 << 0;
733 XChangeProperty (ximagesink->xcontext->disp, window->win,
734 hints_atom, hints_atom, 32, PropModeReplace,
735 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
737 XSync (ximagesink->xcontext->disp, FALSE);
739 g_mutex_unlock (ximagesink->x_lock);
746 /* This function handles a GstXWindow creation */
748 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
750 GstXWindow *xwindow = NULL;
753 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
755 xwindow = g_new0 (GstXWindow, 1);
757 xwindow->width = width;
758 xwindow->height = height;
759 xwindow->internal = TRUE;
761 g_mutex_lock (ximagesink->x_lock);
763 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
764 ximagesink->xcontext->root,
765 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
767 /* We have to do that to prevent X from redrawing the background on
768 ConfigureNotify. This takes away flickering of video when resizing. */
769 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
771 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
772 StructureNotifyMask | PointerMotionMask | KeyPressMask |
773 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
775 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
778 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
780 XSync (ximagesink->xcontext->disp, FALSE);
782 g_mutex_unlock (ximagesink->x_lock);
784 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
786 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
791 /* This function destroys a GstXWindow */
793 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
794 GstXWindow * xwindow)
796 g_return_if_fail (xwindow != NULL);
797 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
799 g_mutex_lock (ximagesink->x_lock);
801 /* If we did not create that window we just free the GC and let it live */
802 if (xwindow->internal)
803 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
805 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
807 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
809 XSync (ximagesink->xcontext->disp, FALSE);
811 g_mutex_unlock (ximagesink->x_lock);
817 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
818 GstXWindow * xwindow)
820 XWindowAttributes attr;
822 g_return_if_fail (xwindow != NULL);
823 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
825 /* Update the window geometry */
826 g_mutex_lock (ximagesink->x_lock);
828 XGetWindowAttributes (ximagesink->xcontext->disp,
829 ximagesink->xwindow->win, &attr);
831 ximagesink->xwindow->width = attr.width;
832 ximagesink->xwindow->height = attr.height;
834 g_mutex_unlock (ximagesink->x_lock);
838 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
840 g_return_if_fail (xwindow != NULL);
841 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
843 g_mutex_lock (ximagesink->x_lock);
845 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
846 ximagesink->xcontext->black);
848 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
849 0, 0, xwindow->width, xwindow->height);
851 XSync (ximagesink->xcontext->disp, FALSE);
853 g_mutex_unlock (ximagesink->x_lock);
856 /* This function handles XEvents that might be in the queue. It generates
857 GstEvent that will be sent upstream in the pipeline to handle interactivity
860 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
864 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
867 guint pointer_x = 0, pointer_y = 0;
868 gboolean pointer_moved = FALSE;
870 /* Then we get all pointer motion events, only the last position is
872 g_mutex_lock (ximagesink->x_lock);
873 while (XCheckWindowEvent (ximagesink->xcontext->disp,
874 ximagesink->xwindow->win, PointerMotionMask, &e)) {
875 g_mutex_unlock (ximagesink->x_lock);
879 pointer_x = e.xmotion.x;
880 pointer_y = e.xmotion.y;
881 pointer_moved = TRUE;
887 g_mutex_lock (ximagesink->x_lock);
889 g_mutex_unlock (ximagesink->x_lock);
892 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
893 pointer_x, pointer_y);
894 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
895 "mouse-move", 0, pointer_x, pointer_y);
899 /* We get all remaining events on our window to throw them upstream */
900 g_mutex_lock (ximagesink->x_lock);
901 while (XCheckWindowEvent (ximagesink->xcontext->disp,
902 ximagesink->xwindow->win,
903 KeyPressMask | KeyReleaseMask |
904 ButtonPressMask | ButtonReleaseMask, &e)) {
907 /* We lock only for the X function call */
908 g_mutex_unlock (ximagesink->x_lock);
912 /* Mouse button pressed/released over our window. We send upstream
913 events for interactivity/navigation */
914 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
915 e.xbutton.button, e.xbutton.x, e.xbutton.x);
916 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
917 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
920 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
921 e.xbutton.button, e.xbutton.x, e.xbutton.x);
922 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
923 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
927 /* Key pressed/released over our window. We send upstream
928 events for interactivity/navigation */
929 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
930 e.xkey.keycode, e.xkey.x, e.xkey.x);
931 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
933 if (keysym != NoSymbol) {
934 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
936 "key-press" : "key-release", XKeysymToString (keysym));
938 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
939 e.type == KeyPress ? "key-press" : "key-release", "unknown");
943 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
946 g_mutex_lock (ximagesink->x_lock);
948 g_mutex_unlock (ximagesink->x_lock);
951 gboolean exposed = FALSE;
953 g_mutex_lock (ximagesink->x_lock);
954 while (XCheckWindowEvent (ximagesink->xcontext->disp,
955 ximagesink->xwindow->win, ExposureMask, &e)) {
956 g_mutex_unlock (ximagesink->x_lock);
966 g_mutex_lock (ximagesink->x_lock);
968 g_mutex_unlock (ximagesink->x_lock);
971 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
977 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
979 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
981 while (ximagesink->running) {
982 if (ximagesink->xwindow) {
983 gst_ximagesink_handle_xevents (ximagesink);
991 /* This function calculates the pixel aspect ratio based on the properties
992 * in the xcontext structure and stores it there. */
994 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
997 {1, 1}, /* regular screen */
998 {16, 15}, /* PAL TV */
999 {11, 10}, /* 525 line Rec.601 video */
1000 {54, 59}, /* 625 line Rec.601 video */
1001 {64, 45}, /* 1280x1024 on 16:9 display */
1002 {5, 3}, /* 1280x1024 on 4:3 display */
1003 {4, 3} /* 800x600 on 16:9 display */
1010 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1012 /* first calculate the "real" ratio based on the X values;
1013 * which is the "physical" w/h divided by the w/h in pixels of the display */
1014 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1015 / (xcontext->heightmm * xcontext->width);
1017 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1019 if (xcontext->width == 720 && xcontext->height == 576) {
1020 ratio = 4.0 * 576 / (3.0 * 720);
1022 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1024 /* now find the one from par[][2] with the lowest delta to the real one */
1028 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1029 gdouble this_delta = DELTA (i);
1031 if (this_delta < delta) {
1037 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1038 par[index][0], par[index][1]);
1040 g_free (xcontext->par);
1041 xcontext->par = g_new0 (GValue, 1);
1042 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1043 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1044 GST_DEBUG ("set xcontext PAR to %d/%d",
1045 gst_value_get_fraction_numerator (xcontext->par),
1046 gst_value_get_fraction_denominator (xcontext->par));
1049 /* This function gets the X Display and global info about it. Everything is
1050 stored in our object and will be cleaned when the object is disposed. Note
1051 here that caps for supported format are generated without any window or
1053 static GstXContext *
1054 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1056 GstXContext *xcontext = NULL;
1057 XPixmapFormatValues *px_formats = NULL;
1058 gint nb_formats = 0, i;
1060 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1062 xcontext = g_new0 (GstXContext, 1);
1064 g_mutex_lock (ximagesink->x_lock);
1066 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1068 if (!xcontext->disp) {
1069 g_mutex_unlock (ximagesink->x_lock);
1071 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1072 ("Could not initialise X output"), ("Could not open display"));
1076 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1077 xcontext->screen_num = DefaultScreen (xcontext->disp);
1078 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1079 xcontext->root = DefaultRootWindow (xcontext->disp);
1080 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1081 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1082 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1084 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1085 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1086 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1087 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1089 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1090 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1092 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1094 /* We get supported pixmap formats at supported depth */
1095 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1098 XCloseDisplay (xcontext->disp);
1099 g_mutex_unlock (ximagesink->x_lock);
1104 /* We get bpp value corresponding to our running depth */
1105 for (i = 0; i < nb_formats; i++) {
1106 if (px_formats[i].depth == xcontext->depth)
1107 xcontext->bpp = px_formats[i].bits_per_pixel;
1112 xcontext->endianness =
1113 (ImageByteOrder (xcontext->disp) ==
1114 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1116 /* Search for XShm extension support */
1118 if (XShmQueryExtension (xcontext->disp) &&
1119 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1120 xcontext->use_xshm = TRUE;
1121 GST_DEBUG ("ximagesink is using XShm extension");
1125 xcontext->use_xshm = FALSE;
1126 GST_DEBUG ("ximagesink is not using XShm extension");
1129 /* our caps system handles 24/32bpp RGB as big-endian. */
1130 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1131 xcontext->endianness == G_LITTLE_ENDIAN) {
1132 xcontext->endianness = G_BIG_ENDIAN;
1133 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1134 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1135 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1136 if (xcontext->bpp == 24) {
1137 xcontext->visual->red_mask >>= 8;
1138 xcontext->visual->green_mask >>= 8;
1139 xcontext->visual->blue_mask >>= 8;
1143 /* update object's par with calculated one if not set yet */
1144 if (!ximagesink->par) {
1145 ximagesink->par = g_new0 (GValue, 1);
1146 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1147 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1149 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1150 "bpp", G_TYPE_INT, xcontext->bpp,
1151 "depth", G_TYPE_INT, xcontext->depth,
1152 "endianness", G_TYPE_INT, xcontext->endianness,
1153 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1154 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1155 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1156 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1157 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1158 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1159 if (ximagesink->par) {
1162 nom = gst_value_get_fraction_numerator (ximagesink->par);
1163 den = gst_value_get_fraction_denominator (ximagesink->par);
1164 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1165 GST_TYPE_FRACTION, nom, den, NULL);
1168 g_mutex_unlock (ximagesink->x_lock);
1170 /* Setup our event listening thread */
1171 ximagesink->event_thread = g_thread_create (
1172 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1177 /* This function cleans the X context. Closing the Display and unrefing the
1178 caps for supported formats. */
1180 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1182 GstXContext *xcontext;
1184 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1186 GST_OBJECT_LOCK (ximagesink);
1187 if (ximagesink->xcontext == NULL) {
1188 GST_OBJECT_UNLOCK (ximagesink);
1192 /* Take the xcontext reference and NULL it while we
1193 * clean it up, so that any buffer-alloced buffers
1194 * arriving after this will be freed correctly */
1195 xcontext = ximagesink->xcontext;
1196 ximagesink->xcontext = NULL;
1198 GST_OBJECT_UNLOCK (ximagesink);
1200 /* Wait for our event thread */
1201 if (ximagesink->event_thread) {
1202 g_thread_join (ximagesink->event_thread);
1203 ximagesink->event_thread = NULL;
1206 gst_caps_unref (xcontext->caps);
1207 g_free (xcontext->par);
1208 g_free (ximagesink->par);
1209 ximagesink->par = NULL;
1211 g_mutex_lock (ximagesink->x_lock);
1213 XCloseDisplay (xcontext->disp);
1215 g_mutex_unlock (ximagesink->x_lock);
1217 g_free (ximagesink->xcontext);
1221 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1224 g_mutex_lock (ximagesink->pool_lock);
1226 while (ximagesink->buffer_pool) {
1227 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1229 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1230 ximagesink->buffer_pool);
1231 gst_ximage_buffer_free (ximage);
1234 g_mutex_unlock (ximagesink->pool_lock);
1240 gst_ximagesink_getcaps (GstBaseSink * bsink)
1242 GstXImageSink *ximagesink;
1246 ximagesink = GST_XIMAGESINK (bsink);
1248 if (ximagesink->xcontext)
1249 return gst_caps_ref (ximagesink->xcontext->caps);
1251 /* get a template copy and add the pixel aspect ratio */
1253 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->
1255 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1256 GstStructure *structure = gst_caps_get_structure (caps, i);
1258 if (ximagesink->par) {
1261 nom = gst_value_get_fraction_numerator (ximagesink->par);
1262 den = gst_value_get_fraction_denominator (ximagesink->par);
1263 gst_structure_set (structure, "pixel-aspect-ratio",
1264 GST_TYPE_FRACTION, nom, den, NULL);
1272 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1274 GstXImageSink *ximagesink;
1275 gboolean ret = TRUE;
1276 GstStructure *structure;
1277 GstCaps *intersection;
1279 gint new_width, new_height;
1282 ximagesink = GST_XIMAGESINK (bsink);
1284 if (!ximagesink->xcontext)
1287 GST_DEBUG_OBJECT (ximagesink,
1288 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1289 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1291 /* We intersect those caps with our template to make sure they are correct */
1292 intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1293 GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1295 if (gst_caps_is_empty (intersection)) {
1299 gst_caps_unref (intersection);
1301 structure = gst_caps_get_structure (caps, 0);
1303 ret &= gst_structure_get_int (structure, "width", &new_width);
1304 ret &= gst_structure_get_int (structure, "height", &new_height);
1305 fps = gst_structure_get_value (structure, "framerate");
1306 ret &= (fps != NULL);
1310 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1311 * otherwise linking should fail */
1312 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1314 if (ximagesink->par) {
1315 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1318 } else if (ximagesink->xcontext->par) {
1319 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1325 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1326 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1327 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1328 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1330 /* Notify application to set xwindow id now */
1331 if (!ximagesink->xwindow) {
1332 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1335 /* Creating our window and our image */
1336 g_assert (GST_VIDEO_SINK_WIDTH (ximagesink) > 0);
1337 g_assert (GST_VIDEO_SINK_HEIGHT (ximagesink) > 0);
1338 if (!ximagesink->xwindow) {
1339 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1340 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1343 /* If our ximage has changed we destroy it, next chain iteration will create
1345 if ((ximagesink->ximage) &&
1346 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1347 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1348 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1349 ximagesink->ximage);
1350 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1351 ximagesink->ximage = NULL;
1359 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1364 static GstStateChangeReturn
1365 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1367 GstXImageSink *ximagesink;
1368 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1369 GstXContext *xcontext = NULL;
1371 ximagesink = GST_XIMAGESINK (element);
1373 switch (transition) {
1374 case GST_STATE_CHANGE_NULL_TO_READY:
1376 /* Initializing the XContext */
1377 if (!ximagesink->xcontext)
1378 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1380 GST_OBJECT_LOCK (ximagesink);
1381 ximagesink->running = TRUE;
1383 ximagesink->xcontext = xcontext;
1384 GST_OBJECT_UNLOCK (ximagesink);
1386 if (!ximagesink->xcontext) {
1387 ret = GST_STATE_CHANGE_FAILURE;
1390 /* call XSynchronize with the current value of synchronous */
1391 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1392 ximagesink->synchronous ? "TRUE" : "FALSE");
1393 g_mutex_lock (ximagesink->x_lock);
1394 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1395 g_mutex_unlock (ximagesink->x_lock);
1397 case GST_STATE_CHANGE_READY_TO_PAUSED:
1399 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1405 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1406 if (ret == GST_STATE_CHANGE_FAILURE)
1409 switch (transition) {
1410 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1412 case GST_STATE_CHANGE_PAUSED_TO_READY:
1413 if (ximagesink->xwindow)
1414 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1415 ximagesink->fps_n = 0;
1416 ximagesink->fps_d = 1;
1417 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1418 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1420 case GST_STATE_CHANGE_READY_TO_NULL:
1421 GST_OBJECT_LOCK (ximagesink);
1422 ximagesink->running = FALSE;
1423 GST_OBJECT_UNLOCK (ximagesink);
1425 if (ximagesink->ximage) {
1426 gst_buffer_unref (ximagesink->ximage);
1427 ximagesink->ximage = NULL;
1429 if (ximagesink->cur_image) {
1430 gst_buffer_unref (ximagesink->cur_image);
1431 ximagesink->cur_image = NULL;
1434 gst_ximagesink_bufferpool_clear (ximagesink);
1436 if (ximagesink->xwindow) {
1437 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1438 ximagesink->xwindow = NULL;
1441 gst_ximagesink_xcontext_clear (ximagesink);
1452 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1453 GstClockTime * start, GstClockTime * end)
1455 GstXImageSink *ximagesink;
1457 ximagesink = GST_XIMAGESINK (bsink);
1459 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1460 *start = GST_BUFFER_TIMESTAMP (buf);
1461 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1462 *end = *start + GST_BUFFER_DURATION (buf);
1464 if (ximagesink->fps_n > 0) {
1466 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1473 static GstFlowReturn
1474 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1476 GstXImageSink *ximagesink;
1478 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1480 ximagesink = GST_XIMAGESINK (bsink);
1482 /* If this buffer has been allocated using our buffer management we simply
1483 put the ximage which is in the PRIVATE pointer */
1484 if (GST_IS_XIMAGE_BUFFER (buf)) {
1485 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1486 gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf));
1488 /* Else we have to copy the data into our private image, */
1489 /* if we have one... */
1490 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1491 if (!ximagesink->ximage) {
1492 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1493 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1494 GST_BUFFER_CAPS (buf));
1495 if (!ximagesink->ximage)
1496 /* The create method should have posted an informative error */
1499 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1500 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1501 ("Failed to create output image buffer of %dx%d pixels",
1502 ximagesink->ximage->width, ximagesink->ximage->height),
1503 ("XServer allocated buffer size did not match input buffer"));
1505 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1506 ximagesink->ximage = NULL;
1510 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1511 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1512 gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage);
1520 /* No image available. That's very bad ! */
1521 GST_DEBUG ("could not create image");
1522 return GST_FLOW_ERROR;
1526 /* Buffer management
1528 * The buffer_alloc function must either return a buffer with given size and
1529 * caps or create a buffer with different caps attached to the buffer. This
1530 * last option is called reverse negotiation, ie, where the sink suggests a
1531 * different format from the upstream peer.
1533 * We try to do reverse negotiation when our geometry changes and we like a
1536 static GstFlowReturn
1537 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1538 GstCaps * caps, GstBuffer ** buf)
1540 GstXImageSink *ximagesink;
1541 GstXImageBuffer *ximage = NULL;
1542 GstStructure *structure = NULL;
1543 GstFlowReturn ret = GST_FLOW_OK;
1544 GstCaps *alloc_caps;
1545 gboolean alloc_unref = FALSE;
1548 ximagesink = GST_XIMAGESINK (bsink);
1550 GST_LOG_OBJECT (ximagesink,
1551 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1552 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1554 /* assume we're going to alloc what was requested, keep track of
1555 * wheter we need to unref or not. When we suggest a new format
1556 * upstream we will create a new caps that we need to unref. */
1558 alloc_unref = FALSE;
1560 /* get struct to see what is requested */
1561 structure = gst_caps_get_structure (caps, 0);
1563 if (gst_structure_get_int (structure, "width", &width) &&
1564 gst_structure_get_int (structure, "height", &height)) {
1565 GstVideoRectangle dst, src, result;
1570 /* We take the flow_lock because the window might go away */
1571 g_mutex_lock (ximagesink->flow_lock);
1572 if (!ximagesink->xwindow) {
1573 g_mutex_unlock (ximagesink->flow_lock);
1577 /* What is our geometry */
1578 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1579 dst.w = ximagesink->xwindow->width;
1580 dst.h = ximagesink->xwindow->height;
1582 g_mutex_unlock (ximagesink->flow_lock);
1584 if (ximagesink->keep_aspect) {
1585 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1587 gst_video_sink_center_rect (src, dst, &result, TRUE);
1589 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1590 "ignoring aspect ratio");
1591 result.x = result.y = 0;
1596 /* We would like another geometry */
1597 if (width != result.w || height != result.h) {
1599 GstCaps *desired_caps;
1600 GstStructure *desired_struct;
1602 /* make a copy of the incomming caps to create the new
1603 * suggestion. We can't use make_writable because we might
1604 * then destroy the original caps which we still need when the
1605 * peer does not accept the suggestion. */
1606 desired_caps = gst_caps_copy (caps);
1607 desired_struct = gst_caps_get_structure (desired_caps, 0);
1609 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1610 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1611 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1613 /* PAR property overrides the X calculated one */
1614 if (ximagesink->par) {
1615 nom = gst_value_get_fraction_numerator (ximagesink->par);
1616 den = gst_value_get_fraction_denominator (ximagesink->par);
1617 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1618 GST_TYPE_FRACTION, nom, den, NULL);
1619 } else if (ximagesink->xcontext->par) {
1620 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1621 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1622 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1623 GST_TYPE_FRACTION, nom, den, NULL);
1626 /* see if peer accepts our new suggestion, if there is no peer, this
1627 * function returns true. */
1628 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1632 bpp = size / height / width;
1633 /* we will not alloc a buffer of the new suggested caps. Make sure
1634 * we also unref this new caps after we set it on the buffer. */
1635 alloc_caps = desired_caps;
1639 size = bpp * width * height;
1640 GST_DEBUG ("peed pad accepts our desired caps %" GST_PTR_FORMAT
1641 " buffer size is now %d bytes", desired_caps, size);
1643 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1645 /* we alloc a buffer with the original incomming caps */
1646 width = GST_VIDEO_SINK_WIDTH (ximagesink);
1647 height = GST_VIDEO_SINK_HEIGHT (ximagesink);
1653 /* Inspect our buffer pool */
1654 g_mutex_lock (ximagesink->pool_lock);
1655 while (ximagesink->buffer_pool) {
1656 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1659 /* Removing from the pool */
1660 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1661 ximagesink->buffer_pool);
1663 /* If the ximage is invalid for our need, destroy */
1664 if ((ximage->width != width) || (ximage->height != height)) {
1665 gst_ximage_buffer_free (ximage);
1668 /* We found a suitable ximage */
1673 g_mutex_unlock (ximagesink->pool_lock);
1675 /* We haven't found anything, creating a new one */
1677 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1679 /* Now we should have a ximage, set appropriate caps on it */
1681 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1684 /* could be our new reffed suggestion or the original unreffed caps */
1686 gst_caps_unref (alloc_caps);
1688 *buf = GST_BUFFER_CAST (ximage);
1693 /* Interfaces stuff */
1696 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1698 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1703 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1705 klass->supported = gst_ximagesink_interface_supported;
1709 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1710 GstStructure * structure)
1712 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1714 gint x_offset, y_offset;
1718 event = gst_event_new_navigation (structure);
1720 /* We are not converting the pointer coordinates as there's no hardware
1721 scaling done here. The only possible scaling is done by videoscale and
1722 videoscale will have to catch those events and tranform the coordinates
1723 to match the applied scaling. So here we just add the offset if the image
1724 is centered in the window. */
1726 /* We take the flow_lock while we look at the window */
1727 g_mutex_lock (ximagesink->flow_lock);
1729 if (!ximagesink->xwindow) {
1730 g_mutex_unlock (ximagesink->flow_lock);
1734 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1735 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1737 g_mutex_unlock (ximagesink->flow_lock);
1739 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1741 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1743 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1745 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1748 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1750 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1751 gst_pad_send_event (pad, event);
1753 gst_object_unref (pad);
1758 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1760 iface->send_event = gst_ximagesink_navigation_send_event;
1764 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1766 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1767 GstXWindow *xwindow = NULL;
1768 XWindowAttributes attr;
1770 /* If we already use that window return */
1771 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win))
1774 /* If the element has not initialized the X11 context try to do so */
1775 if (!ximagesink->xcontext)
1776 ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink);
1778 if (!ximagesink->xcontext) {
1779 GST_WARNING_OBJECT (ximagesink,
1780 "ximagesink was unable to obtain the X11 context.");
1784 /* We acquire the stream lock while setting this window in the element.
1785 We are basically cleaning tons of stuff replacing the old window, putting
1786 images while we do that would surely crash */
1787 g_mutex_lock (ximagesink->flow_lock);
1789 /* If a window is there already we destroy it */
1790 if (ximagesink->xwindow) {
1791 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1792 ximagesink->xwindow = NULL;
1795 /* If the xid is 0 we go back to an internal window */
1796 if (xwindow_id == 0) {
1797 /* If no width/height caps nego did not happen window will be created
1798 during caps nego then */
1799 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1800 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1801 GST_VIDEO_SINK_WIDTH (ximagesink),
1802 GST_VIDEO_SINK_HEIGHT (ximagesink));
1805 xwindow = g_new0 (GstXWindow, 1);
1807 xwindow->win = xwindow_id;
1809 /* We get window geometry, set the event we want to receive,
1811 g_mutex_lock (ximagesink->x_lock);
1812 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1813 xwindow->width = attr.width;
1814 xwindow->height = attr.height;
1815 xwindow->internal = FALSE;
1816 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1817 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1820 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1821 g_mutex_unlock (ximagesink->x_lock);
1825 ximagesink->xwindow = xwindow;
1827 g_mutex_unlock (ximagesink->flow_lock);
1831 gst_ximagesink_expose (GstXOverlay * overlay)
1833 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1835 if (!ximagesink->xwindow)
1838 gst_ximagesink_ximage_put (ximagesink, NULL);
1842 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1844 iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1845 iface->expose = gst_ximagesink_expose;
1848 /* =========================================== */
1850 /* Init & Class init */
1852 /* =========================================== */
1855 gst_ximagesink_set_property (GObject * object, guint prop_id,
1856 const GValue * value, GParamSpec * pspec)
1858 GstXImageSink *ximagesink;
1860 g_return_if_fail (GST_IS_XIMAGESINK (object));
1862 ximagesink = GST_XIMAGESINK (object);
1866 ximagesink->display_name = g_strdup (g_value_get_string (value));
1868 case PROP_SYNCHRONOUS:
1869 ximagesink->synchronous = g_value_get_boolean (value);
1870 if (ximagesink->xcontext) {
1871 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1872 ximagesink->synchronous ? "TRUE" : "FALSE");
1873 g_mutex_lock (ximagesink->x_lock);
1874 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1875 g_mutex_unlock (ximagesink->x_lock);
1878 case PROP_FORCE_ASPECT_RATIO:
1879 ximagesink->keep_aspect = g_value_get_boolean (value);
1881 case PROP_PIXEL_ASPECT_RATIO:
1885 tmp = g_new0 (GValue, 1);
1886 g_value_init (tmp, GST_TYPE_FRACTION);
1888 if (!g_value_transform (value, tmp)) {
1889 GST_WARNING_OBJECT (ximagesink,
1890 "Could not transform string to aspect ratio");
1893 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1894 gst_value_get_fraction_numerator (tmp),
1895 gst_value_get_fraction_denominator (tmp));
1896 g_free (ximagesink->par);
1897 ximagesink->par = tmp;
1902 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1908 gst_ximagesink_get_property (GObject * object, guint prop_id,
1909 GValue * value, GParamSpec * pspec)
1911 GstXImageSink *ximagesink;
1913 g_return_if_fail (GST_IS_XIMAGESINK (object));
1915 ximagesink = GST_XIMAGESINK (object);
1919 g_value_set_string (value, ximagesink->display_name);
1921 case PROP_SYNCHRONOUS:
1922 g_value_set_boolean (value, ximagesink->synchronous);
1924 case PROP_FORCE_ASPECT_RATIO:
1925 g_value_set_boolean (value, ximagesink->keep_aspect);
1927 case PROP_PIXEL_ASPECT_RATIO:
1928 if (ximagesink->par)
1929 g_value_transform (ximagesink->par, value);
1932 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1938 gst_ximagesink_finalize (GObject * object)
1940 GstXImageSink *ximagesink;
1942 ximagesink = GST_XIMAGESINK (object);
1944 if (ximagesink->display_name) {
1945 g_free (ximagesink->display_name);
1946 ximagesink->display_name = NULL;
1948 if (ximagesink->par) {
1949 g_free (ximagesink->par);
1950 ximagesink->par = NULL;
1952 if (ximagesink->x_lock) {
1953 g_mutex_free (ximagesink->x_lock);
1954 ximagesink->x_lock = NULL;
1956 if (ximagesink->flow_lock) {
1957 g_mutex_free (ximagesink->flow_lock);
1958 ximagesink->flow_lock = NULL;
1960 if (ximagesink->pool_lock) {
1961 g_mutex_free (ximagesink->pool_lock);
1962 ximagesink->pool_lock = NULL;
1965 G_OBJECT_CLASS (parent_class)->finalize (object);
1969 gst_ximagesink_init (GstXImageSink * ximagesink)
1971 ximagesink->display_name = NULL;
1972 ximagesink->xcontext = NULL;
1973 ximagesink->xwindow = NULL;
1974 ximagesink->ximage = NULL;
1975 ximagesink->cur_image = NULL;
1977 ximagesink->event_thread = NULL;
1978 ximagesink->running = FALSE;
1980 ximagesink->fps_n = 0;
1981 ximagesink->fps_d = 1;
1983 ximagesink->x_lock = g_mutex_new ();
1984 ximagesink->flow_lock = g_mutex_new ();
1986 ximagesink->par = NULL;
1988 ximagesink->pool_lock = g_mutex_new ();
1989 ximagesink->buffer_pool = NULL;
1991 ximagesink->synchronous = FALSE;
1992 ximagesink->keep_aspect = FALSE;
1996 gst_ximagesink_base_init (gpointer g_class)
1998 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2000 gst_element_class_set_details (element_class, &gst_ximagesink_details);
2002 gst_element_class_add_pad_template (element_class,
2003 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2007 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2009 GObjectClass *gobject_class;
2010 GstElementClass *gstelement_class;
2011 GstBaseSinkClass *gstbasesink_class;
2013 gobject_class = (GObjectClass *) klass;
2014 gstelement_class = (GstElementClass *) klass;
2015 gstbasesink_class = (GstBaseSinkClass *) klass;
2017 parent_class = g_type_class_peek_parent (klass);
2019 gobject_class->finalize = gst_ximagesink_finalize;
2020 gobject_class->set_property = gst_ximagesink_set_property;
2021 gobject_class->get_property = gst_ximagesink_get_property;
2023 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2024 g_param_spec_string ("display", "Display", "X Display name",
2025 NULL, G_PARAM_READWRITE));
2026 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2027 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2028 "the X display in synchronous mode. (used only for debugging)", FALSE,
2029 G_PARAM_READWRITE));
2030 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2031 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2032 "When enabled, reverse caps negotiation (scaling) will respect "
2033 "original aspect ratio", FALSE, G_PARAM_READWRITE));
2034 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2035 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2036 "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2038 gstelement_class->change_state = gst_ximagesink_change_state;
2040 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2041 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2042 gstbasesink_class->buffer_alloc =
2043 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2044 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2045 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2046 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2049 /* ============================================================= */
2051 /* Public Methods */
2053 /* ============================================================= */
2055 /* =========================================== */
2057 /* Object typing & Creation */
2059 /* =========================================== */
2062 gst_ximagesink_get_type (void)
2064 static GType ximagesink_type = 0;
2066 if (!ximagesink_type) {
2067 static const GTypeInfo ximagesink_info = {
2068 sizeof (GstXImageSinkClass),
2069 gst_ximagesink_base_init,
2071 (GClassInitFunc) gst_ximagesink_class_init,
2074 sizeof (GstXImageSink),
2076 (GInstanceInitFunc) gst_ximagesink_init,
2078 static const GInterfaceInfo iface_info = {
2079 (GInterfaceInitFunc) gst_ximagesink_interface_init,
2083 static const GInterfaceInfo navigation_info = {
2084 (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2088 static const GInterfaceInfo overlay_info = {
2089 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2094 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2095 "GstXImageSink", &ximagesink_info, 0);
2097 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2099 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2101 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2104 /* register type in a more safe place instead of at runtime since the
2105 * type registration is not threadsafe. */
2106 gst_ximage_buffer_get_type ();
2109 return ximagesink_type;