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/gstvideometa.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 * iface);
172 static void gst_ximagesink_video_overlay_init (GstVideoOverlayInterface *
174 #define gst_ximagesink_parent_class parent_class
175 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
176 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
177 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
178 gst_ximagesink_video_overlay_init));
180 /* ============================================================= */
182 /* Private Methods */
184 /* ============================================================= */
188 /* We are called with the x_lock taken */
190 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
191 GstXWindow * xwindow, GstVideoRectangle rect)
193 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
194 g_return_if_fail (xwindow != NULL);
196 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
197 ximagesink->xcontext->black);
201 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
202 0, 0, rect.x, xwindow->height);
206 if ((rect.x + rect.w) < xwindow->width) {
207 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
208 rect.x + rect.w, 0, xwindow->width, xwindow->height);
213 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
214 0, 0, xwindow->width, rect.y);
218 if ((rect.y + rect.h) < xwindow->height) {
219 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
220 0, rect.y + rect.h, xwindow->width, xwindow->height);
224 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
226 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
229 GstVideoCropMeta *crop;
230 GstVideoRectangle src, dst, result;
231 gboolean draw_border = FALSE;
233 /* We take the flow_lock. If expose is in there we don't want to run
234 concurrently from the data flow thread */
235 g_mutex_lock (ximagesink->flow_lock);
237 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
238 g_mutex_unlock (ximagesink->flow_lock);
242 /* Draw borders when displaying the first frame. After this
243 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
244 if (!ximagesink->cur_image || ximagesink->draw_border) {
248 /* Store a reference to the last image we put, lose the previous one */
249 if (ximage && ximagesink->cur_image != ximage) {
250 if (ximagesink->cur_image) {
251 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
252 gst_buffer_unref (ximagesink->cur_image);
254 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
255 ximagesink->cur_image = gst_buffer_ref (ximage);
258 /* Expose sends a NULL image, we take the latest frame */
261 if (ximagesink->cur_image) {
262 ximage = ximagesink->cur_image;
264 g_mutex_unlock (ximagesink->flow_lock);
269 meta = gst_buffer_get_ximage_meta (ximage);
270 crop = gst_buffer_get_video_crop_meta (ximage);
273 src.x = crop->x + meta->x;
274 src.y = crop->y + meta->y;
276 src.h = crop->height;
281 src.h = meta->height;
283 dst.w = ximagesink->xwindow->width;
284 dst.h = ximagesink->xwindow->height;
286 gst_video_sink_center_rect (src, dst, &result, FALSE);
288 g_mutex_lock (ximagesink->x_lock);
291 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
293 ximagesink->draw_border = FALSE;
296 if (ximagesink->xcontext->use_xshm) {
297 GST_LOG_OBJECT (ximagesink,
298 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
299 ximage, 0, 0, result.x, result.y, result.w, result.h,
300 ximagesink->xwindow->width, ximagesink->xwindow->height);
301 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
302 ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
303 result.w, result.h, FALSE);
305 #endif /* HAVE_XSHM */
307 GST_LOG_OBJECT (ximagesink,
308 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
309 ximage, 0, 0, result.x, result.y, result.w, result.h,
310 ximagesink->xwindow->width, ximagesink->xwindow->height);
311 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
312 ximagesink->xwindow->gc, meta->ximage, src.x, src.y, result.x, result.y,
316 XSync (ximagesink->xcontext->disp, FALSE);
318 g_mutex_unlock (ximagesink->x_lock);
320 g_mutex_unlock (ximagesink->flow_lock);
326 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
329 Atom hints_atom = None;
332 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
333 g_return_val_if_fail (window != NULL, FALSE);
335 g_mutex_lock (ximagesink->x_lock);
337 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
339 if (hints_atom == None) {
340 g_mutex_unlock (ximagesink->x_lock);
344 hints = g_malloc0 (sizeof (MotifWmHints));
346 hints->flags |= MWM_HINTS_DECORATIONS;
347 hints->decorations = 1 << 0;
349 XChangeProperty (ximagesink->xcontext->disp, window->win,
350 hints_atom, hints_atom, 32, PropModeReplace,
351 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
353 XSync (ximagesink->xcontext->disp, FALSE);
355 g_mutex_unlock (ximagesink->x_lock);
363 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
364 GstXWindow * xwindow, const gchar * media_title)
367 g_free (ximagesink->media_title);
368 ximagesink->media_title = g_strdup (media_title);
371 /* we have a window */
372 if (xwindow->internal) {
373 XTextProperty xproperty;
374 const gchar *app_name;
375 const gchar *title = NULL;
376 gchar *title_mem = NULL;
378 /* set application name as a title */
379 app_name = g_get_application_name ();
381 if (app_name && ximagesink->media_title) {
382 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
384 } else if (app_name) {
386 } else if (ximagesink->media_title) {
387 title = ximagesink->media_title;
391 if ((XStringListToTextProperty (((char **) &title), 1,
393 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
394 XFree (xproperty.value);
403 /* This function handles a GstXWindow creation */
405 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
407 GstXWindow *xwindow = NULL;
410 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
412 xwindow = g_new0 (GstXWindow, 1);
414 xwindow->width = width;
415 xwindow->height = height;
416 xwindow->internal = TRUE;
418 g_mutex_lock (ximagesink->x_lock);
420 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
421 ximagesink->xcontext->root,
422 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
424 /* We have to do that to prevent X from redrawing the background on
425 ConfigureNotify. This takes away flickering of video when resizing. */
426 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
428 /* set application name as a title */
429 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
431 if (ximagesink->handle_events) {
434 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
435 StructureNotifyMask | PointerMotionMask | KeyPressMask |
436 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
438 /* Tell the window manager we'd like delete client messages instead of
440 wm_delete = XInternAtom (ximagesink->xcontext->disp,
441 "WM_DELETE_WINDOW", False);
442 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
446 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
449 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
451 XSync (ximagesink->xcontext->disp, FALSE);
453 g_mutex_unlock (ximagesink->x_lock);
455 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
457 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
463 /* This function destroys a GstXWindow */
465 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
466 GstXWindow * xwindow)
468 g_return_if_fail (xwindow != NULL);
469 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
471 g_mutex_lock (ximagesink->x_lock);
473 /* If we did not create that window we just free the GC and let it live */
474 if (xwindow->internal)
475 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
477 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
479 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
481 XSync (ximagesink->xcontext->disp, FALSE);
483 g_mutex_unlock (ximagesink->x_lock);
489 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
491 XWindowAttributes attr;
492 gboolean reconfigure;
494 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
496 /* Update the window geometry */
497 g_mutex_lock (ximagesink->x_lock);
498 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
499 g_mutex_unlock (ximagesink->x_lock);
503 XGetWindowAttributes (ximagesink->xcontext->disp,
504 ximagesink->xwindow->win, &attr);
506 /* Check if we would suggest a different width/height now */
507 reconfigure = (ximagesink->xwindow->width != attr.width)
508 || (ximagesink->xwindow->height != attr.height);
509 ximagesink->xwindow->width = attr.width;
510 ximagesink->xwindow->height = attr.height;
512 g_mutex_unlock (ximagesink->x_lock);
515 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
516 gst_event_new_reconfigure ());
520 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
522 g_return_if_fail (xwindow != NULL);
523 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
525 g_mutex_lock (ximagesink->x_lock);
527 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
528 ximagesink->xcontext->black);
530 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
531 0, 0, xwindow->width, xwindow->height);
533 XSync (ximagesink->xcontext->disp, FALSE);
535 g_mutex_unlock (ximagesink->x_lock);
538 /* This function handles XEvents that might be in the queue. It generates
539 GstEvent that will be sent upstream in the pipeline to handle interactivity
542 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
545 guint pointer_x = 0, pointer_y = 0;
546 gboolean pointer_moved = FALSE;
547 gboolean exposed = FALSE, configured = FALSE;
549 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
551 /* Then we get all pointer motion events, only the last position is
553 g_mutex_lock (ximagesink->flow_lock);
554 g_mutex_lock (ximagesink->x_lock);
555 while (XCheckWindowEvent (ximagesink->xcontext->disp,
556 ximagesink->xwindow->win, PointerMotionMask, &e)) {
557 g_mutex_unlock (ximagesink->x_lock);
558 g_mutex_unlock (ximagesink->flow_lock);
562 pointer_x = e.xmotion.x;
563 pointer_y = e.xmotion.y;
564 pointer_moved = TRUE;
569 g_mutex_lock (ximagesink->flow_lock);
570 g_mutex_lock (ximagesink->x_lock);
574 g_mutex_unlock (ximagesink->x_lock);
575 g_mutex_unlock (ximagesink->flow_lock);
577 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
578 pointer_x, pointer_y);
579 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
580 "mouse-move", 0, pointer_x, pointer_y);
582 g_mutex_lock (ximagesink->flow_lock);
583 g_mutex_lock (ximagesink->x_lock);
586 /* We get all remaining events on our window to throw them upstream */
587 while (XCheckWindowEvent (ximagesink->xcontext->disp,
588 ximagesink->xwindow->win,
589 KeyPressMask | KeyReleaseMask |
590 ButtonPressMask | ButtonReleaseMask, &e)) {
593 /* We lock only for the X function call */
594 g_mutex_unlock (ximagesink->x_lock);
595 g_mutex_unlock (ximagesink->flow_lock);
599 /* Mouse button pressed/released over our window. We send upstream
600 events for interactivity/navigation */
601 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
602 e.xbutton.button, e.xbutton.x, e.xbutton.x);
603 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
604 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
607 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
608 e.xbutton.button, e.xbutton.x, e.xbutton.x);
609 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
610 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
614 /* Key pressed/released over our window. We send upstream
615 events for interactivity/navigation */
616 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
617 e.xkey.keycode, e.xkey.x, e.xkey.x);
618 g_mutex_lock (ximagesink->x_lock);
619 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
621 g_mutex_unlock (ximagesink->x_lock);
622 if (keysym != NoSymbol) {
623 char *key_str = NULL;
625 g_mutex_lock (ximagesink->x_lock);
626 key_str = XKeysymToString (keysym);
627 g_mutex_unlock (ximagesink->x_lock);
628 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
629 e.type == KeyPress ? "key-press" : "key-release", key_str);
631 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
632 e.type == KeyPress ? "key-press" : "key-release", "unknown");
636 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
639 g_mutex_lock (ximagesink->flow_lock);
640 g_mutex_lock (ximagesink->x_lock);
644 while (XCheckWindowEvent (ximagesink->xcontext->disp,
645 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
650 case ConfigureNotify:
651 g_mutex_unlock (ximagesink->x_lock);
652 gst_ximagesink_xwindow_update_geometry (ximagesink);
653 g_mutex_lock (ximagesink->x_lock);
661 if (ximagesink->handle_expose && (exposed || configured)) {
662 g_mutex_unlock (ximagesink->x_lock);
663 g_mutex_unlock (ximagesink->flow_lock);
665 gst_ximagesink_expose (GST_VIDEO_OVERLAY (ximagesink));
667 g_mutex_lock (ximagesink->flow_lock);
668 g_mutex_lock (ximagesink->x_lock);
671 /* Handle Display events */
672 while (XPending (ximagesink->xcontext->disp)) {
673 XNextEvent (ximagesink->xcontext->disp, &e);
679 wm_delete = XInternAtom (ximagesink->xcontext->disp,
680 "WM_DELETE_WINDOW", False);
681 if (wm_delete == (Atom) e.xclient.data.l[0]) {
682 /* Handle window deletion by posting an error on the bus */
683 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
684 ("Output window was closed"), (NULL));
686 g_mutex_unlock (ximagesink->x_lock);
687 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
688 ximagesink->xwindow = NULL;
689 g_mutex_lock (ximagesink->x_lock);
698 g_mutex_unlock (ximagesink->x_lock);
699 g_mutex_unlock (ximagesink->flow_lock);
703 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
705 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
707 GST_OBJECT_LOCK (ximagesink);
708 while (ximagesink->running) {
709 GST_OBJECT_UNLOCK (ximagesink);
711 if (ximagesink->xwindow) {
712 gst_ximagesink_handle_xevents (ximagesink);
714 /* FIXME: do we want to align this with the framerate or anything else? */
715 g_usleep (G_USEC_PER_SEC / 20);
717 GST_OBJECT_LOCK (ximagesink);
719 GST_OBJECT_UNLOCK (ximagesink);
725 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
727 GThread *thread = NULL;
729 /* don't start the thread too early */
730 if (ximagesink->xcontext == NULL) {
734 GST_OBJECT_LOCK (ximagesink);
735 if (ximagesink->handle_expose || ximagesink->handle_events) {
736 if (!ximagesink->event_thread) {
737 /* Setup our event listening thread */
738 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
739 ximagesink->handle_expose, ximagesink->handle_events);
740 ximagesink->running = TRUE;
741 ximagesink->event_thread = g_thread_create (
742 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
745 if (ximagesink->event_thread) {
746 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
747 ximagesink->handle_expose, ximagesink->handle_events);
748 ximagesink->running = FALSE;
749 /* grab thread and mark it as NULL */
750 thread = ximagesink->event_thread;
751 ximagesink->event_thread = NULL;
754 GST_OBJECT_UNLOCK (ximagesink);
756 /* Wait for our event thread to finish */
758 g_thread_join (thread);
763 /* This function calculates the pixel aspect ratio based on the properties
764 * in the xcontext structure and stores it there. */
766 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
768 static const gint par[][2] = {
769 {1, 1}, /* regular screen */
770 {16, 15}, /* PAL TV */
771 {11, 10}, /* 525 line Rec.601 video */
772 {54, 59}, /* 625 line Rec.601 video */
773 {64, 45}, /* 1280x1024 on 16:9 display */
774 {5, 3}, /* 1280x1024 on 4:3 display */
775 {4, 3} /* 800x600 on 16:9 display */
782 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
784 /* first calculate the "real" ratio based on the X values;
785 * which is the "physical" w/h divided by the w/h in pixels of the display */
786 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
787 / (xcontext->heightmm * xcontext->width);
789 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
791 if (xcontext->width == 720 && xcontext->height == 576) {
792 ratio = 4.0 * 576 / (3.0 * 720);
794 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
796 /* now find the one from par[][2] with the lowest delta to the real one */
800 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
801 gdouble this_delta = DELTA (i);
803 if (this_delta < delta) {
809 GST_DEBUG ("Decided on index %d (%d/%d)", index,
810 par[index][0], par[index][1]);
812 g_free (xcontext->par);
813 xcontext->par = g_new0 (GValue, 1);
814 g_value_init (xcontext->par, GST_TYPE_FRACTION);
815 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
816 GST_DEBUG ("set xcontext PAR to %d/%d",
817 gst_value_get_fraction_numerator (xcontext->par),
818 gst_value_get_fraction_denominator (xcontext->par));
821 /* This function gets the X Display and global info about it. Everything is
822 stored in our object and will be cleaned when the object is disposed. Note
823 here that caps for supported format are generated without any window or
826 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
828 GstXContext *xcontext = NULL;
829 XPixmapFormatValues *px_formats = NULL;
830 gint nb_formats = 0, i;
832 GstVideoFormat vformat;
834 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
836 xcontext = g_new0 (GstXContext, 1);
838 g_mutex_lock (ximagesink->x_lock);
840 xcontext->disp = XOpenDisplay (ximagesink->display_name);
842 if (!xcontext->disp) {
843 g_mutex_unlock (ximagesink->x_lock);
845 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
846 ("Could not initialise X output"), ("Could not open display"));
850 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
851 xcontext->screen_num = DefaultScreen (xcontext->disp);
852 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
853 xcontext->root = DefaultRootWindow (xcontext->disp);
854 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
855 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
856 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
858 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
859 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
860 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
861 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
863 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
864 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
866 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
868 /* We get supported pixmap formats at supported depth */
869 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
872 XCloseDisplay (xcontext->disp);
873 g_mutex_unlock (ximagesink->x_lock);
874 g_free (xcontext->par);
876 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
877 ("Could not get supported pixmap formats"), (NULL));
881 /* We get bpp value corresponding to our running depth */
882 for (i = 0; i < nb_formats; i++) {
883 if (px_formats[i].depth == xcontext->depth)
884 xcontext->bpp = px_formats[i].bits_per_pixel;
889 endianness = (ImageByteOrder (xcontext->disp) ==
890 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
892 /* Search for XShm extension support */
894 if (XShmQueryExtension (xcontext->disp) &&
895 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
896 xcontext->use_xshm = TRUE;
897 GST_DEBUG ("ximagesink is using XShm extension");
899 #endif /* HAVE_XSHM */
901 xcontext->use_xshm = FALSE;
902 GST_DEBUG ("ximagesink is not using XShm extension");
905 vformat = gst_video_format_from_masks (xcontext->depth, xcontext->bpp,
906 endianness, xcontext->visual->red_mask, xcontext->visual->green_mask,
907 xcontext->visual->blue_mask, 0);
909 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
912 /* update object's par with calculated one if not set yet */
913 if (!ximagesink->par) {
914 ximagesink->par = g_new0 (GValue, 1);
915 gst_value_init_and_copy (ximagesink->par, xcontext->par);
916 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
918 xcontext->caps = gst_caps_new_simple ("video/x-raw",
919 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
920 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
921 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
922 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
923 if (ximagesink->par) {
926 nom = gst_value_get_fraction_numerator (ximagesink->par);
927 den = gst_value_get_fraction_denominator (ximagesink->par);
928 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
929 GST_TYPE_FRACTION, nom, den, NULL);
932 g_mutex_unlock (ximagesink->x_lock);
939 GST_ERROR_OBJECT (ximagesink, "unknown format");
944 /* This function cleans the X context. Closing the Display and unrefing the
945 caps for supported formats. */
947 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
949 GstXContext *xcontext;
951 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
953 GST_OBJECT_LOCK (ximagesink);
954 if (ximagesink->xcontext == NULL) {
955 GST_OBJECT_UNLOCK (ximagesink);
959 /* Take the xcontext reference and NULL it while we
960 * clean it up, so that any buffer-alloced buffers
961 * arriving after this will be freed correctly */
962 xcontext = ximagesink->xcontext;
963 ximagesink->xcontext = NULL;
965 GST_OBJECT_UNLOCK (ximagesink);
967 gst_caps_unref (xcontext->caps);
968 g_free (xcontext->par);
969 g_free (ximagesink->par);
970 ximagesink->par = NULL;
972 if (xcontext->last_caps)
973 gst_caps_replace (&xcontext->last_caps, NULL);
975 g_mutex_lock (ximagesink->x_lock);
977 XCloseDisplay (xcontext->disp);
979 g_mutex_unlock (ximagesink->x_lock);
987 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
989 GstXImageSink *ximagesink;
993 ximagesink = GST_XIMAGESINK (bsink);
995 g_mutex_lock (ximagesink->x_lock);
996 if (ximagesink->xcontext) {
999 caps = gst_caps_ref (ximagesink->xcontext->caps);
1002 GstCaps *intersection;
1005 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1006 gst_caps_unref (caps);
1007 caps = intersection;
1010 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1011 GstStructure *s0, *s1;
1013 caps = gst_caps_make_writable (caps);
1015 /* There can only be a single structure because the xcontext
1016 * caps only have a single structure */
1017 s0 = gst_caps_get_structure (caps, 0);
1018 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1020 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1021 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1022 gst_caps_append_structure (caps, s1);
1024 /* This will not change the order but will remove the
1025 * fixed width/height caps again if not possible
1028 GstCaps *intersection;
1031 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1032 gst_caps_unref (caps);
1033 caps = intersection;
1037 g_mutex_unlock (ximagesink->x_lock);
1040 g_mutex_unlock (ximagesink->x_lock);
1042 /* get a template copy and add the pixel aspect ratio */
1043 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1044 if (ximagesink->par) {
1045 caps = gst_caps_make_writable (caps);
1046 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1047 GstStructure *structure = gst_caps_get_structure (caps, i);
1050 nom = gst_value_get_fraction_numerator (ximagesink->par);
1051 den = gst_value_get_fraction_denominator (ximagesink->par);
1052 gst_structure_set (structure, "pixel-aspect-ratio",
1053 GST_TYPE_FRACTION, nom, den, NULL);
1058 GstCaps *intersection;
1061 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1062 gst_caps_unref (caps);
1063 caps = intersection;
1070 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1072 GstXImageSink *ximagesink;
1073 GstStructure *structure;
1075 GstBufferPool *newpool, *oldpool;
1079 ximagesink = GST_XIMAGESINK (bsink);
1081 if (!ximagesink->xcontext)
1084 GST_DEBUG_OBJECT (ximagesink,
1085 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1086 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1088 /* We intersect those caps with our template to make sure they are correct */
1089 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1090 goto incompatible_caps;
1092 if (!gst_video_info_from_caps (&info, caps))
1093 goto invalid_format;
1097 structure = gst_caps_get_structure (caps, 0);
1098 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1099 * otherwise linking should fail */
1100 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1102 if (ximagesink->par) {
1103 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1106 } else if (ximagesink->xcontext->par) {
1107 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1113 GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1114 GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1115 ximagesink->fps_n = info.fps_n;
1116 ximagesink->fps_d = info.fps_d;
1118 /* Notify application to set xwindow id now */
1119 g_mutex_lock (ximagesink->flow_lock);
1120 if (!ximagesink->xwindow) {
1121 g_mutex_unlock (ximagesink->flow_lock);
1122 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1124 g_mutex_unlock (ximagesink->flow_lock);
1127 /* Creating our window and our image */
1128 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1129 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1132 g_mutex_lock (ximagesink->flow_lock);
1133 if (!ximagesink->xwindow) {
1134 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1135 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1138 ximagesink->info = info;
1140 /* Remember to draw borders for next frame */
1141 ximagesink->draw_border = TRUE;
1143 /* create a new pool for the new configuration */
1144 newpool = gst_ximage_buffer_pool_new (ximagesink);
1146 structure = gst_buffer_pool_get_config (newpool);
1147 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1148 if (!gst_buffer_pool_set_config (newpool, structure))
1151 oldpool = ximagesink->pool;
1152 ximagesink->pool = newpool;
1154 /* unref the old sink */
1156 /* we don't deactivate, some elements might still be using it, it will be
1157 * deactivated when the last ref is gone */
1158 gst_object_unref (oldpool);
1160 g_mutex_unlock (ximagesink->flow_lock);
1167 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1172 GST_ERROR_OBJECT (ximagesink, "caps invalid");
1177 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1182 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1183 ("Invalid image size."));
1188 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1189 g_mutex_unlock (ximagesink->flow_lock);
1194 static GstStateChangeReturn
1195 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1197 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1198 GstXImageSink *ximagesink;
1199 GstXContext *xcontext = NULL;
1201 ximagesink = GST_XIMAGESINK (element);
1203 switch (transition) {
1204 case GST_STATE_CHANGE_NULL_TO_READY:
1205 /* Initializing the XContext */
1206 if (ximagesink->xcontext == NULL) {
1207 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1208 if (xcontext == NULL) {
1209 ret = GST_STATE_CHANGE_FAILURE;
1212 GST_OBJECT_LOCK (ximagesink);
1214 ximagesink->xcontext = xcontext;
1215 GST_OBJECT_UNLOCK (ximagesink);
1218 /* call XSynchronize with the current value of synchronous */
1219 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1220 ximagesink->synchronous ? "TRUE" : "FALSE");
1221 g_mutex_lock (ximagesink->x_lock);
1222 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1223 g_mutex_unlock (ximagesink->x_lock);
1224 gst_ximagesink_manage_event_thread (ximagesink);
1226 case GST_STATE_CHANGE_READY_TO_PAUSED:
1227 g_mutex_lock (ximagesink->flow_lock);
1228 if (ximagesink->xwindow)
1229 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1230 g_mutex_unlock (ximagesink->flow_lock);
1232 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1238 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1240 switch (transition) {
1241 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1243 case GST_STATE_CHANGE_PAUSED_TO_READY:
1244 ximagesink->fps_n = 0;
1245 ximagesink->fps_d = 1;
1246 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1247 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1248 g_mutex_lock (ximagesink->flow_lock);
1249 if (ximagesink->pool)
1250 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1251 g_mutex_unlock (ximagesink->flow_lock);
1253 case GST_STATE_CHANGE_READY_TO_NULL:
1254 gst_ximagesink_reset (ximagesink);
1265 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1266 GstClockTime * start, GstClockTime * end)
1268 GstXImageSink *ximagesink;
1270 ximagesink = GST_XIMAGESINK (bsink);
1272 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1273 *start = GST_BUFFER_TIMESTAMP (buf);
1274 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1275 *end = *start + GST_BUFFER_DURATION (buf);
1277 if (ximagesink->fps_n > 0) {
1279 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1286 static GstFlowReturn
1287 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1290 GstXImageSink *ximagesink;
1291 GstXImageMeta *meta;
1292 GstBuffer *to_put = NULL;
1294 ximagesink = GST_XIMAGESINK (vsink);
1296 meta = gst_buffer_get_ximage_meta (buf);
1298 if (meta && meta->sink == ximagesink) {
1299 /* If this buffer has been allocated using our buffer management we simply
1300 put the ximage which is in the PRIVATE pointer */
1301 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1305 GstVideoFrame src, dest;
1307 /* Else we have to copy the data into our private image, */
1308 /* if we have one... */
1309 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1311 /* we should have a pool, configured in setcaps */
1312 if (ximagesink->pool == NULL)
1315 if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1316 goto activate_failed;
1318 /* take a buffer form our pool */
1319 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, NULL);
1320 if (res != GST_FLOW_OK)
1323 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1326 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1327 "slow copy into bufferpool buffer %p", to_put);
1329 if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1330 goto invalid_buffer;
1332 if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1333 gst_video_frame_unmap (&src);
1334 goto invalid_buffer;
1337 gst_video_frame_copy (&dest, &src);
1339 gst_video_frame_unmap (&dest);
1340 gst_video_frame_unmap (&src);
1343 if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1348 gst_buffer_unref (to_put);
1355 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1356 ("Internal error: can't allocate images"),
1357 ("We don't have a bufferpool negotiated"));
1358 return GST_FLOW_ERROR;
1362 /* No image available. That's very bad ! */
1363 GST_WARNING_OBJECT (ximagesink, "could not create image");
1368 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1369 ("Failed to create output image buffer"),
1370 ("XServer allocated buffer size did not match input buffer %"
1371 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1372 gst_buffer_get_size (buf)));
1373 res = GST_FLOW_ERROR;
1378 /* No Window available to put our image into */
1379 GST_WARNING_OBJECT (ximagesink, "could map image");
1385 /* No Window available to put our image into */
1386 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1387 res = GST_FLOW_ERROR;
1392 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1393 res = GST_FLOW_ERROR;
1399 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1401 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1403 switch (GST_EVENT_TYPE (event)) {
1404 case GST_EVENT_TAG:{
1406 gchar *title = NULL;
1408 gst_event_parse_tag (event, &l);
1409 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1412 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1413 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1423 if (GST_BASE_SINK_CLASS (parent_class)->event)
1424 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1430 gst_ximagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1432 GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1433 GstBufferPool *pool;
1434 GstStructure *config;
1439 gst_query_parse_allocation (query, &caps, &need_pool);
1444 g_mutex_lock (ximagesink->flow_lock);
1445 if ((pool = ximagesink->pool))
1446 gst_object_ref (pool);
1447 g_mutex_unlock (ximagesink->flow_lock);
1450 const GstCaps *pcaps;
1452 /* we had a pool, check caps */
1453 config = gst_buffer_pool_get_config (pool);
1454 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1456 GST_DEBUG_OBJECT (ximagesink,
1457 "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1458 if (!gst_caps_is_equal (caps, pcaps)) {
1459 /* different caps, we can't use this pool */
1460 GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1461 gst_object_unref (pool);
1465 if (pool == NULL && need_pool) {
1468 GST_DEBUG_OBJECT (ximagesink, "create new pool");
1469 pool = gst_ximage_buffer_pool_new (ximagesink);
1471 if (!gst_video_info_from_caps (&info, caps))
1474 /* the normal size of a frame */
1477 config = gst_buffer_pool_get_config (pool);
1478 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1479 if (!gst_buffer_pool_set_config (pool, config))
1482 /* we need at least 2 buffer because we hold on to the last one */
1483 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1485 /* we also support various metadata */
1486 gst_query_add_allocation_meta (query, GST_VIDEO_META_API);
1487 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API);
1489 gst_object_unref (pool);
1496 GST_DEBUG_OBJECT (bsink, "no caps specified");
1501 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1506 GST_DEBUG_OBJECT (bsink, "failed setting config");
1511 /* Interfaces stuff */
1513 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1514 GstStructure * structure)
1516 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1518 gint x_offset, y_offset;
1522 event = gst_event_new_navigation (structure);
1524 /* We are not converting the pointer coordinates as there's no hardware
1525 scaling done here. The only possible scaling is done by videoscale and
1526 videoscale will have to catch those events and tranform the coordinates
1527 to match the applied scaling. So here we just add the offset if the image
1528 is centered in the window. */
1530 /* We take the flow_lock while we look at the window */
1531 g_mutex_lock (ximagesink->flow_lock);
1533 if (!ximagesink->xwindow) {
1534 g_mutex_unlock (ximagesink->flow_lock);
1538 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1539 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1541 g_mutex_unlock (ximagesink->flow_lock);
1543 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1545 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1547 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1549 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1552 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1554 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1555 gst_pad_send_event (pad, event);
1557 gst_object_unref (pad);
1562 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1564 iface->send_event = gst_ximagesink_navigation_send_event;
1568 gst_ximagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1570 XID xwindow_id = id;
1571 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1572 GstXWindow *xwindow = NULL;
1573 XWindowAttributes attr;
1575 /* We acquire the stream lock while setting this window in the element.
1576 We are basically cleaning tons of stuff replacing the old window, putting
1577 images while we do that would surely crash */
1578 g_mutex_lock (ximagesink->flow_lock);
1580 /* If we already use that window return */
1581 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1582 g_mutex_unlock (ximagesink->flow_lock);
1586 /* If the element has not initialized the X11 context try to do so */
1587 if (!ximagesink->xcontext &&
1588 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1589 g_mutex_unlock (ximagesink->flow_lock);
1590 /* we have thrown a GST_ELEMENT_ERROR now */
1594 /* If a window is there already we destroy it */
1595 if (ximagesink->xwindow) {
1596 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1597 ximagesink->xwindow = NULL;
1600 /* If the xid is 0 we go back to an internal window */
1601 if (xwindow_id == 0) {
1602 /* If no width/height caps nego did not happen window will be created
1603 during caps nego then */
1604 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1605 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1606 GST_VIDEO_SINK_WIDTH (ximagesink),
1607 GST_VIDEO_SINK_HEIGHT (ximagesink));
1610 xwindow = g_new0 (GstXWindow, 1);
1612 xwindow->win = xwindow_id;
1614 /* We get window geometry, set the event we want to receive,
1616 g_mutex_lock (ximagesink->x_lock);
1617 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1618 xwindow->width = attr.width;
1619 xwindow->height = attr.height;
1620 xwindow->internal = FALSE;
1621 if (ximagesink->handle_events) {
1622 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1623 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1627 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1628 g_mutex_unlock (ximagesink->x_lock);
1632 ximagesink->xwindow = xwindow;
1634 g_mutex_unlock (ximagesink->flow_lock);
1638 gst_ximagesink_expose (GstVideoOverlay * overlay)
1640 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1642 gst_ximagesink_xwindow_update_geometry (ximagesink);
1643 gst_ximagesink_ximage_put (ximagesink, NULL);
1647 gst_ximagesink_set_event_handling (GstVideoOverlay * overlay,
1648 gboolean handle_events)
1650 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1652 ximagesink->handle_events = handle_events;
1654 g_mutex_lock (ximagesink->flow_lock);
1656 if (G_UNLIKELY (!ximagesink->xwindow)) {
1657 g_mutex_unlock (ximagesink->flow_lock);
1661 g_mutex_lock (ximagesink->x_lock);
1663 if (handle_events) {
1664 if (ximagesink->xwindow->internal) {
1665 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1666 ExposureMask | StructureNotifyMask | PointerMotionMask |
1667 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1669 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1670 ExposureMask | StructureNotifyMask | PointerMotionMask |
1671 KeyPressMask | KeyReleaseMask);
1674 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1677 g_mutex_unlock (ximagesink->x_lock);
1679 g_mutex_unlock (ximagesink->flow_lock);
1683 gst_ximagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1685 iface->set_window_handle = gst_ximagesink_set_window_handle;
1686 iface->expose = gst_ximagesink_expose;
1687 iface->handle_events = gst_ximagesink_set_event_handling;
1690 /* =========================================== */
1692 /* Init & Class init */
1694 /* =========================================== */
1697 gst_ximagesink_set_property (GObject * object, guint prop_id,
1698 const GValue * value, GParamSpec * pspec)
1700 GstXImageSink *ximagesink;
1702 g_return_if_fail (GST_IS_XIMAGESINK (object));
1704 ximagesink = GST_XIMAGESINK (object);
1708 ximagesink->display_name = g_strdup (g_value_get_string (value));
1710 case PROP_SYNCHRONOUS:
1711 ximagesink->synchronous = g_value_get_boolean (value);
1712 if (ximagesink->xcontext) {
1713 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1714 ximagesink->synchronous ? "TRUE" : "FALSE");
1715 g_mutex_lock (ximagesink->x_lock);
1716 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1717 g_mutex_unlock (ximagesink->x_lock);
1720 case PROP_FORCE_ASPECT_RATIO:
1721 ximagesink->keep_aspect = g_value_get_boolean (value);
1723 case PROP_PIXEL_ASPECT_RATIO:
1727 tmp = g_new0 (GValue, 1);
1728 g_value_init (tmp, GST_TYPE_FRACTION);
1730 if (!g_value_transform (value, tmp)) {
1731 GST_WARNING_OBJECT (ximagesink,
1732 "Could not transform string to aspect ratio");
1735 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1736 gst_value_get_fraction_numerator (tmp),
1737 gst_value_get_fraction_denominator (tmp));
1738 g_free (ximagesink->par);
1739 ximagesink->par = tmp;
1743 case PROP_HANDLE_EVENTS:
1744 gst_ximagesink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1745 g_value_get_boolean (value));
1746 gst_ximagesink_manage_event_thread (ximagesink);
1748 case PROP_HANDLE_EXPOSE:
1749 ximagesink->handle_expose = g_value_get_boolean (value);
1750 gst_ximagesink_manage_event_thread (ximagesink);
1753 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1759 gst_ximagesink_get_property (GObject * object, guint prop_id,
1760 GValue * value, GParamSpec * pspec)
1762 GstXImageSink *ximagesink;
1764 g_return_if_fail (GST_IS_XIMAGESINK (object));
1766 ximagesink = GST_XIMAGESINK (object);
1770 g_value_set_string (value, ximagesink->display_name);
1772 case PROP_SYNCHRONOUS:
1773 g_value_set_boolean (value, ximagesink->synchronous);
1775 case PROP_FORCE_ASPECT_RATIO:
1776 g_value_set_boolean (value, ximagesink->keep_aspect);
1778 case PROP_PIXEL_ASPECT_RATIO:
1779 if (ximagesink->par)
1780 g_value_transform (ximagesink->par, value);
1782 case PROP_HANDLE_EVENTS:
1783 g_value_set_boolean (value, ximagesink->handle_events);
1785 case PROP_HANDLE_EXPOSE:
1786 g_value_set_boolean (value, ximagesink->handle_expose);
1788 case PROP_WINDOW_WIDTH:
1789 if (ximagesink->xwindow)
1790 g_value_set_uint64 (value, ximagesink->xwindow->width);
1792 g_value_set_uint64 (value, 0);
1794 case PROP_WINDOW_HEIGHT:
1795 if (ximagesink->xwindow)
1796 g_value_set_uint64 (value, ximagesink->xwindow->height);
1798 g_value_set_uint64 (value, 0);
1801 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1807 gst_ximagesink_reset (GstXImageSink * ximagesink)
1811 GST_OBJECT_LOCK (ximagesink);
1812 ximagesink->running = FALSE;
1813 /* grab thread and mark it as NULL */
1814 thread = ximagesink->event_thread;
1815 ximagesink->event_thread = NULL;
1816 GST_OBJECT_UNLOCK (ximagesink);
1818 /* Wait for our event thread to finish before we clean up our stuff. */
1820 g_thread_join (thread);
1822 if (ximagesink->cur_image) {
1823 gst_buffer_unref (ximagesink->cur_image);
1824 ximagesink->cur_image = NULL;
1827 g_mutex_lock (ximagesink->flow_lock);
1829 if (ximagesink->pool) {
1830 gst_object_unref (ximagesink->pool);
1831 ximagesink->pool = NULL;
1834 if (ximagesink->xwindow) {
1835 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1836 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1837 ximagesink->xwindow = NULL;
1839 g_mutex_unlock (ximagesink->flow_lock);
1841 gst_ximagesink_xcontext_clear (ximagesink);
1845 gst_ximagesink_finalize (GObject * object)
1847 GstXImageSink *ximagesink;
1849 ximagesink = GST_XIMAGESINK (object);
1851 gst_ximagesink_reset (ximagesink);
1853 if (ximagesink->display_name) {
1854 g_free (ximagesink->display_name);
1855 ximagesink->display_name = NULL;
1857 if (ximagesink->par) {
1858 g_free (ximagesink->par);
1859 ximagesink->par = NULL;
1861 if (ximagesink->x_lock) {
1862 g_mutex_free (ximagesink->x_lock);
1863 ximagesink->x_lock = NULL;
1865 if (ximagesink->flow_lock) {
1866 g_mutex_free (ximagesink->flow_lock);
1867 ximagesink->flow_lock = NULL;
1870 g_free (ximagesink->media_title);
1872 G_OBJECT_CLASS (parent_class)->finalize (object);
1876 gst_ximagesink_init (GstXImageSink * ximagesink)
1878 ximagesink->display_name = NULL;
1879 ximagesink->xcontext = NULL;
1880 ximagesink->xwindow = NULL;
1881 ximagesink->cur_image = NULL;
1883 ximagesink->event_thread = NULL;
1884 ximagesink->running = FALSE;
1886 ximagesink->fps_n = 0;
1887 ximagesink->fps_d = 1;
1889 ximagesink->x_lock = g_mutex_new ();
1890 ximagesink->flow_lock = g_mutex_new ();
1892 ximagesink->par = NULL;
1894 ximagesink->pool = NULL;
1896 ximagesink->synchronous = FALSE;
1897 ximagesink->keep_aspect = FALSE;
1898 ximagesink->handle_events = TRUE;
1899 ximagesink->handle_expose = TRUE;
1903 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1905 GObjectClass *gobject_class;
1906 GstElementClass *gstelement_class;
1907 GstBaseSinkClass *gstbasesink_class;
1908 GstVideoSinkClass *videosink_class;
1910 gobject_class = (GObjectClass *) klass;
1911 gstelement_class = (GstElementClass *) klass;
1912 gstbasesink_class = (GstBaseSinkClass *) klass;
1913 videosink_class = (GstVideoSinkClass *) klass;
1915 gobject_class->finalize = gst_ximagesink_finalize;
1916 gobject_class->set_property = gst_ximagesink_set_property;
1917 gobject_class->get_property = gst_ximagesink_get_property;
1919 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1920 g_param_spec_string ("display", "Display", "X Display name",
1921 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1922 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1923 g_param_spec_boolean ("synchronous", "Synchronous",
1924 "When enabled, runs the X display in synchronous mode. "
1925 "(unrelated to A/V sync, used only for debugging)", FALSE,
1926 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1927 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1928 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1929 "When enabled, reverse caps negotiation (scaling) will respect "
1930 "original aspect ratio", FALSE,
1931 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1933 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1934 "The pixel aspect ratio of the device", "1/1",
1935 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1936 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1937 g_param_spec_boolean ("handle-events", "Handle XEvents",
1938 "When enabled, XEvents will be selected and handled", TRUE,
1939 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1940 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1941 g_param_spec_boolean ("handle-expose", "Handle expose",
1943 "the current frame will always be drawn in response to X Expose "
1944 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1947 * GstXImageSink:window-width
1949 * Actual width of the video window.
1953 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1954 g_param_spec_uint64 ("window-width", "window-width",
1955 "Width of the window", 0, G_MAXUINT64, 0,
1956 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1959 * GstXImageSink:window-height
1961 * Actual height of the video window.
1965 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1966 g_param_spec_uint64 ("window-height", "window-height",
1967 "Height of the window", 0, G_MAXUINT64, 0,
1968 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1970 gst_element_class_set_details_simple (gstelement_class,
1971 "Video sink", "Sink/Video",
1972 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1974 gst_element_class_add_pad_template (gstelement_class,
1975 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1977 gstelement_class->change_state = gst_ximagesink_change_state;
1979 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1980 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1981 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1982 gstbasesink_class->propose_allocation =
1983 GST_DEBUG_FUNCPTR (gst_ximagesink_propose_allocation);
1984 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1986 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);