2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-ximagesink
23 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24 * display. This element can receive a Window ID from the application through
25 * the XOverlay interface and will then render video frames in this drawable.
26 * If no Window ID was provided by the application, the element will create its
27 * own internal window and render into it.
30 * <title>Scaling</title>
32 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33 * reverse caps negotiation to try to get scaled video frames for the drawable.
34 * This is accomplished by asking the peer pad if it accepts some different caps
35 * which in most cases implies that there is a scaling element in the pipeline,
36 * or that an element generating the video frames can generate them with a
37 * different geometry. This mechanism is handled during buffer allocations, for
38 * each allocation request the video sink will check the drawable geometry, look
39 * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40 * desired video frames and then check that the peer pad accept those new caps.
41 * If it does it will then allocate a buffer in video memory with this new
42 * geometry and return it with the new caps.
46 * <title>Events</title>
48 * XImageSink creates a thread to handle events coming from the drawable. There
49 * are several kind of events that can be grouped in 2 big categories: input
50 * events and window state related events. Input events will be translated to
51 * navigation events and pushed upstream for other elements to react on them.
52 * This includes events such as pointer moves, key press/release, clicks etc...
53 * Other events are used to handle the drawable appearance even when the data
54 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55 * paused, it will receive expose events from the drawable and draw the latest
56 * frame with correct borders/aspect-ratio.
60 * <title>Pixel aspect ratio</title>
62 * When changing state to GST_STATE_READY, XImageSink will open a connection to
63 * the display specified in the #GstXImageSink:display property or the default
64 * display if nothing specified. Once this connection is open it will inspect
65 * the display configuration including the physical display geometry and
66 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67 * video sink will set the calculated pixel aspect ratio on the caps to make
68 * sure that incoming video frames will have the correct pixel aspect ratio for
69 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70 * then possible to enforce a specific pixel aspect ratio using the
71 * #GstXImageSink:pixel-aspect-ratio property.
75 * <title>Examples</title>
77 * gst-launch -v videotestsrc ! queue ! ximagesink
78 * ]| A pipeline to test reverse negotiation. When the test video signal appears
79 * you can resize the window and see that scaled buffers of the desired size are
80 * going to arrive with a short delay. This illustrates how buffers of desired
81 * size are allocated along the way. If you take away the queue, scaling will
82 * happen almost immediately.
84 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85 * ]| A pipeline to test navigation events.
86 * While moving the mouse pointer over the test signal you will see a black box
87 * following the mouse pointer. If you press the mouse button somewhere on the
88 * video and release it somewhere else a green box will appear where you pressed
89 * the button and a red one where you released it. (The navigationtest element
90 * is part of gst-plugins-good.)
92 * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94 * videotestsrc, in most cases the pixel aspect ratio of the display will be
95 * 1/1. This means that videoscale will have to do the scaling to convert
96 * incoming frames to a size that will match the display pixel aspect ratio
97 * (from 320x240 to 320x180 in this case). Note that you might have to escape
98 * some characters for your shell like '\(fraction\)'.
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
111 #include "ximagesink.h"
113 /* Debugging category */
114 #include <gst/gstinfo.h>
116 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
117 #define GST_CAT_DEFAULT gst_debug_ximagesink
122 unsigned long functions;
123 unsigned long decorations;
125 unsigned long status;
127 MotifWmHints, MwmHints;
129 #define MWM_HINTS_DECORATIONS (1L << 1)
131 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
132 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink);
133 static void gst_ximagesink_expose (GstXOverlay * overlay);
135 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
136 GST_STATIC_PAD_TEMPLATE ("sink",
139 GST_STATIC_CAPS ("video/x-raw-rgb, "
140 "framerate = (fraction) [ 0, MAX ], "
141 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
149 PROP_PIXEL_ASPECT_RATIO,
150 PROP_FORCE_ASPECT_RATIO,
157 /* ============================================================= */
161 /* ============================================================= */
163 /* =========================================== */
165 /* Object typing & Creation */
167 /* =========================================== */
168 static void gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass);
169 static void gst_ximagesink_navigation_init (GstNavigationInterface * klass);
170 static void gst_ximagesink_xoverlay_init (GstXOverlayClass * klass);
171 #define gst_ximagesink_parent_class parent_class
172 G_DEFINE_TYPE_WITH_CODE (GstXImageSink, gst_ximagesink, GST_TYPE_VIDEO_SINK,
173 G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
174 gst_ximagesink_interface_init);
175 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION, gst_ximagesink_navigation_init);
176 G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_ximagesink_xoverlay_init));
178 /* ============================================================= */
180 /* Private Methods */
182 /* ============================================================= */
186 /* We are called with the x_lock taken */
188 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
189 GstXWindow * xwindow, GstVideoRectangle rect)
191 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
192 g_return_if_fail (xwindow != NULL);
194 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
195 ximagesink->xcontext->black);
199 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
200 0, 0, rect.x, xwindow->height);
204 if ((rect.x + rect.w) < xwindow->width) {
205 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
206 rect.x + rect.w, 0, xwindow->width, xwindow->height);
211 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
212 0, 0, xwindow->width, rect.y);
216 if ((rect.y + rect.h) < xwindow->height) {
217 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
218 0, rect.y + rect.h, xwindow->width, xwindow->height);
222 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
224 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstBuffer * ximage)
227 GstVideoRectangle src, dst, result;
228 gboolean draw_border = FALSE;
230 /* We take the flow_lock. If expose is in there we don't want to run
231 concurrently from the data flow thread */
232 g_mutex_lock (ximagesink->flow_lock);
234 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
235 g_mutex_unlock (ximagesink->flow_lock);
239 /* Draw borders when displaying the first frame. After this
240 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
241 if (!ximagesink->cur_image || ximagesink->draw_border) {
245 /* Store a reference to the last image we put, lose the previous one */
246 if (ximage && ximagesink->cur_image != ximage) {
247 if (ximagesink->cur_image) {
248 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
249 gst_buffer_unref (ximagesink->cur_image);
251 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
252 ximagesink->cur_image = gst_buffer_ref (ximage);
255 /* Expose sends a NULL image, we take the latest frame */
258 if (ximagesink->cur_image) {
259 ximage = ximagesink->cur_image;
261 g_mutex_unlock (ximagesink->flow_lock);
266 meta = gst_buffer_get_meta_ximage (ximage);
269 src.h = meta->height;
270 dst.w = ximagesink->xwindow->width;
271 dst.h = ximagesink->xwindow->height;
273 gst_video_sink_center_rect (src, dst, &result, FALSE);
275 g_mutex_lock (ximagesink->x_lock);
278 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
280 ximagesink->draw_border = FALSE;
283 if (ximagesink->xcontext->use_xshm) {
284 GST_LOG_OBJECT (ximagesink,
285 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
286 ximage, 0, 0, result.x, result.y, result.w, result.h,
287 ximagesink->xwindow->width, ximagesink->xwindow->height);
288 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
289 ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
290 result.w, result.h, FALSE);
292 #endif /* HAVE_XSHM */
294 GST_LOG_OBJECT (ximagesink,
295 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
296 ximage, 0, 0, result.x, result.y, result.w, result.h,
297 ximagesink->xwindow->width, ximagesink->xwindow->height);
298 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
299 ximagesink->xwindow->gc, meta->ximage, 0, 0, result.x, result.y,
303 XSync (ximagesink->xcontext->disp, FALSE);
305 g_mutex_unlock (ximagesink->x_lock);
307 g_mutex_unlock (ximagesink->flow_lock);
313 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
316 Atom hints_atom = None;
319 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
320 g_return_val_if_fail (window != NULL, FALSE);
322 g_mutex_lock (ximagesink->x_lock);
324 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS",
326 if (hints_atom == None) {
327 g_mutex_unlock (ximagesink->x_lock);
331 hints = g_malloc0 (sizeof (MotifWmHints));
333 hints->flags |= MWM_HINTS_DECORATIONS;
334 hints->decorations = 1 << 0;
336 XChangeProperty (ximagesink->xcontext->disp, window->win,
337 hints_atom, hints_atom, 32, PropModeReplace,
338 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
340 XSync (ximagesink->xcontext->disp, FALSE);
342 g_mutex_unlock (ximagesink->x_lock);
350 gst_ximagesink_xwindow_set_title (GstXImageSink * ximagesink,
351 GstXWindow * xwindow, const gchar * media_title)
354 g_free (ximagesink->media_title);
355 ximagesink->media_title = g_strdup (media_title);
358 /* we have a window */
359 if (xwindow->internal) {
360 XTextProperty xproperty;
361 const gchar *app_name;
362 const gchar *title = NULL;
363 gchar *title_mem = NULL;
365 /* set application name as a title */
366 app_name = g_get_application_name ();
368 if (app_name && ximagesink->media_title) {
369 title = title_mem = g_strconcat (ximagesink->media_title, " : ",
371 } else if (app_name) {
373 } else if (ximagesink->media_title) {
374 title = ximagesink->media_title;
378 if ((XStringListToTextProperty (((char **) &title), 1,
380 XSetWMName (ximagesink->xcontext->disp, xwindow->win, &xproperty);
381 XFree (xproperty.value);
390 /* This function handles a GstXWindow creation */
392 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
394 GstXWindow *xwindow = NULL;
397 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
399 xwindow = g_new0 (GstXWindow, 1);
401 xwindow->width = width;
402 xwindow->height = height;
403 xwindow->internal = TRUE;
405 g_mutex_lock (ximagesink->x_lock);
407 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
408 ximagesink->xcontext->root,
409 0, 0, width, height, 0, 0, ximagesink->xcontext->black);
411 /* We have to do that to prevent X from redrawing the background on
412 ConfigureNotify. This takes away flickering of video when resizing. */
413 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
415 /* set application name as a title */
416 gst_ximagesink_xwindow_set_title (ximagesink, xwindow, NULL);
418 if (ximagesink->handle_events) {
421 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
422 StructureNotifyMask | PointerMotionMask | KeyPressMask |
423 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
425 /* Tell the window manager we'd like delete client messages instead of
427 wm_delete = XInternAtom (ximagesink->xcontext->disp,
428 "WM_DELETE_WINDOW", False);
429 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
433 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
436 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
438 XSync (ximagesink->xcontext->disp, FALSE);
440 g_mutex_unlock (ximagesink->x_lock);
442 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
444 gst_x_overlay_got_window_handle (GST_X_OVERLAY (ximagesink), xwindow->win);
449 /* This function destroys a GstXWindow */
451 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
452 GstXWindow * xwindow)
454 g_return_if_fail (xwindow != NULL);
455 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
457 g_mutex_lock (ximagesink->x_lock);
459 /* If we did not create that window we just free the GC and let it live */
460 if (xwindow->internal)
461 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
463 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
465 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
467 XSync (ximagesink->xcontext->disp, FALSE);
469 g_mutex_unlock (ximagesink->x_lock);
475 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink)
477 XWindowAttributes attr;
479 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
481 /* Update the window geometry */
482 g_mutex_lock (ximagesink->x_lock);
483 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
484 g_mutex_unlock (ximagesink->x_lock);
488 XGetWindowAttributes (ximagesink->xcontext->disp,
489 ximagesink->xwindow->win, &attr);
491 ximagesink->xwindow->width = attr.width;
492 ximagesink->xwindow->height = attr.height;
494 g_mutex_unlock (ximagesink->x_lock);
498 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
500 g_return_if_fail (xwindow != NULL);
501 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
503 g_mutex_lock (ximagesink->x_lock);
505 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
506 ximagesink->xcontext->black);
508 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
509 0, 0, xwindow->width, xwindow->height);
511 XSync (ximagesink->xcontext->disp, FALSE);
513 g_mutex_unlock (ximagesink->x_lock);
516 /* This function handles XEvents that might be in the queue. It generates
517 GstEvent that will be sent upstream in the pipeline to handle interactivity
520 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
523 guint pointer_x = 0, pointer_y = 0;
524 gboolean pointer_moved = FALSE;
525 gboolean exposed = FALSE, configured = FALSE;
527 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
529 /* Then we get all pointer motion events, only the last position is
531 g_mutex_lock (ximagesink->flow_lock);
532 g_mutex_lock (ximagesink->x_lock);
533 while (XCheckWindowEvent (ximagesink->xcontext->disp,
534 ximagesink->xwindow->win, PointerMotionMask, &e)) {
535 g_mutex_unlock (ximagesink->x_lock);
536 g_mutex_unlock (ximagesink->flow_lock);
540 pointer_x = e.xmotion.x;
541 pointer_y = e.xmotion.y;
542 pointer_moved = TRUE;
547 g_mutex_lock (ximagesink->flow_lock);
548 g_mutex_lock (ximagesink->x_lock);
552 g_mutex_unlock (ximagesink->x_lock);
553 g_mutex_unlock (ximagesink->flow_lock);
555 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
556 pointer_x, pointer_y);
557 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
558 "mouse-move", 0, pointer_x, pointer_y);
560 g_mutex_lock (ximagesink->flow_lock);
561 g_mutex_lock (ximagesink->x_lock);
564 /* We get all remaining events on our window to throw them upstream */
565 while (XCheckWindowEvent (ximagesink->xcontext->disp,
566 ximagesink->xwindow->win,
567 KeyPressMask | KeyReleaseMask |
568 ButtonPressMask | ButtonReleaseMask, &e)) {
571 /* We lock only for the X function call */
572 g_mutex_unlock (ximagesink->x_lock);
573 g_mutex_unlock (ximagesink->flow_lock);
577 /* Mouse button pressed/released over our window. We send upstream
578 events for interactivity/navigation */
579 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
580 e.xbutton.button, e.xbutton.x, e.xbutton.x);
581 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
582 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
585 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
586 e.xbutton.button, e.xbutton.x, e.xbutton.x);
587 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
588 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
592 /* Key pressed/released over our window. We send upstream
593 events for interactivity/navigation */
594 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
595 e.xkey.keycode, e.xkey.x, e.xkey.x);
596 g_mutex_lock (ximagesink->x_lock);
597 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
599 g_mutex_unlock (ximagesink->x_lock);
600 if (keysym != NoSymbol) {
601 char *key_str = NULL;
603 g_mutex_lock (ximagesink->x_lock);
604 key_str = XKeysymToString (keysym);
605 g_mutex_unlock (ximagesink->x_lock);
606 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
607 e.type == KeyPress ? "key-press" : "key-release", key_str);
609 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
610 e.type == KeyPress ? "key-press" : "key-release", "unknown");
614 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
617 g_mutex_lock (ximagesink->flow_lock);
618 g_mutex_lock (ximagesink->x_lock);
622 while (XCheckWindowEvent (ximagesink->xcontext->disp,
623 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
628 case ConfigureNotify:
629 g_mutex_unlock (ximagesink->x_lock);
630 gst_ximagesink_xwindow_update_geometry (ximagesink);
631 g_mutex_lock (ximagesink->x_lock);
639 if (ximagesink->handle_expose && (exposed || configured)) {
640 g_mutex_unlock (ximagesink->x_lock);
641 g_mutex_unlock (ximagesink->flow_lock);
643 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
645 g_mutex_lock (ximagesink->flow_lock);
646 g_mutex_lock (ximagesink->x_lock);
649 /* Handle Display events */
650 while (XPending (ximagesink->xcontext->disp)) {
651 XNextEvent (ximagesink->xcontext->disp, &e);
657 wm_delete = XInternAtom (ximagesink->xcontext->disp,
658 "WM_DELETE_WINDOW", False);
659 if (wm_delete == (Atom) e.xclient.data.l[0]) {
660 /* Handle window deletion by posting an error on the bus */
661 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
662 ("Output window was closed"), (NULL));
664 g_mutex_unlock (ximagesink->x_lock);
665 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
666 ximagesink->xwindow = NULL;
667 g_mutex_lock (ximagesink->x_lock);
676 g_mutex_unlock (ximagesink->x_lock);
677 g_mutex_unlock (ximagesink->flow_lock);
681 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
683 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
685 GST_OBJECT_LOCK (ximagesink);
686 while (ximagesink->running) {
687 GST_OBJECT_UNLOCK (ximagesink);
689 if (ximagesink->xwindow) {
690 gst_ximagesink_handle_xevents (ximagesink);
692 /* FIXME: do we want to align this with the framerate or anything else? */
693 g_usleep (G_USEC_PER_SEC / 20);
695 GST_OBJECT_LOCK (ximagesink);
697 GST_OBJECT_UNLOCK (ximagesink);
703 gst_ximagesink_manage_event_thread (GstXImageSink * ximagesink)
705 GThread *thread = NULL;
707 /* don't start the thread too early */
708 if (ximagesink->xcontext == NULL) {
712 GST_OBJECT_LOCK (ximagesink);
713 if (ximagesink->handle_expose || ximagesink->handle_events) {
714 if (!ximagesink->event_thread) {
715 /* Setup our event listening thread */
716 GST_DEBUG_OBJECT (ximagesink, "run xevent thread, expose %d, events %d",
717 ximagesink->handle_expose, ximagesink->handle_events);
718 ximagesink->running = TRUE;
719 ximagesink->event_thread = g_thread_create (
720 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
723 if (ximagesink->event_thread) {
724 GST_DEBUG_OBJECT (ximagesink, "stop xevent thread, expose %d, events %d",
725 ximagesink->handle_expose, ximagesink->handle_events);
726 ximagesink->running = FALSE;
727 /* grab thread and mark it as NULL */
728 thread = ximagesink->event_thread;
729 ximagesink->event_thread = NULL;
732 GST_OBJECT_UNLOCK (ximagesink);
734 /* Wait for our event thread to finish */
736 g_thread_join (thread);
741 /* This function calculates the pixel aspect ratio based on the properties
742 * in the xcontext structure and stores it there. */
744 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
746 static const gint par[][2] = {
747 {1, 1}, /* regular screen */
748 {16, 15}, /* PAL TV */
749 {11, 10}, /* 525 line Rec.601 video */
750 {54, 59}, /* 625 line Rec.601 video */
751 {64, 45}, /* 1280x1024 on 16:9 display */
752 {5, 3}, /* 1280x1024 on 4:3 display */
753 {4, 3} /* 800x600 on 16:9 display */
760 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
762 /* first calculate the "real" ratio based on the X values;
763 * which is the "physical" w/h divided by the w/h in pixels of the display */
764 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
765 / (xcontext->heightmm * xcontext->width);
767 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
769 if (xcontext->width == 720 && xcontext->height == 576) {
770 ratio = 4.0 * 576 / (3.0 * 720);
772 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
774 /* now find the one from par[][2] with the lowest delta to the real one */
778 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
779 gdouble this_delta = DELTA (i);
781 if (this_delta < delta) {
787 GST_DEBUG ("Decided on index %d (%d/%d)", index,
788 par[index][0], par[index][1]);
790 g_free (xcontext->par);
791 xcontext->par = g_new0 (GValue, 1);
792 g_value_init (xcontext->par, GST_TYPE_FRACTION);
793 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
794 GST_DEBUG ("set xcontext PAR to %d/%d",
795 gst_value_get_fraction_numerator (xcontext->par),
796 gst_value_get_fraction_denominator (xcontext->par));
799 /* This function gets the X Display and global info about it. Everything is
800 stored in our object and will be cleaned when the object is disposed. Note
801 here that caps for supported format are generated without any window or
804 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
806 GstXContext *xcontext = NULL;
807 XPixmapFormatValues *px_formats = NULL;
808 gint nb_formats = 0, i;
810 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
812 xcontext = g_new0 (GstXContext, 1);
814 g_mutex_lock (ximagesink->x_lock);
816 xcontext->disp = XOpenDisplay (ximagesink->display_name);
818 if (!xcontext->disp) {
819 g_mutex_unlock (ximagesink->x_lock);
821 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
822 ("Could not initialise X output"), ("Could not open display"));
826 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
827 xcontext->screen_num = DefaultScreen (xcontext->disp);
828 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
829 xcontext->root = DefaultRootWindow (xcontext->disp);
830 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
831 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
832 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
834 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
835 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
836 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
837 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
839 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
840 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
842 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
844 /* We get supported pixmap formats at supported depth */
845 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
848 XCloseDisplay (xcontext->disp);
849 g_mutex_unlock (ximagesink->x_lock);
850 g_free (xcontext->par);
852 GST_ELEMENT_ERROR (ximagesink, RESOURCE, SETTINGS,
853 ("Could not get supported pixmap formats"), (NULL));
857 /* We get bpp value corresponding to our running depth */
858 for (i = 0; i < nb_formats; i++) {
859 if (px_formats[i].depth == xcontext->depth)
860 xcontext->bpp = px_formats[i].bits_per_pixel;
865 xcontext->endianness =
866 (ImageByteOrder (xcontext->disp) ==
867 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
869 /* Search for XShm extension support */
871 if (XShmQueryExtension (xcontext->disp) &&
872 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
873 xcontext->use_xshm = TRUE;
874 GST_DEBUG ("ximagesink is using XShm extension");
876 #endif /* HAVE_XSHM */
878 xcontext->use_xshm = FALSE;
879 GST_DEBUG ("ximagesink is not using XShm extension");
882 /* our caps system handles 24/32bpp RGB as big-endian. */
883 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
884 xcontext->endianness == G_LITTLE_ENDIAN) {
885 xcontext->endianness = G_BIG_ENDIAN;
886 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
887 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
888 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
889 if (xcontext->bpp == 24) {
890 xcontext->visual->red_mask >>= 8;
891 xcontext->visual->green_mask >>= 8;
892 xcontext->visual->blue_mask >>= 8;
896 /* update object's par with calculated one if not set yet */
897 if (!ximagesink->par) {
898 ximagesink->par = g_new0 (GValue, 1);
899 gst_value_init_and_copy (ximagesink->par, xcontext->par);
900 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
902 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
903 "bpp", G_TYPE_INT, xcontext->bpp,
904 "depth", G_TYPE_INT, xcontext->depth,
905 "endianness", G_TYPE_INT, xcontext->endianness,
906 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
907 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
908 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
909 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
910 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
911 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
912 if (ximagesink->par) {
915 nom = gst_value_get_fraction_numerator (ximagesink->par);
916 den = gst_value_get_fraction_denominator (ximagesink->par);
917 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
918 GST_TYPE_FRACTION, nom, den, NULL);
921 g_mutex_unlock (ximagesink->x_lock);
926 /* This function cleans the X context. Closing the Display and unrefing the
927 caps for supported formats. */
929 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
931 GstXContext *xcontext;
933 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
935 GST_OBJECT_LOCK (ximagesink);
936 if (ximagesink->xcontext == NULL) {
937 GST_OBJECT_UNLOCK (ximagesink);
941 /* Take the xcontext reference and NULL it while we
942 * clean it up, so that any buffer-alloced buffers
943 * arriving after this will be freed correctly */
944 xcontext = ximagesink->xcontext;
945 ximagesink->xcontext = NULL;
947 GST_OBJECT_UNLOCK (ximagesink);
949 gst_caps_unref (xcontext->caps);
950 g_free (xcontext->par);
951 g_free (ximagesink->par);
952 ximagesink->par = NULL;
954 if (xcontext->last_caps)
955 gst_caps_replace (&xcontext->last_caps, NULL);
957 g_mutex_lock (ximagesink->x_lock);
959 XCloseDisplay (xcontext->disp);
961 g_mutex_unlock (ximagesink->x_lock);
969 gst_ximagesink_getcaps (GstBaseSink * bsink)
971 GstXImageSink *ximagesink;
975 ximagesink = GST_XIMAGESINK (bsink);
977 if (ximagesink->xcontext)
978 return gst_caps_ref (ximagesink->xcontext->caps);
980 /* get a template copy and add the pixel aspect ratio */
982 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK
983 (ximagesink)->sinkpad));
984 for (i = 0; i < gst_caps_get_size (caps); ++i) {
985 GstStructure *structure = gst_caps_get_structure (caps, i);
987 if (ximagesink->par) {
990 nom = gst_value_get_fraction_numerator (ximagesink->par);
991 den = gst_value_get_fraction_denominator (ximagesink->par);
992 gst_structure_set (structure, "pixel-aspect-ratio",
993 GST_TYPE_FRACTION, nom, den, NULL);
1001 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1003 GstXImageSink *ximagesink;
1004 GstStructure *structure;
1005 GstBufferPool *newpool, *oldpool;
1006 gboolean ret = TRUE;
1008 gint new_width, new_height;
1011 ximagesink = GST_XIMAGESINK (bsink);
1013 if (!ximagesink->xcontext)
1016 GST_DEBUG_OBJECT (ximagesink,
1017 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1018 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1020 /* We intersect those caps with our template to make sure they are correct */
1021 if (!gst_caps_can_intersect (ximagesink->xcontext->caps, caps))
1022 goto incompatible_caps;
1024 structure = gst_caps_get_structure (caps, 0);
1026 ret &= gst_structure_get_int (structure, "width", &new_width);
1027 ret &= gst_structure_get_int (structure, "height", &new_height);
1028 fps = gst_structure_get_value (structure, "framerate");
1029 ret &= (fps != NULL);
1034 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1035 * otherwise linking should fail */
1036 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1038 if (ximagesink->par) {
1039 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1042 } else if (ximagesink->xcontext->par) {
1043 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1049 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1050 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1051 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1052 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1054 /* Notify application to set xwindow id now */
1055 g_mutex_lock (ximagesink->flow_lock);
1056 if (!ximagesink->xwindow) {
1057 g_mutex_unlock (ximagesink->flow_lock);
1058 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1060 g_mutex_unlock (ximagesink->flow_lock);
1063 /* Creating our window and our image */
1064 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1065 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0)
1068 g_mutex_lock (ximagesink->flow_lock);
1069 if (!ximagesink->xwindow) {
1070 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1071 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1073 /* Remember to draw borders for next frame */
1074 ximagesink->draw_border = TRUE;
1076 /* create a new pool for the new configuration */
1077 newpool = gst_ximage_buffer_pool_new (ximagesink);
1079 structure = gst_buffer_pool_get_config (newpool);
1080 gst_buffer_pool_config_set (structure, caps, 0, 0, 0, 0, 0, 16);
1081 if (!gst_buffer_pool_set_config (newpool, structure))
1084 if (!gst_buffer_pool_set_active (newpool, TRUE))
1085 goto activate_failed;
1087 oldpool = ximagesink->pool;
1088 ximagesink->pool = newpool;
1090 /* unref the old sink */
1093 gst_buffer_pool_set_active (oldpool, FALSE);
1094 gst_object_unref (oldpool);
1096 g_mutex_unlock (ximagesink->flow_lock);
1103 GST_ERROR_OBJECT (ximagesink, "caps incompatible");
1108 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1113 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1114 ("Invalid image size."));
1119 GST_ERROR_OBJECT (ximagesink, "failed to set config.");
1120 g_mutex_unlock (ximagesink->flow_lock);
1125 GST_ERROR_OBJECT (ximagesink, "failed to activate bufferpool.");
1126 g_mutex_unlock (ximagesink->flow_lock);
1131 static GstStateChangeReturn
1132 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1134 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1135 GstXImageSink *ximagesink;
1136 GstXContext *xcontext = NULL;
1138 ximagesink = GST_XIMAGESINK (element);
1140 switch (transition) {
1141 case GST_STATE_CHANGE_NULL_TO_READY:
1142 /* Initializing the XContext */
1143 if (ximagesink->xcontext == NULL) {
1144 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1145 if (xcontext == NULL) {
1146 ret = GST_STATE_CHANGE_FAILURE;
1149 GST_OBJECT_LOCK (ximagesink);
1151 ximagesink->xcontext = xcontext;
1152 GST_OBJECT_UNLOCK (ximagesink);
1155 /* call XSynchronize with the current value of synchronous */
1156 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1157 ximagesink->synchronous ? "TRUE" : "FALSE");
1158 g_mutex_lock (ximagesink->x_lock);
1159 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1160 g_mutex_unlock (ximagesink->x_lock);
1161 gst_ximagesink_manage_event_thread (ximagesink);
1163 case GST_STATE_CHANGE_READY_TO_PAUSED:
1164 g_mutex_lock (ximagesink->flow_lock);
1165 if (ximagesink->xwindow)
1166 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1167 g_mutex_unlock (ximagesink->flow_lock);
1169 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1175 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1177 switch (transition) {
1178 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1180 case GST_STATE_CHANGE_PAUSED_TO_READY:
1181 ximagesink->fps_n = 0;
1182 ximagesink->fps_d = 1;
1183 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1184 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1185 g_mutex_lock (ximagesink->flow_lock);
1186 if (ximagesink->pool)
1187 gst_buffer_pool_set_active (ximagesink->pool, FALSE);
1188 g_mutex_unlock (ximagesink->flow_lock);
1190 case GST_STATE_CHANGE_READY_TO_NULL:
1191 gst_ximagesink_reset (ximagesink);
1202 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1203 GstClockTime * start, GstClockTime * end)
1205 GstXImageSink *ximagesink;
1207 ximagesink = GST_XIMAGESINK (bsink);
1209 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1210 *start = GST_BUFFER_TIMESTAMP (buf);
1211 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1212 *end = *start + GST_BUFFER_DURATION (buf);
1214 if (ximagesink->fps_n > 0) {
1216 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1223 static GstFlowReturn
1224 gst_ximagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1227 GstXImageSink *ximagesink;
1228 GstMetaXImage *meta;
1232 ximagesink = GST_XIMAGESINK (vsink);
1234 meta = gst_buffer_get_meta_ximage (buf);
1237 /* If this buffer has been allocated using our buffer management we simply
1238 put the ximage which is in the PRIVATE pointer */
1239 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1246 /* Else we have to copy the data into our private image, */
1247 /* if we have one... */
1248 GST_LOG_OBJECT (ximagesink, "buffer not from our pool, copying");
1250 /* we should have a pool, configured in setcaps */
1251 if (ximagesink->pool == NULL)
1254 /* take a buffer form our pool */
1255 res = gst_buffer_pool_acquire_buffer (ximagesink->pool, &temp, NULL);
1256 if (res != GST_FLOW_OK)
1261 if (gst_buffer_get_size (temp) < gst_buffer_get_size (buf))
1264 data = gst_buffer_map (temp, &size, NULL, GST_MAP_WRITE);
1265 gst_buffer_extract (buf, 0, data, size);
1266 gst_buffer_unmap (temp, data, size);
1271 if (!gst_ximagesink_ximage_put (ximagesink, buf))
1276 gst_buffer_unref (buf);
1283 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1284 ("Internal error: can't allocate images"),
1285 ("We don't have a bufferpool negotiated"));
1286 return GST_FLOW_ERROR;
1290 /* No image available. That's very bad ! */
1291 GST_WARNING_OBJECT (ximagesink, "could not create image");
1296 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1297 ("Failed to create output image buffer"),
1298 ("XServer allocated buffer size did not match input buffer %"
1299 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (temp),
1300 gst_buffer_get_size (buf)));
1301 res = GST_FLOW_ERROR;
1306 /* No Window available to put our image into */
1307 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1308 res = GST_FLOW_ERROR;
1314 gst_ximagesink_event (GstBaseSink * sink, GstEvent * event)
1316 GstXImageSink *ximagesink = GST_XIMAGESINK (sink);
1318 switch (GST_EVENT_TYPE (event)) {
1319 case GST_EVENT_TAG:{
1321 gchar *title = NULL;
1323 gst_event_parse_tag (event, &l);
1324 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1327 GST_DEBUG_OBJECT (ximagesink, "got tags, title='%s'", title);
1328 gst_ximagesink_xwindow_set_title (ximagesink, ximagesink->xwindow,
1338 if (GST_BASE_SINK_CLASS (parent_class)->event)
1339 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1344 /* Interfaces stuff */
1347 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1349 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1354 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1356 klass->supported = gst_ximagesink_interface_supported;
1360 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1361 GstStructure * structure)
1363 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1365 gint x_offset, y_offset;
1369 event = gst_event_new_navigation (structure);
1371 /* We are not converting the pointer coordinates as there's no hardware
1372 scaling done here. The only possible scaling is done by videoscale and
1373 videoscale will have to catch those events and tranform the coordinates
1374 to match the applied scaling. So here we just add the offset if the image
1375 is centered in the window. */
1377 /* We take the flow_lock while we look at the window */
1378 g_mutex_lock (ximagesink->flow_lock);
1380 if (!ximagesink->xwindow) {
1381 g_mutex_unlock (ximagesink->flow_lock);
1385 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1386 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1388 g_mutex_unlock (ximagesink->flow_lock);
1390 if (x_offset > 0 && gst_structure_get_double (structure, "pointer_x", &x)) {
1392 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1394 if (y_offset > 0 && gst_structure_get_double (structure, "pointer_y", &y)) {
1396 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1399 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1401 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1402 gst_pad_send_event (pad, event);
1404 gst_object_unref (pad);
1409 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1411 iface->send_event = gst_ximagesink_navigation_send_event;
1415 gst_ximagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
1417 XID xwindow_id = id;
1418 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1419 GstXWindow *xwindow = NULL;
1420 XWindowAttributes attr;
1422 /* We acquire the stream lock while setting this window in the element.
1423 We are basically cleaning tons of stuff replacing the old window, putting
1424 images while we do that would surely crash */
1425 g_mutex_lock (ximagesink->flow_lock);
1427 /* If we already use that window return */
1428 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1429 g_mutex_unlock (ximagesink->flow_lock);
1433 /* If the element has not initialized the X11 context try to do so */
1434 if (!ximagesink->xcontext &&
1435 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1436 g_mutex_unlock (ximagesink->flow_lock);
1437 /* we have thrown a GST_ELEMENT_ERROR now */
1441 /* If a window is there already we destroy it */
1442 if (ximagesink->xwindow) {
1443 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1444 ximagesink->xwindow = NULL;
1447 /* If the xid is 0 we go back to an internal window */
1448 if (xwindow_id == 0) {
1449 /* If no width/height caps nego did not happen window will be created
1450 during caps nego then */
1451 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1452 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1453 GST_VIDEO_SINK_WIDTH (ximagesink),
1454 GST_VIDEO_SINK_HEIGHT (ximagesink));
1457 xwindow = g_new0 (GstXWindow, 1);
1459 xwindow->win = xwindow_id;
1461 /* We get window geometry, set the event we want to receive,
1463 g_mutex_lock (ximagesink->x_lock);
1464 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1465 xwindow->width = attr.width;
1466 xwindow->height = attr.height;
1467 xwindow->internal = FALSE;
1468 if (ximagesink->handle_events) {
1469 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1470 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1474 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1475 g_mutex_unlock (ximagesink->x_lock);
1479 ximagesink->xwindow = xwindow;
1481 g_mutex_unlock (ximagesink->flow_lock);
1485 gst_ximagesink_expose (GstXOverlay * overlay)
1487 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1489 gst_ximagesink_xwindow_update_geometry (ximagesink);
1490 gst_ximagesink_ximage_put (ximagesink, NULL);
1494 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1495 gboolean handle_events)
1497 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1499 ximagesink->handle_events = handle_events;
1501 g_mutex_lock (ximagesink->flow_lock);
1503 if (G_UNLIKELY (!ximagesink->xwindow)) {
1504 g_mutex_unlock (ximagesink->flow_lock);
1508 g_mutex_lock (ximagesink->x_lock);
1510 if (handle_events) {
1511 if (ximagesink->xwindow->internal) {
1512 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1513 ExposureMask | StructureNotifyMask | PointerMotionMask |
1514 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1516 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1517 ExposureMask | StructureNotifyMask | PointerMotionMask |
1518 KeyPressMask | KeyReleaseMask);
1521 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1524 g_mutex_unlock (ximagesink->x_lock);
1526 g_mutex_unlock (ximagesink->flow_lock);
1530 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1532 iface->set_window_handle = gst_ximagesink_set_window_handle;
1533 iface->expose = gst_ximagesink_expose;
1534 iface->handle_events = gst_ximagesink_set_event_handling;
1537 /* =========================================== */
1539 /* Init & Class init */
1541 /* =========================================== */
1544 gst_ximagesink_set_property (GObject * object, guint prop_id,
1545 const GValue * value, GParamSpec * pspec)
1547 GstXImageSink *ximagesink;
1549 g_return_if_fail (GST_IS_XIMAGESINK (object));
1551 ximagesink = GST_XIMAGESINK (object);
1555 ximagesink->display_name = g_strdup (g_value_get_string (value));
1557 case PROP_SYNCHRONOUS:
1558 ximagesink->synchronous = g_value_get_boolean (value);
1559 if (ximagesink->xcontext) {
1560 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1561 ximagesink->synchronous ? "TRUE" : "FALSE");
1562 g_mutex_lock (ximagesink->x_lock);
1563 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1564 g_mutex_unlock (ximagesink->x_lock);
1567 case PROP_FORCE_ASPECT_RATIO:
1568 ximagesink->keep_aspect = g_value_get_boolean (value);
1570 case PROP_PIXEL_ASPECT_RATIO:
1574 tmp = g_new0 (GValue, 1);
1575 g_value_init (tmp, GST_TYPE_FRACTION);
1577 if (!g_value_transform (value, tmp)) {
1578 GST_WARNING_OBJECT (ximagesink,
1579 "Could not transform string to aspect ratio");
1582 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1583 gst_value_get_fraction_numerator (tmp),
1584 gst_value_get_fraction_denominator (tmp));
1585 g_free (ximagesink->par);
1586 ximagesink->par = tmp;
1590 case PROP_HANDLE_EVENTS:
1591 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
1592 g_value_get_boolean (value));
1593 gst_ximagesink_manage_event_thread (ximagesink);
1595 case PROP_HANDLE_EXPOSE:
1596 ximagesink->handle_expose = g_value_get_boolean (value);
1597 gst_ximagesink_manage_event_thread (ximagesink);
1600 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1606 gst_ximagesink_get_property (GObject * object, guint prop_id,
1607 GValue * value, GParamSpec * pspec)
1609 GstXImageSink *ximagesink;
1611 g_return_if_fail (GST_IS_XIMAGESINK (object));
1613 ximagesink = GST_XIMAGESINK (object);
1617 g_value_set_string (value, ximagesink->display_name);
1619 case PROP_SYNCHRONOUS:
1620 g_value_set_boolean (value, ximagesink->synchronous);
1622 case PROP_FORCE_ASPECT_RATIO:
1623 g_value_set_boolean (value, ximagesink->keep_aspect);
1625 case PROP_PIXEL_ASPECT_RATIO:
1626 if (ximagesink->par)
1627 g_value_transform (ximagesink->par, value);
1629 case PROP_HANDLE_EVENTS:
1630 g_value_set_boolean (value, ximagesink->handle_events);
1632 case PROP_HANDLE_EXPOSE:
1633 g_value_set_boolean (value, ximagesink->handle_expose);
1635 case PROP_WINDOW_WIDTH:
1636 if (ximagesink->xwindow)
1637 g_value_set_uint64 (value, ximagesink->xwindow->width);
1639 g_value_set_uint64 (value, 0);
1641 case PROP_WINDOW_HEIGHT:
1642 if (ximagesink->xwindow)
1643 g_value_set_uint64 (value, ximagesink->xwindow->height);
1645 g_value_set_uint64 (value, 0);
1648 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1654 gst_ximagesink_reset (GstXImageSink * ximagesink)
1658 GST_OBJECT_LOCK (ximagesink);
1659 ximagesink->running = FALSE;
1660 /* grab thread and mark it as NULL */
1661 thread = ximagesink->event_thread;
1662 ximagesink->event_thread = NULL;
1663 GST_OBJECT_UNLOCK (ximagesink);
1665 /* Wait for our event thread to finish before we clean up our stuff. */
1667 g_thread_join (thread);
1669 if (ximagesink->cur_image) {
1670 gst_buffer_unref (ximagesink->cur_image);
1671 ximagesink->cur_image = NULL;
1674 g_mutex_lock (ximagesink->flow_lock);
1676 if (ximagesink->pool) {
1677 gst_object_unref (ximagesink->pool);
1678 ximagesink->pool = NULL;
1681 if (ximagesink->xwindow) {
1682 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1683 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1684 ximagesink->xwindow = NULL;
1686 g_mutex_unlock (ximagesink->flow_lock);
1688 gst_ximagesink_xcontext_clear (ximagesink);
1692 gst_ximagesink_finalize (GObject * object)
1694 GstXImageSink *ximagesink;
1696 ximagesink = GST_XIMAGESINK (object);
1698 gst_ximagesink_reset (ximagesink);
1700 if (ximagesink->display_name) {
1701 g_free (ximagesink->display_name);
1702 ximagesink->display_name = NULL;
1704 if (ximagesink->par) {
1705 g_free (ximagesink->par);
1706 ximagesink->par = NULL;
1708 if (ximagesink->x_lock) {
1709 g_mutex_free (ximagesink->x_lock);
1710 ximagesink->x_lock = NULL;
1712 if (ximagesink->flow_lock) {
1713 g_mutex_free (ximagesink->flow_lock);
1714 ximagesink->flow_lock = NULL;
1717 g_free (ximagesink->media_title);
1719 G_OBJECT_CLASS (parent_class)->finalize (object);
1723 gst_ximagesink_init (GstXImageSink * ximagesink)
1725 ximagesink->display_name = NULL;
1726 ximagesink->xcontext = NULL;
1727 ximagesink->xwindow = NULL;
1728 ximagesink->cur_image = NULL;
1730 ximagesink->event_thread = NULL;
1731 ximagesink->running = FALSE;
1733 ximagesink->fps_n = 0;
1734 ximagesink->fps_d = 1;
1736 ximagesink->x_lock = g_mutex_new ();
1737 ximagesink->flow_lock = g_mutex_new ();
1739 ximagesink->par = NULL;
1741 ximagesink->pool = NULL;
1743 ximagesink->synchronous = FALSE;
1744 ximagesink->keep_aspect = FALSE;
1745 ximagesink->handle_events = TRUE;
1746 ximagesink->handle_expose = TRUE;
1750 gst_ximagesink_class_init (GstXImageSinkClass * klass)
1752 GObjectClass *gobject_class;
1753 GstElementClass *gstelement_class;
1754 GstBaseSinkClass *gstbasesink_class;
1755 GstVideoSinkClass *videosink_class;
1757 gobject_class = (GObjectClass *) klass;
1758 gstelement_class = (GstElementClass *) klass;
1759 gstbasesink_class = (GstBaseSinkClass *) klass;
1760 videosink_class = (GstVideoSinkClass *) klass;
1762 gobject_class->finalize = gst_ximagesink_finalize;
1763 gobject_class->set_property = gst_ximagesink_set_property;
1764 gobject_class->get_property = gst_ximagesink_get_property;
1766 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1767 g_param_spec_string ("display", "Display", "X Display name",
1768 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1769 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1770 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
1771 "the X display in synchronous mode. (used only for debugging)", FALSE,
1772 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1773 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1774 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1775 "When enabled, reverse caps negotiation (scaling) will respect "
1776 "original aspect ratio", FALSE,
1777 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1778 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1779 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1780 "The pixel aspect ratio of the device", "1/1",
1781 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1782 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1783 g_param_spec_boolean ("handle-events", "Handle XEvents",
1784 "When enabled, XEvents will be selected and handled", TRUE,
1785 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1786 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1787 g_param_spec_boolean ("handle-expose", "Handle expose",
1789 "the current frame will always be drawn in response to X Expose "
1790 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1793 * GstXImageSink:window-width
1795 * Actual width of the video window.
1799 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1800 g_param_spec_uint64 ("window-width", "window-width",
1801 "Width of the window", 0, G_MAXUINT64, 0,
1802 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1805 * GstXImageSink:window-height
1807 * Actual height of the video window.
1811 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
1812 g_param_spec_uint64 ("window-height", "window-height",
1813 "Height of the window", 0, G_MAXUINT64, 0,
1814 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1816 gst_element_class_set_details_simple (gstelement_class,
1817 "Video sink", "Sink/Video",
1818 "A standard X based videosink", "Julien Moutte <julien@moutte.net>");
1820 gst_element_class_add_pad_template (gstelement_class,
1821 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
1823 gstelement_class->change_state = gst_ximagesink_change_state;
1825 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
1826 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
1827 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
1828 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_ximagesink_event);
1830 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);