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 #GstVideoOverlay interface and will then render video frames in this
26 * drawable. If no Window ID was provided by the application, the element will
27 * create its 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/videooverlay.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 (GstVideoOverlay * 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_video_overlay_init (GstVideoOverlayIface * iface);
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_VIDEO_OVERLAY,
177 gst_ximagesink_video_overlay_init));
179 /* ============================================================= */
181 /* Private Methods */
183 /* ============================================================= */
187 /* We are called with the x_lock taken */
189 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
190 GstXWindow * xwindow, GstVideoRectangle rect)
192 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
193 g_return_if_fail (xwindow != NULL);
195 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
196 ximagesink->xcontext->black);
200 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
201 0, 0, rect.x, xwindow->height);
205 if ((rect.x + rect.w) < xwindow->width) {
206 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
207 rect.x + rect.w, 0, xwindow->width, xwindow->height);
212 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
213 0, 0, xwindow->width, rect.y);
217 if ((rect.y + rect.h) < xwindow->height) {
218 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
219 0, rect.y + rect.h, xwindow->width, xwindow->height);
223 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
225 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
228 GstMetaVideoCrop *crop;
229 GstVideoRectangle src, dst, result;
230 gboolean draw_border = FALSE;
232 /* We take the flow_lock. If expose is in there we don't want to run
233 concurrently from the data flow thread */
234 g_mutex_lock (ximagesink->flow_lock);
236 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
237 g_mutex_unlock (ximagesink->flow_lock);
241 /* Draw borders when displaying the first frame. After this
242 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
243 if (!ximagesink->cur_image || ximagesink->draw_border) {
247 /* Store a reference to the last image we put, lose the previous one */
248 if (ximage && ximagesink->cur_image != ximage) {
249 if (ximagesink->cur_image) {
250 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
251 gst_buffer_unref (ximagesink->cur_image);
253 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
254 ximagesink->cur_image = gst_buffer_ref (ximage);
257 /* Expose sends a NULL image, we take the latest frame */
260 if (ximagesink->cur_image) {
261 ximage = ximagesink->cur_image;
263 g_mutex_unlock (ximagesink->flow_lock);
268 meta = gst_buffer_get_meta_ximage (ximage);
269 crop = gst_buffer_get_meta_video_crop (ximage);
272 src.x = crop->x + meta->x;
273 src.y = crop->y + meta->y;
275 src.h = crop->height;
280 src.h = meta->height;
282 dst.w = ximagesink->xwindow->width;
283 dst.h = ximagesink->xwindow->height;
285 gst_video_sink_center_rect (src, dst, &result, FALSE);
287 g_mutex_lock (ximagesink->x_lock);
290 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
292 ximagesink->draw_border = FALSE;
295 if (ximagesink->xcontext->use_xshm) {
296 GST_LOG_OBJECT (ximagesink,
297 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
298 ximage, 0, 0, result.x, result.y, result.w, result.h,
299 ximagesink->xwindow->width, ximagesink->xwindow->height);
300 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
301 ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
302 result.w, result.h, FALSE);
304 #endif /* HAVE_XSHM */
306 GST_LOG_OBJECT (ximagesink,
307 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
308 ximage, 0, 0, result.x, result.y, result.w, result.h,
309 ximagesink->xwindow->width, ximagesink->xwindow->height);
310 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
311 ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
315 XSync (ximagesink->xcontext->disp, FALSE);
317 g_mutex_unlock (ximagesink->x_lock);
319 g_mutex_unlock (ximagesink->flow_lock);
325 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
328 Atom hints_atom = None;
331 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
332 g_return_val_if_fail (window != NULL, FALSE);
334 g_mutex_lock (ximagesink->x_lock);
336 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
338 if (hints_atom == None) {
339 g_mutex_unlock (ximagesink->x_lock);
343 hints = g_malloc0 (sizeof (MotifWmHints));
345 hints->flags |= MWM_HINTS_DECORATIONS;
346 hints->decorations = 1 << 0;
348 XChangeProperty (ximagesink->xcontext->disp, window->win,
349 hints_atom, hints_atom, 32, PropModeReplace,
350 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
352 XSync (ximagesink->xcontext->disp, FALSE);
354 g_mutex_unlock (ximagesink->x_lock);
362 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
363 GstXWindow * xwindow, const gchar * media_title)
366 g_free (ximagesink->media_title);
367 ximagesink->media_title = g_strdup (media_title);
370 /* we have a window */
371 if (xwindow->internal) {
372 XTextProperty xproperty;
373 const gchar *app_name;
374 const gchar *title = NULL;
375 gchar *title_mem = NULL;
377 /* set application name as a title */
378 app_name = g_get_application_name ();
380 if (app_name && ximagesink->media_title) {
381 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
383 } else if (app_name) {
385 } else if (ximagesink->media_title) {
386 title = ximagesink->media_title;
390 if ((XStringListToTextProperty (((char **) &title), 1,
392 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
393 XFree (xproperty.value);
402 /* This function handles a GstXWindow creation */
404 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
406 GstXWindow *xwindow = NULL;
409 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
411 xwindow = g_new0 (GstXWindow, 1);
413 xwindow->width = width;
414 xwindow->height = height;
415 xwindow->internal = TRUE;
417 g_mutex_lock (ximagesink->x_lock);
419 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
420 ximagesink->xcontext->root,
421 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
423 /* We have to do that to prevent X from redrawing the background on
424 ConfigureNotify. This takes away flickering of video when resizing. */
425 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
427 /* set application name as a title */
428 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
430 if (ximagesink->handle_events) {
433 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
434 StructureNotifyMask | PointerMotionMask | KeyPressMask |
435 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
437 /* Tell the window manager we'd like delete client messages instead of
439 wm_delete = XInternAtom (ximagesink->xcontext->disp,
440 "WM_DELETE_WINDOW", False);
441 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
445 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
448 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
450 XSync (ximagesink->xcontext->disp, FALSE);
452 g_mutex_unlock (ximagesink->x_lock);
454 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
456 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
462 /* This function destroys a GstXWindow */
464 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
465 GstXWindow * xwindow)
467 g_return_if_fail (xwindow != NULL);
468 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
470 g_mutex_lock (ximagesink->x_lock);
472 /* If we did not create that window we just free the GC and let it live */
473 if (xwindow->internal)
474 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
476 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
478 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
480 XSync (ximagesink->xcontext->disp, FALSE);
482 g_mutex_unlock (ximagesink->x_lock);
488 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
490 XWindowAttributes attr;
491 gboolean reconfigure;
493 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
495 /* Update the window geometry */
496 g_mutex_lock (ximagesink->x_lock);
497 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
498 g_mutex_unlock (ximagesink->x_lock);
502 XGetWindowAttributes (ximagesink->xcontext->disp,
503 ximagesink->xwindow->win, &attr);
505 /* Check if we would suggest a different width/height now */
506 reconfigure = (ximagesink->xwindow->width != attr.width)
507 || (ximagesink->xwindow->height != attr.height);
508 ximagesink->xwindow->width = attr.width;
509 ximagesink->xwindow->height = attr.height;
511 g_mutex_unlock (ximagesink->x_lock);
514 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
515 gst_event_new_reconfigure ());
519 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
521 g_return_if_fail (xwindow != NULL);
522 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
524 g_mutex_lock (ximagesink->x_lock);
526 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
527 ximagesink->xcontext->black);
529 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
530 0, 0, xwindow->width, xwindow->height);
532 XSync (ximagesink->xcontext->disp, FALSE);
534 g_mutex_unlock (ximagesink->x_lock);
537 /* This function handles XEvents that might be in the queue. It generates
538 GstEvent that will be sent upstream in the pipeline to handle interactivity
541 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
544 guint pointer_x = 0, pointer_y = 0;
545 gboolean pointer_moved = FALSE;
546 gboolean exposed = FALSE, configured = FALSE;
548 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
550 /* Then we get all pointer motion events, only the last position is
552 g_mutex_lock (ximagesink->flow_lock);
553 g_mutex_lock (ximagesink->x_lock);
554 while (XCheckWindowEvent (ximagesink->xcontext->disp,
555 ximagesink->xwindow->win, PointerMotionMask, &e)) {
556 g_mutex_unlock (ximagesink->x_lock);
557 g_mutex_unlock (ximagesink->flow_lock);
561 pointer_x = e.xmotion.x;
562 pointer_y = e.xmotion.y;
563 pointer_moved = TRUE;
568 g_mutex_lock (ximagesink->flow_lock);
569 g_mutex_lock (ximagesink->x_lock);
573 g_mutex_unlock (ximagesink->x_lock);
574 g_mutex_unlock (ximagesink->flow_lock);
576 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
577 pointer_x, pointer_y);
578 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
579 "mouse-move", 0, pointer_x, pointer_y);
581 g_mutex_lock (ximagesink->flow_lock);
582 g_mutex_lock (ximagesink->x_lock);
585 /* We get all remaining events on our window to throw them upstream */
586 while (XCheckWindowEvent (ximagesink->xcontext->disp,
587 ximagesink->xwindow->win,
588 KeyPressMask | KeyReleaseMask |
589 ButtonPressMask | ButtonReleaseMask, &e)) {
592 /* We lock only for the X function call */
593 g_mutex_unlock (ximagesink->x_lock);
594 g_mutex_unlock (ximagesink->flow_lock);
598 /* Mouse button pressed/released over our window. We send upstream
599 events for interactivity/navigation */
600 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
601 e.xbutton.button, e.xbutton.x, e.xbutton.x);
602 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
603 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
606 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
607 e.xbutton.button, e.xbutton.x, e.xbutton.x);
608 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
609 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
613 /* Key pressed/released over our window. We send upstream
614 events for interactivity/navigation */
615 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
616 e.xkey.keycode, e.xkey.x, e.xkey.x);
617 g_mutex_lock (ximagesink->x_lock);
618 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
620 g_mutex_unlock (ximagesink->x_lock);
621 if (keysym != NoSymbol) {
622 char *key_str = NULL;
624 g_mutex_lock (ximagesink->x_lock);
625 key_str = XKeysymToString (keysym);
626 g_mutex_unlock (ximagesink->x_lock);
627 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
628 e.type == KeyPress ? "key-press" : "key-release", key_str);
630 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
631 e.type == KeyPress ? "key-press" : "key-release", "unknown");
635 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
638 g_mutex_lock (ximagesink->flow_lock);
639 g_mutex_lock (ximagesink->x_lock);
643 while (XCheckWindowEvent (ximagesink->xcontext->disp,
644 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
649 case ConfigureNotify:
650 g_mutex_unlock (ximagesink->x_lock);
651 gst_ximagesink_xwindow_update_geometry (ximagesink);
652 g_mutex_lock (ximagesink->x_lock);
660 if (ximagesink->handle_expose && (exposed || configured)) {
661 g_mutex_unlock (ximagesink->x_lock);
662 g_mutex_unlock (ximagesink->flow_lock);
664 gst_ximagesink_expose (GST_VIDEO_OVERLAY (ximagesink));
666 g_mutex_lock (ximagesink->flow_lock);
667 g_mutex_lock (ximagesink->x_lock);
670 /* Handle Display events */
671 while (XPending (ximagesink->xcontext->disp)) {
672 XNextEvent (ximagesink->xcontext->disp, &e);
678 wm_delete = XInternAtom (ximagesink->xcontext->disp,
679 "WM_DELETE_WINDOW", False);
680 if (wm_delete == (Atom) e.xclient.data.l[0]) {
681 /* Handle window deletion by posting an error on the bus */
682 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
683 ("Output window was closed"), (NULL));
685 g_mutex_unlock (ximagesink->x_lock);
686 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
687 ximagesink->xwindow = NULL;
688 g_mutex_lock (ximagesink->x_lock);
697 g_mutex_unlock (ximagesink->x_lock);
698 g_mutex_unlock (ximagesink->flow_lock);
702 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
704 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
706 GST_OBJECT_LOCK (ximagesink);
707 while (ximagesink->running) {
708 GST_OBJECT_UNLOCK (ximagesink);
710 if (ximagesink->xwindow) {
711 gst_ximagesink_handle_xevents (ximagesink);
713 /* FIXME: do we want to align this with the framerate or anything else? */
714 g_usleep (G_USEC_PER_SEC / 20);
716 GST_OBJECT_LOCK (ximagesink);
718 GST_OBJECT_UNLOCK (ximagesink);
724 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
726 GThread *thread = NULL;
728 /* don't start the thread too early */
729 if (ximagesink->xcontext == NULL) {
733 GST_OBJECT_LOCK (ximagesink);
734 if (ximagesink->handle_expose || ximagesink->handle_events) {
735 if (!ximagesink->event_thread) {
736 /* Setup our event listening thread */
737 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
738 ximagesink->handle_expose, ximagesink->handle_events);
739 ximagesink->running = TRUE;
740 ximagesink->event_thread = g_thread_create (
741 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
744 if (ximagesink->event_thread) {
745 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
746 ximagesink->handle_expose, ximagesink->handle_events);
747 ximagesink->running = FALSE;
748 /* grab thread and mark it as NULL */
749 thread = ximagesink->event_thread;
750 ximagesink->event_thread = NULL;
753 GST_OBJECT_UNLOCK (ximagesink);
755 /* Wait for our event thread to finish */
757 g_thread_join (thread);
762 /* This function calculates the pixel aspect ratio based on the properties
763 * in the xcontext structure and stores it there. */
765 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
767 static const gint par[][2] = {
768 {1, 1}, /* regular screen */
769 {16, 15}, /* PAL TV */
770 {11, 10}, /* 525 line Rec.601 video */
771 {54, 59}, /* 625 line Rec.601 video */
772 {64, 45}, /* 1280x1024 on 16:9 display */
773 {5, 3}, /* 1280x1024 on 4:3 display */
774 {4, 3} /* 800x600 on 16:9 display */
781 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
783 /* first calculate the "real" ratio based on the X values;
784 * which is the "physical" w/h divided by the w/h in pixels of the display */
785 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
786 / (xcontext->heightmm * xcontext->width);
788 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
790 if (xcontext->width == 720 && xcontext->height == 576) {
791 ratio = 4.0 * 576 / (3.0 * 720);
793 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
795 /* now find the one from par[][2] with the lowest delta to the real one */
799 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
800 gdouble this_delta = DELTA (i);
802 if (this_delta < delta) {
808 GST_DEBUG ("Decided on index %d (%d/%d)", index,
809 par[index][0], par[index][1]);
811 g_free (xcontext->par);
812 xcontext->par = g_new0 (GValue, 1);
813 g_value_init (xcontext->par, GST_TYPE_FRACTION);
814 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
815 GST_DEBUG ("set xcontext PAR to %d/%d",
816 gst_value_get_fraction_numerator (xcontext->par),
817 gst_value_get_fraction_denominator (xcontext->par));
820 /* This function gets the X Display and global info about it. Everything is
821 stored in our object and will be cleaned when the object is disposed. Note
822 here that caps for supported format are generated without any window or
825 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
827 GstXContext *xcontext = NULL;
828 XPixmapFormatValues *px_formats = NULL;
829 gint nb_formats = 0, i;
831 GstVideoFormat vformat;
833 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
835 xcontext = g_new0 (GstXContext, 1);
837 g_mutex_lock (ximagesink->x_lock);
839 xcontext->disp = XOpenDisplay (ximagesink->display_name);
841 if (!xcontext->disp) {
842 g_mutex_unlock (ximagesink->x_lock);
844 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
845 ("Could not initialise X output"), ("Could not open display"));
849 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
850 xcontext->screen_num = DefaultScreen (xcontext->disp);
851 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
852 xcontext->root = DefaultRootWindow (xcontext->disp);
853 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
854 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
855 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
857 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
858 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
859 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
860 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
862 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
863 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
865 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
867 /* We get supported pixmap formats at supported depth */
868 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
871 XCloseDisplay (xcontext->disp);
872 g_mutex_unlock (ximagesink->x_lock);
873 g_free (xcontext->par);
875 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
876 ("Could not get supported pixmap formats"), (NULL));
880 /* We get bpp value corresponding to our running depth */
881 for (i = 0; i < nb_formats; i++) {
882 if (px_formats[i].depth == xcontext->depth)
883 xcontext->bpp = px_formats[i].bits_per_pixel;
888 endianness = (ImageByteOrder (xcontext->disp) ==
889 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
891 /* Search for XShm extension support */
893 if (XShmQueryExtension (xcontext->disp) &&
894 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
895 xcontext->use_xshm = TRUE;
896 GST_DEBUG ("ximagesink is using XShm extension");
898 #endif /* HAVE_XSHM */
900 xcontext->use_xshm = FALSE;
901 GST_DEBUG ("ximagesink is not using XShm extension");
904 vformat = gst_video_format_from_masks (xcontext->depth, xcontext->bpp,
905 endianness, xcontext->visual->red_mask, xcontext->visual->green_mask,
906 xcontext->visual->blue_mask, 0);
908 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
911 /* update object's par with calculated one if not set yet */
912 if (!ximagesink->par) {
913 ximagesink->par = g_new0 (GValue, 1);
914 gst_value_init_and_copy (ximagesink->par, xcontext->par);
915 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
917 xcontext->caps = gst_caps_new_simple ("video/x-raw",
918 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
919 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
920 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
921 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
922 if (ximagesink->par) {
925 nom = gst_value_get_fraction_numerator (ximagesink->par);
926 den = gst_value_get_fraction_denominator (ximagesink->par);
927 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
928 GST_TYPE_FRACTION, nom, den, NULL);
931 g_mutex_unlock (ximagesink->x_lock);
938 GST_ERROR_OBJECT (ximagesink, "unknown format");
943 /* This function cleans the X context. Closing the Display and unrefing the
944 caps for supported formats. */
946 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
948 GstXContext *xcontext;
950 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
952 GST_OBJECT_LOCK (ximagesink);
953 if (ximagesink->xcontext == NULL) {
954 GST_OBJECT_UNLOCK (ximagesink);
958 /* Take the xcontext reference and NULL it while we
959 * clean it up, so that any buffer-alloced buffers
960 * arriving after this will be freed correctly */
961 xcontext = ximagesink->xcontext;
962 ximagesink->xcontext = NULL;
964 GST_OBJECT_UNLOCK (ximagesink);
966 gst_caps_unref (xcontext->caps);
967 g_free (xcontext->par);
968 g_free (ximagesink->par);
969 ximagesink->par = NULL;
971 if (xcontext->last_caps)
972 gst_caps_replace (&xcontext->last_caps, NULL);
974 g_mutex_lock (ximagesink->x_lock);
976 XCloseDisplay (xcontext->disp);
978 g_mutex_unlock (ximagesink->x_lock);
986 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
988 GstXImageSink *ximagesink;
992 ximagesink = GST_XIMAGESINK (bsink);
994 g_mutex_lock (ximagesink->x_lock);
995 if (ximagesink->xcontext) {
998 caps = gst_caps_ref (ximagesink->xcontext->caps);
1001 GstCaps *intersection;
1004 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1005 gst_caps_unref (caps);
1006 caps = intersection;
1009 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1010 GstStructure *s0, *s1;
1012 caps = gst_caps_make_writable (caps);
1014 /* There can only be a single structure because the xcontext
1015 * caps only have a single structure */
1016 s0 = gst_caps_get_structure (caps, 0);
1017 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1019 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1020 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1021 gst_caps_append_structure (caps, s1);
1023 /* This will not change the order but will remove the
1024 * fixed width/height caps again if not possible
1027 GstCaps *intersection;
1030 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1031 gst_caps_unref (caps);
1032 caps = intersection;
1036 g_mutex_unlock (ximagesink->x_lock);
1039 g_mutex_unlock (ximagesink->x_lock);
1041 /* get a template copy and add the pixel aspect ratio */
1042 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1043 if (ximagesink->par) {
1044 caps = gst_caps_make_writable (caps);
1045 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1046 GstStructure *structure = gst_caps_get_structure (caps, i);
1049 nom = gst_value_get_fraction_numerator (ximagesink->par);
1050 den = gst_value_get_fraction_denominator (ximagesink->par);
1051 gst_structure_set (structure, "pixel-aspect-ratio",
1052 GST_TYPE_FRACTION, nom, den, NULL);
1057 GstCaps *intersection;
1060 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1061 gst_caps_unref (caps);
1062 caps = intersection;
1069 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1071 GstXImageSink *ximagesink;
1072 GstStructure *structure;
1074 GstBufferPool *newpool, *oldpool;
1078 ximagesink = GST_XIMAGESINK (bsink);
1080 if (!ximagesink->xcontext)
1083 GST_DEBUG_OBJECT (ximagesink,
1084 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1085 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1087 /* We intersect those caps with our template to make sure they are correct */
1088 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1089 goto incompatible_caps;
1091 if (!gst_video_info_from_caps (&info, caps))
1092 goto invalid_format;
1096 structure = gst_caps_get_structure (caps, 0);
1097 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1098 * otherwise linking should fail */
1099 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1101 if (ximagesink->par) {
1102 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1105 } else if (ximagesink->xcontext->par) {
1106 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1112 GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1113 GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1114 ximagesink->fps_n = info.fps_n;
1115 ximagesink->fps_d = info.fps_d;
1117 /* Notify application to set xwindow id now */
1118 g_mutex_lock (ximagesink->flow_lock);
1119 if (!ximagesink->xwindow) {
1120 g_mutex_unlock (ximagesink->flow_lock);
1121 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1123 g_mutex_unlock (ximagesink->flow_lock);
1126 /* Creating our window and our image */
1127 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1128 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1131 g_mutex_lock (ximagesink->flow_lock);
1132 if (!ximagesink->xwindow) {
1133 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1134 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1137 ximagesink->info = info;
1139 /* Remember to draw borders for next frame */
1140 ximagesink->draw_border = TRUE;
1142 /* create a new pool for the new configuration */
1143 newpool = gst_ximage_buffer_pool_new (ximagesink);
1145 structure = gst_buffer_pool_get_config (newpool);
1146 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1147 if (!gst_buffer_pool_set_config (newpool, structure))
1150 oldpool = ximagesink->pool;
1151 ximagesink->pool = newpool;
1153 /* unref the old sink */
1155 /* we don't deactivate, some elements might still be using it, it will be
1156 * deactivated when the last ref is gone */
1157 gst_object_unref (oldpool);
1159 g_mutex_unlock (ximagesink->flow_lock);
1166 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1171 GST_ERROR_OBJECT (ximagesink, "caps invalid");
1176 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1181 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1182 ("Invalid image size."));
1187 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1188 g_mutex_unlock (ximagesink->flow_lock);
1193 static GstStateChangeReturn
1194 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1196 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1197 GstXImageSink *ximagesink;
1198 GstXContext *xcontext = NULL;
1200 ximagesink = GST_XIMAGESINK (element);
1202 switch (transition) {
1203 case GST_STATE_CHANGE_NULL_TO_READY:
1204 /* Initializing the XContext */
1205 if (ximagesink->xcontext == NULL) {
1206 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1207 if (xcontext == NULL) {
1208 ret = GST_STATE_CHANGE_FAILURE;
1211 GST_OBJECT_LOCK (ximagesink);
1213 ximagesink->xcontext = xcontext;
1214 GST_OBJECT_UNLOCK (ximagesink);
1217 /* call XSynchronize with the current value of synchronous */
1218 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1219 ximagesink->synchronous ? "TRUE" : "FALSE");
1220 g_mutex_lock (ximagesink->x_lock);
1221 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1222 g_mutex_unlock (ximagesink->x_lock);
1223 gst_ximagesink_manage_event_thread (ximagesink);
1225 case GST_STATE_CHANGE_READY_TO_PAUSED:
1226 g_mutex_lock (ximagesink->flow_lock);
1227 if (ximagesink->xwindow)
1228 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1229 g_mutex_unlock (ximagesink->flow_lock);
1231 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1237 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1239 switch (transition) {
1240 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1242 case GST_STATE_CHANGE_PAUSED_TO_READY:
1243 ximagesink->fps_n = 0;
1244 ximagesink->fps_d = 1;
1245 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1246 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1247 g_mutex_lock (ximagesink->flow_lock);
1248 if (ximagesink->pool)
1249 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1250 g_mutex_unlock (ximagesink->flow_lock);
1252 case GST_STATE_CHANGE_READY_TO_NULL:
1253 gst_ximagesink_reset (ximagesink);
1264 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1265 GstClockTime * start, GstClockTime * end)
1267 GstXImageSink *ximagesink;
1269 ximagesink = GST_XIMAGESINK (bsink);
1271 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1272 *start = GST_BUFFER_TIMESTAMP (buf);
1273 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1274 *end = *start + GST_BUFFER_DURATION (buf);
1276 if (ximagesink->fps_n > 0) {
1278 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1285 static GstFlowReturn
1286 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1289 GstXImageSink *ximagesink;
1290 GstMetaXImage *meta;
1291 GstBuffer *to_put = NULL;
1293 ximagesink = GST_XIMAGESINK (vsink);
1295 meta = gst_buffer_get_meta_ximage (buf);
1297 if (meta && meta->sink == ximagesink) {
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");
1304 GstVideoFrame src, dest;
1306 /* Else we have to copy the data into our private image, */
1307 /* if we have one... */
1308 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1310 /* we should have a pool, configured in setcaps */
1311 if (ximagesink->pool == NULL)
1314 if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1315 goto activate_failed;
1317 /* take a buffer form our pool */
1318 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, NULL);
1319 if (res != GST_FLOW_OK)
1322 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1325 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1326 "slow copy into bufferpool buffer %p", to_put);
1328 if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1329 goto invalid_buffer;
1331 if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1332 gst_video_frame_unmap (&src);
1333 goto invalid_buffer;
1336 gst_video_frame_copy (&dest, &src);
1338 gst_video_frame_unmap (&dest);
1339 gst_video_frame_unmap (&src);
1342 if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1347 gst_buffer_unref (to_put);
1354 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1355 ("Internal error: can't allocate images"),
1356 ("We don't have a bufferpool negotiated"));
1357 return GST_FLOW_ERROR;
1361 /* No image available. That's very bad ! */
1362 GST_WARNING_OBJECT (ximagesink, "could not create image");
1367 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1368 ("Failed to create output image buffer"),
1369 ("XServer allocated buffer size did not match input buffer %"
1370 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1371 gst_buffer_get_size (buf)));
1372 res = GST_FLOW_ERROR;
1377 /* No Window available to put our image into */
1378 GST_WARNING_OBJECT (ximagesink, "could map image");
1384 /* No Window available to put our image into */
1385 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1386 res = GST_FLOW_ERROR;
1391 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1392 res = GST_FLOW_ERROR;
1398 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1400 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1402 switch (GST_EVENT_TYPE (event)) {
1403 case GST_EVENT_TAG:{
1405 gchar *title = NULL;
1407 gst_event_parse_tag (event, &l);
1408 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1411 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1412 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1422 if (GST_BASE_SINK_CLASS (parent_class)->event)
1423 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1429 gst_ximagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1431 GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1432 GstBufferPool *pool;
1433 GstStructure *config;
1438 gst_query_parse_allocation (query, &caps, &need_pool);
1443 g_mutex_lock (ximagesink->flow_lock);
1444 if ((pool = ximagesink->pool))
1445 gst_object_ref (pool);
1446 g_mutex_unlock (ximagesink->flow_lock);
1449 const GstCaps *pcaps;
1451 /* we had a pool, check caps */
1452 config = gst_buffer_pool_get_config (pool);
1453 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1455 GST_DEBUG_OBJECT (ximagesink,
1456 "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1457 if (!gst_caps_is_equal (caps, pcaps)) {
1458 /* different caps, we can't use this pool */
1459 GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1460 gst_object_unref (pool);
1464 if (pool == NULL && need_pool) {
1467 GST_DEBUG_OBJECT (ximagesink, "create new pool");
1468 pool = gst_ximage_buffer_pool_new (ximagesink);
1470 if (!gst_video_info_from_caps (&info, caps))
1473 /* the normal size of a frame */
1476 config = gst_buffer_pool_get_config (pool);
1477 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1478 if (!gst_buffer_pool_set_config (pool, config))
1481 /* we need at least 2 buffer because we hold on to the last one */
1482 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1484 /* we also support various metadata */
1485 gst_query_add_allocation_meta (query, GST_META_API_VIDEO);
1486 gst_query_add_allocation_meta (query, GST_META_API_VIDEO_CROP);
1488 gst_object_unref (pool);
1495 GST_DEBUG_OBJECT (bsink, "no caps specified");
1500 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1505 GST_DEBUG_OBJECT (bsink, "failed setting config");
1510 /* Interfaces stuff */
1512 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1513 GstStructure * structure)
1515 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1517 gint x_offset, y_offset;
1521 event = gst_event_new_navigation (structure);
1523 /* We are not converting the pointer coordinates as there's no hardware
1524 scaling done here. The only possible scaling is done by videoscale and
1525 videoscale will have to catch those events and tranform the coordinates
1526 to match the applied scaling. So here we just add the offset if the image
1527 is centered in the window. */
1529 /* We take the flow_lock while we look at the window */
1530 g_mutex_lock (ximagesink->flow_lock);
1532 if (!ximagesink->xwindow) {
1533 g_mutex_unlock (ximagesink->flow_lock);
1537 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1538 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1540 g_mutex_unlock (ximagesink->flow_lock);
1542 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1544 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1546 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1548 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1551 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1553 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1554 gst_pad_send_event (pad, event);
1556 gst_object_unref (pad);
1561 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1563 iface->send_event = gst_ximagesink_navigation_send_event;
1567 gst_ximagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1569 XID xwindow_id = id;
1570 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1571 GstXWindow *xwindow = NULL;
1572 XWindowAttributes attr;
1574 /* We acquire the stream lock while setting this window in the element.
1575 We are basically cleaning tons of stuff replacing the old window, putting
1576 images while we do that would surely crash */
1577 g_mutex_lock (ximagesink->flow_lock);
1579 /* If we already use that window return */
1580 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1581 g_mutex_unlock (ximagesink->flow_lock);
1585 /* If the element has not initialized the X11 context try to do so */
1586 if (!ximagesink->xcontext &&
1587 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1588 g_mutex_unlock (ximagesink->flow_lock);
1589 /* we have thrown a GST_ELEMENT_ERROR now */
1593 /* If a window is there already we destroy it */
1594 if (ximagesink->xwindow) {
1595 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1596 ximagesink->xwindow = NULL;
1599 /* If the xid is 0 we go back to an internal window */
1600 if (xwindow_id == 0) {
1601 /* If no width/height caps nego did not happen window will be created
1602 during caps nego then */
1603 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1604 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1605 GST_VIDEO_SINK_WIDTH (ximagesink),
1606 GST_VIDEO_SINK_HEIGHT (ximagesink));
1609 xwindow = g_new0 (GstXWindow, 1);
1611 xwindow->win = xwindow_id;
1613 /* We get window geometry, set the event we want to receive,
1615 g_mutex_lock (ximagesink->x_lock);
1616 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1617 xwindow->width = attr.width;
1618 xwindow->height = attr.height;
1619 xwindow->internal = FALSE;
1620 if (ximagesink->handle_events) {
1621 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1622 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1626 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1627 g_mutex_unlock (ximagesink->x_lock);
1631 ximagesink->xwindow = xwindow;
1633 g_mutex_unlock (ximagesink->flow_lock);
1637 gst_ximagesink_expose (GstVideoOverlay * overlay)
1639 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1641 gst_ximagesink_xwindow_update_geometry (ximagesink);
1642 gst_ximagesink_ximage_put (ximagesink, NULL);
1646 gst_ximagesink_set_event_handling (GstVideoOverlay * overlay,
1647 gboolean handle_events)
1649 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1651 ximagesink->handle_events = handle_events;
1653 g_mutex_lock (ximagesink->flow_lock);
1655 if (G_UNLIKELY (!ximagesink->xwindow)) {
1656 g_mutex_unlock (ximagesink->flow_lock);
1660 g_mutex_lock (ximagesink->x_lock);
1662 if (handle_events) {
1663 if (ximagesink->xwindow->internal) {
1664 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1665 ExposureMask | StructureNotifyMask | PointerMotionMask |
1666 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1668 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1669 ExposureMask | StructureNotifyMask | PointerMotionMask |
1670 KeyPressMask | KeyReleaseMask);
1673 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1676 g_mutex_unlock (ximagesink->x_lock);
1678 g_mutex_unlock (ximagesink->flow_lock);
1682 gst_ximagesink_video_overlay_init (GstVideoOverlayIface * iface)
1684 iface->set_window_handle = gst_ximagesink_set_window_handle;
1685 iface->expose = gst_ximagesink_expose;
1686 iface->handle_events = gst_ximagesink_set_event_handling;
1689 /* =========================================== */
1691 /* Init & Class init */
1693 /* =========================================== */
1696 gst_ximagesink_set_property (GObject * object, guint prop_id,
1697 const GValue * value, GParamSpec * pspec)
1699 GstXImageSink *ximagesink;
1701 g_return_if_fail (GST_IS_XIMAGESINK (object));
1703 ximagesink = GST_XIMAGESINK (object);
1707 ximagesink->display_name = g_strdup (g_value_get_string (value));
1709 case PROP_SYNCHRONOUS:
1710 ximagesink->synchronous = g_value_get_boolean (value);
1711 if (ximagesink->xcontext) {
1712 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1713 ximagesink->synchronous ? "TRUE" : "FALSE");
1714 g_mutex_lock (ximagesink->x_lock);
1715 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1716 g_mutex_unlock (ximagesink->x_lock);
1719 case PROP_FORCE_ASPECT_RATIO:
1720 ximagesink->keep_aspect = g_value_get_boolean (value);
1722 case PROP_PIXEL_ASPECT_RATIO:
1726 tmp = g_new0 (GValue, 1);
1727 g_value_init (tmp, GST_TYPE_FRACTION);
1729 if (!g_value_transform (value, tmp)) {
1730 GST_WARNING_OBJECT (ximagesink,
1731 "Could not transform string to aspect ratio");
1734 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1735 gst_value_get_fraction_numerator (tmp),
1736 gst_value_get_fraction_denominator (tmp));
1737 g_free (ximagesink->par);
1738 ximagesink->par = tmp;
1742 case PROP_HANDLE_EVENTS:
1743 gst_ximagesink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1744 g_value_get_boolean (value));
1745 gst_ximagesink_manage_event_thread (ximagesink);
1747 case PROP_HANDLE_EXPOSE:
1748 ximagesink->handle_expose = g_value_get_boolean (value);
1749 gst_ximagesink_manage_event_thread (ximagesink);
1752 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1758 gst_ximagesink_get_property (GObject * object, guint prop_id,
1759 GValue * value, GParamSpec * pspec)
1761 GstXImageSink *ximagesink;
1763 g_return_if_fail (GST_IS_XIMAGESINK (object));
1765 ximagesink = GST_XIMAGESINK (object);
1769 g_value_set_string (value, ximagesink->display_name);
1771 case PROP_SYNCHRONOUS:
1772 g_value_set_boolean (value, ximagesink->synchronous);
1774 case PROP_FORCE_ASPECT_RATIO:
1775 g_value_set_boolean (value, ximagesink->keep_aspect);
1777 case PROP_PIXEL_ASPECT_RATIO:
1778 if (ximagesink->par)
1779 g_value_transform (ximagesink->par, value);
1781 case PROP_HANDLE_EVENTS:
1782 g_value_set_boolean (value, ximagesink->handle_events);
1784 case PROP_HANDLE_EXPOSE:
1785 g_value_set_boolean (value, ximagesink->handle_expose);
1787 case PROP_WINDOW_WIDTH:
1788 if (ximagesink->xwindow)
1789 g_value_set_uint64 (value, ximagesink->xwindow->width);
1791 g_value_set_uint64 (value, 0);
1793 case PROP_WINDOW_HEIGHT:
1794 if (ximagesink->xwindow)
1795 g_value_set_uint64 (value, ximagesink->xwindow->height);
1797 g_value_set_uint64 (value, 0);
1800 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1806 gst_ximagesink_reset (GstXImageSink * ximagesink)
1810 GST_OBJECT_LOCK (ximagesink);
1811 ximagesink->running = FALSE;
1812 /* grab thread and mark it as NULL */
1813 thread = ximagesink->event_thread;
1814 ximagesink->event_thread = NULL;
1815 GST_OBJECT_UNLOCK (ximagesink);
1817 /* Wait for our event thread to finish before we clean up our stuff. */
1819 g_thread_join (thread);
1821 if (ximagesink->cur_image) {
1822 gst_buffer_unref (ximagesink->cur_image);
1823 ximagesink->cur_image = NULL;
1826 g_mutex_lock (ximagesink->flow_lock);
1828 if (ximagesink->pool) {
1829 gst_object_unref (ximagesink->pool);
1830 ximagesink->pool = NULL;
1833 if (ximagesink->xwindow) {
1834 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1835 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1836 ximagesink->xwindow = NULL;
1838 g_mutex_unlock (ximagesink->flow_lock);
1840 gst_ximagesink_xcontext_clear (ximagesink);
1844 gst_ximagesink_finalize (GObject * object)
1846 GstXImageSink *ximagesink;
1848 ximagesink = GST_XIMAGESINK (object);
1850 gst_ximagesink_reset (ximagesink);
1852 if (ximagesink->display_name) {
1853 g_free (ximagesink->display_name);
1854 ximagesink->display_name = NULL;
1856 if (ximagesink->par) {
1857 g_free (ximagesink->par);
1858 ximagesink->par = NULL;
1860 if (ximagesink->x_lock) {
1861 g_mutex_free (ximagesink->x_lock);
1862 ximagesink->x_lock = NULL;
1864 if (ximagesink->flow_lock) {
1865 g_mutex_free (ximagesink->flow_lock);
1866 ximagesink->flow_lock = NULL;
1869 g_free (ximagesink->media_title);
1871 G_OBJECT_CLASS (parent_class)->finalize (object);
1875 gst_ximagesink_init (GstXImageSink * ximagesink)
1877 ximagesink->display_name = NULL;
1878 ximagesink->xcontext = NULL;
1879 ximagesink->xwindow = NULL;
1880 ximagesink->cur_image = NULL;
1882 ximagesink->event_thread = NULL;
1883 ximagesink->running = FALSE;
1885 ximagesink->fps_n = 0;
1886 ximagesink->fps_d = 1;
1888 ximagesink->x_lock = g_mutex_new ();
1889 ximagesink->flow_lock = g_mutex_new ();
1891 ximagesink->par = NULL;
1893 ximagesink->pool = NULL;
1895 ximagesink->synchronous = FALSE;
1896 ximagesink->keep_aspect = FALSE;
1897 ximagesink->handle_events = TRUE;
1898 ximagesink->handle_expose = TRUE;
1902 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1904 GObjectClass *gobject_class;
1905 GstElementClass *gstelement_class;
1906 GstBaseSinkClass *gstbasesink_class;
1907 GstVideoSinkClass *videosink_class;
1909 gobject_class = (GObjectClass *) klass;
1910 gstelement_class = (GstElementClass *) klass;
1911 gstbasesink_class = (GstBaseSinkClass *) klass;
1912 videosink_class = (GstVideoSinkClass *) klass;
1914 gobject_class->finalize = gst_ximagesink_finalize;
1915 gobject_class->set_property = gst_ximagesink_set_property;
1916 gobject_class->get_property = gst_ximagesink_get_property;
1918 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1919 g_param_spec_string ("display", "Display", "X Display name",
1920 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1921 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1922 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1923 "the X display in synchronous mode. (used only for debugging)", FALSE,
1924 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1925 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1926 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1927 "When enabled, reverse caps negotiation (scaling) will respect "
1928 "original aspect ratio", FALSE,
1929 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1930 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1931 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1932 "The pixel aspect ratio of the device", "1/1",
1933 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1934 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1935 g_param_spec_boolean ("handle-events", "Handle XEvents",
1936 "When enabled, XEvents will be selected and handled", TRUE,
1937 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1938 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1939 g_param_spec_boolean ("handle-expose", "Handle expose",
1941 "the current frame will always be drawn in response to X Expose "
1942 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1945 * GstXImageSink:window-width
1947 * Actual width of the video window.
1951 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1952 g_param_spec_uint64 ("window-width", "window-width",
1953 "Width of the window", 0, G_MAXUINT64, 0,
1954 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1957 * GstXImageSink:window-height
1959 * Actual height of the video window.
1963 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1964 g_param_spec_uint64 ("window-height", "window-height",
1965 "Height of the window", 0, G_MAXUINT64, 0,
1966 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1968 gst_element_class_set_details_simple (gstelement_class,
1969 "Video sink", "Sink/Video",
1970 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1972 gst_element_class_add_pad_template (gstelement_class,
1973 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1975 gstelement_class->change_state = gst_ximagesink_change_state;
1977 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1978 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1979 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1980 gstbasesink_class->propose_allocation =
1981 GST_DEBUG_FUNCPTR (gst_ximagesink_propose_allocation);
1982 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1984 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);