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 ! videoconvert ! 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, 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>
110 #include <gst/video/gstmetavideo.h>
113 #include "ximagesink.h"
115 /* Debugging category */
116 #include <gst/gstinfo.h>
118 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
119 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
120 #define GST_CAT_DEFAULT gst_debug_ximagesink
125 unsigned long functions;
126 unsigned long decorations;
128 unsigned long status;
130 MotifWmHints, MwmHints;
132 #define MWM_HINTS_DECORATIONS (1L << 1)
134 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
135 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
136 static void gst_ximagesink_expose (GstXOverlay * overlay);
138 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
139 GST_STATIC_PAD_TEMPLATE ("sink",
142 GST_STATIC_CAPS ("video/x-raw, "
143 "framerate = (fraction) [ 0, MAX ], "
144 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
152 PROP_PIXEL_ASPECT_RATIO,
153 PROP_FORCE_ASPECT_RATIO,
160 /* ============================================================= */
164 /* ============================================================= */
166 /* =========================================== */
168 /* Object typing & Creation */
170 /* =========================================== */
171 static void gst_ximagesink_navigation_init (GstNavigationInterface * klass);
172 static void gst_ximagesink_xoverlay_init (GstXOverlayClass * klass);
173 #define gst_ximagesink_parent_class parent_class
174 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
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 GstMetaVideoCrop *crop;
228 GstVideoRectangle src, dst, result;
229 gboolean draw_border = FALSE;
231 /* We take the flow_lock. If expose is in there we don't want to run
232 concurrently from the data flow thread */
233 g_mutex_lock (ximagesink->flow_lock);
235 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
236 g_mutex_unlock (ximagesink->flow_lock);
240 /* Draw borders when displaying the first frame. After this
241 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
242 if (!ximagesink->cur_image || ximagesink->draw_border) {
246 /* Store a reference to the last image we put, lose the previous one */
247 if (ximage && ximagesink->cur_image != ximage) {
248 if (ximagesink->cur_image) {
249 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
250 gst_buffer_unref (ximagesink->cur_image);
252 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
253 ximagesink->cur_image = gst_buffer_ref (ximage);
256 /* Expose sends a NULL image, we take the latest frame */
259 if (ximagesink->cur_image) {
260 ximage = ximagesink->cur_image;
262 g_mutex_unlock (ximagesink->flow_lock);
267 meta = gst_buffer_get_meta_ximage (ximage);
268 crop = gst_buffer_get_meta_video_crop (ximage);
271 src.x = crop->x + meta->x;
272 src.y = crop->y + meta->y;
274 src.h = crop->height;
279 src.h = meta->height;
281 dst.w = ximagesink->xwindow->width;
282 dst.h = ximagesink->xwindow->height;
284 gst_video_sink_center_rect (src, dst, &result, FALSE);
286 g_mutex_lock (ximagesink->x_lock);
289 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
291 ximagesink->draw_border = FALSE;
294 if (ximagesink->xcontext->use_xshm) {
295 GST_LOG_OBJECT (ximagesink,
296 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
297 ximage, 0, 0, result.x, result.y, result.w, result.h,
298 ximagesink->xwindow->width, ximagesink->xwindow->height);
299 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
300 ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
301 result.w, result.h, FALSE);
303 #endif /* HAVE_XSHM */
305 GST_LOG_OBJECT (ximagesink,
306 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
307 ximage, 0, 0, result.x, result.y, result.w, result.h,
308 ximagesink->xwindow->width, ximagesink->xwindow->height);
309 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
310 ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
314 XSync (ximagesink->xcontext->disp, FALSE);
316 g_mutex_unlock (ximagesink->x_lock);
318 g_mutex_unlock (ximagesink->flow_lock);
324 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
327 Atom hints_atom = None;
330 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
331 g_return_val_if_fail (window != NULL, FALSE);
333 g_mutex_lock (ximagesink->x_lock);
335 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
337 if (hints_atom == None) {
338 g_mutex_unlock (ximagesink->x_lock);
342 hints = g_malloc0 (sizeof (MotifWmHints));
344 hints->flags |= MWM_HINTS_DECORATIONS;
345 hints->decorations = 1 << 0;
347 XChangeProperty (ximagesink->xcontext->disp, window->win,
348 hints_atom, hints_atom, 32, PropModeReplace,
349 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
351 XSync (ximagesink->xcontext->disp, FALSE);
353 g_mutex_unlock (ximagesink->x_lock);
361 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
362 GstXWindow * xwindow, const gchar * media_title)
365 g_free (ximagesink->media_title);
366 ximagesink->media_title = g_strdup (media_title);
369 /* we have a window */
370 if (xwindow->internal) {
371 XTextProperty xproperty;
372 const gchar *app_name;
373 const gchar *title = NULL;
374 gchar *title_mem = NULL;
376 /* set application name as a title */
377 app_name = g_get_application_name ();
379 if (app_name && ximagesink->media_title) {
380 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
382 } else if (app_name) {
384 } else if (ximagesink->media_title) {
385 title = ximagesink->media_title;
389 if ((XStringListToTextProperty (((char **) &title), 1,
391 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
392 XFree (xproperty.value);
401 /* This function handles a GstXWindow creation */
403 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
405 GstXWindow *xwindow = NULL;
408 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
410 xwindow = g_new0 (GstXWindow, 1);
412 xwindow->width = width;
413 xwindow->height = height;
414 xwindow->internal = TRUE;
416 g_mutex_lock (ximagesink->x_lock);
418 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
419 ximagesink->xcontext->root,
420 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
422 /* We have to do that to prevent X from redrawing the background on
423 ConfigureNotify. This takes away flickering of video when resizing. */
424 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
426 /* set application name as a title */
427 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
429 if (ximagesink->handle_events) {
432 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
433 StructureNotifyMask | PointerMotionMask | KeyPressMask |
434 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
436 /* Tell the window manager we'd like delete client messages instead of
438 wm_delete = XInternAtom (ximagesink->xcontext->disp,
439 "WM_DELETE_WINDOW", False);
440 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
444 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
447 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
449 XSync (ximagesink->xcontext->disp, FALSE);
451 g_mutex_unlock (ximagesink->x_lock);
453 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
455 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
460 /* This function destroys a GstXWindow */
462 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
463 GstXWindow * xwindow)
465 g_return_if_fail (xwindow != NULL);
466 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
468 g_mutex_lock (ximagesink->x_lock);
470 /* If we did not create that window we just free the GC and let it live */
471 if (xwindow->internal)
472 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
474 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
476 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
478 XSync (ximagesink->xcontext->disp, FALSE);
480 g_mutex_unlock (ximagesink->x_lock);
486 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
488 XWindowAttributes attr;
489 gboolean reconfigure;
491 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
493 /* Update the window geometry */
494 g_mutex_lock (ximagesink->x_lock);
495 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
496 g_mutex_unlock (ximagesink->x_lock);
500 XGetWindowAttributes (ximagesink->xcontext->disp,
501 ximagesink->xwindow->win, &attr);
503 /* Check if we would suggest a different width/height now */
504 reconfigure = (ximagesink->xwindow->width != attr.width)
505 || (ximagesink->xwindow->height != attr.height);
506 ximagesink->xwindow->width = attr.width;
507 ximagesink->xwindow->height = attr.height;
509 g_mutex_unlock (ximagesink->x_lock);
512 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
513 gst_event_new_reconfigure ());
517 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
519 g_return_if_fail (xwindow != NULL);
520 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
522 g_mutex_lock (ximagesink->x_lock);
524 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
525 ximagesink->xcontext->black);
527 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
528 0, 0, xwindow->width, xwindow->height);
530 XSync (ximagesink->xcontext->disp, FALSE);
532 g_mutex_unlock (ximagesink->x_lock);
535 /* This function handles XEvents that might be in the queue. It generates
536 GstEvent that will be sent upstream in the pipeline to handle interactivity
539 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
542 guint pointer_x = 0, pointer_y = 0;
543 gboolean pointer_moved = FALSE;
544 gboolean exposed = FALSE, configured = FALSE;
546 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
548 /* Then we get all pointer motion events, only the last position is
550 g_mutex_lock (ximagesink->flow_lock);
551 g_mutex_lock (ximagesink->x_lock);
552 while (XCheckWindowEvent (ximagesink->xcontext->disp,
553 ximagesink->xwindow->win, PointerMotionMask, &e)) {
554 g_mutex_unlock (ximagesink->x_lock);
555 g_mutex_unlock (ximagesink->flow_lock);
559 pointer_x = e.xmotion.x;
560 pointer_y = e.xmotion.y;
561 pointer_moved = TRUE;
566 g_mutex_lock (ximagesink->flow_lock);
567 g_mutex_lock (ximagesink->x_lock);
571 g_mutex_unlock (ximagesink->x_lock);
572 g_mutex_unlock (ximagesink->flow_lock);
574 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
575 pointer_x, pointer_y);
576 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
577 "mouse-move", 0, pointer_x, pointer_y);
579 g_mutex_lock (ximagesink->flow_lock);
580 g_mutex_lock (ximagesink->x_lock);
583 /* We get all remaining events on our window to throw them upstream */
584 while (XCheckWindowEvent (ximagesink->xcontext->disp,
585 ximagesink->xwindow->win,
586 KeyPressMask | KeyReleaseMask |
587 ButtonPressMask | ButtonReleaseMask, &e)) {
590 /* We lock only for the X function call */
591 g_mutex_unlock (ximagesink->x_lock);
592 g_mutex_unlock (ximagesink->flow_lock);
596 /* Mouse button pressed/released over our window. We send upstream
597 events for interactivity/navigation */
598 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
599 e.xbutton.button, e.xbutton.x, e.xbutton.x);
600 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
601 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
604 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
605 e.xbutton.button, e.xbutton.x, e.xbutton.x);
606 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
607 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
611 /* Key pressed/released over our window. We send upstream
612 events for interactivity/navigation */
613 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
614 e.xkey.keycode, e.xkey.x, e.xkey.x);
615 g_mutex_lock (ximagesink->x_lock);
616 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
618 g_mutex_unlock (ximagesink->x_lock);
619 if (keysym != NoSymbol) {
620 char *key_str = NULL;
622 g_mutex_lock (ximagesink->x_lock);
623 key_str = XKeysymToString (keysym);
624 g_mutex_unlock (ximagesink->x_lock);
625 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
626 e.type == KeyPress ? "key-press" : "key-release", key_str);
628 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
629 e.type == KeyPress ? "key-press" : "key-release", "unknown");
633 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
636 g_mutex_lock (ximagesink->flow_lock);
637 g_mutex_lock (ximagesink->x_lock);
641 while (XCheckWindowEvent (ximagesink->xcontext->disp,
642 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
647 case ConfigureNotify:
648 g_mutex_unlock (ximagesink->x_lock);
649 gst_ximagesink_xwindow_update_geometry (ximagesink);
650 g_mutex_lock (ximagesink->x_lock);
658 if (ximagesink->handle_expose && (exposed || configured)) {
659 g_mutex_unlock (ximagesink->x_lock);
660 g_mutex_unlock (ximagesink->flow_lock);
662 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
664 g_mutex_lock (ximagesink->flow_lock);
665 g_mutex_lock (ximagesink->x_lock);
668 /* Handle Display events */
669 while (XPending (ximagesink->xcontext->disp)) {
670 XNextEvent (ximagesink->xcontext->disp, &e);
676 wm_delete = XInternAtom (ximagesink->xcontext->disp,
677 "WM_DELETE_WINDOW", False);
678 if (wm_delete == (Atom) e.xclient.data.l[0]) {
679 /* Handle window deletion by posting an error on the bus */
680 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
681 ("Output window was closed"), (NULL));
683 g_mutex_unlock (ximagesink->x_lock);
684 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
685 ximagesink->xwindow = NULL;
686 g_mutex_lock (ximagesink->x_lock);
695 g_mutex_unlock (ximagesink->x_lock);
696 g_mutex_unlock (ximagesink->flow_lock);
700 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
702 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
704 GST_OBJECT_LOCK (ximagesink);
705 while (ximagesink->running) {
706 GST_OBJECT_UNLOCK (ximagesink);
708 if (ximagesink->xwindow) {
709 gst_ximagesink_handle_xevents (ximagesink);
711 /* FIXME: do we want to align this with the framerate or anything else? */
712 g_usleep (G_USEC_PER_SEC / 20);
714 GST_OBJECT_LOCK (ximagesink);
716 GST_OBJECT_UNLOCK (ximagesink);
722 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
724 GThread *thread = NULL;
726 /* don't start the thread too early */
727 if (ximagesink->xcontext == NULL) {
731 GST_OBJECT_LOCK (ximagesink);
732 if (ximagesink->handle_expose || ximagesink->handle_events) {
733 if (!ximagesink->event_thread) {
734 /* Setup our event listening thread */
735 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
736 ximagesink->handle_expose, ximagesink->handle_events);
737 ximagesink->running = TRUE;
738 ximagesink->event_thread = g_thread_create (
739 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
742 if (ximagesink->event_thread) {
743 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
744 ximagesink->handle_expose, ximagesink->handle_events);
745 ximagesink->running = FALSE;
746 /* grab thread and mark it as NULL */
747 thread = ximagesink->event_thread;
748 ximagesink->event_thread = NULL;
751 GST_OBJECT_UNLOCK (ximagesink);
753 /* Wait for our event thread to finish */
755 g_thread_join (thread);
760 /* This function calculates the pixel aspect ratio based on the properties
761 * in the xcontext structure and stores it there. */
763 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
765 static const gint par[][2] = {
766 {1, 1}, /* regular screen */
767 {16, 15}, /* PAL TV */
768 {11, 10}, /* 525 line Rec.601 video */
769 {54, 59}, /* 625 line Rec.601 video */
770 {64, 45}, /* 1280x1024 on 16:9 display */
771 {5, 3}, /* 1280x1024 on 4:3 display */
772 {4, 3} /* 800x600 on 16:9 display */
779 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
781 /* first calculate the "real" ratio based on the X values;
782 * which is the "physical" w/h divided by the w/h in pixels of the display */
783 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
784 / (xcontext->heightmm * xcontext->width);
786 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
788 if (xcontext->width == 720 && xcontext->height == 576) {
789 ratio = 4.0 * 576 / (3.0 * 720);
791 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
793 /* now find the one from par[][2] with the lowest delta to the real one */
797 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
798 gdouble this_delta = DELTA (i);
800 if (this_delta < delta) {
806 GST_DEBUG ("Decided on index %d (%d/%d)", index,
807 par[index][0], par[index][1]);
809 g_free (xcontext->par);
810 xcontext->par = g_new0 (GValue, 1);
811 g_value_init (xcontext->par, GST_TYPE_FRACTION);
812 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
813 GST_DEBUG ("set xcontext PAR to %d/%d",
814 gst_value_get_fraction_numerator (xcontext->par),
815 gst_value_get_fraction_denominator (xcontext->par));
818 /* This function gets the X Display and global info about it. Everything is
819 stored in our object and will be cleaned when the object is disposed. Note
820 here that caps for supported format are generated without any window or
823 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
825 GstXContext *xcontext = NULL;
826 XPixmapFormatValues *px_formats = NULL;
827 gint nb_formats = 0, i;
829 GstVideoFormat vformat;
831 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
833 xcontext = g_new0 (GstXContext, 1);
835 g_mutex_lock (ximagesink->x_lock);
837 xcontext->disp = XOpenDisplay (ximagesink->display_name);
839 if (!xcontext->disp) {
840 g_mutex_unlock (ximagesink->x_lock);
842 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
843 ("Could not initialise X output"), ("Could not open display"));
847 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
848 xcontext->screen_num = DefaultScreen (xcontext->disp);
849 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
850 xcontext->root = DefaultRootWindow (xcontext->disp);
851 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
852 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
853 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
855 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
856 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
857 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
858 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
860 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
861 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
863 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
865 /* We get supported pixmap formats at supported depth */
866 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
869 XCloseDisplay (xcontext->disp);
870 g_mutex_unlock (ximagesink->x_lock);
871 g_free (xcontext->par);
873 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
874 ("Could not get supported pixmap formats"), (NULL));
878 /* We get bpp value corresponding to our running depth */
879 for (i = 0; i < nb_formats; i++) {
880 if (px_formats[i].depth == xcontext->depth)
881 xcontext->bpp = px_formats[i].bits_per_pixel;
886 endianness = (ImageByteOrder (xcontext->disp) ==
887 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
889 /* Search for XShm extension support */
891 if (XShmQueryExtension (xcontext->disp) &&
892 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
893 xcontext->use_xshm = TRUE;
894 GST_DEBUG ("ximagesink is using XShm extension");
896 #endif /* HAVE_XSHM */
898 xcontext->use_xshm = FALSE;
899 GST_DEBUG ("ximagesink is not using XShm extension");
902 vformat = gst_video_format_from_masks (xcontext->depth, xcontext->bpp,
903 endianness, xcontext->visual->red_mask, xcontext->visual->green_mask,
904 xcontext->visual->blue_mask, 0);
906 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
909 /* update object's par with calculated one if not set yet */
910 if (!ximagesink->par) {
911 ximagesink->par = g_new0 (GValue, 1);
912 gst_value_init_and_copy (ximagesink->par, xcontext->par);
913 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
915 xcontext->caps = gst_caps_new_simple ("video/x-raw",
916 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
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);
936 GST_ERROR_OBJECT (ximagesink, "unknown format");
941 /* This function cleans the X context. Closing the Display and unrefing the
942 caps for supported formats. */
944 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
946 GstXContext *xcontext;
948 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
950 GST_OBJECT_LOCK (ximagesink);
951 if (ximagesink->xcontext == NULL) {
952 GST_OBJECT_UNLOCK (ximagesink);
956 /* Take the xcontext reference and NULL it while we
957 * clean it up, so that any buffer-alloced buffers
958 * arriving after this will be freed correctly */
959 xcontext = ximagesink->xcontext;
960 ximagesink->xcontext = NULL;
962 GST_OBJECT_UNLOCK (ximagesink);
964 gst_caps_unref (xcontext->caps);
965 g_free (xcontext->par);
966 g_free (ximagesink->par);
967 ximagesink->par = NULL;
969 if (xcontext->last_caps)
970 gst_caps_replace (&xcontext->last_caps, NULL);
972 g_mutex_lock (ximagesink->x_lock);
974 XCloseDisplay (xcontext->disp);
976 g_mutex_unlock (ximagesink->x_lock);
984 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
986 GstXImageSink *ximagesink;
990 ximagesink = GST_XIMAGESINK (bsink);
992 g_mutex_lock (ximagesink->x_lock);
993 if (ximagesink->xcontext) {
996 caps = gst_caps_ref (ximagesink->xcontext->caps);
999 GstCaps *intersection;
1002 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1003 gst_caps_unref (caps);
1004 caps = intersection;
1007 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1008 GstStructure *s0, *s1;
1010 caps = gst_caps_make_writable (caps);
1012 /* There can only be a single structure because the xcontext
1013 * caps only have a single structure */
1014 s0 = gst_caps_get_structure (caps, 0);
1015 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1017 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1018 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1019 gst_caps_append_structure (caps, s1);
1021 /* This will not change the order but will remove the
1022 * fixed width/height caps again if not possible
1025 GstCaps *intersection;
1028 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1029 gst_caps_unref (caps);
1030 caps = intersection;
1034 g_mutex_unlock (ximagesink->x_lock);
1037 g_mutex_unlock (ximagesink->x_lock);
1039 /* get a template copy and add the pixel aspect ratio */
1040 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1041 if (ximagesink->par) {
1042 caps = gst_caps_make_writable (caps);
1043 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1044 GstStructure *structure = gst_caps_get_structure (caps, i);
1047 nom = gst_value_get_fraction_numerator (ximagesink->par);
1048 den = gst_value_get_fraction_denominator (ximagesink->par);
1049 gst_structure_set (structure, "pixel-aspect-ratio",
1050 GST_TYPE_FRACTION, nom, den, NULL);
1055 GstCaps *intersection;
1058 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1059 gst_caps_unref (caps);
1060 caps = intersection;
1067 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1069 GstXImageSink *ximagesink;
1070 GstStructure *structure;
1072 GstBufferPool *newpool, *oldpool;
1076 ximagesink = GST_XIMAGESINK (bsink);
1078 if (!ximagesink->xcontext)
1081 GST_DEBUG_OBJECT (ximagesink,
1082 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1083 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1085 /* We intersect those caps with our template to make sure they are correct */
1086 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1087 goto incompatible_caps;
1089 if (!gst_video_info_from_caps (&info, caps))
1090 goto invalid_format;
1094 structure = gst_caps_get_structure (caps, 0);
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) = info.width;
1111 GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1112 ximagesink->fps_n = info.fps_n;
1113 ximagesink->fps_d = info.fps_d;
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));
1135 ximagesink->info = info;
1137 /* Remember to draw borders for next frame */
1138 ximagesink->draw_border = TRUE;
1140 /* create a new pool for the new configuration */
1141 newpool = gst_ximage_buffer_pool_new (ximagesink);
1143 structure = gst_buffer_pool_get_config (newpool);
1144 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1145 if (!gst_buffer_pool_set_config (newpool, structure))
1148 oldpool = ximagesink->pool;
1149 ximagesink->pool = newpool;
1151 /* unref the old sink */
1153 /* we don't deactivate, some elements might still be using it, it will be
1154 * deactivated when the last ref is gone */
1155 gst_object_unref (oldpool);
1157 g_mutex_unlock (ximagesink->flow_lock);
1164 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1169 GST_ERROR_OBJECT (ximagesink, "caps invalid");
1174 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1179 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1180 ("Invalid image size."));
1185 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1186 g_mutex_unlock (ximagesink->flow_lock);
1191 static GstStateChangeReturn
1192 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1194 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1195 GstXImageSink *ximagesink;
1196 GstXContext *xcontext = NULL;
1198 ximagesink = GST_XIMAGESINK (element);
1200 switch (transition) {
1201 case GST_STATE_CHANGE_NULL_TO_READY:
1202 /* Initializing the XContext */
1203 if (ximagesink->xcontext == NULL) {
1204 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1205 if (xcontext == NULL) {
1206 ret = GST_STATE_CHANGE_FAILURE;
1209 GST_OBJECT_LOCK (ximagesink);
1211 ximagesink->xcontext = xcontext;
1212 GST_OBJECT_UNLOCK (ximagesink);
1215 /* call XSynchronize with the current value of synchronous */
1216 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1217 ximagesink->synchronous ? "TRUE" : "FALSE");
1218 g_mutex_lock (ximagesink->x_lock);
1219 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1220 g_mutex_unlock (ximagesink->x_lock);
1221 gst_ximagesink_manage_event_thread (ximagesink);
1223 case GST_STATE_CHANGE_READY_TO_PAUSED:
1224 g_mutex_lock (ximagesink->flow_lock);
1225 if (ximagesink->xwindow)
1226 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1227 g_mutex_unlock (ximagesink->flow_lock);
1229 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1235 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1237 switch (transition) {
1238 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1240 case GST_STATE_CHANGE_PAUSED_TO_READY:
1241 ximagesink->fps_n = 0;
1242 ximagesink->fps_d = 1;
1243 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1244 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1245 g_mutex_lock (ximagesink->flow_lock);
1246 if (ximagesink->pool)
1247 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1248 g_mutex_unlock (ximagesink->flow_lock);
1250 case GST_STATE_CHANGE_READY_TO_NULL:
1251 gst_ximagesink_reset (ximagesink);
1262 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1263 GstClockTime * start, GstClockTime * end)
1265 GstXImageSink *ximagesink;
1267 ximagesink = GST_XIMAGESINK (bsink);
1269 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1270 *start = GST_BUFFER_TIMESTAMP (buf);
1271 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1272 *end = *start + GST_BUFFER_DURATION (buf);
1274 if (ximagesink->fps_n > 0) {
1276 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1283 static GstFlowReturn
1284 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1287 GstXImageSink *ximagesink;
1288 GstMetaXImage *meta;
1289 GstBuffer *to_put = NULL;
1291 ximagesink = GST_XIMAGESINK (vsink);
1293 meta = gst_buffer_get_meta_ximage (buf);
1295 if (meta && meta->sink == ximagesink) {
1296 /* If this buffer has been allocated using our buffer management we simply
1297 put the ximage which is in the PRIVATE pointer */
1298 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1302 GstVideoFrame src, dest;
1304 /* Else we have to copy the data into our private image, */
1305 /* if we have one... */
1306 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1308 /* we should have a pool, configured in setcaps */
1309 if (ximagesink->pool == NULL)
1312 if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1313 goto activate_failed;
1315 /* take a buffer form our pool */
1316 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, NULL);
1317 if (res != GST_FLOW_OK)
1320 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1323 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1324 "slow copy into bufferpool buffer %p", to_put);
1326 if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1327 goto invalid_buffer;
1329 if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1330 gst_video_frame_unmap (&src);
1331 goto invalid_buffer;
1334 gst_video_frame_copy (&dest, &src);
1336 gst_video_frame_unmap (&dest);
1337 gst_video_frame_unmap (&src);
1340 if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1345 gst_buffer_unref (to_put);
1352 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1353 ("Internal error: can't allocate images"),
1354 ("We don't have a bufferpool negotiated"));
1355 return GST_FLOW_ERROR;
1359 /* No image available. That's very bad ! */
1360 GST_WARNING_OBJECT (ximagesink, "could not create image");
1365 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1366 ("Failed to create output image buffer"),
1367 ("XServer allocated buffer size did not match input buffer %"
1368 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1369 gst_buffer_get_size (buf)));
1370 res = GST_FLOW_ERROR;
1375 /* No Window available to put our image into */
1376 GST_WARNING_OBJECT (ximagesink, "could map image");
1382 /* No Window available to put our image into */
1383 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1384 res = GST_FLOW_ERROR;
1389 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1390 res = GST_FLOW_ERROR;
1396 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1398 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1400 switch (GST_EVENT_TYPE (event)) {
1401 case GST_EVENT_TAG:{
1403 gchar *title = NULL;
1405 gst_event_parse_tag (event, &l);
1406 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1409 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1410 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1420 if (GST_BASE_SINK_CLASS (parent_class)->event)
1421 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1427 gst_ximagesink_setup_allocation (GstBaseSink * bsink, GstQuery * query)
1429 GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1430 GstBufferPool *pool;
1431 GstStructure *config;
1436 gst_query_parse_allocation (query, &caps, &need_pool);
1441 g_mutex_lock (ximagesink->flow_lock);
1442 if ((pool = ximagesink->pool))
1443 gst_object_ref (pool);
1444 g_mutex_unlock (ximagesink->flow_lock);
1447 const GstCaps *pcaps;
1449 /* we had a pool, check caps */
1450 config = gst_buffer_pool_get_config (pool);
1451 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1453 GST_DEBUG_OBJECT (ximagesink,
1454 "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1455 if (!gst_caps_is_equal (caps, pcaps)) {
1456 /* different caps, we can't use this pool */
1457 GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1458 gst_object_unref (pool);
1462 if (pool == NULL && need_pool) {
1465 GST_DEBUG_OBJECT (ximagesink, "create new pool");
1466 pool = gst_ximage_buffer_pool_new (ximagesink);
1468 if (!gst_video_info_from_caps (&info, caps))
1471 /* the normal size of a frame */
1474 config = gst_buffer_pool_get_config (pool);
1475 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1476 if (!gst_buffer_pool_set_config (pool, config))
1479 /* we need at least 2 buffer because we hold on to the last one */
1480 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1482 /* we also support various metadata */
1483 gst_query_add_allocation_meta (query, GST_META_API_VIDEO);
1484 gst_query_add_allocation_meta (query, GST_META_API_VIDEO_CROP);
1486 gst_object_unref (pool);
1493 GST_DEBUG_OBJECT (bsink, "no caps specified");
1498 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1503 GST_DEBUG_OBJECT (bsink, "failed setting config");
1508 /* Interfaces stuff */
1510 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1511 GstStructure * structure)
1513 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1515 gint x_offset, y_offset;
1519 event = gst_event_new_navigation (structure);
1521 /* We are not converting the pointer coordinates as there's no hardware
1522 scaling done here. The only possible scaling is done by videoscale and
1523 videoscale will have to catch those events and tranform the coordinates
1524 to match the applied scaling. So here we just add the offset if the image
1525 is centered in the window. */
1527 /* We take the flow_lock while we look at the window */
1528 g_mutex_lock (ximagesink->flow_lock);
1530 if (!ximagesink->xwindow) {
1531 g_mutex_unlock (ximagesink->flow_lock);
1535 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1536 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1538 g_mutex_unlock (ximagesink->flow_lock);
1540 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1542 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1544 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1546 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1549 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1551 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1552 gst_pad_send_event (pad, event);
1554 gst_object_unref (pad);
1559 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1561 iface->send_event = gst_ximagesink_navigation_send_event;
1565 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
1567 XID xwindow_id = id;
1568 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1569 GstXWindow *xwindow = NULL;
1570 XWindowAttributes attr;
1572 /* We acquire the stream lock while setting this window in the element.
1573 We are basically cleaning tons of stuff replacing the old window, putting
1574 images while we do that would surely crash */
1575 g_mutex_lock (ximagesink->flow_lock);
1577 /* If we already use that window return */
1578 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1579 g_mutex_unlock (ximagesink->flow_lock);
1583 /* If the element has not initialized the X11 context try to do so */
1584 if (!ximagesink->xcontext &&
1585 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1586 g_mutex_unlock (ximagesink->flow_lock);
1587 /* we have thrown a GST_ELEMENT_ERROR now */
1591 /* If a window is there already we destroy it */
1592 if (ximagesink->xwindow) {
1593 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1594 ximagesink->xwindow = NULL;
1597 /* If the xid is 0 we go back to an internal window */
1598 if (xwindow_id == 0) {
1599 /* If no width/height caps nego did not happen window will be created
1600 during caps nego then */
1601 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1602 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1603 GST_VIDEO_SINK_WIDTH (ximagesink),
1604 GST_VIDEO_SINK_HEIGHT (ximagesink));
1607 xwindow = g_new0 (GstXWindow, 1);
1609 xwindow->win = xwindow_id;
1611 /* We get window geometry, set the event we want to receive,
1613 g_mutex_lock (ximagesink->x_lock);
1614 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1615 xwindow->width = attr.width;
1616 xwindow->height = attr.height;
1617 xwindow->internal = FALSE;
1618 if (ximagesink->handle_events) {
1619 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1620 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1624 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1625 g_mutex_unlock (ximagesink->x_lock);
1629 ximagesink->xwindow = xwindow;
1631 g_mutex_unlock (ximagesink->flow_lock);
1635 gst_ximagesink_expose (GstXOverlay * overlay)
1637 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1639 gst_ximagesink_xwindow_update_geometry (ximagesink);
1640 gst_ximagesink_ximage_put (ximagesink, NULL);
1644 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1645 gboolean handle_events)
1647 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1649 ximagesink->handle_events = handle_events;
1651 g_mutex_lock (ximagesink->flow_lock);
1653 if (G_UNLIKELY (!ximagesink->xwindow)) {
1654 g_mutex_unlock (ximagesink->flow_lock);
1658 g_mutex_lock (ximagesink->x_lock);
1660 if (handle_events) {
1661 if (ximagesink->xwindow->internal) {
1662 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1663 ExposureMask | StructureNotifyMask | PointerMotionMask |
1664 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1666 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1667 ExposureMask | StructureNotifyMask | PointerMotionMask |
1668 KeyPressMask | KeyReleaseMask);
1671 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1674 g_mutex_unlock (ximagesink->x_lock);
1676 g_mutex_unlock (ximagesink->flow_lock);
1680 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1682 iface->set_window_handle = gst_ximagesink_set_window_handle;
1683 iface->expose = gst_ximagesink_expose;
1684 iface->handle_events = gst_ximagesink_set_event_handling;
1687 /* =========================================== */
1689 /* Init & Class init */
1691 /* =========================================== */
1694 gst_ximagesink_set_property (GObject * object, guint prop_id,
1695 const GValue * value, GParamSpec * pspec)
1697 GstXImageSink *ximagesink;
1699 g_return_if_fail (GST_IS_XIMAGESINK (object));
1701 ximagesink = GST_XIMAGESINK (object);
1705 ximagesink->display_name = g_strdup (g_value_get_string (value));
1707 case PROP_SYNCHRONOUS:
1708 ximagesink->synchronous = g_value_get_boolean (value);
1709 if (ximagesink->xcontext) {
1710 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1711 ximagesink->synchronous ? "TRUE" : "FALSE");
1712 g_mutex_lock (ximagesink->x_lock);
1713 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1714 g_mutex_unlock (ximagesink->x_lock);
1717 case PROP_FORCE_ASPECT_RATIO:
1718 ximagesink->keep_aspect = g_value_get_boolean (value);
1720 case PROP_PIXEL_ASPECT_RATIO:
1724 tmp = g_new0 (GValue, 1);
1725 g_value_init (tmp, GST_TYPE_FRACTION);
1727 if (!g_value_transform (value, tmp)) {
1728 GST_WARNING_OBJECT (ximagesink,
1729 "Could not transform string to aspect ratio");
1732 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1733 gst_value_get_fraction_numerator (tmp),
1734 gst_value_get_fraction_denominator (tmp));
1735 g_free (ximagesink->par);
1736 ximagesink->par = tmp;
1740 case PROP_HANDLE_EVENTS:
1741 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
1742 g_value_get_boolean (value));
1743 gst_ximagesink_manage_event_thread (ximagesink);
1745 case PROP_HANDLE_EXPOSE:
1746 ximagesink->handle_expose = g_value_get_boolean (value);
1747 gst_ximagesink_manage_event_thread (ximagesink);
1750 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1756 gst_ximagesink_get_property (GObject * object, guint prop_id,
1757 GValue * value, GParamSpec * pspec)
1759 GstXImageSink *ximagesink;
1761 g_return_if_fail (GST_IS_XIMAGESINK (object));
1763 ximagesink = GST_XIMAGESINK (object);
1767 g_value_set_string (value, ximagesink->display_name);
1769 case PROP_SYNCHRONOUS:
1770 g_value_set_boolean (value, ximagesink->synchronous);
1772 case PROP_FORCE_ASPECT_RATIO:
1773 g_value_set_boolean (value, ximagesink->keep_aspect);
1775 case PROP_PIXEL_ASPECT_RATIO:
1776 if (ximagesink->par)
1777 g_value_transform (ximagesink->par, value);
1779 case PROP_HANDLE_EVENTS:
1780 g_value_set_boolean (value, ximagesink->handle_events);
1782 case PROP_HANDLE_EXPOSE:
1783 g_value_set_boolean (value, ximagesink->handle_expose);
1785 case PROP_WINDOW_WIDTH:
1786 if (ximagesink->xwindow)
1787 g_value_set_uint64 (value, ximagesink->xwindow->width);
1789 g_value_set_uint64 (value, 0);
1791 case PROP_WINDOW_HEIGHT:
1792 if (ximagesink->xwindow)
1793 g_value_set_uint64 (value, ximagesink->xwindow->height);
1795 g_value_set_uint64 (value, 0);
1798 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1804 gst_ximagesink_reset (GstXImageSink * ximagesink)
1808 GST_OBJECT_LOCK (ximagesink);
1809 ximagesink->running = FALSE;
1810 /* grab thread and mark it as NULL */
1811 thread = ximagesink->event_thread;
1812 ximagesink->event_thread = NULL;
1813 GST_OBJECT_UNLOCK (ximagesink);
1815 /* Wait for our event thread to finish before we clean up our stuff. */
1817 g_thread_join (thread);
1819 if (ximagesink->cur_image) {
1820 gst_buffer_unref (ximagesink->cur_image);
1821 ximagesink->cur_image = NULL;
1824 g_mutex_lock (ximagesink->flow_lock);
1826 if (ximagesink->pool) {
1827 gst_object_unref (ximagesink->pool);
1828 ximagesink->pool = NULL;
1831 if (ximagesink->xwindow) {
1832 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1833 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1834 ximagesink->xwindow = NULL;
1836 g_mutex_unlock (ximagesink->flow_lock);
1838 gst_ximagesink_xcontext_clear (ximagesink);
1842 gst_ximagesink_finalize (GObject * object)
1844 GstXImageSink *ximagesink;
1846 ximagesink = GST_XIMAGESINK (object);
1848 gst_ximagesink_reset (ximagesink);
1850 if (ximagesink->display_name) {
1851 g_free (ximagesink->display_name);
1852 ximagesink->display_name = NULL;
1854 if (ximagesink->par) {
1855 g_free (ximagesink->par);
1856 ximagesink->par = NULL;
1858 if (ximagesink->x_lock) {
1859 g_mutex_free (ximagesink->x_lock);
1860 ximagesink->x_lock = NULL;
1862 if (ximagesink->flow_lock) {
1863 g_mutex_free (ximagesink->flow_lock);
1864 ximagesink->flow_lock = NULL;
1867 g_free (ximagesink->media_title);
1869 G_OBJECT_CLASS (parent_class)->finalize (object);
1873 gst_ximagesink_init (GstXImageSink * ximagesink)
1875 ximagesink->display_name = NULL;
1876 ximagesink->xcontext = NULL;
1877 ximagesink->xwindow = NULL;
1878 ximagesink->cur_image = NULL;
1880 ximagesink->event_thread = NULL;
1881 ximagesink->running = FALSE;
1883 ximagesink->fps_n = 0;
1884 ximagesink->fps_d = 1;
1886 ximagesink->x_lock = g_mutex_new ();
1887 ximagesink->flow_lock = g_mutex_new ();
1889 ximagesink->par = NULL;
1891 ximagesink->pool = NULL;
1893 ximagesink->synchronous = FALSE;
1894 ximagesink->keep_aspect = FALSE;
1895 ximagesink->handle_events = TRUE;
1896 ximagesink->handle_expose = TRUE;
1900 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1902 GObjectClass *gobject_class;
1903 GstElementClass *gstelement_class;
1904 GstBaseSinkClass *gstbasesink_class;
1905 GstVideoSinkClass *videosink_class;
1907 gobject_class = (GObjectClass *) klass;
1908 gstelement_class = (GstElementClass *) klass;
1909 gstbasesink_class = (GstBaseSinkClass *) klass;
1910 videosink_class = (GstVideoSinkClass *) klass;
1912 gobject_class->finalize = gst_ximagesink_finalize;
1913 gobject_class->set_property = gst_ximagesink_set_property;
1914 gobject_class->get_property = gst_ximagesink_get_property;
1916 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1917 g_param_spec_string ("display", "Display", "X Display name",
1918 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1919 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1920 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1921 "the X display in synchronous mode. (used only for debugging)", FALSE,
1922 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1923 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1924 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1925 "When enabled, reverse caps negotiation (scaling) will respect "
1926 "original aspect ratio", FALSE,
1927 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1928 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1929 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1930 "The pixel aspect ratio of the device", "1/1",
1931 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1933 g_param_spec_boolean ("handle-events", "Handle XEvents",
1934 "When enabled, XEvents will be selected and handled", TRUE,
1935 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1936 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1937 g_param_spec_boolean ("handle-expose", "Handle expose",
1939 "the current frame will always be drawn in response to X Expose "
1940 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1943 * GstXImageSink:window-width
1945 * Actual width of the video window.
1949 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1950 g_param_spec_uint64 ("window-width", "window-width",
1951 "Width of the window", 0, G_MAXUINT64, 0,
1952 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1955 * GstXImageSink:window-height
1957 * Actual height of the video window.
1961 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1962 g_param_spec_uint64 ("window-height", "window-height",
1963 "Height of the window", 0, G_MAXUINT64, 0,
1964 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1966 gst_element_class_set_details_simple (gstelement_class,
1967 "Video sink", "Sink/Video",
1968 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1970 gst_element_class_add_pad_template (gstelement_class,
1971 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1973 gstelement_class->change_state = gst_ximagesink_change_state;
1975 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1976 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1977 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1978 gstbasesink_class->setup_allocation =
1979 GST_DEBUG_FUNCPTR (gst_ximagesink_setup_allocation);
1980 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1982 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);