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,
400 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
401 XFree (xproperty.value);
408 hint->res_name = (char *) app_name;
409 hint->res_class = (char *) "GStreamer";
410 XSetClassHint (ximagesink->xcontext->disp, xwindow->win, hint);
417 /* This function handles a GstXWindow creation */
419 gst_x_image_sink_xwindow_new (GstXImageSink * ximagesink, gint width,
422 GstXWindow *xwindow = NULL;
425 g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
427 xwindow = g_new0 (GstXWindow, 1);
429 xwindow->width = width;
430 xwindow->height = height;
431 xwindow->internal = TRUE;
433 g_mutex_lock (&ximagesink->x_lock);
435 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
436 ximagesink->xcontext->root,
437 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
439 /* We have to do that to prevent X from redrawing the background on
440 ConfigureNotify. This takes away flickering of video when resizing. */
441 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
443 /* set application name as a title */
444 gst_x_image_sink_xwindow_set_title (ximagesink, xwindow, NULL);
446 if (ximagesink->handle_events) {
449 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
450 StructureNotifyMask | PointerMotionMask | KeyPressMask |
451 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
453 /* Tell the window manager we'd like delete client messages instead of
455 wm_delete = XInternAtom (ximagesink->xcontext->disp,
456 "WM_DELETE_WINDOW", False);
457 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
461 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
464 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
466 XSync (ximagesink->xcontext->disp, FALSE);
468 g_mutex_unlock (&ximagesink->x_lock);
470 gst_x_image_sink_xwindow_decorate (ximagesink, xwindow);
472 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (ximagesink),
478 /* This function destroys a GstXWindow */
480 gst_x_image_sink_xwindow_destroy (GstXImageSink * ximagesink,
481 GstXWindow * xwindow)
483 g_return_if_fail (xwindow != NULL);
484 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
486 g_mutex_lock (&ximagesink->x_lock);
488 /* If we did not create that window we just free the GC and let it live */
489 if (xwindow->internal)
490 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
492 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
494 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
496 XSync (ximagesink->xcontext->disp, FALSE);
498 g_mutex_unlock (&ximagesink->x_lock);
504 gst_x_image_sink_xwindow_update_geometry (GstXImageSink * ximagesink)
506 XWindowAttributes attr;
507 gboolean reconfigure;
509 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
511 /* Update the window geometry */
512 g_mutex_lock (&ximagesink->x_lock);
513 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
514 g_mutex_unlock (&ximagesink->x_lock);
518 XGetWindowAttributes (ximagesink->xcontext->disp,
519 ximagesink->xwindow->win, &attr);
521 /* Check if we would suggest a different width/height now */
522 reconfigure = (ximagesink->xwindow->width != attr.width)
523 || (ximagesink->xwindow->height != attr.height);
524 ximagesink->xwindow->width = attr.width;
525 ximagesink->xwindow->height = attr.height;
527 g_mutex_unlock (&ximagesink->x_lock);
530 gst_pad_push_event (GST_BASE_SINK (ximagesink)->sinkpad,
531 gst_event_new_reconfigure ());
535 gst_x_image_sink_xwindow_clear (GstXImageSink * ximagesink,
536 GstXWindow * xwindow)
538 g_return_if_fail (xwindow != NULL);
539 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
541 g_mutex_lock (&ximagesink->x_lock);
543 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
544 ximagesink->xcontext->black);
546 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
547 0, 0, xwindow->width, xwindow->height);
549 XSync (ximagesink->xcontext->disp, FALSE);
551 g_mutex_unlock (&ximagesink->x_lock);
554 /* This function handles XEvents that might be in the queue. It generates
555 GstEvent that will be sent upstream in the pipeline to handle interactivity
558 gst_x_image_sink_handle_xevents (GstXImageSink * ximagesink)
561 gint pointer_x = 0, pointer_y = 0;
562 gboolean pointer_moved = FALSE;
563 gboolean exposed = FALSE, configured = FALSE;
565 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
567 /* Then we get all pointer motion events, only the last position is
569 g_mutex_lock (&ximagesink->flow_lock);
570 g_mutex_lock (&ximagesink->x_lock);
571 while (XCheckWindowEvent (ximagesink->xcontext->disp,
572 ximagesink->xwindow->win, PointerMotionMask, &e)) {
573 g_mutex_unlock (&ximagesink->x_lock);
574 g_mutex_unlock (&ximagesink->flow_lock);
578 pointer_x = e.xmotion.x;
579 pointer_y = e.xmotion.y;
580 pointer_moved = TRUE;
585 g_mutex_lock (&ximagesink->flow_lock);
586 g_mutex_lock (&ximagesink->x_lock);
590 g_mutex_unlock (&ximagesink->x_lock);
591 g_mutex_unlock (&ximagesink->flow_lock);
593 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
594 pointer_x, pointer_y);
595 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
596 "mouse-move", 0, pointer_x, pointer_y);
598 g_mutex_lock (&ximagesink->flow_lock);
599 g_mutex_lock (&ximagesink->x_lock);
602 /* We get all remaining events on our window to throw them upstream */
603 while (XCheckWindowEvent (ximagesink->xcontext->disp,
604 ximagesink->xwindow->win,
605 KeyPressMask | KeyReleaseMask |
606 ButtonPressMask | ButtonReleaseMask, &e)) {
608 const char *key_str = NULL;
610 /* We lock only for the X function call */
611 g_mutex_unlock (&ximagesink->x_lock);
612 g_mutex_unlock (&ximagesink->flow_lock);
616 /* Mouse button pressed/released over our window. We send upstream
617 events for interactivity/navigation */
618 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
619 e.xbutton.button, e.xbutton.x, e.xbutton.x);
620 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
621 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
624 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
625 e.xbutton.button, e.xbutton.x, e.xbutton.x);
626 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
627 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
631 /* Key pressed/released over our window. We send upstream
632 events for interactivity/navigation */
633 g_mutex_lock (&ximagesink->x_lock);
634 if (ximagesink->xcontext->use_xkb) {
635 keysym = XkbKeycodeToKeysym (ximagesink->xcontext->disp,
636 e.xkey.keycode, 0, 0);
638 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
641 if (keysym != NoSymbol) {
642 key_str = XKeysymToString (keysym);
646 g_mutex_unlock (&ximagesink->x_lock);
647 GST_DEBUG_OBJECT (ximagesink,
648 "key %d pressed over window at %d,%d (%s)",
649 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
650 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
651 e.type == KeyPress ? "key-press" : "key-release", key_str);
654 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
657 g_mutex_lock (&ximagesink->flow_lock);
658 g_mutex_lock (&ximagesink->x_lock);
662 while (XCheckWindowEvent (ximagesink->xcontext->disp,
663 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
668 case ConfigureNotify:
669 g_mutex_unlock (&ximagesink->x_lock);
670 gst_x_image_sink_xwindow_update_geometry (ximagesink);
671 g_mutex_lock (&ximagesink->x_lock);
679 if (ximagesink->handle_expose && (exposed || configured)) {
680 g_mutex_unlock (&ximagesink->x_lock);
681 g_mutex_unlock (&ximagesink->flow_lock);
683 gst_x_image_sink_expose (GST_VIDEO_OVERLAY (ximagesink));
685 g_mutex_lock (&ximagesink->flow_lock);
686 g_mutex_lock (&ximagesink->x_lock);
689 /* Handle Display events */
690 while (XPending (ximagesink->xcontext->disp)) {
691 XNextEvent (ximagesink->xcontext->disp, &e);
697 wm_delete = XInternAtom (ximagesink->xcontext->disp,
698 "WM_DELETE_WINDOW", False);
699 if (wm_delete == (Atom) e.xclient.data.l[0]) {
700 /* Handle window deletion by posting an error on the bus */
701 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
702 ("Output window was closed"), (NULL));
704 g_mutex_unlock (&ximagesink->x_lock);
705 gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
706 ximagesink->xwindow = NULL;
707 g_mutex_lock (&ximagesink->x_lock);
716 g_mutex_unlock (&ximagesink->x_lock);
717 g_mutex_unlock (&ximagesink->flow_lock);
721 gst_x_image_sink_event_thread (GstXImageSink * ximagesink)
723 g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
725 GST_OBJECT_LOCK (ximagesink);
726 while (ximagesink->running) {
727 GST_OBJECT_UNLOCK (ximagesink);
729 if (ximagesink->xwindow) {
730 gst_x_image_sink_handle_xevents (ximagesink);
732 /* FIXME: do we want to align this with the framerate or anything else? */
733 g_usleep (G_USEC_PER_SEC / 20);
735 GST_OBJECT_LOCK (ximagesink);
737 GST_OBJECT_UNLOCK (ximagesink);
743 gst_x_image_sink_manage_event_thread (GstXImageSink * ximagesink)
745 GThread *thread = NULL;
747 /* don't start the thread too early */
748 if (ximagesink->xcontext == NULL) {
752 GST_OBJECT_LOCK (ximagesink);
753 if (ximagesink->handle_expose || ximagesink->handle_events) {
754 if (!ximagesink->event_thread) {
755 /* Setup our event listening thread */
756 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
757 ximagesink->handle_expose, ximagesink->handle_events);
758 ximagesink->running = TRUE;
759 ximagesink->event_thread = g_thread_try_new ("ximagesink-events",
760 (GThreadFunc) gst_x_image_sink_event_thread, ximagesink, NULL);
763 if (ximagesink->event_thread) {
764 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
765 ximagesink->handle_expose, ximagesink->handle_events);
766 ximagesink->running = FALSE;
767 /* grab thread and mark it as NULL */
768 thread = ximagesink->event_thread;
769 ximagesink->event_thread = NULL;
772 GST_OBJECT_UNLOCK (ximagesink);
774 /* Wait for our event thread to finish */
776 g_thread_join (thread);
781 /* This function calculates the pixel aspect ratio based on the properties
782 * in the xcontext structure and stores it there. */
784 gst_x_image_sink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
786 static const gint par[][2] = {
787 {1, 1}, /* regular screen */
788 {16, 15}, /* PAL TV */
789 {11, 10}, /* 525 line Rec.601 video */
790 {54, 59}, /* 625 line Rec.601 video */
791 {64, 45}, /* 1280x1024 on 16:9 display */
792 {5, 3}, /* 1280x1024 on 4:3 display */
793 {4, 3} /* 800x600 on 16:9 display */
800 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
802 /* first calculate the "real" ratio based on the X values;
803 * which is the "physical" w/h divided by the w/h in pixels of the display */
804 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
805 / (xcontext->heightmm * xcontext->width);
807 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
809 if (xcontext->width == 720 && xcontext->height == 576) {
810 ratio = 4.0 * 576 / (3.0 * 720);
812 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
814 /* now find the one from par[][2] with the lowest delta to the real one */
818 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
819 gdouble this_delta = DELTA (i);
821 if (this_delta < delta) {
827 GST_DEBUG ("Decided on index %d (%d/%d)", index,
828 par[index][0], par[index][1]);
830 g_free (xcontext->par);
831 xcontext->par = g_new0 (GValue, 1);
832 g_value_init (xcontext->par, GST_TYPE_FRACTION);
833 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
834 GST_DEBUG ("set xcontext PAR to %d/%d",
835 gst_value_get_fraction_numerator (xcontext->par),
836 gst_value_get_fraction_denominator (xcontext->par));
839 /* This function gets the X Display and global info about it. Everything is
840 stored in our object and will be cleaned when the object is disposed. Note
841 here that caps for supported format are generated without any window or
844 gst_x_image_sink_xcontext_get (GstXImageSink * ximagesink)
846 GstXContext *xcontext = NULL;
847 XPixmapFormatValues *px_formats = NULL;
848 gint nb_formats = 0, i;
850 GstVideoFormat vformat;
852 int opcode, event, err;
853 int major = XkbMajorVersion;
854 int minor = XkbMinorVersion;
856 g_return_val_if_fail (GST_IS_X_IMAGE_SINK (ximagesink), NULL);
858 xcontext = g_new0 (GstXContext, 1);
860 g_mutex_lock (&ximagesink->x_lock);
862 xcontext->disp = XOpenDisplay (ximagesink->display_name);
864 if (!xcontext->disp) {
865 g_mutex_unlock (&ximagesink->x_lock);
867 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
868 ("Could not initialise X output"), ("Could not open display"));
872 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
873 xcontext->screen_num = DefaultScreen (xcontext->disp);
874 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
875 xcontext->root = DefaultRootWindow (xcontext->disp);
876 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
877 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
878 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
880 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
881 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
882 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
883 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
885 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
886 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
888 gst_x_image_sink_calculate_pixel_aspect_ratio (xcontext);
890 /* We get supported pixmap formats at supported depth */
891 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
894 XCloseDisplay (xcontext->disp);
895 g_mutex_unlock (&ximagesink->x_lock);
896 g_free (xcontext->par);
898 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
899 ("Could not get supported pixmap formats"), (NULL));
903 /* We get bpp value corresponding to our running depth */
904 for (i = 0; i < nb_formats; i++) {
905 if (px_formats[i].depth == xcontext->depth)
906 xcontext->bpp = px_formats[i].bits_per_pixel;
911 endianness = (ImageByteOrder (xcontext->disp) ==
912 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
914 /* Search for XShm extension support */
916 if (XShmQueryExtension (xcontext->disp) &&
917 gst_x_image_sink_check_xshm_calls (ximagesink, xcontext)) {
918 xcontext->use_xshm = TRUE;
919 GST_DEBUG ("ximagesink is using XShm extension");
921 #endif /* HAVE_XSHM */
923 xcontext->use_xshm = FALSE;
924 GST_DEBUG ("ximagesink is not using XShm extension");
926 if (XkbQueryExtension (xcontext->disp, &opcode, &event, &err, &major, &minor)) {
927 xcontext->use_xkb = TRUE;
928 GST_DEBUG ("ximagesink is using Xkb extension");
930 xcontext->use_xkb = FALSE;
931 GST_DEBUG ("ximagesink is not using Xkb extension");
934 /* extrapolate alpha mask */
935 if (xcontext->depth == 32) {
936 alpha_mask = ~(xcontext->visual->red_mask
937 | xcontext->visual->green_mask | xcontext->visual->blue_mask);
943 gst_video_format_from_masks (xcontext->depth, xcontext->bpp, endianness,
944 xcontext->visual->red_mask, xcontext->visual->green_mask,
945 xcontext->visual->blue_mask, alpha_mask);
947 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
950 /* update object's par with calculated one if not set yet */
951 if (!ximagesink->par) {
952 ximagesink->par = g_new0 (GValue, 1);
953 gst_value_init_and_copy (ximagesink->par, xcontext->par);
954 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
956 xcontext->caps = gst_caps_new_simple ("video/x-raw",
957 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
958 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
959 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
960 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
961 if (ximagesink->par) {
964 nom = gst_value_get_fraction_numerator (ximagesink->par);
965 den = gst_value_get_fraction_denominator (ximagesink->par);
966 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
967 GST_TYPE_FRACTION, nom, den, NULL);
970 g_mutex_unlock (&ximagesink->x_lock);
977 GST_ERROR_OBJECT (ximagesink, "unknown format");
982 /* This function cleans the X context. Closing the Display and unrefing the
983 caps for supported formats. */
985 gst_x_image_sink_xcontext_clear (GstXImageSink * ximagesink)
987 GstXContext *xcontext;
989 g_return_if_fail (GST_IS_X_IMAGE_SINK (ximagesink));
991 GST_OBJECT_LOCK (ximagesink);
992 if (ximagesink->xcontext == NULL) {
993 GST_OBJECT_UNLOCK (ximagesink);
997 /* Take the xcontext reference and NULL it while we
998 * clean it up, so that any buffer-alloced buffers
999 * arriving after this will be freed correctly */
1000 xcontext = ximagesink->xcontext;
1001 ximagesink->xcontext = NULL;
1003 GST_OBJECT_UNLOCK (ximagesink);
1005 gst_caps_unref (xcontext->caps);
1006 g_free (xcontext->par);
1007 g_free (ximagesink->par);
1008 ximagesink->par = NULL;
1010 if (xcontext->last_caps)
1011 gst_caps_replace (&xcontext->last_caps, NULL);
1013 g_mutex_lock (&ximagesink->x_lock);
1015 XCloseDisplay (xcontext->disp);
1017 g_mutex_unlock (&ximagesink->x_lock);
1025 gst_x_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1027 GstXImageSink *ximagesink;
1031 ximagesink = GST_X_IMAGE_SINK (bsink);
1033 g_mutex_lock (&ximagesink->x_lock);
1034 if (ximagesink->xcontext) {
1037 caps = gst_caps_ref (ximagesink->xcontext->caps);
1040 GstCaps *intersection;
1043 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1044 gst_caps_unref (caps);
1045 caps = intersection;
1048 if (gst_caps_is_empty (caps)) {
1049 g_mutex_unlock (&ximagesink->x_lock);
1053 if (ximagesink->xwindow && ximagesink->xwindow->width) {
1054 GstStructure *s0, *s1;
1056 caps = gst_caps_make_writable (caps);
1058 /* There can only be a single structure because the xcontext
1059 * caps only have a single structure */
1060 s0 = gst_caps_get_structure (caps, 0);
1061 s1 = gst_structure_copy (gst_caps_get_structure (caps, 0));
1063 gst_structure_set (s0, "width", G_TYPE_INT, ximagesink->xwindow->width,
1064 "height", G_TYPE_INT, ximagesink->xwindow->height, NULL);
1065 gst_caps_append_structure (caps, s1);
1067 /* This will not change the order but will remove the
1068 * fixed width/height caps again if not possible
1071 GstCaps *intersection;
1074 gst_caps_intersect_full (caps, filter, GST_CAPS_INTERSECT_FIRST);
1075 gst_caps_unref (caps);
1076 caps = intersection;
1080 g_mutex_unlock (&ximagesink->x_lock);
1083 g_mutex_unlock (&ximagesink->x_lock);
1085 /* get a template copy and add the pixel aspect ratio */
1086 caps = gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->sinkpad);
1087 if (ximagesink->par) {
1088 caps = gst_caps_make_writable (caps);
1089 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1090 GstStructure *structure = gst_caps_get_structure (caps, i);
1093 nom = gst_value_get_fraction_numerator (ximagesink->par);
1094 den = gst_value_get_fraction_denominator (ximagesink->par);
1095 gst_structure_set (structure, "pixel-aspect-ratio",
1096 GST_TYPE_FRACTION, nom, den, NULL);
1101 GstCaps *intersection;
1104 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1105 gst_caps_unref (caps);
1106 caps = intersection;
1112 static GstBufferPool *
1113 gst_x_image_sink_create_pool (GstXImageSink * ximagesink, GstCaps * caps,
1114 gsize size, gint min)
1116 static GstAllocationParams params = { 0, 15, 0, 0, };
1117 GstBufferPool *pool;
1118 GstStructure *config;
1120 /* create a new pool for the new configuration */
1121 pool = gst_ximage_buffer_pool_new (ximagesink);
1123 config = gst_buffer_pool_get_config (pool);
1124 gst_buffer_pool_config_set_params (config, caps, size, min, 0);
1125 gst_buffer_pool_config_set_allocator (config, NULL, ¶ms);
1127 if (!gst_buffer_pool_set_config (pool, config))
1134 GST_WARNING_OBJECT (ximagesink, "failed setting config");
1135 gst_object_unref (pool);
1141 gst_x_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1143 GstXImageSink *ximagesink;
1144 GstStructure *structure;
1146 GstBufferPool *newpool, *oldpool;
1149 ximagesink = GST_X_IMAGE_SINK (bsink);
1151 if (!ximagesink->xcontext)
1154 GST_DEBUG_OBJECT (ximagesink,
1155 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1156 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1158 /* We intersect those caps with our template to make sure they are correct */
1159 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1160 goto incompatible_caps;
1162 if (!gst_video_info_from_caps (&info, caps))
1163 goto invalid_format;
1165 structure = gst_caps_get_structure (caps, 0);
1166 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1167 * otherwise linking should fail */
1168 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1170 if (ximagesink->par) {
1171 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1174 } else if (ximagesink->xcontext->par) {
1175 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1181 GST_VIDEO_SINK_WIDTH (ximagesink) = info.width;
1182 GST_VIDEO_SINK_HEIGHT (ximagesink) = info.height;
1183 ximagesink->fps_n = info.fps_n;
1184 ximagesink->fps_d = info.fps_d;
1186 /* Notify application to set xwindow id now */
1187 g_mutex_lock (&ximagesink->flow_lock);
1188 if (!ximagesink->xwindow) {
1189 g_mutex_unlock (&ximagesink->flow_lock);
1190 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (ximagesink));
1192 g_mutex_unlock (&ximagesink->flow_lock);
1195 /* Creating our window and our image */
1196 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1197 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1200 g_mutex_lock (&ximagesink->flow_lock);
1201 if (!ximagesink->xwindow) {
1202 ximagesink->xwindow = gst_x_image_sink_xwindow_new (ximagesink,
1203 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1206 ximagesink->info = info;
1208 /* Remember to draw borders for next frame */
1209 ximagesink->draw_border = TRUE;
1211 /* create a new internal pool for the new configuration */
1212 newpool = gst_x_image_sink_create_pool (ximagesink, caps, info.size, 2);
1214 /* we don't activate the internal pool yet as it may not be needed */
1215 oldpool = ximagesink->pool;
1216 ximagesink->pool = newpool;
1217 g_mutex_unlock (&ximagesink->flow_lock);
1219 /* deactivate and unref the old internal pool */
1221 gst_buffer_pool_set_active (oldpool, FALSE);
1222 gst_object_unref (oldpool);
1230 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1235 GST_ERROR_OBJECT (ximagesink, "caps invalid");
1240 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1245 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1246 ("Invalid image size."));
1251 static GstStateChangeReturn
1252 gst_x_image_sink_change_state (GstElement * element, GstStateChange transition)
1254 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1255 GstXImageSink *ximagesink;
1256 GstXContext *xcontext = NULL;
1258 ximagesink = GST_X_IMAGE_SINK (element);
1260 switch (transition) {
1261 case GST_STATE_CHANGE_NULL_TO_READY:
1262 /* Initializing the XContext */
1263 if (ximagesink->xcontext == NULL) {
1264 xcontext = gst_x_image_sink_xcontext_get (ximagesink);
1265 if (xcontext == NULL) {
1266 ret = GST_STATE_CHANGE_FAILURE;
1269 GST_OBJECT_LOCK (ximagesink);
1271 ximagesink->xcontext = xcontext;
1272 GST_OBJECT_UNLOCK (ximagesink);
1275 /* call XSynchronize with the current value of synchronous */
1276 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1277 ximagesink->synchronous ? "TRUE" : "FALSE");
1278 g_mutex_lock (&ximagesink->x_lock);
1279 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1280 g_mutex_unlock (&ximagesink->x_lock);
1281 gst_x_image_sink_manage_event_thread (ximagesink);
1283 case GST_STATE_CHANGE_READY_TO_PAUSED:
1284 g_mutex_lock (&ximagesink->flow_lock);
1285 if (ximagesink->xwindow)
1286 gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
1287 g_mutex_unlock (&ximagesink->flow_lock);
1289 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1295 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1297 switch (transition) {
1298 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1300 case GST_STATE_CHANGE_PAUSED_TO_READY:
1301 ximagesink->fps_n = 0;
1302 ximagesink->fps_d = 1;
1303 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1304 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1305 g_mutex_lock (&ximagesink->flow_lock);
1306 if (ximagesink->pool)
1307 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1308 g_mutex_unlock (&ximagesink->flow_lock);
1310 case GST_STATE_CHANGE_READY_TO_NULL:
1311 gst_x_image_sink_reset (ximagesink);
1322 gst_x_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1323 GstClockTime * start, GstClockTime * end)
1325 GstXImageSink *ximagesink;
1327 ximagesink = GST_X_IMAGE_SINK (bsink);
1329 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1330 *start = GST_BUFFER_TIMESTAMP (buf);
1331 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1332 *end = *start + GST_BUFFER_DURATION (buf);
1334 if (ximagesink->fps_n > 0) {
1336 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1343 static GstFlowReturn
1344 gst_x_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1347 GstXImageSink *ximagesink;
1348 GstXImageMemory *mem;
1349 GstBuffer *to_put = NULL;
1351 ximagesink = GST_X_IMAGE_SINK (vsink);
1353 if (gst_buffer_n_memory (buf) == 1
1354 && (mem = (GstXImageMemory *) gst_buffer_peek_memory (buf, 0))
1355 && g_strcmp0 (mem->parent.allocator->mem_type, "ximage") == 0
1356 && mem->sink == ximagesink) {
1357 /* If this buffer has been allocated using our buffer management we simply
1358 put the ximage which is in the PRIVATE pointer */
1359 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1363 GstVideoFrame src, dest;
1364 GstBufferPoolAcquireParams params = { 0, };
1366 /* Else we have to copy the data into our private image, */
1367 /* if we have one... */
1368 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1370 /* an internal pool should have been created in setcaps */
1371 if (G_UNLIKELY (ximagesink->pool == NULL))
1374 if (!gst_buffer_pool_set_active (ximagesink->pool, TRUE))
1375 goto activate_failed;
1377 /* take a buffer from our pool, if there is no buffer in the pool something
1378 * is seriously wrong, waiting for the pool here might deadlock when we try
1379 * to go to PAUSED because we never flush the pool. */
1380 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1381 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &to_put, ¶ms);
1382 if (res != GST_FLOW_OK)
1385 GST_CAT_LOG_OBJECT (CAT_PERFORMANCE, ximagesink,
1386 "slow copy into bufferpool buffer %p", to_put);
1388 if (!gst_video_frame_map (&src, &ximagesink->info, buf, GST_MAP_READ))
1389 goto invalid_buffer;
1391 if (!gst_video_frame_map (&dest, &ximagesink->info, to_put, GST_MAP_WRITE)) {
1392 gst_video_frame_unmap (&src);
1393 goto invalid_buffer;
1396 gst_video_frame_copy (&dest, &src);
1398 gst_video_frame_unmap (&dest);
1399 gst_video_frame_unmap (&src);
1402 if (!gst_x_image_sink_ximage_put (ximagesink, to_put))
1407 gst_buffer_unref (to_put);
1414 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1415 ("Internal error: can't allocate images"),
1416 ("We don't have a bufferpool negotiated"));
1417 return GST_FLOW_ERROR;
1421 /* No image available. That's very bad ! */
1422 GST_WARNING_OBJECT (ximagesink, "could not create image");
1427 /* No Window available to put our image into */
1428 GST_WARNING_OBJECT (ximagesink, "could not map image");
1434 /* No Window available to put our image into */
1435 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1436 res = GST_FLOW_ERROR;
1441 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1442 res = GST_FLOW_ERROR;
1448 gst_x_image_sink_event (GstBaseSink * sink, GstEvent * event)
1450 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (sink);
1452 switch (GST_EVENT_TYPE (event)) {
1453 case GST_EVENT_TAG:{
1455 gchar *title = NULL;
1457 gst_event_parse_tag (event, &l);
1458 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1461 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1462 gst_x_image_sink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1472 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1476 gst_x_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1478 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (bsink);
1479 GstBufferPool *pool = NULL;
1485 gst_query_parse_allocation (query, &caps, &need_pool);
1490 if (!gst_video_info_from_caps (&info, caps))
1493 /* the normal size of a frame */
1497 pool = gst_x_image_sink_create_pool (ximagesink, caps, info.size, 0);
1503 /* we need at least 2 buffer because we hold on to the last one */
1504 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1506 gst_object_unref (pool);
1508 /* we also support various metadata */
1509 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1510 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1517 GST_DEBUG_OBJECT (bsink, "no caps specified");
1522 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1527 /* Already warned in create_pool */
1532 /* Interfaces stuff */
1534 gst_x_image_sink_navigation_send_event (GstNavigation * navigation,
1535 GstStructure * structure)
1537 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (navigation);
1538 GstEvent *event = NULL;
1539 gint x_offset, y_offset;
1541 gboolean handled = FALSE;
1543 /* We are not converting the pointer coordinates as there's no hardware
1544 scaling done here. The only possible scaling is done by videoscale and
1545 videoscale will have to catch those events and transform the coordinates
1546 to match the applied scaling. So here we just add the offset if the image
1547 is centered in the window. */
1549 /* We take the flow_lock while we look at the window */
1550 g_mutex_lock (&ximagesink->flow_lock);
1552 if (!ximagesink->xwindow) {
1553 g_mutex_unlock (&ximagesink->flow_lock);
1554 gst_structure_free (structure);
1558 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1559 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1561 g_mutex_unlock (&ximagesink->flow_lock);
1563 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1565 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1567 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1569 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1572 event = gst_event_new_navigation (structure);
1574 gst_event_ref (event);
1575 handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (ximagesink), event);
1578 gst_element_post_message (GST_ELEMENT_CAST (ximagesink),
1579 gst_navigation_message_new_event (GST_OBJECT_CAST (ximagesink),
1582 gst_event_unref (event);
1587 gst_x_image_sink_navigation_init (GstNavigationInterface * iface)
1589 iface->send_event = gst_x_image_sink_navigation_send_event;
1593 gst_x_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1595 XID xwindow_id = id;
1596 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1597 GstXWindow *xwindow = NULL;
1599 /* We acquire the stream lock while setting this window in the element.
1600 We are basically cleaning tons of stuff replacing the old window, putting
1601 images while we do that would surely crash */
1602 g_mutex_lock (&ximagesink->flow_lock);
1604 /* If we already use that window return */
1605 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1606 g_mutex_unlock (&ximagesink->flow_lock);
1610 /* If the element has not initialized the X11 context try to do so */
1611 if (!ximagesink->xcontext &&
1612 !(ximagesink->xcontext = gst_x_image_sink_xcontext_get (ximagesink))) {
1613 g_mutex_unlock (&ximagesink->flow_lock);
1614 /* we have thrown a GST_ELEMENT_ERROR now */
1618 /* If a window is there already we destroy it */
1619 if (ximagesink->xwindow) {
1620 gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1621 ximagesink->xwindow = NULL;
1624 /* If the xid is 0 we go back to an internal window */
1625 if (xwindow_id == 0) {
1626 /* If no width/height caps nego did not happen window will be created
1627 during caps nego then */
1628 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1629 xwindow = gst_x_image_sink_xwindow_new (ximagesink,
1630 GST_VIDEO_SINK_WIDTH (ximagesink),
1631 GST_VIDEO_SINK_HEIGHT (ximagesink));
1634 xwindow = g_new0 (GstXWindow, 1);
1636 xwindow->win = xwindow_id;
1638 /* We set the events we want to receive and create a GC. */
1639 g_mutex_lock (&ximagesink->x_lock);
1640 xwindow->internal = FALSE;
1641 if (ximagesink->handle_events) {
1642 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1643 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1647 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1648 g_mutex_unlock (&ximagesink->x_lock);
1652 ximagesink->xwindow = xwindow;
1653 /* Update the window geometry, possibly generating a reconfigure event. */
1654 gst_x_image_sink_xwindow_update_geometry (ximagesink);
1657 g_mutex_unlock (&ximagesink->flow_lock);
1661 gst_x_image_sink_expose (GstVideoOverlay * overlay)
1663 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1665 gst_x_image_sink_xwindow_update_geometry (ximagesink);
1666 gst_x_image_sink_ximage_put (ximagesink, NULL);
1670 gst_x_image_sink_set_event_handling (GstVideoOverlay * overlay,
1671 gboolean handle_events)
1673 GstXImageSink *ximagesink = GST_X_IMAGE_SINK (overlay);
1675 ximagesink->handle_events = handle_events;
1677 g_mutex_lock (&ximagesink->flow_lock);
1679 if (G_UNLIKELY (!ximagesink->xwindow)) {
1680 g_mutex_unlock (&ximagesink->flow_lock);
1684 g_mutex_lock (&ximagesink->x_lock);
1686 if (handle_events) {
1687 if (ximagesink->xwindow->internal) {
1688 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1689 ExposureMask | StructureNotifyMask | PointerMotionMask |
1690 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1692 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1693 ExposureMask | StructureNotifyMask | PointerMotionMask |
1694 KeyPressMask | KeyReleaseMask);
1697 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1700 g_mutex_unlock (&ximagesink->x_lock);
1702 g_mutex_unlock (&ximagesink->flow_lock);
1706 gst_x_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1708 iface->set_window_handle = gst_x_image_sink_set_window_handle;
1709 iface->expose = gst_x_image_sink_expose;
1710 iface->handle_events = gst_x_image_sink_set_event_handling;
1713 /* =========================================== */
1715 /* Init & Class init */
1717 /* =========================================== */
1720 gst_x_image_sink_set_property (GObject * object, guint prop_id,
1721 const GValue * value, GParamSpec * pspec)
1723 GstXImageSink *ximagesink;
1725 g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
1727 ximagesink = GST_X_IMAGE_SINK (object);
1731 ximagesink->display_name = g_strdup (g_value_get_string (value));
1733 case PROP_SYNCHRONOUS:
1734 ximagesink->synchronous = g_value_get_boolean (value);
1735 if (ximagesink->xcontext) {
1736 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1737 ximagesink->synchronous ? "TRUE" : "FALSE");
1738 g_mutex_lock (&ximagesink->x_lock);
1739 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1740 g_mutex_unlock (&ximagesink->x_lock);
1743 case PROP_FORCE_ASPECT_RATIO:
1744 ximagesink->keep_aspect = g_value_get_boolean (value);
1746 case PROP_PIXEL_ASPECT_RATIO:
1750 tmp = g_new0 (GValue, 1);
1751 g_value_init (tmp, GST_TYPE_FRACTION);
1753 if (!g_value_transform (value, tmp)) {
1754 GST_WARNING_OBJECT (ximagesink,
1755 "Could not transform string to aspect ratio");
1758 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1759 gst_value_get_fraction_numerator (tmp),
1760 gst_value_get_fraction_denominator (tmp));
1761 g_free (ximagesink->par);
1762 ximagesink->par = tmp;
1766 case PROP_HANDLE_EVENTS:
1767 gst_x_image_sink_set_event_handling (GST_VIDEO_OVERLAY (ximagesink),
1768 g_value_get_boolean (value));
1769 gst_x_image_sink_manage_event_thread (ximagesink);
1771 case PROP_HANDLE_EXPOSE:
1772 ximagesink->handle_expose = g_value_get_boolean (value);
1773 gst_x_image_sink_manage_event_thread (ximagesink);
1776 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1782 gst_x_image_sink_get_property (GObject * object, guint prop_id,
1783 GValue * value, GParamSpec * pspec)
1785 GstXImageSink *ximagesink;
1787 g_return_if_fail (GST_IS_X_IMAGE_SINK (object));
1789 ximagesink = GST_X_IMAGE_SINK (object);
1793 g_value_set_string (value, ximagesink->display_name);
1795 case PROP_SYNCHRONOUS:
1796 g_value_set_boolean (value, ximagesink->synchronous);
1798 case PROP_FORCE_ASPECT_RATIO:
1799 g_value_set_boolean (value, ximagesink->keep_aspect);
1801 case PROP_PIXEL_ASPECT_RATIO:
1802 if (ximagesink->par)
1803 g_value_transform (ximagesink->par, value);
1805 case PROP_HANDLE_EVENTS:
1806 g_value_set_boolean (value, ximagesink->handle_events);
1808 case PROP_HANDLE_EXPOSE:
1809 g_value_set_boolean (value, ximagesink->handle_expose);
1811 case PROP_WINDOW_WIDTH:
1812 if (ximagesink->xwindow)
1813 g_value_set_uint64 (value, ximagesink->xwindow->width);
1815 g_value_set_uint64 (value, 0);
1817 case PROP_WINDOW_HEIGHT:
1818 if (ximagesink->xwindow)
1819 g_value_set_uint64 (value, ximagesink->xwindow->height);
1821 g_value_set_uint64 (value, 0);
1824 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1830 gst_x_image_sink_reset (GstXImageSink * ximagesink)
1834 GST_OBJECT_LOCK (ximagesink);
1835 ximagesink->running = FALSE;
1836 /* grab thread and mark it as NULL */
1837 thread = ximagesink->event_thread;
1838 ximagesink->event_thread = NULL;
1839 GST_OBJECT_UNLOCK (ximagesink);
1841 /* Wait for our event thread to finish before we clean up our stuff. */
1843 g_thread_join (thread);
1845 if (ximagesink->cur_image) {
1846 gst_buffer_unref (ximagesink->cur_image);
1847 ximagesink->cur_image = NULL;
1850 g_mutex_lock (&ximagesink->flow_lock);
1852 if (ximagesink->pool) {
1853 gst_object_unref (ximagesink->pool);
1854 ximagesink->pool = NULL;
1857 if (ximagesink->xwindow) {
1858 gst_x_image_sink_xwindow_clear (ximagesink, ximagesink->xwindow);
1859 gst_x_image_sink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1860 ximagesink->xwindow = NULL;
1862 g_mutex_unlock (&ximagesink->flow_lock);
1864 gst_x_image_sink_xcontext_clear (ximagesink);
1868 gst_x_image_sink_finalize (GObject * object)
1870 GstXImageSink *ximagesink;
1872 ximagesink = GST_X_IMAGE_SINK (object);
1874 gst_x_image_sink_reset (ximagesink);
1876 if (ximagesink->display_name) {
1877 g_free (ximagesink->display_name);
1878 ximagesink->display_name = NULL;
1880 if (ximagesink->par) {
1881 g_free (ximagesink->par);
1882 ximagesink->par = NULL;
1884 g_mutex_clear (&ximagesink->x_lock);
1885 g_mutex_clear (&ximagesink->flow_lock);
1887 g_free (ximagesink->media_title);
1889 G_OBJECT_CLASS (parent_class)->finalize (object);
1893 gst_x_image_sink_init (GstXImageSink * ximagesink)
1895 ximagesink->display_name = NULL;
1896 ximagesink->xcontext = NULL;
1897 ximagesink->xwindow = NULL;
1898 ximagesink->cur_image = NULL;
1900 ximagesink->event_thread = NULL;
1901 ximagesink->running = FALSE;
1903 ximagesink->fps_n = 0;
1904 ximagesink->fps_d = 1;
1906 g_mutex_init (&ximagesink->x_lock);
1907 g_mutex_init (&ximagesink->flow_lock);
1909 ximagesink->par = NULL;
1911 ximagesink->pool = NULL;
1913 ximagesink->synchronous = FALSE;
1914 ximagesink->keep_aspect = TRUE;
1915 ximagesink->handle_events = TRUE;
1916 ximagesink->handle_expose = TRUE;
1920 gst_x_image_sink_class_init (GstXImageSinkClass * klass)
1922 GObjectClass *gobject_class;
1923 GstElementClass *gstelement_class;
1924 GstBaseSinkClass *gstbasesink_class;
1925 GstVideoSinkClass *videosink_class;
1927 gobject_class = (GObjectClass *) klass;
1928 gstelement_class = (GstElementClass *) klass;
1929 gstbasesink_class = (GstBaseSinkClass *) klass;
1930 videosink_class = (GstVideoSinkClass *) klass;
1932 gobject_class->finalize = gst_x_image_sink_finalize;
1933 gobject_class->set_property = gst_x_image_sink_set_property;
1934 gobject_class->get_property = gst_x_image_sink_get_property;
1936 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1937 g_param_spec_string ("display", "Display", "X Display name",
1938 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1939 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1940 g_param_spec_boolean ("synchronous", "Synchronous",
1941 "When enabled, runs the X display in synchronous mode. "
1942 "(unrelated to A/V sync, used only for debugging)", FALSE,
1943 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1944 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1945 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1946 "When enabled, reverse caps negotiation (scaling) will respect "
1947 "original aspect ratio", TRUE,
1948 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1949 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1950 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1951 "The pixel aspect ratio of the device", "1/1",
1952 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1953 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1954 g_param_spec_boolean ("handle-events", "Handle XEvents",
1955 "When enabled, XEvents will be selected and handled", TRUE,
1956 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1957 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1958 g_param_spec_boolean ("handle-expose", "Handle expose",
1960 "the current frame will always be drawn in response to X Expose "
1961 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1964 * GstXImageSink:window-width
1966 * Actual width of the video window.
1968 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1969 g_param_spec_uint64 ("window-width", "window-width",
1970 "Width of the window", 0, G_MAXUINT64, 0,
1971 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1974 * GstXImageSink:window-height
1976 * Actual height of the video window.
1978 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1979 g_param_spec_uint64 ("window-height", "window-height",
1980 "Height of the window", 0, G_MAXUINT64, 0,
1981 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1983 gst_element_class_set_static_metadata (gstelement_class,
1984 "Video sink", "Sink/Video",
1985 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1987 gst_element_class_add_static_pad_template (gstelement_class,
1988 &gst_x_image_sink_sink_template_factory);
1990 gstelement_class->change_state = gst_x_image_sink_change_state;
1992 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_getcaps);
1993 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_x_image_sink_setcaps);
1994 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_x_image_sink_get_times);
1995 gstbasesink_class->propose_allocation =
1996 GST_DEBUG_FUNCPTR (gst_x_image_sink_propose_allocation);
1997 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_x_image_sink_event);
1999 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_x_image_sink_show_frame);