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 %" G_GSIZE_FORMAT " bytes",
353 SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
354 if (SHMInfo.shmaddr == ((void *) -1)) {
355 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
356 /* Clean up shm seg */
357 shmctl (SHMInfo.shmid, IPC_RMID, 0);
361 /* Delete the shared memory segment as soon as we manage to attach.
362 * This way, it will be deleted as soon as we detach later, and not
363 * leaked if we crash. */
364 shmctl (SHMInfo.shmid, IPC_RMID, 0);
366 ximage->data = SHMInfo.shmaddr;
367 SHMInfo.readOnly = FALSE;
369 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
370 GST_WARNING ("Failed to XShmAttach");
374 /* Sync to ensure we see any errors we caused */
375 XSync (xcontext->disp, FALSE);
379 /* store whether we succeeded in result */
384 /* Sync to ensure we swallow any errors we caused and reset error_caught */
385 XSync (xcontext->disp, FALSE);
386 error_caught = FALSE;
387 XSetErrorHandler (handler);
390 XShmDetach (xcontext->disp, &SHMInfo);
391 XSync (xcontext->disp, FALSE);
393 if (SHMInfo.shmaddr != ((void *) -1))
394 shmdt (SHMInfo.shmaddr);
396 XDestroyImage (ximage);
399 #endif /* HAVE_XSHM */
401 /* This function handles GstXImageBuffer creation depending on XShm availability */
402 static GstXImageBuffer *
403 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
405 GstXImageBuffer *ximage = NULL;
406 GstStructure *structure = NULL;
407 gboolean succeeded = FALSE;
409 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
411 ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
413 structure = gst_caps_get_structure (caps, 0);
415 if (!gst_structure_get_int (structure, "width", &ximage->width) ||
416 !gst_structure_get_int (structure, "height", &ximage->height)) {
417 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
420 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
421 ximage->width, ximage->height);
423 g_mutex_lock (ximagesink->x_lock);
426 if (ximagesink->xcontext->use_xshm) {
427 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
428 ximagesink->xcontext->visual,
429 ximagesink->xcontext->depth,
430 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
431 if (!ximage->ximage) {
432 g_mutex_unlock (ximagesink->x_lock);
433 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
434 ("Failed to create output image buffer of %dx%d pixels",
435 ximage->width, ximage->height),
436 ("could not XShmCreateImage a %dx%d image",
437 ximage->width, ximage->height));
441 /* we have to use the returned bytes_per_line for our shm size */
442 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
443 GST_LOG_OBJECT (ximagesink,
444 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
445 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
447 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
449 if (ximage->SHMInfo.shmid == -1) {
450 g_mutex_unlock (ximagesink->x_lock);
451 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
452 ("Failed to create output image buffer of %dx%d pixels",
453 ximage->width, ximage->height),
454 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
459 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
460 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
461 g_mutex_unlock (ximagesink->x_lock);
462 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
463 ("Failed to create output image buffer of %dx%d pixels",
464 ximage->width, ximage->height),
465 ("Failed to shmat: %s", g_strerror (errno)));
466 /* Clean up the shared memory segment */
467 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
471 /* Now that we've attached, we can delete the shared memory segment.
472 * This way, it will be deleted as soon as we detach later, and not
473 * leaked if we crash. */
474 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
476 ximage->ximage->data = ximage->SHMInfo.shmaddr;
477 ximage->SHMInfo.readOnly = FALSE;
479 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
480 g_mutex_unlock (ximagesink->x_lock);
481 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
482 ("Failed to create output image buffer of %dx%d pixels",
483 ximage->width, ximage->height), ("Failed to XShmAttach"));
487 XSync (ximagesink->xcontext->disp, FALSE);
489 #endif /* HAVE_XSHM */
491 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
492 ximagesink->xcontext->visual,
493 ximagesink->xcontext->depth,
495 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
496 if (!ximage->ximage) {
497 g_mutex_unlock (ximagesink->x_lock);
498 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
499 ("Failed to create output image buffer of %dx%d pixels",
500 ximage->width, ximage->height),
501 ("could not XCreateImage a %dx%d image",
502 ximage->width, ximage->height));
506 /* we have to use the returned bytes_per_line for our image size */
507 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
508 ximage->ximage->data = g_malloc (ximage->size);
510 XSync (ximagesink->xcontext->disp, FALSE);
514 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
515 GST_BUFFER_SIZE (ximage) = ximage->size;
517 /* Keep a ref to our sink */
518 ximage->ximagesink = gst_object_ref (ximagesink);
520 g_mutex_unlock (ximagesink->x_lock);
523 gst_ximage_buffer_free (ximage);
530 /* This function destroys a GstXImageBuffer handling XShm availability */
532 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
533 GstXImageBuffer * ximage)
535 g_return_if_fail (ximage != NULL);
536 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
538 /* If the destroyed image is the current one we destroy our reference too */
539 if (ximagesink->cur_image == ximage) {
540 ximagesink->cur_image = NULL;
543 /* Hold the object lock to ensure the XContext doesn't disappear */
544 GST_OBJECT_LOCK (ximagesink);
546 /* We might have some buffers destroyed after changing state to NULL */
547 if (!ximagesink->xcontext) {
548 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
550 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
551 shmdt (ximage->SHMInfo.shmaddr);
557 g_mutex_lock (ximagesink->x_lock);
560 if (ximagesink->xcontext->use_xshm) {
561 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
562 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
563 XSync (ximagesink->xcontext->disp, 0);
564 shmdt (ximage->SHMInfo.shmaddr);
567 XDestroyImage (ximage->ximage);
570 #endif /* HAVE_XSHM */
572 if (ximage->ximage) {
573 XDestroyImage (ximage->ximage);
577 XSync (ximagesink->xcontext->disp, FALSE);
579 g_mutex_unlock (ximagesink->x_lock);
582 GST_OBJECT_UNLOCK (ximagesink);
584 if (ximage->ximagesink) {
585 /* Release the ref to our sink */
586 ximage->ximagesink = NULL;
587 gst_object_unref (ximagesink);
593 /* We are called with the x_lock taken */
595 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
596 GstXWindow * xwindow, GstVideoRectangle rect)
598 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
599 g_return_if_fail (xwindow != NULL);
601 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
602 ximagesink->xcontext->black);
606 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
607 0, 0, rect.x, xwindow->height);
611 if ((rect.x + rect.w) < xwindow->width) {
612 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
613 rect.x + rect.w, 0, xwindow->width, xwindow->height);
618 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
619 0, 0, xwindow->width, rect.y);
623 if ((rect.y + rect.h) < xwindow->height) {
624 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
625 0, rect.y + rect.h, xwindow->width, xwindow->height);
629 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
631 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
633 GstVideoRectangle src, dst, result;
634 gboolean draw_border = FALSE;
636 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
638 /* We take the flow_lock. If expose is in there we don't want to run
639 concurrently from the data flow thread */
640 g_mutex_lock (ximagesink->flow_lock);
642 /* Draw borders when displaying the first frame. After this
643 draw borders only on expose event. */
644 if (!ximagesink->cur_image) {
648 /* Store a reference to the last image we put, lose the previous one */
649 if (ximage && ximagesink->cur_image != ximage) {
650 if (ximagesink->cur_image) {
651 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
652 gst_buffer_unref (ximagesink->cur_image);
654 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
655 ximagesink->cur_image =
656 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
659 /* Expose sends a NULL image, we take the latest frame */
662 if (ximagesink->cur_image) {
663 ximage = ximagesink->cur_image;
665 g_mutex_unlock (ximagesink->flow_lock);
670 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
672 src.w = ximage->width;
673 src.h = ximage->height;
674 dst.w = ximagesink->xwindow->width;
675 dst.h = ximagesink->xwindow->height;
677 gst_video_sink_center_rect (src, dst, &result, FALSE);
679 g_mutex_lock (ximagesink->x_lock);
682 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
686 if (ximagesink->xcontext->use_xshm) {
687 GST_LOG_OBJECT (ximagesink,
688 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
689 ximage, 0, 0, result.x, result.y, result.w, result.h,
690 ximagesink->xwindow->width, ximagesink->xwindow->height);
691 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
692 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
693 result.w, result.h, FALSE);
695 #endif /* HAVE_XSHM */
697 GST_LOG_OBJECT (ximagesink,
698 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
699 ximage, 0, 0, result.x, result.y, result.w, result.h,
700 ximagesink->xwindow->width, ximagesink->xwindow->height);
701 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
702 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
706 XSync (ximagesink->xcontext->disp, FALSE);
708 g_mutex_unlock (ximagesink->x_lock);
710 g_mutex_unlock (ximagesink->flow_lock);
714 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
717 Atom hints_atom = None;
720 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
721 g_return_val_if_fail (window != NULL, FALSE);
723 g_mutex_lock (ximagesink->x_lock);
725 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
726 if (hints_atom == None) {
727 g_mutex_unlock (ximagesink->x_lock);
731 hints = g_malloc0 (sizeof (MotifWmHints));
733 hints->flags |= MWM_HINTS_DECORATIONS;
734 hints->decorations = 1 << 0;
736 XChangeProperty (ximagesink->xcontext->disp, window->win,
737 hints_atom, hints_atom, 32, PropModeReplace,
738 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
740 XSync (ximagesink->xcontext->disp, FALSE);
742 g_mutex_unlock (ximagesink->x_lock);
749 /* This function handles a GstXWindow creation */
751 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
753 GstXWindow *xwindow = NULL;
756 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
758 xwindow = g_new0 (GstXWindow, 1);
760 xwindow->width = width;
761 xwindow->height = height;
762 xwindow->internal = TRUE;
764 g_mutex_lock (ximagesink->x_lock);
766 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
767 ximagesink->xcontext->root,
768 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
770 /* We have to do that to prevent X from redrawing the background on
771 ConfigureNotify. This takes away flickering of video when resizing. */
772 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
774 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
775 StructureNotifyMask | PointerMotionMask | KeyPressMask |
776 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
778 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
781 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
783 XSync (ximagesink->xcontext->disp, FALSE);
785 g_mutex_unlock (ximagesink->x_lock);
787 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
789 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
794 /* This function destroys a GstXWindow */
796 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
797 GstXWindow * xwindow)
799 g_return_if_fail (xwindow != NULL);
800 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
802 g_mutex_lock (ximagesink->x_lock);
804 /* If we did not create that window we just free the GC and let it live */
805 if (xwindow->internal)
806 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
808 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
810 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
812 XSync (ximagesink->xcontext->disp, FALSE);
814 g_mutex_unlock (ximagesink->x_lock);
820 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
821 GstXWindow * xwindow)
823 XWindowAttributes attr;
825 g_return_if_fail (xwindow != NULL);
826 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
828 /* Update the window geometry */
829 g_mutex_lock (ximagesink->x_lock);
831 XGetWindowAttributes (ximagesink->xcontext->disp,
832 ximagesink->xwindow->win, &attr);
834 ximagesink->xwindow->width = attr.width;
835 ximagesink->xwindow->height = attr.height;
837 g_mutex_unlock (ximagesink->x_lock);
841 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
843 g_return_if_fail (xwindow != NULL);
844 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
846 g_mutex_lock (ximagesink->x_lock);
848 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
849 ximagesink->xcontext->black);
851 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
852 0, 0, xwindow->width, xwindow->height);
854 XSync (ximagesink->xcontext->disp, FALSE);
856 g_mutex_unlock (ximagesink->x_lock);
859 /* This function handles XEvents that might be in the queue. It generates
860 GstEvent that will be sent upstream in the pipeline to handle interactivity
863 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
867 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
870 guint pointer_x = 0, pointer_y = 0;
871 gboolean pointer_moved = FALSE;
873 /* Then we get all pointer motion events, only the last position is
875 g_mutex_lock (ximagesink->x_lock);
876 while (XCheckWindowEvent (ximagesink->xcontext->disp,
877 ximagesink->xwindow->win, PointerMotionMask, &e)) {
878 g_mutex_unlock (ximagesink->x_lock);
882 pointer_x = e.xmotion.x;
883 pointer_y = e.xmotion.y;
884 pointer_moved = TRUE;
890 g_mutex_lock (ximagesink->x_lock);
892 g_mutex_unlock (ximagesink->x_lock);
895 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
896 pointer_x, pointer_y);
897 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
898 "mouse-move", 0, pointer_x, pointer_y);
902 /* We get all remaining events on our window to throw them upstream */
903 g_mutex_lock (ximagesink->x_lock);
904 while (XCheckWindowEvent (ximagesink->xcontext->disp,
905 ximagesink->xwindow->win,
906 KeyPressMask | KeyReleaseMask |
907 ButtonPressMask | ButtonReleaseMask, &e)) {
910 /* We lock only for the X function call */
911 g_mutex_unlock (ximagesink->x_lock);
915 /* Mouse button pressed/released over our window. We send upstream
916 events for interactivity/navigation */
917 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
918 e.xbutton.button, e.xbutton.x, e.xbutton.x);
919 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
920 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
923 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
924 e.xbutton.button, e.xbutton.x, e.xbutton.x);
925 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
926 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
930 /* Key pressed/released over our window. We send upstream
931 events for interactivity/navigation */
932 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
933 e.xkey.keycode, e.xkey.x, e.xkey.x);
934 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
936 if (keysym != NoSymbol) {
937 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
939 "key-press" : "key-release", XKeysymToString (keysym));
941 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
942 e.type == KeyPress ? "key-press" : "key-release", "unknown");
946 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
949 g_mutex_lock (ximagesink->x_lock);
951 g_mutex_unlock (ximagesink->x_lock);
954 gboolean exposed = FALSE;
956 g_mutex_lock (ximagesink->x_lock);
957 while (XCheckWindowEvent (ximagesink->xcontext->disp,
958 ximagesink->xwindow->win, ExposureMask, &e)) {
959 g_mutex_unlock (ximagesink->x_lock);
969 g_mutex_lock (ximagesink->x_lock);
971 g_mutex_unlock (ximagesink->x_lock);
974 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
980 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
982 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
984 while (ximagesink->running) {
985 if (ximagesink->xwindow) {
986 gst_ximagesink_handle_xevents (ximagesink);
994 /* This function calculates the pixel aspect ratio based on the properties
995 * in the xcontext structure and stores it there. */
997 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1000 {1, 1}, /* regular screen */
1001 {16, 15}, /* PAL TV */
1002 {11, 10}, /* 525 line Rec.601 video */
1003 {54, 59}, /* 625 line Rec.601 video */
1004 {64, 45}, /* 1280x1024 on 16:9 display */
1005 {5, 3}, /* 1280x1024 on 4:3 display */
1006 {4, 3} /* 800x600 on 16:9 display */
1013 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1015 /* first calculate the "real" ratio based on the X values;
1016 * which is the "physical" w/h divided by the w/h in pixels of the display */
1017 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1018 / (xcontext->heightmm * xcontext->width);
1020 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1022 if (xcontext->width == 720 && xcontext->height == 576) {
1023 ratio = 4.0 * 576 / (3.0 * 720);
1025 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1027 /* now find the one from par[][2] with the lowest delta to the real one */
1031 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1032 gdouble this_delta = DELTA (i);
1034 if (this_delta < delta) {
1040 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1041 par[index][0], par[index][1]);
1043 g_free (xcontext->par);
1044 xcontext->par = g_new0 (GValue, 1);
1045 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1046 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1047 GST_DEBUG ("set xcontext PAR to %d/%d",
1048 gst_value_get_fraction_numerator (xcontext->par),
1049 gst_value_get_fraction_denominator (xcontext->par));
1052 /* This function gets the X Display and global info about it. Everything is
1053 stored in our object and will be cleaned when the object is disposed. Note
1054 here that caps for supported format are generated without any window or
1056 static GstXContext *
1057 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1059 GstXContext *xcontext = NULL;
1060 XPixmapFormatValues *px_formats = NULL;
1061 gint nb_formats = 0, i;
1063 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1065 xcontext = g_new0 (GstXContext, 1);
1067 g_mutex_lock (ximagesink->x_lock);
1069 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1071 if (!xcontext->disp) {
1072 g_mutex_unlock (ximagesink->x_lock);
1074 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1075 ("Could not initialise X output"), ("Could not open display"));
1079 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1080 xcontext->screen_num = DefaultScreen (xcontext->disp);
1081 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1082 xcontext->root = DefaultRootWindow (xcontext->disp);
1083 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1084 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1085 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1087 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1088 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1089 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1090 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1092 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1093 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1095 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1097 /* We get supported pixmap formats at supported depth */
1098 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1101 XCloseDisplay (xcontext->disp);
1102 g_mutex_unlock (ximagesink->x_lock);
1107 /* We get bpp value corresponding to our running depth */
1108 for (i = 0; i < nb_formats; i++) {
1109 if (px_formats[i].depth == xcontext->depth)
1110 xcontext->bpp = px_formats[i].bits_per_pixel;
1115 xcontext->endianness =
1116 (ImageByteOrder (xcontext->disp) ==
1117 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1119 /* Search for XShm extension support */
1121 if (XShmQueryExtension (xcontext->disp) &&
1122 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1123 xcontext->use_xshm = TRUE;
1124 GST_DEBUG ("ximagesink is using XShm extension");
1128 xcontext->use_xshm = FALSE;
1129 GST_DEBUG ("ximagesink is not using XShm extension");
1132 /* our caps system handles 24/32bpp RGB as big-endian. */
1133 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1134 xcontext->endianness == G_LITTLE_ENDIAN) {
1135 xcontext->endianness = G_BIG_ENDIAN;
1136 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1137 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1138 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1139 if (xcontext->bpp == 24) {
1140 xcontext->visual->red_mask >>= 8;
1141 xcontext->visual->green_mask >>= 8;
1142 xcontext->visual->blue_mask >>= 8;
1146 /* update object's par with calculated one if not set yet */
1147 if (!ximagesink->par) {
1148 ximagesink->par = g_new0 (GValue, 1);
1149 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1150 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1152 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1153 "bpp", G_TYPE_INT, xcontext->bpp,
1154 "depth", G_TYPE_INT, xcontext->depth,
1155 "endianness", G_TYPE_INT, xcontext->endianness,
1156 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1157 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1158 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1159 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1160 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1161 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1162 if (ximagesink->par) {
1165 nom = gst_value_get_fraction_numerator (ximagesink->par);
1166 den = gst_value_get_fraction_denominator (ximagesink->par);
1167 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1168 GST_TYPE_FRACTION, nom, den, NULL);
1171 g_mutex_unlock (ximagesink->x_lock);
1173 /* Setup our event listening thread */
1174 ximagesink->event_thread = g_thread_create (
1175 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1180 /* This function cleans the X context. Closing the Display and unrefing the
1181 caps for supported formats. */
1183 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1185 GstXContext *xcontext;
1187 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1189 GST_OBJECT_LOCK (ximagesink);
1190 if (ximagesink->xcontext == NULL) {
1191 GST_OBJECT_UNLOCK (ximagesink);
1195 /* Take the xcontext reference and NULL it while we
1196 * clean it up, so that any buffer-alloced buffers
1197 * arriving after this will be freed correctly */
1198 xcontext = ximagesink->xcontext;
1199 ximagesink->xcontext = NULL;
1201 GST_OBJECT_UNLOCK (ximagesink);
1203 /* Wait for our event thread */
1204 if (ximagesink->event_thread) {
1205 g_thread_join (ximagesink->event_thread);
1206 ximagesink->event_thread = NULL;
1209 gst_caps_unref (xcontext->caps);
1210 g_free (xcontext->par);
1211 g_free (ximagesink->par);
1212 ximagesink->par = NULL;
1214 g_mutex_lock (ximagesink->x_lock);
1216 XCloseDisplay (xcontext->disp);
1218 g_mutex_unlock (ximagesink->x_lock);
1220 g_free (ximagesink->xcontext);
1224 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1227 g_mutex_lock (ximagesink->pool_lock);
1229 while (ximagesink->buffer_pool) {
1230 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1232 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1233 ximagesink->buffer_pool);
1234 gst_ximage_buffer_free (ximage);
1237 g_mutex_unlock (ximagesink->pool_lock);
1243 gst_ximagesink_getcaps (GstBaseSink * bsink)
1245 GstXImageSink *ximagesink;
1249 ximagesink = GST_XIMAGESINK (bsink);
1251 if (ximagesink->xcontext)
1252 return gst_caps_ref (ximagesink->xcontext->caps);
1254 /* get a template copy and add the pixel aspect ratio */
1256 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->
1258 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1259 GstStructure *structure = gst_caps_get_structure (caps, i);
1261 if (ximagesink->par) {
1264 nom = gst_value_get_fraction_numerator (ximagesink->par);
1265 den = gst_value_get_fraction_denominator (ximagesink->par);
1266 gst_structure_set (structure, "pixel-aspect-ratio",
1267 GST_TYPE_FRACTION, nom, den, NULL);
1275 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1277 GstXImageSink *ximagesink;
1278 gboolean ret = TRUE;
1279 GstStructure *structure;
1280 GstCaps *intersection;
1282 gint new_width, new_height;
1285 ximagesink = GST_XIMAGESINK (bsink);
1287 if (!ximagesink->xcontext)
1290 GST_DEBUG_OBJECT (ximagesink,
1291 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1292 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1294 /* We intersect those caps with our template to make sure they are correct */
1295 intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1296 GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1298 if (gst_caps_is_empty (intersection)) {
1299 gst_caps_unref (intersection);
1303 gst_caps_unref (intersection);
1305 structure = gst_caps_get_structure (caps, 0);
1307 ret &= gst_structure_get_int (structure, "width", &new_width);
1308 ret &= gst_structure_get_int (structure, "height", &new_height);
1309 fps = gst_structure_get_value (structure, "framerate");
1310 ret &= (fps != NULL);
1314 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1315 * otherwise linking should fail */
1316 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1318 if (ximagesink->par) {
1319 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1322 } else if (ximagesink->xcontext->par) {
1323 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1329 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1330 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1331 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1332 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1334 /* Notify application to set xwindow id now */
1335 if (!ximagesink->xwindow) {
1336 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1339 /* Creating our window and our image */
1340 g_assert (GST_VIDEO_SINK_WIDTH (ximagesink) > 0);
1341 g_assert (GST_VIDEO_SINK_HEIGHT (ximagesink) > 0);
1342 if (!ximagesink->xwindow) {
1343 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1344 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1347 /* If our ximage has changed we destroy it, next chain iteration will create
1349 if ((ximagesink->ximage) &&
1350 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1351 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1352 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1353 ximagesink->ximage);
1354 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1355 ximagesink->ximage = NULL;
1363 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1368 static GstStateChangeReturn
1369 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1371 GstXImageSink *ximagesink;
1372 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1373 GstXContext *xcontext = NULL;
1375 ximagesink = GST_XIMAGESINK (element);
1377 switch (transition) {
1378 case GST_STATE_CHANGE_NULL_TO_READY:
1380 /* Initializing the XContext */
1381 if (!ximagesink->xcontext)
1382 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1384 GST_OBJECT_LOCK (ximagesink);
1385 ximagesink->running = TRUE;
1387 ximagesink->xcontext = xcontext;
1388 GST_OBJECT_UNLOCK (ximagesink);
1390 if (!ximagesink->xcontext) {
1391 ret = GST_STATE_CHANGE_FAILURE;
1394 /* call XSynchronize with the current value of synchronous */
1395 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1396 ximagesink->synchronous ? "TRUE" : "FALSE");
1397 g_mutex_lock (ximagesink->x_lock);
1398 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1399 g_mutex_unlock (ximagesink->x_lock);
1401 case GST_STATE_CHANGE_READY_TO_PAUSED:
1403 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1409 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1410 if (ret == GST_STATE_CHANGE_FAILURE)
1413 switch (transition) {
1414 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1416 case GST_STATE_CHANGE_PAUSED_TO_READY:
1417 if (ximagesink->xwindow)
1418 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1419 ximagesink->fps_n = 0;
1420 ximagesink->fps_d = 1;
1421 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1422 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1424 case GST_STATE_CHANGE_READY_TO_NULL:
1425 GST_OBJECT_LOCK (ximagesink);
1426 ximagesink->running = FALSE;
1427 GST_OBJECT_UNLOCK (ximagesink);
1429 if (ximagesink->ximage) {
1430 gst_buffer_unref (ximagesink->ximage);
1431 ximagesink->ximage = NULL;
1433 if (ximagesink->cur_image) {
1434 gst_buffer_unref (ximagesink->cur_image);
1435 ximagesink->cur_image = NULL;
1438 gst_ximagesink_bufferpool_clear (ximagesink);
1440 if (ximagesink->xwindow) {
1441 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1442 ximagesink->xwindow = NULL;
1445 gst_ximagesink_xcontext_clear (ximagesink);
1456 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1457 GstClockTime * start, GstClockTime * end)
1459 GstXImageSink *ximagesink;
1461 ximagesink = GST_XIMAGESINK (bsink);
1463 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1464 *start = GST_BUFFER_TIMESTAMP (buf);
1465 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1466 *end = *start + GST_BUFFER_DURATION (buf);
1468 if (ximagesink->fps_n > 0) {
1470 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1477 static GstFlowReturn
1478 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1480 GstXImageSink *ximagesink;
1482 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1484 ximagesink = GST_XIMAGESINK (bsink);
1486 /* If this buffer has been allocated using our buffer management we simply
1487 put the ximage which is in the PRIVATE pointer */
1488 if (GST_IS_XIMAGE_BUFFER (buf)) {
1489 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1490 gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf));
1492 /* Else we have to copy the data into our private image, */
1493 /* if we have one... */
1494 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1495 if (!ximagesink->ximage) {
1496 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1497 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1498 GST_BUFFER_CAPS (buf));
1499 if (!ximagesink->ximage)
1500 /* The create method should have posted an informative error */
1503 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1504 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1505 ("Failed to create output image buffer of %dx%d pixels",
1506 ximagesink->ximage->width, ximagesink->ximage->height),
1507 ("XServer allocated buffer size did not match input buffer"));
1509 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1510 ximagesink->ximage = NULL;
1514 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1515 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1516 gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage);
1524 /* No image available. That's very bad ! */
1525 GST_DEBUG ("could not create image");
1526 return GST_FLOW_ERROR;
1530 /* Buffer management
1532 * The buffer_alloc function must either return a buffer with given size and
1533 * caps or create a buffer with different caps attached to the buffer. This
1534 * last option is called reverse negotiation, ie, where the sink suggests a
1535 * different format from the upstream peer.
1537 * We try to do reverse negotiation when our geometry changes and we like a
1540 static GstFlowReturn
1541 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1542 GstCaps * caps, GstBuffer ** buf)
1544 GstXImageSink *ximagesink;
1545 GstXImageBuffer *ximage = NULL;
1546 GstStructure *structure = NULL;
1547 GstFlowReturn ret = GST_FLOW_OK;
1548 GstCaps *alloc_caps;
1549 gboolean alloc_unref = FALSE;
1552 ximagesink = GST_XIMAGESINK (bsink);
1554 GST_LOG_OBJECT (ximagesink,
1555 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1556 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1558 /* assume we're going to alloc what was requested, keep track of
1559 * wheter we need to unref or not. When we suggest a new format
1560 * upstream we will create a new caps that we need to unref. */
1562 alloc_unref = FALSE;
1564 /* get struct to see what is requested */
1565 structure = gst_caps_get_structure (caps, 0);
1567 if (gst_structure_get_int (structure, "width", &width) &&
1568 gst_structure_get_int (structure, "height", &height)) {
1569 GstVideoRectangle dst, src, result;
1574 /* We take the flow_lock because the window might go away */
1575 g_mutex_lock (ximagesink->flow_lock);
1576 if (!ximagesink->xwindow) {
1577 g_mutex_unlock (ximagesink->flow_lock);
1581 /* What is our geometry */
1582 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1583 dst.w = ximagesink->xwindow->width;
1584 dst.h = ximagesink->xwindow->height;
1586 g_mutex_unlock (ximagesink->flow_lock);
1588 if (ximagesink->keep_aspect) {
1589 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1591 gst_video_sink_center_rect (src, dst, &result, TRUE);
1593 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1594 "ignoring aspect ratio");
1595 result.x = result.y = 0;
1600 /* We would like another geometry */
1601 if (width != result.w || height != result.h) {
1603 GstCaps *desired_caps;
1604 GstStructure *desired_struct;
1606 /* make a copy of the incomming caps to create the new
1607 * suggestion. We can't use make_writable because we might
1608 * then destroy the original caps which we still need when the
1609 * peer does not accept the suggestion. */
1610 desired_caps = gst_caps_copy (caps);
1611 desired_struct = gst_caps_get_structure (desired_caps, 0);
1613 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1614 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1615 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1617 /* PAR property overrides the X calculated one */
1618 if (ximagesink->par) {
1619 nom = gst_value_get_fraction_numerator (ximagesink->par);
1620 den = gst_value_get_fraction_denominator (ximagesink->par);
1621 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1622 GST_TYPE_FRACTION, nom, den, NULL);
1623 } else if (ximagesink->xcontext->par) {
1624 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1625 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1626 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1627 GST_TYPE_FRACTION, nom, den, NULL);
1630 /* see if peer accepts our new suggestion, if there is no peer, this
1631 * function returns true. */
1632 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1636 bpp = size / height / width;
1637 /* we will not alloc a buffer of the new suggested caps. Make sure
1638 * we also unref this new caps after we set it on the buffer. */
1639 alloc_caps = desired_caps;
1643 size = bpp * width * height;
1644 GST_DEBUG ("peed pad accepts our desired caps %" GST_PTR_FORMAT
1645 " buffer size is now %d bytes", desired_caps, size);
1647 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1649 /* we alloc a buffer with the original incomming caps */
1650 width = GST_VIDEO_SINK_WIDTH (ximagesink);
1651 height = GST_VIDEO_SINK_HEIGHT (ximagesink);
1657 /* Inspect our buffer pool */
1658 g_mutex_lock (ximagesink->pool_lock);
1659 while (ximagesink->buffer_pool) {
1660 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1663 /* Removing from the pool */
1664 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1665 ximagesink->buffer_pool);
1667 /* If the ximage is invalid for our need, destroy */
1668 if ((ximage->width != width) || (ximage->height != height)) {
1669 gst_ximage_buffer_free (ximage);
1672 /* We found a suitable ximage */
1677 g_mutex_unlock (ximagesink->pool_lock);
1679 /* We haven't found anything, creating a new one */
1681 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1683 /* Now we should have a ximage, set appropriate caps on it */
1685 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1688 /* could be our new reffed suggestion or the original unreffed caps */
1690 gst_caps_unref (alloc_caps);
1692 *buf = GST_BUFFER_CAST (ximage);
1697 /* Interfaces stuff */
1700 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1702 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1707 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1709 klass->supported = gst_ximagesink_interface_supported;
1713 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1714 GstStructure * structure)
1716 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1718 gint x_offset, y_offset;
1722 event = gst_event_new_navigation (structure);
1724 /* We are not converting the pointer coordinates as there's no hardware
1725 scaling done here. The only possible scaling is done by videoscale and
1726 videoscale will have to catch those events and tranform the coordinates
1727 to match the applied scaling. So here we just add the offset if the image
1728 is centered in the window. */
1730 /* We take the flow_lock while we look at the window */
1731 g_mutex_lock (ximagesink->flow_lock);
1733 if (!ximagesink->xwindow) {
1734 g_mutex_unlock (ximagesink->flow_lock);
1738 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1739 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1741 g_mutex_unlock (ximagesink->flow_lock);
1743 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1745 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1747 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1749 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1752 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1754 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1755 gst_pad_send_event (pad, event);
1757 gst_object_unref (pad);
1762 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1764 iface->send_event = gst_ximagesink_navigation_send_event;
1768 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1770 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1771 GstXWindow *xwindow = NULL;
1772 XWindowAttributes attr;
1774 /* If we already use that window return */
1775 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win))
1778 /* If the element has not initialized the X11 context try to do so */
1779 if (!ximagesink->xcontext)
1780 ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink);
1782 if (!ximagesink->xcontext) {
1783 GST_WARNING_OBJECT (ximagesink,
1784 "ximagesink was unable to obtain the X11 context.");
1788 /* We acquire the stream lock while setting this window in the element.
1789 We are basically cleaning tons of stuff replacing the old window, putting
1790 images while we do that would surely crash */
1791 g_mutex_lock (ximagesink->flow_lock);
1793 /* If a window is there already we destroy it */
1794 if (ximagesink->xwindow) {
1795 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1796 ximagesink->xwindow = NULL;
1799 /* If the xid is 0 we go back to an internal window */
1800 if (xwindow_id == 0) {
1801 /* If no width/height caps nego did not happen window will be created
1802 during caps nego then */
1803 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1804 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1805 GST_VIDEO_SINK_WIDTH (ximagesink),
1806 GST_VIDEO_SINK_HEIGHT (ximagesink));
1809 xwindow = g_new0 (GstXWindow, 1);
1811 xwindow->win = xwindow_id;
1813 /* We get window geometry, set the event we want to receive,
1815 g_mutex_lock (ximagesink->x_lock);
1816 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1817 xwindow->width = attr.width;
1818 xwindow->height = attr.height;
1819 xwindow->internal = FALSE;
1820 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1821 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1824 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1825 g_mutex_unlock (ximagesink->x_lock);
1829 ximagesink->xwindow = xwindow;
1831 g_mutex_unlock (ximagesink->flow_lock);
1835 gst_ximagesink_expose (GstXOverlay * overlay)
1837 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1839 if (!ximagesink->xwindow)
1842 gst_ximagesink_ximage_put (ximagesink, NULL);
1846 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1848 iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1849 iface->expose = gst_ximagesink_expose;
1852 /* =========================================== */
1854 /* Init & Class init */
1856 /* =========================================== */
1859 gst_ximagesink_set_property (GObject * object, guint prop_id,
1860 const GValue * value, GParamSpec * pspec)
1862 GstXImageSink *ximagesink;
1864 g_return_if_fail (GST_IS_XIMAGESINK (object));
1866 ximagesink = GST_XIMAGESINK (object);
1870 ximagesink->display_name = g_strdup (g_value_get_string (value));
1872 case PROP_SYNCHRONOUS:
1873 ximagesink->synchronous = g_value_get_boolean (value);
1874 if (ximagesink->xcontext) {
1875 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1876 ximagesink->synchronous ? "TRUE" : "FALSE");
1877 g_mutex_lock (ximagesink->x_lock);
1878 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1879 g_mutex_unlock (ximagesink->x_lock);
1882 case PROP_FORCE_ASPECT_RATIO:
1883 ximagesink->keep_aspect = g_value_get_boolean (value);
1885 case PROP_PIXEL_ASPECT_RATIO:
1889 tmp = g_new0 (GValue, 1);
1890 g_value_init (tmp, GST_TYPE_FRACTION);
1892 if (!g_value_transform (value, tmp)) {
1893 GST_WARNING_OBJECT (ximagesink,
1894 "Could not transform string to aspect ratio");
1897 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1898 gst_value_get_fraction_numerator (tmp),
1899 gst_value_get_fraction_denominator (tmp));
1900 g_free (ximagesink->par);
1901 ximagesink->par = tmp;
1906 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1912 gst_ximagesink_get_property (GObject * object, guint prop_id,
1913 GValue * value, GParamSpec * pspec)
1915 GstXImageSink *ximagesink;
1917 g_return_if_fail (GST_IS_XIMAGESINK (object));
1919 ximagesink = GST_XIMAGESINK (object);
1923 g_value_set_string (value, ximagesink->display_name);
1925 case PROP_SYNCHRONOUS:
1926 g_value_set_boolean (value, ximagesink->synchronous);
1928 case PROP_FORCE_ASPECT_RATIO:
1929 g_value_set_boolean (value, ximagesink->keep_aspect);
1931 case PROP_PIXEL_ASPECT_RATIO:
1932 if (ximagesink->par)
1933 g_value_transform (ximagesink->par, value);
1936 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1942 gst_ximagesink_finalize (GObject * object)
1944 GstXImageSink *ximagesink;
1946 ximagesink = GST_XIMAGESINK (object);
1948 if (ximagesink->display_name) {
1949 g_free (ximagesink->display_name);
1950 ximagesink->display_name = NULL;
1952 if (ximagesink->par) {
1953 g_free (ximagesink->par);
1954 ximagesink->par = NULL;
1956 if (ximagesink->x_lock) {
1957 g_mutex_free (ximagesink->x_lock);
1958 ximagesink->x_lock = NULL;
1960 if (ximagesink->flow_lock) {
1961 g_mutex_free (ximagesink->flow_lock);
1962 ximagesink->flow_lock = NULL;
1964 if (ximagesink->pool_lock) {
1965 g_mutex_free (ximagesink->pool_lock);
1966 ximagesink->pool_lock = NULL;
1969 G_OBJECT_CLASS (parent_class)->finalize (object);
1973 gst_ximagesink_init (GstXImageSink * ximagesink)
1975 ximagesink->display_name = NULL;
1976 ximagesink->xcontext = NULL;
1977 ximagesink->xwindow = NULL;
1978 ximagesink->ximage = NULL;
1979 ximagesink->cur_image = NULL;
1981 ximagesink->event_thread = NULL;
1982 ximagesink->running = FALSE;
1984 ximagesink->fps_n = 0;
1985 ximagesink->fps_d = 1;
1987 ximagesink->x_lock = g_mutex_new ();
1988 ximagesink->flow_lock = g_mutex_new ();
1990 ximagesink->par = NULL;
1992 ximagesink->pool_lock = g_mutex_new ();
1993 ximagesink->buffer_pool = NULL;
1995 ximagesink->synchronous = FALSE;
1996 ximagesink->keep_aspect = FALSE;
2000 gst_ximagesink_base_init (gpointer g_class)
2002 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2004 gst_element_class_set_details (element_class, &gst_ximagesink_details);
2006 gst_element_class_add_pad_template (element_class,
2007 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2011 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2013 GObjectClass *gobject_class;
2014 GstElementClass *gstelement_class;
2015 GstBaseSinkClass *gstbasesink_class;
2017 gobject_class = (GObjectClass *) klass;
2018 gstelement_class = (GstElementClass *) klass;
2019 gstbasesink_class = (GstBaseSinkClass *) klass;
2021 parent_class = g_type_class_peek_parent (klass);
2023 gobject_class->finalize = gst_ximagesink_finalize;
2024 gobject_class->set_property = gst_ximagesink_set_property;
2025 gobject_class->get_property = gst_ximagesink_get_property;
2027 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2028 g_param_spec_string ("display", "Display", "X Display name",
2029 NULL, G_PARAM_READWRITE));
2030 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2031 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2032 "the X display in synchronous mode. (used only for debugging)", FALSE,
2033 G_PARAM_READWRITE));
2034 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2035 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2036 "When enabled, reverse caps negotiation (scaling) will respect "
2037 "original aspect ratio", FALSE, G_PARAM_READWRITE));
2038 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2039 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2040 "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2042 gstelement_class->change_state = gst_ximagesink_change_state;
2044 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2045 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2046 gstbasesink_class->buffer_alloc =
2047 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2048 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2049 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2050 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2053 /* ============================================================= */
2055 /* Public Methods */
2057 /* ============================================================= */
2059 /* =========================================== */
2061 /* Object typing & Creation */
2063 /* =========================================== */
2066 gst_ximagesink_get_type (void)
2068 static GType ximagesink_type = 0;
2070 if (!ximagesink_type) {
2071 static const GTypeInfo ximagesink_info = {
2072 sizeof (GstXImageSinkClass),
2073 gst_ximagesink_base_init,
2075 (GClassInitFunc) gst_ximagesink_class_init,
2078 sizeof (GstXImageSink),
2080 (GInstanceInitFunc) gst_ximagesink_init,
2082 static const GInterfaceInfo iface_info = {
2083 (GInterfaceInitFunc) gst_ximagesink_interface_init,
2087 static const GInterfaceInfo navigation_info = {
2088 (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2092 static const GInterfaceInfo overlay_info = {
2093 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2098 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2099 "GstXImageSink", &ximagesink_info, 0);
2101 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2103 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2105 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2108 /* register type and create class in a more safe place instead of at
2109 * runtime since the type registration and class creation is not
2111 g_type_class_ref (gst_ximage_buffer_get_type ());
2114 return ximagesink_type;