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
23 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24 * display. This element can receive a Window ID from the application through
25 * the XOverlay interface and will then render video frames in this drawable.
26 * If no Window ID was provided by the application, the element will create its
27 * own internal window and render into it.
30 * <title>Scaling</title>
32 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33 * reverse caps negotiation to try to get scaled video frames for the drawable.
34 * This is accomplished by asking the peer pad if it accepts some different caps
35 * which in most cases implies that there is a scaling element in the pipeline,
36 * or that an element generating the video frames can generate them with a
37 * different geometry. This mechanism is handled during buffer allocations, for
38 * each allocation request the video sink will check the drawable geometry, look
39 * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40 * desired video frames and then check that the peer pad accept those new caps.
41 * If it does it will then allocate a buffer in video memory with this new
42 * 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.
60 * <title>Pixel aspect ratio</title>
62 * When changing state to GST_STATE_READY, XImageSink will open a connection to
63 * the display specified in the #GstXImageSink:display property or the default
64 * display if nothing specified. Once this connection is open it will inspect
65 * the display configuration including the physical display geometry and
66 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67 * video sink will set the calculated pixel aspect ratio on the caps to make
68 * sure that incoming video frames will have the correct pixel aspect ratio for
69 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70 * then possible to enforce a specific pixel aspect ratio using the
71 * #GstXImageSink:pixel-aspect-ratio property.
75 * <title>Examples</title>
77 * gst-launch -v videotestsrc ! queue ! ximagesink
78 * ]| A pipeline to test reverse negotiation. When the test video signal appears
79 * you can resize the window and see that scaled buffers of the desired size are
80 * going to arrive with a short delay. This illustrates how buffers of desired
81 * size are allocated along the way. If you take away the queue, scaling will
82 * happen almost immediately.
84 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85 * ]| A pipeline to test navigation events.
86 * While moving the mouse pointer over the test signal you will see a black box
87 * following the mouse pointer. If you press the mouse button somewhere on the
88 * video and release it somewhere else a green box will appear where you pressed
89 * the button and a red one where you released it. (The navigationtest element
90 * is part of gst-plugins-good.)
92 * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94 * videotestsrc, in most cases the pixel aspect ratio of the display will be
95 * 1/1. This means that videoscale will have to do the scaling to convert
96 * incoming frames to a size that will match the display pixel aspect ratio
97 * (from 320x240 to 320x180 in this case). Note that you might have to escape
98 * some characters for your shell like '\(fraction\)'.
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
111 #include "ximagesink.h"
113 /* Debugging category */
114 #include <gst/gstinfo.h>
116 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
117 #define GST_CAT_DEFAULT gst_debug_ximagesink
122 unsigned long functions;
123 unsigned long decorations;
125 unsigned long status;
127 MotifWmHints, MwmHints;
129 #define MWM_HINTS_DECORATIONS (1L << 1)
131 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
132 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
133 GstXImageBuffer * ximage);
134 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
135 GstXWindow * xwindow);
136 static void gst_ximagesink_expose (GstXOverlay * overlay);
138 /* ElementFactory information */
139 static const GstElementDetails gst_ximagesink_details =
140 GST_ELEMENT_DETAILS ("Video sink",
142 "A standard X based videosink",
143 "Julien Moutte <julien@moutte.net>");
145 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
146 GST_STATIC_PAD_TEMPLATE ("sink",
149 GST_STATIC_CAPS ("video/x-raw-rgb, "
150 "framerate = (fraction) [ 0, MAX ], "
151 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
159 PROP_PIXEL_ASPECT_RATIO,
160 PROP_FORCE_ASPECT_RATIO,
165 static GstVideoSinkClass *parent_class = NULL;
167 /* ============================================================= */
169 /* Private Methods */
171 /* ============================================================= */
175 static GstBufferClass *ximage_buffer_parent_class = NULL;
177 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
179 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
180 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
181 #define GST_XIMAGE_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
183 /* So some words about GstMiniObject, this is pretty messy...
184 GstMiniObject does not use the standard finalizing of GObjects, you are
185 supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
186 which will handle its own refcount system and call gst_mini_object_free.
187 gst_mini_object_free will call the class finalize method which is not the
188 one from GObject, after calling this finalize method it will free the object
189 instance for you if the refcount is still 0 so you should not chain up */
191 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
193 GstXImageSink *ximagesink = NULL;
194 gboolean recycled = FALSE;
197 g_return_if_fail (ximage != NULL);
199 ximagesink = ximage->ximagesink;
200 if (G_UNLIKELY (ximagesink == NULL)) {
201 GST_WARNING_OBJECT (ximagesink, "no sink found");
205 GST_OBJECT_LOCK (ximagesink);
206 running = ximagesink->running;
207 GST_OBJECT_UNLOCK (ximagesink);
209 if (running == FALSE) {
210 /* If the sink is shutting down, need to clear the image */
211 GST_DEBUG_OBJECT (ximagesink,
212 "destroy image %p because the sink is shutting down", ximage);
213 gst_ximagesink_ximage_destroy (ximagesink, ximage);
214 } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
215 (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
216 /* If our geometry changed we can't reuse that image. */
217 GST_DEBUG_OBJECT (ximagesink,
218 "destroy image %p as its size changed %dx%d vs current %dx%d",
219 ximage, ximage->width, ximage->height,
220 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
221 gst_ximagesink_ximage_destroy (ximagesink, ximage);
223 /* In that case we can reuse the image and add it to our image pool. */
224 GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
225 /* need to increment the refcount again to recycle */
226 gst_buffer_ref (GST_BUFFER_CAST (ximage));
227 g_mutex_lock (ximagesink->pool_lock);
228 ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
229 g_mutex_unlock (ximagesink->pool_lock);
234 GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->finalize
235 (GST_MINI_OBJECT (ximage));
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 ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
266 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
267 gst_ximage_buffer_finalize;
271 gst_ximage_buffer_get_type (void)
273 static GType _gst_ximage_buffer_type;
275 if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
276 static const GTypeInfo ximage_buffer_info = {
277 sizeof (GstBufferClass),
280 gst_ximage_buffer_class_init,
283 sizeof (GstXImageBuffer),
285 (GInstanceInitFunc) gst_ximage_buffer_init,
288 _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
289 "GstXImageBuffer", &ximage_buffer_info, 0);
291 return _gst_ximage_buffer_type;
296 static gboolean error_caught = FALSE;
299 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
301 char error_msg[1024];
303 XGetErrorText (display, xevent->error_code, error_msg, 1024);
304 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
309 #ifdef HAVE_XSHM /* Check that XShm calls actually work */
312 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
313 GstXContext * xcontext)
316 XShmSegmentInfo SHMInfo;
318 int (*handler) (Display *, XErrorEvent *);
319 gboolean result = FALSE;
320 gboolean did_attach = FALSE;
322 g_return_val_if_fail (xcontext != NULL, FALSE);
324 /* Sync to ensure any older errors are already processed */
325 XSync (xcontext->disp, FALSE);
327 /* Set defaults so we don't free these later unnecessarily */
328 SHMInfo.shmaddr = ((void *) -1);
331 /* Setting an error handler to catch failure */
332 error_caught = FALSE;
333 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
335 /* Trying to create a 1x1 ximage */
336 GST_DEBUG ("XShmCreateImage of 1x1");
338 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
339 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
341 /* Might cause an error, sync to ensure it is noticed */
342 XSync (xcontext->disp, FALSE);
343 if (!ximage || error_caught) {
344 GST_WARNING ("could not XShmCreateImage a 1x1 image");
347 size = ximage->height * ximage->bytes_per_line;
349 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
350 if (SHMInfo.shmid == -1) {
351 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
356 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
357 if (SHMInfo.shmaddr == ((void *) -1)) {
358 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
359 /* Clean up shm seg */
360 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
364 ximage->data = SHMInfo.shmaddr;
365 SHMInfo.readOnly = FALSE;
367 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
368 GST_WARNING ("Failed to XShmAttach");
369 /* Clean up shm seg */
370 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
374 /* Sync to ensure we see any errors we caused */
375 XSync (xcontext->disp, FALSE);
377 /* Delete the shared memory segment as soon as everyone is attached.
378 * This way, it will be deleted as soon as we detach later, and not
379 * leaked if we crash. */
380 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
384 /* store whether we succeeded in result */
389 /* Sync to ensure we swallow any errors we caused and reset error_caught */
390 XSync (xcontext->disp, FALSE);
391 error_caught = FALSE;
392 XSetErrorHandler (handler);
395 XShmDetach (xcontext->disp, &SHMInfo);
396 XSync (xcontext->disp, FALSE);
398 if (SHMInfo.shmaddr != ((void *) -1))
399 shmdt (SHMInfo.shmaddr);
401 XDestroyImage (ximage);
404 #endif /* HAVE_XSHM */
406 /* This function handles GstXImageBuffer creation depending on XShm availability */
407 static GstXImageBuffer *
408 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
410 GstXImageBuffer *ximage = NULL;
411 GstStructure *structure = NULL;
412 gboolean succeeded = FALSE;
413 int (*handler) (Display *, XErrorEvent *);
415 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
417 ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
419 structure = gst_caps_get_structure (caps, 0);
421 if (!gst_structure_get_int (structure, "width", &ximage->width) ||
422 !gst_structure_get_int (structure, "height", &ximage->height)) {
423 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
426 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
427 ximage->width, ximage->height);
429 g_mutex_lock (ximagesink->x_lock);
431 /* Setting an error handler to catch failure */
432 error_caught = FALSE;
433 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
436 if (ximagesink->xcontext->use_xshm) {
437 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
438 ximagesink->xcontext->visual,
439 ximagesink->xcontext->depth,
440 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
441 if (!ximage->ximage || error_caught) {
442 g_mutex_unlock (ximagesink->x_lock);
443 /* Reset error handler */
444 error_caught = FALSE;
445 XSetErrorHandler (handler);
447 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
448 ("Failed to create output image buffer of %dx%d pixels",
449 ximage->width, ximage->height),
450 ("could not XShmCreateImage a %dx%d image",
451 ximage->width, ximage->height));
455 /* we have to use the returned bytes_per_line for our shm size */
456 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
457 GST_LOG_OBJECT (ximagesink,
458 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
459 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
461 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
463 if (ximage->SHMInfo.shmid == -1) {
464 g_mutex_unlock (ximagesink->x_lock);
465 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
466 ("Failed to create output image buffer of %dx%d pixels",
467 ximage->width, ximage->height),
468 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
473 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
474 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
475 g_mutex_unlock (ximagesink->x_lock);
476 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
477 ("Failed to create output image buffer of %dx%d pixels",
478 ximage->width, ximage->height),
479 ("Failed to shmat: %s", g_strerror (errno)));
480 /* Clean up the shared memory segment */
481 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
485 ximage->ximage->data = ximage->SHMInfo.shmaddr;
486 ximage->SHMInfo.readOnly = FALSE;
488 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
489 /* Clean up shm seg */
490 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
492 g_mutex_unlock (ximagesink->x_lock);
493 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
494 ("Failed to create output image buffer of %dx%d pixels",
495 ximage->width, ximage->height), ("Failed to XShmAttach"));
499 XSync (ximagesink->xcontext->disp, FALSE);
501 /* Now that everyone has attached, we can delete the shared memory segment.
502 * This way, it will be deleted as soon as we detach later, and not
503 * leaked if we crash. */
504 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
507 #endif /* HAVE_XSHM */
511 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
512 ximagesink->xcontext->visual,
513 ximagesink->xcontext->depth,
515 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
516 if (!ximage->ximage || error_caught) {
517 g_mutex_unlock (ximagesink->x_lock);
518 /* Reset error handler */
519 error_caught = FALSE;
520 XSetErrorHandler (handler);
522 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
523 ("Failed to create output image buffer of %dx%d pixels",
524 ximage->width, ximage->height),
525 ("could not XCreateImage a %dx%d image",
526 ximage->width, ximage->height));
530 /* upstream will assume that rowstrides are multiples of 4, but this
531 * doesn't always seem to be the case with XCreateImage() */
532 if ((ximage->ximage->bytes_per_line % 4) != 0) {
533 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
537 /* we have to use the returned bytes_per_line for our image size */
538 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
540 /* alloc a bit more for unexpected strides to avoid crashes upstream.
541 * FIXME: if we get an unrounded stride, the image will be displayed
542 * distorted, since all upstream elements assume a rounded stride */
544 GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
545 ximage->ximage->height;
546 ximage->ximage->data = g_malloc (allocsize);
547 GST_LOG_OBJECT (ximagesink,
548 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
549 "stride %d", ximage->size, allocsize, ximage->width,
550 ximage->ximage->bytes_per_line);
552 XSync (ximagesink->xcontext->disp, FALSE);
555 /* Reset error handler */
556 error_caught = FALSE;
557 XSetErrorHandler (handler);
561 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
562 GST_BUFFER_SIZE (ximage) = ximage->size;
564 /* Keep a ref to our sink */
565 ximage->ximagesink = gst_object_ref (ximagesink);
567 g_mutex_unlock (ximagesink->x_lock);
570 gst_ximage_buffer_free (ximage);
577 /* This function destroys a GstXImageBuffer handling XShm availability */
579 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
580 GstXImageBuffer * ximage)
582 g_return_if_fail (ximage != NULL);
583 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
585 /* Hold the object lock to ensure the XContext doesn't disappear */
586 GST_OBJECT_LOCK (ximagesink);
588 /* If the destroyed image is the current one we destroy our reference too */
589 if (ximagesink->cur_image == ximage) {
590 ximagesink->cur_image = NULL;
593 /* We might have some buffers destroyed after changing state to NULL */
594 if (!ximagesink->xcontext) {
595 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
597 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
598 shmdt (ximage->SHMInfo.shmaddr);
604 g_mutex_lock (ximagesink->x_lock);
607 if (ximagesink->xcontext->use_xshm) {
608 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
609 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
610 XSync (ximagesink->xcontext->disp, 0);
611 shmdt (ximage->SHMInfo.shmaddr);
614 XDestroyImage (ximage->ximage);
617 #endif /* HAVE_XSHM */
619 if (ximage->ximage) {
620 XDestroyImage (ximage->ximage);
624 XSync (ximagesink->xcontext->disp, FALSE);
626 g_mutex_unlock (ximagesink->x_lock);
629 GST_OBJECT_UNLOCK (ximagesink);
631 if (ximage->ximagesink) {
632 /* Release the ref to our sink */
633 ximage->ximagesink = NULL;
634 gst_object_unref (ximagesink);
640 /* We are called with the x_lock taken */
642 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
643 GstXWindow * xwindow, GstVideoRectangle rect)
645 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
646 g_return_if_fail (xwindow != NULL);
648 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
649 ximagesink->xcontext->black);
653 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
654 0, 0, rect.x, xwindow->height);
658 if ((rect.x + rect.w) < xwindow->width) {
659 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
660 rect.x + rect.w, 0, xwindow->width, xwindow->height);
665 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
666 0, 0, xwindow->width, rect.y);
670 if ((rect.y + rect.h) < xwindow->height) {
671 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
672 0, rect.y + rect.h, xwindow->width, xwindow->height);
676 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
678 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
680 GstVideoRectangle src, dst, result;
681 gboolean draw_border = FALSE;
683 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
685 /* We take the flow_lock. If expose is in there we don't want to run
686 concurrently from the data flow thread */
687 g_mutex_lock (ximagesink->flow_lock);
689 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
690 g_mutex_unlock (ximagesink->flow_lock);
694 /* Draw borders when displaying the first frame. After this
695 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
696 if (!ximagesink->cur_image || ximagesink->draw_border) {
700 /* Store a reference to the last image we put, lose the previous one */
701 if (ximage && ximagesink->cur_image != ximage) {
702 if (ximagesink->cur_image) {
703 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
704 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
706 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
707 ximagesink->cur_image =
708 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
711 /* Expose sends a NULL image, we take the latest frame */
714 if (ximagesink->cur_image) {
715 ximage = ximagesink->cur_image;
717 g_mutex_unlock (ximagesink->flow_lock);
722 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
724 src.w = ximage->width;
725 src.h = ximage->height;
726 dst.w = ximagesink->xwindow->width;
727 dst.h = ximagesink->xwindow->height;
729 gst_video_sink_center_rect (src, dst, &result, FALSE);
731 g_mutex_lock (ximagesink->x_lock);
734 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
736 ximagesink->draw_border = FALSE;
739 if (ximagesink->xcontext->use_xshm) {
740 GST_LOG_OBJECT (ximagesink,
741 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
742 ximage, 0, 0, result.x, result.y, result.w, result.h,
743 ximagesink->xwindow->width, ximagesink->xwindow->height);
744 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
745 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
746 result.w, result.h, FALSE);
748 #endif /* HAVE_XSHM */
750 GST_LOG_OBJECT (ximagesink,
751 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
752 ximage, 0, 0, result.x, result.y, result.w, result.h,
753 ximagesink->xwindow->width, ximagesink->xwindow->height);
754 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
755 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
759 XSync (ximagesink->xcontext->disp, FALSE);
761 g_mutex_unlock (ximagesink->x_lock);
763 g_mutex_unlock (ximagesink->flow_lock);
769 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
772 Atom hints_atom = None;
775 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
776 g_return_val_if_fail (window != NULL, FALSE);
778 g_mutex_lock (ximagesink->x_lock);
780 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
781 if (hints_atom == None) {
782 g_mutex_unlock (ximagesink->x_lock);
786 hints = g_malloc0 (sizeof (MotifWmHints));
788 hints->flags |= MWM_HINTS_DECORATIONS;
789 hints->decorations = 1 << 0;
791 XChangeProperty (ximagesink->xcontext->disp, window->win,
792 hints_atom, hints_atom, 32, PropModeReplace,
793 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
795 XSync (ximagesink->xcontext->disp, FALSE);
797 g_mutex_unlock (ximagesink->x_lock);
804 /* This function handles a GstXWindow creation */
806 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
808 GstXWindow *xwindow = NULL;
811 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
813 xwindow = g_new0 (GstXWindow, 1);
815 xwindow->width = width;
816 xwindow->height = height;
817 xwindow->internal = TRUE;
819 g_mutex_lock (ximagesink->x_lock);
821 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
822 ximagesink->xcontext->root,
823 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
825 /* We have to do that to prevent X from redrawing the background on
826 ConfigureNotify. This takes away flickering of video when resizing. */
827 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
829 if (ximagesink->handle_events) {
832 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
833 StructureNotifyMask | PointerMotionMask | KeyPressMask |
834 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
836 /* Tell the window manager we'd like delete client messages instead of
838 wm_delete = XInternAtom (ximagesink->xcontext->disp,
839 "WM_DELETE_WINDOW", False);
840 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
844 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
847 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
849 XSync (ximagesink->xcontext->disp, FALSE);
851 g_mutex_unlock (ximagesink->x_lock);
853 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
855 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
860 /* This function destroys a GstXWindow */
862 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
863 GstXWindow * xwindow)
865 g_return_if_fail (xwindow != NULL);
866 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
868 g_mutex_lock (ximagesink->x_lock);
870 /* If we did not create that window we just free the GC and let it live */
871 if (xwindow->internal)
872 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
874 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
876 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
878 XSync (ximagesink->xcontext->disp, FALSE);
880 g_mutex_unlock (ximagesink->x_lock);
886 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
887 GstXWindow * xwindow)
889 XWindowAttributes attr;
891 g_return_if_fail (xwindow != NULL);
892 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
894 /* Update the window geometry */
895 g_mutex_lock (ximagesink->x_lock);
897 XGetWindowAttributes (ximagesink->xcontext->disp,
898 ximagesink->xwindow->win, &attr);
900 ximagesink->xwindow->width = attr.width;
901 ximagesink->xwindow->height = attr.height;
903 g_mutex_unlock (ximagesink->x_lock);
907 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
909 g_return_if_fail (xwindow != NULL);
910 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
912 g_mutex_lock (ximagesink->x_lock);
914 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
915 ximagesink->xcontext->black);
917 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
918 0, 0, xwindow->width, xwindow->height);
920 XSync (ximagesink->xcontext->disp, FALSE);
922 g_mutex_unlock (ximagesink->x_lock);
925 /* This function handles XEvents that might be in the queue. It generates
926 GstEvent that will be sent upstream in the pipeline to handle interactivity
929 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
932 guint pointer_x = 0, pointer_y = 0;
933 gboolean pointer_moved = FALSE;
934 gboolean exposed = FALSE, configured = FALSE;
936 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
938 /* Then we get all pointer motion events, only the last position is
940 g_mutex_lock (ximagesink->flow_lock);
941 g_mutex_lock (ximagesink->x_lock);
942 while (XCheckWindowEvent (ximagesink->xcontext->disp,
943 ximagesink->xwindow->win, PointerMotionMask, &e)) {
944 g_mutex_unlock (ximagesink->x_lock);
945 g_mutex_unlock (ximagesink->flow_lock);
949 pointer_x = e.xmotion.x;
950 pointer_y = e.xmotion.y;
951 pointer_moved = TRUE;
956 g_mutex_lock (ximagesink->flow_lock);
957 g_mutex_lock (ximagesink->x_lock);
961 g_mutex_unlock (ximagesink->x_lock);
962 g_mutex_unlock (ximagesink->flow_lock);
964 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
965 pointer_x, pointer_y);
966 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
967 "mouse-move", 0, pointer_x, pointer_y);
969 g_mutex_lock (ximagesink->flow_lock);
970 g_mutex_lock (ximagesink->x_lock);
973 /* We get all remaining events on our window to throw them upstream */
974 while (XCheckWindowEvent (ximagesink->xcontext->disp,
975 ximagesink->xwindow->win,
976 KeyPressMask | KeyReleaseMask |
977 ButtonPressMask | ButtonReleaseMask, &e)) {
980 /* We lock only for the X function call */
981 g_mutex_unlock (ximagesink->x_lock);
982 g_mutex_unlock (ximagesink->flow_lock);
986 /* Mouse button pressed/released over our window. We send upstream
987 events for interactivity/navigation */
988 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
989 e.xbutton.button, e.xbutton.x, e.xbutton.x);
990 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
991 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
994 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
995 e.xbutton.button, e.xbutton.x, e.xbutton.x);
996 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
997 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1001 /* Key pressed/released over our window. We send upstream
1002 events for interactivity/navigation */
1003 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1004 e.xkey.keycode, e.xkey.x, e.xkey.x);
1005 g_mutex_lock (ximagesink->x_lock);
1006 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1008 g_mutex_unlock (ximagesink->x_lock);
1009 if (keysym != NoSymbol) {
1010 char *key_str = NULL;
1012 g_mutex_lock (ximagesink->x_lock);
1013 key_str = XKeysymToString (keysym);
1014 g_mutex_unlock (ximagesink->x_lock);
1015 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1016 e.type == KeyPress ? "key-press" : "key-release", key_str);
1019 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1020 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1024 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1027 g_mutex_lock (ximagesink->flow_lock);
1028 g_mutex_lock (ximagesink->x_lock);
1031 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1032 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1037 case ConfigureNotify:
1045 if (ximagesink->handle_expose && (exposed || configured)) {
1046 g_mutex_unlock (ximagesink->x_lock);
1047 g_mutex_unlock (ximagesink->flow_lock);
1049 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1051 g_mutex_lock (ximagesink->flow_lock);
1052 g_mutex_lock (ximagesink->x_lock);
1055 /* Handle Display events */
1056 while (XPending (ximagesink->xcontext->disp)) {
1057 XNextEvent (ximagesink->xcontext->disp, &e);
1060 case ClientMessage:{
1063 wm_delete = XInternAtom (ximagesink->xcontext->disp,
1064 "WM_DELETE_WINDOW", False);
1065 if (wm_delete == (Atom) e.xclient.data.l[0]) {
1066 /* Handle window deletion by posting an error on the bus */
1067 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1068 ("Output window was closed"), (NULL));
1070 g_mutex_unlock (ximagesink->x_lock);
1071 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1072 ximagesink->xwindow = NULL;
1073 g_mutex_lock (ximagesink->x_lock);
1082 g_mutex_unlock (ximagesink->x_lock);
1083 g_mutex_unlock (ximagesink->flow_lock);
1087 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1089 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1091 GST_OBJECT_LOCK (ximagesink);
1092 while (ximagesink->running) {
1093 GST_OBJECT_UNLOCK (ximagesink);
1095 if (ximagesink->xwindow) {
1096 gst_ximagesink_handle_xevents (ximagesink);
1100 GST_OBJECT_LOCK (ximagesink);
1102 GST_OBJECT_UNLOCK (ximagesink);
1107 /* This function calculates the pixel aspect ratio based on the properties
1108 * in the xcontext structure and stores it there. */
1110 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1112 static const gint par[][2] = {
1113 {1, 1}, /* regular screen */
1114 {16, 15}, /* PAL TV */
1115 {11, 10}, /* 525 line Rec.601 video */
1116 {54, 59}, /* 625 line Rec.601 video */
1117 {64, 45}, /* 1280x1024 on 16:9 display */
1118 {5, 3}, /* 1280x1024 on 4:3 display */
1119 {4, 3} /* 800x600 on 16:9 display */
1126 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1128 /* first calculate the "real" ratio based on the X values;
1129 * which is the "physical" w/h divided by the w/h in pixels of the display */
1130 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1131 / (xcontext->heightmm * xcontext->width);
1133 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1135 if (xcontext->width == 720 && xcontext->height == 576) {
1136 ratio = 4.0 * 576 / (3.0 * 720);
1138 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1140 /* now find the one from par[][2] with the lowest delta to the real one */
1144 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1145 gdouble this_delta = DELTA (i);
1147 if (this_delta < delta) {
1153 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1154 par[index][0], par[index][1]);
1156 g_free (xcontext->par);
1157 xcontext->par = g_new0 (GValue, 1);
1158 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1159 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1160 GST_DEBUG ("set xcontext PAR to %d/%d",
1161 gst_value_get_fraction_numerator (xcontext->par),
1162 gst_value_get_fraction_denominator (xcontext->par));
1165 /* This function gets the X Display and global info about it. Everything is
1166 stored in our object and will be cleaned when the object is disposed. Note
1167 here that caps for supported format are generated without any window or
1169 static GstXContext *
1170 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1172 GstXContext *xcontext = NULL;
1173 XPixmapFormatValues *px_formats = NULL;
1174 gint nb_formats = 0, i;
1176 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1178 xcontext = g_new0 (GstXContext, 1);
1180 g_mutex_lock (ximagesink->x_lock);
1182 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1184 if (!xcontext->disp) {
1185 g_mutex_unlock (ximagesink->x_lock);
1187 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1188 ("Could not initialise X output"), ("Could not open display"));
1192 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1193 xcontext->screen_num = DefaultScreen (xcontext->disp);
1194 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1195 xcontext->root = DefaultRootWindow (xcontext->disp);
1196 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1197 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1198 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1200 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1201 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1202 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1203 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1205 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1206 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1208 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1210 /* We get supported pixmap formats at supported depth */
1211 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1214 XCloseDisplay (xcontext->disp);
1215 g_mutex_unlock (ximagesink->x_lock);
1216 g_free (xcontext->par);
1221 /* We get bpp value corresponding to our running depth */
1222 for (i = 0; i < nb_formats; i++) {
1223 if (px_formats[i].depth == xcontext->depth)
1224 xcontext->bpp = px_formats[i].bits_per_pixel;
1229 xcontext->endianness =
1230 (ImageByteOrder (xcontext->disp) ==
1231 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1233 /* Search for XShm extension support */
1235 if (XShmQueryExtension (xcontext->disp) &&
1236 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1237 xcontext->use_xshm = TRUE;
1238 GST_DEBUG ("ximagesink is using XShm extension");
1242 xcontext->use_xshm = FALSE;
1243 GST_DEBUG ("ximagesink is not using XShm extension");
1246 /* our caps system handles 24/32bpp RGB as big-endian. */
1247 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1248 xcontext->endianness == G_LITTLE_ENDIAN) {
1249 xcontext->endianness = G_BIG_ENDIAN;
1250 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1251 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1252 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1253 if (xcontext->bpp == 24) {
1254 xcontext->visual->red_mask >>= 8;
1255 xcontext->visual->green_mask >>= 8;
1256 xcontext->visual->blue_mask >>= 8;
1260 /* update object's par with calculated one if not set yet */
1261 if (!ximagesink->par) {
1262 ximagesink->par = g_new0 (GValue, 1);
1263 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1264 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1266 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1267 "bpp", G_TYPE_INT, xcontext->bpp,
1268 "depth", G_TYPE_INT, xcontext->depth,
1269 "endianness", G_TYPE_INT, xcontext->endianness,
1270 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1271 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1272 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1273 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1274 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1275 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1276 if (ximagesink->par) {
1279 nom = gst_value_get_fraction_numerator (ximagesink->par);
1280 den = gst_value_get_fraction_denominator (ximagesink->par);
1281 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1282 GST_TYPE_FRACTION, nom, den, NULL);
1285 g_mutex_unlock (ximagesink->x_lock);
1287 /* Setup our event listening thread */
1288 GST_OBJECT_LOCK (ximagesink);
1289 ximagesink->running = TRUE;
1290 ximagesink->event_thread = g_thread_create (
1291 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1292 GST_OBJECT_UNLOCK (ximagesink);
1297 /* This function cleans the X context. Closing the Display and unrefing the
1298 caps for supported formats. */
1300 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1302 GstXContext *xcontext;
1304 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1306 GST_OBJECT_LOCK (ximagesink);
1307 if (ximagesink->xcontext == NULL) {
1308 GST_OBJECT_UNLOCK (ximagesink);
1312 /* Take the xcontext reference and NULL it while we
1313 * clean it up, so that any buffer-alloced buffers
1314 * arriving after this will be freed correctly */
1315 xcontext = ximagesink->xcontext;
1316 ximagesink->xcontext = NULL;
1318 GST_OBJECT_UNLOCK (ximagesink);
1320 gst_caps_unref (xcontext->caps);
1321 g_free (xcontext->par);
1322 g_free (ximagesink->par);
1323 ximagesink->par = NULL;
1325 g_mutex_lock (ximagesink->x_lock);
1327 XCloseDisplay (xcontext->disp);
1329 g_mutex_unlock (ximagesink->x_lock);
1335 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1338 g_mutex_lock (ximagesink->pool_lock);
1340 while (ximagesink->buffer_pool) {
1341 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1343 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1344 ximagesink->buffer_pool);
1345 gst_ximage_buffer_free (ximage);
1348 g_mutex_unlock (ximagesink->pool_lock);
1354 gst_ximagesink_getcaps (GstBaseSink * bsink)
1356 GstXImageSink *ximagesink;
1360 ximagesink = GST_XIMAGESINK (bsink);
1362 if (ximagesink->xcontext)
1363 return gst_caps_ref (ximagesink->xcontext->caps);
1365 /* get a template copy and add the pixel aspect ratio */
1367 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1368 (ximagesink)->sinkpad));
1369 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1370 GstStructure *structure = gst_caps_get_structure (caps, i);
1372 if (ximagesink->par) {
1375 nom = gst_value_get_fraction_numerator (ximagesink->par);
1376 den = gst_value_get_fraction_denominator (ximagesink->par);
1377 gst_structure_set (structure, "pixel-aspect-ratio",
1378 GST_TYPE_FRACTION, nom, den, NULL);
1386 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1388 GstXImageSink *ximagesink;
1389 gboolean ret = TRUE;
1390 GstStructure *structure;
1391 GstCaps *intersection;
1393 gint new_width, new_height;
1396 ximagesink = GST_XIMAGESINK (bsink);
1398 if (!ximagesink->xcontext)
1401 GST_DEBUG_OBJECT (ximagesink,
1402 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1403 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1405 /* We intersect those caps with our template to make sure they are correct */
1406 intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1407 GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1409 if (gst_caps_is_empty (intersection)) {
1410 gst_caps_unref (intersection);
1414 gst_caps_unref (intersection);
1416 structure = gst_caps_get_structure (caps, 0);
1418 ret &= gst_structure_get_int (structure, "width", &new_width);
1419 ret &= gst_structure_get_int (structure, "height", &new_height);
1420 fps = gst_structure_get_value (structure, "framerate");
1421 ret &= (fps != NULL);
1425 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1426 * otherwise linking should fail */
1427 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1429 if (ximagesink->par) {
1430 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1433 } else if (ximagesink->xcontext->par) {
1434 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1440 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1441 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1442 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1443 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1445 /* Notify application to set xwindow id now */
1446 g_mutex_lock (ximagesink->flow_lock);
1447 if (!ximagesink->xwindow) {
1448 g_mutex_unlock (ximagesink->flow_lock);
1449 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1451 g_mutex_unlock (ximagesink->flow_lock);
1454 /* Creating our window and our image */
1455 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1456 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1457 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1458 ("Invalid image size."));
1462 g_mutex_lock (ximagesink->flow_lock);
1463 if (!ximagesink->xwindow) {
1464 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1465 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1467 /* Remember to draw borders for next frame */
1468 ximagesink->draw_border = TRUE;
1469 g_mutex_unlock (ximagesink->flow_lock);
1471 /* If our ximage has changed we destroy it, next chain iteration will create
1473 if ((ximagesink->ximage) &&
1474 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1475 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1476 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1477 ximagesink->ximage);
1478 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1479 ximagesink->ximage = NULL;
1487 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1492 static GstStateChangeReturn
1493 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1495 GstXImageSink *ximagesink;
1496 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1497 GstXContext *xcontext = NULL;
1499 ximagesink = GST_XIMAGESINK (element);
1501 switch (transition) {
1502 case GST_STATE_CHANGE_NULL_TO_READY:
1504 /* Initializing the XContext */
1505 if (ximagesink->xcontext == NULL) {
1506 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1507 if (xcontext == NULL) {
1508 ret = GST_STATE_CHANGE_FAILURE;
1511 GST_OBJECT_LOCK (ximagesink);
1513 ximagesink->xcontext = xcontext;
1514 GST_OBJECT_UNLOCK (ximagesink);
1517 /* call XSynchronize with the current value of synchronous */
1518 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1519 ximagesink->synchronous ? "TRUE" : "FALSE");
1520 g_mutex_lock (ximagesink->x_lock);
1521 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1522 g_mutex_unlock (ximagesink->x_lock);
1524 case GST_STATE_CHANGE_READY_TO_PAUSED:
1525 g_mutex_lock (ximagesink->flow_lock);
1526 if (ximagesink->xwindow)
1527 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1528 g_mutex_unlock (ximagesink->flow_lock);
1530 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1536 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1538 switch (transition) {
1539 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1541 case GST_STATE_CHANGE_PAUSED_TO_READY:
1542 ximagesink->fps_n = 0;
1543 ximagesink->fps_d = 1;
1544 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1545 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1547 case GST_STATE_CHANGE_READY_TO_NULL:
1548 gst_ximagesink_reset (ximagesink);
1559 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1560 GstClockTime * start, GstClockTime * end)
1562 GstXImageSink *ximagesink;
1564 ximagesink = GST_XIMAGESINK (bsink);
1566 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1567 *start = GST_BUFFER_TIMESTAMP (buf);
1568 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1569 *end = *start + GST_BUFFER_DURATION (buf);
1571 if (ximagesink->fps_n > 0) {
1573 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1580 static GstFlowReturn
1581 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1583 GstXImageSink *ximagesink;
1585 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1587 ximagesink = GST_XIMAGESINK (bsink);
1589 /* If this buffer has been allocated using our buffer management we simply
1590 put the ximage which is in the PRIVATE pointer */
1591 if (GST_IS_XIMAGE_BUFFER (buf)) {
1592 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1593 if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1596 /* Else we have to copy the data into our private image, */
1597 /* if we have one... */
1598 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1599 if (!ximagesink->ximage) {
1600 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1601 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1602 GST_BUFFER_CAPS (buf));
1603 if (!ximagesink->ximage)
1604 /* The create method should have posted an informative error */
1607 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1608 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1609 ("Failed to create output image buffer of %dx%d pixels",
1610 ximagesink->ximage->width, ximagesink->ximage->height),
1611 ("XServer allocated buffer size did not match input buffer"));
1613 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1614 ximagesink->ximage = NULL;
1618 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1619 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1620 if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1629 /* No image available. That's very bad ! */
1630 GST_DEBUG ("could not create image");
1631 return GST_FLOW_ERROR;
1635 /* No Window available to put our image into */
1636 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1637 return GST_FLOW_ERROR;
1641 /* Buffer management
1643 * The buffer_alloc function must either return a buffer with given size and
1644 * caps or create a buffer with different caps attached to the buffer. This
1645 * last option is called reverse negotiation, ie, where the sink suggests a
1646 * different format from the upstream peer.
1648 * We try to do reverse negotiation when our geometry changes and we like a
1651 static GstFlowReturn
1652 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1653 GstCaps * caps, GstBuffer ** buf)
1655 GstXImageSink *ximagesink;
1656 GstXImageBuffer *ximage = NULL;
1657 GstStructure *structure = NULL;
1658 GstFlowReturn ret = GST_FLOW_OK;
1659 GstCaps *alloc_caps;
1660 gboolean alloc_unref = FALSE;
1663 ximagesink = GST_XIMAGESINK (bsink);
1665 GST_LOG_OBJECT (ximagesink,
1666 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1667 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1669 /* assume we're going to alloc what was requested, keep track of
1670 * wheter we need to unref or not. When we suggest a new format
1671 * upstream we will create a new caps that we need to unref. */
1673 alloc_unref = FALSE;
1675 /* get struct to see what is requested */
1676 structure = gst_caps_get_structure (caps, 0);
1678 if (gst_structure_get_int (structure, "width", &width) &&
1679 gst_structure_get_int (structure, "height", &height)) {
1680 GstVideoRectangle dst, src, result;
1685 /* We take the flow_lock because the window might go away */
1686 g_mutex_lock (ximagesink->flow_lock);
1687 if (!ximagesink->xwindow) {
1688 g_mutex_unlock (ximagesink->flow_lock);
1692 /* What is our geometry */
1693 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1694 dst.w = ximagesink->xwindow->width;
1695 dst.h = ximagesink->xwindow->height;
1697 g_mutex_unlock (ximagesink->flow_lock);
1699 if (ximagesink->keep_aspect) {
1700 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1702 gst_video_sink_center_rect (src, dst, &result, TRUE);
1704 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1705 "ignoring aspect ratio");
1706 result.x = result.y = 0;
1711 /* We would like another geometry */
1712 if (width != result.w || height != result.h) {
1714 GstCaps *desired_caps;
1715 GstStructure *desired_struct;
1717 /* make a copy of the incomming caps to create the new
1718 * suggestion. We can't use make_writable because we might
1719 * then destroy the original caps which we still need when the
1720 * peer does not accept the suggestion. */
1721 desired_caps = gst_caps_copy (caps);
1722 desired_struct = gst_caps_get_structure (desired_caps, 0);
1724 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1725 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1726 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1728 /* PAR property overrides the X calculated one */
1729 if (ximagesink->par) {
1730 nom = gst_value_get_fraction_numerator (ximagesink->par);
1731 den = gst_value_get_fraction_denominator (ximagesink->par);
1732 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1733 GST_TYPE_FRACTION, nom, den, NULL);
1734 } else if (ximagesink->xcontext->par) {
1735 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1736 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1737 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1738 GST_TYPE_FRACTION, nom, den, NULL);
1741 /* see if peer accepts our new suggestion, if there is no peer, this
1742 * function returns true. */
1743 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1745 /* we will not alloc a buffer of the new suggested caps. Make sure
1746 * we also unref this new caps after we set it on the buffer. */
1747 alloc_caps = desired_caps;
1751 GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
1754 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1756 /* we alloc a buffer with the original incomming caps already in the
1757 * width and height variables */
1763 /* Inspect our buffer pool */
1764 g_mutex_lock (ximagesink->pool_lock);
1765 while (ximagesink->buffer_pool) {
1766 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1769 /* Removing from the pool */
1770 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1771 ximagesink->buffer_pool);
1773 /* If the ximage is invalid for our need, destroy */
1774 if ((ximage->width != width) || (ximage->height != height)) {
1775 gst_ximage_buffer_free (ximage);
1778 /* We found a suitable ximage */
1783 g_mutex_unlock (ximagesink->pool_lock);
1785 /* We haven't found anything, creating a new one */
1787 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1789 /* Now we should have a ximage, set appropriate caps on it */
1791 /* Make sure the buffer is cleared of any previously used flags */
1792 GST_MINI_OBJECT_CAST (ximage)->flags = 0;
1793 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1796 /* could be our new reffed suggestion or the original unreffed caps */
1798 gst_caps_unref (alloc_caps);
1800 *buf = GST_BUFFER_CAST (ximage);
1805 /* Interfaces stuff */
1808 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1810 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1815 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1817 klass->supported = gst_ximagesink_interface_supported;
1821 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1822 GstStructure * structure)
1824 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1826 gint x_offset, y_offset;
1830 event = gst_event_new_navigation (structure);
1832 /* We are not converting the pointer coordinates as there's no hardware
1833 scaling done here. The only possible scaling is done by videoscale and
1834 videoscale will have to catch those events and tranform the coordinates
1835 to match the applied scaling. So here we just add the offset if the image
1836 is centered in the window. */
1838 /* We take the flow_lock while we look at the window */
1839 g_mutex_lock (ximagesink->flow_lock);
1841 if (!ximagesink->xwindow) {
1842 g_mutex_unlock (ximagesink->flow_lock);
1846 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1847 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1849 g_mutex_unlock (ximagesink->flow_lock);
1851 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1853 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1855 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1857 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1860 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1862 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1863 gst_pad_send_event (pad, event);
1865 gst_object_unref (pad);
1870 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1872 iface->send_event = gst_ximagesink_navigation_send_event;
1876 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1878 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1879 GstXWindow *xwindow = NULL;
1880 XWindowAttributes attr;
1882 /* We acquire the stream lock while setting this window in the element.
1883 We are basically cleaning tons of stuff replacing the old window, putting
1884 images while we do that would surely crash */
1885 g_mutex_lock (ximagesink->flow_lock);
1887 /* If we already use that window return */
1888 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1889 g_mutex_unlock (ximagesink->flow_lock);
1893 /* If the element has not initialized the X11 context try to do so */
1894 if (!ximagesink->xcontext &&
1895 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1896 g_mutex_unlock (ximagesink->flow_lock);
1897 /* we have thrown a GST_ELEMENT_ERROR now */
1901 /* If a window is there already we destroy it */
1902 if (ximagesink->xwindow) {
1903 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1904 ximagesink->xwindow = NULL;
1907 /* If the xid is 0 we go back to an internal window */
1908 if (xwindow_id == 0) {
1909 /* If no width/height caps nego did not happen window will be created
1910 during caps nego then */
1911 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1912 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1913 GST_VIDEO_SINK_WIDTH (ximagesink),
1914 GST_VIDEO_SINK_HEIGHT (ximagesink));
1917 xwindow = g_new0 (GstXWindow, 1);
1919 xwindow->win = xwindow_id;
1921 /* We get window geometry, set the event we want to receive,
1923 g_mutex_lock (ximagesink->x_lock);
1924 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1925 xwindow->width = attr.width;
1926 xwindow->height = attr.height;
1927 xwindow->internal = FALSE;
1928 if (ximagesink->handle_events) {
1929 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1930 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1934 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1935 g_mutex_unlock (ximagesink->x_lock);
1939 ximagesink->xwindow = xwindow;
1941 g_mutex_unlock (ximagesink->flow_lock);
1945 gst_ximagesink_expose (GstXOverlay * overlay)
1947 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1949 gst_ximagesink_ximage_put (ximagesink, NULL);
1953 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1954 gboolean handle_events)
1956 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1958 ximagesink->handle_events = handle_events;
1960 g_mutex_lock (ximagesink->flow_lock);
1962 if (G_UNLIKELY (!ximagesink->xwindow)) {
1963 g_mutex_unlock (ximagesink->flow_lock);
1967 g_mutex_lock (ximagesink->x_lock);
1969 if (handle_events) {
1970 if (ximagesink->xwindow->internal) {
1971 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1972 ExposureMask | StructureNotifyMask | PointerMotionMask |
1973 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1975 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1976 ExposureMask | StructureNotifyMask | PointerMotionMask |
1977 KeyPressMask | KeyReleaseMask);
1980 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1983 g_mutex_unlock (ximagesink->x_lock);
1985 g_mutex_unlock (ximagesink->flow_lock);
1989 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1991 iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1992 iface->expose = gst_ximagesink_expose;
1993 iface->handle_events = gst_ximagesink_set_event_handling;
1996 /* =========================================== */
1998 /* Init & Class init */
2000 /* =========================================== */
2003 gst_ximagesink_set_property (GObject * object, guint prop_id,
2004 const GValue * value, GParamSpec * pspec)
2006 GstXImageSink *ximagesink;
2008 g_return_if_fail (GST_IS_XIMAGESINK (object));
2010 ximagesink = GST_XIMAGESINK (object);
2014 ximagesink->display_name = g_strdup (g_value_get_string (value));
2016 case PROP_SYNCHRONOUS:
2017 ximagesink->synchronous = g_value_get_boolean (value);
2018 if (ximagesink->xcontext) {
2019 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2020 ximagesink->synchronous ? "TRUE" : "FALSE");
2021 g_mutex_lock (ximagesink->x_lock);
2022 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2023 g_mutex_unlock (ximagesink->x_lock);
2026 case PROP_FORCE_ASPECT_RATIO:
2027 ximagesink->keep_aspect = g_value_get_boolean (value);
2029 case PROP_PIXEL_ASPECT_RATIO:
2033 tmp = g_new0 (GValue, 1);
2034 g_value_init (tmp, GST_TYPE_FRACTION);
2036 if (!g_value_transform (value, tmp)) {
2037 GST_WARNING_OBJECT (ximagesink,
2038 "Could not transform string to aspect ratio");
2041 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2042 gst_value_get_fraction_numerator (tmp),
2043 gst_value_get_fraction_denominator (tmp));
2044 g_free (ximagesink->par);
2045 ximagesink->par = tmp;
2049 case PROP_HANDLE_EVENTS:
2050 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2051 g_value_get_boolean (value));
2053 case PROP_HANDLE_EXPOSE:
2054 ximagesink->handle_expose = g_value_get_boolean (value);
2057 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2063 gst_ximagesink_get_property (GObject * object, guint prop_id,
2064 GValue * value, GParamSpec * pspec)
2066 GstXImageSink *ximagesink;
2068 g_return_if_fail (GST_IS_XIMAGESINK (object));
2070 ximagesink = GST_XIMAGESINK (object);
2074 g_value_set_string (value, ximagesink->display_name);
2076 case PROP_SYNCHRONOUS:
2077 g_value_set_boolean (value, ximagesink->synchronous);
2079 case PROP_FORCE_ASPECT_RATIO:
2080 g_value_set_boolean (value, ximagesink->keep_aspect);
2082 case PROP_PIXEL_ASPECT_RATIO:
2083 if (ximagesink->par)
2084 g_value_transform (ximagesink->par, value);
2086 case PROP_HANDLE_EVENTS:
2087 g_value_set_boolean (value, ximagesink->handle_events);
2089 case PROP_HANDLE_EXPOSE:
2090 g_value_set_boolean (value, ximagesink->handle_expose);
2093 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2099 gst_ximagesink_reset (GstXImageSink * ximagesink)
2103 GST_OBJECT_LOCK (ximagesink);
2104 ximagesink->running = FALSE;
2105 /* grab thread and mark it as NULL */
2106 thread = ximagesink->event_thread;
2107 ximagesink->event_thread = NULL;
2108 GST_OBJECT_UNLOCK (ximagesink);
2110 /* Wait for our event thread to finish before we clean up our stuff. */
2112 g_thread_join (thread);
2114 if (ximagesink->ximage) {
2115 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
2116 ximagesink->ximage = NULL;
2118 if (ximagesink->cur_image) {
2119 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
2120 ximagesink->cur_image = NULL;
2123 gst_ximagesink_bufferpool_clear (ximagesink);
2125 g_mutex_lock (ximagesink->flow_lock);
2126 if (ximagesink->xwindow) {
2127 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2128 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2129 ximagesink->xwindow = NULL;
2131 g_mutex_unlock (ximagesink->flow_lock);
2133 gst_ximagesink_xcontext_clear (ximagesink);
2137 gst_ximagesink_finalize (GObject * object)
2139 GstXImageSink *ximagesink;
2141 ximagesink = GST_XIMAGESINK (object);
2143 gst_ximagesink_reset (ximagesink);
2145 if (ximagesink->display_name) {
2146 g_free (ximagesink->display_name);
2147 ximagesink->display_name = NULL;
2149 if (ximagesink->par) {
2150 g_free (ximagesink->par);
2151 ximagesink->par = NULL;
2153 if (ximagesink->x_lock) {
2154 g_mutex_free (ximagesink->x_lock);
2155 ximagesink->x_lock = NULL;
2157 if (ximagesink->flow_lock) {
2158 g_mutex_free (ximagesink->flow_lock);
2159 ximagesink->flow_lock = NULL;
2161 if (ximagesink->pool_lock) {
2162 g_mutex_free (ximagesink->pool_lock);
2163 ximagesink->pool_lock = NULL;
2166 G_OBJECT_CLASS (parent_class)->finalize (object);
2170 gst_ximagesink_init (GstXImageSink * ximagesink)
2172 ximagesink->display_name = NULL;
2173 ximagesink->xcontext = NULL;
2174 ximagesink->xwindow = NULL;
2175 ximagesink->ximage = NULL;
2176 ximagesink->cur_image = NULL;
2178 ximagesink->event_thread = NULL;
2179 ximagesink->running = FALSE;
2181 ximagesink->fps_n = 0;
2182 ximagesink->fps_d = 1;
2184 ximagesink->x_lock = g_mutex_new ();
2185 ximagesink->flow_lock = g_mutex_new ();
2187 ximagesink->par = NULL;
2189 ximagesink->pool_lock = g_mutex_new ();
2190 ximagesink->buffer_pool = NULL;
2192 ximagesink->synchronous = FALSE;
2193 ximagesink->keep_aspect = FALSE;
2194 ximagesink->handle_events = TRUE;
2195 ximagesink->handle_expose = TRUE;
2199 gst_ximagesink_base_init (gpointer g_class)
2201 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2203 gst_element_class_set_details (element_class, &gst_ximagesink_details);
2205 gst_element_class_add_pad_template (element_class,
2206 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2210 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2212 GObjectClass *gobject_class;
2213 GstElementClass *gstelement_class;
2214 GstBaseSinkClass *gstbasesink_class;
2216 gobject_class = (GObjectClass *) klass;
2217 gstelement_class = (GstElementClass *) klass;
2218 gstbasesink_class = (GstBaseSinkClass *) klass;
2220 parent_class = g_type_class_peek_parent (klass);
2222 gobject_class->finalize = gst_ximagesink_finalize;
2223 gobject_class->set_property = gst_ximagesink_set_property;
2224 gobject_class->get_property = gst_ximagesink_get_property;
2226 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2227 g_param_spec_string ("display", "Display", "X Display name",
2228 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2229 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2230 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2231 "the X display in synchronous mode. (used only for debugging)", FALSE,
2232 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2233 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2234 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2235 "When enabled, reverse caps negotiation (scaling) will respect "
2236 "original aspect ratio", FALSE,
2237 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2238 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2239 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2240 "The pixel aspect ratio of the device", "1/1",
2241 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2242 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2243 g_param_spec_boolean ("handle-events", "Handle XEvents",
2244 "When enabled, XEvents will be selected and handled", TRUE,
2245 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2246 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2247 g_param_spec_boolean ("handle-expose", "Handle expose",
2249 "the current frame will always be drawn in response to X Expose "
2250 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2252 gstelement_class->change_state = gst_ximagesink_change_state;
2254 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2255 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2256 gstbasesink_class->buffer_alloc =
2257 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2258 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2259 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2260 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2263 /* ============================================================= */
2265 /* Public Methods */
2267 /* ============================================================= */
2269 /* =========================================== */
2271 /* Object typing & Creation */
2273 /* =========================================== */
2276 gst_ximagesink_get_type (void)
2278 static GType ximagesink_type = 0;
2280 if (!ximagesink_type) {
2281 static const GTypeInfo ximagesink_info = {
2282 sizeof (GstXImageSinkClass),
2283 gst_ximagesink_base_init,
2285 (GClassInitFunc) gst_ximagesink_class_init,
2288 sizeof (GstXImageSink),
2290 (GInstanceInitFunc) gst_ximagesink_init,
2292 static const GInterfaceInfo iface_info = {
2293 (GInterfaceInitFunc) gst_ximagesink_interface_init,
2297 static const GInterfaceInfo navigation_info = {
2298 (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2302 static const GInterfaceInfo overlay_info = {
2303 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2308 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2309 "GstXImageSink", &ximagesink_info, 0);
2311 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2313 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2315 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2318 /* register type and create class in a more safe place instead of at
2319 * runtime since the type registration and class creation is not
2321 g_type_class_ref (gst_ximage_buffer_get_type ());
2324 return ximagesink_type;