2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theorically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink 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.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/xoverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/video.h>
123 #include <gst/video/gstmetavideo.h>
126 #include "xvimagesink.h"
128 /* Debugging category */
129 #include <gst/gstinfo.h>
131 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
132 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
133 #define GST_CAT_DEFAULT gst_debug_xvimagesink
138 unsigned long functions;
139 unsigned long decorations;
141 unsigned long status;
143 MotifWmHints, MwmHints;
145 #define MWM_HINTS_DECORATIONS (1L << 1)
147 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
148 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
150 static void gst_xvimagesink_expose (GstXOverlay * overlay);
152 /* Default template - initiated with class struct to allow gst-register to work
154 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
155 GST_STATIC_PAD_TEMPLATE ("sink",
158 GST_STATIC_CAPS ("video/x-raw-rgb, "
159 "framerate = (fraction) [ 0, MAX ], "
160 "width = (int) [ 1, MAX ], "
161 "height = (int) [ 1, MAX ]; "
163 "framerate = (fraction) [ 0, MAX ], "
164 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
176 PROP_PIXEL_ASPECT_RATIO,
177 PROP_FORCE_ASPECT_RATIO,
183 PROP_AUTOPAINT_COLORKEY,
190 /* ============================================================= */
194 /* ============================================================= */
196 /* =========================================== */
198 /* Object typing & Creation */
200 /* =========================================== */
201 static void gst_xvimagesink_interface_init (GstImplementsInterfaceClass *
203 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
204 static void gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface);
205 static void gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface);
207 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
209 #define gst_xvimagesink_parent_class parent_class
210 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
211 G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
212 gst_xvimagesink_interface_init);
213 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
214 gst_xvimagesink_navigation_init);
215 G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_xvimagesink_xoverlay_init);
216 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
217 gst_xvimagesink_colorbalance_init);
218 G_IMPLEMENT_INTERFACE (GST_TYPE_PROPERTY_PROBE,
219 gst_xvimagesink_property_probe_interface_init));
222 /* ============================================================= */
224 /* Private Methods */
226 /* ============================================================= */
229 /* We are called with the x_lock taken */
231 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
232 GstXWindow * xwindow, GstVideoRectangle rect)
236 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
237 g_return_if_fail (xwindow != NULL);
239 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
240 xvimagesink->xcontext->black);
243 if (rect.x > xvimagesink->render_rect.x) {
244 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
245 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
246 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
250 t1 = rect.x + rect.w;
251 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
253 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
254 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
258 if (rect.y > xvimagesink->render_rect.y) {
259 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
260 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
261 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
265 t1 = rect.y + rect.h;
266 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
268 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
269 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
273 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
274 * if no window was available */
276 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
278 GstMetaXvImage *meta;
279 GstVideoRectangle result;
280 gboolean draw_border = FALSE;
282 /* We take the flow_lock. If expose is in there we don't want to run
283 concurrently from the data flow thread */
284 g_mutex_lock (xvimagesink->flow_lock);
286 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
287 g_mutex_unlock (xvimagesink->flow_lock);
291 /* Draw borders when displaying the first frame. After this
292 draw borders only on expose event or after a size change. */
293 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
297 /* Store a reference to the last image we put, lose the previous one */
298 if (xvimage && xvimagesink->cur_image != xvimage) {
299 if (xvimagesink->cur_image) {
300 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
301 gst_buffer_unref (xvimagesink->cur_image);
303 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
304 xvimagesink->cur_image = gst_buffer_ref (xvimage);
307 /* Expose sends a NULL image, we take the latest frame */
309 if (xvimagesink->cur_image) {
311 xvimage = xvimagesink->cur_image;
313 g_mutex_unlock (xvimagesink->flow_lock);
318 meta = gst_buffer_get_meta_xvimage (xvimage);
320 if (xvimagesink->keep_aspect) {
321 GstVideoRectangle src, dst;
323 /* We use the calculated geometry from _setcaps as a source to respect
324 source and screen pixel aspect ratios. */
325 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
326 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
327 dst.w = xvimagesink->render_rect.w;
328 dst.h = xvimagesink->render_rect.h;
330 gst_video_sink_center_rect (src, dst, &result, TRUE);
331 result.x += xvimagesink->render_rect.x;
332 result.y += xvimagesink->render_rect.y;
334 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
337 g_mutex_lock (xvimagesink->x_lock);
339 if (draw_border && xvimagesink->draw_borders) {
340 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
342 xvimagesink->redraw_border = FALSE;
345 if (xvimagesink->xcontext->use_xshm) {
346 GST_LOG_OBJECT (xvimagesink,
347 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
348 GST_PTR_FORMAT, meta->width, meta->height,
349 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
351 XvShmPutImage (xvimagesink->xcontext->disp,
352 xvimagesink->xcontext->xv_port_id,
353 xvimagesink->xwindow->win,
354 xvimagesink->xwindow->gc, meta->xvimage,
355 xvimagesink->disp_x, xvimagesink->disp_y,
356 xvimagesink->disp_width, xvimagesink->disp_height,
357 result.x, result.y, result.w, result.h, FALSE);
359 #endif /* HAVE_XSHM */
361 XvPutImage (xvimagesink->xcontext->disp,
362 xvimagesink->xcontext->xv_port_id,
363 xvimagesink->xwindow->win,
364 xvimagesink->xwindow->gc, meta->xvimage,
365 xvimagesink->disp_x, xvimagesink->disp_y,
366 xvimagesink->disp_width, xvimagesink->disp_height,
367 result.x, result.y, result.w, result.h);
370 XSync (xvimagesink->xcontext->disp, FALSE);
372 g_mutex_unlock (xvimagesink->x_lock);
374 g_mutex_unlock (xvimagesink->flow_lock);
380 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
383 Atom hints_atom = None;
386 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
387 g_return_val_if_fail (window != NULL, FALSE);
389 g_mutex_lock (xvimagesink->x_lock);
391 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
393 if (hints_atom == None) {
394 g_mutex_unlock (xvimagesink->x_lock);
398 hints = g_malloc0 (sizeof (MotifWmHints));
400 hints->flags |= MWM_HINTS_DECORATIONS;
401 hints->decorations = 1 << 0;
403 XChangeProperty (xvimagesink->xcontext->disp, window->win,
404 hints_atom, hints_atom, 32, PropModeReplace,
405 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
407 XSync (xvimagesink->xcontext->disp, FALSE);
409 g_mutex_unlock (xvimagesink->x_lock);
417 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
418 GstXWindow * xwindow, const gchar * media_title)
421 g_free (xvimagesink->media_title);
422 xvimagesink->media_title = g_strdup (media_title);
425 /* we have a window */
426 if (xwindow->internal) {
427 XTextProperty xproperty;
428 const gchar *app_name;
429 const gchar *title = NULL;
430 gchar *title_mem = NULL;
432 /* set application name as a title */
433 app_name = g_get_application_name ();
435 if (app_name && xvimagesink->media_title) {
436 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
438 } else if (app_name) {
440 } else if (xvimagesink->media_title) {
441 title = xvimagesink->media_title;
445 if ((XStringListToTextProperty (((char **) &title), 1,
447 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
448 XFree (xproperty.value);
457 /* This function handles a GstXWindow creation
458 * The width and height are the actual pixel size on the display */
460 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
461 gint width, gint height)
463 GstXWindow *xwindow = NULL;
466 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
468 xwindow = g_new0 (GstXWindow, 1);
470 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
471 xvimagesink->render_rect.w = width;
472 xvimagesink->render_rect.h = height;
474 xwindow->width = width;
475 xwindow->height = height;
476 xwindow->internal = TRUE;
478 g_mutex_lock (xvimagesink->x_lock);
480 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
481 xvimagesink->xcontext->root,
482 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
484 /* We have to do that to prevent X from redrawing the background on
485 * ConfigureNotify. This takes away flickering of video when resizing. */
486 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
488 /* set application name as a title */
489 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
491 if (xvimagesink->handle_events) {
494 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
495 StructureNotifyMask | PointerMotionMask | KeyPressMask |
496 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
498 /* Tell the window manager we'd like delete client messages instead of
500 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
501 "WM_DELETE_WINDOW", True);
502 if (wm_delete != None) {
503 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
508 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
509 xwindow->win, 0, &values);
511 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
513 XSync (xvimagesink->xcontext->disp, FALSE);
515 g_mutex_unlock (xvimagesink->x_lock);
517 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
519 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
524 /* This function destroys a GstXWindow */
526 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
527 GstXWindow * xwindow)
529 g_return_if_fail (xwindow != NULL);
530 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
532 g_mutex_lock (xvimagesink->x_lock);
534 /* If we did not create that window we just free the GC and let it live */
535 if (xwindow->internal)
536 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
538 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
540 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
542 XSync (xvimagesink->xcontext->disp, FALSE);
544 g_mutex_unlock (xvimagesink->x_lock);
550 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
552 XWindowAttributes attr;
554 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
556 /* Update the window geometry */
557 g_mutex_lock (xvimagesink->x_lock);
558 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
559 g_mutex_unlock (xvimagesink->x_lock);
563 XGetWindowAttributes (xvimagesink->xcontext->disp,
564 xvimagesink->xwindow->win, &attr);
566 xvimagesink->xwindow->width = attr.width;
567 xvimagesink->xwindow->height = attr.height;
569 if (!xvimagesink->have_render_rect) {
570 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
571 xvimagesink->render_rect.w = attr.width;
572 xvimagesink->render_rect.h = attr.height;
575 g_mutex_unlock (xvimagesink->x_lock);
579 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
580 GstXWindow * xwindow)
582 g_return_if_fail (xwindow != NULL);
583 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
585 g_mutex_lock (xvimagesink->x_lock);
587 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
590 XSync (xvimagesink->xcontext->disp, FALSE);
592 g_mutex_unlock (xvimagesink->x_lock);
595 /* This function commits our internal colorbalance settings to our grabbed Xv
596 port. If the xcontext is not initialized yet it simply returns */
598 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
600 GList *channels = NULL;
602 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
604 /* If we haven't initialized the X context we can't update anything */
605 if (xvimagesink->xcontext == NULL)
608 /* Don't set the attributes if they haven't been changed, to avoid
609 * rounding errors changing the values */
610 if (!xvimagesink->cb_changed)
613 /* For each channel of the colorbalance we calculate the correct value
614 doing range conversion and then set the Xv port attribute to match our
616 channels = xvimagesink->xcontext->channels_list;
619 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
620 GstColorBalanceChannel *channel = NULL;
623 gdouble convert_coef;
625 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
626 g_object_ref (channel);
628 /* Our range conversion coef */
629 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
631 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
632 value = xvimagesink->hue;
633 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
634 value = xvimagesink->saturation;
635 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
636 value = xvimagesink->contrast;
637 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
638 value = xvimagesink->brightness;
640 g_warning ("got an unknown channel %s", channel->label);
641 g_object_unref (channel);
645 /* Committing to Xv port */
646 g_mutex_lock (xvimagesink->x_lock);
648 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
649 if (prop_atom != None) {
652 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
653 XvSetPortAttribute (xvimagesink->xcontext->disp,
654 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
656 g_mutex_unlock (xvimagesink->x_lock);
658 g_object_unref (channel);
660 channels = g_list_next (channels);
664 /* This function handles XEvents that might be in the queue. It generates
665 GstEvent that will be sent upstream in the pipeline to handle interactivity
666 and navigation. It will also listen for configure events on the window to
667 trigger caps renegotiation so on the fly software scaling can work. */
669 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
672 guint pointer_x = 0, pointer_y = 0;
673 gboolean pointer_moved = FALSE;
674 gboolean exposed = FALSE, configured = FALSE;
676 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
678 /* Handle Interaction, produces navigation events */
680 /* We get all pointer motion events, only the last position is
682 g_mutex_lock (xvimagesink->flow_lock);
683 g_mutex_lock (xvimagesink->x_lock);
684 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
685 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
686 g_mutex_unlock (xvimagesink->x_lock);
687 g_mutex_unlock (xvimagesink->flow_lock);
691 pointer_x = e.xmotion.x;
692 pointer_y = e.xmotion.y;
693 pointer_moved = TRUE;
698 g_mutex_lock (xvimagesink->flow_lock);
699 g_mutex_lock (xvimagesink->x_lock);
703 g_mutex_unlock (xvimagesink->x_lock);
704 g_mutex_unlock (xvimagesink->flow_lock);
706 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
707 pointer_x, pointer_y);
708 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
709 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
711 g_mutex_lock (xvimagesink->flow_lock);
712 g_mutex_lock (xvimagesink->x_lock);
715 /* We get all events on our window to throw them upstream */
716 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
717 xvimagesink->xwindow->win,
718 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
722 /* We lock only for the X function call */
723 g_mutex_unlock (xvimagesink->x_lock);
724 g_mutex_unlock (xvimagesink->flow_lock);
728 /* Mouse button pressed over our window. We send upstream
729 events for interactivity/navigation */
730 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
731 e.xbutton.button, e.xbutton.x, e.xbutton.y);
732 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
733 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
736 /* Mouse button released over our window. We send upstream
737 events for interactivity/navigation */
738 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
739 e.xbutton.button, e.xbutton.x, e.xbutton.y);
740 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
741 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
745 /* Key pressed/released over our window. We send upstream
746 events for interactivity/navigation */
747 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
748 e.xkey.keycode, e.xkey.x, e.xkey.y);
749 g_mutex_lock (xvimagesink->x_lock);
750 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
752 g_mutex_unlock (xvimagesink->x_lock);
753 if (keysym != NoSymbol) {
754 char *key_str = NULL;
756 g_mutex_lock (xvimagesink->x_lock);
757 key_str = XKeysymToString (keysym);
758 g_mutex_unlock (xvimagesink->x_lock);
759 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
760 e.type == KeyPress ? "key-press" : "key-release", key_str);
762 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
763 e.type == KeyPress ? "key-press" : "key-release", "unknown");
767 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
770 g_mutex_lock (xvimagesink->flow_lock);
771 g_mutex_lock (xvimagesink->x_lock);
775 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
776 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
781 case ConfigureNotify:
782 g_mutex_unlock (xvimagesink->x_lock);
783 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
784 g_mutex_lock (xvimagesink->x_lock);
792 if (xvimagesink->handle_expose && (exposed || configured)) {
793 g_mutex_unlock (xvimagesink->x_lock);
794 g_mutex_unlock (xvimagesink->flow_lock);
796 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
798 g_mutex_lock (xvimagesink->flow_lock);
799 g_mutex_lock (xvimagesink->x_lock);
802 /* Handle Display events */
803 while (XPending (xvimagesink->xcontext->disp)) {
804 XNextEvent (xvimagesink->xcontext->disp, &e);
810 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
811 "WM_DELETE_WINDOW", True);
812 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
813 /* Handle window deletion by posting an error on the bus */
814 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
815 ("Output window was closed"), (NULL));
817 g_mutex_unlock (xvimagesink->x_lock);
818 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
819 xvimagesink->xwindow = NULL;
820 g_mutex_lock (xvimagesink->x_lock);
829 g_mutex_unlock (xvimagesink->x_lock);
830 g_mutex_unlock (xvimagesink->flow_lock);
834 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
835 XvAdaptorInfo * adaptors, int adaptor_no)
840 /* Do we support XvImageMask ? */
841 if (!(adaptors[adaptor_no].type & XvImageMask)) {
842 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
843 adaptors[adaptor_no].name);
847 /* We found such an adaptor, looking for an available port */
848 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
849 /* We try to grab the port */
850 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
851 if (Success == res) {
852 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
853 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
854 adaptors[adaptor_no].num_ports);
856 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
857 adaptors[adaptor_no].name, res);
862 /* This function generates a caps with all supported format by the first
863 Xv grabable port we find. We store each one of the supported formats in a
864 format list and append the format to a newly created caps that we return
865 If this function does not return NULL because of an error, it also grabs
866 the port via XvGrabPort */
868 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
869 GstXContext * xcontext)
872 XvAdaptorInfo *adaptors;
874 XvImageFormatValues *formats = NULL;
876 XvEncodingInfo *encodings = NULL;
877 gulong max_w = G_MAXINT, max_h = G_MAXINT;
878 GstCaps *caps = NULL;
879 GstCaps *rgb_caps = NULL;
881 g_return_val_if_fail (xcontext != NULL, NULL);
883 /* First let's check that XVideo extension is available */
884 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
885 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
886 ("Could not initialise Xv output"),
887 ("XVideo extension is not available"));
891 /* Then we get adaptors list */
892 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
893 &xcontext->nb_adaptors, &adaptors)) {
894 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
895 ("Could not initialise Xv output"),
896 ("Failed getting XV adaptors list"));
900 xcontext->xv_port_id = 0;
902 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
905 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
907 /* Now fill up our adaptor name array */
908 for (i = 0; i < xcontext->nb_adaptors; i++) {
909 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
912 if (xvimagesink->adaptor_no >= 0 &&
913 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
914 /* Find xv port from user defined adaptor */
915 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
916 xvimagesink->adaptor_no);
919 if (!xcontext->xv_port_id) {
920 /* Now search for an adaptor that supports XvImageMask */
921 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
922 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
923 xvimagesink->adaptor_no = i;
927 XvFreeAdaptorInfo (adaptors);
929 if (!xcontext->xv_port_id) {
930 xvimagesink->adaptor_no = -1;
931 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
932 ("Could not initialise Xv output"), ("No port available"));
936 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
939 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
940 xcontext->xv_port_id, &count);
941 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
942 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
943 static const char colorkey[] = "XV_COLORKEY";
945 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
947 xvimagesink->have_autopaint_colorkey = FALSE;
948 xvimagesink->have_double_buffer = FALSE;
949 xvimagesink->have_colorkey = FALSE;
951 for (i = 0; ((i < count) && todo); i++)
952 if (!strcmp (attr[i].name, autopaint)) {
953 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
955 /* turn on autopaint colorkey */
956 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
957 (xvimagesink->autopaint_colorkey ? 1 : 0));
959 xvimagesink->have_autopaint_colorkey = TRUE;
960 } else if (!strcmp (attr[i].name, dbl_buffer)) {
961 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
963 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
964 (xvimagesink->double_buffer ? 1 : 0));
966 xvimagesink->have_double_buffer = TRUE;
967 } else if (!strcmp (attr[i].name, colorkey)) {
968 /* Set the colorkey, default is something that is dark but hopefully
969 * won't randomly appear on the screen elsewhere (ie not black or greys)
970 * can be overridden by setting "colorkey" property
972 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
974 gboolean set_attr = TRUE;
977 /* set a colorkey in the right format RGB565/RGB888
978 * We only handle these 2 cases, because they're the only types of
979 * devices we've encountered. If we don't recognise it, leave it alone
981 cr = (xvimagesink->colorkey >> 16);
982 cg = (xvimagesink->colorkey >> 8) & 0xFF;
983 cb = (xvimagesink->colorkey) & 0xFF;
984 switch (xcontext->depth) {
985 case 16: /* RGB 565 */
989 ckey = (cr << 11) | (cg << 5) | cb;
992 case 32: /* RGB 888 / ARGB 8888 */
993 ckey = (cr << 16) | (cg << 8) | cb;
996 GST_DEBUG_OBJECT (xvimagesink,
997 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1004 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1005 (guint32) attr[i].max_value);
1006 GST_LOG_OBJECT (xvimagesink,
1007 "Setting color key for display depth %d to 0x%x",
1008 xcontext->depth, ckey);
1010 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1014 xvimagesink->have_colorkey = TRUE;
1020 /* Get the list of encodings supported by the adapter and look for the
1021 * XV_IMAGE encoding so we can determine the maximum width and height
1023 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1026 for (i = 0; i < nb_encodings; i++) {
1027 GST_LOG_OBJECT (xvimagesink,
1028 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1029 i, encodings[i].name, encodings[i].width, encodings[i].height,
1030 encodings[i].rate.numerator, encodings[i].rate.denominator);
1031 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1032 max_w = encodings[i].width;
1033 max_h = encodings[i].height;
1037 XvFreeEncodingInfo (encodings);
1039 /* We get all image formats supported by our port */
1040 formats = XvListImageFormats (xcontext->disp,
1041 xcontext->xv_port_id, &nb_formats);
1042 caps = gst_caps_new_empty ();
1043 for (i = 0; i < nb_formats; i++) {
1044 GstCaps *format_caps = NULL;
1045 gboolean is_rgb_format = FALSE;
1047 /* We set the image format of the xcontext to an existing one. This
1048 is just some valid image format for making our xshm calls check before
1049 caps negotiation really happens. */
1050 xcontext->im_format = formats[i].id;
1052 switch (formats[i].type) {
1055 XvImageFormatValues *fmt = &(formats[i]);
1056 gint endianness = G_BIG_ENDIAN;
1058 if (fmt->byte_order == LSBFirst) {
1059 /* our caps system handles 24/32bpp RGB as big-endian. */
1060 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1061 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1062 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1063 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1065 if (fmt->bits_per_pixel == 24) {
1066 fmt->red_mask >>= 8;
1067 fmt->green_mask >>= 8;
1068 fmt->blue_mask >>= 8;
1071 endianness = G_LITTLE_ENDIAN;
1074 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1075 "endianness", G_TYPE_INT, endianness,
1076 "depth", G_TYPE_INT, fmt->depth,
1077 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1078 "red_mask", G_TYPE_INT, fmt->red_mask,
1079 "green_mask", G_TYPE_INT, fmt->green_mask,
1080 "blue_mask", G_TYPE_INT, fmt->blue_mask,
1081 "width", GST_TYPE_INT_RANGE, 1, max_w,
1082 "height", GST_TYPE_INT_RANGE, 1, max_h,
1083 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1085 is_rgb_format = TRUE;
1089 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1090 "format", GST_TYPE_FOURCC, formats[i].id,
1091 "width", GST_TYPE_INT_RANGE, 1, max_w,
1092 "height", GST_TYPE_INT_RANGE, 1, max_h,
1093 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1096 g_assert_not_reached ();
1101 GstXvImageFormat *format = NULL;
1103 format = g_new0 (GstXvImageFormat, 1);
1105 format->format = formats[i].id;
1106 format->caps = gst_caps_copy (format_caps);
1107 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1110 if (is_rgb_format) {
1111 if (rgb_caps == NULL)
1112 rgb_caps = format_caps;
1114 gst_caps_append (rgb_caps, format_caps);
1116 gst_caps_append (caps, format_caps);
1120 /* Collected all caps into either the caps or rgb_caps structures.
1121 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1123 gst_caps_append (caps, rgb_caps);
1128 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1130 if (gst_caps_is_empty (caps)) {
1131 gst_caps_unref (caps);
1132 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1133 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1134 ("No supported format found"));
1142 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1144 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1146 GST_OBJECT_LOCK (xvimagesink);
1147 while (xvimagesink->running) {
1148 GST_OBJECT_UNLOCK (xvimagesink);
1150 if (xvimagesink->xwindow) {
1151 gst_xvimagesink_handle_xevents (xvimagesink);
1153 /* FIXME: do we want to align this with the framerate or anything else? */
1154 g_usleep (G_USEC_PER_SEC / 20);
1156 GST_OBJECT_LOCK (xvimagesink);
1158 GST_OBJECT_UNLOCK (xvimagesink);
1164 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1166 GThread *thread = NULL;
1168 /* don't start the thread too early */
1169 if (xvimagesink->xcontext == NULL) {
1173 GST_OBJECT_LOCK (xvimagesink);
1174 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1175 if (!xvimagesink->event_thread) {
1176 /* Setup our event listening thread */
1177 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1178 xvimagesink->handle_expose, xvimagesink->handle_events);
1179 xvimagesink->running = TRUE;
1180 xvimagesink->event_thread = g_thread_create (
1181 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1184 if (xvimagesink->event_thread) {
1185 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1186 xvimagesink->handle_expose, xvimagesink->handle_events);
1187 xvimagesink->running = FALSE;
1188 /* grab thread and mark it as NULL */
1189 thread = xvimagesink->event_thread;
1190 xvimagesink->event_thread = NULL;
1193 GST_OBJECT_UNLOCK (xvimagesink);
1195 /* Wait for our event thread to finish */
1197 g_thread_join (thread);
1202 /* This function calculates the pixel aspect ratio based on the properties
1203 * in the xcontext structure and stores it there. */
1205 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1207 static const gint par[][2] = {
1208 {1, 1}, /* regular screen */
1209 {16, 15}, /* PAL TV */
1210 {11, 10}, /* 525 line Rec.601 video */
1211 {54, 59}, /* 625 line Rec.601 video */
1212 {64, 45}, /* 1280x1024 on 16:9 display */
1213 {5, 3}, /* 1280x1024 on 4:3 display */
1214 {4, 3} /* 800x600 on 16:9 display */
1221 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1223 /* first calculate the "real" ratio based on the X values;
1224 * which is the "physical" w/h divided by the w/h in pixels of the display */
1225 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1226 / (xcontext->heightmm * xcontext->width);
1228 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1230 if (xcontext->width == 720 && xcontext->height == 576) {
1231 ratio = 4.0 * 576 / (3.0 * 720);
1233 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1235 /* now find the one from par[][2] with the lowest delta to the real one */
1239 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1240 gdouble this_delta = DELTA (i);
1242 if (this_delta < delta) {
1248 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1249 par[index][0], par[index][1]);
1251 g_free (xcontext->par);
1252 xcontext->par = g_new0 (GValue, 1);
1253 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1254 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1255 GST_DEBUG ("set xcontext PAR to %d/%d",
1256 gst_value_get_fraction_numerator (xcontext->par),
1257 gst_value_get_fraction_denominator (xcontext->par));
1260 /* This function gets the X Display and global info about it. Everything is
1261 stored in our object and will be cleaned when the object is disposed. Note
1262 here that caps for supported format are generated without any window or
1264 static GstXContext *
1265 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1267 GstXContext *xcontext = NULL;
1268 XPixmapFormatValues *px_formats = NULL;
1269 gint nb_formats = 0, i, j, N_attr;
1270 XvAttribute *xv_attr;
1272 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1273 "XV_BRIGHTNESS", "XV_CONTRAST"
1276 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1278 xcontext = g_new0 (GstXContext, 1);
1279 xcontext->im_format = 0;
1281 g_mutex_lock (xvimagesink->x_lock);
1283 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1285 if (!xcontext->disp) {
1286 g_mutex_unlock (xvimagesink->x_lock);
1288 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1289 ("Could not initialise Xv output"), ("Could not open display"));
1293 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1294 xcontext->screen_num = DefaultScreen (xcontext->disp);
1295 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1296 xcontext->root = DefaultRootWindow (xcontext->disp);
1297 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1298 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1299 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1301 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1302 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1303 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1304 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1306 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1307 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1309 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1310 /* We get supported pixmap formats at supported depth */
1311 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1314 XCloseDisplay (xcontext->disp);
1315 g_mutex_unlock (xvimagesink->x_lock);
1316 g_free (xcontext->par);
1318 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1319 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1323 /* We get bpp value corresponding to our running depth */
1324 for (i = 0; i < nb_formats; i++) {
1325 if (px_formats[i].depth == xcontext->depth)
1326 xcontext->bpp = px_formats[i].bits_per_pixel;
1331 xcontext->endianness =
1332 (ImageByteOrder (xcontext->disp) ==
1333 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1335 /* our caps system handles 24/32bpp RGB as big-endian. */
1336 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1337 xcontext->endianness == G_LITTLE_ENDIAN) {
1338 xcontext->endianness = G_BIG_ENDIAN;
1339 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1340 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1341 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1342 if (xcontext->bpp == 24) {
1343 xcontext->visual->red_mask >>= 8;
1344 xcontext->visual->green_mask >>= 8;
1345 xcontext->visual->blue_mask >>= 8;
1349 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1351 /* Search for XShm extension support */
1353 if (XShmQueryExtension (xcontext->disp) &&
1354 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1355 xcontext->use_xshm = TRUE;
1356 GST_DEBUG ("xvimagesink is using XShm extension");
1358 #endif /* HAVE_XSHM */
1360 xcontext->use_xshm = FALSE;
1361 GST_DEBUG ("xvimagesink is not using XShm extension");
1364 if (!xcontext->caps) {
1365 XCloseDisplay (xcontext->disp);
1366 g_mutex_unlock (xvimagesink->x_lock);
1367 g_free (xcontext->par);
1369 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1373 xv_attr = XvQueryPortAttributes (xcontext->disp,
1374 xcontext->xv_port_id, &N_attr);
1377 /* Generate the channels list */
1378 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1379 XvAttribute *matching_attr = NULL;
1381 /* Retrieve the property atom if it exists. If it doesn't exist,
1382 * the attribute itself must not either, so we can skip */
1383 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1384 if (prop_atom == None)
1387 if (xv_attr != NULL) {
1388 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1389 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1390 matching_attr = xv_attr + j;
1393 if (matching_attr) {
1394 GstColorBalanceChannel *channel;
1396 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1397 channel->label = g_strdup (channels[i]);
1398 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1399 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1401 xcontext->channels_list = g_list_append (xcontext->channels_list,
1404 /* If the colorbalance settings have not been touched we get Xv values
1405 as defaults and update our internal variables */
1406 if (!xvimagesink->cb_changed) {
1409 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1411 /* Normalize val to [-1000, 1000] */
1412 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1413 (double) (channel->max_value - channel->min_value));
1415 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1416 xvimagesink->hue = val;
1417 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1418 xvimagesink->saturation = val;
1419 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1420 xvimagesink->brightness = val;
1421 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1422 xvimagesink->contrast = val;
1430 g_mutex_unlock (xvimagesink->x_lock);
1435 /* This function cleans the X context. Closing the Display, releasing the XV
1436 port and unrefing the caps for supported formats. */
1438 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1440 GList *formats_list, *channels_list;
1441 GstXContext *xcontext;
1444 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1446 GST_OBJECT_LOCK (xvimagesink);
1447 if (xvimagesink->xcontext == NULL) {
1448 GST_OBJECT_UNLOCK (xvimagesink);
1452 /* Take the XContext from the sink and clean it up */
1453 xcontext = xvimagesink->xcontext;
1454 xvimagesink->xcontext = NULL;
1456 GST_OBJECT_UNLOCK (xvimagesink);
1459 formats_list = xcontext->formats_list;
1461 while (formats_list) {
1462 GstXvImageFormat *format = formats_list->data;
1464 gst_caps_unref (format->caps);
1466 formats_list = g_list_next (formats_list);
1469 if (xcontext->formats_list)
1470 g_list_free (xcontext->formats_list);
1472 channels_list = xcontext->channels_list;
1474 while (channels_list) {
1475 GstColorBalanceChannel *channel = channels_list->data;
1477 g_object_unref (channel);
1478 channels_list = g_list_next (channels_list);
1481 if (xcontext->channels_list)
1482 g_list_free (xcontext->channels_list);
1484 gst_caps_unref (xcontext->caps);
1485 if (xcontext->last_caps)
1486 gst_caps_replace (&xcontext->last_caps, NULL);
1488 for (i = 0; i < xcontext->nb_adaptors; i++) {
1489 g_free (xcontext->adaptors[i]);
1492 g_free (xcontext->adaptors);
1494 g_free (xcontext->par);
1496 g_mutex_lock (xvimagesink->x_lock);
1498 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1500 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1502 XCloseDisplay (xcontext->disp);
1504 g_mutex_unlock (xvimagesink->x_lock);
1512 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1514 GstXvImageSink *xvimagesink;
1517 xvimagesink = GST_XVIMAGESINK (bsink);
1519 if (xvimagesink->xcontext) {
1521 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1522 GST_CAPS_INTERSECT_FIRST);
1524 return gst_caps_ref (xvimagesink->xcontext->caps);
1527 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1529 GstCaps *intersection;
1532 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1533 gst_caps_unref (caps);
1534 caps = intersection;
1540 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1542 GstXvImageSink *xvimagesink;
1543 GstStructure *structure;
1544 GstBufferPool *newpool, *oldpool;
1546 guint32 im_format = 0;
1547 gint video_width, video_height;
1548 gint disp_x, disp_y;
1549 gint disp_width, disp_height;
1550 gint video_par_n, video_par_d; /* video's PAR */
1551 gint display_par_n, display_par_d; /* display's PAR */
1552 const GValue *caps_par;
1553 const GValue *caps_disp_reg;
1558 xvimagesink = GST_XVIMAGESINK (bsink);
1560 GST_DEBUG_OBJECT (xvimagesink,
1561 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1562 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1564 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1565 goto incompatible_caps;
1567 structure = gst_caps_get_structure (caps, 0);
1568 ret = gst_structure_get_int (structure, "width", &video_width);
1569 ret &= gst_structure_get_int (structure, "height", &video_height);
1570 fps = gst_structure_get_value (structure, "framerate");
1571 ret &= (fps != NULL);
1574 goto incomplete_caps;
1576 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
1577 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
1579 xvimagesink->video_width = video_width;
1580 xvimagesink->video_height = video_height;
1582 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
1583 if (im_format == -1)
1584 goto invalid_format;
1586 if (!gst_video_get_size_from_caps (caps, &size))
1587 goto invalid_format;
1589 /* get aspect ratio from caps if it's present, and
1590 * convert video width and height to a display width and height
1591 * using wd / hd = wv / hv * PARv / PARd */
1593 /* get video's PAR */
1594 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1596 video_par_n = gst_value_get_fraction_numerator (caps_par);
1597 video_par_d = gst_value_get_fraction_denominator (caps_par);
1602 /* get display's PAR */
1603 if (xvimagesink->par) {
1604 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1605 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1611 /* get the display region */
1612 caps_disp_reg = gst_structure_get_value (structure, "display-region");
1613 if (caps_disp_reg) {
1614 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
1615 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
1616 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
1618 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
1620 disp_x = disp_y = 0;
1621 disp_width = video_width;
1622 disp_height = video_height;
1625 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
1626 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
1629 xvimagesink->disp_x = disp_x;
1630 xvimagesink->disp_y = disp_y;
1631 xvimagesink->disp_width = disp_width;
1632 xvimagesink->disp_height = disp_height;
1634 GST_DEBUG_OBJECT (xvimagesink,
1635 "video width/height: %dx%d, calculated display ratio: %d/%d",
1636 video_width, video_height, num, den);
1638 /* now find a width x height that respects this display ratio.
1639 * prefer those that have one of w/h the same as the incoming video
1640 * using wd / hd = num / den */
1642 /* start with same height, because of interlaced video */
1643 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1644 if (video_height % den == 0) {
1645 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1646 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1647 gst_util_uint64_scale_int (video_height, num, den);
1648 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
1649 } else if (video_width % num == 0) {
1650 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1651 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
1652 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1653 gst_util_uint64_scale_int (video_width, den, num);
1655 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1656 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1657 gst_util_uint64_scale_int (video_height, num, den);
1658 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
1660 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1661 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1663 /* Notify application to set xwindow id now */
1664 g_mutex_lock (xvimagesink->flow_lock);
1665 if (!xvimagesink->xwindow) {
1666 g_mutex_unlock (xvimagesink->flow_lock);
1667 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
1669 g_mutex_unlock (xvimagesink->flow_lock);
1672 /* Creating our window and our image with the display size in pixels */
1673 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1674 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1675 goto no_display_size;
1677 g_mutex_lock (xvimagesink->flow_lock);
1678 if (!xvimagesink->xwindow) {
1679 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1680 GST_VIDEO_SINK_WIDTH (xvimagesink),
1681 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1684 /* After a resize, we want to redraw the borders in case the new frame size
1685 * doesn't cover the same area */
1686 xvimagesink->redraw_border = TRUE;
1688 /* create a new pool for the new configuration */
1689 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1691 structure = gst_buffer_pool_get_config (newpool);
1692 gst_buffer_pool_config_set (structure, caps, size, 0, 0, 0, 15);
1693 if (!gst_buffer_pool_set_config (newpool, structure))
1696 if (!gst_buffer_pool_set_active (newpool, TRUE))
1697 goto activate_failed;
1699 oldpool = xvimagesink->pool;
1700 xvimagesink->pool = newpool;
1701 g_mutex_unlock (xvimagesink->flow_lock);
1703 /* unref the old sink */
1706 gst_buffer_pool_set_active (oldpool, FALSE);
1707 gst_object_unref (oldpool);
1715 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1720 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
1721 "height or framerate from intersected caps");
1726 GST_DEBUG_OBJECT (xvimagesink,
1727 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1732 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1733 ("Error calculating the output display ratio of the video."));
1738 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1739 ("Error calculating the output display ratio of the video."));
1744 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1745 g_mutex_unlock (xvimagesink->flow_lock);
1750 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1751 g_mutex_unlock (xvimagesink->flow_lock);
1752 gst_object_unref (newpool);
1757 static GstStateChangeReturn
1758 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1760 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1761 GstXvImageSink *xvimagesink;
1762 GstXContext *xcontext = NULL;
1764 xvimagesink = GST_XVIMAGESINK (element);
1766 switch (transition) {
1767 case GST_STATE_CHANGE_NULL_TO_READY:
1768 /* Initializing the XContext */
1769 if (xvimagesink->xcontext == NULL) {
1770 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1771 if (xcontext == NULL) {
1772 ret = GST_STATE_CHANGE_FAILURE;
1775 GST_OBJECT_LOCK (xvimagesink);
1777 xvimagesink->xcontext = xcontext;
1778 GST_OBJECT_UNLOCK (xvimagesink);
1781 /* update object's par with calculated one if not set yet */
1782 if (!xvimagesink->par) {
1783 xvimagesink->par = g_new0 (GValue, 1);
1784 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1785 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1787 /* call XSynchronize with the current value of synchronous */
1788 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1789 xvimagesink->synchronous ? "TRUE" : "FALSE");
1790 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1791 gst_xvimagesink_update_colorbalance (xvimagesink);
1792 gst_xvimagesink_manage_event_thread (xvimagesink);
1794 case GST_STATE_CHANGE_READY_TO_PAUSED:
1796 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1798 case GST_STATE_CHANGE_PAUSED_TO_READY:
1804 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1806 switch (transition) {
1807 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1809 case GST_STATE_CHANGE_PAUSED_TO_READY:
1810 xvimagesink->fps_n = 0;
1811 xvimagesink->fps_d = 1;
1812 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1813 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1814 g_mutex_lock (xvimagesink->flow_lock);
1815 if (xvimagesink->pool)
1816 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1817 g_mutex_unlock (xvimagesink->flow_lock);
1819 case GST_STATE_CHANGE_READY_TO_NULL:
1820 gst_xvimagesink_reset (xvimagesink);
1831 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1832 GstClockTime * start, GstClockTime * end)
1834 GstXvImageSink *xvimagesink;
1836 xvimagesink = GST_XVIMAGESINK (bsink);
1838 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1839 *start = GST_BUFFER_TIMESTAMP (buf);
1840 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1841 *end = *start + GST_BUFFER_DURATION (buf);
1843 if (xvimagesink->fps_n > 0) {
1845 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1846 xvimagesink->fps_n);
1852 static GstFlowReturn
1853 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1856 GstXvImageSink *xvimagesink;
1857 GstMetaXvImage *meta;
1860 xvimagesink = GST_XVIMAGESINK (vsink);
1862 meta = gst_buffer_get_meta_xvimage (buf);
1865 /* If this buffer has been allocated using our buffer management we simply
1866 put the ximage which is in the PRIVATE pointer */
1867 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1875 /* Else we have to copy the data into our private image, */
1876 /* if we have one... */
1877 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1879 /* we should have a pool, configured in setcaps */
1880 if (xvimagesink->pool == NULL)
1883 /* take a buffer form our pool */
1884 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1885 if (res != GST_FLOW_OK)
1888 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1891 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1892 "slow copy into bufferpool buffer %p", to_put);
1894 data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1895 gst_buffer_fill (to_put, 0, data, size);
1896 gst_buffer_unmap (buf, data, size);
1899 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1904 gst_buffer_unref (to_put);
1911 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1912 ("Internal error: can't allocate images"),
1913 ("We don't have a bufferpool negotiated"));
1914 return GST_FLOW_ERROR;
1918 /* No image available. That's very bad ! */
1919 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1924 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1925 ("Failed to create output image buffer"),
1926 ("XServer allocated buffer size did not match input buffer %"
1927 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1928 gst_buffer_get_size (buf)));
1929 res = GST_FLOW_ERROR;
1934 /* No Window available to put our image into */
1935 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1936 res = GST_FLOW_ERROR;
1942 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1944 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1946 switch (GST_EVENT_TYPE (event)) {
1947 case GST_EVENT_TAG:{
1949 gchar *title = NULL;
1951 gst_event_parse_tag (event, &l);
1952 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1955 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1956 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1966 if (GST_BASE_SINK_CLASS (parent_class)->event)
1967 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1973 gst_xvimagesink_sink_query (GstPad * sinkpad, GstQuery * query)
1975 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (GST_PAD_PARENT (sinkpad));
1976 gboolean res = TRUE;
1978 switch (GST_QUERY_TYPE (query)) {
1979 case GST_QUERY_ALLOCATION:
1981 GstBufferPool *pool;
1982 GstStructure *config;
1987 gst_query_parse_allocation (query, &caps, &need_pool);
1992 g_mutex_lock (xvimagesink->flow_lock);
1993 if ((pool = xvimagesink->pool))
1994 gst_object_ref (pool);
1995 g_mutex_unlock (xvimagesink->flow_lock);
1998 const GstCaps *pcaps;
2000 /* we had a pool, check caps */
2001 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
2002 config = gst_buffer_pool_get_config (pool);
2003 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL,
2006 if (!gst_caps_is_equal (caps, pcaps)) {
2007 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
2008 /* different caps, we can't use this pool */
2009 gst_object_unref (pool);
2013 if (pool == NULL && need_pool) {
2014 GstVideoFormat format;
2017 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
2018 pool = gst_xvimage_buffer_pool_new (xvimagesink);
2020 if (!gst_video_format_parse_caps (caps, &format, &width, &height))
2023 /* the normal size of a frame */
2024 size = gst_video_format_get_size (format, width, height);
2026 config = gst_buffer_pool_get_config (pool);
2027 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 15);
2028 if (!gst_buffer_pool_set_config (pool, config))
2031 gst_query_set_allocation_params (query, size, 0, 0, 0, 15, pool);
2033 /* we also support various metadata */
2034 gst_query_add_allocation_meta (query, GST_META_API_VIDEO);
2036 gst_object_unref (pool);
2048 GST_DEBUG_OBJECT (sinkpad, "no caps specified");
2053 GST_DEBUG_OBJECT (sinkpad, "invalid caps specified");
2058 GST_DEBUG_OBJECT (sinkpad, "failed setting config");
2063 /* Interfaces stuff */
2066 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2068 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2069 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE)
2076 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2078 klass->supported = gst_xvimagesink_interface_supported;
2082 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2083 GstStructure * structure)
2085 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2088 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2090 GstVideoRectangle src, dst, result;
2091 gdouble x, y, xscale = 1.0, yscale = 1.0;
2093 event = gst_event_new_navigation (structure);
2095 /* We take the flow_lock while we look at the window */
2096 g_mutex_lock (xvimagesink->flow_lock);
2098 if (!xvimagesink->xwindow) {
2099 g_mutex_unlock (xvimagesink->flow_lock);
2103 if (xvimagesink->keep_aspect) {
2104 /* We get the frame position using the calculated geometry from _setcaps
2105 that respect pixel aspect ratios */
2106 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2107 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2108 dst.w = xvimagesink->render_rect.w;
2109 dst.h = xvimagesink->render_rect.h;
2111 gst_video_sink_center_rect (src, dst, &result, TRUE);
2112 result.x += xvimagesink->render_rect.x;
2113 result.y += xvimagesink->render_rect.y;
2115 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2118 g_mutex_unlock (xvimagesink->flow_lock);
2120 /* We calculate scaling using the original video frames geometry to include
2121 pixel aspect ratio scaling. */
2122 xscale = (gdouble) xvimagesink->video_width / result.w;
2123 yscale = (gdouble) xvimagesink->video_height / result.h;
2125 /* Converting pointer coordinates to the non scaled geometry */
2126 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2127 x = MIN (x, result.x + result.w);
2128 x = MAX (x - result.x, 0);
2129 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2130 (gdouble) x * xscale, NULL);
2132 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2133 y = MIN (y, result.y + result.h);
2134 y = MAX (y - result.y, 0);
2135 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2136 (gdouble) y * yscale, NULL);
2139 gst_pad_send_event (peer, event);
2140 gst_object_unref (peer);
2145 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2147 iface->send_event = gst_xvimagesink_navigation_send_event;
2151 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2153 XID xwindow_id = id;
2154 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2155 GstXWindow *xwindow = NULL;
2157 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2159 g_mutex_lock (xvimagesink->flow_lock);
2161 /* If we already use that window return */
2162 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2163 g_mutex_unlock (xvimagesink->flow_lock);
2167 /* If the element has not initialized the X11 context try to do so */
2168 if (!xvimagesink->xcontext &&
2169 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2170 g_mutex_unlock (xvimagesink->flow_lock);
2171 /* we have thrown a GST_ELEMENT_ERROR now */
2175 gst_xvimagesink_update_colorbalance (xvimagesink);
2177 /* If a window is there already we destroy it */
2178 if (xvimagesink->xwindow) {
2179 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2180 xvimagesink->xwindow = NULL;
2183 /* If the xid is 0 we go back to an internal window */
2184 if (xwindow_id == 0) {
2185 /* If no width/height caps nego did not happen window will be created
2186 during caps nego then */
2187 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2188 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2190 gst_xvimagesink_xwindow_new (xvimagesink,
2191 GST_VIDEO_SINK_WIDTH (xvimagesink),
2192 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2195 XWindowAttributes attr;
2197 xwindow = g_new0 (GstXWindow, 1);
2198 xwindow->win = xwindow_id;
2200 /* Set the event we want to receive and create a GC */
2201 g_mutex_lock (xvimagesink->x_lock);
2203 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2205 xwindow->width = attr.width;
2206 xwindow->height = attr.height;
2207 xwindow->internal = FALSE;
2208 if (!xvimagesink->have_render_rect) {
2209 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2210 xvimagesink->render_rect.w = attr.width;
2211 xvimagesink->render_rect.h = attr.height;
2213 if (xvimagesink->handle_events) {
2214 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2215 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2219 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2220 xwindow->win, 0, NULL);
2221 g_mutex_unlock (xvimagesink->x_lock);
2225 xvimagesink->xwindow = xwindow;
2227 g_mutex_unlock (xvimagesink->flow_lock);
2231 gst_xvimagesink_expose (GstXOverlay * overlay)
2233 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2235 GST_DEBUG ("doing expose");
2236 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2237 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2241 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2242 gboolean handle_events)
2244 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2246 xvimagesink->handle_events = handle_events;
2248 g_mutex_lock (xvimagesink->flow_lock);
2250 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2251 g_mutex_unlock (xvimagesink->flow_lock);
2255 g_mutex_lock (xvimagesink->x_lock);
2257 if (handle_events) {
2258 if (xvimagesink->xwindow->internal) {
2259 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2260 ExposureMask | StructureNotifyMask | PointerMotionMask |
2261 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2263 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2264 ExposureMask | StructureNotifyMask | PointerMotionMask |
2265 KeyPressMask | KeyReleaseMask);
2268 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2271 g_mutex_unlock (xvimagesink->x_lock);
2273 g_mutex_unlock (xvimagesink->flow_lock);
2277 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2278 gint width, gint height)
2280 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2282 /* FIXME: how about some locking? */
2283 if (width >= 0 && height >= 0) {
2284 xvimagesink->render_rect.x = x;
2285 xvimagesink->render_rect.y = y;
2286 xvimagesink->render_rect.w = width;
2287 xvimagesink->render_rect.h = height;
2288 xvimagesink->have_render_rect = TRUE;
2290 xvimagesink->render_rect.x = 0;
2291 xvimagesink->render_rect.y = 0;
2292 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2293 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2294 xvimagesink->have_render_rect = FALSE;
2299 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2301 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2302 iface->expose = gst_xvimagesink_expose;
2303 iface->handle_events = gst_xvimagesink_set_event_handling;
2304 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2307 static const GList *
2308 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2310 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2312 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2314 if (xvimagesink->xcontext)
2315 return xvimagesink->xcontext->channels_list;
2321 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2322 GstColorBalanceChannel * channel, gint value)
2324 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2326 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2327 g_return_if_fail (channel->label != NULL);
2329 xvimagesink->cb_changed = TRUE;
2331 /* Normalize val to [-1000, 1000] */
2332 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2333 (double) (channel->max_value - channel->min_value));
2335 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2336 xvimagesink->hue = value;
2337 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2338 xvimagesink->saturation = value;
2339 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2340 xvimagesink->contrast = value;
2341 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2342 xvimagesink->brightness = value;
2344 g_warning ("got an unknown channel %s", channel->label);
2348 gst_xvimagesink_update_colorbalance (xvimagesink);
2352 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2353 GstColorBalanceChannel * channel)
2355 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2358 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2359 g_return_val_if_fail (channel->label != NULL, 0);
2361 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2362 value = xvimagesink->hue;
2363 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2364 value = xvimagesink->saturation;
2365 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2366 value = xvimagesink->contrast;
2367 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2368 value = xvimagesink->brightness;
2370 g_warning ("got an unknown channel %s", channel->label);
2373 /* Normalize val to [channel->min_value, channel->max_value] */
2374 value = channel->min_value + (channel->max_value - channel->min_value) *
2375 (value + 1000) / 2000;
2381 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
2383 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2384 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2385 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2386 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2389 static const GList *
2390 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2392 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2393 static GList *list = NULL;
2396 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2398 g_list_append (list, g_object_class_find_property (klass,
2399 "autopaint-colorkey"));
2401 g_list_append (list, g_object_class_find_property (klass,
2404 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2411 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2412 guint prop_id, const GParamSpec * pspec)
2414 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2418 case PROP_AUTOPAINT_COLORKEY:
2419 case PROP_DOUBLE_BUFFER:
2421 GST_DEBUG_OBJECT (xvimagesink,
2422 "probing device list and get capabilities");
2423 if (!xvimagesink->xcontext) {
2424 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2425 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2429 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2435 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2436 guint prop_id, const GParamSpec * pspec)
2438 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2439 gboolean ret = FALSE;
2443 case PROP_AUTOPAINT_COLORKEY:
2444 case PROP_DOUBLE_BUFFER:
2446 if (xvimagesink->xcontext != NULL) {
2453 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2460 static GValueArray *
2461 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2462 guint prop_id, const GParamSpec * pspec)
2464 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2465 GValueArray *array = NULL;
2467 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2468 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2477 GValue value = { 0 };
2479 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2480 g_value_init (&value, G_TYPE_STRING);
2482 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2483 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2485 g_value_set_string (&value, adaptor_id_s);
2486 g_value_array_append (array, &value);
2487 g_free (adaptor_id_s);
2489 g_value_unset (&value);
2492 case PROP_AUTOPAINT_COLORKEY:
2493 if (xvimagesink->have_autopaint_colorkey) {
2494 GValue value = { 0 };
2496 array = g_value_array_new (2);
2497 g_value_init (&value, G_TYPE_BOOLEAN);
2498 g_value_set_boolean (&value, FALSE);
2499 g_value_array_append (array, &value);
2500 g_value_set_boolean (&value, TRUE);
2501 g_value_array_append (array, &value);
2502 g_value_unset (&value);
2505 case PROP_DOUBLE_BUFFER:
2506 if (xvimagesink->have_double_buffer) {
2507 GValue value = { 0 };
2509 array = g_value_array_new (2);
2510 g_value_init (&value, G_TYPE_BOOLEAN);
2511 g_value_set_boolean (&value, FALSE);
2512 g_value_array_append (array, &value);
2513 g_value_set_boolean (&value, TRUE);
2514 g_value_array_append (array, &value);
2515 g_value_unset (&value);
2519 if (xvimagesink->have_colorkey) {
2520 GValue value = { 0 };
2522 array = g_value_array_new (1);
2523 g_value_init (&value, GST_TYPE_INT_RANGE);
2524 gst_value_set_int_range (&value, 0, 0xffffff);
2525 g_value_array_append (array, &value);
2526 g_value_unset (&value);
2530 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2539 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2542 iface->get_properties = gst_xvimagesink_probe_get_properties;
2543 iface->probe_property = gst_xvimagesink_probe_probe_property;
2544 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2545 iface->get_values = gst_xvimagesink_probe_get_values;
2548 /* =========================================== */
2550 /* Init & Class init */
2552 /* =========================================== */
2555 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2556 const GValue * value, GParamSpec * pspec)
2558 GstXvImageSink *xvimagesink;
2560 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2562 xvimagesink = GST_XVIMAGESINK (object);
2566 xvimagesink->hue = g_value_get_int (value);
2567 xvimagesink->cb_changed = TRUE;
2568 gst_xvimagesink_update_colorbalance (xvimagesink);
2571 xvimagesink->contrast = g_value_get_int (value);
2572 xvimagesink->cb_changed = TRUE;
2573 gst_xvimagesink_update_colorbalance (xvimagesink);
2575 case PROP_BRIGHTNESS:
2576 xvimagesink->brightness = g_value_get_int (value);
2577 xvimagesink->cb_changed = TRUE;
2578 gst_xvimagesink_update_colorbalance (xvimagesink);
2580 case PROP_SATURATION:
2581 xvimagesink->saturation = g_value_get_int (value);
2582 xvimagesink->cb_changed = TRUE;
2583 gst_xvimagesink_update_colorbalance (xvimagesink);
2586 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2588 case PROP_SYNCHRONOUS:
2589 xvimagesink->synchronous = g_value_get_boolean (value);
2590 if (xvimagesink->xcontext) {
2591 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2592 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2593 xvimagesink->synchronous ? "TRUE" : "FALSE");
2596 case PROP_PIXEL_ASPECT_RATIO:
2597 g_free (xvimagesink->par);
2598 xvimagesink->par = g_new0 (GValue, 1);
2599 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2600 if (!g_value_transform (value, xvimagesink->par)) {
2601 g_warning ("Could not transform string to aspect ratio");
2602 gst_value_set_fraction (xvimagesink->par, 1, 1);
2604 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2605 gst_value_get_fraction_numerator (xvimagesink->par),
2606 gst_value_get_fraction_denominator (xvimagesink->par));
2608 case PROP_FORCE_ASPECT_RATIO:
2609 xvimagesink->keep_aspect = g_value_get_boolean (value);
2611 case PROP_HANDLE_EVENTS:
2612 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
2613 g_value_get_boolean (value));
2614 gst_xvimagesink_manage_event_thread (xvimagesink);
2617 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2619 case PROP_HANDLE_EXPOSE:
2620 xvimagesink->handle_expose = g_value_get_boolean (value);
2621 gst_xvimagesink_manage_event_thread (xvimagesink);
2623 case PROP_DOUBLE_BUFFER:
2624 xvimagesink->double_buffer = g_value_get_boolean (value);
2626 case PROP_AUTOPAINT_COLORKEY:
2627 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2630 xvimagesink->colorkey = g_value_get_int (value);
2632 case PROP_DRAW_BORDERS:
2633 xvimagesink->draw_borders = g_value_get_boolean (value);
2636 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2642 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2643 GValue * value, GParamSpec * pspec)
2645 GstXvImageSink *xvimagesink;
2647 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2649 xvimagesink = GST_XVIMAGESINK (object);
2653 g_value_set_int (value, xvimagesink->hue);
2656 g_value_set_int (value, xvimagesink->contrast);
2658 case PROP_BRIGHTNESS:
2659 g_value_set_int (value, xvimagesink->brightness);
2661 case PROP_SATURATION:
2662 g_value_set_int (value, xvimagesink->saturation);
2665 g_value_set_string (value, xvimagesink->display_name);
2667 case PROP_SYNCHRONOUS:
2668 g_value_set_boolean (value, xvimagesink->synchronous);
2670 case PROP_PIXEL_ASPECT_RATIO:
2671 if (xvimagesink->par)
2672 g_value_transform (xvimagesink->par, value);
2674 case PROP_FORCE_ASPECT_RATIO:
2675 g_value_set_boolean (value, xvimagesink->keep_aspect);
2677 case PROP_HANDLE_EVENTS:
2678 g_value_set_boolean (value, xvimagesink->handle_events);
2682 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2684 g_value_set_string (value, adaptor_no_s);
2685 g_free (adaptor_no_s);
2688 case PROP_DEVICE_NAME:
2689 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2690 g_value_set_string (value,
2691 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2693 g_value_set_string (value, NULL);
2696 case PROP_HANDLE_EXPOSE:
2697 g_value_set_boolean (value, xvimagesink->handle_expose);
2699 case PROP_DOUBLE_BUFFER:
2700 g_value_set_boolean (value, xvimagesink->double_buffer);
2702 case PROP_AUTOPAINT_COLORKEY:
2703 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2706 g_value_set_int (value, xvimagesink->colorkey);
2708 case PROP_DRAW_BORDERS:
2709 g_value_set_boolean (value, xvimagesink->draw_borders);
2711 case PROP_WINDOW_WIDTH:
2712 if (xvimagesink->xwindow)
2713 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2715 g_value_set_uint64 (value, 0);
2717 case PROP_WINDOW_HEIGHT:
2718 if (xvimagesink->xwindow)
2719 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2721 g_value_set_uint64 (value, 0);
2724 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2730 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2734 GST_OBJECT_LOCK (xvimagesink);
2735 xvimagesink->running = FALSE;
2736 /* grab thread and mark it as NULL */
2737 thread = xvimagesink->event_thread;
2738 xvimagesink->event_thread = NULL;
2739 GST_OBJECT_UNLOCK (xvimagesink);
2741 /* Wait for our event thread to finish before we clean up our stuff. */
2743 g_thread_join (thread);
2745 if (xvimagesink->cur_image) {
2746 gst_buffer_unref (xvimagesink->cur_image);
2747 xvimagesink->cur_image = NULL;
2750 g_mutex_lock (xvimagesink->flow_lock);
2752 if (xvimagesink->pool) {
2753 gst_object_unref (xvimagesink->pool);
2754 xvimagesink->pool = NULL;
2757 if (xvimagesink->xwindow) {
2758 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2759 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2760 xvimagesink->xwindow = NULL;
2762 g_mutex_unlock (xvimagesink->flow_lock);
2764 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2765 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2766 xvimagesink->have_render_rect = FALSE;
2768 gst_xvimagesink_xcontext_clear (xvimagesink);
2771 /* Finalize is called only once, dispose can be called multiple times.
2772 * We use mutexes and don't reset stuff to NULL here so let's register
2775 gst_xvimagesink_finalize (GObject * object)
2777 GstXvImageSink *xvimagesink;
2779 xvimagesink = GST_XVIMAGESINK (object);
2781 gst_xvimagesink_reset (xvimagesink);
2783 if (xvimagesink->display_name) {
2784 g_free (xvimagesink->display_name);
2785 xvimagesink->display_name = NULL;
2788 if (xvimagesink->par) {
2789 g_free (xvimagesink->par);
2790 xvimagesink->par = NULL;
2792 if (xvimagesink->x_lock) {
2793 g_mutex_free (xvimagesink->x_lock);
2794 xvimagesink->x_lock = NULL;
2796 if (xvimagesink->flow_lock) {
2797 g_mutex_free (xvimagesink->flow_lock);
2798 xvimagesink->flow_lock = NULL;
2801 g_free (xvimagesink->media_title);
2803 G_OBJECT_CLASS (parent_class)->finalize (object);
2807 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2809 /* for the ALLOCATION query */
2810 gst_pad_set_query_function (GST_BASE_SINK (xvimagesink)->sinkpad,
2811 gst_xvimagesink_sink_query);
2813 xvimagesink->display_name = NULL;
2814 xvimagesink->adaptor_no = 0;
2815 xvimagesink->xcontext = NULL;
2816 xvimagesink->xwindow = NULL;
2817 xvimagesink->cur_image = NULL;
2819 xvimagesink->hue = xvimagesink->saturation = 0;
2820 xvimagesink->contrast = xvimagesink->brightness = 0;
2821 xvimagesink->cb_changed = FALSE;
2823 xvimagesink->fps_n = 0;
2824 xvimagesink->fps_d = 0;
2825 xvimagesink->video_width = 0;
2826 xvimagesink->video_height = 0;
2828 xvimagesink->x_lock = g_mutex_new ();
2829 xvimagesink->flow_lock = g_mutex_new ();
2831 xvimagesink->pool = NULL;
2833 xvimagesink->synchronous = FALSE;
2834 xvimagesink->double_buffer = TRUE;
2835 xvimagesink->running = FALSE;
2836 xvimagesink->keep_aspect = FALSE;
2837 xvimagesink->handle_events = TRUE;
2838 xvimagesink->par = NULL;
2839 xvimagesink->handle_expose = TRUE;
2840 xvimagesink->autopaint_colorkey = TRUE;
2842 /* on 16bit displays this becomes r,g,b = 1,2,3
2843 * on 24bit displays this becomes r,g,b = 8,8,16
2844 * as a port atom value
2846 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2847 xvimagesink->draw_borders = TRUE;
2851 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2853 GObjectClass *gobject_class;
2854 GstElementClass *gstelement_class;
2855 GstBaseSinkClass *gstbasesink_class;
2856 GstVideoSinkClass *videosink_class;
2858 gobject_class = (GObjectClass *) klass;
2859 gstelement_class = (GstElementClass *) klass;
2860 gstbasesink_class = (GstBaseSinkClass *) klass;
2861 videosink_class = (GstVideoSinkClass *) klass;
2863 parent_class = g_type_class_peek_parent (klass);
2865 gobject_class->set_property = gst_xvimagesink_set_property;
2866 gobject_class->get_property = gst_xvimagesink_get_property;
2868 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2869 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2870 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2871 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2872 g_param_spec_int ("brightness", "Brightness",
2873 "The brightness of the video", -1000, 1000, 0,
2874 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2875 g_object_class_install_property (gobject_class, PROP_HUE,
2876 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2877 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2878 g_object_class_install_property (gobject_class, PROP_SATURATION,
2879 g_param_spec_int ("saturation", "Saturation",
2880 "The saturation of the video", -1000, 1000, 0,
2881 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2882 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2883 g_param_spec_string ("display", "Display", "X Display name", NULL,
2884 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2885 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2886 g_param_spec_boolean ("synchronous", "Synchronous",
2887 "When enabled, runs "
2888 "the X display in synchronous mode. (used only for debugging)", FALSE,
2889 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2890 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2891 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2892 "The pixel aspect ratio of the device", "1/1",
2893 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2894 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2895 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2896 "When enabled, scaling will respect original aspect ratio", FALSE,
2897 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2898 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2899 g_param_spec_boolean ("handle-events", "Handle XEvents",
2900 "When enabled, XEvents will be selected and handled", TRUE,
2901 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2902 g_object_class_install_property (gobject_class, PROP_DEVICE,
2903 g_param_spec_string ("device", "Adaptor number",
2904 "The number of the video adaptor", "0",
2905 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2906 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2907 g_param_spec_string ("device-name", "Adaptor name",
2908 "The name of the video adaptor", NULL,
2909 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2911 * GstXvImageSink:handle-expose
2913 * When enabled, the current frame will always be drawn in response to X
2918 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2919 g_param_spec_boolean ("handle-expose", "Handle expose",
2921 "the current frame will always be drawn in response to X Expose "
2922 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2925 * GstXvImageSink:double-buffer
2927 * Whether to double-buffer the output.
2931 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2932 g_param_spec_boolean ("double-buffer", "Double-buffer",
2933 "Whether to double-buffer the output", TRUE,
2934 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2936 * GstXvImageSink:autopaint-colorkey
2938 * Whether to autofill overlay with colorkey
2942 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2943 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2944 "Whether to autofill overlay with colorkey", TRUE,
2945 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2947 * GstXvImageSink:colorkey
2949 * Color to use for the overlay mask.
2953 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2954 g_param_spec_int ("colorkey", "Colorkey",
2955 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2956 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2959 * GstXvImageSink:draw-borders
2961 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2962 * unused parts of the video area.
2966 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2967 g_param_spec_boolean ("draw-borders", "Colorkey",
2968 "Draw black borders to fill unused area in force-aspect-ratio mode",
2969 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2972 * GstXvImageSink:window-width
2974 * Actual width of the video window.
2978 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2979 g_param_spec_uint64 ("window-width", "window-width",
2980 "Width of the window", 0, G_MAXUINT64, 0,
2981 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2984 * GstXvImageSink:window-height
2986 * Actual height of the video window.
2990 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2991 g_param_spec_uint64 ("window-height", "window-height",
2992 "Height of the window", 0, G_MAXUINT64, 0,
2993 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2995 gobject_class->finalize = gst_xvimagesink_finalize;
2997 gst_element_class_set_details_simple (gstelement_class,
2998 "Video sink", "Sink/Video",
2999 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
3001 gst_element_class_add_pad_template (gstelement_class,
3002 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
3004 gstelement_class->change_state =
3005 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3007 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3008 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3009 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3010 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3012 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);