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 static void gst_ximagesink_expose (GstXOverlay * overlay);
137 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
138 GST_STATIC_PAD_TEMPLATE ("sink",
141 GST_STATIC_CAPS ("video/x-raw-rgb, "
142 "framerate = (fraction) [ 0, MAX ], "
143 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
151 PROP_PIXEL_ASPECT_RATIO,
152 PROP_FORCE_ASPECT_RATIO,
159 static GstVideoSinkClass *parent_class = NULL;
161 /* ============================================================= */
163 /* Private Methods */
165 /* ============================================================= */
169 static GstBufferClass *ximage_buffer_parent_class = NULL;
171 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
173 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
174 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
175 #define GST_XIMAGE_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
177 /* So some words about GstMiniObject, this is pretty messy...
178 GstMiniObject does not use the standard finalizing of GObjects, you are
179 supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
180 which will handle its own refcount system and call gst_mini_object_free.
181 gst_mini_object_free will call the class finalize method which is not the
182 one from GObject, after calling this finalize method it will free the object
183 instance for you if the refcount is still 0 so you should not chain up */
185 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
187 GstXImageSink *ximagesink = NULL;
188 gboolean recycled = FALSE;
191 g_return_if_fail (ximage != NULL);
193 ximagesink = ximage->ximagesink;
194 if (G_UNLIKELY (ximagesink == NULL)) {
195 GST_WARNING_OBJECT (ximagesink, "no sink found");
199 GST_OBJECT_LOCK (ximagesink);
200 running = ximagesink->running;
201 GST_OBJECT_UNLOCK (ximagesink);
203 if (running == FALSE) {
204 /* If the sink is shutting down, need to clear the image */
205 GST_DEBUG_OBJECT (ximagesink,
206 "destroy image %p because the sink is shutting down", ximage);
207 gst_ximagesink_ximage_destroy (ximagesink, ximage);
208 } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
209 (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
210 /* If our geometry changed we can't reuse that image. */
211 GST_DEBUG_OBJECT (ximagesink,
212 "destroy image %p as its size changed %dx%d vs current %dx%d",
213 ximage, ximage->width, ximage->height,
214 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
215 gst_ximagesink_ximage_destroy (ximagesink, ximage);
217 /* In that case we can reuse the image and add it to our image pool. */
218 GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
219 /* need to increment the refcount again to recycle */
220 gst_buffer_ref (GST_BUFFER_CAST (ximage));
221 g_mutex_lock (ximagesink->pool_lock);
222 ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
223 g_mutex_unlock (ximagesink->pool_lock);
228 GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->finalize
229 (GST_MINI_OBJECT (ximage));
236 gst_ximage_buffer_free (GstXImageBuffer * ximage)
238 /* make sure it is not recycled */
241 gst_buffer_unref (GST_BUFFER_CAST (ximage));
245 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
248 ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
249 ximage_buffer->SHMInfo.shmid = -1;
254 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
256 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
258 ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
260 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
261 gst_ximage_buffer_finalize;
265 gst_ximage_buffer_get_type (void)
267 static GType _gst_ximage_buffer_type;
269 if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
270 static const GTypeInfo ximage_buffer_info = {
271 sizeof (GstBufferClass),
274 gst_ximage_buffer_class_init,
277 sizeof (GstXImageBuffer),
279 (GInstanceInitFunc) gst_ximage_buffer_init,
282 _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
283 "GstXImageBuffer", &ximage_buffer_info, 0);
285 return _gst_ximage_buffer_type;
290 static gboolean error_caught = FALSE;
293 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
295 char error_msg[1024];
297 XGetErrorText (display, xevent->error_code, error_msg, 1024);
298 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
303 #ifdef HAVE_XSHM /* Check that XShm calls actually work */
306 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
307 GstXContext * xcontext)
310 XShmSegmentInfo SHMInfo;
312 int (*handler) (Display *, XErrorEvent *);
313 gboolean result = FALSE;
314 gboolean did_attach = FALSE;
316 g_return_val_if_fail (xcontext != NULL, FALSE);
318 /* Sync to ensure any older errors are already processed */
319 XSync (xcontext->disp, FALSE);
321 /* Set defaults so we don't free these later unnecessarily */
322 SHMInfo.shmaddr = ((void *) -1);
325 /* Setting an error handler to catch failure */
326 error_caught = FALSE;
327 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
329 /* Trying to create a 1x1 ximage */
330 GST_DEBUG ("XShmCreateImage of 1x1");
332 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
333 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
335 /* Might cause an error, sync to ensure it is noticed */
336 XSync (xcontext->disp, FALSE);
337 if (!ximage || error_caught) {
338 GST_WARNING ("could not XShmCreateImage a 1x1 image");
341 size = ximage->height * ximage->bytes_per_line;
343 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
344 if (SHMInfo.shmid == -1) {
345 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
350 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
351 if (SHMInfo.shmaddr == ((void *) -1)) {
352 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
353 /* Clean up shm seg */
354 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
358 ximage->data = SHMInfo.shmaddr;
359 SHMInfo.readOnly = FALSE;
361 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
362 GST_WARNING ("Failed to XShmAttach");
363 /* Clean up shm seg */
364 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
368 /* Sync to ensure we see any errors we caused */
369 XSync (xcontext->disp, FALSE);
371 /* Delete the shared memory segment as soon as everyone is attached.
372 * This way, it will be deleted as soon as we detach later, and not
373 * leaked if we crash. */
374 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
378 /* store whether we succeeded in result */
383 /* Sync to ensure we swallow any errors we caused and reset error_caught */
384 XSync (xcontext->disp, FALSE);
385 error_caught = FALSE;
386 XSetErrorHandler (handler);
389 XShmDetach (xcontext->disp, &SHMInfo);
390 XSync (xcontext->disp, FALSE);
392 if (SHMInfo.shmaddr != ((void *) -1))
393 shmdt (SHMInfo.shmaddr);
395 XDestroyImage (ximage);
398 #endif /* HAVE_XSHM */
400 /* This function handles GstXImageBuffer creation depending on XShm availability */
401 static GstXImageBuffer *
402 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
404 GstXImageBuffer *ximage = NULL;
405 GstStructure *structure = NULL;
406 gboolean succeeded = FALSE;
407 int (*handler) (Display *, XErrorEvent *);
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);
425 /* Setting an error handler to catch failure */
426 error_caught = FALSE;
427 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
430 if (ximagesink->xcontext->use_xshm) {
431 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
432 ximagesink->xcontext->visual,
433 ximagesink->xcontext->depth,
434 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
435 if (!ximage->ximage || error_caught) {
436 g_mutex_unlock (ximagesink->x_lock);
438 /* Reset error flag */
439 error_caught = FALSE;
442 GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
443 ("Failed to create output image buffer of %dx%d pixels",
444 ximage->width, ximage->height),
445 ("could not XShmCreateImage a %dx%d image",
446 ximage->width, ximage->height));
448 /* Retry without XShm */
449 ximagesink->xcontext->use_xshm = FALSE;
451 /* Hold X mutex again to try without XShm */
452 g_mutex_lock (ximagesink->x_lock);
456 /* we have to use the returned bytes_per_line for our shm size */
457 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
458 GST_LOG_OBJECT (ximagesink,
459 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
460 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
462 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
464 if (ximage->SHMInfo.shmid == -1) {
465 g_mutex_unlock (ximagesink->x_lock);
466 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
467 ("Failed to create output image buffer of %dx%d pixels",
468 ximage->width, ximage->height),
469 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
474 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
475 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
476 g_mutex_unlock (ximagesink->x_lock);
477 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
478 ("Failed to create output image buffer of %dx%d pixels",
479 ximage->width, ximage->height),
480 ("Failed to shmat: %s", g_strerror (errno)));
481 /* Clean up the shared memory segment */
482 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
486 ximage->ximage->data = ximage->SHMInfo.shmaddr;
487 ximage->SHMInfo.readOnly = FALSE;
489 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
490 /* Clean up shm seg */
491 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
493 g_mutex_unlock (ximagesink->x_lock);
494 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
495 ("Failed to create output image buffer of %dx%d pixels",
496 ximage->width, ximage->height), ("Failed to XShmAttach"));
500 XSync (ximagesink->xcontext->disp, FALSE);
502 /* Now that everyone has attached, we can delete the shared memory segment.
503 * This way, it will be deleted as soon as we detach later, and not
504 * leaked if we crash. */
505 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
509 #endif /* HAVE_XSHM */
513 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
514 ximagesink->xcontext->visual,
515 ximagesink->xcontext->depth,
517 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
518 if (!ximage->ximage || error_caught) {
519 g_mutex_unlock (ximagesink->x_lock);
520 /* Reset error handler */
521 error_caught = FALSE;
522 XSetErrorHandler (handler);
524 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
525 ("Failed to create output image buffer of %dx%d pixels",
526 ximage->width, ximage->height),
527 ("could not XCreateImage a %dx%d image",
528 ximage->width, ximage->height));
532 /* upstream will assume that rowstrides are multiples of 4, but this
533 * doesn't always seem to be the case with XCreateImage() */
534 if ((ximage->ximage->bytes_per_line % 4) != 0) {
535 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
539 /* we have to use the returned bytes_per_line for our image size */
540 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
542 /* alloc a bit more for unexpected strides to avoid crashes upstream.
543 * FIXME: if we get an unrounded stride, the image will be displayed
544 * distorted, since all upstream elements assume a rounded stride */
546 GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
547 ximage->ximage->height;
548 ximage->ximage->data = g_malloc (allocsize);
549 GST_LOG_OBJECT (ximagesink,
550 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
551 "stride %d", ximage->size, allocsize, ximage->width,
552 ximage->ximage->bytes_per_line);
554 XSync (ximagesink->xcontext->disp, FALSE);
557 /* Reset error handler */
558 error_caught = FALSE;
559 XSetErrorHandler (handler);
563 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
564 GST_BUFFER_SIZE (ximage) = ximage->size;
566 /* Keep a ref to our sink */
567 ximage->ximagesink = gst_object_ref (ximagesink);
569 g_mutex_unlock (ximagesink->x_lock);
572 gst_ximage_buffer_free (ximage);
579 /* This function destroys a GstXImageBuffer handling XShm availability */
581 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
582 GstXImageBuffer * ximage)
584 g_return_if_fail (ximage != NULL);
585 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
587 /* Hold the object lock to ensure the XContext doesn't disappear */
588 GST_OBJECT_LOCK (ximagesink);
590 /* If the destroyed image is the current one we destroy our reference too */
591 if (ximagesink->cur_image == ximage) {
592 ximagesink->cur_image = NULL;
595 /* We might have some buffers destroyed after changing state to NULL */
596 if (!ximagesink->xcontext) {
597 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
599 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
600 shmdt (ximage->SHMInfo.shmaddr);
606 g_mutex_lock (ximagesink->x_lock);
609 if (ximagesink->xcontext->use_xshm) {
610 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
611 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
612 XSync (ximagesink->xcontext->disp, 0);
613 shmdt (ximage->SHMInfo.shmaddr);
616 XDestroyImage (ximage->ximage);
619 #endif /* HAVE_XSHM */
621 if (ximage->ximage) {
622 XDestroyImage (ximage->ximage);
626 XSync (ximagesink->xcontext->disp, FALSE);
628 g_mutex_unlock (ximagesink->x_lock);
631 GST_OBJECT_UNLOCK (ximagesink);
633 if (ximage->ximagesink) {
634 /* Release the ref to our sink */
635 ximage->ximagesink = NULL;
636 gst_object_unref (ximagesink);
642 /* We are called with the x_lock taken */
644 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
645 GstXWindow * xwindow, GstVideoRectangle rect)
647 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
648 g_return_if_fail (xwindow != NULL);
650 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
651 ximagesink->xcontext->black);
655 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
656 0, 0, rect.x, xwindow->height);
660 if ((rect.x + rect.w) < xwindow->width) {
661 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
662 rect.x + rect.w, 0, xwindow->width, xwindow->height);
667 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
668 0, 0, xwindow->width, rect.y);
672 if ((rect.y + rect.h) < xwindow->height) {
673 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
674 0, rect.y + rect.h, xwindow->width, xwindow->height);
678 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
680 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
682 GstVideoRectangle src, dst, result;
683 gboolean draw_border = FALSE;
685 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
687 /* We take the flow_lock. If expose is in there we don't want to run
688 concurrently from the data flow thread */
689 g_mutex_lock (ximagesink->flow_lock);
691 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
692 g_mutex_unlock (ximagesink->flow_lock);
696 /* Draw borders when displaying the first frame. After this
697 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
698 if (!ximagesink->cur_image || ximagesink->draw_border) {
702 /* Store a reference to the last image we put, lose the previous one */
703 if (ximage && ximagesink->cur_image != ximage) {
704 if (ximagesink->cur_image) {
705 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
706 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
708 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
709 ximagesink->cur_image =
710 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
713 /* Expose sends a NULL image, we take the latest frame */
716 if (ximagesink->cur_image) {
717 ximage = ximagesink->cur_image;
719 g_mutex_unlock (ximagesink->flow_lock);
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);
805 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
806 GstXWindow * xwindow, const gchar * media_title)
809 g_free (ximagesink->media_title);
810 ximagesink->media_title = g_strdup (media_title);
813 /* we have a window */
814 if (xwindow->internal) {
815 XTextProperty xproperty;
816 const gchar *app_name;
817 const gchar *title = NULL;
818 gchar *title_mem = NULL;
820 /* set application name as a title */
821 app_name = g_get_application_name ();
823 if (app_name && ximagesink->media_title) {
824 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
826 } else if (app_name) {
828 } else if (ximagesink->media_title) {
829 title = ximagesink->media_title;
833 if ((XStringListToTextProperty (((char **) &title), 1,
835 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
836 XFree (xproperty.value);
845 /* This function handles a GstXWindow creation */
847 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
849 GstXWindow *xwindow = NULL;
852 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
854 xwindow = g_new0 (GstXWindow, 1);
856 xwindow->width = width;
857 xwindow->height = height;
858 xwindow->internal = TRUE;
860 g_mutex_lock (ximagesink->x_lock);
862 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
863 ximagesink->xcontext->root,
864 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
866 /* We have to do that to prevent X from redrawing the background on
867 ConfigureNotify. This takes away flickering of video when resizing. */
868 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
870 /* set application name as a title */
871 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
873 if (ximagesink->handle_events) {
876 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
877 StructureNotifyMask | PointerMotionMask | KeyPressMask |
878 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
880 /* Tell the window manager we'd like delete client messages instead of
882 wm_delete = XInternAtom (ximagesink->xcontext->disp,
883 "WM_DELETE_WINDOW", False);
884 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
888 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
891 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
893 XSync (ximagesink->xcontext->disp, FALSE);
895 g_mutex_unlock (ximagesink->x_lock);
897 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
899 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
904 /* This function destroys a GstXWindow */
906 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
907 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 /* If we did not create that window we just free the GC and let it live */
915 if (xwindow->internal)
916 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
918 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
920 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
922 XSync (ximagesink->xcontext->disp, FALSE);
924 g_mutex_unlock (ximagesink->x_lock);
930 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
932 XWindowAttributes attr;
934 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
936 /* Update the window geometry */
937 g_mutex_lock (ximagesink->x_lock);
938 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
939 g_mutex_unlock (ximagesink->x_lock);
943 XGetWindowAttributes (ximagesink->xcontext->disp,
944 ximagesink->xwindow->win, &attr);
946 ximagesink->xwindow->width = attr.width;
947 ximagesink->xwindow->height = attr.height;
949 g_mutex_unlock (ximagesink->x_lock);
953 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
955 g_return_if_fail (xwindow != NULL);
956 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
958 g_mutex_lock (ximagesink->x_lock);
960 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
961 ximagesink->xcontext->black);
963 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
964 0, 0, xwindow->width, xwindow->height);
966 XSync (ximagesink->xcontext->disp, FALSE);
968 g_mutex_unlock (ximagesink->x_lock);
971 /* This function handles XEvents that might be in the queue. It generates
972 GstEvent that will be sent upstream in the pipeline to handle interactivity
975 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
978 guint pointer_x = 0, pointer_y = 0;
979 gboolean pointer_moved = FALSE;
980 gboolean exposed = FALSE, configured = FALSE;
982 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
984 /* Then we get all pointer motion events, only the last position is
986 g_mutex_lock (ximagesink->flow_lock);
987 g_mutex_lock (ximagesink->x_lock);
988 while (XCheckWindowEvent (ximagesink->xcontext->disp,
989 ximagesink->xwindow->win, PointerMotionMask, &e)) {
990 g_mutex_unlock (ximagesink->x_lock);
991 g_mutex_unlock (ximagesink->flow_lock);
995 pointer_x = e.xmotion.x;
996 pointer_y = e.xmotion.y;
997 pointer_moved = TRUE;
1002 g_mutex_lock (ximagesink->flow_lock);
1003 g_mutex_lock (ximagesink->x_lock);
1006 if (pointer_moved) {
1007 g_mutex_unlock (ximagesink->x_lock);
1008 g_mutex_unlock (ximagesink->flow_lock);
1010 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
1011 pointer_x, pointer_y);
1012 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1013 "mouse-move", 0, pointer_x, pointer_y);
1015 g_mutex_lock (ximagesink->flow_lock);
1016 g_mutex_lock (ximagesink->x_lock);
1019 /* We get all remaining events on our window to throw them upstream */
1020 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1021 ximagesink->xwindow->win,
1022 KeyPressMask | KeyReleaseMask |
1023 ButtonPressMask | ButtonReleaseMask, &e)) {
1026 /* We lock only for the X function call */
1027 g_mutex_unlock (ximagesink->x_lock);
1028 g_mutex_unlock (ximagesink->flow_lock);
1032 /* Mouse button pressed/released over our window. We send upstream
1033 events for interactivity/navigation */
1034 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
1035 e.xbutton.button, e.xbutton.x, e.xbutton.x);
1036 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1037 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1040 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
1041 e.xbutton.button, e.xbutton.x, e.xbutton.x);
1042 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1043 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1047 /* Key pressed/released over our window. We send upstream
1048 events for interactivity/navigation */
1049 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1050 e.xkey.keycode, e.xkey.x, e.xkey.x);
1051 g_mutex_lock (ximagesink->x_lock);
1052 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1054 g_mutex_unlock (ximagesink->x_lock);
1055 if (keysym != NoSymbol) {
1056 char *key_str = NULL;
1058 g_mutex_lock (ximagesink->x_lock);
1059 key_str = XKeysymToString (keysym);
1060 g_mutex_unlock (ximagesink->x_lock);
1061 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1062 e.type == KeyPress ? "key-press" : "key-release", key_str);
1065 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1066 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1070 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1073 g_mutex_lock (ximagesink->flow_lock);
1074 g_mutex_lock (ximagesink->x_lock);
1077 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1078 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1083 case ConfigureNotify:
1084 g_mutex_unlock (ximagesink->x_lock);
1085 gst_ximagesink_xwindow_update_geometry (ximagesink);
1086 g_mutex_lock (ximagesink->x_lock);
1094 if (ximagesink->handle_expose && (exposed || configured)) {
1095 g_mutex_unlock (ximagesink->x_lock);
1096 g_mutex_unlock (ximagesink->flow_lock);
1098 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1100 g_mutex_lock (ximagesink->flow_lock);
1101 g_mutex_lock (ximagesink->x_lock);
1104 /* Handle Display events */
1105 while (XPending (ximagesink->xcontext->disp)) {
1106 XNextEvent (ximagesink->xcontext->disp, &e);
1109 case ClientMessage:{
1112 wm_delete = XInternAtom (ximagesink->xcontext->disp,
1113 "WM_DELETE_WINDOW", False);
1114 if (wm_delete == (Atom) e.xclient.data.l[0]) {
1115 /* Handle window deletion by posting an error on the bus */
1116 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1117 ("Output window was closed"), (NULL));
1119 g_mutex_unlock (ximagesink->x_lock);
1120 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1121 ximagesink->xwindow = NULL;
1122 g_mutex_lock (ximagesink->x_lock);
1131 g_mutex_unlock (ximagesink->x_lock);
1132 g_mutex_unlock (ximagesink->flow_lock);
1136 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1138 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1140 GST_OBJECT_LOCK (ximagesink);
1141 while (ximagesink->running) {
1142 GST_OBJECT_UNLOCK (ximagesink);
1144 if (ximagesink->xwindow) {
1145 gst_ximagesink_handle_xevents (ximagesink);
1147 /* FIXME: do we want to align this with the framerate or anything else? */
1148 g_usleep (G_USEC_PER_SEC / 20);
1150 GST_OBJECT_LOCK (ximagesink);
1152 GST_OBJECT_UNLOCK (ximagesink);
1158 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
1160 GThread *thread = NULL;
1162 /* don't start the thread too early */
1163 if (ximagesink->xcontext == NULL) {
1167 GST_OBJECT_LOCK (ximagesink);
1168 if (ximagesink->handle_expose || ximagesink->handle_events) {
1169 if (!ximagesink->event_thread) {
1170 /* Setup our event listening thread */
1171 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
1172 ximagesink->handle_expose, ximagesink->handle_events);
1173 ximagesink->running = TRUE;
1174 ximagesink->event_thread = g_thread_create (
1175 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1178 if (ximagesink->event_thread) {
1179 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
1180 ximagesink->handle_expose, ximagesink->handle_events);
1181 ximagesink->running = FALSE;
1182 /* grab thread and mark it as NULL */
1183 thread = ximagesink->event_thread;
1184 ximagesink->event_thread = NULL;
1187 GST_OBJECT_UNLOCK (ximagesink);
1189 /* Wait for our event thread to finish */
1191 g_thread_join (thread);
1196 /* This function calculates the pixel aspect ratio based on the properties
1197 * in the xcontext structure and stores it there. */
1199 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1201 static const gint par[][2] = {
1202 {1, 1}, /* regular screen */
1203 {16, 15}, /* PAL TV */
1204 {11, 10}, /* 525 line Rec.601 video */
1205 {54, 59}, /* 625 line Rec.601 video */
1206 {64, 45}, /* 1280x1024 on 16:9 display */
1207 {5, 3}, /* 1280x1024 on 4:3 display */
1208 {4, 3} /* 800x600 on 16:9 display */
1215 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1217 /* first calculate the "real" ratio based on the X values;
1218 * which is the "physical" w/h divided by the w/h in pixels of the display */
1219 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1220 / (xcontext->heightmm * xcontext->width);
1222 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1224 if (xcontext->width == 720 && xcontext->height == 576) {
1225 ratio = 4.0 * 576 / (3.0 * 720);
1227 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1229 /* now find the one from par[][2] with the lowest delta to the real one */
1233 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1234 gdouble this_delta = DELTA (i);
1236 if (this_delta < delta) {
1242 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1243 par[index][0], par[index][1]);
1245 g_free (xcontext->par);
1246 xcontext->par = g_new0 (GValue, 1);
1247 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1248 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1249 GST_DEBUG ("set xcontext PAR to %d/%d",
1250 gst_value_get_fraction_numerator (xcontext->par),
1251 gst_value_get_fraction_denominator (xcontext->par));
1254 /* This function gets the X Display and global info about it. Everything is
1255 stored in our object and will be cleaned when the object is disposed. Note
1256 here that caps for supported format are generated without any window or
1258 static GstXContext *
1259 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1261 GstXContext *xcontext = NULL;
1262 XPixmapFormatValues *px_formats = NULL;
1263 gint nb_formats = 0, i;
1265 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1267 xcontext = g_new0 (GstXContext, 1);
1269 g_mutex_lock (ximagesink->x_lock);
1271 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1273 if (!xcontext->disp) {
1274 g_mutex_unlock (ximagesink->x_lock);
1276 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1277 ("Could not initialise X output"), ("Could not open display"));
1281 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1282 xcontext->screen_num = DefaultScreen (xcontext->disp);
1283 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1284 xcontext->root = DefaultRootWindow (xcontext->disp);
1285 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1286 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1287 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1289 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1290 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1291 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1292 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1294 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1295 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1297 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1299 /* We get supported pixmap formats at supported depth */
1300 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1303 XCloseDisplay (xcontext->disp);
1304 g_mutex_unlock (ximagesink->x_lock);
1305 g_free (xcontext->par);
1307 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1308 ("Could not get supported pixmap formats"), (NULL));
1312 /* We get bpp value corresponding to our running depth */
1313 for (i = 0; i < nb_formats; i++) {
1314 if (px_formats[i].depth == xcontext->depth)
1315 xcontext->bpp = px_formats[i].bits_per_pixel;
1320 xcontext->endianness =
1321 (ImageByteOrder (xcontext->disp) ==
1322 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1324 /* Search for XShm extension support */
1326 if (XShmQueryExtension (xcontext->disp) &&
1327 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1328 xcontext->use_xshm = TRUE;
1329 GST_DEBUG ("ximagesink is using XShm extension");
1333 xcontext->use_xshm = FALSE;
1334 GST_DEBUG ("ximagesink is not using XShm extension");
1337 /* our caps system handles 24/32bpp RGB as big-endian. */
1338 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1339 xcontext->endianness == G_LITTLE_ENDIAN) {
1340 xcontext->endianness = G_BIG_ENDIAN;
1341 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1342 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1343 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1344 if (xcontext->bpp == 24) {
1345 xcontext->visual->red_mask >>= 8;
1346 xcontext->visual->green_mask >>= 8;
1347 xcontext->visual->blue_mask >>= 8;
1351 /* update object's par with calculated one if not set yet */
1352 if (!ximagesink->par) {
1353 ximagesink->par = g_new0 (GValue, 1);
1354 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1355 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1357 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1358 "bpp", G_TYPE_INT, xcontext->bpp,
1359 "depth", G_TYPE_INT, xcontext->depth,
1360 "endianness", G_TYPE_INT, xcontext->endianness,
1361 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1362 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1363 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1364 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1365 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1366 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1367 if (ximagesink->par) {
1370 nom = gst_value_get_fraction_numerator (ximagesink->par);
1371 den = gst_value_get_fraction_denominator (ximagesink->par);
1372 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1373 GST_TYPE_FRACTION, nom, den, NULL);
1376 g_mutex_unlock (ximagesink->x_lock);
1381 /* This function cleans the X context. Closing the Display and unrefing the
1382 caps for supported formats. */
1384 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1386 GstXContext *xcontext;
1388 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1390 GST_OBJECT_LOCK (ximagesink);
1391 if (ximagesink->xcontext == NULL) {
1392 GST_OBJECT_UNLOCK (ximagesink);
1396 /* Take the xcontext reference and NULL it while we
1397 * clean it up, so that any buffer-alloced buffers
1398 * arriving after this will be freed correctly */
1399 xcontext = ximagesink->xcontext;
1400 ximagesink->xcontext = NULL;
1402 GST_OBJECT_UNLOCK (ximagesink);
1404 gst_caps_unref (xcontext->caps);
1405 g_free (xcontext->par);
1406 g_free (ximagesink->par);
1407 ximagesink->par = NULL;
1409 if (xcontext->last_caps)
1410 gst_caps_replace (&xcontext->last_caps, NULL);
1412 g_mutex_lock (ximagesink->x_lock);
1414 XCloseDisplay (xcontext->disp);
1416 g_mutex_unlock (ximagesink->x_lock);
1422 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1425 g_mutex_lock (ximagesink->pool_lock);
1427 while (ximagesink->buffer_pool) {
1428 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1430 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1431 ximagesink->buffer_pool);
1432 gst_ximage_buffer_free (ximage);
1435 g_mutex_unlock (ximagesink->pool_lock);
1441 gst_ximagesink_getcaps (GstBaseSink * bsink)
1443 GstXImageSink *ximagesink;
1447 ximagesink = GST_XIMAGESINK (bsink);
1449 if (ximagesink->xcontext)
1450 return gst_caps_ref (ximagesink->xcontext->caps);
1452 /* get a template copy and add the pixel aspect ratio */
1454 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1455 (ximagesink)->sinkpad));
1456 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1457 GstStructure *structure = gst_caps_get_structure (caps, i);
1459 if (ximagesink->par) {
1462 nom = gst_value_get_fraction_numerator (ximagesink->par);
1463 den = gst_value_get_fraction_denominator (ximagesink->par);
1464 gst_structure_set (structure, "pixel-aspect-ratio",
1465 GST_TYPE_FRACTION, nom, den, NULL);
1473 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1475 GstXImageSink *ximagesink;
1476 gboolean ret = TRUE;
1477 GstStructure *structure;
1479 gint new_width, new_height;
1482 ximagesink = GST_XIMAGESINK (bsink);
1484 if (!ximagesink->xcontext)
1487 GST_DEBUG_OBJECT (ximagesink,
1488 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1489 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1491 /* We intersect those caps with our template to make sure they are correct */
1492 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1493 goto incompatible_caps;
1495 structure = gst_caps_get_structure (caps, 0);
1497 ret &= gst_structure_get_int (structure, "width", &new_width);
1498 ret &= gst_structure_get_int (structure, "height", &new_height);
1499 fps = gst_structure_get_value (structure, "framerate");
1500 ret &= (fps != NULL);
1504 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1505 * otherwise linking should fail */
1506 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1508 if (ximagesink->par) {
1509 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1512 } else if (ximagesink->xcontext->par) {
1513 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1519 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1520 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1521 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1522 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1524 /* Notify application to set xwindow id now */
1525 g_mutex_lock (ximagesink->flow_lock);
1526 if (!ximagesink->xwindow) {
1527 g_mutex_unlock (ximagesink->flow_lock);
1528 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1530 g_mutex_unlock (ximagesink->flow_lock);
1533 /* Creating our window and our image */
1534 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1535 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1536 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1537 ("Invalid image size."));
1541 g_mutex_lock (ximagesink->flow_lock);
1542 if (!ximagesink->xwindow) {
1543 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1544 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1546 /* Remember to draw borders for next frame */
1547 ximagesink->draw_border = TRUE;
1548 g_mutex_unlock (ximagesink->flow_lock);
1550 /* If our ximage has changed we destroy it, next chain iteration will create
1552 if ((ximagesink->ximage) &&
1553 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1554 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1555 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1556 ximagesink->ximage);
1557 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1558 ximagesink->ximage = NULL;
1566 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1571 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1576 static GstStateChangeReturn
1577 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1579 GstXImageSink *ximagesink;
1580 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1581 GstXContext *xcontext = NULL;
1583 ximagesink = GST_XIMAGESINK (element);
1585 switch (transition) {
1586 case GST_STATE_CHANGE_NULL_TO_READY:
1588 /* Initializing the XContext */
1589 if (ximagesink->xcontext == NULL) {
1590 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1591 if (xcontext == NULL) {
1592 ret = GST_STATE_CHANGE_FAILURE;
1595 GST_OBJECT_LOCK (ximagesink);
1597 ximagesink->xcontext = xcontext;
1598 GST_OBJECT_UNLOCK (ximagesink);
1601 /* call XSynchronize with the current value of synchronous */
1602 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1603 ximagesink->synchronous ? "TRUE" : "FALSE");
1604 g_mutex_lock (ximagesink->x_lock);
1605 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1606 g_mutex_unlock (ximagesink->x_lock);
1607 gst_ximagesink_manage_event_thread (ximagesink);
1609 case GST_STATE_CHANGE_READY_TO_PAUSED:
1610 g_mutex_lock (ximagesink->flow_lock);
1611 if (ximagesink->xwindow)
1612 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1613 g_mutex_unlock (ximagesink->flow_lock);
1615 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1621 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1623 switch (transition) {
1624 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1626 case GST_STATE_CHANGE_PAUSED_TO_READY:
1627 ximagesink->fps_n = 0;
1628 ximagesink->fps_d = 1;
1629 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1630 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1632 case GST_STATE_CHANGE_READY_TO_NULL:
1633 gst_ximagesink_reset (ximagesink);
1644 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1645 GstClockTime * start, GstClockTime * end)
1647 GstXImageSink *ximagesink;
1649 ximagesink = GST_XIMAGESINK (bsink);
1651 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1652 *start = GST_BUFFER_TIMESTAMP (buf);
1653 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1654 *end = *start + GST_BUFFER_DURATION (buf);
1656 if (ximagesink->fps_n > 0) {
1658 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1665 static GstFlowReturn
1666 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1668 GstXImageSink *ximagesink;
1670 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1672 ximagesink = GST_XIMAGESINK (vsink);
1674 /* This shouldn't really happen because state changes will fail
1675 * if the xcontext can't be allocated */
1676 if (!ximagesink->xcontext)
1677 return GST_FLOW_ERROR;
1679 /* If this buffer has been allocated using our buffer management we simply
1680 put the ximage which is in the PRIVATE pointer */
1681 if (GST_IS_XIMAGE_BUFFER (buf)) {
1682 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1683 if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1686 /* Else we have to copy the data into our private image, */
1687 /* if we have one... */
1688 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1689 if (!ximagesink->ximage) {
1690 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1691 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1692 GST_BUFFER_CAPS (buf));
1693 if (!ximagesink->ximage)
1694 /* The create method should have posted an informative error */
1697 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1698 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1699 ("Failed to create output image buffer of %dx%d pixels",
1700 ximagesink->ximage->width, ximagesink->ximage->height),
1701 ("XServer allocated buffer size did not match input buffer"));
1703 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1704 ximagesink->ximage = NULL;
1708 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1709 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1710 if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1719 /* No image available. That's very bad ! */
1720 GST_WARNING_OBJECT (ximagesink, "could not create image");
1721 return GST_FLOW_ERROR;
1725 /* No Window available to put our image into */
1726 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1727 return GST_FLOW_ERROR;
1733 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1735 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1737 switch (GST_EVENT_TYPE (event)) {
1738 case GST_EVENT_TAG:{
1740 gchar *title = NULL;
1742 gst_event_parse_tag (event, &l);
1743 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1746 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1747 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1757 if (GST_BASE_SINK_CLASS (parent_class)->event)
1758 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1764 /* Buffer management
1766 * The buffer_alloc function must either return a buffer with given size and
1767 * caps or create a buffer with different caps attached to the buffer. This
1768 * last option is called reverse negotiation, ie, where the sink suggests a
1769 * different format from the upstream peer.
1771 * We try to do reverse negotiation when our geometry changes and we like a
1774 static GstFlowReturn
1775 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1776 GstCaps * caps, GstBuffer ** buf)
1778 GstXImageSink *ximagesink;
1779 GstXImageBuffer *ximage = NULL;
1780 GstStructure *structure = NULL;
1781 GstFlowReturn ret = GST_FLOW_OK;
1782 GstCaps *alloc_caps;
1783 gboolean alloc_unref = FALSE;
1785 GstVideoRectangle dst, src, result;
1786 gboolean caps_accepted = FALSE;
1788 ximagesink = GST_XIMAGESINK (bsink);
1790 if (G_UNLIKELY (!caps)) {
1791 GST_WARNING_OBJECT (ximagesink, "have no caps, doing fallback allocation");
1797 /* This shouldn't really happen because state changes will fail
1798 * if the xcontext can't be allocated */
1799 if (!ximagesink->xcontext)
1800 return GST_FLOW_ERROR;
1802 GST_LOG_OBJECT (ximagesink,
1803 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1804 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1806 /* assume we're going to alloc what was requested, keep track of
1807 * wheter we need to unref or not. When we suggest a new format
1808 * upstream we will create a new caps that we need to unref. */
1810 alloc_unref = FALSE;
1812 /* get struct to see what is requested */
1813 structure = gst_caps_get_structure (caps, 0);
1814 if (!gst_structure_get_int (structure, "width", &width) ||
1815 !gst_structure_get_int (structure, "height", &height)) {
1816 GST_WARNING_OBJECT (ximagesink, "invalid caps for buffer allocation %"
1817 GST_PTR_FORMAT, caps);
1818 ret = GST_FLOW_NOT_NEGOTIATED;
1825 /* We take the flow_lock because the window might go away */
1826 g_mutex_lock (ximagesink->flow_lock);
1827 if (!ximagesink->xwindow) {
1828 g_mutex_unlock (ximagesink->flow_lock);
1832 /* What is our geometry */
1833 dst.w = ximagesink->xwindow->width;
1834 dst.h = ximagesink->xwindow->height;
1836 g_mutex_unlock (ximagesink->flow_lock);
1838 if (ximagesink->keep_aspect) {
1839 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1841 gst_video_sink_center_rect (src, dst, &result, TRUE);
1843 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1844 "ignoring aspect ratio");
1845 result.x = result.y = 0;
1850 /* We would like another geometry */
1851 if (width != result.w || height != result.h) {
1853 GstCaps *desired_caps;
1854 GstStructure *desired_struct;
1856 /* make a copy of the incomming caps to create the new
1857 * suggestion. We can't use make_writable because we might
1858 * then destroy the original caps which we still need when the
1859 * peer does not accept the suggestion. */
1860 desired_caps = gst_caps_copy (caps);
1861 desired_struct = gst_caps_get_structure (desired_caps, 0);
1863 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1864 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1865 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1867 /* PAR property overrides the X calculated one */
1868 if (ximagesink->par) {
1869 nom = gst_value_get_fraction_numerator (ximagesink->par);
1870 den = gst_value_get_fraction_denominator (ximagesink->par);
1871 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1872 GST_TYPE_FRACTION, nom, den, NULL);
1873 } else if (ximagesink->xcontext->par) {
1874 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1875 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1876 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1877 GST_TYPE_FRACTION, nom, den, NULL);
1881 /* see if peer accepts our new suggestion, if there is no peer, this
1882 * function returns true. */
1883 if (!ximagesink->xcontext->last_caps ||
1884 !gst_caps_is_equal (desired_caps, ximagesink->xcontext->last_caps)) {
1886 gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1889 /* Suggestion failed, prevent future attempts for the same caps
1890 * to fail as well. */
1892 gst_caps_replace (&ximagesink->xcontext->last_caps, desired_caps);
1895 if (caps_accepted) {
1896 /* we will not alloc a buffer of the new suggested caps. Make sure
1897 * we also unref this new caps after we set it on the buffer. */
1898 alloc_caps = desired_caps;
1902 GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
1905 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1907 /* we alloc a buffer with the original incomming caps already in the
1908 * width and height variables */
1909 gst_caps_unref (desired_caps);
1914 /* Inspect our buffer pool */
1915 g_mutex_lock (ximagesink->pool_lock);
1916 while (ximagesink->buffer_pool) {
1917 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1920 /* Removing from the pool */
1921 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1922 ximagesink->buffer_pool);
1924 /* If the ximage is invalid for our need, destroy */
1925 if ((ximage->width != width) || (ximage->height != height)) {
1926 gst_ximage_buffer_free (ximage);
1929 /* We found a suitable ximage */
1934 g_mutex_unlock (ximagesink->pool_lock);
1936 /* We haven't found anything, creating a new one */
1938 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1940 /* Now we should have a ximage, set appropriate caps on it */
1942 /* Make sure the buffer is cleared of any previously used flags */
1943 GST_MINI_OBJECT_CAST (ximage)->flags = 0;
1944 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1947 /* could be our new reffed suggestion or the original unreffed caps */
1949 gst_caps_unref (alloc_caps);
1951 *buf = GST_BUFFER_CAST (ximage);
1957 /* Interfaces stuff */
1960 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1962 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY)
1969 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1971 klass->supported = gst_ximagesink_interface_supported;
1975 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1976 GstStructure * structure)
1978 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1980 gint x_offset, y_offset;
1984 event = gst_event_new_navigation (structure);
1986 /* We are not converting the pointer coordinates as there's no hardware
1987 scaling done here. The only possible scaling is done by videoscale and
1988 videoscale will have to catch those events and tranform the coordinates
1989 to match the applied scaling. So here we just add the offset if the image
1990 is centered in the window. */
1992 /* We take the flow_lock while we look at the window */
1993 g_mutex_lock (ximagesink->flow_lock);
1995 if (!ximagesink->xwindow) {
1996 g_mutex_unlock (ximagesink->flow_lock);
2000 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
2001 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
2003 g_mutex_unlock (ximagesink->flow_lock);
2005 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
2007 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
2009 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
2011 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
2014 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
2016 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
2017 gst_pad_send_event (pad, event);
2019 gst_object_unref (pad);
2024 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
2026 iface->send_event = gst_ximagesink_navigation_send_event;
2030 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2032 XID xwindow_id = id;
2033 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2034 GstXWindow *xwindow = NULL;
2035 XWindowAttributes attr;
2037 /* We acquire the stream lock while setting this window in the element.
2038 We are basically cleaning tons of stuff replacing the old window, putting
2039 images while we do that would surely crash */
2040 g_mutex_lock (ximagesink->flow_lock);
2042 /* If we already use that window return */
2043 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
2044 g_mutex_unlock (ximagesink->flow_lock);
2048 /* If the element has not initialized the X11 context try to do so */
2049 if (!ximagesink->xcontext &&
2050 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
2051 g_mutex_unlock (ximagesink->flow_lock);
2052 /* we have thrown a GST_ELEMENT_ERROR now */
2056 /* If a window is there already we destroy it */
2057 if (ximagesink->xwindow) {
2058 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2059 ximagesink->xwindow = NULL;
2062 /* If the xid is 0 we go back to an internal window */
2063 if (xwindow_id == 0) {
2064 /* If no width/height caps nego did not happen window will be created
2065 during caps nego then */
2066 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
2067 xwindow = gst_ximagesink_xwindow_new (ximagesink,
2068 GST_VIDEO_SINK_WIDTH (ximagesink),
2069 GST_VIDEO_SINK_HEIGHT (ximagesink));
2072 xwindow = g_new0 (GstXWindow, 1);
2074 xwindow->win = xwindow_id;
2076 /* We get window geometry, set the event we want to receive,
2078 g_mutex_lock (ximagesink->x_lock);
2079 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
2080 xwindow->width = attr.width;
2081 xwindow->height = attr.height;
2082 xwindow->internal = FALSE;
2083 if (ximagesink->handle_events) {
2084 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
2085 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2089 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
2090 g_mutex_unlock (ximagesink->x_lock);
2094 ximagesink->xwindow = xwindow;
2096 g_mutex_unlock (ximagesink->flow_lock);
2100 gst_ximagesink_expose (GstXOverlay * overlay)
2102 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2104 gst_ximagesink_xwindow_update_geometry (ximagesink);
2105 gst_ximagesink_ximage_put (ximagesink, NULL);
2109 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
2110 gboolean handle_events)
2112 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2114 ximagesink->handle_events = handle_events;
2116 g_mutex_lock (ximagesink->flow_lock);
2118 if (G_UNLIKELY (!ximagesink->xwindow)) {
2119 g_mutex_unlock (ximagesink->flow_lock);
2123 g_mutex_lock (ximagesink->x_lock);
2125 if (handle_events) {
2126 if (ximagesink->xwindow->internal) {
2127 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2128 ExposureMask | StructureNotifyMask | PointerMotionMask |
2129 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2131 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2132 ExposureMask | StructureNotifyMask | PointerMotionMask |
2133 KeyPressMask | KeyReleaseMask);
2136 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
2139 g_mutex_unlock (ximagesink->x_lock);
2141 g_mutex_unlock (ximagesink->flow_lock);
2145 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
2147 iface->set_window_handle = gst_ximagesink_set_window_handle;
2148 iface->expose = gst_ximagesink_expose;
2149 iface->handle_events = gst_ximagesink_set_event_handling;
2152 /* =========================================== */
2154 /* Init & Class init */
2156 /* =========================================== */
2159 gst_ximagesink_set_property (GObject * object, guint prop_id,
2160 const GValue * value, GParamSpec * pspec)
2162 GstXImageSink *ximagesink;
2164 g_return_if_fail (GST_IS_XIMAGESINK (object));
2166 ximagesink = GST_XIMAGESINK (object);
2170 ximagesink->display_name = g_strdup (g_value_get_string (value));
2172 case PROP_SYNCHRONOUS:
2173 ximagesink->synchronous = g_value_get_boolean (value);
2174 if (ximagesink->xcontext) {
2175 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2176 ximagesink->synchronous ? "TRUE" : "FALSE");
2177 g_mutex_lock (ximagesink->x_lock);
2178 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2179 g_mutex_unlock (ximagesink->x_lock);
2182 case PROP_FORCE_ASPECT_RATIO:
2183 ximagesink->keep_aspect = g_value_get_boolean (value);
2185 case PROP_PIXEL_ASPECT_RATIO:
2189 tmp = g_new0 (GValue, 1);
2190 g_value_init (tmp, GST_TYPE_FRACTION);
2192 if (!g_value_transform (value, tmp)) {
2193 GST_WARNING_OBJECT (ximagesink,
2194 "Could not transform string to aspect ratio");
2197 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2198 gst_value_get_fraction_numerator (tmp),
2199 gst_value_get_fraction_denominator (tmp));
2200 g_free (ximagesink->par);
2201 ximagesink->par = tmp;
2205 case PROP_HANDLE_EVENTS:
2206 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2207 g_value_get_boolean (value));
2208 gst_ximagesink_manage_event_thread (ximagesink);
2210 case PROP_HANDLE_EXPOSE:
2211 ximagesink->handle_expose = g_value_get_boolean (value);
2212 gst_ximagesink_manage_event_thread (ximagesink);
2215 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2221 gst_ximagesink_get_property (GObject * object, guint prop_id,
2222 GValue * value, GParamSpec * pspec)
2224 GstXImageSink *ximagesink;
2226 g_return_if_fail (GST_IS_XIMAGESINK (object));
2228 ximagesink = GST_XIMAGESINK (object);
2232 g_value_set_string (value, ximagesink->display_name);
2234 case PROP_SYNCHRONOUS:
2235 g_value_set_boolean (value, ximagesink->synchronous);
2237 case PROP_FORCE_ASPECT_RATIO:
2238 g_value_set_boolean (value, ximagesink->keep_aspect);
2240 case PROP_PIXEL_ASPECT_RATIO:
2241 if (ximagesink->par)
2242 g_value_transform (ximagesink->par, value);
2244 case PROP_HANDLE_EVENTS:
2245 g_value_set_boolean (value, ximagesink->handle_events);
2247 case PROP_HANDLE_EXPOSE:
2248 g_value_set_boolean (value, ximagesink->handle_expose);
2250 case PROP_WINDOW_WIDTH:
2251 if (ximagesink->xwindow)
2252 g_value_set_uint64 (value, ximagesink->xwindow->width);
2254 g_value_set_uint64 (value, 0);
2256 case PROP_WINDOW_HEIGHT:
2257 if (ximagesink->xwindow)
2258 g_value_set_uint64 (value, ximagesink->xwindow->height);
2260 g_value_set_uint64 (value, 0);
2263 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2269 gst_ximagesink_reset (GstXImageSink * ximagesink)
2273 GST_OBJECT_LOCK (ximagesink);
2274 ximagesink->running = FALSE;
2275 /* grab thread and mark it as NULL */
2276 thread = ximagesink->event_thread;
2277 ximagesink->event_thread = NULL;
2278 GST_OBJECT_UNLOCK (ximagesink);
2280 /* Wait for our event thread to finish before we clean up our stuff. */
2282 g_thread_join (thread);
2284 if (ximagesink->ximage) {
2285 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
2286 ximagesink->ximage = NULL;
2288 if (ximagesink->cur_image) {
2289 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
2290 ximagesink->cur_image = NULL;
2293 gst_ximagesink_bufferpool_clear (ximagesink);
2295 g_mutex_lock (ximagesink->flow_lock);
2296 if (ximagesink->xwindow) {
2297 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2298 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2299 ximagesink->xwindow = NULL;
2301 g_mutex_unlock (ximagesink->flow_lock);
2303 gst_ximagesink_xcontext_clear (ximagesink);
2307 gst_ximagesink_finalize (GObject * object)
2309 GstXImageSink *ximagesink;
2311 ximagesink = GST_XIMAGESINK (object);
2313 gst_ximagesink_reset (ximagesink);
2315 if (ximagesink->display_name) {
2316 g_free (ximagesink->display_name);
2317 ximagesink->display_name = NULL;
2319 if (ximagesink->par) {
2320 g_free (ximagesink->par);
2321 ximagesink->par = NULL;
2323 if (ximagesink->x_lock) {
2324 g_mutex_free (ximagesink->x_lock);
2325 ximagesink->x_lock = NULL;
2327 if (ximagesink->flow_lock) {
2328 g_mutex_free (ximagesink->flow_lock);
2329 ximagesink->flow_lock = NULL;
2331 if (ximagesink->pool_lock) {
2332 g_mutex_free (ximagesink->pool_lock);
2333 ximagesink->pool_lock = NULL;
2336 g_free (ximagesink->media_title);
2338 G_OBJECT_CLASS (parent_class)->finalize (object);
2342 gst_ximagesink_init (GstXImageSink * ximagesink)
2344 ximagesink->display_name = NULL;
2345 ximagesink->xcontext = NULL;
2346 ximagesink->xwindow = NULL;
2347 ximagesink->ximage = NULL;
2348 ximagesink->cur_image = NULL;
2350 ximagesink->event_thread = NULL;
2351 ximagesink->running = FALSE;
2353 ximagesink->fps_n = 0;
2354 ximagesink->fps_d = 1;
2356 ximagesink->x_lock = g_mutex_new ();
2357 ximagesink->flow_lock = g_mutex_new ();
2359 ximagesink->par = NULL;
2361 ximagesink->pool_lock = g_mutex_new ();
2362 ximagesink->buffer_pool = NULL;
2364 ximagesink->synchronous = FALSE;
2365 ximagesink->keep_aspect = FALSE;
2366 ximagesink->handle_events = TRUE;
2367 ximagesink->handle_expose = TRUE;
2371 gst_ximagesink_base_init (gpointer g_class)
2373 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2375 gst_element_class_set_details_simple (element_class,
2376 "Video sink", "Sink/Video",
2377 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
2379 gst_element_class_add_pad_template (element_class,
2380 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2384 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2386 GObjectClass *gobject_class;
2387 GstElementClass *gstelement_class;
2388 GstBaseSinkClass *gstbasesink_class;
2389 GstVideoSinkClass *videosink_class;
2391 gobject_class = (GObjectClass *) klass;
2392 gstelement_class = (GstElementClass *) klass;
2393 gstbasesink_class = (GstBaseSinkClass *) klass;
2394 videosink_class = (GstVideoSinkClass *) klass;
2396 parent_class = g_type_class_peek_parent (klass);
2398 gobject_class->finalize = gst_ximagesink_finalize;
2399 gobject_class->set_property = gst_ximagesink_set_property;
2400 gobject_class->get_property = gst_ximagesink_get_property;
2402 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2403 g_param_spec_string ("display", "Display", "X Display name",
2404 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2405 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2406 g_param_spec_boolean ("synchronous", "Synchronous",
2407 "When enabled, runs the X display in synchronous mode. "
2408 "(unrelated to A/V sync, used only for debugging)", FALSE,
2409 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2410 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2411 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2412 "When enabled, reverse caps negotiation (scaling) will respect "
2413 "original aspect ratio", FALSE,
2414 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2415 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2416 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2417 "The pixel aspect ratio of the device", "1/1",
2418 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2419 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2420 g_param_spec_boolean ("handle-events", "Handle XEvents",
2421 "When enabled, XEvents will be selected and handled", TRUE,
2422 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2423 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2424 g_param_spec_boolean ("handle-expose", "Handle expose",
2426 "the current frame will always be drawn in response to X Expose "
2427 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2430 * GstXImageSink:window-width
2432 * Actual width of the video window.
2436 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2437 g_param_spec_uint64 ("window-width", "window-width",
2438 "Width of the window", 0, G_MAXUINT64, 0,
2439 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2442 * GstXImageSink:window-height
2444 * Actual height of the video window.
2448 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2449 g_param_spec_uint64 ("window-height", "window-height",
2450 "Height of the window", 0, G_MAXUINT64, 0,
2451 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2453 gstelement_class->change_state = gst_ximagesink_change_state;
2455 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2456 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2457 gstbasesink_class->buffer_alloc =
2458 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2459 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2460 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
2462 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2465 /* ============================================================= */
2467 /* Public Methods */
2469 /* ============================================================= */
2471 /* =========================================== */
2473 /* Object typing & Creation */
2475 /* =========================================== */
2478 gst_ximagesink_get_type (void)
2480 static GType ximagesink_type = 0;
2482 if (!ximagesink_type) {
2483 static const GTypeInfo ximagesink_info = {
2484 sizeof (GstXImageSinkClass),
2485 gst_ximagesink_base_init,
2487 (GClassInitFunc) gst_ximagesink_class_init,
2490 sizeof (GstXImageSink), 0, (GInstanceInitFunc) gst_ximagesink_init,
2492 static const GInterfaceInfo iface_info = {
2493 (GInterfaceInitFunc) gst_ximagesink_interface_init, NULL, NULL,
2495 static const GInterfaceInfo navigation_info = {
2496 (GInterfaceInitFunc) gst_ximagesink_navigation_init, NULL, NULL,
2498 static const GInterfaceInfo overlay_info = {
2499 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init, NULL, NULL,
2502 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2503 "GstXImageSink", &ximagesink_info, 0);
2505 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2507 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2509 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2512 /* register type and create class in a more safe place instead of at
2513 * runtime since the type registration and class creation is not
2515 g_type_class_ref (gst_ximage_buffer_get_type ());
2518 return ximagesink_type;