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., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, 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/video/navigation.h>
108 #include <gst/video/videooverlay.h>
110 #include <gst/video/gstvideometa.h>
113 #include "ximagesink.h"
115 /* Debugging category */
116 #include <gst/gstinfo.h>
118 /* for XkbKeycodeToKeysym */
119 #include <X11/XKBlib.h>
121 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
122 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
123 #define GST_CAT_DEFAULT gst_debug_ximagesink
128 unsigned long functions;
129 unsigned long decorations;
131 unsigned long status;
133 MotifWmHints, MwmHints;
135 #define MWM_HINTS_DECORATIONS (1L << 1)
137 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
138 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
139 static void gst_ximagesink_expose (GstVideoOverlay * overlay);
141 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
142 GST_STATIC_PAD_TEMPLATE ("sink",
145 GST_STATIC_CAPS ("video/x-raw, "
146 "framerate = (fraction) [ 0, MAX ], "
147 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
155 PROP_PIXEL_ASPECT_RATIO,
156 PROP_FORCE_ASPECT_RATIO,
163 /* ============================================================= */
167 /* ============================================================= */
169 /* =========================================== */
171 /* Object typing & Creation */
173 /* =========================================== */
174 static void gst_ximagesink_navigation_init (GstNavigationInterface * iface);
175 static void gst_ximagesink_video_overlay_init (GstVideoOverlayInterface *
177 #define gst_ximagesink_parent_class parent_class
178 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
179 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
180 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
181 gst_ximagesink_video_overlay_init));
183 /* ============================================================= */
185 /* Private Methods */
187 /* ============================================================= */
191 /* We are called with the x_lock taken */
193 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
194 GstXWindow * xwindow, GstVideoRectangle rect)
196 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
197 g_return_if_fail (xwindow != NULL);
199 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
200 ximagesink->xcontext->black);
204 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
205 0, 0, rect.x, xwindow->height);
209 if ((rect.x + rect.w) < xwindow->width) {
210 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
211 rect.x + rect.w, 0, xwindow->width, xwindow->height);
216 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
217 0, 0, xwindow->width, rect.y);
221 if ((rect.y + rect.h) < xwindow->height) {
222 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
223 0, rect.y + rect.h, xwindow->width, xwindow->height);
227 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
229 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
231 GstXImageMemory *mem;
232 GstVideoCropMeta *crop;
233 GstVideoRectangle src, dst, result;
234 gboolean draw_border = FALSE;
236 /* We take the flow_lock. If expose is in there we don't want to run
237 concurrently from the data flow thread */
238 g_mutex_lock (&ximagesink->flow_lock);
240 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
241 g_mutex_unlock (&ximagesink->flow_lock);
245 /* Draw borders when displaying the first frame. After this
246 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
247 if (!ximagesink->cur_image || ximagesink->draw_border) {
251 /* Store a reference to the last image we put, lose the previous one */
252 if (ximage && ximagesink->cur_image != ximage) {
253 if (ximagesink->cur_image) {
254 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
255 gst_buffer_unref (ximagesink->cur_image);
257 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
258 ximagesink->cur_image = gst_buffer_ref (ximage);
261 /* Expose sends a NULL image, we take the latest frame */
264 if (ximagesink->cur_image) {
265 ximage = ximagesink->cur_image;
267 g_mutex_unlock (&ximagesink->flow_lock);
272 mem = (GstXImageMemory *) gst_buffer_peek_memory (ximage, 0);
273 crop = gst_buffer_get_video_crop_meta (ximage);
276 src.x = crop->x + mem->x;
277 src.y = crop->y + mem->y;
279 src.h = crop->height;
280 GST_LOG_OBJECT (ximagesink,
281 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
288 dst.w = ximagesink->xwindow->width;
289 dst.h = ximagesink->xwindow->height;
291 gst_video_sink_center_rect (src, dst, &result, FALSE);
293 g_mutex_lock (&ximagesink->x_lock);
296 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
298 ximagesink->draw_border = FALSE;
301 if (ximagesink->xcontext->use_xshm) {
302 GST_LOG_OBJECT (ximagesink,
303 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
304 ximage, 0, 0, result.x, result.y, result.w, result.h,
305 ximagesink->xwindow->width, ximagesink->xwindow->height);
306 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
307 ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
308 result.w, result.h, FALSE);
310 #endif /* HAVE_XSHM */
312 GST_LOG_OBJECT (ximagesink,
313 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
314 ximage, 0, 0, result.x, result.y, result.w, result.h,
315 ximagesink->xwindow->width, ximagesink->xwindow->height);
316 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
317 ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
321 XSync (ximagesink->xcontext->disp, FALSE);
323 g_mutex_unlock (&ximagesink->x_lock);
325 g_mutex_unlock (&ximagesink->flow_lock);
331 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
334 Atom hints_atom = None;
337 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
338 g_return_val_if_fail (window != NULL, FALSE);
340 g_mutex_lock (&ximagesink->x_lock);
342 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
344 if (hints_atom == None) {
345 g_mutex_unlock (&ximagesink->x_lock);
349 hints = g_malloc0 (sizeof (MotifWmHints));
351 hints->flags |= MWM_HINTS_DECORATIONS;
352 hints->decorations = 1 << 0;
354 XChangeProperty (ximagesink->xcontext->disp, window->win,
355 hints_atom, hints_atom, 32, PropModeReplace,
356 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
358 XSync (ximagesink->xcontext->disp, FALSE);
360 g_mutex_unlock (&ximagesink->x_lock);
368 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
369 GstXWindow * xwindow, const gchar * media_title)
372 g_free (ximagesink->media_title);
373 ximagesink->media_title = g_strdup (media_title);
376 /* we have a window */
377 if (xwindow->internal) {
378 XTextProperty xproperty;
379 const gchar *app_name;
380 const gchar *title = NULL;
381 gchar *title_mem = NULL;
383 /* set application name as a title */
384 app_name = g_get_application_name ();
386 if (app_name && ximagesink->media_title) {
387 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
389 } else if (app_name) {
391 } else if (ximagesink->media_title) {
392 title = ximagesink->media_title;
396 if ((XStringListToTextProperty (((char **) &title), 1,
398 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
399 XFree (xproperty.value);
408 /* This function handles a GstXWindow creation */
410 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
412 GstXWindow *xwindow = NULL;
415 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
417 xwindow = g_new0 (GstXWindow, 1);
419 xwindow->width = width;
420 xwindow->height = height;
421 xwindow->internal = TRUE;
423 g_mutex_lock (&ximagesink->x_lock);
425 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
426 ximagesink->xcontext->root,
427 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
429 /* We have to do that to prevent X from redrawing the background on
430 ConfigureNotify. This takes away flickering of video when resizing. */
431 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
433 /* set application name as a title */
434 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
436 if (ximagesink->handle_events) {
439 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
440 StructureNotifyMask | PointerMotionMask | KeyPressMask |
441 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
443 /* Tell the window manager we'd like delete client messages instead of
445 wm_delete = XInternAtom (ximagesink->xcontext->disp,
446 "WM_DELETE_WINDOW", False);
447 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
451 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
454 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
456 XSync (ximagesink->xcontext->disp, FALSE);
458 g_mutex_unlock (&ximagesink->x_lock);
460 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
462 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
468 /* This function destroys a GstXWindow */
470 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
471 GstXWindow * xwindow)
473 g_return_if_fail (xwindow != NULL);
474 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
476 g_mutex_lock (&ximagesink->x_lock);
478 /* If we did not create that window we just free the GC and let it live */
479 if (xwindow->internal)
480 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
482 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
484 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
486 XSync (ximagesink->xcontext->disp, FALSE);
488 g_mutex_unlock (&ximagesink->x_lock);
494 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
496 XWindowAttributes attr;
497 gboolean reconfigure;
499 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
501 /* Update the window geometry */
502 g_mutex_lock (&ximagesink->x_lock);
503 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
504 g_mutex_unlock (&ximagesink->x_lock);
508 XGetWindowAttributes (ximagesink->xcontext->disp,
509 ximagesink->xwindow->win, &attr);
511 /* Check if we would suggest a different width/height now */
512 reconfigure = (ximagesink->xwindow->width != attr.width)
513 || (ximagesink->xwindow->height != attr.height);
514 ximagesink->xwindow->width = attr.width;
515 ximagesink->xwindow->height = attr.height;
517 g_mutex_unlock (&ximagesink->x_lock);
520 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
521 gst_event_new_reconfigure ());
525 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
527 g_return_if_fail (xwindow != NULL);
528 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
530 g_mutex_lock (&ximagesink->x_lock);
532 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
533 ximagesink->xcontext->black);
535 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
536 0, 0, xwindow->width, xwindow->height);
538 XSync (ximagesink->xcontext->disp, FALSE);
540 g_mutex_unlock (&ximagesink->x_lock);
543 /* This function handles XEvents that might be in the queue. It generates
544 GstEvent that will be sent upstream in the pipeline to handle interactivity
547 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
550 guint pointer_x = 0, pointer_y = 0;
551 gboolean pointer_moved = FALSE;
552 gboolean exposed = FALSE, configured = FALSE;
554 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
556 /* Then we get all pointer motion events, only the last position is
558 g_mutex_lock (&ximagesink->flow_lock);
559 g_mutex_lock (&ximagesink->x_lock);
560 while (XCheckWindowEvent (ximagesink->xcontext->disp,
561 ximagesink->xwindow->win, PointerMotionMask, &e)) {
562 g_mutex_unlock (&ximagesink->x_lock);
563 g_mutex_unlock (&ximagesink->flow_lock);
567 pointer_x = e.xmotion.x;
568 pointer_y = e.xmotion.y;
569 pointer_moved = TRUE;
574 g_mutex_lock (&ximagesink->flow_lock);
575 g_mutex_lock (&ximagesink->x_lock);
579 g_mutex_unlock (&ximagesink->x_lock);
580 g_mutex_unlock (&ximagesink->flow_lock);
582 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
583 pointer_x, pointer_y);
584 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
585 "mouse-move", 0, pointer_x, pointer_y);
587 g_mutex_lock (&ximagesink->flow_lock);
588 g_mutex_lock (&ximagesink->x_lock);
591 /* We get all remaining events on our window to throw them upstream */
592 while (XCheckWindowEvent (ximagesink->xcontext->disp,
593 ximagesink->xwindow->win,
594 KeyPressMask | KeyReleaseMask |
595 ButtonPressMask | ButtonReleaseMask, &e)) {
597 const char *key_str = NULL;
599 /* We lock only for the X function call */
600 g_mutex_unlock (&ximagesink->x_lock);
601 g_mutex_unlock (&ximagesink->flow_lock);
605 /* Mouse button pressed/released over our window. We send upstream
606 events for interactivity/navigation */
607 GST_DEBUG ("ximagesink button %d pressed 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-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
613 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
614 e.xbutton.button, e.xbutton.x, e.xbutton.x);
615 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
616 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
620 /* Key pressed/released over our window. We send upstream
621 events for interactivity/navigation */
622 g_mutex_lock (&ximagesink->x_lock);
623 keysym = XkbKeycodeToKeysym (ximagesink->xcontext->disp,
624 e.xkey.keycode, 0, 0);
625 if (keysym != NoSymbol) {
626 key_str = XKeysymToString (keysym);
630 g_mutex_unlock (&ximagesink->x_lock);
631 GST_DEBUG_OBJECT (ximagesink,
632 "key %d pressed over window at %d,%d (%s)",
633 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
634 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
635 e.type == KeyPress ? "key-press" : "key-release", key_str);
638 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
641 g_mutex_lock (&ximagesink->flow_lock);
642 g_mutex_lock (&ximagesink->x_lock);
646 while (XCheckWindowEvent (ximagesink->xcontext->disp,
647 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
652 case ConfigureNotify:
653 g_mutex_unlock (&ximagesink->x_lock);
654 gst_ximagesink_xwindow_update_geometry (ximagesink);
655 g_mutex_lock (&ximagesink->x_lock);
663 if (ximagesink->handle_expose && (exposed || configured)) {
664 g_mutex_unlock (&ximagesink->x_lock);
665 g_mutex_unlock (&ximagesink->flow_lock);
667 gst_ximagesink_expose (GST_VIDEO_OVERLAY (ximagesink));
669 g_mutex_lock (&ximagesink->flow_lock);
670 g_mutex_lock (&ximagesink->x_lock);
673 /* Handle Display events */
674 while (XPending (ximagesink->xcontext->disp)) {
675 XNextEvent (ximagesink->xcontext->disp, &e);
681 wm_delete = XInternAtom (ximagesink->xcontext->disp,
682 "WM_DELETE_WINDOW", False);
683 if (wm_delete == (Atom) e.xclient.data.l[0]) {
684 /* Handle window deletion by posting an error on the bus */
685 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
686 ("Output window was closed"), (NULL));
688 g_mutex_unlock (&ximagesink->x_lock);
689 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
690 ximagesink->xwindow = NULL;
691 g_mutex_lock (&ximagesink->x_lock);
700 g_mutex_unlock (&ximagesink->x_lock);
701 g_mutex_unlock (&ximagesink->flow_lock);
705 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
707 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
709 GST_OBJECT_LOCK (ximagesink);
710 while (ximagesink->running) {
711 GST_OBJECT_UNLOCK (ximagesink);
713 if (ximagesink->xwindow) {
714 gst_ximagesink_handle_xevents (ximagesink);
716 /* FIXME: do we want to align this with the framerate or anything else? */
717 g_usleep (G_USEC_PER_SEC / 20);
719 GST_OBJECT_LOCK (ximagesink);
721 GST_OBJECT_UNLOCK (ximagesink);
727 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
729 GThread *thread = NULL;
731 /* don't start the thread too early */
732 if (ximagesink->xcontext == NULL) {
736 GST_OBJECT_LOCK (ximagesink);
737 if (ximagesink->handle_expose || ximagesink->handle_events) {
738 if (!ximagesink->event_thread) {
739 /* Setup our event listening thread */
740 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
741 ximagesink->handle_expose, ximagesink->handle_events);
742 ximagesink->running = TRUE;
743 ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
744 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, NULL);
747 if (ximagesink->event_thread) {
748 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
749 ximagesink->handle_expose, ximagesink->handle_events);
750 ximagesink->running = FALSE;
751 /* grab thread and mark it as NULL */
752 thread = ximagesink->event_thread;
753 ximagesink->event_thread = NULL;
756 GST_OBJECT_UNLOCK (ximagesink);
758 /* Wait for our event thread to finish */
760 g_thread_join (thread);
765 /* This function calculates the pixel aspect ratio based on the properties
766 * in the xcontext structure and stores it there. */
768 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
770 static const gint par[][2] = {
771 {1, 1}, /* regular screen */
772 {16, 15}, /* PAL TV */
773 {11, 10}, /* 525 line Rec.601 video */
774 {54, 59}, /* 625 line Rec.601 video */
775 {64, 45}, /* 1280x1024 on 16:9 display */
776 {5, 3}, /* 1280x1024 on 4:3 display */
777 {4, 3} /* 800x600 on 16:9 display */
784 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
786 /* first calculate the "real" ratio based on the X values;
787 * which is the "physical" w/h divided by the w/h in pixels of the display */
788 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
789 / (xcontext->heightmm * xcontext->width);
791 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
793 if (xcontext->width == 720 && xcontext->height == 576) {
794 ratio = 4.0 * 576 / (3.0 * 720);
796 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
798 /* now find the one from par[][2] with the lowest delta to the real one */
802 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
803 gdouble this_delta = DELTA (i);
805 if (this_delta < delta) {
811 GST_DEBUG ("Decided on index %d (%d/%d)", index,
812 par[index][0], par[index][1]);
814 g_free (xcontext->par);
815 xcontext->par = g_new0 (GValue, 1);
816 g_value_init (xcontext->par, GST_TYPE_FRACTION);
817 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
818 GST_DEBUG ("set xcontext PAR to %d/%d",
819 gst_value_get_fraction_numerator (xcontext->par),
820 gst_value_get_fraction_denominator (xcontext->par));
823 /* This function gets the X Display and global info about it. Everything is
824 stored in our object and will be cleaned when the object is disposed. Note
825 here that caps for supported format are generated without any window or
828 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
830 GstXContext *xcontext = NULL;
831 XPixmapFormatValues *px_formats = NULL;
832 gint nb_formats = 0, i;
834 GstVideoFormat vformat;
837 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
839 xcontext = g_new0 (GstXContext, 1);
841 g_mutex_lock (&ximagesink->x_lock);
843 xcontext->disp = XOpenDisplay (ximagesink->display_name);
845 if (!xcontext->disp) {
846 g_mutex_unlock (&ximagesink->x_lock);
848 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
849 ("Could not initialise X output"), ("Could not open display"));
853 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
854 xcontext->screen_num = DefaultScreen (xcontext->disp);
855 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
856 xcontext->root = DefaultRootWindow (xcontext->disp);
857 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
858 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
859 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
861 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
862 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
863 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
864 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
866 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
867 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
869 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
871 /* We get supported pixmap formats at supported depth */
872 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
875 XCloseDisplay (xcontext->disp);
876 g_mutex_unlock (&ximagesink->x_lock);
877 g_free (xcontext->par);
879 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
880 ("Could not get supported pixmap formats"), (NULL));
884 /* We get bpp value corresponding to our running depth */
885 for (i = 0; i < nb_formats; i++) {
886 if (px_formats[i].depth == xcontext->depth)
887 xcontext->bpp = px_formats[i].bits_per_pixel;
892 endianness = (ImageByteOrder (xcontext->disp) ==
893 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
895 /* Search for XShm extension support */
897 if (XShmQueryExtension (xcontext->disp) &&
898 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
899 xcontext->use_xshm = TRUE;
900 GST_DEBUG ("ximagesink is using XShm extension");
902 #endif /* HAVE_XSHM */
904 xcontext->use_xshm = FALSE;
905 GST_DEBUG ("ximagesink is not using XShm extension");
908 /* extrapolate alpha mask */
909 alpha_mask = ~(xcontext->visual->red_mask
910 | xcontext->visual->green_mask | xcontext->visual->blue_mask);
911 alpha_mask &= 0xffffffff;
914 gst_video_format_from_masks (xcontext->depth, xcontext->bpp, endianness,
915 xcontext->visual->red_mask, xcontext->visual->green_mask,
916 xcontext->visual->blue_mask, alpha_mask);
918 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
921 /* update object's par with calculated one if not set yet */
922 if (!ximagesink->par) {
923 ximagesink->par = g_new0 (GValue, 1);
924 gst_value_init_and_copy (ximagesink->par, xcontext->par);
925 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
927 xcontext->caps = gst_caps_new_simple ("video/x-raw",
928 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
929 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
930 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
931 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
932 if (ximagesink->par) {
935 nom = gst_value_get_fraction_numerator (ximagesink->par);
936 den = gst_value_get_fraction_denominator (ximagesink->par);
937 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
938 GST_TYPE_FRACTION, nom, den, NULL);
941 g_mutex_unlock (&ximagesink->x_lock);
948 GST_ERROR_OBJECT (ximagesink, "unknown format");
953 /* This function cleans the X context. Closing the Display and unrefing the
954 caps for supported formats. */
956 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
958 GstXContext *xcontext;
960 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
962 GST_OBJECT_LOCK (ximagesink);
963 if (ximagesink->xcontext == NULL) {
964 GST_OBJECT_UNLOCK (ximagesink);
968 /* Take the xcontext reference and NULL it while we
969 * clean it up, so that any buffer-alloced buffers
970 * arriving after this will be freed correctly */
971 xcontext = ximagesink->xcontext;
972 ximagesink->xcontext = NULL;
974 GST_OBJECT_UNLOCK (ximagesink);
976 gst_caps_unref (xcontext->caps);
977 g_free (xcontext->par);
978 g_free (ximagesink->par);
979 ximagesink->par = NULL;
981 if (xcontext->last_caps)
982 gst_caps_replace (&xcontext->last_caps, NULL);
984 g_mutex_lock (&ximagesink->x_lock);
986 XCloseDisplay (xcontext->disp);
988 g_mutex_unlock (&ximagesink->x_lock);
996 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
998 GstXImageSink *ximagesink;
1002 ximagesink = GST_XIMAGESINK (bsink);
1004 g_mutex_lock (&ximagesink->x_lock);
1005 if (ximagesink->xcontext) {
1008 caps = gst_caps_ref (ximagesink->xcontext->caps);
1011 GstCaps *intersection;
1014 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1015 gst_caps_unref (caps);
1016 caps = intersection;
1019 if (gst_caps_is_empty (caps)) {
1020 g_mutex_unlock (&ximagesink->x_lock);
1024 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1025 GstStructure *s0, *s1;
1027 caps = gst_caps_make_writable (caps);
1029 /* There can only be a single structure because the xcontext
1030 * caps only have a single structure */
1031 s0 = gst_caps_get_structure (caps, 0);
1032 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1034 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1035 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1036 gst_caps_append_structure (caps, s1);
1038 /* This will not change the order but will remove the
1039 * fixed width/height caps again if not possible
1042 GstCaps *intersection;
1045 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1046 gst_caps_unref (caps);
1047 caps = intersection;
1051 g_mutex_unlock (&ximagesink->x_lock);
1054 g_mutex_unlock (&ximagesink->x_lock);
1056 /* get a template copy and add the pixel aspect ratio */
1057 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1058 if (ximagesink->par) {
1059 caps = gst_caps_make_writable (caps);
1060 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1061 GstStructure *structure = gst_caps_get_structure (caps, i);
1064 nom = gst_value_get_fraction_numerator (ximagesink->par);
1065 den = gst_value_get_fraction_denominator (ximagesink->par);
1066 gst_structure_set (structure, "pixel-aspect-ratio",
1067 GST_TYPE_FRACTION, nom, den, NULL);
1072 GstCaps *intersection;
1075 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1076 gst_caps_unref (caps);
1077 caps = intersection;
1084 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1086 GstXImageSink *ximagesink;
1087 GstStructure *structure;
1089 GstBufferPool *newpool, *oldpool;
1092 static GstAllocationParams params = { 0, 15, 0, 0, };
1094 ximagesink = GST_XIMAGESINK (bsink);
1096 if (!ximagesink->xcontext)
1099 GST_DEBUG_OBJECT (ximagesink,
1100 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1101 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1103 /* We intersect those caps with our template to make sure they are correct */
1104 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1105 goto incompatible_caps;
1107 if (!gst_video_info_from_caps (&info, caps))
1108 goto invalid_format;
1112 structure = gst_caps_get_structure (caps, 0);
1113 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1114 * otherwise linking should fail */
1115 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1117 if (ximagesink->par) {
1118 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1121 } else if (ximagesink->xcontext->par) {
1122 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1128 GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1129 GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1130 ximagesink->fps_n = info.fps_n;
1131 ximagesink->fps_d = info.fps_d;
1133 /* Notify application to set xwindow id now */
1134 g_mutex_lock (&ximagesink->flow_lock);
1135 if (!ximagesink->xwindow) {
1136 g_mutex_unlock (&ximagesink->flow_lock);
1137 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1139 g_mutex_unlock (&ximagesink->flow_lock);
1142 /* Creating our window and our image */
1143 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1144 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1147 g_mutex_lock (&ximagesink->flow_lock);
1148 if (!ximagesink->xwindow) {
1149 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1150 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1153 ximagesink->info = info;
1155 /* Remember to draw borders for next frame */
1156 ximagesink->draw_border = TRUE;
1158 /* create a new pool for the new configuration */
1159 newpool = gst_ximage_buffer_pool_new (ximagesink);
1161 structure = gst_buffer_pool_get_config (newpool);
1162 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1163 gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms);
1164 if (!gst_buffer_pool_set_config (newpool, structure))
1167 oldpool = ximagesink->pool;
1168 /* we don't activate the pool yet, this will be done by downstream after it
1169 * has configured the pool. If downstream does not want our pool we will
1170 * activate it when we render into it */
1171 ximagesink->pool = newpool;
1172 g_mutex_unlock (&ximagesink->flow_lock);
1174 /* unref the old sink */
1176 /* we don't deactivate, some elements might still be using it, it will be
1177 * deactivated when the last ref is gone */
1178 gst_object_unref (oldpool);
1186 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1191 GST_ERROR_OBJECT (ximagesink, "caps invalid");
1196 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1201 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1202 ("Invalid image size."));
1207 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1208 g_mutex_unlock (&ximagesink->flow_lock);
1213 static GstStateChangeReturn
1214 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1216 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1217 GstXImageSink *ximagesink;
1218 GstXContext *xcontext = NULL;
1220 ximagesink = GST_XIMAGESINK (element);
1222 switch (transition) {
1223 case GST_STATE_CHANGE_NULL_TO_READY:
1224 /* Initializing the XContext */
1225 if (ximagesink->xcontext == NULL) {
1226 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1227 if (xcontext == NULL) {
1228 ret = GST_STATE_CHANGE_FAILURE;
1231 GST_OBJECT_LOCK (ximagesink);
1233 ximagesink->xcontext = xcontext;
1234 GST_OBJECT_UNLOCK (ximagesink);
1237 /* call XSynchronize with the current value of synchronous */
1238 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1239 ximagesink->synchronous ? "TRUE" : "FALSE");
1240 g_mutex_lock (&ximagesink->x_lock);
1241 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1242 g_mutex_unlock (&ximagesink->x_lock);
1243 gst_ximagesink_manage_event_thread (ximagesink);
1245 case GST_STATE_CHANGE_READY_TO_PAUSED:
1246 g_mutex_lock (&ximagesink->flow_lock);
1247 if (ximagesink->xwindow)
1248 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1249 g_mutex_unlock (&ximagesink->flow_lock);
1251 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1257 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1259 switch (transition) {
1260 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1262 case GST_STATE_CHANGE_PAUSED_TO_READY:
1263 ximagesink->fps_n = 0;
1264 ximagesink->fps_d = 1;
1265 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1266 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1267 g_mutex_lock (&ximagesink->flow_lock);
1268 if (ximagesink->pool)
1269 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1270 g_mutex_unlock (&ximagesink->flow_lock);
1272 case GST_STATE_CHANGE_READY_TO_NULL:
1273 gst_ximagesink_reset (ximagesink);
1284 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1285 GstClockTime * start, GstClockTime * end)
1287 GstXImageSink *ximagesink;
1289 ximagesink = GST_XIMAGESINK (bsink);
1291 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1292 *start = GST_BUFFER_TIMESTAMP (buf);
1293 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1294 *end = *start + GST_BUFFER_DURATION (buf);
1296 if (ximagesink->fps_n > 0) {
1298 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1305 static GstFlowReturn
1306 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1309 GstXImageSink *ximagesink;
1310 GstXImageMemory *mem;
1311 GstBuffer *to_put = NULL;
1313 ximagesink = GST_XIMAGESINK (vsink);
1315 if (gst_buffer_n_memory (buf) == 1
1316 && (mem = (GstXImageMemory *) gst_buffer_peek_memory (buf, 0))
1317 && g_strcmp0 (mem->parent.allocator->mem_type, "ximage") == 0
1318 && mem->sink == ximagesink) {
1319 /* If this buffer has been allocated using our buffer management we simply
1320 put the ximage which is in the PRIVATE pointer */
1321 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1325 GstVideoFrame src, dest;
1326 GstBufferPoolAcquireParams params = { 0, };
1328 /* Else we have to copy the data into our private image, */
1329 /* if we have one... */
1330 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1332 /* we should have a pool, configured in setcaps */
1333 if (ximagesink->pool == NULL)
1336 if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1337 goto activate_failed;
1339 /* take a buffer from our pool, if there is no buffer in the pool something
1340 * is seriously wrong, waiting for the pool here might deadlock when we try
1341 * to go to PAUSED because we never flush the pool. */
1342 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1343 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, ¶ms);
1344 if (res != GST_FLOW_OK)
1347 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1348 "slow copy into bufferpool buffer %p", to_put);
1350 if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1351 goto invalid_buffer;
1353 if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1354 gst_video_frame_unmap (&src);
1355 goto invalid_buffer;
1358 gst_video_frame_copy (&dest, &src);
1360 gst_video_frame_unmap (&dest);
1361 gst_video_frame_unmap (&src);
1364 if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1369 gst_buffer_unref (to_put);
1376 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1377 ("Internal error: can't allocate images"),
1378 ("We don't have a bufferpool negotiated"));
1379 return GST_FLOW_ERROR;
1383 /* No image available. That's very bad ! */
1384 GST_WARNING_OBJECT (ximagesink, "could not create image");
1389 /* No Window available to put our image into */
1390 GST_WARNING_OBJECT (ximagesink, "could not map image");
1396 /* No Window available to put our image into */
1397 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1398 res = GST_FLOW_ERROR;
1403 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1404 res = GST_FLOW_ERROR;
1410 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1412 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1414 switch (GST_EVENT_TYPE (event)) {
1415 case GST_EVENT_TAG:{
1417 gchar *title = NULL;
1419 gst_event_parse_tag (event, &l);
1420 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1423 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1424 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1434 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1438 gst_ximagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1440 GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1441 GstBufferPool *pool;
1442 GstStructure *config;
1447 gst_query_parse_allocation (query, &caps, &need_pool);
1452 g_mutex_lock (&ximagesink->flow_lock);
1453 if ((pool = ximagesink->pool))
1454 gst_object_ref (pool);
1455 g_mutex_unlock (&ximagesink->flow_lock);
1460 /* we had a pool, check caps */
1461 config = gst_buffer_pool_get_config (pool);
1462 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1464 GST_DEBUG_OBJECT (ximagesink,
1465 "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1466 if (!gst_caps_is_equal (caps, pcaps)) {
1467 /* different caps, we can't use this pool */
1468 GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1469 gst_object_unref (pool);
1472 gst_structure_free (config);
1474 if (pool == NULL && need_pool) {
1477 if (!gst_video_info_from_caps (&info, caps))
1480 GST_DEBUG_OBJECT (ximagesink, "create new pool");
1481 pool = gst_ximage_buffer_pool_new (ximagesink);
1483 /* the normal size of a frame */
1486 config = gst_buffer_pool_get_config (pool);
1487 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1488 if (!gst_buffer_pool_set_config (pool, config))
1492 /* we need at least 2 buffer because we hold on to the last one */
1493 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1494 gst_object_unref (pool);
1497 /* we also support various metadata */
1498 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1499 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1506 GST_DEBUG_OBJECT (bsink, "no caps specified");
1511 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1516 GST_DEBUG_OBJECT (bsink, "failed setting config");
1517 gst_object_unref (pool);
1522 /* Interfaces stuff */
1524 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1525 GstStructure * structure)
1527 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1529 gint x_offset, y_offset;
1533 event = gst_event_new_navigation (structure);
1535 /* We are not converting the pointer coordinates as there's no hardware
1536 scaling done here. The only possible scaling is done by videoscale and
1537 videoscale will have to catch those events and tranform the coordinates
1538 to match the applied scaling. So here we just add the offset if the image
1539 is centered in the window. */
1541 /* We take the flow_lock while we look at the window */
1542 g_mutex_lock (&ximagesink->flow_lock);
1544 if (!ximagesink->xwindow) {
1545 g_mutex_unlock (&ximagesink->flow_lock);
1549 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1550 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1552 g_mutex_unlock (&ximagesink->flow_lock);
1554 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1556 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1558 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1560 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1563 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1565 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1566 gst_pad_send_event (pad, event);
1568 gst_object_unref (pad);
1573 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1575 iface->send_event = gst_ximagesink_navigation_send_event;
1579 gst_ximagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1581 XID xwindow_id = id;
1582 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1583 GstXWindow *xwindow = NULL;
1584 XWindowAttributes attr;
1586 /* We acquire the stream lock while setting this window in the element.
1587 We are basically cleaning tons of stuff replacing the old window, putting
1588 images while we do that would surely crash */
1589 g_mutex_lock (&ximagesink->flow_lock);
1591 /* If we already use that window return */
1592 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1593 g_mutex_unlock (&ximagesink->flow_lock);
1597 /* If the element has not initialized the X11 context try to do so */
1598 if (!ximagesink->xcontext &&
1599 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1600 g_mutex_unlock (&ximagesink->flow_lock);
1601 /* we have thrown a GST_ELEMENT_ERROR now */
1605 /* If a window is there already we destroy it */
1606 if (ximagesink->xwindow) {
1607 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1608 ximagesink->xwindow = NULL;
1611 /* If the xid is 0 we go back to an internal window */
1612 if (xwindow_id == 0) {
1613 /* If no width/height caps nego did not happen window will be created
1614 during caps nego then */
1615 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1616 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1617 GST_VIDEO_SINK_WIDTH (ximagesink),
1618 GST_VIDEO_SINK_HEIGHT (ximagesink));
1621 xwindow = g_new0 (GstXWindow, 1);
1623 xwindow->win = xwindow_id;
1625 /* We get window geometry, set the event we want to receive,
1627 g_mutex_lock (&ximagesink->x_lock);
1628 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1629 xwindow->width = attr.width;
1630 xwindow->height = attr.height;
1631 xwindow->internal = FALSE;
1632 if (ximagesink->handle_events) {
1633 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1634 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1638 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1639 g_mutex_unlock (&ximagesink->x_lock);
1643 ximagesink->xwindow = xwindow;
1645 g_mutex_unlock (&ximagesink->flow_lock);
1649 gst_ximagesink_expose (GstVideoOverlay * overlay)
1651 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1653 gst_ximagesink_xwindow_update_geometry (ximagesink);
1654 gst_ximagesink_ximage_put (ximagesink, NULL);
1658 gst_ximagesink_set_event_handling (GstVideoOverlay * overlay,
1659 gboolean handle_events)
1661 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1663 ximagesink->handle_events = handle_events;
1665 g_mutex_lock (&ximagesink->flow_lock);
1667 if (G_UNLIKELY (!ximagesink->xwindow)) {
1668 g_mutex_unlock (&ximagesink->flow_lock);
1672 g_mutex_lock (&ximagesink->x_lock);
1674 if (handle_events) {
1675 if (ximagesink->xwindow->internal) {
1676 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1677 ExposureMask | StructureNotifyMask | PointerMotionMask |
1678 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1680 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1681 ExposureMask | StructureNotifyMask | PointerMotionMask |
1682 KeyPressMask | KeyReleaseMask);
1685 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1688 g_mutex_unlock (&ximagesink->x_lock);
1690 g_mutex_unlock (&ximagesink->flow_lock);
1694 gst_ximagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1696 iface->set_window_handle = gst_ximagesink_set_window_handle;
1697 iface->expose = gst_ximagesink_expose;
1698 iface->handle_events = gst_ximagesink_set_event_handling;
1701 /* =========================================== */
1703 /* Init & Class init */
1705 /* =========================================== */
1708 gst_ximagesink_set_property (GObject * object, guint prop_id,
1709 const GValue * value, GParamSpec * pspec)
1711 GstXImageSink *ximagesink;
1713 g_return_if_fail (GST_IS_XIMAGESINK (object));
1715 ximagesink = GST_XIMAGESINK (object);
1719 ximagesink->display_name = g_strdup (g_value_get_string (value));
1721 case PROP_SYNCHRONOUS:
1722 ximagesink->synchronous = g_value_get_boolean (value);
1723 if (ximagesink->xcontext) {
1724 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1725 ximagesink->synchronous ? "TRUE" : "FALSE");
1726 g_mutex_lock (&ximagesink->x_lock);
1727 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1728 g_mutex_unlock (&ximagesink->x_lock);
1731 case PROP_FORCE_ASPECT_RATIO:
1732 ximagesink->keep_aspect = g_value_get_boolean (value);
1734 case PROP_PIXEL_ASPECT_RATIO:
1738 tmp = g_new0 (GValue, 1);
1739 g_value_init (tmp, GST_TYPE_FRACTION);
1741 if (!g_value_transform (value, tmp)) {
1742 GST_WARNING_OBJECT (ximagesink,
1743 "Could not transform string to aspect ratio");
1746 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1747 gst_value_get_fraction_numerator (tmp),
1748 gst_value_get_fraction_denominator (tmp));
1749 g_free (ximagesink->par);
1750 ximagesink->par = tmp;
1754 case PROP_HANDLE_EVENTS:
1755 gst_ximagesink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1756 g_value_get_boolean (value));
1757 gst_ximagesink_manage_event_thread (ximagesink);
1759 case PROP_HANDLE_EXPOSE:
1760 ximagesink->handle_expose = g_value_get_boolean (value);
1761 gst_ximagesink_manage_event_thread (ximagesink);
1764 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1770 gst_ximagesink_get_property (GObject * object, guint prop_id,
1771 GValue * value, GParamSpec * pspec)
1773 GstXImageSink *ximagesink;
1775 g_return_if_fail (GST_IS_XIMAGESINK (object));
1777 ximagesink = GST_XIMAGESINK (object);
1781 g_value_set_string (value, ximagesink->display_name);
1783 case PROP_SYNCHRONOUS:
1784 g_value_set_boolean (value, ximagesink->synchronous);
1786 case PROP_FORCE_ASPECT_RATIO:
1787 g_value_set_boolean (value, ximagesink->keep_aspect);
1789 case PROP_PIXEL_ASPECT_RATIO:
1790 if (ximagesink->par)
1791 g_value_transform (ximagesink->par, value);
1793 case PROP_HANDLE_EVENTS:
1794 g_value_set_boolean (value, ximagesink->handle_events);
1796 case PROP_HANDLE_EXPOSE:
1797 g_value_set_boolean (value, ximagesink->handle_expose);
1799 case PROP_WINDOW_WIDTH:
1800 if (ximagesink->xwindow)
1801 g_value_set_uint64 (value, ximagesink->xwindow->width);
1803 g_value_set_uint64 (value, 0);
1805 case PROP_WINDOW_HEIGHT:
1806 if (ximagesink->xwindow)
1807 g_value_set_uint64 (value, ximagesink->xwindow->height);
1809 g_value_set_uint64 (value, 0);
1812 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1818 gst_ximagesink_reset (GstXImageSink * ximagesink)
1822 GST_OBJECT_LOCK (ximagesink);
1823 ximagesink->running = FALSE;
1824 /* grab thread and mark it as NULL */
1825 thread = ximagesink->event_thread;
1826 ximagesink->event_thread = NULL;
1827 GST_OBJECT_UNLOCK (ximagesink);
1829 /* Wait for our event thread to finish before we clean up our stuff. */
1831 g_thread_join (thread);
1833 if (ximagesink->cur_image) {
1834 gst_buffer_unref (ximagesink->cur_image);
1835 ximagesink->cur_image = NULL;
1838 g_mutex_lock (&ximagesink->flow_lock);
1840 if (ximagesink->pool) {
1841 gst_object_unref (ximagesink->pool);
1842 ximagesink->pool = NULL;
1845 if (ximagesink->xwindow) {
1846 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1847 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1848 ximagesink->xwindow = NULL;
1850 g_mutex_unlock (&ximagesink->flow_lock);
1852 gst_ximagesink_xcontext_clear (ximagesink);
1856 gst_ximagesink_finalize (GObject * object)
1858 GstXImageSink *ximagesink;
1860 ximagesink = GST_XIMAGESINK (object);
1862 gst_ximagesink_reset (ximagesink);
1864 if (ximagesink->display_name) {
1865 g_free (ximagesink->display_name);
1866 ximagesink->display_name = NULL;
1868 if (ximagesink->par) {
1869 g_free (ximagesink->par);
1870 ximagesink->par = NULL;
1872 g_mutex_clear (&ximagesink->x_lock);
1873 g_mutex_clear (&ximagesink->flow_lock);
1875 g_free (ximagesink->media_title);
1877 G_OBJECT_CLASS (parent_class)->finalize (object);
1881 gst_ximagesink_init (GstXImageSink * ximagesink)
1883 ximagesink->display_name = NULL;
1884 ximagesink->xcontext = NULL;
1885 ximagesink->xwindow = NULL;
1886 ximagesink->cur_image = NULL;
1888 ximagesink->event_thread = NULL;
1889 ximagesink->running = FALSE;
1891 ximagesink->fps_n = 0;
1892 ximagesink->fps_d = 1;
1894 g_mutex_init (&ximagesink->x_lock);
1895 g_mutex_init (&ximagesink->flow_lock);
1897 ximagesink->par = NULL;
1899 ximagesink->pool = NULL;
1901 ximagesink->synchronous = FALSE;
1902 ximagesink->keep_aspect = TRUE;
1903 ximagesink->handle_events = TRUE;
1904 ximagesink->handle_expose = TRUE;
1908 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1910 GObjectClass *gobject_class;
1911 GstElementClass *gstelement_class;
1912 GstBaseSinkClass *gstbasesink_class;
1913 GstVideoSinkClass *videosink_class;
1915 gobject_class = (GObjectClass *) klass;
1916 gstelement_class = (GstElementClass *) klass;
1917 gstbasesink_class = (GstBaseSinkClass *) klass;
1918 videosink_class = (GstVideoSinkClass *) klass;
1920 gobject_class->finalize = gst_ximagesink_finalize;
1921 gobject_class->set_property = gst_ximagesink_set_property;
1922 gobject_class->get_property = gst_ximagesink_get_property;
1924 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1925 g_param_spec_string ("display", "Display", "X Display name",
1926 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1927 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1928 g_param_spec_boolean ("synchronous", "Synchronous",
1929 "When enabled, runs the X display in synchronous mode. "
1930 "(unrelated to A/V sync, used only for debugging)", FALSE,
1931 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1933 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1934 "When enabled, reverse caps negotiation (scaling) will respect "
1935 "original aspect ratio", TRUE,
1936 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1937 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1938 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1939 "The pixel aspect ratio of the device", "1/1",
1940 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1941 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1942 g_param_spec_boolean ("handle-events", "Handle XEvents",
1943 "When enabled, XEvents will be selected and handled", TRUE,
1944 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1945 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1946 g_param_spec_boolean ("handle-expose", "Handle expose",
1948 "the current frame will always be drawn in response to X Expose "
1949 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1952 * GstXImageSink:window-width
1954 * Actual width of the video window.
1958 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1959 g_param_spec_uint64 ("window-width", "window-width",
1960 "Width of the window", 0, G_MAXUINT64, 0,
1961 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1964 * GstXImageSink:window-height
1966 * Actual height of the video window.
1970 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1971 g_param_spec_uint64 ("window-height", "window-height",
1972 "Height of the window", 0, G_MAXUINT64, 0,
1973 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1975 gst_element_class_set_static_metadata (gstelement_class,
1976 "Video sink", "Sink/Video",
1977 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1979 gst_element_class_add_pad_template (gstelement_class,
1980 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1982 gstelement_class->change_state = gst_ximagesink_change_state;
1984 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1985 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1986 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1987 gstbasesink_class->propose_allocation =
1988 GST_DEBUG_FUNCPTR (gst_ximagesink_propose_allocation);
1989 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1991 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);