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-1.0 -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-1.0 -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-1.0 -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 = { 0, };
234 GstVideoRectangle dst = { 0, };
235 GstVideoRectangle result;
236 gboolean draw_border = FALSE;
238 /* We take the flow_lock. If expose is in there we don't want to run
239 concurrently from the data flow thread */
240 g_mutex_lock (&ximagesink->flow_lock);
242 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
243 g_mutex_unlock (&ximagesink->flow_lock);
247 /* Draw borders when displaying the first frame. After this
248 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
249 if (!ximagesink->cur_image || ximagesink->draw_border) {
253 /* Store a reference to the last image we put, lose the previous one */
254 if (ximage && ximagesink->cur_image != ximage) {
255 if (ximagesink->cur_image) {
256 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
257 gst_buffer_unref (ximagesink->cur_image);
259 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
260 ximagesink->cur_image = gst_buffer_ref (ximage);
263 /* Expose sends a NULL image, we take the latest frame */
266 if (ximagesink->cur_image) {
267 ximage = ximagesink->cur_image;
269 g_mutex_unlock (&ximagesink->flow_lock);
274 mem = (GstXImageMemory *) gst_buffer_peek_memory (ximage, 0);
275 crop = gst_buffer_get_video_crop_meta (ximage);
278 src.x = crop->x + mem->x;
279 src.y = crop->y + mem->y;
281 src.h = crop->height;
282 GST_LOG_OBJECT (ximagesink,
283 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
290 dst.w = ximagesink->xwindow->width;
291 dst.h = ximagesink->xwindow->height;
293 gst_video_sink_center_rect (src, dst, &result, FALSE);
295 g_mutex_lock (&ximagesink->x_lock);
298 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
300 ximagesink->draw_border = FALSE;
303 if (ximagesink->xcontext->use_xshm) {
304 GST_LOG_OBJECT (ximagesink,
305 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
306 ximage, 0, 0, result.x, result.y, result.w, result.h,
307 ximagesink->xwindow->width, ximagesink->xwindow->height);
308 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
309 ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
310 result.w, result.h, FALSE);
312 #endif /* HAVE_XSHM */
314 GST_LOG_OBJECT (ximagesink,
315 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
316 ximage, 0, 0, result.x, result.y, result.w, result.h,
317 ximagesink->xwindow->width, ximagesink->xwindow->height);
318 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
319 ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
323 XSync (ximagesink->xcontext->disp, FALSE);
325 g_mutex_unlock (&ximagesink->x_lock);
327 g_mutex_unlock (&ximagesink->flow_lock);
333 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
336 Atom hints_atom = None;
339 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
340 g_return_val_if_fail (window != NULL, FALSE);
342 g_mutex_lock (&ximagesink->x_lock);
344 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
346 if (hints_atom == None) {
347 g_mutex_unlock (&ximagesink->x_lock);
351 hints = g_malloc0 (sizeof (MotifWmHints));
353 hints->flags |= MWM_HINTS_DECORATIONS;
354 hints->decorations = 1 << 0;
356 XChangeProperty (ximagesink->xcontext->disp, window->win,
357 hints_atom, hints_atom, 32, PropModeReplace,
358 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
360 XSync (ximagesink->xcontext->disp, FALSE);
362 g_mutex_unlock (&ximagesink->x_lock);
370 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
371 GstXWindow * xwindow, const gchar * media_title)
374 g_free (ximagesink->media_title);
375 ximagesink->media_title = g_strdup (media_title);
378 /* we have a window */
379 if (xwindow->internal) {
380 XTextProperty xproperty;
381 XClassHint *hint = XAllocClassHint ();
382 const gchar *app_name;
383 const gchar *title = NULL;
384 gchar *title_mem = NULL;
386 /* set application name as a title */
387 app_name = g_get_application_name ();
389 if (app_name && ximagesink->media_title) {
390 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
392 } else if (app_name) {
394 } else if (ximagesink->media_title) {
395 title = ximagesink->media_title;
399 if ((XStringListToTextProperty (((char **) &title), 1,
401 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
402 XFree (xproperty.value);
409 hint->res_name = (char *) app_name;
410 hint->res_class = (char *) "GStreamer";
411 XSetClassHint (ximagesink->xcontext->disp, xwindow->win, hint);
418 /* This function handles a GstXWindow creation */
420 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
422 GstXWindow *xwindow = NULL;
425 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
427 xwindow = g_new0 (GstXWindow, 1);
429 xwindow->width = width;
430 xwindow->height = height;
431 xwindow->internal = TRUE;
433 g_mutex_lock (&ximagesink->x_lock);
435 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
436 ximagesink->xcontext->root,
437 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
439 /* We have to do that to prevent X from redrawing the background on
440 ConfigureNotify. This takes away flickering of video when resizing. */
441 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
443 /* set application name as a title */
444 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
446 if (ximagesink->handle_events) {
449 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
450 StructureNotifyMask | PointerMotionMask | KeyPressMask |
451 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
453 /* Tell the window manager we'd like delete client messages instead of
455 wm_delete = XInternAtom (ximagesink->xcontext->disp,
456 "WM_DELETE_WINDOW", False);
457 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
461 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
464 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
466 XSync (ximagesink->xcontext->disp, FALSE);
468 g_mutex_unlock (&ximagesink->x_lock);
470 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
472 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
478 /* This function destroys a GstXWindow */
480 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
481 GstXWindow * xwindow)
483 g_return_if_fail (xwindow != NULL);
484 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
486 g_mutex_lock (&ximagesink->x_lock);
488 /* If we did not create that window we just free the GC and let it live */
489 if (xwindow->internal)
490 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
492 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
494 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
496 XSync (ximagesink->xcontext->disp, FALSE);
498 g_mutex_unlock (&ximagesink->x_lock);
504 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
506 XWindowAttributes attr;
507 gboolean reconfigure;
509 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
511 /* Update the window geometry */
512 g_mutex_lock (&ximagesink->x_lock);
513 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
514 g_mutex_unlock (&ximagesink->x_lock);
518 XGetWindowAttributes (ximagesink->xcontext->disp,
519 ximagesink->xwindow->win, &attr);
521 /* Check if we would suggest a different width/height now */
522 reconfigure = (ximagesink->xwindow->width != attr.width)
523 || (ximagesink->xwindow->height != attr.height);
524 ximagesink->xwindow->width = attr.width;
525 ximagesink->xwindow->height = attr.height;
527 g_mutex_unlock (&ximagesink->x_lock);
530 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
531 gst_event_new_reconfigure ());
535 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
537 g_return_if_fail (xwindow != NULL);
538 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
540 g_mutex_lock (&ximagesink->x_lock);
542 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
543 ximagesink->xcontext->black);
545 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
546 0, 0, xwindow->width, xwindow->height);
548 XSync (ximagesink->xcontext->disp, FALSE);
550 g_mutex_unlock (&ximagesink->x_lock);
553 /* This function handles XEvents that might be in the queue. It generates
554 GstEvent that will be sent upstream in the pipeline to handle interactivity
557 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
560 guint pointer_x = 0, pointer_y = 0;
561 gboolean pointer_moved = FALSE;
562 gboolean exposed = FALSE, configured = FALSE;
564 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
566 /* Then we get all pointer motion events, only the last position is
568 g_mutex_lock (&ximagesink->flow_lock);
569 g_mutex_lock (&ximagesink->x_lock);
570 while (XCheckWindowEvent (ximagesink->xcontext->disp,
571 ximagesink->xwindow->win, PointerMotionMask, &e)) {
572 g_mutex_unlock (&ximagesink->x_lock);
573 g_mutex_unlock (&ximagesink->flow_lock);
577 pointer_x = e.xmotion.x;
578 pointer_y = e.xmotion.y;
579 pointer_moved = TRUE;
584 g_mutex_lock (&ximagesink->flow_lock);
585 g_mutex_lock (&ximagesink->x_lock);
589 g_mutex_unlock (&ximagesink->x_lock);
590 g_mutex_unlock (&ximagesink->flow_lock);
592 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
593 pointer_x, pointer_y);
594 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
595 "mouse-move", 0, pointer_x, pointer_y);
597 g_mutex_lock (&ximagesink->flow_lock);
598 g_mutex_lock (&ximagesink->x_lock);
601 /* We get all remaining events on our window to throw them upstream */
602 while (XCheckWindowEvent (ximagesink->xcontext->disp,
603 ximagesink->xwindow->win,
604 KeyPressMask | KeyReleaseMask |
605 ButtonPressMask | ButtonReleaseMask, &e)) {
607 const char *key_str = NULL;
609 /* We lock only for the X function call */
610 g_mutex_unlock (&ximagesink->x_lock);
611 g_mutex_unlock (&ximagesink->flow_lock);
615 /* Mouse button pressed/released over our window. We send upstream
616 events for interactivity/navigation */
617 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
618 e.xbutton.button, e.xbutton.x, e.xbutton.x);
619 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
620 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
623 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
624 e.xbutton.button, e.xbutton.x, e.xbutton.x);
625 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
626 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
630 /* Key pressed/released over our window. We send upstream
631 events for interactivity/navigation */
632 g_mutex_lock (&ximagesink->x_lock);
633 keysym = XkbKeycodeToKeysym (ximagesink->xcontext->disp,
634 e.xkey.keycode, 0, 0);
635 if (keysym != NoSymbol) {
636 key_str = XKeysymToString (keysym);
640 g_mutex_unlock (&ximagesink->x_lock);
641 GST_DEBUG_OBJECT (ximagesink,
642 "key %d pressed over window at %d,%d (%s)",
643 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
644 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
645 e.type == KeyPress ? "key-press" : "key-release", key_str);
648 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
651 g_mutex_lock (&ximagesink->flow_lock);
652 g_mutex_lock (&ximagesink->x_lock);
656 while (XCheckWindowEvent (ximagesink->xcontext->disp,
657 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
662 case ConfigureNotify:
663 g_mutex_unlock (&ximagesink->x_lock);
664 gst_ximagesink_xwindow_update_geometry (ximagesink);
665 g_mutex_lock (&ximagesink->x_lock);
673 if (ximagesink->handle_expose && (exposed || configured)) {
674 g_mutex_unlock (&ximagesink->x_lock);
675 g_mutex_unlock (&ximagesink->flow_lock);
677 gst_ximagesink_expose (GST_VIDEO_OVERLAY (ximagesink));
679 g_mutex_lock (&ximagesink->flow_lock);
680 g_mutex_lock (&ximagesink->x_lock);
683 /* Handle Display events */
684 while (XPending (ximagesink->xcontext->disp)) {
685 XNextEvent (ximagesink->xcontext->disp, &e);
691 wm_delete = XInternAtom (ximagesink->xcontext->disp,
692 "WM_DELETE_WINDOW", False);
693 if (wm_delete == (Atom) e.xclient.data.l[0]) {
694 /* Handle window deletion by posting an error on the bus */
695 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
696 ("Output window was closed"), (NULL));
698 g_mutex_unlock (&ximagesink->x_lock);
699 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
700 ximagesink->xwindow = NULL;
701 g_mutex_lock (&ximagesink->x_lock);
710 g_mutex_unlock (&ximagesink->x_lock);
711 g_mutex_unlock (&ximagesink->flow_lock);
715 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
717 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
719 GST_OBJECT_LOCK (ximagesink);
720 while (ximagesink->running) {
721 GST_OBJECT_UNLOCK (ximagesink);
723 if (ximagesink->xwindow) {
724 gst_ximagesink_handle_xevents (ximagesink);
726 /* FIXME: do we want to align this with the framerate or anything else? */
727 g_usleep (G_USEC_PER_SEC / 20);
729 GST_OBJECT_LOCK (ximagesink);
731 GST_OBJECT_UNLOCK (ximagesink);
737 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
739 GThread *thread = NULL;
741 /* don't start the thread too early */
742 if (ximagesink->xcontext == NULL) {
746 GST_OBJECT_LOCK (ximagesink);
747 if (ximagesink->handle_expose || ximagesink->handle_events) {
748 if (!ximagesink->event_thread) {
749 /* Setup our event listening thread */
750 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
751 ximagesink->handle_expose, ximagesink->handle_events);
752 ximagesink->running = TRUE;
753 ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
754 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, NULL);
757 if (ximagesink->event_thread) {
758 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
759 ximagesink->handle_expose, ximagesink->handle_events);
760 ximagesink->running = FALSE;
761 /* grab thread and mark it as NULL */
762 thread = ximagesink->event_thread;
763 ximagesink->event_thread = NULL;
766 GST_OBJECT_UNLOCK (ximagesink);
768 /* Wait for our event thread to finish */
770 g_thread_join (thread);
775 /* This function calculates the pixel aspect ratio based on the properties
776 * in the xcontext structure and stores it there. */
778 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
780 static const gint par[][2] = {
781 {1, 1}, /* regular screen */
782 {16, 15}, /* PAL TV */
783 {11, 10}, /* 525 line Rec.601 video */
784 {54, 59}, /* 625 line Rec.601 video */
785 {64, 45}, /* 1280x1024 on 16:9 display */
786 {5, 3}, /* 1280x1024 on 4:3 display */
787 {4, 3} /* 800x600 on 16:9 display */
794 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
796 /* first calculate the "real" ratio based on the X values;
797 * which is the "physical" w/h divided by the w/h in pixels of the display */
798 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
799 / (xcontext->heightmm * xcontext->width);
801 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
803 if (xcontext->width == 720 && xcontext->height == 576) {
804 ratio = 4.0 * 576 / (3.0 * 720);
806 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
808 /* now find the one from par[][2] with the lowest delta to the real one */
812 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
813 gdouble this_delta = DELTA (i);
815 if (this_delta < delta) {
821 GST_DEBUG ("Decided on index %d (%d/%d)", index,
822 par[index][0], par[index][1]);
824 g_free (xcontext->par);
825 xcontext->par = g_new0 (GValue, 1);
826 g_value_init (xcontext->par, GST_TYPE_FRACTION);
827 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
828 GST_DEBUG ("set xcontext PAR to %d/%d",
829 gst_value_get_fraction_numerator (xcontext->par),
830 gst_value_get_fraction_denominator (xcontext->par));
833 /* This function gets the X Display and global info about it. Everything is
834 stored in our object and will be cleaned when the object is disposed. Note
835 here that caps for supported format are generated without any window or
838 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
840 GstXContext *xcontext = NULL;
841 XPixmapFormatValues *px_formats = NULL;
842 gint nb_formats = 0, i;
844 GstVideoFormat vformat;
847 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
849 xcontext = g_new0 (GstXContext, 1);
851 g_mutex_lock (&ximagesink->x_lock);
853 xcontext->disp = XOpenDisplay (ximagesink->display_name);
855 if (!xcontext->disp) {
856 g_mutex_unlock (&ximagesink->x_lock);
858 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
859 ("Could not initialise X output"), ("Could not open display"));
863 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
864 xcontext->screen_num = DefaultScreen (xcontext->disp);
865 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
866 xcontext->root = DefaultRootWindow (xcontext->disp);
867 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
868 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
869 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
871 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
872 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
873 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
874 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
876 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
877 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
879 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
881 /* We get supported pixmap formats at supported depth */
882 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
885 XCloseDisplay (xcontext->disp);
886 g_mutex_unlock (&ximagesink->x_lock);
887 g_free (xcontext->par);
889 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
890 ("Could not get supported pixmap formats"), (NULL));
894 /* We get bpp value corresponding to our running depth */
895 for (i = 0; i < nb_formats; i++) {
896 if (px_formats[i].depth == xcontext->depth)
897 xcontext->bpp = px_formats[i].bits_per_pixel;
902 endianness = (ImageByteOrder (xcontext->disp) ==
903 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
905 /* Search for XShm extension support */
907 if (XShmQueryExtension (xcontext->disp) &&
908 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
909 xcontext->use_xshm = TRUE;
910 GST_DEBUG ("ximagesink is using XShm extension");
912 #endif /* HAVE_XSHM */
914 xcontext->use_xshm = FALSE;
915 GST_DEBUG ("ximagesink is not using XShm extension");
918 /* extrapolate alpha mask */
919 if (xcontext->depth == 32) {
920 alpha_mask = ~(xcontext->visual->red_mask
921 | xcontext->visual->green_mask | xcontext->visual->blue_mask);
927 gst_video_format_from_masks (xcontext->depth, xcontext->bpp, endianness,
928 xcontext->visual->red_mask, xcontext->visual->green_mask,
929 xcontext->visual->blue_mask, alpha_mask);
931 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
934 /* update object's par with calculated one if not set yet */
935 if (!ximagesink->par) {
936 ximagesink->par = g_new0 (GValue, 1);
937 gst_value_init_and_copy (ximagesink->par, xcontext->par);
938 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
940 xcontext->caps = gst_caps_new_simple ("video/x-raw",
941 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
942 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
943 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
944 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
945 if (ximagesink->par) {
948 nom = gst_value_get_fraction_numerator (ximagesink->par);
949 den = gst_value_get_fraction_denominator (ximagesink->par);
950 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
951 GST_TYPE_FRACTION, nom, den, NULL);
954 g_mutex_unlock (&ximagesink->x_lock);
961 GST_ERROR_OBJECT (ximagesink, "unknown format");
966 /* This function cleans the X context. Closing the Display and unrefing the
967 caps for supported formats. */
969 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
971 GstXContext *xcontext;
973 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
975 GST_OBJECT_LOCK (ximagesink);
976 if (ximagesink->xcontext == NULL) {
977 GST_OBJECT_UNLOCK (ximagesink);
981 /* Take the xcontext reference and NULL it while we
982 * clean it up, so that any buffer-alloced buffers
983 * arriving after this will be freed correctly */
984 xcontext = ximagesink->xcontext;
985 ximagesink->xcontext = NULL;
987 GST_OBJECT_UNLOCK (ximagesink);
989 gst_caps_unref (xcontext->caps);
990 g_free (xcontext->par);
991 g_free (ximagesink->par);
992 ximagesink->par = NULL;
994 if (xcontext->last_caps)
995 gst_caps_replace (&xcontext->last_caps, NULL);
997 g_mutex_lock (&ximagesink->x_lock);
999 XCloseDisplay (xcontext->disp);
1001 g_mutex_unlock (&ximagesink->x_lock);
1009 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1011 GstXImageSink *ximagesink;
1015 ximagesink = GST_XIMAGESINK (bsink);
1017 g_mutex_lock (&ximagesink->x_lock);
1018 if (ximagesink->xcontext) {
1021 caps = gst_caps_ref (ximagesink->xcontext->caps);
1024 GstCaps *intersection;
1027 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1028 gst_caps_unref (caps);
1029 caps = intersection;
1032 if (gst_caps_is_empty (caps)) {
1033 g_mutex_unlock (&ximagesink->x_lock);
1037 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1038 GstStructure *s0, *s1;
1040 caps = gst_caps_make_writable (caps);
1042 /* There can only be a single structure because the xcontext
1043 * caps only have a single structure */
1044 s0 = gst_caps_get_structure (caps, 0);
1045 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1047 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1048 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1049 gst_caps_append_structure (caps, s1);
1051 /* This will not change the order but will remove the
1052 * fixed width/height caps again if not possible
1055 GstCaps *intersection;
1058 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1059 gst_caps_unref (caps);
1060 caps = intersection;
1064 g_mutex_unlock (&ximagesink->x_lock);
1067 g_mutex_unlock (&ximagesink->x_lock);
1069 /* get a template copy and add the pixel aspect ratio */
1070 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1071 if (ximagesink->par) {
1072 caps = gst_caps_make_writable (caps);
1073 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1074 GstStructure *structure = gst_caps_get_structure (caps, i);
1077 nom = gst_value_get_fraction_numerator (ximagesink->par);
1078 den = gst_value_get_fraction_denominator (ximagesink->par);
1079 gst_structure_set (structure, "pixel-aspect-ratio",
1080 GST_TYPE_FRACTION, nom, den, NULL);
1085 GstCaps *intersection;
1088 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1089 gst_caps_unref (caps);
1090 caps = intersection;
1097 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1099 GstXImageSink *ximagesink;
1100 GstStructure *structure;
1102 GstBufferPool *newpool, *oldpool;
1105 static GstAllocationParams params = { 0, 15, 0, 0, };
1107 ximagesink = GST_XIMAGESINK (bsink);
1109 if (!ximagesink->xcontext)
1112 GST_DEBUG_OBJECT (ximagesink,
1113 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1114 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1116 /* We intersect those caps with our template to make sure they are correct */
1117 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1118 goto incompatible_caps;
1120 if (!gst_video_info_from_caps (&info, caps))
1121 goto invalid_format;
1125 structure = gst_caps_get_structure (caps, 0);
1126 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1127 * otherwise linking should fail */
1128 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1130 if (ximagesink->par) {
1131 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1134 } else if (ximagesink->xcontext->par) {
1135 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1141 GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1142 GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1143 ximagesink->fps_n = info.fps_n;
1144 ximagesink->fps_d = info.fps_d;
1146 /* Notify application to set xwindow id now */
1147 g_mutex_lock (&ximagesink->flow_lock);
1148 if (!ximagesink->xwindow) {
1149 g_mutex_unlock (&ximagesink->flow_lock);
1150 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1152 g_mutex_unlock (&ximagesink->flow_lock);
1155 /* Creating our window and our image */
1156 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1157 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1160 g_mutex_lock (&ximagesink->flow_lock);
1161 if (!ximagesink->xwindow) {
1162 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1163 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1166 ximagesink->info = info;
1168 /* Remember to draw borders for next frame */
1169 ximagesink->draw_border = TRUE;
1171 /* create a new pool for the new configuration */
1172 newpool = gst_ximage_buffer_pool_new (ximagesink);
1174 structure = gst_buffer_pool_get_config (newpool);
1175 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1176 gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms);
1177 if (!gst_buffer_pool_set_config (newpool, structure))
1180 oldpool = ximagesink->pool;
1181 /* we don't activate the pool yet, this will be done by downstream after it
1182 * has configured the pool. If downstream does not want our pool we will
1183 * activate it when we render into it */
1184 ximagesink->pool = newpool;
1185 g_mutex_unlock (&ximagesink->flow_lock);
1187 /* unref the old sink */
1189 /* we don't deactivate, some elements might still be using it, it will be
1190 * deactivated when the last ref is gone */
1191 gst_object_unref (oldpool);
1199 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1204 GST_ERROR_OBJECT (ximagesink, "caps invalid");
1209 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1214 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1215 ("Invalid image size."));
1220 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1221 g_mutex_unlock (&ximagesink->flow_lock);
1226 static GstStateChangeReturn
1227 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1229 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1230 GstXImageSink *ximagesink;
1231 GstXContext *xcontext = NULL;
1233 ximagesink = GST_XIMAGESINK (element);
1235 switch (transition) {
1236 case GST_STATE_CHANGE_NULL_TO_READY:
1237 /* Initializing the XContext */
1238 if (ximagesink->xcontext == NULL) {
1239 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1240 if (xcontext == NULL) {
1241 ret = GST_STATE_CHANGE_FAILURE;
1244 GST_OBJECT_LOCK (ximagesink);
1246 ximagesink->xcontext = xcontext;
1247 GST_OBJECT_UNLOCK (ximagesink);
1250 /* call XSynchronize with the current value of synchronous */
1251 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1252 ximagesink->synchronous ? "TRUE" : "FALSE");
1253 g_mutex_lock (&ximagesink->x_lock);
1254 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1255 g_mutex_unlock (&ximagesink->x_lock);
1256 gst_ximagesink_manage_event_thread (ximagesink);
1258 case GST_STATE_CHANGE_READY_TO_PAUSED:
1259 g_mutex_lock (&ximagesink->flow_lock);
1260 if (ximagesink->xwindow)
1261 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1262 g_mutex_unlock (&ximagesink->flow_lock);
1264 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1270 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1272 switch (transition) {
1273 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1275 case GST_STATE_CHANGE_PAUSED_TO_READY:
1276 ximagesink->fps_n = 0;
1277 ximagesink->fps_d = 1;
1278 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1279 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1280 g_mutex_lock (&ximagesink->flow_lock);
1281 if (ximagesink->pool)
1282 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1283 g_mutex_unlock (&ximagesink->flow_lock);
1285 case GST_STATE_CHANGE_READY_TO_NULL:
1286 gst_ximagesink_reset (ximagesink);
1297 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1298 GstClockTime * start, GstClockTime * end)
1300 GstXImageSink *ximagesink;
1302 ximagesink = GST_XIMAGESINK (bsink);
1304 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1305 *start = GST_BUFFER_TIMESTAMP (buf);
1306 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1307 *end = *start + GST_BUFFER_DURATION (buf);
1309 if (ximagesink->fps_n > 0) {
1311 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1318 static GstFlowReturn
1319 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1322 GstXImageSink *ximagesink;
1323 GstXImageMemory *mem;
1324 GstBuffer *to_put = NULL;
1326 ximagesink = GST_XIMAGESINK (vsink);
1328 if (gst_buffer_n_memory (buf) == 1
1329 && (mem = (GstXImageMemory *) gst_buffer_peek_memory (buf, 0))
1330 && g_strcmp0 (mem->parent.allocator->mem_type, "ximage") == 0
1331 && mem->sink == ximagesink) {
1332 /* If this buffer has been allocated using our buffer management we simply
1333 put the ximage which is in the PRIVATE pointer */
1334 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1338 GstVideoFrame src, dest;
1339 GstBufferPoolAcquireParams params = { 0, };
1341 /* Else we have to copy the data into our private image, */
1342 /* if we have one... */
1343 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1345 /* we should have a pool, configured in setcaps */
1346 if (ximagesink->pool == NULL)
1349 if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1350 goto activate_failed;
1352 /* take a buffer from our pool, if there is no buffer in the pool something
1353 * is seriously wrong, waiting for the pool here might deadlock when we try
1354 * to go to PAUSED because we never flush the pool. */
1355 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1356 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, ¶ms);
1357 if (res != GST_FLOW_OK)
1360 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, ximagesink,
1361 "slow copy into bufferpool buffer %p", to_put);
1363 if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1364 goto invalid_buffer;
1366 if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1367 gst_video_frame_unmap (&src);
1368 goto invalid_buffer;
1371 gst_video_frame_copy (&dest, &src);
1373 gst_video_frame_unmap (&dest);
1374 gst_video_frame_unmap (&src);
1377 if (!gst_ximagesink_ximage_put (ximagesink, to_put))
1382 gst_buffer_unref (to_put);
1389 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1390 ("Internal error: can't allocate images"),
1391 ("We don't have a bufferpool negotiated"));
1392 return GST_FLOW_ERROR;
1396 /* No image available. That's very bad ! */
1397 GST_WARNING_OBJECT (ximagesink, "could not create image");
1402 /* No Window available to put our image into */
1403 GST_WARNING_OBJECT (ximagesink, "could not map image");
1409 /* No Window available to put our image into */
1410 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1411 res = GST_FLOW_ERROR;
1416 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1417 res = GST_FLOW_ERROR;
1423 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1425 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1427 switch (GST_EVENT_TYPE (event)) {
1428 case GST_EVENT_TAG:{
1430 gchar *title = NULL;
1432 gst_event_parse_tag (event, &l);
1433 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1436 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1437 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1447 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1451 gst_ximagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1453 GstXImageSink *ximagesink = GST_XIMAGESINK (bsink);
1454 GstBufferPool *pool;
1455 GstStructure *config;
1460 gst_query_parse_allocation (query, &caps, &need_pool);
1465 g_mutex_lock (&ximagesink->flow_lock);
1466 if ((pool = ximagesink->pool))
1467 gst_object_ref (pool);
1468 g_mutex_unlock (&ximagesink->flow_lock);
1473 /* we had a pool, check caps */
1474 config = gst_buffer_pool_get_config (pool);
1475 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1477 GST_DEBUG_OBJECT (ximagesink,
1478 "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1479 if (!gst_caps_is_equal (caps, pcaps)) {
1480 /* different caps, we can't use this pool */
1481 GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1482 gst_object_unref (pool);
1485 gst_structure_free (config);
1487 if (pool == NULL && need_pool) {
1490 if (!gst_video_info_from_caps (&info, caps))
1493 GST_DEBUG_OBJECT (ximagesink, "create new pool");
1494 pool = gst_ximage_buffer_pool_new (ximagesink);
1496 /* the normal size of a frame */
1499 config = gst_buffer_pool_get_config (pool);
1500 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1501 if (!gst_buffer_pool_set_config (pool, config))
1505 /* we need at least 2 buffer because we hold on to the last one */
1506 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1507 gst_object_unref (pool);
1510 /* we also support various metadata */
1511 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1512 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1519 GST_DEBUG_OBJECT (bsink, "no caps specified");
1524 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1529 GST_DEBUG_OBJECT (bsink, "failed setting config");
1530 gst_object_unref (pool);
1535 /* Interfaces stuff */
1537 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1538 GstStructure * structure)
1540 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1542 gint x_offset, y_offset;
1546 event = gst_event_new_navigation (structure);
1548 /* We are not converting the pointer coordinates as there's no hardware
1549 scaling done here. The only possible scaling is done by videoscale and
1550 videoscale will have to catch those events and tranform the coordinates
1551 to match the applied scaling. So here we just add the offset if the image
1552 is centered in the window. */
1554 /* We take the flow_lock while we look at the window */
1555 g_mutex_lock (&ximagesink->flow_lock);
1557 if (!ximagesink->xwindow) {
1558 g_mutex_unlock (&ximagesink->flow_lock);
1562 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1563 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1565 g_mutex_unlock (&ximagesink->flow_lock);
1567 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1569 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1571 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1573 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1576 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1578 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1579 gst_pad_send_event (pad, event);
1581 gst_object_unref (pad);
1586 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1588 iface->send_event = gst_ximagesink_navigation_send_event;
1592 gst_ximagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1594 XID xwindow_id = id;
1595 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1596 GstXWindow *xwindow = NULL;
1597 XWindowAttributes attr;
1599 /* We acquire the stream lock while setting this window in the element.
1600 We are basically cleaning tons of stuff replacing the old window, putting
1601 images while we do that would surely crash */
1602 g_mutex_lock (&ximagesink->flow_lock);
1604 /* If we already use that window return */
1605 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1606 g_mutex_unlock (&ximagesink->flow_lock);
1610 /* If the element has not initialized the X11 context try to do so */
1611 if (!ximagesink->xcontext &&
1612 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1613 g_mutex_unlock (&ximagesink->flow_lock);
1614 /* we have thrown a GST_ELEMENT_ERROR now */
1618 /* If a window is there already we destroy it */
1619 if (ximagesink->xwindow) {
1620 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1621 ximagesink->xwindow = NULL;
1624 /* If the xid is 0 we go back to an internal window */
1625 if (xwindow_id == 0) {
1626 /* If no width/height caps nego did not happen window will be created
1627 during caps nego then */
1628 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1629 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1630 GST_VIDEO_SINK_WIDTH (ximagesink),
1631 GST_VIDEO_SINK_HEIGHT (ximagesink));
1634 xwindow = g_new0 (GstXWindow, 1);
1636 xwindow->win = xwindow_id;
1638 /* We get window geometry, set the event we want to receive,
1640 g_mutex_lock (&ximagesink->x_lock);
1641 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1642 xwindow->width = attr.width;
1643 xwindow->height = attr.height;
1644 xwindow->internal = FALSE;
1645 if (ximagesink->handle_events) {
1646 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1647 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1651 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1652 g_mutex_unlock (&ximagesink->x_lock);
1656 ximagesink->xwindow = xwindow;
1658 g_mutex_unlock (&ximagesink->flow_lock);
1662 gst_ximagesink_expose (GstVideoOverlay * overlay)
1664 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1666 gst_ximagesink_xwindow_update_geometry (ximagesink);
1667 gst_ximagesink_ximage_put (ximagesink, NULL);
1671 gst_ximagesink_set_event_handling (GstVideoOverlay * overlay,
1672 gboolean handle_events)
1674 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1676 ximagesink->handle_events = handle_events;
1678 g_mutex_lock (&ximagesink->flow_lock);
1680 if (G_UNLIKELY (!ximagesink->xwindow)) {
1681 g_mutex_unlock (&ximagesink->flow_lock);
1685 g_mutex_lock (&ximagesink->x_lock);
1687 if (handle_events) {
1688 if (ximagesink->xwindow->internal) {
1689 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1690 ExposureMask | StructureNotifyMask | PointerMotionMask |
1691 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1693 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1694 ExposureMask | StructureNotifyMask | PointerMotionMask |
1695 KeyPressMask | KeyReleaseMask);
1698 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1701 g_mutex_unlock (&ximagesink->x_lock);
1703 g_mutex_unlock (&ximagesink->flow_lock);
1707 gst_ximagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1709 iface->set_window_handle = gst_ximagesink_set_window_handle;
1710 iface->expose = gst_ximagesink_expose;
1711 iface->handle_events = gst_ximagesink_set_event_handling;
1714 /* =========================================== */
1716 /* Init & Class init */
1718 /* =========================================== */
1721 gst_ximagesink_set_property (GObject * object, guint prop_id,
1722 const GValue * value, GParamSpec * pspec)
1724 GstXImageSink *ximagesink;
1726 g_return_if_fail (GST_IS_XIMAGESINK (object));
1728 ximagesink = GST_XIMAGESINK (object);
1732 ximagesink->display_name = g_strdup (g_value_get_string (value));
1734 case PROP_SYNCHRONOUS:
1735 ximagesink->synchronous = g_value_get_boolean (value);
1736 if (ximagesink->xcontext) {
1737 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1738 ximagesink->synchronous ? "TRUE" : "FALSE");
1739 g_mutex_lock (&ximagesink->x_lock);
1740 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1741 g_mutex_unlock (&ximagesink->x_lock);
1744 case PROP_FORCE_ASPECT_RATIO:
1745 ximagesink->keep_aspect = g_value_get_boolean (value);
1747 case PROP_PIXEL_ASPECT_RATIO:
1751 tmp = g_new0 (GValue, 1);
1752 g_value_init (tmp, GST_TYPE_FRACTION);
1754 if (!g_value_transform (value, tmp)) {
1755 GST_WARNING_OBJECT (ximagesink,
1756 "Could not transform string to aspect ratio");
1759 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1760 gst_value_get_fraction_numerator (tmp),
1761 gst_value_get_fraction_denominator (tmp));
1762 g_free (ximagesink->par);
1763 ximagesink->par = tmp;
1767 case PROP_HANDLE_EVENTS:
1768 gst_ximagesink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1769 g_value_get_boolean (value));
1770 gst_ximagesink_manage_event_thread (ximagesink);
1772 case PROP_HANDLE_EXPOSE:
1773 ximagesink->handle_expose = g_value_get_boolean (value);
1774 gst_ximagesink_manage_event_thread (ximagesink);
1777 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1783 gst_ximagesink_get_property (GObject * object, guint prop_id,
1784 GValue * value, GParamSpec * pspec)
1786 GstXImageSink *ximagesink;
1788 g_return_if_fail (GST_IS_XIMAGESINK (object));
1790 ximagesink = GST_XIMAGESINK (object);
1794 g_value_set_string (value, ximagesink->display_name);
1796 case PROP_SYNCHRONOUS:
1797 g_value_set_boolean (value, ximagesink->synchronous);
1799 case PROP_FORCE_ASPECT_RATIO:
1800 g_value_set_boolean (value, ximagesink->keep_aspect);
1802 case PROP_PIXEL_ASPECT_RATIO:
1803 if (ximagesink->par)
1804 g_value_transform (ximagesink->par, value);
1806 case PROP_HANDLE_EVENTS:
1807 g_value_set_boolean (value, ximagesink->handle_events);
1809 case PROP_HANDLE_EXPOSE:
1810 g_value_set_boolean (value, ximagesink->handle_expose);
1812 case PROP_WINDOW_WIDTH:
1813 if (ximagesink->xwindow)
1814 g_value_set_uint64 (value, ximagesink->xwindow->width);
1816 g_value_set_uint64 (value, 0);
1818 case PROP_WINDOW_HEIGHT:
1819 if (ximagesink->xwindow)
1820 g_value_set_uint64 (value, ximagesink->xwindow->height);
1822 g_value_set_uint64 (value, 0);
1825 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1831 gst_ximagesink_reset (GstXImageSink * ximagesink)
1835 GST_OBJECT_LOCK (ximagesink);
1836 ximagesink->running = FALSE;
1837 /* grab thread and mark it as NULL */
1838 thread = ximagesink->event_thread;
1839 ximagesink->event_thread = NULL;
1840 GST_OBJECT_UNLOCK (ximagesink);
1842 /* Wait for our event thread to finish before we clean up our stuff. */
1844 g_thread_join (thread);
1846 if (ximagesink->cur_image) {
1847 gst_buffer_unref (ximagesink->cur_image);
1848 ximagesink->cur_image = NULL;
1851 g_mutex_lock (&ximagesink->flow_lock);
1853 if (ximagesink->pool) {
1854 gst_object_unref (ximagesink->pool);
1855 ximagesink->pool = NULL;
1858 if (ximagesink->xwindow) {
1859 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1860 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1861 ximagesink->xwindow = NULL;
1863 g_mutex_unlock (&ximagesink->flow_lock);
1865 gst_ximagesink_xcontext_clear (ximagesink);
1869 gst_ximagesink_finalize (GObject * object)
1871 GstXImageSink *ximagesink;
1873 ximagesink = GST_XIMAGESINK (object);
1875 gst_ximagesink_reset (ximagesink);
1877 if (ximagesink->display_name) {
1878 g_free (ximagesink->display_name);
1879 ximagesink->display_name = NULL;
1881 if (ximagesink->par) {
1882 g_free (ximagesink->par);
1883 ximagesink->par = NULL;
1885 g_mutex_clear (&ximagesink->x_lock);
1886 g_mutex_clear (&ximagesink->flow_lock);
1888 g_free (ximagesink->media_title);
1890 G_OBJECT_CLASS (parent_class)->finalize (object);
1894 gst_ximagesink_init (GstXImageSink * ximagesink)
1896 ximagesink->display_name = NULL;
1897 ximagesink->xcontext = NULL;
1898 ximagesink->xwindow = NULL;
1899 ximagesink->cur_image = NULL;
1901 ximagesink->event_thread = NULL;
1902 ximagesink->running = FALSE;
1904 ximagesink->fps_n = 0;
1905 ximagesink->fps_d = 1;
1907 g_mutex_init (&ximagesink->x_lock);
1908 g_mutex_init (&ximagesink->flow_lock);
1910 ximagesink->par = NULL;
1912 ximagesink->pool = NULL;
1914 ximagesink->synchronous = FALSE;
1915 ximagesink->keep_aspect = TRUE;
1916 ximagesink->handle_events = TRUE;
1917 ximagesink->handle_expose = TRUE;
1921 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1923 GObjectClass *gobject_class;
1924 GstElementClass *gstelement_class;
1925 GstBaseSinkClass *gstbasesink_class;
1926 GstVideoSinkClass *videosink_class;
1928 gobject_class = (GObjectClass *) klass;
1929 gstelement_class = (GstElementClass *) klass;
1930 gstbasesink_class = (GstBaseSinkClass *) klass;
1931 videosink_class = (GstVideoSinkClass *) klass;
1933 gobject_class->finalize = gst_ximagesink_finalize;
1934 gobject_class->set_property = gst_ximagesink_set_property;
1935 gobject_class->get_property = gst_ximagesink_get_property;
1937 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1938 g_param_spec_string ("display", "Display", "X Display name",
1939 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1940 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1941 g_param_spec_boolean ("synchronous", "Synchronous",
1942 "When enabled, runs the X display in synchronous mode. "
1943 "(unrelated to A/V sync, used only for debugging)", FALSE,
1944 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1945 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1946 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1947 "When enabled, reverse caps negotiation (scaling) will respect "
1948 "original aspect ratio", TRUE,
1949 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1950 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1951 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1952 "The pixel aspect ratio of the device", "1/1",
1953 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1954 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1955 g_param_spec_boolean ("handle-events", "Handle XEvents",
1956 "When enabled, XEvents will be selected and handled", TRUE,
1957 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1958 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1959 g_param_spec_boolean ("handle-expose", "Handle expose",
1961 "the current frame will always be drawn in response to X Expose "
1962 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1965 * GstXImageSink:window-width
1967 * Actual width of the video window.
1969 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1970 g_param_spec_uint64 ("window-width", "window-width",
1971 "Width of the window", 0, G_MAXUINT64, 0,
1972 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1975 * GstXImageSink:window-height
1977 * Actual height of the video window.
1979 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1980 g_param_spec_uint64 ("window-height", "window-height",
1981 "Height of the window", 0, G_MAXUINT64, 0,
1982 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1984 gst_element_class_set_static_metadata (gstelement_class,
1985 "Video sink", "Sink/Video",
1986 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1988 gst_element_class_add_pad_template (gstelement_class,
1989 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1991 gstelement_class->change_state = gst_ximagesink_change_state;
1993 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1994 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1995 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1996 gstbasesink_class->propose_allocation =
1997 GST_DEBUG_FUNCPTR (gst_ximagesink_propose_allocation);
1998 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
2000 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);