2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-ximagesink
23 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24 * display. This element can receive a Window ID from the application through
25 * the XOverlay interface and will then render video frames in this drawable.
26 * If no Window ID was provided by the application, the element will create its
27 * own internal window and render into it.
30 * <title>Scaling</title>
32 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33 * reverse caps negotiation to try to get scaled video frames for the drawable.
34 * This is accomplished by asking the peer pad if it accepts some different caps
35 * which in most cases implies that there is a scaling element in the pipeline,
36 * or that an element generating the video frames can generate them with a
37 * different geometry. This mechanism is handled during buffer allocations, for
38 * each allocation request the video sink will check the drawable geometry, look
39 * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40 * desired video frames and then check that the peer pad accept those new caps.
41 * If it does it will then allocate a buffer in video memory with this new
42 * geometry and return it with the new caps.
46 * <title>Events</title>
48 * XImageSink creates a thread to handle events coming from the drawable. There
49 * are several kind of events that can be grouped in 2 big categories: input
50 * events and window state related events. Input events will be translated to
51 * navigation events and pushed upstream for other elements to react on them.
52 * This includes events such as pointer moves, key press/release, clicks etc...
53 * Other events are used to handle the drawable appearance even when the data
54 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55 * paused, it will receive expose events from the drawable and draw the latest
56 * frame with correct borders/aspect-ratio.
60 * <title>Pixel aspect ratio</title>
62 * When changing state to GST_STATE_READY, XImageSink will open a connection to
63 * the display specified in the #GstXImageSink:display property or the default
64 * display if nothing specified. Once this connection is open it will inspect
65 * the display configuration including the physical display geometry and
66 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67 * video sink will set the calculated pixel aspect ratio on the caps to make
68 * sure that incoming video frames will have the correct pixel aspect ratio for
69 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70 * then possible to enforce a specific pixel aspect ratio using the
71 * #GstXImageSink:pixel-aspect-ratio property.
75 * <title>Examples</title>
77 * gst-launch -v videotestsrc ! queue ! ximagesink
78 * ]| A pipeline to test reverse negotiation. When the test video signal appears
79 * you can resize the window and see that scaled buffers of the desired size are
80 * going to arrive with a short delay. This illustrates how buffers of desired
81 * size are allocated along the way. If you take away the queue, scaling will
82 * happen almost immediately.
84 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! 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-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94 * videotestsrc, in most cases the pixel aspect ratio of the display will be
95 * 1/1. This means that videoscale will have to do the scaling to convert
96 * incoming frames to a size that will match the display pixel aspect ratio
97 * (from 320x240 to 320x180 in this case). Note that you might have to escape
98 * some characters for your shell like '\(fraction\)'.
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
110 #include <gst/video/gstmetavideo.h>
113 #include "ximagesink.h"
115 /* Debugging category */
116 #include <gst/gstinfo.h>
118 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
119 #define GST_CAT_DEFAULT gst_debug_ximagesink
124 unsigned long functions;
125 unsigned long decorations;
127 unsigned long status;
129 MotifWmHints, MwmHints;
131 #define MWM_HINTS_DECORATIONS (1L << 1)
133 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
134 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
135 static void gst_ximagesink_expose (GstXOverlay * overlay);
137 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
138 GST_STATIC_PAD_TEMPLATE ("sink",
141 GST_STATIC_CAPS ("video/x-raw-rgb, "
142 "framerate = (fraction) [ 0, MAX ], "
143 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
151 PROP_PIXEL_ASPECT_RATIO,
152 PROP_FORCE_ASPECT_RATIO,
159 /* ============================================================= */
163 /* ============================================================= */
165 /* =========================================== */
167 /* Object typing & Creation */
169 /* =========================================== */
170 static void gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass);
171 static void gst_ximagesink_navigation_init (GstNavigationInterface * klass);
172 static void gst_ximagesink_xoverlay_init (GstXOverlayClass * klass);
173 #define gst_ximagesink_parent_class parent_class
174 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
175 G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
176 gst_ximagesink_interface_init);
177 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
178 G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_ximagesink_xoverlay_init));
180 /* ============================================================= */
182 /* Private Methods */
184 /* ============================================================= */
188 /* We are called with the x_lock taken */
190 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
191 GstXWindow * xwindow, GstVideoRectangle rect)
193 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
194 g_return_if_fail (xwindow != NULL);
196 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
197 ximagesink->xcontext->black);
201 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
202 0, 0, rect.x, xwindow->height);
206 if ((rect.x + rect.w) < xwindow->width) {
207 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
208 rect.x + rect.w, 0, xwindow->width, xwindow->height);
213 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
214 0, 0, xwindow->width, rect.y);
218 if ((rect.y + rect.h) < xwindow->height) {
219 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
220 0, rect.y + rect.h, xwindow->width, xwindow->height);
224 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
226 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
229 GstVideoRectangle src, dst, result;
230 gboolean draw_border = FALSE;
232 /* We take the flow_lock. If expose is in there we don't want to run
233 concurrently from the data flow thread */
234 g_mutex_lock (ximagesink->flow_lock);
236 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
237 g_mutex_unlock (ximagesink->flow_lock);
241 /* Draw borders when displaying the first frame. After this
242 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
243 if (!ximagesink->cur_image || ximagesink->draw_border) {
247 /* Store a reference to the last image we put, lose the previous one */
248 if (ximage && ximagesink->cur_image != ximage) {
249 if (ximagesink->cur_image) {
250 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
251 gst_buffer_unref (ximagesink->cur_image);
253 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
254 ximagesink->cur_image = gst_buffer_ref (ximage);
257 /* Expose sends a NULL image, we take the latest frame */
260 if (ximagesink->cur_image) {
261 ximage = ximagesink->cur_image;
263 g_mutex_unlock (ximagesink->flow_lock);
268 meta = gst_buffer_get_meta_ximage (ximage);
271 src.h = meta->height;
272 dst.w = ximagesink->xwindow->width;
273 dst.h = ximagesink->xwindow->height;
275 gst_video_sink_center_rect (src, dst, &result, FALSE);
277 g_mutex_lock (ximagesink->x_lock);
280 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
282 ximagesink->draw_border = FALSE;
285 if (ximagesink->xcontext->use_xshm) {
286 GST_LOG_OBJECT (ximagesink,
287 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
288 ximage, 0, 0, result.x, result.y, result.w, result.h,
289 ximagesink->xwindow->width, ximagesink->xwindow->height);
290 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
291 ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
292 result.w, result.h, FALSE);
294 #endif /* HAVE_XSHM */
296 GST_LOG_OBJECT (ximagesink,
297 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
298 ximage, 0, 0, result.x, result.y, result.w, result.h,
299 ximagesink->xwindow->width, ximagesink->xwindow->height);
300 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
301 ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
305 XSync (ximagesink->xcontext->disp, FALSE);
307 g_mutex_unlock (ximagesink->x_lock);
309 g_mutex_unlock (ximagesink->flow_lock);
315 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
318 Atom hints_atom = None;
321 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
322 g_return_val_if_fail (window != NULL, FALSE);
324 g_mutex_lock (ximagesink->x_lock);
326 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
328 if (hints_atom == None) {
329 g_mutex_unlock (ximagesink->x_lock);
333 hints = g_malloc0 (sizeof (MotifWmHints));
335 hints->flags |= MWM_HINTS_DECORATIONS;
336 hints->decorations = 1 << 0;
338 XChangeProperty (ximagesink->xcontext->disp, window->win,
339 hints_atom, hints_atom, 32, PropModeReplace,
340 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
342 XSync (ximagesink->xcontext->disp, FALSE);
344 g_mutex_unlock (ximagesink->x_lock);
352 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
353 GstXWindow * xwindow, const gchar * media_title)
356 g_free (ximagesink->media_title);
357 ximagesink->media_title = g_strdup (media_title);
360 /* we have a window */
361 if (xwindow->internal) {
362 XTextProperty xproperty;
363 const gchar *app_name;
364 const gchar *title = NULL;
365 gchar *title_mem = NULL;
367 /* set application name as a title */
368 app_name = g_get_application_name ();
370 if (app_name && ximagesink->media_title) {
371 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
373 } else if (app_name) {
375 } else if (ximagesink->media_title) {
376 title = ximagesink->media_title;
380 if ((XStringListToTextProperty (((char **) &title), 1,
382 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
383 XFree (xproperty.value);
392 /* This function handles a GstXWindow creation */
394 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
396 GstXWindow *xwindow = NULL;
399 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
401 xwindow = g_new0 (GstXWindow, 1);
403 xwindow->width = width;
404 xwindow->height = height;
405 xwindow->internal = TRUE;
407 g_mutex_lock (ximagesink->x_lock);
409 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
410 ximagesink->xcontext->root,
411 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
413 /* We have to do that to prevent X from redrawing the background on
414 ConfigureNotify. This takes away flickering of video when resizing. */
415 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
417 /* set application name as a title */
418 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
420 if (ximagesink->handle_events) {
423 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
424 StructureNotifyMask | PointerMotionMask | KeyPressMask |
425 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
427 /* Tell the window manager we'd like delete client messages instead of
429 wm_delete = XInternAtom (ximagesink->xcontext->disp,
430 "WM_DELETE_WINDOW", False);
431 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
435 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
438 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
440 XSync (ximagesink->xcontext->disp, FALSE);
442 g_mutex_unlock (ximagesink->x_lock);
444 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
446 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
451 /* This function destroys a GstXWindow */
453 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
454 GstXWindow * xwindow)
456 g_return_if_fail (xwindow != NULL);
457 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
459 g_mutex_lock (ximagesink->x_lock);
461 /* If we did not create that window we just free the GC and let it live */
462 if (xwindow->internal)
463 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
465 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
467 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
469 XSync (ximagesink->xcontext->disp, FALSE);
471 g_mutex_unlock (ximagesink->x_lock);
477 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
479 XWindowAttributes attr;
480 gboolean reconfigure;
482 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
484 /* Update the window geometry */
485 g_mutex_lock (ximagesink->x_lock);
486 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
487 g_mutex_unlock (ximagesink->x_lock);
491 XGetWindowAttributes (ximagesink->xcontext->disp,
492 ximagesink->xwindow->win, &attr);
494 /* Check if we would suggest a different width/height now */
495 reconfigure = (ximagesink->xwindow->width != attr.width)
496 || (ximagesink->xwindow->height != attr.height);
497 ximagesink->xwindow->width = attr.width;
498 ximagesink->xwindow->height = attr.height;
500 g_mutex_unlock (ximagesink->x_lock);
503 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
504 gst_event_new_reconfigure ());
508 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
510 g_return_if_fail (xwindow != NULL);
511 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
513 g_mutex_lock (ximagesink->x_lock);
515 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
516 ximagesink->xcontext->black);
518 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
519 0, 0, xwindow->width, xwindow->height);
521 XSync (ximagesink->xcontext->disp, FALSE);
523 g_mutex_unlock (ximagesink->x_lock);
526 /* This function handles XEvents that might be in the queue. It generates
527 GstEvent that will be sent upstream in the pipeline to handle interactivity
530 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
533 guint pointer_x = 0, pointer_y = 0;
534 gboolean pointer_moved = FALSE;
535 gboolean exposed = FALSE, configured = FALSE;
537 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
539 /* Then we get all pointer motion events, only the last position is
541 g_mutex_lock (ximagesink->flow_lock);
542 g_mutex_lock (ximagesink->x_lock);
543 while (XCheckWindowEvent (ximagesink->xcontext->disp,
544 ximagesink->xwindow->win, PointerMotionMask, &e)) {
545 g_mutex_unlock (ximagesink->x_lock);
546 g_mutex_unlock (ximagesink->flow_lock);
550 pointer_x = e.xmotion.x;
551 pointer_y = e.xmotion.y;
552 pointer_moved = TRUE;
557 g_mutex_lock (ximagesink->flow_lock);
558 g_mutex_lock (ximagesink->x_lock);
562 g_mutex_unlock (ximagesink->x_lock);
563 g_mutex_unlock (ximagesink->flow_lock);
565 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
566 pointer_x, pointer_y);
567 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
568 "mouse-move", 0, pointer_x, pointer_y);
570 g_mutex_lock (ximagesink->flow_lock);
571 g_mutex_lock (ximagesink->x_lock);
574 /* We get all remaining events on our window to throw them upstream */
575 while (XCheckWindowEvent (ximagesink->xcontext->disp,
576 ximagesink->xwindow->win,
577 KeyPressMask | KeyReleaseMask |
578 ButtonPressMask | ButtonReleaseMask, &e)) {
581 /* We lock only for the X function call */
582 g_mutex_unlock (ximagesink->x_lock);
583 g_mutex_unlock (ximagesink->flow_lock);
587 /* Mouse button pressed/released over our window. We send upstream
588 events for interactivity/navigation */
589 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
590 e.xbutton.button, e.xbutton.x, e.xbutton.x);
591 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
592 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
595 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
596 e.xbutton.button, e.xbutton.x, e.xbutton.x);
597 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
598 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
602 /* Key pressed/released over our window. We send upstream
603 events for interactivity/navigation */
604 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
605 e.xkey.keycode, e.xkey.x, e.xkey.x);
606 g_mutex_lock (ximagesink->x_lock);
607 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
609 g_mutex_unlock (ximagesink->x_lock);
610 if (keysym != NoSymbol) {
611 char *key_str = NULL;
613 g_mutex_lock (ximagesink->x_lock);
614 key_str = XKeysymToString (keysym);
615 g_mutex_unlock (ximagesink->x_lock);
616 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
617 e.type == KeyPress ? "key-press" : "key-release", key_str);
619 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
620 e.type == KeyPress ? "key-press" : "key-release", "unknown");
624 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
627 g_mutex_lock (ximagesink->flow_lock);
628 g_mutex_lock (ximagesink->x_lock);
632 while (XCheckWindowEvent (ximagesink->xcontext->disp,
633 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
638 case ConfigureNotify:
639 g_mutex_unlock (ximagesink->x_lock);
640 gst_ximagesink_xwindow_update_geometry (ximagesink);
641 g_mutex_lock (ximagesink->x_lock);
649 if (ximagesink->handle_expose && (exposed || configured)) {
650 g_mutex_unlock (ximagesink->x_lock);
651 g_mutex_unlock (ximagesink->flow_lock);
653 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
655 g_mutex_lock (ximagesink->flow_lock);
656 g_mutex_lock (ximagesink->x_lock);
659 /* Handle Display events */
660 while (XPending (ximagesink->xcontext->disp)) {
661 XNextEvent (ximagesink->xcontext->disp, &e);
667 wm_delete = XInternAtom (ximagesink->xcontext->disp,
668 "WM_DELETE_WINDOW", False);
669 if (wm_delete == (Atom) e.xclient.data.l[0]) {
670 /* Handle window deletion by posting an error on the bus */
671 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
672 ("Output window was closed"), (NULL));
674 g_mutex_unlock (ximagesink->x_lock);
675 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
676 ximagesink->xwindow = NULL;
677 g_mutex_lock (ximagesink->x_lock);
686 g_mutex_unlock (ximagesink->x_lock);
687 g_mutex_unlock (ximagesink->flow_lock);
691 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
693 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
695 GST_OBJECT_LOCK (ximagesink);
696 while (ximagesink->running) {
697 GST_OBJECT_UNLOCK (ximagesink);
699 if (ximagesink->xwindow) {
700 gst_ximagesink_handle_xevents (ximagesink);
702 /* FIXME: do we want to align this with the framerate or anything else? */
703 g_usleep (G_USEC_PER_SEC / 20);
705 GST_OBJECT_LOCK (ximagesink);
707 GST_OBJECT_UNLOCK (ximagesink);
713 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
715 GThread *thread = NULL;
717 /* don't start the thread too early */
718 if (ximagesink->xcontext == NULL) {
722 GST_OBJECT_LOCK (ximagesink);
723 if (ximagesink->handle_expose || ximagesink->handle_events) {
724 if (!ximagesink->event_thread) {
725 /* Setup our event listening thread */
726 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
727 ximagesink->handle_expose, ximagesink->handle_events);
728 ximagesink->running = TRUE;
729 ximagesink->event_thread = g_thread_create (
730 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
733 if (ximagesink->event_thread) {
734 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
735 ximagesink->handle_expose, ximagesink->handle_events);
736 ximagesink->running = FALSE;
737 /* grab thread and mark it as NULL */
738 thread = ximagesink->event_thread;
739 ximagesink->event_thread = NULL;
742 GST_OBJECT_UNLOCK (ximagesink);
744 /* Wait for our event thread to finish */
746 g_thread_join (thread);
751 /* This function calculates the pixel aspect ratio based on the properties
752 * in the xcontext structure and stores it there. */
754 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
756 static const gint par[][2] = {
757 {1, 1}, /* regular screen */
758 {16, 15}, /* PAL TV */
759 {11, 10}, /* 525 line Rec.601 video */
760 {54, 59}, /* 625 line Rec.601 video */
761 {64, 45}, /* 1280x1024 on 16:9 display */
762 {5, 3}, /* 1280x1024 on 4:3 display */
763 {4, 3} /* 800x600 on 16:9 display */
770 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
772 /* first calculate the "real" ratio based on the X values;
773 * which is the "physical" w/h divided by the w/h in pixels of the display */
774 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
775 / (xcontext->heightmm * xcontext->width);
777 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
779 if (xcontext->width == 720 && xcontext->height == 576) {
780 ratio = 4.0 * 576 / (3.0 * 720);
782 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
784 /* now find the one from par[][2] with the lowest delta to the real one */
788 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
789 gdouble this_delta = DELTA (i);
791 if (this_delta < delta) {
797 GST_DEBUG ("Decided on index %d (%d/%d)", index,
798 par[index][0], par[index][1]);
800 g_free (xcontext->par);
801 xcontext->par = g_new0 (GValue, 1);
802 g_value_init (xcontext->par, GST_TYPE_FRACTION);
803 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
804 GST_DEBUG ("set xcontext PAR to %d/%d",
805 gst_value_get_fraction_numerator (xcontext->par),
806 gst_value_get_fraction_denominator (xcontext->par));
809 /* This function gets the X Display and global info about it. Everything is
810 stored in our object and will be cleaned when the object is disposed. Note
811 here that caps for supported format are generated without any window or
814 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
816 GstXContext *xcontext = NULL;
817 XPixmapFormatValues *px_formats = NULL;
818 gint nb_formats = 0, i;
820 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
822 xcontext = g_new0 (GstXContext, 1);
824 g_mutex_lock (ximagesink->x_lock);
826 xcontext->disp = XOpenDisplay (ximagesink->display_name);
828 if (!xcontext->disp) {
829 g_mutex_unlock (ximagesink->x_lock);
831 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
832 ("Could not initialise X output"), ("Could not open display"));
836 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
837 xcontext->screen_num = DefaultScreen (xcontext->disp);
838 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
839 xcontext->root = DefaultRootWindow (xcontext->disp);
840 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
841 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
842 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
844 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
845 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
846 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
847 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
849 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
850 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
852 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
854 /* We get supported pixmap formats at supported depth */
855 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
858 XCloseDisplay (xcontext->disp);
859 g_mutex_unlock (ximagesink->x_lock);
860 g_free (xcontext->par);
862 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
863 ("Could not get supported pixmap formats"), (NULL));
867 /* We get bpp value corresponding to our running depth */
868 for (i = 0; i < nb_formats; i++) {
869 if (px_formats[i].depth == xcontext->depth)
870 xcontext->bpp = px_formats[i].bits_per_pixel;
875 xcontext->endianness =
876 (ImageByteOrder (xcontext->disp) ==
877 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
879 /* Search for XShm extension support */
881 if (XShmQueryExtension (xcontext->disp) &&
882 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
883 xcontext->use_xshm = TRUE;
884 GST_DEBUG ("ximagesink is using XShm extension");
886 #endif /* HAVE_XSHM */
888 xcontext->use_xshm = FALSE;
889 GST_DEBUG ("ximagesink is not using XShm extension");
892 /* our caps system handles 24/32bpp RGB as big-endian. */
893 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
894 xcontext->endianness == G_LITTLE_ENDIAN) {
895 xcontext->endianness = G_BIG_ENDIAN;
896 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
897 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
898 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
899 if (xcontext->bpp == 24) {
900 xcontext->visual->red_mask >>= 8;
901 xcontext->visual->green_mask >>= 8;
902 xcontext->visual->blue_mask >>= 8;
906 /* update object's par with calculated one if not set yet */
907 if (!ximagesink->par) {
908 ximagesink->par = g_new0 (GValue, 1);
909 gst_value_init_and_copy (ximagesink->par, xcontext->par);
910 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
912 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
913 "bpp", G_TYPE_INT, xcontext->bpp,
914 "depth", G_TYPE_INT, xcontext->depth,
915 "endianness", G_TYPE_INT, xcontext->endianness,
916 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
917 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
918 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
919 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
920 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
921 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
922 if (ximagesink->par) {
925 nom = gst_value_get_fraction_numerator (ximagesink->par);
926 den = gst_value_get_fraction_denominator (ximagesink->par);
927 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
928 GST_TYPE_FRACTION, nom, den, NULL);
931 g_mutex_unlock (ximagesink->x_lock);
936 /* This function cleans the X context. Closing the Display and unrefing the
937 caps for supported formats. */
939 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
941 GstXContext *xcontext;
943 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
945 GST_OBJECT_LOCK (ximagesink);
946 if (ximagesink->xcontext == NULL) {
947 GST_OBJECT_UNLOCK (ximagesink);
951 /* Take the xcontext reference and NULL it while we
952 * clean it up, so that any buffer-alloced buffers
953 * arriving after this will be freed correctly */
954 xcontext = ximagesink->xcontext;
955 ximagesink->xcontext = NULL;
957 GST_OBJECT_UNLOCK (ximagesink);
959 gst_caps_unref (xcontext->caps);
960 g_free (xcontext->par);
961 g_free (ximagesink->par);
962 ximagesink->par = NULL;
964 if (xcontext->last_caps)
965 gst_caps_replace (&xcontext->last_caps, NULL);
967 g_mutex_lock (ximagesink->x_lock);
969 XCloseDisplay (xcontext->disp);
971 g_mutex_unlock (ximagesink->x_lock);
979 gst_ximagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
981 GstXImageSink *ximagesink;
985 ximagesink = GST_XIMAGESINK (bsink);
987 g_mutex_lock (ximagesink->x_lock);
988 if (ximagesink->xcontext) {
991 caps = gst_caps_ref (ximagesink->xcontext->caps);
994 GstCaps *intersection;
997 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
998 gst_caps_unref (caps);
1002 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1003 GstStructure *s0, *s1;
1005 caps = gst_caps_make_writable (caps);
1007 /* There can only be a single structure because the xcontext
1008 * caps only have a single structure */
1009 s0 = gst_caps_get_structure (caps, 0);
1010 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1012 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1013 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1014 gst_caps_append_structure (caps, s1);
1016 /* This will not change the order but will remove the
1017 * fixed width/height caps again if not possible
1020 GstCaps *intersection;
1023 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1024 gst_caps_unref (caps);
1025 caps = intersection;
1029 g_mutex_unlock (ximagesink->x_lock);
1032 g_mutex_unlock (ximagesink->x_lock);
1034 /* get a template copy and add the pixel aspect ratio */
1035 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1036 if (ximagesink->par) {
1037 caps = gst_caps_make_writable (caps);
1038 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1039 GstStructure *structure = gst_caps_get_structure (caps, i);
1042 nom = gst_value_get_fraction_numerator (ximagesink->par);
1043 den = gst_value_get_fraction_denominator (ximagesink->par);
1044 gst_structure_set (structure, "pixel-aspect-ratio",
1045 GST_TYPE_FRACTION, nom, den, NULL);
1050 GstCaps *intersection;
1053 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1054 gst_caps_unref (caps);
1055 caps = intersection;
1062 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1064 GstXImageSink *ximagesink;
1065 GstStructure *structure;
1066 GstBufferPool *newpool, *oldpool;
1067 gboolean ret = TRUE;
1069 gint new_width, new_height;
1073 ximagesink = GST_XIMAGESINK (bsink);
1075 if (!ximagesink->xcontext)
1078 GST_DEBUG_OBJECT (ximagesink,
1079 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1080 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1082 /* We intersect those caps with our template to make sure they are correct */
1083 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1084 goto incompatible_caps;
1086 structure = gst_caps_get_structure (caps, 0);
1088 ret &= gst_structure_get_int (structure, "width", &new_width);
1089 ret &= gst_structure_get_int (structure, "height", &new_height);
1090 fps = gst_structure_get_value (structure, "framerate");
1091 ret &= (fps != NULL);
1096 if (!gst_video_get_size_from_caps (caps, &size))
1099 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1100 * otherwise linking should fail */
1101 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1103 if (ximagesink->par) {
1104 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1107 } else if (ximagesink->xcontext->par) {
1108 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1114 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1115 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1116 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1117 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1119 /* Notify application to set xwindow id now */
1120 g_mutex_lock (ximagesink->flow_lock);
1121 if (!ximagesink->xwindow) {
1122 g_mutex_unlock (ximagesink->flow_lock);
1123 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1125 g_mutex_unlock (ximagesink->flow_lock);
1128 /* Creating our window and our image */
1129 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1130 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1133 g_mutex_lock (ximagesink->flow_lock);
1134 if (!ximagesink->xwindow) {
1135 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1136 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1138 /* Remember to draw borders for next frame */
1139 ximagesink->draw_border = TRUE;
1141 /* create a new pool for the new configuration */
1142 newpool = gst_ximage_buffer_pool_new (ximagesink);
1144 structure = gst_buffer_pool_get_config (newpool);
1145 gst_buffer_pool_config_set (structure, caps, size, 0, 0, 0, 15);
1146 if (!gst_buffer_pool_set_config (newpool, structure))
1149 if (!gst_buffer_pool_set_active (newpool, TRUE))
1150 goto activate_failed;
1152 oldpool = ximagesink->pool;
1153 ximagesink->pool = newpool;
1155 /* unref the old sink */
1158 gst_buffer_pool_set_active (oldpool, FALSE);
1159 gst_object_unref (oldpool);
1161 g_mutex_unlock (ximagesink->flow_lock);
1168 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1173 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1178 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1179 ("Invalid image size."));
1184 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1185 g_mutex_unlock (ximagesink->flow_lock);
1190 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1191 g_mutex_unlock (ximagesink->flow_lock);
1192 gst_object_unref (newpool);
1197 static GstStateChangeReturn
1198 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1200 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1201 GstXImageSink *ximagesink;
1202 GstXContext *xcontext = NULL;
1204 ximagesink = GST_XIMAGESINK (element);
1206 switch (transition) {
1207 case GST_STATE_CHANGE_NULL_TO_READY:
1208 /* Initializing the XContext */
1209 if (ximagesink->xcontext == NULL) {
1210 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1211 if (xcontext == NULL) {
1212 ret = GST_STATE_CHANGE_FAILURE;
1215 GST_OBJECT_LOCK (ximagesink);
1217 ximagesink->xcontext = xcontext;
1218 GST_OBJECT_UNLOCK (ximagesink);
1221 /* call XSynchronize with the current value of synchronous */
1222 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1223 ximagesink->synchronous ? "TRUE" : "FALSE");
1224 g_mutex_lock (ximagesink->x_lock);
1225 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1226 g_mutex_unlock (ximagesink->x_lock);
1227 gst_ximagesink_manage_event_thread (ximagesink);
1229 case GST_STATE_CHANGE_READY_TO_PAUSED:
1230 g_mutex_lock (ximagesink->flow_lock);
1231 if (ximagesink->xwindow)
1232 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1233 g_mutex_unlock (ximagesink->flow_lock);
1235 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1241 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1243 switch (transition) {
1244 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1246 case GST_STATE_CHANGE_PAUSED_TO_READY:
1247 ximagesink->fps_n = 0;
1248 ximagesink->fps_d = 1;
1249 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1250 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1251 g_mutex_lock (ximagesink->flow_lock);
1252 if (ximagesink->pool)
1253 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1254 g_mutex_unlock (ximagesink->flow_lock);
1256 case GST_STATE_CHANGE_READY_TO_NULL:
1257 gst_ximagesink_reset (ximagesink);
1268 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1269 GstClockTime * start, GstClockTime * end)
1271 GstXImageSink *ximagesink;
1273 ximagesink = GST_XIMAGESINK (bsink);
1275 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1276 *start = GST_BUFFER_TIMESTAMP (buf);
1277 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1278 *end = *start + GST_BUFFER_DURATION (buf);
1280 if (ximagesink->fps_n > 0) {
1282 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1289 static GstFlowReturn
1290 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1293 GstXImageSink *ximagesink;
1294 GstMetaXImage *meta;
1298 ximagesink = GST_XIMAGESINK (vsink);
1300 meta = gst_buffer_get_meta_ximage (buf);
1303 /* If this buffer has been allocated using our buffer management we simply
1304 put the ximage which is in the PRIVATE pointer */
1305 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1312 /* Else we have to copy the data into our private image, */
1313 /* if we have one... */
1314 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1316 /* we should have a pool, configured in setcaps */
1317 if (ximagesink->pool == NULL)
1320 /* take a buffer form our pool */
1321 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &temp, NULL);
1322 if (res != GST_FLOW_OK)
1327 if (gst_buffer_get_size (temp) < gst_buffer_get_size (buf))
1330 data = gst_buffer_map (temp, &size, NULL, GST_MAP_WRITE);
1331 gst_buffer_extract (buf, 0, data, size);
1332 gst_buffer_unmap (temp, data, size);
1337 if (!gst_ximagesink_ximage_put (ximagesink, buf))
1342 gst_buffer_unref (buf);
1349 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1350 ("Internal error: can't allocate images"),
1351 ("We don't have a bufferpool negotiated"));
1352 return GST_FLOW_ERROR;
1356 /* No image available. That's very bad ! */
1357 GST_WARNING_OBJECT (ximagesink, "could not create image");
1362 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1363 ("Failed to create output image buffer"),
1364 ("XServer allocated buffer size did not match input buffer %"
1365 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (temp),
1366 gst_buffer_get_size (buf)));
1367 res = GST_FLOW_ERROR;
1372 /* No Window available to put our image into */
1373 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1374 res = GST_FLOW_ERROR;
1380 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1382 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1384 switch (GST_EVENT_TYPE (event)) {
1385 case GST_EVENT_TAG:{
1387 gchar *title = NULL;
1389 gst_event_parse_tag (event, &l);
1390 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1393 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1394 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1404 if (GST_BASE_SINK_CLASS (parent_class)->event)
1405 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1411 gst_ximagesink_sink_query (GstPad * sinkpad, GstQuery * query)
1413 GstXImageSink *ximagesink = GST_XIMAGESINK (GST_PAD_PARENT (sinkpad));
1414 gboolean res = TRUE;
1416 switch (GST_QUERY_TYPE (query)) {
1417 case GST_QUERY_ALLOCATION:
1419 GstBufferPool *pool;
1420 GstStructure *config;
1425 gst_query_parse_allocation (query, &caps, &need_pool);
1430 g_mutex_lock (ximagesink->flow_lock);
1431 if ((pool = ximagesink->pool))
1432 gst_object_ref (pool);
1433 g_mutex_unlock (ximagesink->flow_lock);
1436 const GstCaps *pcaps;
1438 /* we had a pool, check caps */
1439 config = gst_buffer_pool_get_config (pool);
1440 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL,
1443 GST_DEBUG_OBJECT (ximagesink,
1444 "we had a pool with caps %" GST_PTR_FORMAT, pcaps);
1445 if (!gst_caps_is_equal (caps, pcaps)) {
1446 /* different caps, we can't use this pool */
1447 GST_DEBUG_OBJECT (ximagesink, "pool has different caps");
1448 gst_object_unref (pool);
1452 if (pool == NULL && need_pool) {
1453 GstVideoFormat format;
1456 GST_DEBUG_OBJECT (ximagesink, "create new pool");
1457 pool = gst_ximage_buffer_pool_new (ximagesink);
1459 if (!gst_video_format_parse_caps (caps, &format, &width, &height))
1462 /* the normal size of a frame */
1463 size = gst_video_format_get_size (format, width, height);
1465 config = gst_buffer_pool_get_config (pool);
1466 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 15);
1467 if (!gst_buffer_pool_set_config (pool, config))
1470 gst_query_set_allocation_params (query, size, 0, 0, 0, 15, pool);
1472 /* we also support various metadata */
1473 gst_query_add_allocation_meta (query, GST_META_API_VIDEO);
1475 gst_object_unref (pool);
1487 GST_DEBUG_OBJECT (sinkpad, "no caps specified");
1492 GST_DEBUG_OBJECT (sinkpad, "invalid caps specified");
1497 GST_DEBUG_OBJECT (sinkpad, "failed setting config");
1502 /* Interfaces stuff */
1505 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1507 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY)
1514 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1516 klass->supported = gst_ximagesink_interface_supported;
1520 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1521 GstStructure * structure)
1523 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1525 gint x_offset, y_offset;
1529 event = gst_event_new_navigation (structure);
1531 /* We are not converting the pointer coordinates as there's no hardware
1532 scaling done here. The only possible scaling is done by videoscale and
1533 videoscale will have to catch those events and tranform the coordinates
1534 to match the applied scaling. So here we just add the offset if the image
1535 is centered in the window. */
1537 /* We take the flow_lock while we look at the window */
1538 g_mutex_lock (ximagesink->flow_lock);
1540 if (!ximagesink->xwindow) {
1541 g_mutex_unlock (ximagesink->flow_lock);
1545 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1546 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1548 g_mutex_unlock (ximagesink->flow_lock);
1550 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1552 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1554 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1556 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1559 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1561 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1562 gst_pad_send_event (pad, event);
1564 gst_object_unref (pad);
1569 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1571 iface->send_event = gst_ximagesink_navigation_send_event;
1575 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
1577 XID xwindow_id = id;
1578 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1579 GstXWindow *xwindow = NULL;
1580 XWindowAttributes attr;
1582 /* We acquire the stream lock while setting this window in the element.
1583 We are basically cleaning tons of stuff replacing the old window, putting
1584 images while we do that would surely crash */
1585 g_mutex_lock (ximagesink->flow_lock);
1587 /* If we already use that window return */
1588 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1589 g_mutex_unlock (ximagesink->flow_lock);
1593 /* If the element has not initialized the X11 context try to do so */
1594 if (!ximagesink->xcontext &&
1595 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1596 g_mutex_unlock (ximagesink->flow_lock);
1597 /* we have thrown a GST_ELEMENT_ERROR now */
1601 /* If a window is there already we destroy it */
1602 if (ximagesink->xwindow) {
1603 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1604 ximagesink->xwindow = NULL;
1607 /* If the xid is 0 we go back to an internal window */
1608 if (xwindow_id == 0) {
1609 /* If no width/height caps nego did not happen window will be created
1610 during caps nego then */
1611 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1612 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1613 GST_VIDEO_SINK_WIDTH (ximagesink),
1614 GST_VIDEO_SINK_HEIGHT (ximagesink));
1617 xwindow = g_new0 (GstXWindow, 1);
1619 xwindow->win = xwindow_id;
1621 /* We get window geometry, set the event we want to receive,
1623 g_mutex_lock (ximagesink->x_lock);
1624 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1625 xwindow->width = attr.width;
1626 xwindow->height = attr.height;
1627 xwindow->internal = FALSE;
1628 if (ximagesink->handle_events) {
1629 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1630 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1634 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1635 g_mutex_unlock (ximagesink->x_lock);
1639 ximagesink->xwindow = xwindow;
1641 g_mutex_unlock (ximagesink->flow_lock);
1645 gst_ximagesink_expose (GstXOverlay * overlay)
1647 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1649 gst_ximagesink_xwindow_update_geometry (ximagesink);
1650 gst_ximagesink_ximage_put (ximagesink, NULL);
1654 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1655 gboolean handle_events)
1657 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1659 ximagesink->handle_events = handle_events;
1661 g_mutex_lock (ximagesink->flow_lock);
1663 if (G_UNLIKELY (!ximagesink->xwindow)) {
1664 g_mutex_unlock (ximagesink->flow_lock);
1668 g_mutex_lock (ximagesink->x_lock);
1670 if (handle_events) {
1671 if (ximagesink->xwindow->internal) {
1672 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1673 ExposureMask | StructureNotifyMask | PointerMotionMask |
1674 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1676 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1677 ExposureMask | StructureNotifyMask | PointerMotionMask |
1678 KeyPressMask | KeyReleaseMask);
1681 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1684 g_mutex_unlock (ximagesink->x_lock);
1686 g_mutex_unlock (ximagesink->flow_lock);
1690 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1692 iface->set_window_handle = gst_ximagesink_set_window_handle;
1693 iface->expose = gst_ximagesink_expose;
1694 iface->handle_events = gst_ximagesink_set_event_handling;
1697 /* =========================================== */
1699 /* Init & Class init */
1701 /* =========================================== */
1704 gst_ximagesink_set_property (GObject * object, guint prop_id,
1705 const GValue * value, GParamSpec * pspec)
1707 GstXImageSink *ximagesink;
1709 g_return_if_fail (GST_IS_XIMAGESINK (object));
1711 ximagesink = GST_XIMAGESINK (object);
1715 ximagesink->display_name = g_strdup (g_value_get_string (value));
1717 case PROP_SYNCHRONOUS:
1718 ximagesink->synchronous = g_value_get_boolean (value);
1719 if (ximagesink->xcontext) {
1720 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1721 ximagesink->synchronous ? "TRUE" : "FALSE");
1722 g_mutex_lock (ximagesink->x_lock);
1723 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1724 g_mutex_unlock (ximagesink->x_lock);
1727 case PROP_FORCE_ASPECT_RATIO:
1728 ximagesink->keep_aspect = g_value_get_boolean (value);
1730 case PROP_PIXEL_ASPECT_RATIO:
1734 tmp = g_new0 (GValue, 1);
1735 g_value_init (tmp, GST_TYPE_FRACTION);
1737 if (!g_value_transform (value, tmp)) {
1738 GST_WARNING_OBJECT (ximagesink,
1739 "Could not transform string to aspect ratio");
1742 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1743 gst_value_get_fraction_numerator (tmp),
1744 gst_value_get_fraction_denominator (tmp));
1745 g_free (ximagesink->par);
1746 ximagesink->par = tmp;
1750 case PROP_HANDLE_EVENTS:
1751 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
1752 g_value_get_boolean (value));
1753 gst_ximagesink_manage_event_thread (ximagesink);
1755 case PROP_HANDLE_EXPOSE:
1756 ximagesink->handle_expose = g_value_get_boolean (value);
1757 gst_ximagesink_manage_event_thread (ximagesink);
1760 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1766 gst_ximagesink_get_property (GObject * object, guint prop_id,
1767 GValue * value, GParamSpec * pspec)
1769 GstXImageSink *ximagesink;
1771 g_return_if_fail (GST_IS_XIMAGESINK (object));
1773 ximagesink = GST_XIMAGESINK (object);
1777 g_value_set_string (value, ximagesink->display_name);
1779 case PROP_SYNCHRONOUS:
1780 g_value_set_boolean (value, ximagesink->synchronous);
1782 case PROP_FORCE_ASPECT_RATIO:
1783 g_value_set_boolean (value, ximagesink->keep_aspect);
1785 case PROP_PIXEL_ASPECT_RATIO:
1786 if (ximagesink->par)
1787 g_value_transform (ximagesink->par, value);
1789 case PROP_HANDLE_EVENTS:
1790 g_value_set_boolean (value, ximagesink->handle_events);
1792 case PROP_HANDLE_EXPOSE:
1793 g_value_set_boolean (value, ximagesink->handle_expose);
1795 case PROP_WINDOW_WIDTH:
1796 if (ximagesink->xwindow)
1797 g_value_set_uint64 (value, ximagesink->xwindow->width);
1799 g_value_set_uint64 (value, 0);
1801 case PROP_WINDOW_HEIGHT:
1802 if (ximagesink->xwindow)
1803 g_value_set_uint64 (value, ximagesink->xwindow->height);
1805 g_value_set_uint64 (value, 0);
1808 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1814 gst_ximagesink_reset (GstXImageSink * ximagesink)
1818 GST_OBJECT_LOCK (ximagesink);
1819 ximagesink->running = FALSE;
1820 /* grab thread and mark it as NULL */
1821 thread = ximagesink->event_thread;
1822 ximagesink->event_thread = NULL;
1823 GST_OBJECT_UNLOCK (ximagesink);
1825 /* Wait for our event thread to finish before we clean up our stuff. */
1827 g_thread_join (thread);
1829 if (ximagesink->cur_image) {
1830 gst_buffer_unref (ximagesink->cur_image);
1831 ximagesink->cur_image = NULL;
1834 g_mutex_lock (ximagesink->flow_lock);
1836 if (ximagesink->pool) {
1837 gst_object_unref (ximagesink->pool);
1838 ximagesink->pool = NULL;
1841 if (ximagesink->xwindow) {
1842 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1843 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1844 ximagesink->xwindow = NULL;
1846 g_mutex_unlock (ximagesink->flow_lock);
1848 gst_ximagesink_xcontext_clear (ximagesink);
1852 gst_ximagesink_finalize (GObject * object)
1854 GstXImageSink *ximagesink;
1856 ximagesink = GST_XIMAGESINK (object);
1858 gst_ximagesink_reset (ximagesink);
1860 if (ximagesink->display_name) {
1861 g_free (ximagesink->display_name);
1862 ximagesink->display_name = NULL;
1864 if (ximagesink->par) {
1865 g_free (ximagesink->par);
1866 ximagesink->par = NULL;
1868 if (ximagesink->x_lock) {
1869 g_mutex_free (ximagesink->x_lock);
1870 ximagesink->x_lock = NULL;
1872 if (ximagesink->flow_lock) {
1873 g_mutex_free (ximagesink->flow_lock);
1874 ximagesink->flow_lock = NULL;
1877 g_free (ximagesink->media_title);
1879 G_OBJECT_CLASS (parent_class)->finalize (object);
1883 gst_ximagesink_init (GstXImageSink * ximagesink)
1885 /* for the ALLOCATION query */
1886 gst_pad_set_query_function (GST_BASE_SINK (ximagesink)->sinkpad,
1887 gst_ximagesink_sink_query);
1889 ximagesink->display_name = NULL;
1890 ximagesink->xcontext = NULL;
1891 ximagesink->xwindow = NULL;
1892 ximagesink->cur_image = NULL;
1894 ximagesink->event_thread = NULL;
1895 ximagesink->running = FALSE;
1897 ximagesink->fps_n = 0;
1898 ximagesink->fps_d = 1;
1900 ximagesink->x_lock = g_mutex_new ();
1901 ximagesink->flow_lock = g_mutex_new ();
1903 ximagesink->par = NULL;
1905 ximagesink->pool = NULL;
1907 ximagesink->synchronous = FALSE;
1908 ximagesink->keep_aspect = FALSE;
1909 ximagesink->handle_events = TRUE;
1910 ximagesink->handle_expose = TRUE;
1914 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1916 GObjectClass *gobject_class;
1917 GstElementClass *gstelement_class;
1918 GstBaseSinkClass *gstbasesink_class;
1919 GstVideoSinkClass *videosink_class;
1921 gobject_class = (GObjectClass *) klass;
1922 gstelement_class = (GstElementClass *) klass;
1923 gstbasesink_class = (GstBaseSinkClass *) klass;
1924 videosink_class = (GstVideoSinkClass *) klass;
1926 gobject_class->finalize = gst_ximagesink_finalize;
1927 gobject_class->set_property = gst_ximagesink_set_property;
1928 gobject_class->get_property = gst_ximagesink_get_property;
1930 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1931 g_param_spec_string ("display", "Display", "X Display name",
1932 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1933 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1934 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1935 "the X display in synchronous mode. (used only for debugging)", FALSE,
1936 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1937 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1938 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1939 "When enabled, reverse caps negotiation (scaling) will respect "
1940 "original aspect ratio", FALSE,
1941 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1942 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1943 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1944 "The pixel aspect ratio of the device", "1/1",
1945 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1946 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1947 g_param_spec_boolean ("handle-events", "Handle XEvents",
1948 "When enabled, XEvents will be selected and handled", TRUE,
1949 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1950 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1951 g_param_spec_boolean ("handle-expose", "Handle expose",
1953 "the current frame will always be drawn in response to X Expose "
1954 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1957 * GstXImageSink:window-width
1959 * Actual width of the video window.
1963 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1964 g_param_spec_uint64 ("window-width", "window-width",
1965 "Width of the window", 0, G_MAXUINT64, 0,
1966 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1969 * GstXImageSink:window-height
1971 * Actual height of the video window.
1975 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1976 g_param_spec_uint64 ("window-height", "window-height",
1977 "Height of the window", 0, G_MAXUINT64, 0,
1978 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1980 gst_element_class_set_details_simple (gstelement_class,
1981 "Video sink", "Sink/Video",
1982 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1984 gst_element_class_add_pad_template (gstelement_class,
1985 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1987 gstelement_class->change_state = gst_ximagesink_change_state;
1989 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1990 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1991 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1992 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1994 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);