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_xwindow_update_geometry (GstXImageSink * ximagesink);
133 static void gst_ximagesink_expose (GstXOverlay * overlay);
135 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
136 GST_STATIC_PAD_TEMPLATE ("sink",
139 GST_STATIC_CAPS ("video/x-raw-rgb, "
140 "framerate = (fraction) [ 0, MAX ], "
141 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
149 PROP_PIXEL_ASPECT_RATIO,
150 PROP_FORCE_ASPECT_RATIO,
157 /* ============================================================= */
161 /* ============================================================= */
163 /* =========================================== */
165 /* Object typing & Creation */
167 /* =========================================== */
168 static void gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass);
169 static void gst_ximagesink_navigation_init (GstNavigationInterface * klass);
170 static void gst_ximagesink_xoverlay_init (GstXOverlayClass * klass);
171 #define gst_ximagesink_parent_class parent_class
172 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
173 G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
174 gst_ximagesink_interface_init);
175 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
176 G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_ximagesink_xoverlay_init));
178 /* ============================================================= */
180 /* Private Methods */
182 /* ============================================================= */
186 /* We are called with the x_lock taken */
188 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
189 GstXWindow * xwindow, GstVideoRectangle rect)
191 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
192 g_return_if_fail (xwindow != NULL);
194 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
195 ximagesink->xcontext->black);
199 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
200 0, 0, rect.x, xwindow->height);
204 if ((rect.x + rect.w) < xwindow->width) {
205 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
206 rect.x + rect.w, 0, xwindow->width, xwindow->height);
211 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
212 0, 0, xwindow->width, rect.y);
216 if ((rect.y + rect.h) < xwindow->height) {
217 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
218 0, rect.y + rect.h, xwindow->width, xwindow->height);
222 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
224 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
227 GstVideoRectangle src, dst, result;
228 gboolean draw_border = FALSE;
230 /* We take the flow_lock. If expose is in there we don't want to run
231 concurrently from the data flow thread */
232 g_mutex_lock (ximagesink->flow_lock);
234 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
235 g_mutex_unlock (ximagesink->flow_lock);
239 /* Draw borders when displaying the first frame. After this
240 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
241 if (!ximagesink->cur_image || ximagesink->draw_border) {
245 /* Store a reference to the last image we put, lose the previous one */
246 if (ximage && ximagesink->cur_image != ximage) {
247 if (ximagesink->cur_image) {
248 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
249 gst_buffer_unref (ximagesink->cur_image);
251 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
252 ximagesink->cur_image = gst_buffer_ref (ximage);
255 /* Expose sends a NULL image, we take the latest frame */
258 if (ximagesink->cur_image) {
259 ximage = ximagesink->cur_image;
261 g_mutex_unlock (ximagesink->flow_lock);
266 meta = gst_buffer_get_meta_ximage (ximage);
269 src.h = meta->height;
270 dst.w = ximagesink->xwindow->width;
271 dst.h = ximagesink->xwindow->height;
273 gst_video_sink_center_rect (src, dst, &result, FALSE);
275 g_mutex_lock (ximagesink->x_lock);
278 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
280 ximagesink->draw_border = FALSE;
283 if (ximagesink->xcontext->use_xshm) {
284 GST_LOG_OBJECT (ximagesink,
285 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
286 ximage, 0, 0, result.x, result.y, result.w, result.h,
287 ximagesink->xwindow->width, ximagesink->xwindow->height);
288 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
289 ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
290 result.w, result.h, FALSE);
292 #endif /* HAVE_XSHM */
294 GST_LOG_OBJECT (ximagesink,
295 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
296 ximage, 0, 0, result.x, result.y, result.w, result.h,
297 ximagesink->xwindow->width, ximagesink->xwindow->height);
298 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
299 ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
303 XSync (ximagesink->xcontext->disp, FALSE);
305 g_mutex_unlock (ximagesink->x_lock);
307 g_mutex_unlock (ximagesink->flow_lock);
313 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
316 Atom hints_atom = None;
319 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
320 g_return_val_if_fail (window != NULL, FALSE);
322 g_mutex_lock (ximagesink->x_lock);
324 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
326 if (hints_atom == None) {
327 g_mutex_unlock (ximagesink->x_lock);
331 hints = g_malloc0 (sizeof (MotifWmHints));
333 hints->flags |= MWM_HINTS_DECORATIONS;
334 hints->decorations = 1 << 0;
336 XChangeProperty (ximagesink->xcontext->disp, window->win,
337 hints_atom, hints_atom, 32, PropModeReplace,
338 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
340 XSync (ximagesink->xcontext->disp, FALSE);
342 g_mutex_unlock (ximagesink->x_lock);
350 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
351 GstXWindow * xwindow, const gchar * media_title)
354 g_free (ximagesink->media_title);
355 ximagesink->media_title = g_strdup (media_title);
358 /* we have a window */
359 if (xwindow->internal) {
360 XTextProperty xproperty;
361 const gchar *app_name;
362 const gchar *title = NULL;
363 gchar *title_mem = NULL;
365 /* set application name as a title */
366 app_name = g_get_application_name ();
368 if (app_name && ximagesink->media_title) {
369 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
371 } else if (app_name) {
373 } else if (ximagesink->media_title) {
374 title = ximagesink->media_title;
378 if ((XStringListToTextProperty (((char **) &title), 1,
380 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
381 XFree (xproperty.value);
390 /* This function handles a GstXWindow creation */
392 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
394 GstXWindow *xwindow = NULL;
397 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
399 xwindow = g_new0 (GstXWindow, 1);
401 xwindow->width = width;
402 xwindow->height = height;
403 xwindow->internal = TRUE;
405 g_mutex_lock (ximagesink->x_lock);
407 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
408 ximagesink->xcontext->root,
409 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
411 /* We have to do that to prevent X from redrawing the background on
412 ConfigureNotify. This takes away flickering of video when resizing. */
413 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
415 /* set application name as a title */
416 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
418 if (ximagesink->handle_events) {
421 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
422 StructureNotifyMask | PointerMotionMask | KeyPressMask |
423 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
425 /* Tell the window manager we'd like delete client messages instead of
427 wm_delete = XInternAtom (ximagesink->xcontext->disp,
428 "WM_DELETE_WINDOW", False);
429 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
433 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
436 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
438 XSync (ximagesink->xcontext->disp, FALSE);
440 g_mutex_unlock (ximagesink->x_lock);
442 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
444 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
449 /* This function destroys a GstXWindow */
451 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
452 GstXWindow * xwindow)
454 g_return_if_fail (xwindow != NULL);
455 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
457 g_mutex_lock (ximagesink->x_lock);
459 /* If we did not create that window we just free the GC and let it live */
460 if (xwindow->internal)
461 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
463 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
465 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
467 XSync (ximagesink->xcontext->disp, FALSE);
469 g_mutex_unlock (ximagesink->x_lock);
475 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
477 XWindowAttributes attr;
478 gboolean reconfigure;
480 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
482 /* Update the window geometry */
483 g_mutex_lock (ximagesink->x_lock);
484 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
485 g_mutex_unlock (ximagesink->x_lock);
489 XGetWindowAttributes (ximagesink->xcontext->disp,
490 ximagesink->xwindow->win, &attr);
492 /* Check if we would suggest a different width/height now */
493 reconfigure = (ximagesink->xwindow->width != attr.width)
494 || (ximagesink->xwindow->height != attr.height);
495 ximagesink->xwindow->width = attr.width;
496 ximagesink->xwindow->height = attr.height;
498 g_mutex_unlock (ximagesink->x_lock);
501 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
502 gst_event_new_reconfigure ());
506 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
508 g_return_if_fail (xwindow != NULL);
509 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
511 g_mutex_lock (ximagesink->x_lock);
513 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
514 ximagesink->xcontext->black);
516 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
517 0, 0, xwindow->width, xwindow->height);
519 XSync (ximagesink->xcontext->disp, FALSE);
521 g_mutex_unlock (ximagesink->x_lock);
524 /* This function handles XEvents that might be in the queue. It generates
525 GstEvent that will be sent upstream in the pipeline to handle interactivity
528 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
531 guint pointer_x = 0, pointer_y = 0;
532 gboolean pointer_moved = FALSE;
533 gboolean exposed = FALSE, configured = FALSE;
535 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
537 /* Then we get all pointer motion events, only the last position is
539 g_mutex_lock (ximagesink->flow_lock);
540 g_mutex_lock (ximagesink->x_lock);
541 while (XCheckWindowEvent (ximagesink->xcontext->disp,
542 ximagesink->xwindow->win, PointerMotionMask, &e)) {
543 g_mutex_unlock (ximagesink->x_lock);
544 g_mutex_unlock (ximagesink->flow_lock);
548 pointer_x = e.xmotion.x;
549 pointer_y = e.xmotion.y;
550 pointer_moved = TRUE;
555 g_mutex_lock (ximagesink->flow_lock);
556 g_mutex_lock (ximagesink->x_lock);
560 g_mutex_unlock (ximagesink->x_lock);
561 g_mutex_unlock (ximagesink->flow_lock);
563 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
564 pointer_x, pointer_y);
565 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
566 "mouse-move", 0, pointer_x, pointer_y);
568 g_mutex_lock (ximagesink->flow_lock);
569 g_mutex_lock (ximagesink->x_lock);
572 /* We get all remaining events on our window to throw them upstream */
573 while (XCheckWindowEvent (ximagesink->xcontext->disp,
574 ximagesink->xwindow->win,
575 KeyPressMask | KeyReleaseMask |
576 ButtonPressMask | ButtonReleaseMask, &e)) {
579 /* We lock only for the X function call */
580 g_mutex_unlock (ximagesink->x_lock);
581 g_mutex_unlock (ximagesink->flow_lock);
585 /* Mouse button pressed/released over our window. We send upstream
586 events for interactivity/navigation */
587 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
588 e.xbutton.button, e.xbutton.x, e.xbutton.x);
589 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
590 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
593 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
594 e.xbutton.button, e.xbutton.x, e.xbutton.x);
595 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
596 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
600 /* Key pressed/released over our window. We send upstream
601 events for interactivity/navigation */
602 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
603 e.xkey.keycode, e.xkey.x, e.xkey.x);
604 g_mutex_lock (ximagesink->x_lock);
605 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
607 g_mutex_unlock (ximagesink->x_lock);
608 if (keysym != NoSymbol) {
609 char *key_str = NULL;
611 g_mutex_lock (ximagesink->x_lock);
612 key_str = XKeysymToString (keysym);
613 g_mutex_unlock (ximagesink->x_lock);
614 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
615 e.type == KeyPress ? "key-press" : "key-release", key_str);
617 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
618 e.type == KeyPress ? "key-press" : "key-release", "unknown");
622 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
625 g_mutex_lock (ximagesink->flow_lock);
626 g_mutex_lock (ximagesink->x_lock);
630 while (XCheckWindowEvent (ximagesink->xcontext->disp,
631 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
636 case ConfigureNotify:
637 g_mutex_unlock (ximagesink->x_lock);
638 gst_ximagesink_xwindow_update_geometry (ximagesink);
639 g_mutex_lock (ximagesink->x_lock);
647 if (ximagesink->handle_expose && (exposed || configured)) {
648 g_mutex_unlock (ximagesink->x_lock);
649 g_mutex_unlock (ximagesink->flow_lock);
651 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
653 g_mutex_lock (ximagesink->flow_lock);
654 g_mutex_lock (ximagesink->x_lock);
657 /* Handle Display events */
658 while (XPending (ximagesink->xcontext->disp)) {
659 XNextEvent (ximagesink->xcontext->disp, &e);
665 wm_delete = XInternAtom (ximagesink->xcontext->disp,
666 "WM_DELETE_WINDOW", False);
667 if (wm_delete == (Atom) e.xclient.data.l[0]) {
668 /* Handle window deletion by posting an error on the bus */
669 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
670 ("Output window was closed"), (NULL));
672 g_mutex_unlock (ximagesink->x_lock);
673 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
674 ximagesink->xwindow = NULL;
675 g_mutex_lock (ximagesink->x_lock);
684 g_mutex_unlock (ximagesink->x_lock);
685 g_mutex_unlock (ximagesink->flow_lock);
689 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
691 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
693 GST_OBJECT_LOCK (ximagesink);
694 while (ximagesink->running) {
695 GST_OBJECT_UNLOCK (ximagesink);
697 if (ximagesink->xwindow) {
698 gst_ximagesink_handle_xevents (ximagesink);
700 /* FIXME: do we want to align this with the framerate or anything else? */
701 g_usleep (G_USEC_PER_SEC / 20);
703 GST_OBJECT_LOCK (ximagesink);
705 GST_OBJECT_UNLOCK (ximagesink);
711 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
713 GThread *thread = NULL;
715 /* don't start the thread too early */
716 if (ximagesink->xcontext == NULL) {
720 GST_OBJECT_LOCK (ximagesink);
721 if (ximagesink->handle_expose || ximagesink->handle_events) {
722 if (!ximagesink->event_thread) {
723 /* Setup our event listening thread */
724 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
725 ximagesink->handle_expose, ximagesink->handle_events);
726 ximagesink->running = TRUE;
727 ximagesink->event_thread = g_thread_create (
728 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
731 if (ximagesink->event_thread) {
732 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
733 ximagesink->handle_expose, ximagesink->handle_events);
734 ximagesink->running = FALSE;
735 /* grab thread and mark it as NULL */
736 thread = ximagesink->event_thread;
737 ximagesink->event_thread = NULL;
740 GST_OBJECT_UNLOCK (ximagesink);
742 /* Wait for our event thread to finish */
744 g_thread_join (thread);
749 /* This function calculates the pixel aspect ratio based on the properties
750 * in the xcontext structure and stores it there. */
752 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
754 static const gint par[][2] = {
755 {1, 1}, /* regular screen */
756 {16, 15}, /* PAL TV */
757 {11, 10}, /* 525 line Rec.601 video */
758 {54, 59}, /* 625 line Rec.601 video */
759 {64, 45}, /* 1280x1024 on 16:9 display */
760 {5, 3}, /* 1280x1024 on 4:3 display */
761 {4, 3} /* 800x600 on 16:9 display */
768 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
770 /* first calculate the "real" ratio based on the X values;
771 * which is the "physical" w/h divided by the w/h in pixels of the display */
772 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
773 / (xcontext->heightmm * xcontext->width);
775 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
777 if (xcontext->width == 720 && xcontext->height == 576) {
778 ratio = 4.0 * 576 / (3.0 * 720);
780 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
782 /* now find the one from par[][2] with the lowest delta to the real one */
786 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
787 gdouble this_delta = DELTA (i);
789 if (this_delta < delta) {
795 GST_DEBUG ("Decided on index %d (%d/%d)", index,
796 par[index][0], par[index][1]);
798 g_free (xcontext->par);
799 xcontext->par = g_new0 (GValue, 1);
800 g_value_init (xcontext->par, GST_TYPE_FRACTION);
801 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
802 GST_DEBUG ("set xcontext PAR to %d/%d",
803 gst_value_get_fraction_numerator (xcontext->par),
804 gst_value_get_fraction_denominator (xcontext->par));
807 /* This function gets the X Display and global info about it. Everything is
808 stored in our object and will be cleaned when the object is disposed. Note
809 here that caps for supported format are generated without any window or
812 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
814 GstXContext *xcontext = NULL;
815 XPixmapFormatValues *px_formats = NULL;
816 gint nb_formats = 0, i;
818 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
820 xcontext = g_new0 (GstXContext, 1);
822 g_mutex_lock (ximagesink->x_lock);
824 xcontext->disp = XOpenDisplay (ximagesink->display_name);
826 if (!xcontext->disp) {
827 g_mutex_unlock (ximagesink->x_lock);
829 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
830 ("Could not initialise X output"), ("Could not open display"));
834 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
835 xcontext->screen_num = DefaultScreen (xcontext->disp);
836 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
837 xcontext->root = DefaultRootWindow (xcontext->disp);
838 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
839 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
840 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
842 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
843 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
844 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
845 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
847 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
848 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
850 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
852 /* We get supported pixmap formats at supported depth */
853 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
856 XCloseDisplay (xcontext->disp);
857 g_mutex_unlock (ximagesink->x_lock);
858 g_free (xcontext->par);
860 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
861 ("Could not get supported pixmap formats"), (NULL));
865 /* We get bpp value corresponding to our running depth */
866 for (i = 0; i < nb_formats; i++) {
867 if (px_formats[i].depth == xcontext->depth)
868 xcontext->bpp = px_formats[i].bits_per_pixel;
873 xcontext->endianness =
874 (ImageByteOrder (xcontext->disp) ==
875 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
877 /* Search for XShm extension support */
879 if (XShmQueryExtension (xcontext->disp) &&
880 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
881 xcontext->use_xshm = TRUE;
882 GST_DEBUG ("ximagesink is using XShm extension");
884 #endif /* HAVE_XSHM */
886 xcontext->use_xshm = FALSE;
887 GST_DEBUG ("ximagesink is not using XShm extension");
890 /* our caps system handles 24/32bpp RGB as big-endian. */
891 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
892 xcontext->endianness == G_LITTLE_ENDIAN) {
893 xcontext->endianness = G_BIG_ENDIAN;
894 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
895 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
896 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
897 if (xcontext->bpp == 24) {
898 xcontext->visual->red_mask >>= 8;
899 xcontext->visual->green_mask >>= 8;
900 xcontext->visual->blue_mask >>= 8;
904 /* update object's par with calculated one if not set yet */
905 if (!ximagesink->par) {
906 ximagesink->par = g_new0 (GValue, 1);
907 gst_value_init_and_copy (ximagesink->par, xcontext->par);
908 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
910 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
911 "bpp", G_TYPE_INT, xcontext->bpp,
912 "depth", G_TYPE_INT, xcontext->depth,
913 "endianness", G_TYPE_INT, xcontext->endianness,
914 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
915 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
916 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
917 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
918 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
919 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
920 if (ximagesink->par) {
923 nom = gst_value_get_fraction_numerator (ximagesink->par);
924 den = gst_value_get_fraction_denominator (ximagesink->par);
925 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
926 GST_TYPE_FRACTION, nom, den, NULL);
929 g_mutex_unlock (ximagesink->x_lock);
934 /* This function cleans the X context. Closing the Display and unrefing the
935 caps for supported formats. */
937 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
939 GstXContext *xcontext;
941 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
943 GST_OBJECT_LOCK (ximagesink);
944 if (ximagesink->xcontext == NULL) {
945 GST_OBJECT_UNLOCK (ximagesink);
949 /* Take the xcontext reference and NULL it while we
950 * clean it up, so that any buffer-alloced buffers
951 * arriving after this will be freed correctly */
952 xcontext = ximagesink->xcontext;
953 ximagesink->xcontext = NULL;
955 GST_OBJECT_UNLOCK (ximagesink);
957 gst_caps_unref (xcontext->caps);
958 g_free (xcontext->par);
959 g_free (ximagesink->par);
960 ximagesink->par = NULL;
962 if (xcontext->last_caps)
963 gst_caps_replace (&xcontext->last_caps, NULL);
965 g_mutex_lock (ximagesink->x_lock);
967 XCloseDisplay (xcontext->disp);
969 g_mutex_unlock (ximagesink->x_lock);
977 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
979 GstXImageSink *ximagesink;
983 ximagesink = GST_XIMAGESINK (bsink);
985 g_mutex_lock (ximagesink->x_lock);
986 if (ximagesink->xcontext) {
989 caps = gst_caps_ref (ximagesink->xcontext->caps);
992 GstCaps *intersection;
995 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
996 gst_caps_unref (caps);
1000 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1001 GstStructure *s0, *s1;
1003 caps = gst_caps_make_writable (caps);
1005 /* There can only be a single structure because the xcontext
1006 * caps only have a single structure */
1007 s0 = gst_caps_get_structure (caps, 0);
1008 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1010 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1011 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1012 gst_caps_append_structure (caps, s1);
1014 /* This will not change the order but will remove the
1015 * fixed width/height caps again if not possible
1018 GstCaps *intersection;
1021 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1022 gst_caps_unref (caps);
1023 caps = intersection;
1027 g_mutex_unlock (ximagesink->x_lock);
1030 g_mutex_unlock (ximagesink->x_lock);
1032 /* get a template copy and add the pixel aspect ratio */
1034 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
1035 (ximagesink)->sinkpad));
1036 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1037 GstStructure *structure = gst_caps_get_structure (caps, i);
1039 if (ximagesink->par) {
1042 nom = gst_value_get_fraction_numerator (ximagesink->par);
1043 den = gst_value_get_fraction_denominator (ximagesink->par);
1044 gst_structure_set (structure, "pixel-aspect-ratio",
1045 GST_TYPE_FRACTION, nom, den, NULL);
1050 GstCaps *intersection;
1053 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1054 gst_caps_unref (caps);
1055 caps = intersection;
1062 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1064 GstXImageSink *ximagesink;
1065 GstStructure *structure;
1066 GstBufferPool *newpool, *oldpool;
1067 gboolean ret = TRUE;
1069 gint new_width, new_height;
1072 ximagesink = GST_XIMAGESINK (bsink);
1074 if (!ximagesink->xcontext)
1077 GST_DEBUG_OBJECT (ximagesink,
1078 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1079 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1081 /* We intersect those caps with our template to make sure they are correct */
1082 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1083 goto incompatible_caps;
1085 structure = gst_caps_get_structure (caps, 0);
1087 ret &= gst_structure_get_int (structure, "width", &new_width);
1088 ret &= gst_structure_get_int (structure, "height", &new_height);
1089 fps = gst_structure_get_value (structure, "framerate");
1090 ret &= (fps != NULL);
1095 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1096 * otherwise linking should fail */
1097 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1099 if (ximagesink->par) {
1100 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1103 } else if (ximagesink->xcontext->par) {
1104 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1110 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1111 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1112 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1113 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1115 /* Notify application to set xwindow id now */
1116 g_mutex_lock (ximagesink->flow_lock);
1117 if (!ximagesink->xwindow) {
1118 g_mutex_unlock (ximagesink->flow_lock);
1119 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1121 g_mutex_unlock (ximagesink->flow_lock);
1124 /* Creating our window and our image */
1125 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1126 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1129 g_mutex_lock (ximagesink->flow_lock);
1130 if (!ximagesink->xwindow) {
1131 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1132 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1134 /* Remember to draw borders for next frame */
1135 ximagesink->draw_border = TRUE;
1137 /* create a new pool for the new configuration */
1138 newpool = gst_ximage_buffer_pool_new (ximagesink);
1140 structure = gst_buffer_pool_get_config (newpool);
1141 gst_buffer_pool_config_set (structure, caps, 0, 0, 0, 0, 0, 16);
1142 if (!gst_buffer_pool_set_config (newpool, structure))
1145 if (!gst_buffer_pool_set_active (newpool, TRUE))
1146 goto activate_failed;
1148 oldpool = ximagesink->pool;
1149 ximagesink->pool = newpool;
1151 /* unref the old sink */
1154 gst_buffer_pool_set_active (oldpool, FALSE);
1155 gst_object_unref (oldpool);
1157 g_mutex_unlock (ximagesink->flow_lock);
1164 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1169 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1174 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1175 ("Invalid image size."));
1180 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1181 g_mutex_unlock (ximagesink->flow_lock);
1186 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1187 g_mutex_unlock (ximagesink->flow_lock);
1192 static GstStateChangeReturn
1193 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1195 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1196 GstXImageSink *ximagesink;
1197 GstXContext *xcontext = NULL;
1199 ximagesink = GST_XIMAGESINK (element);
1201 switch (transition) {
1202 case GST_STATE_CHANGE_NULL_TO_READY:
1203 /* Initializing the XContext */
1204 if (ximagesink->xcontext == NULL) {
1205 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1206 if (xcontext == NULL) {
1207 ret = GST_STATE_CHANGE_FAILURE;
1210 GST_OBJECT_LOCK (ximagesink);
1212 ximagesink->xcontext = xcontext;
1213 GST_OBJECT_UNLOCK (ximagesink);
1216 /* call XSynchronize with the current value of synchronous */
1217 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1218 ximagesink->synchronous ? "TRUE" : "FALSE");
1219 g_mutex_lock (ximagesink->x_lock);
1220 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1221 g_mutex_unlock (ximagesink->x_lock);
1222 gst_ximagesink_manage_event_thread (ximagesink);
1224 case GST_STATE_CHANGE_READY_TO_PAUSED:
1225 g_mutex_lock (ximagesink->flow_lock);
1226 if (ximagesink->xwindow)
1227 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1228 g_mutex_unlock (ximagesink->flow_lock);
1230 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1236 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1238 switch (transition) {
1239 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1241 case GST_STATE_CHANGE_PAUSED_TO_READY:
1242 ximagesink->fps_n = 0;
1243 ximagesink->fps_d = 1;
1244 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1245 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1246 g_mutex_lock (ximagesink->flow_lock);
1247 if (ximagesink->pool)
1248 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1249 g_mutex_unlock (ximagesink->flow_lock);
1251 case GST_STATE_CHANGE_READY_TO_NULL:
1252 gst_ximagesink_reset (ximagesink);
1263 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1264 GstClockTime * start, GstClockTime * end)
1266 GstXImageSink *ximagesink;
1268 ximagesink = GST_XIMAGESINK (bsink);
1270 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1271 *start = GST_BUFFER_TIMESTAMP (buf);
1272 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1273 *end = *start + GST_BUFFER_DURATION (buf);
1275 if (ximagesink->fps_n > 0) {
1277 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1284 static GstFlowReturn
1285 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1288 GstXImageSink *ximagesink;
1289 GstMetaXImage *meta;
1293 ximagesink = GST_XIMAGESINK (vsink);
1295 meta = gst_buffer_get_meta_ximage (buf);
1298 /* If this buffer has been allocated using our buffer management we simply
1299 put the ximage which is in the PRIVATE pointer */
1300 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1307 /* Else we have to copy the data into our private image, */
1308 /* if we have one... */
1309 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1311 /* we should have a pool, configured in setcaps */
1312 if (ximagesink->pool == NULL)
1315 /* take a buffer form our pool */
1316 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &temp, NULL);
1317 if (res != GST_FLOW_OK)
1322 if (gst_buffer_get_size (temp) < gst_buffer_get_size (buf))
1325 data = gst_buffer_map (temp, &size, NULL, GST_MAP_WRITE);
1326 gst_buffer_extract (buf, 0, data, size);
1327 gst_buffer_unmap (temp, data, size);
1332 if (!gst_ximagesink_ximage_put (ximagesink, buf))
1337 gst_buffer_unref (buf);
1344 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1345 ("Internal error: can't allocate images"),
1346 ("We don't have a bufferpool negotiated"));
1347 return GST_FLOW_ERROR;
1351 /* No image available. That's very bad ! */
1352 GST_WARNING_OBJECT (ximagesink, "could not create image");
1357 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1358 ("Failed to create output image buffer"),
1359 ("XServer allocated buffer size did not match input buffer %"
1360 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (temp),
1361 gst_buffer_get_size (buf)));
1362 res = GST_FLOW_ERROR;
1367 /* No Window available to put our image into */
1368 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1369 res = GST_FLOW_ERROR;
1375 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1377 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1379 switch (GST_EVENT_TYPE (event)) {
1380 case GST_EVENT_TAG:{
1382 gchar *title = NULL;
1384 gst_event_parse_tag (event, &l);
1385 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1388 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1389 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1399 if (GST_BASE_SINK_CLASS (parent_class)->event)
1400 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1405 /* Interfaces stuff */
1408 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1410 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1415 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1417 klass->supported = gst_ximagesink_interface_supported;
1421 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1422 GstStructure * structure)
1424 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1426 gint x_offset, y_offset;
1430 event = gst_event_new_navigation (structure);
1432 /* We are not converting the pointer coordinates as there's no hardware
1433 scaling done here. The only possible scaling is done by videoscale and
1434 videoscale will have to catch those events and tranform the coordinates
1435 to match the applied scaling. So here we just add the offset if the image
1436 is centered in the window. */
1438 /* We take the flow_lock while we look at the window */
1439 g_mutex_lock (ximagesink->flow_lock);
1441 if (!ximagesink->xwindow) {
1442 g_mutex_unlock (ximagesink->flow_lock);
1446 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1447 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1449 g_mutex_unlock (ximagesink->flow_lock);
1451 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1453 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1455 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1457 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1460 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1462 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1463 gst_pad_send_event (pad, event);
1465 gst_object_unref (pad);
1470 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1472 iface->send_event = gst_ximagesink_navigation_send_event;
1476 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
1478 XID xwindow_id = id;
1479 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1480 GstXWindow *xwindow = NULL;
1481 XWindowAttributes attr;
1483 /* We acquire the stream lock while setting this window in the element.
1484 We are basically cleaning tons of stuff replacing the old window, putting
1485 images while we do that would surely crash */
1486 g_mutex_lock (ximagesink->flow_lock);
1488 /* If we already use that window return */
1489 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1490 g_mutex_unlock (ximagesink->flow_lock);
1494 /* If the element has not initialized the X11 context try to do so */
1495 if (!ximagesink->xcontext &&
1496 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1497 g_mutex_unlock (ximagesink->flow_lock);
1498 /* we have thrown a GST_ELEMENT_ERROR now */
1502 /* If a window is there already we destroy it */
1503 if (ximagesink->xwindow) {
1504 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1505 ximagesink->xwindow = NULL;
1508 /* If the xid is 0 we go back to an internal window */
1509 if (xwindow_id == 0) {
1510 /* If no width/height caps nego did not happen window will be created
1511 during caps nego then */
1512 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1513 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1514 GST_VIDEO_SINK_WIDTH (ximagesink),
1515 GST_VIDEO_SINK_HEIGHT (ximagesink));
1518 xwindow = g_new0 (GstXWindow, 1);
1520 xwindow->win = xwindow_id;
1522 /* We get window geometry, set the event we want to receive,
1524 g_mutex_lock (ximagesink->x_lock);
1525 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1526 xwindow->width = attr.width;
1527 xwindow->height = attr.height;
1528 xwindow->internal = FALSE;
1529 if (ximagesink->handle_events) {
1530 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1531 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1535 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1536 g_mutex_unlock (ximagesink->x_lock);
1540 ximagesink->xwindow = xwindow;
1542 g_mutex_unlock (ximagesink->flow_lock);
1546 gst_ximagesink_expose (GstXOverlay * overlay)
1548 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1550 gst_ximagesink_xwindow_update_geometry (ximagesink);
1551 gst_ximagesink_ximage_put (ximagesink, NULL);
1555 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1556 gboolean handle_events)
1558 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1560 ximagesink->handle_events = handle_events;
1562 g_mutex_lock (ximagesink->flow_lock);
1564 if (G_UNLIKELY (!ximagesink->xwindow)) {
1565 g_mutex_unlock (ximagesink->flow_lock);
1569 g_mutex_lock (ximagesink->x_lock);
1571 if (handle_events) {
1572 if (ximagesink->xwindow->internal) {
1573 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1574 ExposureMask | StructureNotifyMask | PointerMotionMask |
1575 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1577 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1578 ExposureMask | StructureNotifyMask | PointerMotionMask |
1579 KeyPressMask | KeyReleaseMask);
1582 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1585 g_mutex_unlock (ximagesink->x_lock);
1587 g_mutex_unlock (ximagesink->flow_lock);
1591 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1593 iface->set_window_handle = gst_ximagesink_set_window_handle;
1594 iface->expose = gst_ximagesink_expose;
1595 iface->handle_events = gst_ximagesink_set_event_handling;
1598 /* =========================================== */
1600 /* Init & Class init */
1602 /* =========================================== */
1605 gst_ximagesink_set_property (GObject * object, guint prop_id,
1606 const GValue * value, GParamSpec * pspec)
1608 GstXImageSink *ximagesink;
1610 g_return_if_fail (GST_IS_XIMAGESINK (object));
1612 ximagesink = GST_XIMAGESINK (object);
1616 ximagesink->display_name = g_strdup (g_value_get_string (value));
1618 case PROP_SYNCHRONOUS:
1619 ximagesink->synchronous = g_value_get_boolean (value);
1620 if (ximagesink->xcontext) {
1621 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1622 ximagesink->synchronous ? "TRUE" : "FALSE");
1623 g_mutex_lock (ximagesink->x_lock);
1624 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1625 g_mutex_unlock (ximagesink->x_lock);
1628 case PROP_FORCE_ASPECT_RATIO:
1629 ximagesink->keep_aspect = g_value_get_boolean (value);
1631 case PROP_PIXEL_ASPECT_RATIO:
1635 tmp = g_new0 (GValue, 1);
1636 g_value_init (tmp, GST_TYPE_FRACTION);
1638 if (!g_value_transform (value, tmp)) {
1639 GST_WARNING_OBJECT (ximagesink,
1640 "Could not transform string to aspect ratio");
1643 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1644 gst_value_get_fraction_numerator (tmp),
1645 gst_value_get_fraction_denominator (tmp));
1646 g_free (ximagesink->par);
1647 ximagesink->par = tmp;
1651 case PROP_HANDLE_EVENTS:
1652 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
1653 g_value_get_boolean (value));
1654 gst_ximagesink_manage_event_thread (ximagesink);
1656 case PROP_HANDLE_EXPOSE:
1657 ximagesink->handle_expose = g_value_get_boolean (value);
1658 gst_ximagesink_manage_event_thread (ximagesink);
1661 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1667 gst_ximagesink_get_property (GObject * object, guint prop_id,
1668 GValue * value, GParamSpec * pspec)
1670 GstXImageSink *ximagesink;
1672 g_return_if_fail (GST_IS_XIMAGESINK (object));
1674 ximagesink = GST_XIMAGESINK (object);
1678 g_value_set_string (value, ximagesink->display_name);
1680 case PROP_SYNCHRONOUS:
1681 g_value_set_boolean (value, ximagesink->synchronous);
1683 case PROP_FORCE_ASPECT_RATIO:
1684 g_value_set_boolean (value, ximagesink->keep_aspect);
1686 case PROP_PIXEL_ASPECT_RATIO:
1687 if (ximagesink->par)
1688 g_value_transform (ximagesink->par, value);
1690 case PROP_HANDLE_EVENTS:
1691 g_value_set_boolean (value, ximagesink->handle_events);
1693 case PROP_HANDLE_EXPOSE:
1694 g_value_set_boolean (value, ximagesink->handle_expose);
1696 case PROP_WINDOW_WIDTH:
1697 if (ximagesink->xwindow)
1698 g_value_set_uint64 (value, ximagesink->xwindow->width);
1700 g_value_set_uint64 (value, 0);
1702 case PROP_WINDOW_HEIGHT:
1703 if (ximagesink->xwindow)
1704 g_value_set_uint64 (value, ximagesink->xwindow->height);
1706 g_value_set_uint64 (value, 0);
1709 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1715 gst_ximagesink_reset (GstXImageSink * ximagesink)
1719 GST_OBJECT_LOCK (ximagesink);
1720 ximagesink->running = FALSE;
1721 /* grab thread and mark it as NULL */
1722 thread = ximagesink->event_thread;
1723 ximagesink->event_thread = NULL;
1724 GST_OBJECT_UNLOCK (ximagesink);
1726 /* Wait for our event thread to finish before we clean up our stuff. */
1728 g_thread_join (thread);
1730 if (ximagesink->cur_image) {
1731 gst_buffer_unref (ximagesink->cur_image);
1732 ximagesink->cur_image = NULL;
1735 g_mutex_lock (ximagesink->flow_lock);
1737 if (ximagesink->pool) {
1738 gst_object_unref (ximagesink->pool);
1739 ximagesink->pool = NULL;
1742 if (ximagesink->xwindow) {
1743 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1744 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1745 ximagesink->xwindow = NULL;
1747 g_mutex_unlock (ximagesink->flow_lock);
1749 gst_ximagesink_xcontext_clear (ximagesink);
1753 gst_ximagesink_finalize (GObject * object)
1755 GstXImageSink *ximagesink;
1757 ximagesink = GST_XIMAGESINK (object);
1759 gst_ximagesink_reset (ximagesink);
1761 if (ximagesink->display_name) {
1762 g_free (ximagesink->display_name);
1763 ximagesink->display_name = NULL;
1765 if (ximagesink->par) {
1766 g_free (ximagesink->par);
1767 ximagesink->par = NULL;
1769 if (ximagesink->x_lock) {
1770 g_mutex_free (ximagesink->x_lock);
1771 ximagesink->x_lock = NULL;
1773 if (ximagesink->flow_lock) {
1774 g_mutex_free (ximagesink->flow_lock);
1775 ximagesink->flow_lock = NULL;
1778 g_free (ximagesink->media_title);
1780 G_OBJECT_CLASS (parent_class)->finalize (object);
1784 gst_ximagesink_init (GstXImageSink * ximagesink)
1786 ximagesink->display_name = NULL;
1787 ximagesink->xcontext = NULL;
1788 ximagesink->xwindow = NULL;
1789 ximagesink->cur_image = NULL;
1791 ximagesink->event_thread = NULL;
1792 ximagesink->running = FALSE;
1794 ximagesink->fps_n = 0;
1795 ximagesink->fps_d = 1;
1797 ximagesink->x_lock = g_mutex_new ();
1798 ximagesink->flow_lock = g_mutex_new ();
1800 ximagesink->par = NULL;
1802 ximagesink->pool = NULL;
1804 ximagesink->synchronous = FALSE;
1805 ximagesink->keep_aspect = FALSE;
1806 ximagesink->handle_events = TRUE;
1807 ximagesink->handle_expose = TRUE;
1811 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1813 GObjectClass *gobject_class;
1814 GstElementClass *gstelement_class;
1815 GstBaseSinkClass *gstbasesink_class;
1816 GstVideoSinkClass *videosink_class;
1818 gobject_class = (GObjectClass *) klass;
1819 gstelement_class = (GstElementClass *) klass;
1820 gstbasesink_class = (GstBaseSinkClass *) klass;
1821 videosink_class = (GstVideoSinkClass *) klass;
1823 gobject_class->finalize = gst_ximagesink_finalize;
1824 gobject_class->set_property = gst_ximagesink_set_property;
1825 gobject_class->get_property = gst_ximagesink_get_property;
1827 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1828 g_param_spec_string ("display", "Display", "X Display name",
1829 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1830 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1831 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1832 "the X display in synchronous mode. (used only for debugging)", FALSE,
1833 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1834 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1835 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1836 "When enabled, reverse caps negotiation (scaling) will respect "
1837 "original aspect ratio", FALSE,
1838 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1839 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1840 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1841 "The pixel aspect ratio of the device", "1/1",
1842 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1843 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1844 g_param_spec_boolean ("handle-events", "Handle XEvents",
1845 "When enabled, XEvents will be selected and handled", TRUE,
1846 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1847 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1848 g_param_spec_boolean ("handle-expose", "Handle expose",
1850 "the current frame will always be drawn in response to X Expose "
1851 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1854 * GstXImageSink:window-width
1856 * Actual width of the video window.
1860 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1861 g_param_spec_uint64 ("window-width", "window-width",
1862 "Width of the window", 0, G_MAXUINT64, 0,
1863 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1866 * GstXImageSink:window-height
1868 * Actual height of the video window.
1872 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1873 g_param_spec_uint64 ("window-height", "window-height",
1874 "Height of the window", 0, G_MAXUINT64, 0,
1875 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1877 gst_element_class_set_details_simple (gstelement_class,
1878 "Video sink", "Sink/Video",
1879 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1881 gst_element_class_add_pad_template (gstelement_class,
1882 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1884 gstelement_class->change_state = gst_ximagesink_change_state;
1886 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1887 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1888 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1889 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1891 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);