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
24 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
25 * display. This element can receive a Window ID from the application through
26 * the #GstVideoOverlay interface and will then render video frames in this
27 * drawable. If no Window ID was provided by the application, the element will
28 * create its own internal window and render into it.
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 * XImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
56 * ## Pixel aspect ratio
58 * When changing state to GST_STATE_READY, XImageSink will open a connection to
59 * the display specified in the #GstXImageSink:display property or the default
60 * display if nothing specified. Once this connection is open it will inspect
61 * the display configuration including the physical display geometry and
62 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
63 * video sink will set the calculated pixel aspect ratio on the caps to make
64 * sure that incoming video frames will have the correct pixel aspect ratio for
65 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
66 * then possible to enforce a specific pixel aspect ratio using the
67 * #GstXImageSink:pixel-aspect-ratio property.
71 * gst-launch-1.0 -v videotestsrc ! queue ! ximagesink
73 * A pipeline to test reverse negotiation. When the test video signal appears
74 * you can resize the window and see that scaled buffers of the desired size are
75 * going to arrive with a short delay. This illustrates how buffers of desired
76 * size are allocated along the way. If you take away the queue, scaling will
77 * happen almost immediately.
79 * gst-launch-1.0 -v videotestsrc ! navigationtest ! videoconvert ! ximagesink
81 * A pipeline to test navigation events.
82 * While moving the mouse pointer over the test signal you will see a black box
83 * following the mouse pointer. If you press the mouse button somewhere on the
84 * video and release it somewhere else a green box will appear where you pressed
85 * the button and a red one where you released it. (The navigationtest element
86 * is part of gst-plugins-good.)
88 * gst-launch-1.0 -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
90 * This is faking a 4/3 pixel aspect ratio caps on video frames produced by
91 * videotestsrc, in most cases the pixel aspect ratio of the display will be
92 * 1/1. This means that videoscale will have to do the scaling to convert
93 * incoming frames to a size that will match the display pixel aspect ratio
94 * (from 320x240 to 320x180 in this case). Note that you might have to escape
95 * some characters for your shell like '\(fraction\)'.
104 #include <gst/video/navigation.h>
105 #include <gst/video/videooverlay.h>
107 #include <gst/video/gstvideometa.h>
110 #include "ximagesink.h"
112 /* Debugging category */
113 #include <gst/gstinfo.h>
115 /* for XkbKeycodeToKeysym */
116 #include <X11/XKBlib.h>
118 GST_DEBUG_CATEGORY_EXTERN (gst_debug_x_image_sink);
119 GST_DEBUG_CATEGORY_EXTERN (CAT_PERFORMANCE);
120 #define GST_CAT_DEFAULT gst_debug_x_image_sink
125 unsigned long functions;
126 unsigned long decorations;
128 unsigned long status;
130 MotifWmHints, MwmHints;
132 #define MWM_HINTS_DECORATIONS (1L << 1)
134 static void gst_x_image_sink_reset (GstXImageSink * ximagesink);
135 static void gst_x_image_sink_xwindow_update_geometry (GstXImageSink *
137 static void gst_x_image_sink_expose (GstVideoOverlay * overlay);
139 static GstStaticPadTemplate gst_x_image_sink_sink_template_factory =
140 GST_STATIC_PAD_TEMPLATE ("sink",
143 GST_STATIC_CAPS ("video/x-raw, "
144 "framerate = (fraction) [ 0, MAX ], "
145 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
153 PROP_PIXEL_ASPECT_RATIO,
154 PROP_FORCE_ASPECT_RATIO,
161 /* ============================================================= */
165 /* ============================================================= */
167 /* =========================================== */
169 /* Object typing & Creation */
171 /* =========================================== */
172 static void gst_x_image_sink_navigation_init (GstNavigationInterface * iface);
173 static void gst_x_image_sink_video_overlay_init (GstVideoOverlayInterface *
175 #define gst_x_image_sink_parent_class parent_class
176 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_x_image_sink, GST_TYPE_VIDEO_SINK,
177 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
178 gst_x_image_sink_navigation_init);
179 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
180 gst_x_image_sink_video_overlay_init));
182 /* ============================================================= */
184 /* Private Methods */
186 /* ============================================================= */
190 /* We are called with the x_lock taken */
192 gst_x_image_sink_xwindow_draw_borders (GstXImageSink * ximagesink,
193 GstXWindow * xwindow, GstVideoRectangle rect)
195 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
196 g_return_if_fail (xwindow != NULL);
198 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
199 ximagesink->xcontext->black);
203 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
204 0, 0, rect.x, xwindow->height);
208 if ((rect.x + rect.w) < xwindow->width) {
209 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
210 rect.x + rect.w, 0, xwindow->width, xwindow->height);
215 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
216 0, 0, xwindow->width, rect.y);
220 if ((rect.y + rect.h) < xwindow->height) {
221 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
222 0, rect.y + rect.h, xwindow->width, xwindow->height);
226 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
228 gst_x_image_sink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
230 GstXImageMemory *mem;
231 GstVideoCropMeta *crop;
232 GstVideoRectangle src = { 0, };
233 GstVideoRectangle dst = { 0, };
234 GstVideoRectangle result;
235 gboolean draw_border = FALSE;
237 /* We take the flow_lock. If expose is in there we don't want to run
238 concurrently from the data flow thread */
239 g_mutex_lock (&ximagesink->flow_lock);
241 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
242 g_mutex_unlock (&ximagesink->flow_lock);
246 /* Draw borders when displaying the first frame. After this
247 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
248 if (!ximagesink->cur_image || ximagesink->draw_border) {
252 /* Store a reference to the last image we put, lose the previous one */
253 if (ximage && ximagesink->cur_image != ximage) {
254 if (ximagesink->cur_image) {
255 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
256 gst_buffer_unref (ximagesink->cur_image);
258 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
259 ximagesink->cur_image = gst_buffer_ref (ximage);
262 /* Expose sends a NULL image, we take the latest frame */
265 if (ximagesink->cur_image) {
266 ximage = ximagesink->cur_image;
268 g_mutex_unlock (&ximagesink->flow_lock);
273 mem = (GstXImageMemory *) gst_buffer_peek_memory (ximage, 0);
274 crop = gst_buffer_get_video_crop_meta (ximage);
277 src.x = crop->x + mem->x;
278 src.y = crop->y + mem->y;
280 src.h = crop->height;
281 GST_LOG_OBJECT (ximagesink,
282 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
289 dst.w = ximagesink->xwindow->width;
290 dst.h = ximagesink->xwindow->height;
292 gst_video_sink_center_rect (src, dst, &result, FALSE);
294 g_mutex_lock (&ximagesink->x_lock);
297 gst_x_image_sink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
299 ximagesink->draw_border = FALSE;
302 if (ximagesink->xcontext->use_xshm) {
303 GST_LOG_OBJECT (ximagesink,
304 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
305 ximage, 0, 0, result.x, result.y, result.w, result.h,
306 ximagesink->xwindow->width, ximagesink->xwindow->height);
307 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
308 ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
309 result.w, result.h, FALSE);
311 #endif /* HAVE_XSHM */
313 GST_LOG_OBJECT (ximagesink,
314 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
315 ximage, 0, 0, result.x, result.y, result.w, result.h,
316 ximagesink->xwindow->width, ximagesink->xwindow->height);
317 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
318 ximagesink->xwindow->gc, mem->ximage, src.x, src.y, result.x, result.y,
322 XSync (ximagesink->xcontext->disp, FALSE);
324 g_mutex_unlock (&ximagesink->x_lock);
326 g_mutex_unlock (&ximagesink->flow_lock);
332 gst_x_image_sink_xwindow_decorate (GstXImageSink * ximagesink,
335 Atom hints_atom = None;
338 g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), FALSE);
339 g_return_val_if_fail (window != NULL, FALSE);
341 g_mutex_lock (&ximagesink->x_lock);
343 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
345 if (hints_atom == None) {
346 g_mutex_unlock (&ximagesink->x_lock);
350 hints = g_malloc0 (sizeof (MotifWmHints));
352 hints->flags |= MWM_HINTS_DECORATIONS;
353 hints->decorations = 1 << 0;
355 XChangeProperty (ximagesink->xcontext->disp, window->win,
356 hints_atom, hints_atom, 32, PropModeReplace,
357 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
359 XSync (ximagesink->xcontext->disp, FALSE);
361 g_mutex_unlock (&ximagesink->x_lock);
369 gst_x_image_sink_xwindow_set_title (GstXImageSink * ximagesink,
370 GstXWindow * xwindow, const gchar * media_title)
373 g_free (ximagesink->media_title);
374 ximagesink->media_title = g_strdup (media_title);
377 /* we have a window */
378 if (xwindow->internal) {
379 XTextProperty xproperty;
380 XClassHint *hint = XAllocClassHint ();
381 const gchar *app_name;
382 const gchar *title = NULL;
383 gchar *title_mem = NULL;
385 /* set application name as a title */
386 app_name = g_get_application_name ();
388 if (app_name && ximagesink->media_title) {
389 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
391 } else if (app_name) {
393 } else if (ximagesink->media_title) {
394 title = ximagesink->media_title;
398 if ((XStringListToTextProperty (((char **) &title), 1,
401 XInternAtom (ximagesink->xcontext->disp, "_NET_WM_NAME", 0);
403 XInternAtom (ximagesink->xcontext->disp, "UTF8_STRING", 0);
404 XChangeProperty (ximagesink->xcontext->disp, xwindow->win,
405 _NET_WM_NAME, UTF8_STRING, 8, 0, (unsigned char *) title,
407 XSync (ximagesink->xcontext->disp, False);
409 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
410 XFree (xproperty.value);
417 hint->res_name = (char *) app_name;
418 hint->res_class = (char *) "GStreamer";
419 XSetClassHint (ximagesink->xcontext->disp, xwindow->win, hint);
426 /* This function handles a GstXWindow creation */
428 gst_x_image_sink_xwindow_new (GstXImageSink * ximagesink, gint width,
431 GstXWindow *xwindow = NULL;
434 g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
436 xwindow = g_new0 (GstXWindow, 1);
438 xwindow->width = width;
439 xwindow->height = height;
440 xwindow->internal = TRUE;
442 g_mutex_lock (&ximagesink->x_lock);
444 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
445 ximagesink->xcontext->root,
446 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
448 /* We have to do that to prevent X from redrawing the background on
449 ConfigureNotify. This takes away flickering of video when resizing. */
450 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
452 /* set application name as a title */
453 gst_x_image_sink_xwindow_set_title (ximagesink, xwindow, NULL);
455 if (ximagesink->handle_events) {
458 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
459 StructureNotifyMask | PointerMotionMask | KeyPressMask |
460 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
462 /* Tell the window manager we'd like delete client messages instead of
464 wm_delete = XInternAtom (ximagesink->xcontext->disp,
465 "WM_DELETE_WINDOW", False);
466 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
470 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
473 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
475 XSync (ximagesink->xcontext->disp, FALSE);
477 g_mutex_unlock (&ximagesink->x_lock);
479 gst_x_image_sink_xwindow_decorate (ximagesink, xwindow);
481 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
487 /* This function destroys a GstXWindow */
489 gst_x_image_sink_xwindow_destroy (GstXImageSink * ximagesink,
490 GstXWindow * xwindow)
492 g_return_if_fail (xwindow != NULL);
493 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
495 g_mutex_lock (&ximagesink->x_lock);
497 /* If we did not create that window we just free the GC and let it live */
498 if (xwindow->internal)
499 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
501 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
503 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
505 XSync (ximagesink->xcontext->disp, FALSE);
507 g_mutex_unlock (&ximagesink->x_lock);
513 gst_x_image_sink_xwindow_update_geometry (GstXImageSink * ximagesink)
515 XWindowAttributes attr;
516 gboolean reconfigure;
518 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
520 /* Update the window geometry */
521 g_mutex_lock (&ximagesink->x_lock);
522 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
523 g_mutex_unlock (&ximagesink->x_lock);
527 XGetWindowAttributes (ximagesink->xcontext->disp,
528 ximagesink->xwindow->win, &attr);
530 /* Check if we would suggest a different width/height now */
531 reconfigure = (ximagesink->xwindow->width != attr.width)
532 || (ximagesink->xwindow->height != attr.height);
533 ximagesink->xwindow->width = attr.width;
534 ximagesink->xwindow->height = attr.height;
536 g_mutex_unlock (&ximagesink->x_lock);
539 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
540 gst_event_new_reconfigure ());
544 gst_x_image_sink_xwindow_clear (GstXImageSink * ximagesink,
545 GstXWindow * xwindow)
547 g_return_if_fail (xwindow != NULL);
548 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
550 g_mutex_lock (&ximagesink->x_lock);
552 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
553 ximagesink->xcontext->black);
555 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
556 0, 0, xwindow->width, xwindow->height);
558 XSync (ximagesink->xcontext->disp, FALSE);
560 g_mutex_unlock (&ximagesink->x_lock);
563 /* This function handles XEvents that might be in the queue. It generates
564 GstEvent that will be sent upstream in the pipeline to handle interactivity
567 gst_x_image_sink_handle_xevents (GstXImageSink * ximagesink)
570 gint pointer_x = 0, pointer_y = 0;
571 gboolean pointer_moved = FALSE;
572 gboolean exposed = FALSE, configured = FALSE;
574 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
576 /* Then we get all pointer motion events, only the last position is
578 g_mutex_lock (&ximagesink->flow_lock);
579 g_mutex_lock (&ximagesink->x_lock);
580 while (XCheckWindowEvent (ximagesink->xcontext->disp,
581 ximagesink->xwindow->win, PointerMotionMask, &e)) {
582 g_mutex_unlock (&ximagesink->x_lock);
583 g_mutex_unlock (&ximagesink->flow_lock);
587 pointer_x = e.xmotion.x;
588 pointer_y = e.xmotion.y;
589 pointer_moved = TRUE;
594 g_mutex_lock (&ximagesink->flow_lock);
595 g_mutex_lock (&ximagesink->x_lock);
599 g_mutex_unlock (&ximagesink->x_lock);
600 g_mutex_unlock (&ximagesink->flow_lock);
602 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
603 pointer_x, pointer_y);
604 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
605 "mouse-move", 0, pointer_x, pointer_y);
607 g_mutex_lock (&ximagesink->flow_lock);
608 g_mutex_lock (&ximagesink->x_lock);
611 /* We get all remaining events on our window to throw them upstream */
612 while (XCheckWindowEvent (ximagesink->xcontext->disp,
613 ximagesink->xwindow->win,
614 KeyPressMask | KeyReleaseMask |
615 ButtonPressMask | ButtonReleaseMask, &e)) {
617 const char *key_str = NULL;
619 /* We lock only for the X function call */
620 g_mutex_unlock (&ximagesink->x_lock);
621 g_mutex_unlock (&ximagesink->flow_lock);
625 /* Mouse button pressed/released over our window. We send upstream
626 events for interactivity/navigation */
627 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
628 e.xbutton.button, e.xbutton.x, e.xbutton.x);
629 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
630 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
633 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
634 e.xbutton.button, e.xbutton.x, e.xbutton.x);
635 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
636 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
640 /* Key pressed/released over our window. We send upstream
641 events for interactivity/navigation */
642 g_mutex_lock (&ximagesink->x_lock);
643 if (ximagesink->xcontext->use_xkb) {
644 keysym = XkbKeycodeToKeysym (ximagesink->xcontext->disp,
645 e.xkey.keycode, 0, 0);
647 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
650 if (keysym != NoSymbol) {
651 key_str = XKeysymToString (keysym);
655 g_mutex_unlock (&ximagesink->x_lock);
656 GST_DEBUG_OBJECT (ximagesink,
657 "key %d pressed over window at %d,%d (%s)",
658 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
659 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
660 e.type == KeyPress ? "key-press" : "key-release", key_str);
663 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
666 g_mutex_lock (&ximagesink->flow_lock);
667 g_mutex_lock (&ximagesink->x_lock);
671 while (XCheckWindowEvent (ximagesink->xcontext->disp,
672 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
677 case ConfigureNotify:
678 g_mutex_unlock (&ximagesink->x_lock);
679 gst_x_image_sink_xwindow_update_geometry (ximagesink);
680 g_mutex_lock (&ximagesink->x_lock);
688 if (ximagesink->handle_expose && (exposed || configured)) {
689 g_mutex_unlock (&ximagesink->x_lock);
690 g_mutex_unlock (&ximagesink->flow_lock);
692 gst_x_image_sink_expose (GST_VIDEO_OVERLAY (ximagesink));
694 g_mutex_lock (&ximagesink->flow_lock);
695 g_mutex_lock (&ximagesink->x_lock);
698 /* Handle Display events */
699 while (XPending (ximagesink->xcontext->disp)) {
700 XNextEvent (ximagesink->xcontext->disp, &e);
706 wm_delete = XInternAtom (ximagesink->xcontext->disp,
707 "WM_DELETE_WINDOW", False);
708 if (wm_delete == (Atom) e.xclient.data.l[0]) {
709 /* Handle window deletion by posting an error on the bus */
710 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
711 ("Output window was closed"), (NULL));
713 g_mutex_unlock (&ximagesink->x_lock);
714 gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
715 ximagesink->xwindow = NULL;
716 g_mutex_lock (&ximagesink->x_lock);
725 g_mutex_unlock (&ximagesink->x_lock);
726 g_mutex_unlock (&ximagesink->flow_lock);
730 gst_x_image_sink_event_thread (GstXImageSink * ximagesink)
732 g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
734 GST_OBJECT_LOCK (ximagesink);
735 while (ximagesink->running) {
736 GST_OBJECT_UNLOCK (ximagesink);
738 if (ximagesink->xwindow) {
739 gst_x_image_sink_handle_xevents (ximagesink);
741 /* FIXME: do we want to align this with the framerate or anything else? */
742 g_usleep (G_USEC_PER_SEC / 20);
744 GST_OBJECT_LOCK (ximagesink);
746 GST_OBJECT_UNLOCK (ximagesink);
752 gst_x_image_sink_manage_event_thread (GstXImageSink * ximagesink)
754 GThread *thread = NULL;
756 /* don't start the thread too early */
757 if (ximagesink->xcontext == NULL) {
761 GST_OBJECT_LOCK (ximagesink);
762 if (ximagesink->handle_expose || ximagesink->handle_events) {
763 if (!ximagesink->event_thread) {
764 /* Setup our event listening thread */
765 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
766 ximagesink->handle_expose, ximagesink->handle_events);
767 ximagesink->running = TRUE;
768 ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
769 (GThreadFunc) gst_x_image_sink_event_thread, ximagesink, NULL);
772 if (ximagesink->event_thread) {
773 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
774 ximagesink->handle_expose, ximagesink->handle_events);
775 ximagesink->running = FALSE;
776 /* grab thread and mark it as NULL */
777 thread = ximagesink->event_thread;
778 ximagesink->event_thread = NULL;
781 GST_OBJECT_UNLOCK (ximagesink);
783 /* Wait for our event thread to finish */
785 g_thread_join (thread);
790 /* This function calculates the pixel aspect ratio based on the properties
791 * in the xcontext structure and stores it there. */
793 gst_x_image_sink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
795 static const gint par[][2] = {
796 {1, 1}, /* regular screen */
797 {16, 15}, /* PAL TV */
798 {11, 10}, /* 525 line Rec.601 video */
799 {54, 59}, /* 625 line Rec.601 video */
800 {64, 45}, /* 1280x1024 on 16:9 display */
801 {5, 3}, /* 1280x1024 on 4:3 display */
802 {4, 3} /* 800x600 on 16:9 display */
809 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
811 /* first calculate the "real" ratio based on the X values;
812 * which is the "physical" w/h divided by the w/h in pixels of the display */
813 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
814 / (xcontext->heightmm * xcontext->width);
816 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
818 if (xcontext->width == 720 && xcontext->height == 576) {
819 ratio = 4.0 * 576 / (3.0 * 720);
821 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
823 /* now find the one from par[][2] with the lowest delta to the real one */
827 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
828 gdouble this_delta = DELTA (i);
830 if (this_delta < delta) {
836 GST_DEBUG ("Decided on index %d (%d/%d)", index,
837 par[index][0], par[index][1]);
839 g_free (xcontext->par);
840 xcontext->par = g_new0 (GValue, 1);
841 g_value_init (xcontext->par, GST_TYPE_FRACTION);
842 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
843 GST_DEBUG ("set xcontext PAR to %d/%d",
844 gst_value_get_fraction_numerator (xcontext->par),
845 gst_value_get_fraction_denominator (xcontext->par));
848 /* This function gets the X Display and global info about it. Everything is
849 stored in our object and will be cleaned when the object is disposed. Note
850 here that caps for supported format are generated without any window or
853 gst_x_image_sink_xcontext_get (GstXImageSink * ximagesink)
855 GstXContext *xcontext = NULL;
856 XPixmapFormatValues *px_formats = NULL;
857 gint nb_formats = 0, i;
859 GstVideoFormat vformat;
861 int opcode, event, err;
862 int major = XkbMajorVersion;
863 int minor = XkbMinorVersion;
865 g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
867 xcontext = g_new0 (GstXContext, 1);
869 g_mutex_lock (&ximagesink->x_lock);
871 xcontext->disp = XOpenDisplay (ximagesink->display_name);
873 if (!xcontext->disp) {
874 g_mutex_unlock (&ximagesink->x_lock);
876 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
877 ("Could not initialise X output"), ("Could not open display"));
881 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
882 xcontext->screen_num = DefaultScreen (xcontext->disp);
883 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
884 xcontext->root = DefaultRootWindow (xcontext->disp);
885 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
886 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
887 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
889 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
890 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
891 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
892 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
894 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
895 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
897 gst_x_image_sink_calculate_pixel_aspect_ratio (xcontext);
899 /* We get supported pixmap formats at supported depth */
900 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
903 XCloseDisplay (xcontext->disp);
904 g_mutex_unlock (&ximagesink->x_lock);
905 g_free (xcontext->par);
907 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
908 ("Could not get supported pixmap formats"), (NULL));
912 /* We get bpp value corresponding to our running depth */
913 for (i = 0; i < nb_formats; i++) {
914 if (px_formats[i].depth == xcontext->depth)
915 xcontext->bpp = px_formats[i].bits_per_pixel;
920 endianness = (ImageByteOrder (xcontext->disp) ==
921 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
923 /* Search for XShm extension support */
925 if (XShmQueryExtension (xcontext->disp) &&
926 gst_x_image_sink_check_xshm_calls (ximagesink, xcontext)) {
927 xcontext->use_xshm = TRUE;
928 GST_DEBUG ("ximagesink is using XShm extension");
930 #endif /* HAVE_XSHM */
932 xcontext->use_xshm = FALSE;
933 GST_DEBUG ("ximagesink is not using XShm extension");
935 if (XkbQueryExtension (xcontext->disp, &opcode, &event, &err, &major, &minor)) {
936 xcontext->use_xkb = TRUE;
937 GST_DEBUG ("ximagesink is using Xkb extension");
939 xcontext->use_xkb = FALSE;
940 GST_DEBUG ("ximagesink is not using Xkb extension");
943 /* extrapolate alpha mask */
944 if (xcontext->depth == 32) {
945 alpha_mask = ~(xcontext->visual->red_mask
946 | xcontext->visual->green_mask | xcontext->visual->blue_mask);
952 gst_video_format_from_masks (xcontext->depth, xcontext->bpp, endianness,
953 xcontext->visual->red_mask, xcontext->visual->green_mask,
954 xcontext->visual->blue_mask, alpha_mask);
956 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
959 /* update object's par with calculated one if not set yet */
960 if (!ximagesink->par) {
961 ximagesink->par = g_new0 (GValue, 1);
962 gst_value_init_and_copy (ximagesink->par, xcontext->par);
963 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
965 xcontext->caps = gst_caps_new_simple ("video/x-raw",
966 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
967 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
968 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
969 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
970 if (ximagesink->par) {
973 nom = gst_value_get_fraction_numerator (ximagesink->par);
974 den = gst_value_get_fraction_denominator (ximagesink->par);
975 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
976 GST_TYPE_FRACTION, nom, den, NULL);
979 g_mutex_unlock (&ximagesink->x_lock);
986 GST_ERROR_OBJECT (ximagesink, "unknown format");
991 /* This function cleans the X context. Closing the Display and unrefing the
992 caps for supported formats. */
994 gst_x_image_sink_xcontext_clear (GstXImageSink * ximagesink)
996 GstXContext *xcontext;
998 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
1000 GST_OBJECT_LOCK (ximagesink);
1001 if (ximagesink->xcontext == NULL) {
1002 GST_OBJECT_UNLOCK (ximagesink);
1006 /* Take the xcontext reference and NULL it while we
1007 * clean it up, so that any buffer-alloced buffers
1008 * arriving after this will be freed correctly */
1009 xcontext = ximagesink->xcontext;
1010 ximagesink->xcontext = NULL;
1012 GST_OBJECT_UNLOCK (ximagesink);
1014 gst_caps_unref (xcontext->caps);
1015 g_free (xcontext->par);
1016 g_free (ximagesink->par);
1017 ximagesink->par = NULL;
1019 if (xcontext->last_caps)
1020 gst_caps_replace (&xcontext->last_caps, NULL);
1022 g_mutex_lock (&ximagesink->x_lock);
1024 XCloseDisplay (xcontext->disp);
1026 g_mutex_unlock (&ximagesink->x_lock);
1034 gst_x_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1036 GstXImageSink *ximagesink;
1040 ximagesink = GST_X_IMAGE_SINK (bsink);
1042 g_mutex_lock (&ximagesink->x_lock);
1043 if (ximagesink->xcontext) {
1046 caps = gst_caps_ref (ximagesink->xcontext->caps);
1049 GstCaps *intersection;
1052 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1053 gst_caps_unref (caps);
1054 caps = intersection;
1057 if (gst_caps_is_empty (caps)) {
1058 g_mutex_unlock (&ximagesink->x_lock);
1062 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1063 GstStructure *s0, *s1;
1065 caps = gst_caps_make_writable (caps);
1067 /* There can only be a single structure because the xcontext
1068 * caps only have a single structure */
1069 s0 = gst_caps_get_structure (caps, 0);
1070 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1072 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1073 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1074 gst_caps_append_structure (caps, s1);
1076 /* This will not change the order but will remove the
1077 * fixed width/height caps again if not possible
1080 GstCaps *intersection;
1083 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1084 gst_caps_unref (caps);
1085 caps = intersection;
1089 g_mutex_unlock (&ximagesink->x_lock);
1092 g_mutex_unlock (&ximagesink->x_lock);
1094 /* get a template copy and add the pixel aspect ratio */
1095 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1096 if (ximagesink->par) {
1097 caps = gst_caps_make_writable (caps);
1098 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1099 GstStructure *structure = gst_caps_get_structure (caps, i);
1102 nom = gst_value_get_fraction_numerator (ximagesink->par);
1103 den = gst_value_get_fraction_denominator (ximagesink->par);
1104 gst_structure_set (structure, "pixel-aspect-ratio",
1105 GST_TYPE_FRACTION, nom, den, NULL);
1110 GstCaps *intersection;
1113 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1114 gst_caps_unref (caps);
1115 caps = intersection;
1121 static GstBufferPool *
1122 gst_x_image_sink_create_pool (GstXImageSink * ximagesink, GstCaps * caps,
1123 gsize size, gint min)
1125 static GstAllocationParams params = { 0, 15, 0, 0, };
1126 GstBufferPool *pool;
1127 GstStructure *config;
1129 /* create a new pool for the new configuration */
1130 pool = gst_ximage_buffer_pool_new (ximagesink);
1132 config = gst_buffer_pool_get_config (pool);
1133 gst_buffer_pool_config_set_params (config, caps, size, min, 0);
1134 gst_buffer_pool_config_set_allocator (config, NULL, ¶ms);
1136 if (!gst_buffer_pool_set_config (pool, config))
1143 GST_WARNING_OBJECT (ximagesink, "failed setting config");
1144 gst_object_unref (pool);
1150 gst_x_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1152 GstXImageSink *ximagesink;
1153 GstStructure *structure;
1155 GstBufferPool *newpool, *oldpool;
1158 ximagesink = GST_X_IMAGE_SINK (bsink);
1160 if (!ximagesink->xcontext)
1163 GST_DEBUG_OBJECT (ximagesink,
1164 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1165 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1167 /* We intersect those caps with our template to make sure they are correct */
1168 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1169 goto incompatible_caps;
1171 if (!gst_video_info_from_caps (&info, caps))
1172 goto invalid_format;
1174 structure = gst_caps_get_structure (caps, 0);
1175 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1176 * otherwise linking should fail */
1177 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1179 if (ximagesink->par) {
1180 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1183 } else if (ximagesink->xcontext->par) {
1184 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1190 GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1191 GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1192 ximagesink->fps_n = info.fps_n;
1193 ximagesink->fps_d = info.fps_d;
1195 /* Notify application to set xwindow id now */
1196 g_mutex_lock (&ximagesink->flow_lock);
1197 if (!ximagesink->xwindow) {
1198 g_mutex_unlock (&ximagesink->flow_lock);
1199 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1201 g_mutex_unlock (&ximagesink->flow_lock);
1204 /* Creating our window and our image */
1205 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1206 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1209 g_mutex_lock (&ximagesink->flow_lock);
1210 if (!ximagesink->xwindow) {
1211 ximagesink->xwindow = gst_x_image_sink_xwindow_new (ximagesink,
1212 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1215 ximagesink->info = info;
1217 /* Remember to draw borders for next frame */
1218 ximagesink->draw_border = TRUE;
1220 /* create a new internal pool for the new configuration */
1221 newpool = gst_x_image_sink_create_pool (ximagesink, caps, info.size, 2);
1223 /* we don't activate the internal pool yet as it may not be needed */
1224 oldpool = ximagesink->pool;
1225 ximagesink->pool = newpool;
1226 g_mutex_unlock (&ximagesink->flow_lock);
1228 /* deactivate and unref the old internal pool */
1230 gst_buffer_pool_set_active (oldpool, FALSE);
1231 gst_object_unref (oldpool);
1239 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1244 GST_ERROR_OBJECT (ximagesink, "caps invalid");
1249 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1254 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1255 ("Invalid image size."));
1260 static GstStateChangeReturn
1261 gst_x_image_sink_change_state (GstElement * element, GstStateChange transition)
1263 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1264 GstXImageSink *ximagesink;
1265 GstXContext *xcontext = NULL;
1267 ximagesink = GST_X_IMAGE_SINK (element);
1269 switch (transition) {
1270 case GST_STATE_CHANGE_NULL_TO_READY:
1271 /* Initializing the XContext */
1272 if (ximagesink->xcontext == NULL) {
1273 xcontext = gst_x_image_sink_xcontext_get (ximagesink);
1274 if (xcontext == NULL) {
1275 ret = GST_STATE_CHANGE_FAILURE;
1278 GST_OBJECT_LOCK (ximagesink);
1280 ximagesink->xcontext = xcontext;
1281 GST_OBJECT_UNLOCK (ximagesink);
1284 /* call XSynchronize with the current value of synchronous */
1285 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1286 ximagesink->synchronous ? "TRUE" : "FALSE");
1287 g_mutex_lock (&ximagesink->x_lock);
1288 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1289 g_mutex_unlock (&ximagesink->x_lock);
1290 gst_x_image_sink_manage_event_thread (ximagesink);
1292 case GST_STATE_CHANGE_READY_TO_PAUSED:
1293 g_mutex_lock (&ximagesink->flow_lock);
1294 if (ximagesink->xwindow)
1295 gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
1296 g_mutex_unlock (&ximagesink->flow_lock);
1298 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1304 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1306 switch (transition) {
1307 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1309 case GST_STATE_CHANGE_PAUSED_TO_READY:
1310 ximagesink->fps_n = 0;
1311 ximagesink->fps_d = 1;
1312 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1313 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1314 g_mutex_lock (&ximagesink->flow_lock);
1315 if (ximagesink->pool)
1316 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1317 g_mutex_unlock (&ximagesink->flow_lock);
1319 case GST_STATE_CHANGE_READY_TO_NULL:
1320 gst_x_image_sink_reset (ximagesink);
1331 gst_x_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1332 GstClockTime * start, GstClockTime * end)
1334 GstXImageSink *ximagesink;
1336 ximagesink = GST_X_IMAGE_SINK (bsink);
1338 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1339 *start = GST_BUFFER_TIMESTAMP (buf);
1340 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1341 *end = *start + GST_BUFFER_DURATION (buf);
1343 if (ximagesink->fps_n > 0) {
1345 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1352 static GstFlowReturn
1353 gst_x_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1356 GstXImageSink *ximagesink;
1357 GstXImageMemory *mem;
1358 GstBuffer *to_put = NULL;
1360 ximagesink = GST_X_IMAGE_SINK (vsink);
1362 if (gst_buffer_n_memory (buf) == 1
1363 && (mem = (GstXImageMemory *) gst_buffer_peek_memory (buf, 0))
1364 && g_strcmp0 (mem->parent.allocator->mem_type, "ximage") == 0
1365 && mem->sink == ximagesink) {
1366 /* If this buffer has been allocated using our buffer management we simply
1367 put the ximage which is in the PRIVATE pointer */
1368 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1372 GstVideoFrame src, dest;
1373 GstBufferPoolAcquireParams params = { 0, };
1375 /* Else we have to copy the data into our private image, */
1376 /* if we have one... */
1377 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1379 /* an internal pool should have been created in setcaps */
1380 if (G_UNLIKELY (ximagesink->pool == NULL))
1383 if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1384 goto activate_failed;
1386 /* take a buffer from our pool, if there is no buffer in the pool something
1387 * is seriously wrong, waiting for the pool here might deadlock when we try
1388 * to go to PAUSED because we never flush the pool. */
1389 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1390 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, ¶ms);
1391 if (res != GST_FLOW_OK)
1394 GST_CAT_LOG_OBJECT (CAT_PERFORMANCE, ximagesink,
1395 "slow copy into bufferpool buffer %p", to_put);
1397 if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1398 goto invalid_buffer;
1400 if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1401 gst_video_frame_unmap (&src);
1402 goto invalid_buffer;
1405 gst_video_frame_copy (&dest, &src);
1407 gst_video_frame_unmap (&dest);
1408 gst_video_frame_unmap (&src);
1411 if (!gst_x_image_sink_ximage_put (ximagesink, to_put))
1416 gst_buffer_unref (to_put);
1423 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1424 ("Internal error: can't allocate images"),
1425 ("We don't have a bufferpool negotiated"));
1426 return GST_FLOW_ERROR;
1430 /* No image available. That's very bad ! */
1431 GST_WARNING_OBJECT (ximagesink, "could not create image");
1436 /* No Window available to put our image into */
1437 GST_WARNING_OBJECT (ximagesink, "could not map image");
1443 /* No Window available to put our image into */
1444 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1445 res = GST_FLOW_ERROR;
1450 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1451 res = GST_FLOW_ERROR;
1457 gst_x_image_sink_event (GstBaseSink * sink, GstEvent * event)
1459 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (sink);
1461 switch (GST_EVENT_TYPE (event)) {
1462 case GST_EVENT_TAG:{
1464 gchar *title = NULL;
1466 gst_event_parse_tag (event, &l);
1467 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1470 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1471 gst_x_image_sink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1481 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1485 gst_x_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1487 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (bsink);
1488 GstBufferPool *pool = NULL;
1494 gst_query_parse_allocation (query, &caps, &need_pool);
1499 if (!gst_video_info_from_caps (&info, caps))
1502 /* the normal size of a frame */
1506 pool = gst_x_image_sink_create_pool (ximagesink, caps, info.size, 0);
1512 /* we need at least 2 buffer because we hold on to the last one */
1513 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1515 gst_object_unref (pool);
1517 /* we also support various metadata */
1518 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1519 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1526 GST_DEBUG_OBJECT (bsink, "no caps specified");
1531 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1536 /* Already warned in create_pool */
1541 /* Interfaces stuff */
1543 gst_x_image_sink_navigation_send_event (GstNavigation * navigation,
1544 GstStructure * structure)
1546 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (navigation);
1547 GstEvent *event = NULL;
1548 gint x_offset, y_offset;
1550 gboolean handled = FALSE;
1552 /* We are not converting the pointer coordinates as there's no hardware
1553 scaling done here. The only possible scaling is done by videoscale and
1554 videoscale will have to catch those events and transform the coordinates
1555 to match the applied scaling. So here we just add the offset if the image
1556 is centered in the window. */
1558 /* We take the flow_lock while we look at the window */
1559 g_mutex_lock (&ximagesink->flow_lock);
1561 if (!ximagesink->xwindow) {
1562 g_mutex_unlock (&ximagesink->flow_lock);
1563 gst_structure_free (structure);
1567 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1568 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1570 g_mutex_unlock (&ximagesink->flow_lock);
1572 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1574 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1576 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1578 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1581 event = gst_event_new_navigation (structure);
1583 gst_event_ref (event);
1584 handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (ximagesink), event);
1587 gst_element_post_message (GST_ELEMENT_CAST (ximagesink),
1588 gst_navigation_message_new_event (GST_OBJECT_CAST (ximagesink),
1591 gst_event_unref (event);
1596 gst_x_image_sink_navigation_init (GstNavigationInterface * iface)
1598 iface->send_event = gst_x_image_sink_navigation_send_event;
1602 gst_x_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1604 XID xwindow_id = id;
1605 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1606 GstXWindow *xwindow = NULL;
1608 /* We acquire the stream lock while setting this window in the element.
1609 We are basically cleaning tons of stuff replacing the old window, putting
1610 images while we do that would surely crash */
1611 g_mutex_lock (&ximagesink->flow_lock);
1613 /* If we already use that window return */
1614 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1615 g_mutex_unlock (&ximagesink->flow_lock);
1619 /* If the element has not initialized the X11 context try to do so */
1620 if (!ximagesink->xcontext &&
1621 !(ximagesink->xcontext = gst_x_image_sink_xcontext_get (ximagesink))) {
1622 g_mutex_unlock (&ximagesink->flow_lock);
1623 /* we have thrown a GST_ELEMENT_ERROR now */
1627 /* If a window is there already we destroy it */
1628 if (ximagesink->xwindow) {
1629 gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1630 ximagesink->xwindow = NULL;
1633 /* If the xid is 0 we go back to an internal window */
1634 if (xwindow_id == 0) {
1635 /* If no width/height caps nego did not happen window will be created
1636 during caps nego then */
1637 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1638 xwindow = gst_x_image_sink_xwindow_new (ximagesink,
1639 GST_VIDEO_SINK_WIDTH (ximagesink),
1640 GST_VIDEO_SINK_HEIGHT (ximagesink));
1643 xwindow = g_new0 (GstXWindow, 1);
1645 xwindow->win = xwindow_id;
1647 /* We set the events we want to receive and create a GC. */
1648 g_mutex_lock (&ximagesink->x_lock);
1649 xwindow->internal = FALSE;
1650 if (ximagesink->handle_events) {
1651 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1652 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1656 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1657 g_mutex_unlock (&ximagesink->x_lock);
1661 ximagesink->xwindow = xwindow;
1662 /* Update the window geometry, possibly generating a reconfigure event. */
1663 gst_x_image_sink_xwindow_update_geometry (ximagesink);
1666 g_mutex_unlock (&ximagesink->flow_lock);
1670 gst_x_image_sink_expose (GstVideoOverlay * overlay)
1672 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1674 gst_x_image_sink_xwindow_update_geometry (ximagesink);
1675 gst_x_image_sink_ximage_put (ximagesink, NULL);
1679 gst_x_image_sink_set_event_handling (GstVideoOverlay * overlay,
1680 gboolean handle_events)
1682 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1684 ximagesink->handle_events = handle_events;
1686 g_mutex_lock (&ximagesink->flow_lock);
1688 if (G_UNLIKELY (!ximagesink->xwindow)) {
1689 g_mutex_unlock (&ximagesink->flow_lock);
1693 g_mutex_lock (&ximagesink->x_lock);
1695 if (handle_events) {
1696 if (ximagesink->xwindow->internal) {
1697 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1698 ExposureMask | StructureNotifyMask | PointerMotionMask |
1699 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1701 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1702 ExposureMask | StructureNotifyMask | PointerMotionMask |
1703 KeyPressMask | KeyReleaseMask);
1706 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1709 g_mutex_unlock (&ximagesink->x_lock);
1711 g_mutex_unlock (&ximagesink->flow_lock);
1715 gst_x_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1717 iface->set_window_handle = gst_x_image_sink_set_window_handle;
1718 iface->expose = gst_x_image_sink_expose;
1719 iface->handle_events = gst_x_image_sink_set_event_handling;
1722 /* =========================================== */
1724 /* Init & Class init */
1726 /* =========================================== */
1729 gst_x_image_sink_set_property (GObject * object, guint prop_id,
1730 const GValue * value, GParamSpec * pspec)
1732 GstXImageSink *ximagesink;
1734 g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
1736 ximagesink = GST_X_IMAGE_SINK (object);
1740 ximagesink->display_name = g_strdup (g_value_get_string (value));
1742 case PROP_SYNCHRONOUS:
1743 ximagesink->synchronous = g_value_get_boolean (value);
1744 if (ximagesink->xcontext) {
1745 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1746 ximagesink->synchronous ? "TRUE" : "FALSE");
1747 g_mutex_lock (&ximagesink->x_lock);
1748 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1749 g_mutex_unlock (&ximagesink->x_lock);
1752 case PROP_FORCE_ASPECT_RATIO:
1753 ximagesink->keep_aspect = g_value_get_boolean (value);
1755 case PROP_PIXEL_ASPECT_RATIO:
1759 tmp = g_new0 (GValue, 1);
1760 g_value_init (tmp, GST_TYPE_FRACTION);
1762 if (!g_value_transform (value, tmp)) {
1763 GST_WARNING_OBJECT (ximagesink,
1764 "Could not transform string to aspect ratio");
1767 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1768 gst_value_get_fraction_numerator (tmp),
1769 gst_value_get_fraction_denominator (tmp));
1770 g_free (ximagesink->par);
1771 ximagesink->par = tmp;
1775 case PROP_HANDLE_EVENTS:
1776 gst_x_image_sink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1777 g_value_get_boolean (value));
1778 gst_x_image_sink_manage_event_thread (ximagesink);
1780 case PROP_HANDLE_EXPOSE:
1781 ximagesink->handle_expose = g_value_get_boolean (value);
1782 gst_x_image_sink_manage_event_thread (ximagesink);
1785 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1791 gst_x_image_sink_get_property (GObject * object, guint prop_id,
1792 GValue * value, GParamSpec * pspec)
1794 GstXImageSink *ximagesink;
1796 g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
1798 ximagesink = GST_X_IMAGE_SINK (object);
1802 g_value_set_string (value, ximagesink->display_name);
1804 case PROP_SYNCHRONOUS:
1805 g_value_set_boolean (value, ximagesink->synchronous);
1807 case PROP_FORCE_ASPECT_RATIO:
1808 g_value_set_boolean (value, ximagesink->keep_aspect);
1810 case PROP_PIXEL_ASPECT_RATIO:
1811 if (ximagesink->par)
1812 g_value_transform (ximagesink->par, value);
1814 case PROP_HANDLE_EVENTS:
1815 g_value_set_boolean (value, ximagesink->handle_events);
1817 case PROP_HANDLE_EXPOSE:
1818 g_value_set_boolean (value, ximagesink->handle_expose);
1820 case PROP_WINDOW_WIDTH:
1821 if (ximagesink->xwindow)
1822 g_value_set_uint64 (value, ximagesink->xwindow->width);
1824 g_value_set_uint64 (value, 0);
1826 case PROP_WINDOW_HEIGHT:
1827 if (ximagesink->xwindow)
1828 g_value_set_uint64 (value, ximagesink->xwindow->height);
1830 g_value_set_uint64 (value, 0);
1833 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1839 gst_x_image_sink_reset (GstXImageSink * ximagesink)
1843 GST_OBJECT_LOCK (ximagesink);
1844 ximagesink->running = FALSE;
1845 /* grab thread and mark it as NULL */
1846 thread = ximagesink->event_thread;
1847 ximagesink->event_thread = NULL;
1848 GST_OBJECT_UNLOCK (ximagesink);
1850 /* Wait for our event thread to finish before we clean up our stuff. */
1852 g_thread_join (thread);
1854 if (ximagesink->cur_image) {
1855 gst_buffer_unref (ximagesink->cur_image);
1856 ximagesink->cur_image = NULL;
1859 g_mutex_lock (&ximagesink->flow_lock);
1861 if (ximagesink->pool) {
1862 gst_object_unref (ximagesink->pool);
1863 ximagesink->pool = NULL;
1866 if (ximagesink->xwindow) {
1867 gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
1868 gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1869 ximagesink->xwindow = NULL;
1871 g_mutex_unlock (&ximagesink->flow_lock);
1873 gst_x_image_sink_xcontext_clear (ximagesink);
1877 gst_x_image_sink_finalize (GObject * object)
1879 GstXImageSink *ximagesink;
1881 ximagesink = GST_X_IMAGE_SINK (object);
1883 gst_x_image_sink_reset (ximagesink);
1885 if (ximagesink->display_name) {
1886 g_free (ximagesink->display_name);
1887 ximagesink->display_name = NULL;
1889 if (ximagesink->par) {
1890 g_free (ximagesink->par);
1891 ximagesink->par = NULL;
1893 g_mutex_clear (&ximagesink->x_lock);
1894 g_mutex_clear (&ximagesink->flow_lock);
1896 g_free (ximagesink->media_title);
1898 G_OBJECT_CLASS (parent_class)->finalize (object);
1902 gst_x_image_sink_init (GstXImageSink * ximagesink)
1904 ximagesink->display_name = NULL;
1905 ximagesink->xcontext = NULL;
1906 ximagesink->xwindow = NULL;
1907 ximagesink->cur_image = NULL;
1909 ximagesink->event_thread = NULL;
1910 ximagesink->running = FALSE;
1912 ximagesink->fps_n = 0;
1913 ximagesink->fps_d = 1;
1915 g_mutex_init (&ximagesink->x_lock);
1916 g_mutex_init (&ximagesink->flow_lock);
1918 ximagesink->par = NULL;
1920 ximagesink->pool = NULL;
1922 ximagesink->synchronous = FALSE;
1923 ximagesink->keep_aspect = TRUE;
1924 ximagesink->handle_events = TRUE;
1925 ximagesink->handle_expose = TRUE;
1929 gst_x_image_sink_class_init (GstXImageSinkClass * klass)
1931 GObjectClass *gobject_class;
1932 GstElementClass *gstelement_class;
1933 GstBaseSinkClass *gstbasesink_class;
1934 GstVideoSinkClass *videosink_class;
1936 gobject_class = (GObjectClass *) klass;
1937 gstelement_class = (GstElementClass *) klass;
1938 gstbasesink_class = (GstBaseSinkClass *) klass;
1939 videosink_class = (GstVideoSinkClass *) klass;
1941 gobject_class->finalize = gst_x_image_sink_finalize;
1942 gobject_class->set_property = gst_x_image_sink_set_property;
1943 gobject_class->get_property = gst_x_image_sink_get_property;
1945 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1946 g_param_spec_string ("display", "Display", "X Display name",
1947 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1948 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1949 g_param_spec_boolean ("synchronous", "Synchronous",
1950 "When enabled, runs the X display in synchronous mode. "
1951 "(unrelated to A/V sync, used only for debugging)", FALSE,
1952 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1953 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1954 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1955 "When enabled, reverse caps negotiation (scaling) will respect "
1956 "original aspect ratio", TRUE,
1957 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1958 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1959 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1960 "The pixel aspect ratio of the device", "1/1",
1961 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1962 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1963 g_param_spec_boolean ("handle-events", "Handle XEvents",
1964 "When enabled, XEvents will be selected and handled", TRUE,
1965 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1966 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1967 g_param_spec_boolean ("handle-expose", "Handle expose",
1969 "the current frame will always be drawn in response to X Expose "
1970 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1973 * GstXImageSink:window-width
1975 * Actual width of the video window.
1977 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1978 g_param_spec_uint64 ("window-width", "window-width",
1979 "Width of the window", 0, G_MAXUINT64, 0,
1980 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1983 * GstXImageSink:window-height
1985 * Actual height of the video window.
1987 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1988 g_param_spec_uint64 ("window-height", "window-height",
1989 "Height of the window", 0, G_MAXUINT64, 0,
1990 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1992 gst_element_class_set_static_metadata (gstelement_class,
1993 "Video sink", "Sink/Video",
1994 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1996 gst_element_class_add_static_pad_template (gstelement_class,
1997 &gst_x_image_sink_sink_template_factory);
1999 gstelement_class->change_state = gst_x_image_sink_change_state;
2001 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_getcaps);
2002 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_setcaps);
2003 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_x_image_sink_get_times);
2004 gstbasesink_class->propose_allocation =
2005 GST_DEBUG_FUNCPTR (gst_x_image_sink_propose_allocation);
2006 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_x_image_sink_event);
2008 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_x_image_sink_show_frame);