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)) {
1302 gst_caps_unref (intersection);
1304 structure = gst_caps_get_structure (caps, 0);
1306 ret &= gst_structure_get_int (structure, "width", &new_width);
1307 ret &= gst_structure_get_int (structure, "height", &new_height);
1308 fps = gst_structure_get_value (structure, "framerate");
1309 ret &= (fps != NULL);
1313 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1314 * otherwise linking should fail */
1315 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1317 if (ximagesink->par) {
1318 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1321 } else if (ximagesink->xcontext->par) {
1322 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1328 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1329 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1330 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1331 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1333 /* Notify application to set xwindow id now */
1334 if (!ximagesink->xwindow) {
1335 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1338 /* Creating our window and our image */
1339 g_assert (GST_VIDEO_SINK_WIDTH (ximagesink) > 0);
1340 g_assert (GST_VIDEO_SINK_HEIGHT (ximagesink) > 0);
1341 if (!ximagesink->xwindow) {
1342 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1343 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1346 /* If our ximage has changed we destroy it, next chain iteration will create
1348 if ((ximagesink->ximage) &&
1349 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1350 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1351 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1352 ximagesink->ximage);
1353 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1354 ximagesink->ximage = NULL;
1362 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1367 static GstStateChangeReturn
1368 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1370 GstXImageSink *ximagesink;
1371 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1372 GstXContext *xcontext = NULL;
1374 ximagesink = GST_XIMAGESINK (element);
1376 switch (transition) {
1377 case GST_STATE_CHANGE_NULL_TO_READY:
1379 /* Initializing the XContext */
1380 if (!ximagesink->xcontext)
1381 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1383 GST_OBJECT_LOCK (ximagesink);
1384 ximagesink->running = TRUE;
1386 ximagesink->xcontext = xcontext;
1387 GST_OBJECT_UNLOCK (ximagesink);
1389 if (!ximagesink->xcontext) {
1390 ret = GST_STATE_CHANGE_FAILURE;
1393 /* call XSynchronize with the current value of synchronous */
1394 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1395 ximagesink->synchronous ? "TRUE" : "FALSE");
1396 g_mutex_lock (ximagesink->x_lock);
1397 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1398 g_mutex_unlock (ximagesink->x_lock);
1400 case GST_STATE_CHANGE_READY_TO_PAUSED:
1402 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1408 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1409 if (ret == GST_STATE_CHANGE_FAILURE)
1412 switch (transition) {
1413 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1415 case GST_STATE_CHANGE_PAUSED_TO_READY:
1416 if (ximagesink->xwindow)
1417 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1418 ximagesink->fps_n = 0;
1419 ximagesink->fps_d = 1;
1420 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1421 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1423 case GST_STATE_CHANGE_READY_TO_NULL:
1424 GST_OBJECT_LOCK (ximagesink);
1425 ximagesink->running = FALSE;
1426 GST_OBJECT_UNLOCK (ximagesink);
1428 if (ximagesink->ximage) {
1429 gst_buffer_unref (ximagesink->ximage);
1430 ximagesink->ximage = NULL;
1432 if (ximagesink->cur_image) {
1433 gst_buffer_unref (ximagesink->cur_image);
1434 ximagesink->cur_image = NULL;
1437 gst_ximagesink_bufferpool_clear (ximagesink);
1439 if (ximagesink->xwindow) {
1440 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1441 ximagesink->xwindow = NULL;
1444 gst_ximagesink_xcontext_clear (ximagesink);
1455 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1456 GstClockTime * start, GstClockTime * end)
1458 GstXImageSink *ximagesink;
1460 ximagesink = GST_XIMAGESINK (bsink);
1462 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1463 *start = GST_BUFFER_TIMESTAMP (buf);
1464 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1465 *end = *start + GST_BUFFER_DURATION (buf);
1467 if (ximagesink->fps_n > 0) {
1469 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1476 static GstFlowReturn
1477 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1479 GstXImageSink *ximagesink;
1481 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1483 ximagesink = GST_XIMAGESINK (bsink);
1485 /* If this buffer has been allocated using our buffer management we simply
1486 put the ximage which is in the PRIVATE pointer */
1487 if (GST_IS_XIMAGE_BUFFER (buf)) {
1488 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1489 gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf));
1491 /* Else we have to copy the data into our private image, */
1492 /* if we have one... */
1493 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1494 if (!ximagesink->ximage) {
1495 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1496 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1497 GST_BUFFER_CAPS (buf));
1498 if (!ximagesink->ximage)
1499 /* The create method should have posted an informative error */
1502 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1503 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1504 ("Failed to create output image buffer of %dx%d pixels",
1505 ximagesink->ximage->width, ximagesink->ximage->height),
1506 ("XServer allocated buffer size did not match input buffer"));
1508 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1509 ximagesink->ximage = NULL;
1513 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1514 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1515 gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage);
1523 /* No image available. That's very bad ! */
1524 GST_DEBUG ("could not create image");
1525 return GST_FLOW_ERROR;
1529 /* Buffer management
1531 * The buffer_alloc function must either return a buffer with given size and
1532 * caps or create a buffer with different caps attached to the buffer. This
1533 * last option is called reverse negotiation, ie, where the sink suggests a
1534 * different format from the upstream peer.
1536 * We try to do reverse negotiation when our geometry changes and we like a
1539 static GstFlowReturn
1540 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1541 GstCaps * caps, GstBuffer ** buf)
1543 GstXImageSink *ximagesink;
1544 GstXImageBuffer *ximage = NULL;
1545 GstStructure *structure = NULL;
1546 GstFlowReturn ret = GST_FLOW_OK;
1547 GstCaps *alloc_caps;
1548 gboolean alloc_unref = FALSE;
1551 ximagesink = GST_XIMAGESINK (bsink);
1553 GST_LOG_OBJECT (ximagesink,
1554 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1555 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1557 /* assume we're going to alloc what was requested, keep track of
1558 * wheter we need to unref or not. When we suggest a new format
1559 * upstream we will create a new caps that we need to unref. */
1561 alloc_unref = FALSE;
1563 /* get struct to see what is requested */
1564 structure = gst_caps_get_structure (caps, 0);
1566 if (gst_structure_get_int (structure, "width", &width) &&
1567 gst_structure_get_int (structure, "height", &height)) {
1568 GstVideoRectangle dst, src, result;
1573 /* We take the flow_lock because the window might go away */
1574 g_mutex_lock (ximagesink->flow_lock);
1575 if (!ximagesink->xwindow) {
1576 g_mutex_unlock (ximagesink->flow_lock);
1580 /* What is our geometry */
1581 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1582 dst.w = ximagesink->xwindow->width;
1583 dst.h = ximagesink->xwindow->height;
1585 g_mutex_unlock (ximagesink->flow_lock);
1587 if (ximagesink->keep_aspect) {
1588 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1590 gst_video_sink_center_rect (src, dst, &result, TRUE);
1592 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1593 "ignoring aspect ratio");
1594 result.x = result.y = 0;
1599 /* We would like another geometry */
1600 if (width != result.w || height != result.h) {
1602 GstCaps *desired_caps;
1603 GstStructure *desired_struct;
1605 /* make a copy of the incomming caps to create the new
1606 * suggestion. We can't use make_writable because we might
1607 * then destroy the original caps which we still need when the
1608 * peer does not accept the suggestion. */
1609 desired_caps = gst_caps_copy (caps);
1610 desired_struct = gst_caps_get_structure (desired_caps, 0);
1612 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1613 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1614 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1616 /* PAR property overrides the X calculated one */
1617 if (ximagesink->par) {
1618 nom = gst_value_get_fraction_numerator (ximagesink->par);
1619 den = gst_value_get_fraction_denominator (ximagesink->par);
1620 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1621 GST_TYPE_FRACTION, nom, den, NULL);
1622 } else if (ximagesink->xcontext->par) {
1623 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1624 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1625 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1626 GST_TYPE_FRACTION, nom, den, NULL);
1629 /* see if peer accepts our new suggestion, if there is no peer, this
1630 * function returns true. */
1631 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1635 bpp = size / height / width;
1636 /* we will not alloc a buffer of the new suggested caps. Make sure
1637 * we also unref this new caps after we set it on the buffer. */
1638 alloc_caps = desired_caps;
1642 size = bpp * width * height;
1643 GST_DEBUG ("peed pad accepts our desired caps %" GST_PTR_FORMAT
1644 " buffer size is now %d bytes", desired_caps, size);
1646 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1648 /* we alloc a buffer with the original incomming caps */
1649 width = GST_VIDEO_SINK_WIDTH (ximagesink);
1650 height = GST_VIDEO_SINK_HEIGHT (ximagesink);
1656 /* Inspect our buffer pool */
1657 g_mutex_lock (ximagesink->pool_lock);
1658 while (ximagesink->buffer_pool) {
1659 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1662 /* Removing from the pool */
1663 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1664 ximagesink->buffer_pool);
1666 /* If the ximage is invalid for our need, destroy */
1667 if ((ximage->width != width) || (ximage->height != height)) {
1668 gst_ximage_buffer_free (ximage);
1671 /* We found a suitable ximage */
1676 g_mutex_unlock (ximagesink->pool_lock);
1678 /* We haven't found anything, creating a new one */
1680 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1682 /* Now we should have a ximage, set appropriate caps on it */
1684 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1687 /* could be our new reffed suggestion or the original unreffed caps */
1689 gst_caps_unref (alloc_caps);
1691 *buf = GST_BUFFER_CAST (ximage);
1696 /* Interfaces stuff */
1699 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1701 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1706 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1708 klass->supported = gst_ximagesink_interface_supported;
1712 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1713 GstStructure * structure)
1715 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1717 gint x_offset, y_offset;
1721 event = gst_event_new_navigation (structure);
1723 /* We are not converting the pointer coordinates as there's no hardware
1724 scaling done here. The only possible scaling is done by videoscale and
1725 videoscale will have to catch those events and tranform the coordinates
1726 to match the applied scaling. So here we just add the offset if the image
1727 is centered in the window. */
1729 /* We take the flow_lock while we look at the window */
1730 g_mutex_lock (ximagesink->flow_lock);
1732 if (!ximagesink->xwindow) {
1733 g_mutex_unlock (ximagesink->flow_lock);
1737 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1738 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1740 g_mutex_unlock (ximagesink->flow_lock);
1742 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1744 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1746 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1748 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1751 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1753 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1754 gst_pad_send_event (pad, event);
1756 gst_object_unref (pad);
1761 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1763 iface->send_event = gst_ximagesink_navigation_send_event;
1767 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1769 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1770 GstXWindow *xwindow = NULL;
1771 XWindowAttributes attr;
1773 /* If we already use that window return */
1774 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win))
1777 /* If the element has not initialized the X11 context try to do so */
1778 if (!ximagesink->xcontext)
1779 ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink);
1781 if (!ximagesink->xcontext) {
1782 GST_WARNING_OBJECT (ximagesink,
1783 "ximagesink was unable to obtain the X11 context.");
1787 /* We acquire the stream lock while setting this window in the element.
1788 We are basically cleaning tons of stuff replacing the old window, putting
1789 images while we do that would surely crash */
1790 g_mutex_lock (ximagesink->flow_lock);
1792 /* If a window is there already we destroy it */
1793 if (ximagesink->xwindow) {
1794 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1795 ximagesink->xwindow = NULL;
1798 /* If the xid is 0 we go back to an internal window */
1799 if (xwindow_id == 0) {
1800 /* If no width/height caps nego did not happen window will be created
1801 during caps nego then */
1802 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1803 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1804 GST_VIDEO_SINK_WIDTH (ximagesink),
1805 GST_VIDEO_SINK_HEIGHT (ximagesink));
1808 xwindow = g_new0 (GstXWindow, 1);
1810 xwindow->win = xwindow_id;
1812 /* We get window geometry, set the event we want to receive,
1814 g_mutex_lock (ximagesink->x_lock);
1815 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1816 xwindow->width = attr.width;
1817 xwindow->height = attr.height;
1818 xwindow->internal = FALSE;
1819 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1820 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1823 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1824 g_mutex_unlock (ximagesink->x_lock);
1828 ximagesink->xwindow = xwindow;
1830 g_mutex_unlock (ximagesink->flow_lock);
1834 gst_ximagesink_expose (GstXOverlay * overlay)
1836 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1838 if (!ximagesink->xwindow)
1841 gst_ximagesink_ximage_put (ximagesink, NULL);
1845 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1847 iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1848 iface->expose = gst_ximagesink_expose;
1851 /* =========================================== */
1853 /* Init & Class init */
1855 /* =========================================== */
1858 gst_ximagesink_set_property (GObject * object, guint prop_id,
1859 const GValue * value, GParamSpec * pspec)
1861 GstXImageSink *ximagesink;
1863 g_return_if_fail (GST_IS_XIMAGESINK (object));
1865 ximagesink = GST_XIMAGESINK (object);
1869 ximagesink->display_name = g_strdup (g_value_get_string (value));
1871 case PROP_SYNCHRONOUS:
1872 ximagesink->synchronous = g_value_get_boolean (value);
1873 if (ximagesink->xcontext) {
1874 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1875 ximagesink->synchronous ? "TRUE" : "FALSE");
1876 g_mutex_lock (ximagesink->x_lock);
1877 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1878 g_mutex_unlock (ximagesink->x_lock);
1881 case PROP_FORCE_ASPECT_RATIO:
1882 ximagesink->keep_aspect = g_value_get_boolean (value);
1884 case PROP_PIXEL_ASPECT_RATIO:
1888 tmp = g_new0 (GValue, 1);
1889 g_value_init (tmp, GST_TYPE_FRACTION);
1891 if (!g_value_transform (value, tmp)) {
1892 GST_WARNING_OBJECT (ximagesink,
1893 "Could not transform string to aspect ratio");
1896 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1897 gst_value_get_fraction_numerator (tmp),
1898 gst_value_get_fraction_denominator (tmp));
1899 g_free (ximagesink->par);
1900 ximagesink->par = tmp;
1905 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1911 gst_ximagesink_get_property (GObject * object, guint prop_id,
1912 GValue * value, GParamSpec * pspec)
1914 GstXImageSink *ximagesink;
1916 g_return_if_fail (GST_IS_XIMAGESINK (object));
1918 ximagesink = GST_XIMAGESINK (object);
1922 g_value_set_string (value, ximagesink->display_name);
1924 case PROP_SYNCHRONOUS:
1925 g_value_set_boolean (value, ximagesink->synchronous);
1927 case PROP_FORCE_ASPECT_RATIO:
1928 g_value_set_boolean (value, ximagesink->keep_aspect);
1930 case PROP_PIXEL_ASPECT_RATIO:
1931 if (ximagesink->par)
1932 g_value_transform (ximagesink->par, value);
1935 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1941 gst_ximagesink_finalize (GObject * object)
1943 GstXImageSink *ximagesink;
1945 ximagesink = GST_XIMAGESINK (object);
1947 if (ximagesink->display_name) {
1948 g_free (ximagesink->display_name);
1949 ximagesink->display_name = NULL;
1951 if (ximagesink->par) {
1952 g_free (ximagesink->par);
1953 ximagesink->par = NULL;
1955 if (ximagesink->x_lock) {
1956 g_mutex_free (ximagesink->x_lock);
1957 ximagesink->x_lock = NULL;
1959 if (ximagesink->flow_lock) {
1960 g_mutex_free (ximagesink->flow_lock);
1961 ximagesink->flow_lock = NULL;
1963 if (ximagesink->pool_lock) {
1964 g_mutex_free (ximagesink->pool_lock);
1965 ximagesink->pool_lock = NULL;
1968 G_OBJECT_CLASS (parent_class)->finalize (object);
1972 gst_ximagesink_init (GstXImageSink * ximagesink)
1974 ximagesink->display_name = NULL;
1975 ximagesink->xcontext = NULL;
1976 ximagesink->xwindow = NULL;
1977 ximagesink->ximage = NULL;
1978 ximagesink->cur_image = NULL;
1980 ximagesink->event_thread = NULL;
1981 ximagesink->running = FALSE;
1983 ximagesink->fps_n = 0;
1984 ximagesink->fps_d = 1;
1986 ximagesink->x_lock = g_mutex_new ();
1987 ximagesink->flow_lock = g_mutex_new ();
1989 ximagesink->par = NULL;
1991 ximagesink->pool_lock = g_mutex_new ();
1992 ximagesink->buffer_pool = NULL;
1994 ximagesink->synchronous = FALSE;
1995 ximagesink->keep_aspect = FALSE;
1999 gst_ximagesink_base_init (gpointer g_class)
2001 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2003 gst_element_class_set_details (element_class, &gst_ximagesink_details);
2005 gst_element_class_add_pad_template (element_class,
2006 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2010 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2012 GObjectClass *gobject_class;
2013 GstElementClass *gstelement_class;
2014 GstBaseSinkClass *gstbasesink_class;
2016 gobject_class = (GObjectClass *) klass;
2017 gstelement_class = (GstElementClass *) klass;
2018 gstbasesink_class = (GstBaseSinkClass *) klass;
2020 parent_class = g_type_class_peek_parent (klass);
2022 gobject_class->finalize = gst_ximagesink_finalize;
2023 gobject_class->set_property = gst_ximagesink_set_property;
2024 gobject_class->get_property = gst_ximagesink_get_property;
2026 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2027 g_param_spec_string ("display", "Display", "X Display name",
2028 NULL, G_PARAM_READWRITE));
2029 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2030 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2031 "the X display in synchronous mode. (used only for debugging)", FALSE,
2032 G_PARAM_READWRITE));
2033 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2034 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2035 "When enabled, reverse caps negotiation (scaling) will respect "
2036 "original aspect ratio", FALSE, G_PARAM_READWRITE));
2037 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2038 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2039 "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2041 gstelement_class->change_state = gst_ximagesink_change_state;
2043 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2044 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2045 gstbasesink_class->buffer_alloc =
2046 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2047 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2048 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2049 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2052 /* ============================================================= */
2054 /* Public Methods */
2056 /* ============================================================= */
2058 /* =========================================== */
2060 /* Object typing & Creation */
2062 /* =========================================== */
2065 gst_ximagesink_get_type (void)
2067 static GType ximagesink_type = 0;
2069 if (!ximagesink_type) {
2070 static const GTypeInfo ximagesink_info = {
2071 sizeof (GstXImageSinkClass),
2072 gst_ximagesink_base_init,
2074 (GClassInitFunc) gst_ximagesink_class_init,
2077 sizeof (GstXImageSink),
2079 (GInstanceInitFunc) gst_ximagesink_init,
2081 static const GInterfaceInfo iface_info = {
2082 (GInterfaceInitFunc) gst_ximagesink_interface_init,
2086 static const GInterfaceInfo navigation_info = {
2087 (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2091 static const GInterfaceInfo overlay_info = {
2092 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2097 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2098 "GstXImageSink", &ximagesink_info, 0);
2100 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2102 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2104 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2107 /* register type and create class in a more safe place instead of at
2108 * runtime since the type registration and class creation is not
2110 g_type_class_ref (gst_ximage_buffer_get_type ());
2113 return ximagesink_type;