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 #include "gst/glib-compat-private.h"
118 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
119 #define GST_CAT_DEFAULT gst_debug_ximagesink
124 unsigned long functions;
125 unsigned long decorations;
127 unsigned long status;
129 MotifWmHints, MwmHints;
131 #define MWM_HINTS_DECORATIONS (1L << 1)
133 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
134 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
135 GstXImageBuffer * ximage);
136 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
137 static void gst_ximagesink_expose (GstXOverlay * overlay);
139 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
140 GST_STATIC_PAD_TEMPLATE ("sink",
143 GST_STATIC_CAPS ("video/x-raw-rgb, "
144 "framerate = (fraction) [ 0, MAX ], "
145 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
153 PROP_PIXEL_ASPECT_RATIO,
154 PROP_FORCE_ASPECT_RATIO,
161 static GstVideoSinkClass *parent_class = NULL;
163 /* ============================================================= */
165 /* Private Methods */
167 /* ============================================================= */
171 static GstBufferClass *ximage_buffer_parent_class = NULL;
173 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
175 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
176 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
177 #define GST_XIMAGE_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
179 /* So some words about GstMiniObject, this is pretty messy...
180 GstMiniObject does not use the standard finalizing of GObjects, you are
181 supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
182 which will handle its own refcount system and call gst_mini_object_free.
183 gst_mini_object_free will call the class finalize method which is not the
184 one from GObject, after calling this finalize method it will free the object
185 instance for you if the refcount is still 0 so you should not chain up */
187 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
189 GstXImageSink *ximagesink = NULL;
190 gboolean recycled = FALSE;
193 g_return_if_fail (ximage != NULL);
195 ximagesink = ximage->ximagesink;
196 if (G_UNLIKELY (ximagesink == NULL)) {
197 GST_WARNING_OBJECT (ximagesink, "no sink found");
201 GST_OBJECT_LOCK (ximagesink);
202 running = ximagesink->running;
203 GST_OBJECT_UNLOCK (ximagesink);
205 if (running == FALSE) {
206 /* If the sink is shutting down, need to clear the image */
207 GST_DEBUG_OBJECT (ximagesink,
208 "destroy image %p because the sink is shutting down", ximage);
209 gst_ximagesink_ximage_destroy (ximagesink, ximage);
210 } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
211 (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
212 /* If our geometry changed we can't reuse that image. */
213 GST_DEBUG_OBJECT (ximagesink,
214 "destroy image %p as its size changed %dx%d vs current %dx%d",
215 ximage, ximage->width, ximage->height,
216 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
217 gst_ximagesink_ximage_destroy (ximagesink, ximage);
219 /* In that case we can reuse the image and add it to our image pool. */
220 GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
221 /* need to increment the refcount again to recycle */
222 gst_buffer_ref (GST_BUFFER_CAST (ximage));
223 g_mutex_lock (ximagesink->pool_lock);
224 ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
225 g_mutex_unlock (ximagesink->pool_lock);
230 GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->finalize
231 (GST_MINI_OBJECT (ximage));
238 gst_ximage_buffer_free (GstXImageBuffer * ximage)
240 /* make sure it is not recycled */
243 gst_buffer_unref (GST_BUFFER_CAST (ximage));
247 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
250 ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
251 ximage_buffer->SHMInfo.shmid = -1;
256 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
258 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
260 ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
262 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
263 gst_ximage_buffer_finalize;
267 gst_ximage_buffer_get_type (void)
269 static GType _gst_ximage_buffer_type;
271 if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
272 static const GTypeInfo ximage_buffer_info = {
273 sizeof (GstBufferClass),
276 gst_ximage_buffer_class_init,
279 sizeof (GstXImageBuffer),
281 (GInstanceInitFunc) gst_ximage_buffer_init,
284 _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
285 "GstXImageBuffer", &ximage_buffer_info, 0);
287 return _gst_ximage_buffer_type;
292 static gboolean error_caught = FALSE;
295 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
297 char error_msg[1024];
299 XGetErrorText (display, xevent->error_code, error_msg, 1024);
300 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
305 #ifdef HAVE_XSHM /* Check that XShm calls actually work */
308 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
309 GstXContext * xcontext)
312 XShmSegmentInfo SHMInfo;
314 int (*handler) (Display *, XErrorEvent *);
315 gboolean result = FALSE;
316 gboolean did_attach = FALSE;
318 g_return_val_if_fail (xcontext != NULL, FALSE);
320 /* Sync to ensure any older errors are already processed */
321 XSync (xcontext->disp, FALSE);
323 /* Set defaults so we don't free these later unnecessarily */
324 SHMInfo.shmaddr = ((void *) -1);
327 /* Setting an error handler to catch failure */
328 error_caught = FALSE;
329 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
331 /* Trying to create a 1x1 ximage */
332 GST_DEBUG ("XShmCreateImage of 1x1");
334 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
335 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
337 /* Might cause an error, sync to ensure it is noticed */
338 XSync (xcontext->disp, FALSE);
339 if (!ximage || error_caught) {
340 GST_WARNING ("could not XShmCreateImage a 1x1 image");
343 size = ximage->height * ximage->bytes_per_line;
345 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
346 if (SHMInfo.shmid == -1) {
347 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
352 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
353 if (SHMInfo.shmaddr == ((void *) -1)) {
354 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
355 /* Clean up shm seg */
356 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
360 ximage->data = SHMInfo.shmaddr;
361 SHMInfo.readOnly = FALSE;
363 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
364 GST_WARNING ("Failed to XShmAttach");
365 /* Clean up shm seg */
366 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
370 /* Sync to ensure we see any errors we caused */
371 XSync (xcontext->disp, FALSE);
373 /* Delete the shared memory segment as soon as everyone is attached.
374 * This way, it will be deleted as soon as we detach later, and not
375 * leaked if we crash. */
376 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
380 /* store whether we succeeded in result */
385 /* Sync to ensure we swallow any errors we caused and reset error_caught */
386 XSync (xcontext->disp, FALSE);
387 error_caught = FALSE;
388 XSetErrorHandler (handler);
391 XShmDetach (xcontext->disp, &SHMInfo);
392 XSync (xcontext->disp, FALSE);
394 if (SHMInfo.shmaddr != ((void *) -1))
395 shmdt (SHMInfo.shmaddr);
397 XDestroyImage (ximage);
400 #endif /* HAVE_XSHM */
402 /* This function handles GstXImageBuffer creation depending on XShm availability */
403 static GstXImageBuffer *
404 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
406 GstXImageBuffer *ximage = NULL;
407 GstStructure *structure = NULL;
408 gboolean succeeded = FALSE;
409 int (*handler) (Display *, XErrorEvent *);
411 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
413 ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
415 structure = gst_caps_get_structure (caps, 0);
417 if (!gst_structure_get_int (structure, "width", &ximage->width) ||
418 !gst_structure_get_int (structure, "height", &ximage->height)) {
419 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
422 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
423 ximage->width, ximage->height);
425 g_mutex_lock (ximagesink->x_lock);
427 /* Setting an error handler to catch failure */
428 error_caught = FALSE;
429 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
432 if (ximagesink->xcontext->use_xshm) {
433 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
434 ximagesink->xcontext->visual,
435 ximagesink->xcontext->depth,
436 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
437 if (!ximage->ximage || error_caught) {
438 g_mutex_unlock (ximagesink->x_lock);
440 /* Reset error flag */
441 error_caught = FALSE;
444 GST_ELEMENT_WARNING (ximagesink, RESOURCE, WRITE,
445 ("Failed to create output image buffer of %dx%d pixels",
446 ximage->width, ximage->height),
447 ("could not XShmCreateImage a %dx%d image",
448 ximage->width, ximage->height));
450 /* Retry without XShm */
451 ximagesink->xcontext->use_xshm = FALSE;
453 /* Hold X mutex again to try without XShm */
454 g_mutex_lock (ximagesink->x_lock);
458 /* we have to use the returned bytes_per_line for our shm size */
459 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
460 GST_LOG_OBJECT (ximagesink,
461 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
462 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
464 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
466 if (ximage->SHMInfo.shmid == -1) {
467 g_mutex_unlock (ximagesink->x_lock);
468 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
469 ("Failed to create output image buffer of %dx%d pixels",
470 ximage->width, ximage->height),
471 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
476 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
477 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
478 g_mutex_unlock (ximagesink->x_lock);
479 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
480 ("Failed to create output image buffer of %dx%d pixels",
481 ximage->width, ximage->height),
482 ("Failed to shmat: %s", g_strerror (errno)));
483 /* Clean up the shared memory segment */
484 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
488 ximage->ximage->data = ximage->SHMInfo.shmaddr;
489 ximage->SHMInfo.readOnly = FALSE;
491 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
492 /* Clean up shm seg */
493 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
495 g_mutex_unlock (ximagesink->x_lock);
496 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
497 ("Failed to create output image buffer of %dx%d pixels",
498 ximage->width, ximage->height), ("Failed to XShmAttach"));
502 XSync (ximagesink->xcontext->disp, FALSE);
504 /* Now that everyone has attached, we can delete the shared memory segment.
505 * This way, it will be deleted as soon as we detach later, and not
506 * leaked if we crash. */
507 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
511 #endif /* HAVE_XSHM */
515 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
516 ximagesink->xcontext->visual,
517 ximagesink->xcontext->depth,
519 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
520 if (!ximage->ximage || error_caught) {
521 g_mutex_unlock (ximagesink->x_lock);
522 /* Reset error handler */
523 error_caught = FALSE;
524 XSetErrorHandler (handler);
526 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
527 ("Failed to create output image buffer of %dx%d pixels",
528 ximage->width, ximage->height),
529 ("could not XCreateImage a %dx%d image",
530 ximage->width, ximage->height));
534 /* upstream will assume that rowstrides are multiples of 4, but this
535 * doesn't always seem to be the case with XCreateImage() */
536 if ((ximage->ximage->bytes_per_line % 4) != 0) {
537 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
541 /* we have to use the returned bytes_per_line for our image size */
542 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
544 /* alloc a bit more for unexpected strides to avoid crashes upstream.
545 * FIXME: if we get an unrounded stride, the image will be displayed
546 * distorted, since all upstream elements assume a rounded stride */
548 GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
549 ximage->ximage->height;
550 ximage->ximage->data = g_malloc (allocsize);
551 GST_LOG_OBJECT (ximagesink,
552 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
553 "stride %d", ximage->size, allocsize, ximage->width,
554 ximage->ximage->bytes_per_line);
556 XSync (ximagesink->xcontext->disp, FALSE);
559 /* Reset error handler */
560 error_caught = FALSE;
561 XSetErrorHandler (handler);
565 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
566 GST_BUFFER_SIZE (ximage) = ximage->size;
568 /* Keep a ref to our sink */
569 ximage->ximagesink = gst_object_ref (ximagesink);
571 g_mutex_unlock (ximagesink->x_lock);
574 gst_ximage_buffer_free (ximage);
581 /* This function destroys a GstXImageBuffer handling XShm availability */
583 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
584 GstXImageBuffer * ximage)
586 g_return_if_fail (ximage != NULL);
587 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
589 /* Hold the object lock to ensure the XContext doesn't disappear */
590 GST_OBJECT_LOCK (ximagesink);
592 /* If the destroyed image is the current one we destroy our reference too */
593 if (ximagesink->cur_image == ximage) {
594 ximagesink->cur_image = NULL;
597 /* We might have some buffers destroyed after changing state to NULL */
598 if (!ximagesink->xcontext) {
599 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
601 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
602 shmdt (ximage->SHMInfo.shmaddr);
608 g_mutex_lock (ximagesink->x_lock);
611 if (ximagesink->xcontext->use_xshm) {
612 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
613 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
614 XSync (ximagesink->xcontext->disp, 0);
615 shmdt (ximage->SHMInfo.shmaddr);
618 XDestroyImage (ximage->ximage);
621 #endif /* HAVE_XSHM */
623 if (ximage->ximage) {
624 XDestroyImage (ximage->ximage);
628 XSync (ximagesink->xcontext->disp, FALSE);
630 g_mutex_unlock (ximagesink->x_lock);
633 GST_OBJECT_UNLOCK (ximagesink);
635 if (ximage->ximagesink) {
636 /* Release the ref to our sink */
637 ximage->ximagesink = NULL;
638 gst_object_unref (ximagesink);
644 /* We are called with the x_lock taken */
646 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
647 GstXWindow * xwindow, GstVideoRectangle rect)
649 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
650 g_return_if_fail (xwindow != NULL);
652 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
653 ximagesink->xcontext->black);
657 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
658 0, 0, rect.x, xwindow->height);
662 if ((rect.x + rect.w) < xwindow->width) {
663 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
664 rect.x + rect.w, 0, xwindow->width, xwindow->height);
669 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
670 0, 0, xwindow->width, rect.y);
674 if ((rect.y + rect.h) < xwindow->height) {
675 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
676 0, rect.y + rect.h, xwindow->width, xwindow->height);
680 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
682 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
684 GstVideoRectangle src, dst, result;
685 gboolean draw_border = FALSE;
687 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
689 /* We take the flow_lock. If expose is in there we don't want to run
690 concurrently from the data flow thread */
691 g_mutex_lock (ximagesink->flow_lock);
693 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
694 g_mutex_unlock (ximagesink->flow_lock);
698 /* Draw borders when displaying the first frame. After this
699 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
700 if (!ximagesink->cur_image || ximagesink->draw_border) {
704 /* Store a reference to the last image we put, lose the previous one */
705 if (ximage && ximagesink->cur_image != ximage) {
706 if (ximagesink->cur_image) {
707 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
708 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
710 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
711 ximagesink->cur_image =
712 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
715 /* Expose sends a NULL image, we take the latest frame */
718 if (ximagesink->cur_image) {
719 ximage = ximagesink->cur_image;
721 g_mutex_unlock (ximagesink->flow_lock);
726 src.w = ximage->width;
727 src.h = ximage->height;
728 dst.w = ximagesink->xwindow->width;
729 dst.h = ximagesink->xwindow->height;
731 gst_video_sink_center_rect (src, dst, &result, FALSE);
733 g_mutex_lock (ximagesink->x_lock);
736 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
738 ximagesink->draw_border = FALSE;
741 if (ximagesink->xcontext->use_xshm) {
742 GST_LOG_OBJECT (ximagesink,
743 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
744 ximage, 0, 0, result.x, result.y, result.w, result.h,
745 ximagesink->xwindow->width, ximagesink->xwindow->height);
746 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
747 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
748 result.w, result.h, FALSE);
750 #endif /* HAVE_XSHM */
752 GST_LOG_OBJECT (ximagesink,
753 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
754 ximage, 0, 0, result.x, result.y, result.w, result.h,
755 ximagesink->xwindow->width, ximagesink->xwindow->height);
756 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
757 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
761 XSync (ximagesink->xcontext->disp, FALSE);
763 g_mutex_unlock (ximagesink->x_lock);
765 g_mutex_unlock (ximagesink->flow_lock);
771 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
774 Atom hints_atom = None;
777 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
778 g_return_val_if_fail (window != NULL, FALSE);
780 g_mutex_lock (ximagesink->x_lock);
782 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
783 if (hints_atom == None) {
784 g_mutex_unlock (ximagesink->x_lock);
788 hints = g_malloc0 (sizeof (MotifWmHints));
790 hints->flags |= MWM_HINTS_DECORATIONS;
791 hints->decorations = 1 << 0;
793 XChangeProperty (ximagesink->xcontext->disp, window->win,
794 hints_atom, hints_atom, 32, PropModeReplace,
795 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
797 XSync (ximagesink->xcontext->disp, FALSE);
799 g_mutex_unlock (ximagesink->x_lock);
807 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
808 GstXWindow * xwindow, const gchar * media_title)
811 g_free (ximagesink->media_title);
812 ximagesink->media_title = g_strdup (media_title);
815 /* we have a window */
816 if (xwindow->internal) {
817 XTextProperty xproperty;
818 const gchar *app_name;
819 const gchar *title = NULL;
820 gchar *title_mem = NULL;
822 /* set application name as a title */
823 app_name = g_get_application_name ();
825 if (app_name && ximagesink->media_title) {
826 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
828 } else if (app_name) {
830 } else if (ximagesink->media_title) {
831 title = ximagesink->media_title;
835 if ((XStringListToTextProperty (((char **) &title), 1,
837 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
838 XFree (xproperty.value);
847 /* This function handles a GstXWindow creation */
849 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
851 GstXWindow *xwindow = NULL;
854 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
856 xwindow = g_new0 (GstXWindow, 1);
858 xwindow->width = width;
859 xwindow->height = height;
860 xwindow->internal = TRUE;
862 g_mutex_lock (ximagesink->x_lock);
864 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
865 ximagesink->xcontext->root,
866 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
868 /* We have to do that to prevent X from redrawing the background on
869 ConfigureNotify. This takes away flickering of video when resizing. */
870 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
872 /* set application name as a title */
873 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
875 if (ximagesink->handle_events) {
878 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
879 StructureNotifyMask | PointerMotionMask | KeyPressMask |
880 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
882 /* Tell the window manager we'd like delete client messages instead of
884 wm_delete = XInternAtom (ximagesink->xcontext->disp,
885 "WM_DELETE_WINDOW", False);
886 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
890 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
893 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
895 XSync (ximagesink->xcontext->disp, FALSE);
897 g_mutex_unlock (ximagesink->x_lock);
899 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
901 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
906 /* This function destroys a GstXWindow */
908 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
909 GstXWindow * xwindow)
911 g_return_if_fail (xwindow != NULL);
912 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
914 g_mutex_lock (ximagesink->x_lock);
916 /* If we did not create that window we just free the GC and let it live */
917 if (xwindow->internal)
918 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
920 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
922 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
924 XSync (ximagesink->xcontext->disp, FALSE);
926 g_mutex_unlock (ximagesink->x_lock);
932 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
934 XWindowAttributes attr;
936 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
938 /* Update the window geometry */
939 g_mutex_lock (ximagesink->x_lock);
940 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
941 g_mutex_unlock (ximagesink->x_lock);
945 XGetWindowAttributes (ximagesink->xcontext->disp,
946 ximagesink->xwindow->win, &attr);
948 ximagesink->xwindow->width = attr.width;
949 ximagesink->xwindow->height = attr.height;
951 g_mutex_unlock (ximagesink->x_lock);
955 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
957 g_return_if_fail (xwindow != NULL);
958 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
960 g_mutex_lock (ximagesink->x_lock);
962 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
963 ximagesink->xcontext->black);
965 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
966 0, 0, xwindow->width, xwindow->height);
968 XSync (ximagesink->xcontext->disp, FALSE);
970 g_mutex_unlock (ximagesink->x_lock);
973 /* This function handles XEvents that might be in the queue. It generates
974 GstEvent that will be sent upstream in the pipeline to handle interactivity
977 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
980 guint pointer_x = 0, pointer_y = 0;
981 gboolean pointer_moved = FALSE;
982 gboolean exposed = FALSE, configured = FALSE;
984 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
986 /* Then we get all pointer motion events, only the last position is
988 g_mutex_lock (ximagesink->flow_lock);
989 g_mutex_lock (ximagesink->x_lock);
990 while (XCheckWindowEvent (ximagesink->xcontext->disp,
991 ximagesink->xwindow->win, PointerMotionMask, &e)) {
992 g_mutex_unlock (ximagesink->x_lock);
993 g_mutex_unlock (ximagesink->flow_lock);
997 pointer_x = e.xmotion.x;
998 pointer_y = e.xmotion.y;
999 pointer_moved = TRUE;
1004 g_mutex_lock (ximagesink->flow_lock);
1005 g_mutex_lock (ximagesink->x_lock);
1008 if (pointer_moved) {
1009 g_mutex_unlock (ximagesink->x_lock);
1010 g_mutex_unlock (ximagesink->flow_lock);
1012 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
1013 pointer_x, pointer_y);
1014 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1015 "mouse-move", 0, pointer_x, pointer_y);
1017 g_mutex_lock (ximagesink->flow_lock);
1018 g_mutex_lock (ximagesink->x_lock);
1021 /* We get all remaining events on our window to throw them upstream */
1022 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1023 ximagesink->xwindow->win,
1024 KeyPressMask | KeyReleaseMask |
1025 ButtonPressMask | ButtonReleaseMask, &e)) {
1028 /* We lock only for the X function call */
1029 g_mutex_unlock (ximagesink->x_lock);
1030 g_mutex_unlock (ximagesink->flow_lock);
1034 /* Mouse button pressed/released over our window. We send upstream
1035 events for interactivity/navigation */
1036 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
1037 e.xbutton.button, e.xbutton.x, e.xbutton.x);
1038 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1039 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1042 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
1043 e.xbutton.button, e.xbutton.x, e.xbutton.x);
1044 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1045 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1049 /* Key pressed/released over our window. We send upstream
1050 events for interactivity/navigation */
1051 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1052 e.xkey.keycode, e.xkey.x, e.xkey.x);
1053 g_mutex_lock (ximagesink->x_lock);
1054 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1056 g_mutex_unlock (ximagesink->x_lock);
1057 if (keysym != NoSymbol) {
1058 char *key_str = NULL;
1060 g_mutex_lock (ximagesink->x_lock);
1061 key_str = XKeysymToString (keysym);
1062 g_mutex_unlock (ximagesink->x_lock);
1063 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1064 e.type == KeyPress ? "key-press" : "key-release", key_str);
1067 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1068 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1072 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1075 g_mutex_lock (ximagesink->flow_lock);
1076 g_mutex_lock (ximagesink->x_lock);
1079 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1080 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1085 case ConfigureNotify:
1086 g_mutex_unlock (ximagesink->x_lock);
1087 gst_ximagesink_xwindow_update_geometry (ximagesink);
1088 g_mutex_lock (ximagesink->x_lock);
1096 if (ximagesink->handle_expose && (exposed || configured)) {
1097 g_mutex_unlock (ximagesink->x_lock);
1098 g_mutex_unlock (ximagesink->flow_lock);
1100 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1102 g_mutex_lock (ximagesink->flow_lock);
1103 g_mutex_lock (ximagesink->x_lock);
1106 /* Handle Display events */
1107 while (XPending (ximagesink->xcontext->disp)) {
1108 XNextEvent (ximagesink->xcontext->disp, &e);
1111 case ClientMessage:{
1114 wm_delete = XInternAtom (ximagesink->xcontext->disp,
1115 "WM_DELETE_WINDOW", False);
1116 if (wm_delete == (Atom) e.xclient.data.l[0]) {
1117 /* Handle window deletion by posting an error on the bus */
1118 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1119 ("Output window was closed"), (NULL));
1121 g_mutex_unlock (ximagesink->x_lock);
1122 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1123 ximagesink->xwindow = NULL;
1124 g_mutex_lock (ximagesink->x_lock);
1133 g_mutex_unlock (ximagesink->x_lock);
1134 g_mutex_unlock (ximagesink->flow_lock);
1138 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1140 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1142 GST_OBJECT_LOCK (ximagesink);
1143 while (ximagesink->running) {
1144 GST_OBJECT_UNLOCK (ximagesink);
1146 if (ximagesink->xwindow) {
1147 gst_ximagesink_handle_xevents (ximagesink);
1149 /* FIXME: do we want to align this with the framerate or anything else? */
1150 g_usleep (G_USEC_PER_SEC / 20);
1152 GST_OBJECT_LOCK (ximagesink);
1154 GST_OBJECT_UNLOCK (ximagesink);
1160 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
1162 GThread *thread = NULL;
1164 /* don't start the thread too early */
1165 if (ximagesink->xcontext == NULL) {
1169 GST_OBJECT_LOCK (ximagesink);
1170 if (ximagesink->handle_expose || ximagesink->handle_events) {
1171 if (!ximagesink->event_thread) {
1172 /* Setup our event listening thread */
1173 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
1174 ximagesink->handle_expose, ximagesink->handle_events);
1175 ximagesink->running = TRUE;
1176 #if !GLIB_CHECK_VERSION (2, 31, 0)
1177 ximagesink->event_thread = g_thread_create (
1178 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1180 ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
1181 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, NULL);
1185 if (ximagesink->event_thread) {
1186 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
1187 ximagesink->handle_expose, ximagesink->handle_events);
1188 ximagesink->running = FALSE;
1189 /* grab thread and mark it as NULL */
1190 thread = ximagesink->event_thread;
1191 ximagesink->event_thread = NULL;
1194 GST_OBJECT_UNLOCK (ximagesink);
1196 /* Wait for our event thread to finish */
1198 g_thread_join (thread);
1203 /* This function calculates the pixel aspect ratio based on the properties
1204 * in the xcontext structure and stores it there. */
1206 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1208 static const gint par[][2] = {
1209 {1, 1}, /* regular screen */
1210 {16, 15}, /* PAL TV */
1211 {11, 10}, /* 525 line Rec.601 video */
1212 {54, 59}, /* 625 line Rec.601 video */
1213 {64, 45}, /* 1280x1024 on 16:9 display */
1214 {5, 3}, /* 1280x1024 on 4:3 display */
1215 {4, 3} /* 800x600 on 16:9 display */
1222 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1224 /* first calculate the "real" ratio based on the X values;
1225 * which is the "physical" w/h divided by the w/h in pixels of the display */
1226 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1227 / (xcontext->heightmm * xcontext->width);
1229 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1231 if (xcontext->width == 720 && xcontext->height == 576) {
1232 ratio = 4.0 * 576 / (3.0 * 720);
1234 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1236 /* now find the one from par[][2] with the lowest delta to the real one */
1240 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1241 gdouble this_delta = DELTA (i);
1243 if (this_delta < delta) {
1249 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1250 par[index][0], par[index][1]);
1252 g_free (xcontext->par);
1253 xcontext->par = g_new0 (GValue, 1);
1254 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1255 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1256 GST_DEBUG ("set xcontext PAR to %d/%d",
1257 gst_value_get_fraction_numerator (xcontext->par),
1258 gst_value_get_fraction_denominator (xcontext->par));
1261 /* This function gets the X Display and global info about it. Everything is
1262 stored in our object and will be cleaned when the object is disposed. Note
1263 here that caps for supported format are generated without any window or
1265 static GstXContext *
1266 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1268 GstXContext *xcontext = NULL;
1269 XPixmapFormatValues *px_formats = NULL;
1270 gint nb_formats = 0, i;
1272 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1274 xcontext = g_new0 (GstXContext, 1);
1276 g_mutex_lock (ximagesink->x_lock);
1278 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1280 if (!xcontext->disp) {
1281 g_mutex_unlock (ximagesink->x_lock);
1283 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1284 ("Could not initialise X output"), ("Could not open display"));
1288 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1289 xcontext->screen_num = DefaultScreen (xcontext->disp);
1290 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1291 xcontext->root = DefaultRootWindow (xcontext->disp);
1292 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1293 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1294 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1296 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1297 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1298 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1299 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1301 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1302 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1304 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1306 /* We get supported pixmap formats at supported depth */
1307 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1310 XCloseDisplay (xcontext->disp);
1311 g_mutex_unlock (ximagesink->x_lock);
1312 g_free (xcontext->par);
1314 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1315 ("Could not get supported pixmap formats"), (NULL));
1319 /* We get bpp value corresponding to our running depth */
1320 for (i = 0; i < nb_formats; i++) {
1321 if (px_formats[i].depth == xcontext->depth)
1322 xcontext->bpp = px_formats[i].bits_per_pixel;
1327 xcontext->endianness =
1328 (ImageByteOrder (xcontext->disp) ==
1329 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1331 /* Search for XShm extension support */
1333 if (XShmQueryExtension (xcontext->disp) &&
1334 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1335 xcontext->use_xshm = TRUE;
1336 GST_DEBUG ("ximagesink is using XShm extension");
1340 xcontext->use_xshm = FALSE;
1341 GST_DEBUG ("ximagesink is not using XShm extension");
1344 /* our caps system handles 24/32bpp RGB as big-endian. */
1345 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1346 xcontext->endianness == G_LITTLE_ENDIAN) {
1347 xcontext->endianness = G_BIG_ENDIAN;
1348 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1349 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1350 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1351 if (xcontext->bpp == 24) {
1352 xcontext->visual->red_mask >>= 8;
1353 xcontext->visual->green_mask >>= 8;
1354 xcontext->visual->blue_mask >>= 8;
1358 /* update object's par with calculated one if not set yet */
1359 if (!ximagesink->par) {
1360 ximagesink->par = g_new0 (GValue, 1);
1361 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1362 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1364 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1365 "bpp", G_TYPE_INT, xcontext->bpp,
1366 "depth", G_TYPE_INT, xcontext->depth,
1367 "endianness", G_TYPE_INT, xcontext->endianness,
1368 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1369 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1370 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1371 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1372 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1373 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1374 if (ximagesink->par) {
1377 nom = gst_value_get_fraction_numerator (ximagesink->par);
1378 den = gst_value_get_fraction_denominator (ximagesink->par);
1379 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1380 GST_TYPE_FRACTION, nom, den, NULL);
1383 g_mutex_unlock (ximagesink->x_lock);
1388 /* This function cleans the X context. Closing the Display and unrefing the
1389 caps for supported formats. */
1391 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1393 GstXContext *xcontext;
1395 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1397 GST_OBJECT_LOCK (ximagesink);
1398 if (ximagesink->xcontext == NULL) {
1399 GST_OBJECT_UNLOCK (ximagesink);
1403 /* Take the xcontext reference and NULL it while we
1404 * clean it up, so that any buffer-alloced buffers
1405 * arriving after this will be freed correctly */
1406 xcontext = ximagesink->xcontext;
1407 ximagesink->xcontext = NULL;
1409 GST_OBJECT_UNLOCK (ximagesink);
1411 gst_caps_unref (xcontext->caps);
1412 g_free (xcontext->par);
1413 g_free (ximagesink->par);
1414 ximagesink->par = NULL;
1416 if (xcontext->last_caps)
1417 gst_caps_replace (&xcontext->last_caps, NULL);
1419 g_mutex_lock (ximagesink->x_lock);
1421 XCloseDisplay (xcontext->disp);
1423 g_mutex_unlock (ximagesink->x_lock);
1429 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1432 g_mutex_lock (ximagesink->pool_lock);
1434 while (ximagesink->buffer_pool) {
1435 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1437 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1438 ximagesink->buffer_pool);
1439 gst_ximage_buffer_free (ximage);
1442 g_mutex_unlock (ximagesink->pool_lock);
1448 gst_ximagesink_getcaps (GstBaseSink * bsink)
1450 GstXImageSink *ximagesink;
1454 ximagesink = GST_XIMAGESINK (bsink);
1456 if (ximagesink->xcontext)
1457 return gst_caps_ref (ximagesink->xcontext->caps);
1459 /* get a template copy and add the pixel aspect ratio */
1461 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1462 (ximagesink)->sinkpad));
1463 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1464 GstStructure *structure = gst_caps_get_structure (caps, i);
1466 if (ximagesink->par) {
1469 nom = gst_value_get_fraction_numerator (ximagesink->par);
1470 den = gst_value_get_fraction_denominator (ximagesink->par);
1471 gst_structure_set (structure, "pixel-aspect-ratio",
1472 GST_TYPE_FRACTION, nom, den, NULL);
1480 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1482 GstXImageSink *ximagesink;
1483 gboolean ret = TRUE;
1484 GstStructure *structure;
1486 gint new_width, new_height;
1489 ximagesink = GST_XIMAGESINK (bsink);
1491 if (!ximagesink->xcontext)
1494 GST_DEBUG_OBJECT (ximagesink,
1495 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1496 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1498 /* We intersect those caps with our template to make sure they are correct */
1499 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1500 goto incompatible_caps;
1502 structure = gst_caps_get_structure (caps, 0);
1504 ret &= gst_structure_get_int (structure, "width", &new_width);
1505 ret &= gst_structure_get_int (structure, "height", &new_height);
1506 fps = gst_structure_get_value (structure, "framerate");
1507 ret &= (fps != NULL);
1511 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1512 * otherwise linking should fail */
1513 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1515 if (ximagesink->par) {
1516 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1519 } else if (ximagesink->xcontext->par) {
1520 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1526 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1527 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1528 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1529 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1531 /* Notify application to set xwindow id now */
1532 g_mutex_lock (ximagesink->flow_lock);
1533 if (!ximagesink->xwindow) {
1534 g_mutex_unlock (ximagesink->flow_lock);
1535 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1537 g_mutex_unlock (ximagesink->flow_lock);
1540 /* Creating our window and our image */
1541 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1542 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1543 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1544 ("Invalid image size."));
1548 g_mutex_lock (ximagesink->flow_lock);
1549 if (!ximagesink->xwindow) {
1550 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1551 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1553 /* Remember to draw borders for next frame */
1554 ximagesink->draw_border = TRUE;
1555 g_mutex_unlock (ximagesink->flow_lock);
1557 /* If our ximage has changed we destroy it, next chain iteration will create
1559 if ((ximagesink->ximage) &&
1560 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1561 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1562 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1563 ximagesink->ximage);
1564 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1565 ximagesink->ximage = NULL;
1573 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1578 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1583 static GstStateChangeReturn
1584 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1586 GstXImageSink *ximagesink;
1587 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1588 GstXContext *xcontext = NULL;
1590 ximagesink = GST_XIMAGESINK (element);
1592 switch (transition) {
1593 case GST_STATE_CHANGE_NULL_TO_READY:
1595 /* Initializing the XContext */
1596 if (ximagesink->xcontext == NULL) {
1597 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1598 if (xcontext == NULL) {
1599 ret = GST_STATE_CHANGE_FAILURE;
1602 GST_OBJECT_LOCK (ximagesink);
1604 ximagesink->xcontext = xcontext;
1605 GST_OBJECT_UNLOCK (ximagesink);
1608 /* call XSynchronize with the current value of synchronous */
1609 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1610 ximagesink->synchronous ? "TRUE" : "FALSE");
1611 g_mutex_lock (ximagesink->x_lock);
1612 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1613 g_mutex_unlock (ximagesink->x_lock);
1614 gst_ximagesink_manage_event_thread (ximagesink);
1616 case GST_STATE_CHANGE_READY_TO_PAUSED:
1617 g_mutex_lock (ximagesink->flow_lock);
1618 if (ximagesink->xwindow)
1619 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1620 g_mutex_unlock (ximagesink->flow_lock);
1622 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1628 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1630 switch (transition) {
1631 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1633 case GST_STATE_CHANGE_PAUSED_TO_READY:
1634 ximagesink->fps_n = 0;
1635 ximagesink->fps_d = 1;
1636 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1637 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1639 case GST_STATE_CHANGE_READY_TO_NULL:
1640 gst_ximagesink_reset (ximagesink);
1651 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1652 GstClockTime * start, GstClockTime * end)
1654 GstXImageSink *ximagesink;
1656 ximagesink = GST_XIMAGESINK (bsink);
1658 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1659 *start = GST_BUFFER_TIMESTAMP (buf);
1660 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1661 *end = *start + GST_BUFFER_DURATION (buf);
1663 if (ximagesink->fps_n > 0) {
1665 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1672 static GstFlowReturn
1673 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1675 GstXImageSink *ximagesink;
1677 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1679 ximagesink = GST_XIMAGESINK (vsink);
1681 /* This shouldn't really happen because state changes will fail
1682 * if the xcontext can't be allocated */
1683 if (!ximagesink->xcontext)
1684 return GST_FLOW_ERROR;
1686 /* If this buffer has been allocated using our buffer management we simply
1687 put the ximage which is in the PRIVATE pointer */
1688 if (GST_IS_XIMAGE_BUFFER (buf)) {
1689 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1690 if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1693 /* Else we have to copy the data into our private image, */
1694 /* if we have one... */
1695 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1696 if (!ximagesink->ximage) {
1697 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1698 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1699 GST_BUFFER_CAPS (buf));
1700 if (!ximagesink->ximage)
1701 /* The create method should have posted an informative error */
1704 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1705 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1706 ("Failed to create output image buffer of %dx%d pixels",
1707 ximagesink->ximage->width, ximagesink->ximage->height),
1708 ("XServer allocated buffer size did not match input buffer"));
1710 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1711 ximagesink->ximage = NULL;
1715 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1716 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1717 if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1726 /* No image available. That's very bad ! */
1727 GST_WARNING_OBJECT (ximagesink, "could not create image");
1728 return GST_FLOW_ERROR;
1732 /* No Window available to put our image into */
1733 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1734 return GST_FLOW_ERROR;
1740 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1742 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1744 switch (GST_EVENT_TYPE (event)) {
1745 case GST_EVENT_TAG:{
1747 gchar *title = NULL;
1749 gst_event_parse_tag (event, &l);
1750 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1753 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1754 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1764 if (GST_BASE_SINK_CLASS (parent_class)->event)
1765 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1771 /* Buffer management
1773 * The buffer_alloc function must either return a buffer with given size and
1774 * caps or create a buffer with different caps attached to the buffer. This
1775 * last option is called reverse negotiation, ie, where the sink suggests a
1776 * different format from the upstream peer.
1778 * We try to do reverse negotiation when our geometry changes and we like a
1781 static GstFlowReturn
1782 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1783 GstCaps * caps, GstBuffer ** buf)
1785 GstXImageSink *ximagesink;
1786 GstXImageBuffer *ximage = NULL;
1787 GstStructure *structure = NULL;
1788 GstFlowReturn ret = GST_FLOW_OK;
1789 GstCaps *alloc_caps;
1790 gboolean alloc_unref = FALSE;
1792 GstVideoRectangle dst, src, result;
1793 gboolean caps_accepted = FALSE;
1795 ximagesink = GST_XIMAGESINK (bsink);
1797 if (G_UNLIKELY (!caps)) {
1798 GST_WARNING_OBJECT (ximagesink, "have no caps, doing fallback allocation");
1804 /* This shouldn't really happen because state changes will fail
1805 * if the xcontext can't be allocated */
1806 if (!ximagesink->xcontext)
1807 return GST_FLOW_ERROR;
1809 GST_LOG_OBJECT (ximagesink,
1810 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1811 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1813 /* assume we're going to alloc what was requested, keep track of
1814 * whether we need to unref or not. When we suggest a new format
1815 * upstream we will create a new caps that we need to unref. */
1817 alloc_unref = FALSE;
1819 /* get struct to see what is requested */
1820 structure = gst_caps_get_structure (caps, 0);
1821 if (!gst_structure_get_int (structure, "width", &width) ||
1822 !gst_structure_get_int (structure, "height", &height)) {
1823 GST_WARNING_OBJECT (ximagesink, "invalid caps for buffer allocation %"
1824 GST_PTR_FORMAT, caps);
1825 ret = GST_FLOW_NOT_NEGOTIATED;
1832 /* We take the flow_lock because the window might go away */
1833 g_mutex_lock (ximagesink->flow_lock);
1834 if (!ximagesink->xwindow) {
1835 g_mutex_unlock (ximagesink->flow_lock);
1839 /* What is our geometry */
1840 dst.w = ximagesink->xwindow->width;
1841 dst.h = ximagesink->xwindow->height;
1843 g_mutex_unlock (ximagesink->flow_lock);
1845 if (ximagesink->keep_aspect) {
1846 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1848 gst_video_sink_center_rect (src, dst, &result, TRUE);
1850 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1851 "ignoring aspect ratio");
1852 result.x = result.y = 0;
1857 /* We would like another geometry */
1858 if (width != result.w || height != result.h) {
1860 GstCaps *desired_caps;
1861 GstStructure *desired_struct;
1863 /* make a copy of the incomming caps to create the new
1864 * suggestion. We can't use make_writable because we might
1865 * then destroy the original caps which we still need when the
1866 * peer does not accept the suggestion. */
1867 desired_caps = gst_caps_copy (caps);
1868 desired_struct = gst_caps_get_structure (desired_caps, 0);
1870 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1871 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1872 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1874 /* PAR property overrides the X calculated one */
1875 if (ximagesink->par) {
1876 nom = gst_value_get_fraction_numerator (ximagesink->par);
1877 den = gst_value_get_fraction_denominator (ximagesink->par);
1878 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1879 GST_TYPE_FRACTION, nom, den, NULL);
1880 } else if (ximagesink->xcontext->par) {
1881 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1882 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1883 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1884 GST_TYPE_FRACTION, nom, den, NULL);
1888 /* see if peer accepts our new suggestion, if there is no peer, this
1889 * function returns true. */
1890 if (!ximagesink->xcontext->last_caps ||
1891 !gst_caps_is_equal (desired_caps, ximagesink->xcontext->last_caps)) {
1893 gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1896 /* Suggestion failed, prevent future attempts for the same caps
1897 * to fail as well. */
1899 gst_caps_replace (&ximagesink->xcontext->last_caps, desired_caps);
1902 if (caps_accepted) {
1903 /* we will not alloc a buffer of the new suggested caps. Make sure
1904 * we also unref this new caps after we set it on the buffer. */
1905 alloc_caps = desired_caps;
1909 GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
1912 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1914 /* we alloc a buffer with the original incomming caps already in the
1915 * width and height variables */
1916 gst_caps_unref (desired_caps);
1921 /* Inspect our buffer pool */
1922 g_mutex_lock (ximagesink->pool_lock);
1923 while (ximagesink->buffer_pool) {
1924 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1927 /* Removing from the pool */
1928 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1929 ximagesink->buffer_pool);
1931 /* If the ximage is invalid for our need, destroy */
1932 if ((ximage->width != width) || (ximage->height != height)) {
1933 gst_ximage_buffer_free (ximage);
1936 /* We found a suitable ximage */
1941 g_mutex_unlock (ximagesink->pool_lock);
1943 /* We haven't found anything, creating a new one */
1945 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1947 /* Now we should have a ximage, set appropriate caps on it */
1949 /* Make sure the buffer is cleared of any previously used flags */
1950 GST_MINI_OBJECT_CAST (ximage)->flags = 0;
1951 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1954 /* could be our new reffed suggestion or the original unreffed caps */
1956 gst_caps_unref (alloc_caps);
1958 *buf = GST_BUFFER_CAST (ximage);
1964 /* Interfaces stuff */
1967 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1969 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY)
1976 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1978 klass->supported = gst_ximagesink_interface_supported;
1982 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1983 GstStructure * structure)
1985 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1987 gint x_offset, y_offset;
1991 event = gst_event_new_navigation (structure);
1993 /* We are not converting the pointer coordinates as there's no hardware
1994 scaling done here. The only possible scaling is done by videoscale and
1995 videoscale will have to catch those events and tranform the coordinates
1996 to match the applied scaling. So here we just add the offset if the image
1997 is centered in the window. */
1999 /* We take the flow_lock while we look at the window */
2000 g_mutex_lock (ximagesink->flow_lock);
2002 if (!ximagesink->xwindow) {
2003 g_mutex_unlock (ximagesink->flow_lock);
2007 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
2008 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
2010 g_mutex_unlock (ximagesink->flow_lock);
2012 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
2014 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
2016 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
2018 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
2021 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
2023 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
2024 gst_pad_send_event (pad, event);
2026 gst_object_unref (pad);
2031 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
2033 iface->send_event = gst_ximagesink_navigation_send_event;
2037 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2039 XID xwindow_id = id;
2040 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2041 GstXWindow *xwindow = NULL;
2042 XWindowAttributes attr;
2044 /* We acquire the stream lock while setting this window in the element.
2045 We are basically cleaning tons of stuff replacing the old window, putting
2046 images while we do that would surely crash */
2047 g_mutex_lock (ximagesink->flow_lock);
2049 /* If we already use that window return */
2050 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
2051 g_mutex_unlock (ximagesink->flow_lock);
2055 /* If the element has not initialized the X11 context try to do so */
2056 if (!ximagesink->xcontext &&
2057 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
2058 g_mutex_unlock (ximagesink->flow_lock);
2059 /* we have thrown a GST_ELEMENT_ERROR now */
2063 /* If a window is there already we destroy it */
2064 if (ximagesink->xwindow) {
2065 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2066 ximagesink->xwindow = NULL;
2069 /* If the xid is 0 we go back to an internal window */
2070 if (xwindow_id == 0) {
2071 /* If no width/height caps nego did not happen window will be created
2072 during caps nego then */
2073 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
2074 xwindow = gst_ximagesink_xwindow_new (ximagesink,
2075 GST_VIDEO_SINK_WIDTH (ximagesink),
2076 GST_VIDEO_SINK_HEIGHT (ximagesink));
2079 xwindow = g_new0 (GstXWindow, 1);
2081 xwindow->win = xwindow_id;
2083 /* We get window geometry, set the event we want to receive,
2085 g_mutex_lock (ximagesink->x_lock);
2086 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
2087 xwindow->width = attr.width;
2088 xwindow->height = attr.height;
2089 xwindow->internal = FALSE;
2090 if (ximagesink->handle_events) {
2091 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
2092 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2096 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
2097 g_mutex_unlock (ximagesink->x_lock);
2101 ximagesink->xwindow = xwindow;
2103 g_mutex_unlock (ximagesink->flow_lock);
2107 gst_ximagesink_expose (GstXOverlay * overlay)
2109 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2111 gst_ximagesink_xwindow_update_geometry (ximagesink);
2112 gst_ximagesink_ximage_put (ximagesink, NULL);
2116 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
2117 gboolean handle_events)
2119 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2121 ximagesink->handle_events = handle_events;
2123 g_mutex_lock (ximagesink->flow_lock);
2125 if (G_UNLIKELY (!ximagesink->xwindow)) {
2126 g_mutex_unlock (ximagesink->flow_lock);
2130 g_mutex_lock (ximagesink->x_lock);
2132 if (handle_events) {
2133 if (ximagesink->xwindow->internal) {
2134 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2135 ExposureMask | StructureNotifyMask | PointerMotionMask |
2136 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2138 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2139 ExposureMask | StructureNotifyMask | PointerMotionMask |
2140 KeyPressMask | KeyReleaseMask);
2143 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
2146 g_mutex_unlock (ximagesink->x_lock);
2148 g_mutex_unlock (ximagesink->flow_lock);
2152 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
2154 iface->set_window_handle = gst_ximagesink_set_window_handle;
2155 iface->expose = gst_ximagesink_expose;
2156 iface->handle_events = gst_ximagesink_set_event_handling;
2159 /* =========================================== */
2161 /* Init & Class init */
2163 /* =========================================== */
2166 gst_ximagesink_set_property (GObject * object, guint prop_id,
2167 const GValue * value, GParamSpec * pspec)
2169 GstXImageSink *ximagesink;
2171 g_return_if_fail (GST_IS_XIMAGESINK (object));
2173 ximagesink = GST_XIMAGESINK (object);
2177 ximagesink->display_name = g_strdup (g_value_get_string (value));
2179 case PROP_SYNCHRONOUS:
2180 ximagesink->synchronous = g_value_get_boolean (value);
2181 if (ximagesink->xcontext) {
2182 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2183 ximagesink->synchronous ? "TRUE" : "FALSE");
2184 g_mutex_lock (ximagesink->x_lock);
2185 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2186 g_mutex_unlock (ximagesink->x_lock);
2189 case PROP_FORCE_ASPECT_RATIO:
2190 ximagesink->keep_aspect = g_value_get_boolean (value);
2192 case PROP_PIXEL_ASPECT_RATIO:
2196 tmp = g_new0 (GValue, 1);
2197 g_value_init (tmp, GST_TYPE_FRACTION);
2199 if (!g_value_transform (value, tmp)) {
2200 GST_WARNING_OBJECT (ximagesink,
2201 "Could not transform string to aspect ratio");
2204 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2205 gst_value_get_fraction_numerator (tmp),
2206 gst_value_get_fraction_denominator (tmp));
2207 g_free (ximagesink->par);
2208 ximagesink->par = tmp;
2212 case PROP_HANDLE_EVENTS:
2213 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2214 g_value_get_boolean (value));
2215 gst_ximagesink_manage_event_thread (ximagesink);
2217 case PROP_HANDLE_EXPOSE:
2218 ximagesink->handle_expose = g_value_get_boolean (value);
2219 gst_ximagesink_manage_event_thread (ximagesink);
2222 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2228 gst_ximagesink_get_property (GObject * object, guint prop_id,
2229 GValue * value, GParamSpec * pspec)
2231 GstXImageSink *ximagesink;
2233 g_return_if_fail (GST_IS_XIMAGESINK (object));
2235 ximagesink = GST_XIMAGESINK (object);
2239 g_value_set_string (value, ximagesink->display_name);
2241 case PROP_SYNCHRONOUS:
2242 g_value_set_boolean (value, ximagesink->synchronous);
2244 case PROP_FORCE_ASPECT_RATIO:
2245 g_value_set_boolean (value, ximagesink->keep_aspect);
2247 case PROP_PIXEL_ASPECT_RATIO:
2248 if (ximagesink->par)
2249 g_value_transform (ximagesink->par, value);
2251 case PROP_HANDLE_EVENTS:
2252 g_value_set_boolean (value, ximagesink->handle_events);
2254 case PROP_HANDLE_EXPOSE:
2255 g_value_set_boolean (value, ximagesink->handle_expose);
2257 case PROP_WINDOW_WIDTH:
2258 if (ximagesink->xwindow)
2259 g_value_set_uint64 (value, ximagesink->xwindow->width);
2261 g_value_set_uint64 (value, 0);
2263 case PROP_WINDOW_HEIGHT:
2264 if (ximagesink->xwindow)
2265 g_value_set_uint64 (value, ximagesink->xwindow->height);
2267 g_value_set_uint64 (value, 0);
2270 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2276 gst_ximagesink_reset (GstXImageSink * ximagesink)
2280 GST_OBJECT_LOCK (ximagesink);
2281 ximagesink->running = FALSE;
2282 /* grab thread and mark it as NULL */
2283 thread = ximagesink->event_thread;
2284 ximagesink->event_thread = NULL;
2285 GST_OBJECT_UNLOCK (ximagesink);
2287 /* Wait for our event thread to finish before we clean up our stuff. */
2289 g_thread_join (thread);
2291 if (ximagesink->ximage) {
2292 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
2293 ximagesink->ximage = NULL;
2295 if (ximagesink->cur_image) {
2296 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
2297 ximagesink->cur_image = NULL;
2300 gst_ximagesink_bufferpool_clear (ximagesink);
2302 g_mutex_lock (ximagesink->flow_lock);
2303 if (ximagesink->xwindow) {
2304 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2305 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2306 ximagesink->xwindow = NULL;
2308 g_mutex_unlock (ximagesink->flow_lock);
2310 gst_ximagesink_xcontext_clear (ximagesink);
2314 gst_ximagesink_finalize (GObject * object)
2316 GstXImageSink *ximagesink;
2318 ximagesink = GST_XIMAGESINK (object);
2320 gst_ximagesink_reset (ximagesink);
2322 if (ximagesink->display_name) {
2323 g_free (ximagesink->display_name);
2324 ximagesink->display_name = NULL;
2326 if (ximagesink->par) {
2327 g_free (ximagesink->par);
2328 ximagesink->par = NULL;
2330 if (ximagesink->x_lock) {
2331 g_mutex_free (ximagesink->x_lock);
2332 ximagesink->x_lock = NULL;
2334 if (ximagesink->flow_lock) {
2335 g_mutex_free (ximagesink->flow_lock);
2336 ximagesink->flow_lock = NULL;
2338 if (ximagesink->pool_lock) {
2339 g_mutex_free (ximagesink->pool_lock);
2340 ximagesink->pool_lock = NULL;
2343 g_free (ximagesink->media_title);
2345 G_OBJECT_CLASS (parent_class)->finalize (object);
2349 gst_ximagesink_init (GstXImageSink * ximagesink)
2351 ximagesink->display_name = NULL;
2352 ximagesink->xcontext = NULL;
2353 ximagesink->xwindow = NULL;
2354 ximagesink->ximage = NULL;
2355 ximagesink->cur_image = NULL;
2357 ximagesink->event_thread = NULL;
2358 ximagesink->running = FALSE;
2360 ximagesink->fps_n = 0;
2361 ximagesink->fps_d = 1;
2363 ximagesink->x_lock = g_mutex_new ();
2364 ximagesink->flow_lock = g_mutex_new ();
2366 ximagesink->par = NULL;
2368 ximagesink->pool_lock = g_mutex_new ();
2369 ximagesink->buffer_pool = NULL;
2371 ximagesink->synchronous = FALSE;
2372 ximagesink->keep_aspect = FALSE;
2373 ximagesink->handle_events = TRUE;
2374 ximagesink->handle_expose = TRUE;
2378 gst_ximagesink_base_init (gpointer g_class)
2380 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2382 gst_element_class_set_details_simple (element_class,
2383 "Video sink", "Sink/Video",
2384 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
2386 gst_element_class_add_static_pad_template (element_class,
2387 &gst_ximagesink_sink_template_factory);
2391 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2393 GObjectClass *gobject_class;
2394 GstElementClass *gstelement_class;
2395 GstBaseSinkClass *gstbasesink_class;
2396 GstVideoSinkClass *videosink_class;
2398 gobject_class = (GObjectClass *) klass;
2399 gstelement_class = (GstElementClass *) klass;
2400 gstbasesink_class = (GstBaseSinkClass *) klass;
2401 videosink_class = (GstVideoSinkClass *) klass;
2403 parent_class = g_type_class_peek_parent (klass);
2405 gobject_class->finalize = gst_ximagesink_finalize;
2406 gobject_class->set_property = gst_ximagesink_set_property;
2407 gobject_class->get_property = gst_ximagesink_get_property;
2409 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2410 g_param_spec_string ("display", "Display", "X Display name",
2411 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2412 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2413 g_param_spec_boolean ("synchronous", "Synchronous",
2414 "When enabled, runs the X display in synchronous mode. "
2415 "(unrelated to A/V sync, used only for debugging)", FALSE,
2416 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2417 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2418 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2419 "When enabled, reverse caps negotiation (scaling) will respect "
2420 "original aspect ratio", FALSE,
2421 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2422 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2423 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2424 "The pixel aspect ratio of the device", "1/1",
2425 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2426 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2427 g_param_spec_boolean ("handle-events", "Handle XEvents",
2428 "When enabled, XEvents will be selected and handled", TRUE,
2429 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2430 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2431 g_param_spec_boolean ("handle-expose", "Handle expose",
2433 "the current frame will always be drawn in response to X Expose "
2434 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2437 * GstXImageSink:window-width
2439 * Actual width of the video window.
2443 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2444 g_param_spec_uint64 ("window-width", "window-width",
2445 "Width of the window", 0, G_MAXUINT64, 0,
2446 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2449 * GstXImageSink:window-height
2451 * Actual height of the video window.
2455 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2456 g_param_spec_uint64 ("window-height", "window-height",
2457 "Height of the window", 0, G_MAXUINT64, 0,
2458 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2460 gstelement_class->change_state = gst_ximagesink_change_state;
2462 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2463 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2464 gstbasesink_class->buffer_alloc =
2465 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2466 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2467 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
2469 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2472 /* ============================================================= */
2474 /* Public Methods */
2476 /* ============================================================= */
2478 /* =========================================== */
2480 /* Object typing & Creation */
2482 /* =========================================== */
2485 gst_ximagesink_get_type (void)
2487 static GType ximagesink_type = 0;
2489 if (!ximagesink_type) {
2490 static const GTypeInfo ximagesink_info = {
2491 sizeof (GstXImageSinkClass),
2492 gst_ximagesink_base_init,
2494 (GClassInitFunc) gst_ximagesink_class_init,
2497 sizeof (GstXImageSink), 0, (GInstanceInitFunc) gst_ximagesink_init,
2499 static const GInterfaceInfo iface_info = {
2500 (GInterfaceInitFunc) gst_ximagesink_interface_init, NULL, NULL,
2502 static const GInterfaceInfo navigation_info = {
2503 (GInterfaceInitFunc) gst_ximagesink_navigation_init, NULL, NULL,
2505 static const GInterfaceInfo overlay_info = {
2506 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init, NULL, NULL,
2509 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2510 "GstXImageSink", &ximagesink_info, 0);
2512 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2514 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2516 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2519 /* register type and create class in a more safe place instead of at
2520 * runtime since the type registration and class creation is not
2522 g_type_class_ref (gst_ximage_buffer_get_type ());
2525 return ximagesink_type;