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);
437 /* Reset error handler */
438 error_caught = FALSE;
439 XSetErrorHandler (handler);
441 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
442 ("Failed to create output image buffer of %dx%d pixels",
443 ximage->width, ximage->height),
444 ("could not XShmCreateImage a %dx%d image",
445 ximage->width, ximage->height));
449 /* we have to use the returned bytes_per_line for our shm size */
450 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
451 GST_LOG_OBJECT (ximagesink,
452 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
453 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
455 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
457 if (ximage->SHMInfo.shmid == -1) {
458 g_mutex_unlock (ximagesink->x_lock);
459 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
460 ("Failed to create output image buffer of %dx%d pixels",
461 ximage->width, ximage->height),
462 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
467 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
468 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
469 g_mutex_unlock (ximagesink->x_lock);
470 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
471 ("Failed to create output image buffer of %dx%d pixels",
472 ximage->width, ximage->height),
473 ("Failed to shmat: %s", g_strerror (errno)));
474 /* Clean up the shared memory segment */
475 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
479 ximage->ximage->data = ximage->SHMInfo.shmaddr;
480 ximage->SHMInfo.readOnly = FALSE;
482 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
483 /* Clean up shm seg */
484 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
486 g_mutex_unlock (ximagesink->x_lock);
487 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
488 ("Failed to create output image buffer of %dx%d pixels",
489 ximage->width, ximage->height), ("Failed to XShmAttach"));
493 XSync (ximagesink->xcontext->disp, FALSE);
495 /* Now that everyone has attached, we can delete the shared memory segment.
496 * This way, it will be deleted as soon as we detach later, and not
497 * leaked if we crash. */
498 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
501 #endif /* HAVE_XSHM */
505 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
506 ximagesink->xcontext->visual,
507 ximagesink->xcontext->depth,
509 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
510 if (!ximage->ximage || error_caught) {
511 g_mutex_unlock (ximagesink->x_lock);
512 /* Reset error handler */
513 error_caught = FALSE;
514 XSetErrorHandler (handler);
516 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
517 ("Failed to create output image buffer of %dx%d pixels",
518 ximage->width, ximage->height),
519 ("could not XCreateImage a %dx%d image",
520 ximage->width, ximage->height));
524 /* upstream will assume that rowstrides are multiples of 4, but this
525 * doesn't always seem to be the case with XCreateImage() */
526 if ((ximage->ximage->bytes_per_line % 4) != 0) {
527 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
531 /* we have to use the returned bytes_per_line for our image size */
532 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
534 /* alloc a bit more for unexpected strides to avoid crashes upstream.
535 * FIXME: if we get an unrounded stride, the image will be displayed
536 * distorted, since all upstream elements assume a rounded stride */
538 GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
539 ximage->ximage->height;
540 ximage->ximage->data = g_malloc (allocsize);
541 GST_LOG_OBJECT (ximagesink,
542 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
543 "stride %d", ximage->size, allocsize, ximage->width,
544 ximage->ximage->bytes_per_line);
546 XSync (ximagesink->xcontext->disp, FALSE);
549 /* Reset error handler */
550 error_caught = FALSE;
551 XSetErrorHandler (handler);
555 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
556 GST_BUFFER_SIZE (ximage) = ximage->size;
558 /* Keep a ref to our sink */
559 ximage->ximagesink = gst_object_ref (ximagesink);
561 g_mutex_unlock (ximagesink->x_lock);
564 gst_ximage_buffer_free (ximage);
571 /* This function destroys a GstXImageBuffer handling XShm availability */
573 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
574 GstXImageBuffer * ximage)
576 g_return_if_fail (ximage != NULL);
577 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
579 /* Hold the object lock to ensure the XContext doesn't disappear */
580 GST_OBJECT_LOCK (ximagesink);
582 /* If the destroyed image is the current one we destroy our reference too */
583 if (ximagesink->cur_image == ximage) {
584 ximagesink->cur_image = NULL;
587 /* We might have some buffers destroyed after changing state to NULL */
588 if (!ximagesink->xcontext) {
589 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
591 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
592 shmdt (ximage->SHMInfo.shmaddr);
598 g_mutex_lock (ximagesink->x_lock);
601 if (ximagesink->xcontext->use_xshm) {
602 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
603 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
604 XSync (ximagesink->xcontext->disp, 0);
605 shmdt (ximage->SHMInfo.shmaddr);
608 XDestroyImage (ximage->ximage);
611 #endif /* HAVE_XSHM */
613 if (ximage->ximage) {
614 XDestroyImage (ximage->ximage);
618 XSync (ximagesink->xcontext->disp, FALSE);
620 g_mutex_unlock (ximagesink->x_lock);
623 GST_OBJECT_UNLOCK (ximagesink);
625 if (ximage->ximagesink) {
626 /* Release the ref to our sink */
627 ximage->ximagesink = NULL;
628 gst_object_unref (ximagesink);
634 /* We are called with the x_lock taken */
636 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
637 GstXWindow * xwindow, GstVideoRectangle rect)
639 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
640 g_return_if_fail (xwindow != NULL);
642 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
643 ximagesink->xcontext->black);
647 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
648 0, 0, rect.x, xwindow->height);
652 if ((rect.x + rect.w) < xwindow->width) {
653 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
654 rect.x + rect.w, 0, xwindow->width, xwindow->height);
659 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
660 0, 0, xwindow->width, rect.y);
664 if ((rect.y + rect.h) < xwindow->height) {
665 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
666 0, rect.y + rect.h, xwindow->width, xwindow->height);
670 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
672 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
674 GstVideoRectangle src, dst, result;
675 gboolean draw_border = FALSE;
677 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
679 /* We take the flow_lock. If expose is in there we don't want to run
680 concurrently from the data flow thread */
681 g_mutex_lock (ximagesink->flow_lock);
683 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
684 g_mutex_unlock (ximagesink->flow_lock);
688 /* Draw borders when displaying the first frame. After this
689 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
690 if (!ximagesink->cur_image || ximagesink->draw_border) {
694 /* Store a reference to the last image we put, lose the previous one */
695 if (ximage && ximagesink->cur_image != ximage) {
696 if (ximagesink->cur_image) {
697 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
698 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
700 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
701 ximagesink->cur_image =
702 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
705 /* Expose sends a NULL image, we take the latest frame */
708 if (ximagesink->cur_image) {
709 ximage = ximagesink->cur_image;
711 g_mutex_unlock (ximagesink->flow_lock);
716 src.w = ximage->width;
717 src.h = ximage->height;
718 dst.w = ximagesink->xwindow->width;
719 dst.h = ximagesink->xwindow->height;
721 gst_video_sink_center_rect (src, dst, &result, FALSE);
723 g_mutex_lock (ximagesink->x_lock);
726 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
728 ximagesink->draw_border = FALSE;
731 if (ximagesink->xcontext->use_xshm) {
732 GST_LOG_OBJECT (ximagesink,
733 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
734 ximage, 0, 0, result.x, result.y, result.w, result.h,
735 ximagesink->xwindow->width, ximagesink->xwindow->height);
736 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
737 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
738 result.w, result.h, FALSE);
740 #endif /* HAVE_XSHM */
742 GST_LOG_OBJECT (ximagesink,
743 "XPutImage 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 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
747 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
751 XSync (ximagesink->xcontext->disp, FALSE);
753 g_mutex_unlock (ximagesink->x_lock);
755 g_mutex_unlock (ximagesink->flow_lock);
761 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
764 Atom hints_atom = None;
767 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
768 g_return_val_if_fail (window != NULL, FALSE);
770 g_mutex_lock (ximagesink->x_lock);
772 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
773 if (hints_atom == None) {
774 g_mutex_unlock (ximagesink->x_lock);
778 hints = g_malloc0 (sizeof (MotifWmHints));
780 hints->flags |= MWM_HINTS_DECORATIONS;
781 hints->decorations = 1 << 0;
783 XChangeProperty (ximagesink->xcontext->disp, window->win,
784 hints_atom, hints_atom, 32, PropModeReplace,
785 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
787 XSync (ximagesink->xcontext->disp, FALSE);
789 g_mutex_unlock (ximagesink->x_lock);
797 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
798 GstXWindow * xwindow, const gchar * media_title)
801 g_free (ximagesink->media_title);
802 ximagesink->media_title = g_strdup (media_title);
805 /* we have a window */
806 if (xwindow->internal) {
807 XTextProperty xproperty;
808 const gchar *app_name;
809 const gchar *title = NULL;
810 gchar *title_mem = NULL;
812 /* set application name as a title */
813 app_name = g_get_application_name ();
815 if (app_name && ximagesink->media_title) {
816 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
818 } else if (app_name) {
820 } else if (ximagesink->media_title) {
821 title = ximagesink->media_title;
825 if ((XStringListToTextProperty (((char **) &title), 1,
827 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
828 XFree (xproperty.value);
837 /* This function handles a GstXWindow creation */
839 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
841 GstXWindow *xwindow = NULL;
844 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
846 xwindow = g_new0 (GstXWindow, 1);
848 xwindow->width = width;
849 xwindow->height = height;
850 xwindow->internal = TRUE;
852 g_mutex_lock (ximagesink->x_lock);
854 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
855 ximagesink->xcontext->root,
856 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
858 /* We have to do that to prevent X from redrawing the background on
859 ConfigureNotify. This takes away flickering of video when resizing. */
860 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
862 /* set application name as a title */
863 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
865 if (ximagesink->handle_events) {
868 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
869 StructureNotifyMask | PointerMotionMask | KeyPressMask |
870 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
872 /* Tell the window manager we'd like delete client messages instead of
874 wm_delete = XInternAtom (ximagesink->xcontext->disp,
875 "WM_DELETE_WINDOW", False);
876 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
880 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
883 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
885 XSync (ximagesink->xcontext->disp, FALSE);
887 g_mutex_unlock (ximagesink->x_lock);
889 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
891 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
896 /* This function destroys a GstXWindow */
898 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
899 GstXWindow * xwindow)
901 g_return_if_fail (xwindow != NULL);
902 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
904 g_mutex_lock (ximagesink->x_lock);
906 /* If we did not create that window we just free the GC and let it live */
907 if (xwindow->internal)
908 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
910 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
912 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
914 XSync (ximagesink->xcontext->disp, FALSE);
916 g_mutex_unlock (ximagesink->x_lock);
922 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
924 XWindowAttributes attr;
926 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
928 /* Update the window geometry */
929 g_mutex_lock (ximagesink->x_lock);
930 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
931 g_mutex_unlock (ximagesink->x_lock);
935 XGetWindowAttributes (ximagesink->xcontext->disp,
936 ximagesink->xwindow->win, &attr);
938 ximagesink->xwindow->width = attr.width;
939 ximagesink->xwindow->height = attr.height;
941 g_mutex_unlock (ximagesink->x_lock);
945 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
947 g_return_if_fail (xwindow != NULL);
948 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
950 g_mutex_lock (ximagesink->x_lock);
952 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
953 ximagesink->xcontext->black);
955 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
956 0, 0, xwindow->width, xwindow->height);
958 XSync (ximagesink->xcontext->disp, FALSE);
960 g_mutex_unlock (ximagesink->x_lock);
963 /* This function handles XEvents that might be in the queue. It generates
964 GstEvent that will be sent upstream in the pipeline to handle interactivity
967 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
970 guint pointer_x = 0, pointer_y = 0;
971 gboolean pointer_moved = FALSE;
972 gboolean exposed = FALSE, configured = FALSE;
974 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
976 /* Then we get all pointer motion events, only the last position is
978 g_mutex_lock (ximagesink->flow_lock);
979 g_mutex_lock (ximagesink->x_lock);
980 while (XCheckWindowEvent (ximagesink->xcontext->disp,
981 ximagesink->xwindow->win, PointerMotionMask, &e)) {
982 g_mutex_unlock (ximagesink->x_lock);
983 g_mutex_unlock (ximagesink->flow_lock);
987 pointer_x = e.xmotion.x;
988 pointer_y = e.xmotion.y;
989 pointer_moved = TRUE;
994 g_mutex_lock (ximagesink->flow_lock);
995 g_mutex_lock (ximagesink->x_lock);
999 g_mutex_unlock (ximagesink->x_lock);
1000 g_mutex_unlock (ximagesink->flow_lock);
1002 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
1003 pointer_x, pointer_y);
1004 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1005 "mouse-move", 0, pointer_x, pointer_y);
1007 g_mutex_lock (ximagesink->flow_lock);
1008 g_mutex_lock (ximagesink->x_lock);
1011 /* We get all remaining events on our window to throw them upstream */
1012 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1013 ximagesink->xwindow->win,
1014 KeyPressMask | KeyReleaseMask |
1015 ButtonPressMask | ButtonReleaseMask, &e)) {
1018 /* We lock only for the X function call */
1019 g_mutex_unlock (ximagesink->x_lock);
1020 g_mutex_unlock (ximagesink->flow_lock);
1024 /* Mouse button pressed/released over our window. We send upstream
1025 events for interactivity/navigation */
1026 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
1027 e.xbutton.button, e.xbutton.x, e.xbutton.x);
1028 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1029 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1032 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
1033 e.xbutton.button, e.xbutton.x, e.xbutton.x);
1034 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
1035 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1039 /* Key pressed/released over our window. We send upstream
1040 events for interactivity/navigation */
1041 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1042 e.xkey.keycode, e.xkey.x, e.xkey.x);
1043 g_mutex_lock (ximagesink->x_lock);
1044 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1046 g_mutex_unlock (ximagesink->x_lock);
1047 if (keysym != NoSymbol) {
1048 char *key_str = NULL;
1050 g_mutex_lock (ximagesink->x_lock);
1051 key_str = XKeysymToString (keysym);
1052 g_mutex_unlock (ximagesink->x_lock);
1053 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1054 e.type == KeyPress ? "key-press" : "key-release", key_str);
1057 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1058 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1062 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1065 g_mutex_lock (ximagesink->flow_lock);
1066 g_mutex_lock (ximagesink->x_lock);
1069 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1070 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1075 case ConfigureNotify:
1076 g_mutex_unlock (ximagesink->x_lock);
1077 gst_ximagesink_xwindow_update_geometry (ximagesink);
1078 g_mutex_lock (ximagesink->x_lock);
1086 if (ximagesink->handle_expose && (exposed || configured)) {
1087 g_mutex_unlock (ximagesink->x_lock);
1088 g_mutex_unlock (ximagesink->flow_lock);
1090 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1092 g_mutex_lock (ximagesink->flow_lock);
1093 g_mutex_lock (ximagesink->x_lock);
1096 /* Handle Display events */
1097 while (XPending (ximagesink->xcontext->disp)) {
1098 XNextEvent (ximagesink->xcontext->disp, &e);
1101 case ClientMessage:{
1104 wm_delete = XInternAtom (ximagesink->xcontext->disp,
1105 "WM_DELETE_WINDOW", False);
1106 if (wm_delete == (Atom) e.xclient.data.l[0]) {
1107 /* Handle window deletion by posting an error on the bus */
1108 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1109 ("Output window was closed"), (NULL));
1111 g_mutex_unlock (ximagesink->x_lock);
1112 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1113 ximagesink->xwindow = NULL;
1114 g_mutex_lock (ximagesink->x_lock);
1123 g_mutex_unlock (ximagesink->x_lock);
1124 g_mutex_unlock (ximagesink->flow_lock);
1128 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1130 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1132 GST_OBJECT_LOCK (ximagesink);
1133 while (ximagesink->running) {
1134 GST_OBJECT_UNLOCK (ximagesink);
1136 if (ximagesink->xwindow) {
1137 gst_ximagesink_handle_xevents (ximagesink);
1139 /* FIXME: do we want to align this with the framerate or anything else? */
1140 g_usleep (G_USEC_PER_SEC / 20);
1142 GST_OBJECT_LOCK (ximagesink);
1144 GST_OBJECT_UNLOCK (ximagesink);
1150 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
1152 GThread *thread = NULL;
1154 /* don't start the thread too early */
1155 if (ximagesink->xcontext == NULL) {
1159 GST_OBJECT_LOCK (ximagesink);
1160 if (ximagesink->handle_expose || ximagesink->handle_events) {
1161 if (!ximagesink->event_thread) {
1162 /* Setup our event listening thread */
1163 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
1164 ximagesink->handle_expose, ximagesink->handle_events);
1165 ximagesink->running = TRUE;
1166 ximagesink->event_thread = g_thread_create (
1167 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1170 if (ximagesink->event_thread) {
1171 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
1172 ximagesink->handle_expose, ximagesink->handle_events);
1173 ximagesink->running = FALSE;
1174 /* grab thread and mark it as NULL */
1175 thread = ximagesink->event_thread;
1176 ximagesink->event_thread = NULL;
1179 GST_OBJECT_UNLOCK (ximagesink);
1181 /* Wait for our event thread to finish */
1183 g_thread_join (thread);
1188 /* This function calculates the pixel aspect ratio based on the properties
1189 * in the xcontext structure and stores it there. */
1191 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1193 static const gint par[][2] = {
1194 {1, 1}, /* regular screen */
1195 {16, 15}, /* PAL TV */
1196 {11, 10}, /* 525 line Rec.601 video */
1197 {54, 59}, /* 625 line Rec.601 video */
1198 {64, 45}, /* 1280x1024 on 16:9 display */
1199 {5, 3}, /* 1280x1024 on 4:3 display */
1200 {4, 3} /* 800x600 on 16:9 display */
1207 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1209 /* first calculate the "real" ratio based on the X values;
1210 * which is the "physical" w/h divided by the w/h in pixels of the display */
1211 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1212 / (xcontext->heightmm * xcontext->width);
1214 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1216 if (xcontext->width == 720 && xcontext->height == 576) {
1217 ratio = 4.0 * 576 / (3.0 * 720);
1219 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1221 /* now find the one from par[][2] with the lowest delta to the real one */
1225 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1226 gdouble this_delta = DELTA (i);
1228 if (this_delta < delta) {
1234 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1235 par[index][0], par[index][1]);
1237 g_free (xcontext->par);
1238 xcontext->par = g_new0 (GValue, 1);
1239 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1240 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1241 GST_DEBUG ("set xcontext PAR to %d/%d",
1242 gst_value_get_fraction_numerator (xcontext->par),
1243 gst_value_get_fraction_denominator (xcontext->par));
1246 /* This function gets the X Display and global info about it. Everything is
1247 stored in our object and will be cleaned when the object is disposed. Note
1248 here that caps for supported format are generated without any window or
1250 static GstXContext *
1251 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1253 GstXContext *xcontext = NULL;
1254 XPixmapFormatValues *px_formats = NULL;
1255 gint nb_formats = 0, i;
1257 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1259 xcontext = g_new0 (GstXContext, 1);
1261 g_mutex_lock (ximagesink->x_lock);
1263 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1265 if (!xcontext->disp) {
1266 g_mutex_unlock (ximagesink->x_lock);
1268 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1269 ("Could not initialise X output"), ("Could not open display"));
1273 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1274 xcontext->screen_num = DefaultScreen (xcontext->disp);
1275 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1276 xcontext->root = DefaultRootWindow (xcontext->disp);
1277 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1278 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1279 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1281 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1282 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1283 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1284 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1286 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1287 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1289 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1291 /* We get supported pixmap formats at supported depth */
1292 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1295 XCloseDisplay (xcontext->disp);
1296 g_mutex_unlock (ximagesink->x_lock);
1297 g_free (xcontext->par);
1299 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1300 ("Could not get supported pixmap formats"), (NULL));
1304 /* We get bpp value corresponding to our running depth */
1305 for (i = 0; i < nb_formats; i++) {
1306 if (px_formats[i].depth == xcontext->depth)
1307 xcontext->bpp = px_formats[i].bits_per_pixel;
1312 xcontext->endianness =
1313 (ImageByteOrder (xcontext->disp) ==
1314 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1316 /* Search for XShm extension support */
1318 if (XShmQueryExtension (xcontext->disp) &&
1319 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1320 xcontext->use_xshm = TRUE;
1321 GST_DEBUG ("ximagesink is using XShm extension");
1325 xcontext->use_xshm = FALSE;
1326 GST_DEBUG ("ximagesink is not using XShm extension");
1329 /* our caps system handles 24/32bpp RGB as big-endian. */
1330 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1331 xcontext->endianness == G_LITTLE_ENDIAN) {
1332 xcontext->endianness = G_BIG_ENDIAN;
1333 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1334 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1335 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1336 if (xcontext->bpp == 24) {
1337 xcontext->visual->red_mask >>= 8;
1338 xcontext->visual->green_mask >>= 8;
1339 xcontext->visual->blue_mask >>= 8;
1343 /* update object's par with calculated one if not set yet */
1344 if (!ximagesink->par) {
1345 ximagesink->par = g_new0 (GValue, 1);
1346 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1347 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1349 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1350 "bpp", G_TYPE_INT, xcontext->bpp,
1351 "depth", G_TYPE_INT, xcontext->depth,
1352 "endianness", G_TYPE_INT, xcontext->endianness,
1353 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1354 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1355 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1356 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1357 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1358 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1359 if (ximagesink->par) {
1362 nom = gst_value_get_fraction_numerator (ximagesink->par);
1363 den = gst_value_get_fraction_denominator (ximagesink->par);
1364 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1365 GST_TYPE_FRACTION, nom, den, NULL);
1368 g_mutex_unlock (ximagesink->x_lock);
1373 /* This function cleans the X context. Closing the Display and unrefing the
1374 caps for supported formats. */
1376 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1378 GstXContext *xcontext;
1380 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1382 GST_OBJECT_LOCK (ximagesink);
1383 if (ximagesink->xcontext == NULL) {
1384 GST_OBJECT_UNLOCK (ximagesink);
1388 /* Take the xcontext reference and NULL it while we
1389 * clean it up, so that any buffer-alloced buffers
1390 * arriving after this will be freed correctly */
1391 xcontext = ximagesink->xcontext;
1392 ximagesink->xcontext = NULL;
1394 GST_OBJECT_UNLOCK (ximagesink);
1396 gst_caps_unref (xcontext->caps);
1397 g_free (xcontext->par);
1398 g_free (ximagesink->par);
1399 ximagesink->par = NULL;
1401 if (xcontext->last_caps)
1402 gst_caps_replace (&xcontext->last_caps, NULL);
1404 g_mutex_lock (ximagesink->x_lock);
1406 XCloseDisplay (xcontext->disp);
1408 g_mutex_unlock (ximagesink->x_lock);
1414 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1417 g_mutex_lock (ximagesink->pool_lock);
1419 while (ximagesink->buffer_pool) {
1420 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1422 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1423 ximagesink->buffer_pool);
1424 gst_ximage_buffer_free (ximage);
1427 g_mutex_unlock (ximagesink->pool_lock);
1433 gst_ximagesink_getcaps (GstBaseSink * bsink)
1435 GstXImageSink *ximagesink;
1439 ximagesink = GST_XIMAGESINK (bsink);
1441 if (ximagesink->xcontext)
1442 return gst_caps_ref (ximagesink->xcontext->caps);
1444 /* get a template copy and add the pixel aspect ratio */
1446 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1447 (ximagesink)->sinkpad));
1448 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1449 GstStructure *structure = gst_caps_get_structure (caps, i);
1451 if (ximagesink->par) {
1454 nom = gst_value_get_fraction_numerator (ximagesink->par);
1455 den = gst_value_get_fraction_denominator (ximagesink->par);
1456 gst_structure_set (structure, "pixel-aspect-ratio",
1457 GST_TYPE_FRACTION, nom, den, NULL);
1465 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1467 GstXImageSink *ximagesink;
1468 gboolean ret = TRUE;
1469 GstStructure *structure;
1471 gint new_width, new_height;
1474 ximagesink = GST_XIMAGESINK (bsink);
1476 if (!ximagesink->xcontext)
1479 GST_DEBUG_OBJECT (ximagesink,
1480 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1481 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1483 /* We intersect those caps with our template to make sure they are correct */
1484 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1485 goto incompatible_caps;
1487 structure = gst_caps_get_structure (caps, 0);
1489 ret &= gst_structure_get_int (structure, "width", &new_width);
1490 ret &= gst_structure_get_int (structure, "height", &new_height);
1491 fps = gst_structure_get_value (structure, "framerate");
1492 ret &= (fps != NULL);
1496 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1497 * otherwise linking should fail */
1498 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1500 if (ximagesink->par) {
1501 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1504 } else if (ximagesink->xcontext->par) {
1505 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1511 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1512 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1513 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1514 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1516 /* Notify application to set xwindow id now */
1517 g_mutex_lock (ximagesink->flow_lock);
1518 if (!ximagesink->xwindow) {
1519 g_mutex_unlock (ximagesink->flow_lock);
1520 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1522 g_mutex_unlock (ximagesink->flow_lock);
1525 /* Creating our window and our image */
1526 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1527 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1528 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1529 ("Invalid image size."));
1533 g_mutex_lock (ximagesink->flow_lock);
1534 if (!ximagesink->xwindow) {
1535 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1536 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1538 /* Remember to draw borders for next frame */
1539 ximagesink->draw_border = TRUE;
1540 g_mutex_unlock (ximagesink->flow_lock);
1542 /* If our ximage has changed we destroy it, next chain iteration will create
1544 if ((ximagesink->ximage) &&
1545 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1546 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1547 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1548 ximagesink->ximage);
1549 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1550 ximagesink->ximage = NULL;
1558 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1563 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1568 static GstStateChangeReturn
1569 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1571 GstXImageSink *ximagesink;
1572 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1573 GstXContext *xcontext = NULL;
1575 ximagesink = GST_XIMAGESINK (element);
1577 switch (transition) {
1578 case GST_STATE_CHANGE_NULL_TO_READY:
1580 /* Initializing the XContext */
1581 if (ximagesink->xcontext == NULL) {
1582 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1583 if (xcontext == NULL) {
1584 ret = GST_STATE_CHANGE_FAILURE;
1587 GST_OBJECT_LOCK (ximagesink);
1589 ximagesink->xcontext = xcontext;
1590 GST_OBJECT_UNLOCK (ximagesink);
1593 /* call XSynchronize with the current value of synchronous */
1594 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1595 ximagesink->synchronous ? "TRUE" : "FALSE");
1596 g_mutex_lock (ximagesink->x_lock);
1597 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1598 g_mutex_unlock (ximagesink->x_lock);
1599 gst_ximagesink_manage_event_thread (ximagesink);
1601 case GST_STATE_CHANGE_READY_TO_PAUSED:
1602 g_mutex_lock (ximagesink->flow_lock);
1603 if (ximagesink->xwindow)
1604 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1605 g_mutex_unlock (ximagesink->flow_lock);
1607 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1613 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1615 switch (transition) {
1616 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1618 case GST_STATE_CHANGE_PAUSED_TO_READY:
1619 ximagesink->fps_n = 0;
1620 ximagesink->fps_d = 1;
1621 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1622 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1624 case GST_STATE_CHANGE_READY_TO_NULL:
1625 gst_ximagesink_reset (ximagesink);
1636 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1637 GstClockTime * start, GstClockTime * end)
1639 GstXImageSink *ximagesink;
1641 ximagesink = GST_XIMAGESINK (bsink);
1643 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1644 *start = GST_BUFFER_TIMESTAMP (buf);
1645 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1646 *end = *start + GST_BUFFER_DURATION (buf);
1648 if (ximagesink->fps_n > 0) {
1650 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1657 static GstFlowReturn
1658 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1660 GstXImageSink *ximagesink;
1662 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1664 ximagesink = GST_XIMAGESINK (vsink);
1666 /* This shouldn't really happen because state changes will fail
1667 * if the xcontext can't be allocated */
1668 if (!ximagesink->xcontext)
1669 return GST_FLOW_ERROR;
1671 /* If this buffer has been allocated using our buffer management we simply
1672 put the ximage which is in the PRIVATE pointer */
1673 if (GST_IS_XIMAGE_BUFFER (buf)) {
1674 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1675 if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1678 /* Else we have to copy the data into our private image, */
1679 /* if we have one... */
1680 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1681 if (!ximagesink->ximage) {
1682 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1683 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1684 GST_BUFFER_CAPS (buf));
1685 if (!ximagesink->ximage)
1686 /* The create method should have posted an informative error */
1689 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1690 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1691 ("Failed to create output image buffer of %dx%d pixels",
1692 ximagesink->ximage->width, ximagesink->ximage->height),
1693 ("XServer allocated buffer size did not match input buffer"));
1695 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1696 ximagesink->ximage = NULL;
1700 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1701 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1702 if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1711 /* No image available. That's very bad ! */
1712 GST_WARNING_OBJECT (ximagesink, "could not create image");
1713 return GST_FLOW_ERROR;
1717 /* No Window available to put our image into */
1718 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1719 return GST_FLOW_ERROR;
1725 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1727 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1729 switch (GST_EVENT_TYPE (event)) {
1730 case GST_EVENT_TAG:{
1732 gchar *title = NULL;
1734 gst_event_parse_tag (event, &l);
1735 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1738 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1739 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1749 if (GST_BASE_SINK_CLASS (parent_class)->event)
1750 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1756 /* Buffer management
1758 * The buffer_alloc function must either return a buffer with given size and
1759 * caps or create a buffer with different caps attached to the buffer. This
1760 * last option is called reverse negotiation, ie, where the sink suggests a
1761 * different format from the upstream peer.
1763 * We try to do reverse negotiation when our geometry changes and we like a
1766 static GstFlowReturn
1767 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1768 GstCaps * caps, GstBuffer ** buf)
1770 GstXImageSink *ximagesink;
1771 GstXImageBuffer *ximage = NULL;
1772 GstStructure *structure = NULL;
1773 GstFlowReturn ret = GST_FLOW_OK;
1774 GstCaps *alloc_caps;
1775 gboolean alloc_unref = FALSE;
1777 GstVideoRectangle dst, src, result;
1778 gboolean caps_accepted = FALSE;
1780 ximagesink = GST_XIMAGESINK (bsink);
1782 if (G_UNLIKELY (!caps)) {
1783 GST_WARNING_OBJECT (ximagesink, "have no caps, doing fallback allocation");
1789 /* This shouldn't really happen because state changes will fail
1790 * if the xcontext can't be allocated */
1791 if (!ximagesink->xcontext)
1792 return GST_FLOW_ERROR;
1794 GST_LOG_OBJECT (ximagesink,
1795 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1796 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1798 /* assume we're going to alloc what was requested, keep track of
1799 * wheter we need to unref or not. When we suggest a new format
1800 * upstream we will create a new caps that we need to unref. */
1802 alloc_unref = FALSE;
1804 /* get struct to see what is requested */
1805 structure = gst_caps_get_structure (caps, 0);
1806 if (!gst_structure_get_int (structure, "width", &width) ||
1807 !gst_structure_get_int (structure, "height", &height)) {
1808 GST_WARNING_OBJECT (ximagesink, "invalid caps for buffer allocation %"
1809 GST_PTR_FORMAT, caps);
1810 ret = GST_FLOW_NOT_NEGOTIATED;
1817 /* We take the flow_lock because the window might go away */
1818 g_mutex_lock (ximagesink->flow_lock);
1819 if (!ximagesink->xwindow) {
1820 g_mutex_unlock (ximagesink->flow_lock);
1824 /* What is our geometry */
1825 dst.w = ximagesink->xwindow->width;
1826 dst.h = ximagesink->xwindow->height;
1828 g_mutex_unlock (ximagesink->flow_lock);
1830 if (ximagesink->keep_aspect) {
1831 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1833 gst_video_sink_center_rect (src, dst, &result, TRUE);
1835 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1836 "ignoring aspect ratio");
1837 result.x = result.y = 0;
1842 /* We would like another geometry */
1843 if (width != result.w || height != result.h) {
1845 GstCaps *desired_caps;
1846 GstStructure *desired_struct;
1848 /* make a copy of the incomming caps to create the new
1849 * suggestion. We can't use make_writable because we might
1850 * then destroy the original caps which we still need when the
1851 * peer does not accept the suggestion. */
1852 desired_caps = gst_caps_copy (caps);
1853 desired_struct = gst_caps_get_structure (desired_caps, 0);
1855 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1856 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1857 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1859 /* PAR property overrides the X calculated one */
1860 if (ximagesink->par) {
1861 nom = gst_value_get_fraction_numerator (ximagesink->par);
1862 den = gst_value_get_fraction_denominator (ximagesink->par);
1863 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1864 GST_TYPE_FRACTION, nom, den, NULL);
1865 } else if (ximagesink->xcontext->par) {
1866 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1867 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1868 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1869 GST_TYPE_FRACTION, nom, den, NULL);
1873 /* see if peer accepts our new suggestion, if there is no peer, this
1874 * function returns true. */
1875 if (!ximagesink->xcontext->last_caps ||
1876 !gst_caps_is_equal (desired_caps, ximagesink->xcontext->last_caps)) {
1878 gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1881 /* Suggestion failed, prevent future attempts for the same caps
1882 * to fail as well. */
1884 gst_caps_replace (&ximagesink->xcontext->last_caps, desired_caps);
1887 if (caps_accepted) {
1888 /* we will not alloc a buffer of the new suggested caps. Make sure
1889 * we also unref this new caps after we set it on the buffer. */
1890 alloc_caps = desired_caps;
1894 GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT,
1897 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1899 /* we alloc a buffer with the original incomming caps already in the
1900 * width and height variables */
1901 gst_caps_unref (desired_caps);
1906 /* Inspect our buffer pool */
1907 g_mutex_lock (ximagesink->pool_lock);
1908 while (ximagesink->buffer_pool) {
1909 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1912 /* Removing from the pool */
1913 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1914 ximagesink->buffer_pool);
1916 /* If the ximage is invalid for our need, destroy */
1917 if ((ximage->width != width) || (ximage->height != height)) {
1918 gst_ximage_buffer_free (ximage);
1921 /* We found a suitable ximage */
1926 g_mutex_unlock (ximagesink->pool_lock);
1928 /* We haven't found anything, creating a new one */
1930 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1932 /* Now we should have a ximage, set appropriate caps on it */
1934 /* Make sure the buffer is cleared of any previously used flags */
1935 GST_MINI_OBJECT_CAST (ximage)->flags = 0;
1936 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1939 /* could be our new reffed suggestion or the original unreffed caps */
1941 gst_caps_unref (alloc_caps);
1943 *buf = GST_BUFFER_CAST (ximage);
1949 /* Interfaces stuff */
1952 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1954 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1959 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1961 klass->supported = gst_ximagesink_interface_supported;
1965 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1966 GstStructure * structure)
1968 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1970 gint x_offset, y_offset;
1974 event = gst_event_new_navigation (structure);
1976 /* We are not converting the pointer coordinates as there's no hardware
1977 scaling done here. The only possible scaling is done by videoscale and
1978 videoscale will have to catch those events and tranform the coordinates
1979 to match the applied scaling. So here we just add the offset if the image
1980 is centered in the window. */
1982 /* We take the flow_lock while we look at the window */
1983 g_mutex_lock (ximagesink->flow_lock);
1985 if (!ximagesink->xwindow) {
1986 g_mutex_unlock (ximagesink->flow_lock);
1990 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1991 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1993 g_mutex_unlock (ximagesink->flow_lock);
1995 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1997 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1999 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
2001 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
2004 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
2006 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
2007 gst_pad_send_event (pad, event);
2009 gst_object_unref (pad);
2014 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
2016 iface->send_event = gst_ximagesink_navigation_send_event;
2020 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2022 XID xwindow_id = id;
2023 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2024 GstXWindow *xwindow = NULL;
2025 XWindowAttributes attr;
2027 /* We acquire the stream lock while setting this window in the element.
2028 We are basically cleaning tons of stuff replacing the old window, putting
2029 images while we do that would surely crash */
2030 g_mutex_lock (ximagesink->flow_lock);
2032 /* If we already use that window return */
2033 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
2034 g_mutex_unlock (ximagesink->flow_lock);
2038 /* If the element has not initialized the X11 context try to do so */
2039 if (!ximagesink->xcontext &&
2040 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
2041 g_mutex_unlock (ximagesink->flow_lock);
2042 /* we have thrown a GST_ELEMENT_ERROR now */
2046 /* If a window is there already we destroy it */
2047 if (ximagesink->xwindow) {
2048 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2049 ximagesink->xwindow = NULL;
2052 /* If the xid is 0 we go back to an internal window */
2053 if (xwindow_id == 0) {
2054 /* If no width/height caps nego did not happen window will be created
2055 during caps nego then */
2056 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
2057 xwindow = gst_ximagesink_xwindow_new (ximagesink,
2058 GST_VIDEO_SINK_WIDTH (ximagesink),
2059 GST_VIDEO_SINK_HEIGHT (ximagesink));
2062 xwindow = g_new0 (GstXWindow, 1);
2064 xwindow->win = xwindow_id;
2066 /* We get window geometry, set the event we want to receive,
2068 g_mutex_lock (ximagesink->x_lock);
2069 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
2070 xwindow->width = attr.width;
2071 xwindow->height = attr.height;
2072 xwindow->internal = FALSE;
2073 if (ximagesink->handle_events) {
2074 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
2075 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2079 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
2080 g_mutex_unlock (ximagesink->x_lock);
2084 ximagesink->xwindow = xwindow;
2086 g_mutex_unlock (ximagesink->flow_lock);
2090 gst_ximagesink_expose (GstXOverlay * overlay)
2092 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2094 gst_ximagesink_xwindow_update_geometry (ximagesink);
2095 gst_ximagesink_ximage_put (ximagesink, NULL);
2099 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
2100 gboolean handle_events)
2102 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
2104 ximagesink->handle_events = handle_events;
2106 g_mutex_lock (ximagesink->flow_lock);
2108 if (G_UNLIKELY (!ximagesink->xwindow)) {
2109 g_mutex_unlock (ximagesink->flow_lock);
2113 g_mutex_lock (ximagesink->x_lock);
2115 if (handle_events) {
2116 if (ximagesink->xwindow->internal) {
2117 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2118 ExposureMask | StructureNotifyMask | PointerMotionMask |
2119 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2121 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
2122 ExposureMask | StructureNotifyMask | PointerMotionMask |
2123 KeyPressMask | KeyReleaseMask);
2126 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
2129 g_mutex_unlock (ximagesink->x_lock);
2131 g_mutex_unlock (ximagesink->flow_lock);
2135 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
2137 iface->set_window_handle = gst_ximagesink_set_window_handle;
2138 iface->expose = gst_ximagesink_expose;
2139 iface->handle_events = gst_ximagesink_set_event_handling;
2142 /* =========================================== */
2144 /* Init & Class init */
2146 /* =========================================== */
2149 gst_ximagesink_set_property (GObject * object, guint prop_id,
2150 const GValue * value, GParamSpec * pspec)
2152 GstXImageSink *ximagesink;
2154 g_return_if_fail (GST_IS_XIMAGESINK (object));
2156 ximagesink = GST_XIMAGESINK (object);
2160 ximagesink->display_name = g_strdup (g_value_get_string (value));
2162 case PROP_SYNCHRONOUS:
2163 ximagesink->synchronous = g_value_get_boolean (value);
2164 if (ximagesink->xcontext) {
2165 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2166 ximagesink->synchronous ? "TRUE" : "FALSE");
2167 g_mutex_lock (ximagesink->x_lock);
2168 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2169 g_mutex_unlock (ximagesink->x_lock);
2172 case PROP_FORCE_ASPECT_RATIO:
2173 ximagesink->keep_aspect = g_value_get_boolean (value);
2175 case PROP_PIXEL_ASPECT_RATIO:
2179 tmp = g_new0 (GValue, 1);
2180 g_value_init (tmp, GST_TYPE_FRACTION);
2182 if (!g_value_transform (value, tmp)) {
2183 GST_WARNING_OBJECT (ximagesink,
2184 "Could not transform string to aspect ratio");
2187 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2188 gst_value_get_fraction_numerator (tmp),
2189 gst_value_get_fraction_denominator (tmp));
2190 g_free (ximagesink->par);
2191 ximagesink->par = tmp;
2195 case PROP_HANDLE_EVENTS:
2196 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2197 g_value_get_boolean (value));
2198 gst_ximagesink_manage_event_thread (ximagesink);
2200 case PROP_HANDLE_EXPOSE:
2201 ximagesink->handle_expose = g_value_get_boolean (value);
2202 gst_ximagesink_manage_event_thread (ximagesink);
2205 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2211 gst_ximagesink_get_property (GObject * object, guint prop_id,
2212 GValue * value, GParamSpec * pspec)
2214 GstXImageSink *ximagesink;
2216 g_return_if_fail (GST_IS_XIMAGESINK (object));
2218 ximagesink = GST_XIMAGESINK (object);
2222 g_value_set_string (value, ximagesink->display_name);
2224 case PROP_SYNCHRONOUS:
2225 g_value_set_boolean (value, ximagesink->synchronous);
2227 case PROP_FORCE_ASPECT_RATIO:
2228 g_value_set_boolean (value, ximagesink->keep_aspect);
2230 case PROP_PIXEL_ASPECT_RATIO:
2231 if (ximagesink->par)
2232 g_value_transform (ximagesink->par, value);
2234 case PROP_HANDLE_EVENTS:
2235 g_value_set_boolean (value, ximagesink->handle_events);
2237 case PROP_HANDLE_EXPOSE:
2238 g_value_set_boolean (value, ximagesink->handle_expose);
2240 case PROP_WINDOW_WIDTH:
2241 if (ximagesink->xwindow)
2242 g_value_set_uint64 (value, ximagesink->xwindow->width);
2244 g_value_set_uint64 (value, 0);
2246 case PROP_WINDOW_HEIGHT:
2247 if (ximagesink->xwindow)
2248 g_value_set_uint64 (value, ximagesink->xwindow->height);
2250 g_value_set_uint64 (value, 0);
2253 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2259 gst_ximagesink_reset (GstXImageSink * ximagesink)
2263 GST_OBJECT_LOCK (ximagesink);
2264 ximagesink->running = FALSE;
2265 /* grab thread and mark it as NULL */
2266 thread = ximagesink->event_thread;
2267 ximagesink->event_thread = NULL;
2268 GST_OBJECT_UNLOCK (ximagesink);
2270 /* Wait for our event thread to finish before we clean up our stuff. */
2272 g_thread_join (thread);
2274 if (ximagesink->ximage) {
2275 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
2276 ximagesink->ximage = NULL;
2278 if (ximagesink->cur_image) {
2279 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->cur_image));
2280 ximagesink->cur_image = NULL;
2283 gst_ximagesink_bufferpool_clear (ximagesink);
2285 g_mutex_lock (ximagesink->flow_lock);
2286 if (ximagesink->xwindow) {
2287 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2288 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2289 ximagesink->xwindow = NULL;
2291 g_mutex_unlock (ximagesink->flow_lock);
2293 gst_ximagesink_xcontext_clear (ximagesink);
2297 gst_ximagesink_finalize (GObject * object)
2299 GstXImageSink *ximagesink;
2301 ximagesink = GST_XIMAGESINK (object);
2303 gst_ximagesink_reset (ximagesink);
2305 if (ximagesink->display_name) {
2306 g_free (ximagesink->display_name);
2307 ximagesink->display_name = NULL;
2309 if (ximagesink->par) {
2310 g_free (ximagesink->par);
2311 ximagesink->par = NULL;
2313 if (ximagesink->x_lock) {
2314 g_mutex_free (ximagesink->x_lock);
2315 ximagesink->x_lock = NULL;
2317 if (ximagesink->flow_lock) {
2318 g_mutex_free (ximagesink->flow_lock);
2319 ximagesink->flow_lock = NULL;
2321 if (ximagesink->pool_lock) {
2322 g_mutex_free (ximagesink->pool_lock);
2323 ximagesink->pool_lock = NULL;
2326 g_free (ximagesink->media_title);
2328 G_OBJECT_CLASS (parent_class)->finalize (object);
2332 gst_ximagesink_init (GstXImageSink * ximagesink)
2334 ximagesink->display_name = NULL;
2335 ximagesink->xcontext = NULL;
2336 ximagesink->xwindow = NULL;
2337 ximagesink->ximage = NULL;
2338 ximagesink->cur_image = NULL;
2340 ximagesink->event_thread = NULL;
2341 ximagesink->running = FALSE;
2343 ximagesink->fps_n = 0;
2344 ximagesink->fps_d = 1;
2346 ximagesink->x_lock = g_mutex_new ();
2347 ximagesink->flow_lock = g_mutex_new ();
2349 ximagesink->par = NULL;
2351 ximagesink->pool_lock = g_mutex_new ();
2352 ximagesink->buffer_pool = NULL;
2354 ximagesink->synchronous = FALSE;
2355 ximagesink->keep_aspect = FALSE;
2356 ximagesink->handle_events = TRUE;
2357 ximagesink->handle_expose = TRUE;
2361 gst_ximagesink_base_init (gpointer g_class)
2363 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2365 gst_element_class_set_details_simple (element_class,
2366 "Video sink", "Sink/Video",
2367 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
2369 gst_element_class_add_pad_template (element_class,
2370 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2374 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2376 GObjectClass *gobject_class;
2377 GstElementClass *gstelement_class;
2378 GstBaseSinkClass *gstbasesink_class;
2379 GstVideoSinkClass *videosink_class;
2381 gobject_class = (GObjectClass *) klass;
2382 gstelement_class = (GstElementClass *) klass;
2383 gstbasesink_class = (GstBaseSinkClass *) klass;
2384 videosink_class = (GstVideoSinkClass *) klass;
2386 parent_class = g_type_class_peek_parent (klass);
2388 gobject_class->finalize = gst_ximagesink_finalize;
2389 gobject_class->set_property = gst_ximagesink_set_property;
2390 gobject_class->get_property = gst_ximagesink_get_property;
2392 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2393 g_param_spec_string ("display", "Display", "X Display name",
2394 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2395 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2396 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2397 "the X display in synchronous mode. (used only for debugging)", FALSE,
2398 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2399 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2400 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2401 "When enabled, reverse caps negotiation (scaling) will respect "
2402 "original aspect ratio", FALSE,
2403 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2404 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2405 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2406 "The pixel aspect ratio of the device", "1/1",
2407 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2408 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2409 g_param_spec_boolean ("handle-events", "Handle XEvents",
2410 "When enabled, XEvents will be selected and handled", TRUE,
2411 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2412 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2413 g_param_spec_boolean ("handle-expose", "Handle expose",
2415 "the current frame will always be drawn in response to X Expose "
2416 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2419 * GstXImageSink:window-width
2421 * Actual width of the video window.
2425 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2426 g_param_spec_uint64 ("window-width", "window-width",
2427 "Width of the window", 0, G_MAXUINT64, 0,
2428 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2431 * GstXImageSink:window-height
2433 * Actual height of the video window.
2437 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2438 g_param_spec_uint64 ("window-height", "window-height",
2439 "Height of the window", 0, G_MAXUINT64, 0,
2440 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2442 gstelement_class->change_state = gst_ximagesink_change_state;
2444 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2445 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2446 gstbasesink_class->buffer_alloc =
2447 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2448 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2449 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
2451 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2454 /* ============================================================= */
2456 /* Public Methods */
2458 /* ============================================================= */
2460 /* =========================================== */
2462 /* Object typing & Creation */
2464 /* =========================================== */
2467 gst_ximagesink_get_type (void)
2469 static GType ximagesink_type = 0;
2471 if (!ximagesink_type) {
2472 static const GTypeInfo ximagesink_info = {
2473 sizeof (GstXImageSinkClass),
2474 gst_ximagesink_base_init,
2476 (GClassInitFunc) gst_ximagesink_class_init,
2479 sizeof (GstXImageSink), 0, (GInstanceInitFunc) gst_ximagesink_init,
2481 static const GInterfaceInfo iface_info = {
2482 (GInterfaceInitFunc) gst_ximagesink_interface_init, NULL, NULL,
2484 static const GInterfaceInfo navigation_info = {
2485 (GInterfaceInitFunc) gst_ximagesink_navigation_init, NULL, NULL,
2487 static const GInterfaceInfo overlay_info = {
2488 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init, NULL, NULL,
2491 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2492 "GstXImageSink", &ximagesink_info, 0);
2494 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2496 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2498 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2501 /* register type and create class in a more safe place instead of at
2502 * runtime since the type registration and class creation is not
2504 g_type_class_ref (gst_ximage_buffer_get_type ());
2507 return ximagesink_type;