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 theoretically
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, 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 */
112 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
113 * with newer GLib versions (>= 2.31.0) */
114 #define GLIB_DISABLE_DEPRECATION_WARNINGS
121 #include <gst/video/navigation.h>
122 #include <gst/video/videooverlay.h>
123 #include <gst/video/colorbalance.h>
124 /* Helper functions */
125 #include <gst/video/gstvideometa.h>
128 #include "xvimagesink.h"
130 /* Debugging category */
131 #include <gst/gstinfo.h>
133 #include "gst/glib-compat-private.h"
135 /* for XkbKeycodeToKeysym */
136 #include <X11/XKBlib.h>
138 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
139 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
140 #define GST_CAT_DEFAULT gst_debug_xvimagesink
145 unsigned long functions;
146 unsigned long decorations;
148 unsigned long status;
150 MotifWmHints, MwmHints;
152 #define MWM_HINTS_DECORATIONS (1L << 1)
154 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
155 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
157 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
159 /* Default template - initiated with class struct to allow gst-register to work
161 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
162 GST_STATIC_PAD_TEMPLATE ("sink",
165 GST_STATIC_CAPS ("video/x-raw, "
166 "framerate = (fraction) [ 0, MAX ], "
167 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
179 PROP_PIXEL_ASPECT_RATIO,
180 PROP_FORCE_ASPECT_RATIO,
186 PROP_AUTOPAINT_COLORKEY,
193 /* ============================================================= */
197 /* ============================================================= */
199 /* =========================================== */
201 /* Object typing & Creation */
203 /* =========================================== */
204 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
205 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
207 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
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_NAVIGATION,
212 gst_xvimagesink_navigation_init);
213 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
214 gst_xvimagesink_video_overlay_init);
215 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
216 gst_xvimagesink_colorbalance_init));
219 /* ============================================================= */
221 /* Private Methods */
223 /* ============================================================= */
226 /* We are called with the x_lock taken */
228 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
229 GstXWindow * xwindow, GstVideoRectangle rect)
233 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
234 g_return_if_fail (xwindow != NULL);
236 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
237 xvimagesink->xcontext->black);
240 if (rect.x > xvimagesink->render_rect.x) {
241 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
242 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
243 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
247 t1 = rect.x + rect.w;
248 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
250 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
251 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
255 if (rect.y > xvimagesink->render_rect.y) {
256 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
257 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
258 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
262 t1 = rect.y + rect.h;
263 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
265 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
266 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
270 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
271 * if no window was available */
273 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
275 GstXvImageMeta *meta;
276 GstVideoCropMeta *crop;
277 GstVideoRectangle result;
278 gboolean draw_border = FALSE;
279 GstVideoRectangle src, dst;
281 /* We take the flow_lock. If expose is in there we don't want to run
282 concurrently from the data flow thread */
283 g_mutex_lock (xvimagesink->flow_lock);
285 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
286 g_mutex_unlock (xvimagesink->flow_lock);
290 /* Draw borders when displaying the first frame. After this
291 draw borders only on expose event or after a size change. */
292 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
296 /* Store a reference to the last image we put, lose the previous one */
297 if (xvimage && xvimagesink->cur_image != xvimage) {
298 if (xvimagesink->cur_image) {
299 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
300 gst_buffer_unref (xvimagesink->cur_image);
302 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
303 xvimagesink->cur_image = gst_buffer_ref (xvimage);
306 /* Expose sends a NULL image, we take the latest frame */
308 if (xvimagesink->cur_image) {
310 xvimage = xvimagesink->cur_image;
312 g_mutex_unlock (xvimagesink->flow_lock);
317 meta = gst_buffer_get_xvimage_meta (xvimage);
319 crop = gst_buffer_get_video_crop_meta (xvimage);
322 src.x = crop->x + meta->x;
323 src.y = crop->y + meta->y;
325 src.h = crop->height;
326 GST_LOG_OBJECT (xvimagesink,
327 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
332 src.h = meta->height;
335 if (xvimagesink->keep_aspect) {
338 /* We take the size of the source material as it was negotiated and
339 * corrected for DAR. This size can be different from the cropped size in
340 * which case the image will be scaled to fit the negotiated size. */
341 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
342 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
343 dst.w = xvimagesink->render_rect.w;
344 dst.h = xvimagesink->render_rect.h;
346 gst_video_sink_center_rect (s, dst, &result, TRUE);
347 result.x += xvimagesink->render_rect.x;
348 result.y += xvimagesink->render_rect.y;
350 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
353 g_mutex_lock (xvimagesink->x_lock);
355 if (draw_border && xvimagesink->draw_borders) {
356 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
358 xvimagesink->redraw_border = FALSE;
361 if (xvimagesink->xcontext->use_xshm) {
362 GST_LOG_OBJECT (xvimagesink,
363 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
364 GST_PTR_FORMAT, meta->width, meta->height,
365 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
367 XvShmPutImage (xvimagesink->xcontext->disp,
368 xvimagesink->xcontext->xv_port_id,
369 xvimagesink->xwindow->win,
370 xvimagesink->xwindow->gc, meta->xvimage,
371 src.x, src.y, src.w, src.h,
372 result.x, result.y, result.w, result.h, FALSE);
374 #endif /* HAVE_XSHM */
376 XvPutImage (xvimagesink->xcontext->disp,
377 xvimagesink->xcontext->xv_port_id,
378 xvimagesink->xwindow->win,
379 xvimagesink->xwindow->gc, meta->xvimage,
380 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
383 XSync (xvimagesink->xcontext->disp, FALSE);
385 g_mutex_unlock (xvimagesink->x_lock);
387 g_mutex_unlock (xvimagesink->flow_lock);
393 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
396 Atom hints_atom = None;
399 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
400 g_return_val_if_fail (window != NULL, FALSE);
402 g_mutex_lock (xvimagesink->x_lock);
404 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
406 if (hints_atom == None) {
407 g_mutex_unlock (xvimagesink->x_lock);
411 hints = g_malloc0 (sizeof (MotifWmHints));
413 hints->flags |= MWM_HINTS_DECORATIONS;
414 hints->decorations = 1 << 0;
416 XChangeProperty (xvimagesink->xcontext->disp, window->win,
417 hints_atom, hints_atom, 32, PropModeReplace,
418 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
420 XSync (xvimagesink->xcontext->disp, FALSE);
422 g_mutex_unlock (xvimagesink->x_lock);
430 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
431 GstXWindow * xwindow, const gchar * media_title)
434 g_free (xvimagesink->media_title);
435 xvimagesink->media_title = g_strdup (media_title);
438 /* we have a window */
439 if (xwindow->internal) {
440 XTextProperty xproperty;
441 const gchar *app_name;
442 const gchar *title = NULL;
443 gchar *title_mem = NULL;
445 /* set application name as a title */
446 app_name = g_get_application_name ();
448 if (app_name && xvimagesink->media_title) {
449 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
451 } else if (app_name) {
453 } else if (xvimagesink->media_title) {
454 title = xvimagesink->media_title;
458 if ((XStringListToTextProperty (((char **) &title), 1,
460 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
461 XFree (xproperty.value);
470 /* This function handles a GstXWindow creation
471 * The width and height are the actual pixel size on the display */
473 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
474 gint width, gint height)
476 GstXWindow *xwindow = NULL;
479 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
481 xwindow = g_new0 (GstXWindow, 1);
483 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
484 xvimagesink->render_rect.w = width;
485 xvimagesink->render_rect.h = height;
487 xwindow->width = width;
488 xwindow->height = height;
489 xwindow->internal = TRUE;
491 g_mutex_lock (xvimagesink->x_lock);
493 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
494 xvimagesink->xcontext->root,
495 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
497 /* We have to do that to prevent X from redrawing the background on
498 * ConfigureNotify. This takes away flickering of video when resizing. */
499 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
501 /* set application name as a title */
502 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
504 if (xvimagesink->handle_events) {
507 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
508 StructureNotifyMask | PointerMotionMask | KeyPressMask |
509 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
511 /* Tell the window manager we'd like delete client messages instead of
513 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
514 "WM_DELETE_WINDOW", True);
515 if (wm_delete != None) {
516 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
521 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
522 xwindow->win, 0, &values);
524 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
526 XSync (xvimagesink->xcontext->disp, FALSE);
528 g_mutex_unlock (xvimagesink->x_lock);
530 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
532 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
538 /* This function destroys a GstXWindow */
540 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
541 GstXWindow * xwindow)
543 g_return_if_fail (xwindow != NULL);
544 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
546 g_mutex_lock (xvimagesink->x_lock);
548 /* If we did not create that window we just free the GC and let it live */
549 if (xwindow->internal)
550 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
552 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
554 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
556 XSync (xvimagesink->xcontext->disp, FALSE);
558 g_mutex_unlock (xvimagesink->x_lock);
564 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
566 XWindowAttributes attr;
568 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
570 /* Update the window geometry */
571 g_mutex_lock (xvimagesink->x_lock);
572 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
573 g_mutex_unlock (xvimagesink->x_lock);
577 XGetWindowAttributes (xvimagesink->xcontext->disp,
578 xvimagesink->xwindow->win, &attr);
580 xvimagesink->xwindow->width = attr.width;
581 xvimagesink->xwindow->height = attr.height;
583 if (!xvimagesink->have_render_rect) {
584 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
585 xvimagesink->render_rect.w = attr.width;
586 xvimagesink->render_rect.h = attr.height;
589 g_mutex_unlock (xvimagesink->x_lock);
593 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
594 GstXWindow * xwindow)
596 g_return_if_fail (xwindow != NULL);
597 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
599 g_mutex_lock (xvimagesink->x_lock);
601 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
604 XSync (xvimagesink->xcontext->disp, FALSE);
606 g_mutex_unlock (xvimagesink->x_lock);
609 /* This function commits our internal colorbalance settings to our grabbed Xv
610 port. If the xcontext is not initialized yet it simply returns */
612 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
614 GList *channels = NULL;
616 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
618 /* If we haven't initialized the X context we can't update anything */
619 if (xvimagesink->xcontext == NULL)
622 /* Don't set the attributes if they haven't been changed, to avoid
623 * rounding errors changing the values */
624 if (!xvimagesink->cb_changed)
627 /* For each channel of the colorbalance we calculate the correct value
628 doing range conversion and then set the Xv port attribute to match our
630 channels = xvimagesink->xcontext->channels_list;
633 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
634 GstColorBalanceChannel *channel = NULL;
637 gdouble convert_coef;
639 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
640 g_object_ref (channel);
642 /* Our range conversion coef */
643 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
645 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
646 value = xvimagesink->hue;
647 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
648 value = xvimagesink->saturation;
649 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
650 value = xvimagesink->contrast;
651 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
652 value = xvimagesink->brightness;
654 g_warning ("got an unknown channel %s", channel->label);
655 g_object_unref (channel);
659 /* Committing to Xv port */
660 g_mutex_lock (xvimagesink->x_lock);
662 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
663 if (prop_atom != None) {
666 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
667 XvSetPortAttribute (xvimagesink->xcontext->disp,
668 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
670 g_mutex_unlock (xvimagesink->x_lock);
672 g_object_unref (channel);
674 channels = g_list_next (channels);
678 /* This function handles XEvents that might be in the queue. It generates
679 GstEvent that will be sent upstream in the pipeline to handle interactivity
680 and navigation. It will also listen for configure events on the window to
681 trigger caps renegotiation so on the fly software scaling can work. */
683 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
686 guint pointer_x = 0, pointer_y = 0;
687 gboolean pointer_moved = FALSE;
688 gboolean exposed = FALSE, configured = FALSE;
690 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
692 /* Handle Interaction, produces navigation events */
694 /* We get all pointer motion events, only the last position is
696 g_mutex_lock (xvimagesink->flow_lock);
697 g_mutex_lock (xvimagesink->x_lock);
698 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
699 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
700 g_mutex_unlock (xvimagesink->x_lock);
701 g_mutex_unlock (xvimagesink->flow_lock);
705 pointer_x = e.xmotion.x;
706 pointer_y = e.xmotion.y;
707 pointer_moved = TRUE;
712 g_mutex_lock (xvimagesink->flow_lock);
713 g_mutex_lock (xvimagesink->x_lock);
717 g_mutex_unlock (xvimagesink->x_lock);
718 g_mutex_unlock (xvimagesink->flow_lock);
720 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
721 pointer_x, pointer_y);
722 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
723 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
725 g_mutex_lock (xvimagesink->flow_lock);
726 g_mutex_lock (xvimagesink->x_lock);
729 /* We get all events on our window to throw them upstream */
730 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
731 xvimagesink->xwindow->win,
732 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
735 const char *key_str = NULL;
737 /* We lock only for the X function call */
738 g_mutex_unlock (xvimagesink->x_lock);
739 g_mutex_unlock (xvimagesink->flow_lock);
743 /* Mouse button pressed over our window. We send upstream
744 events for interactivity/navigation */
745 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
746 e.xbutton.button, e.xbutton.x, e.xbutton.y);
747 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
748 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
751 /* Mouse button released over our window. We send upstream
752 events for interactivity/navigation */
753 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
754 e.xbutton.button, e.xbutton.x, e.xbutton.y);
755 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
756 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
760 /* Key pressed/released over our window. We send upstream
761 events for interactivity/navigation */
762 g_mutex_lock (xvimagesink->x_lock);
763 keysym = XkbKeycodeToKeysym (xvimagesink->xcontext->disp,
764 e.xkey.keycode, 0, 0);
765 if (keysym != NoSymbol) {
766 key_str = XKeysymToString (keysym);
770 g_mutex_unlock (xvimagesink->x_lock);
771 GST_DEBUG_OBJECT (xvimagesink,
772 "key %d pressed over window at %d,%d (%s)",
773 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
774 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
775 e.type == KeyPress ? "key-press" : "key-release", key_str);
778 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
781 g_mutex_lock (xvimagesink->flow_lock);
782 g_mutex_lock (xvimagesink->x_lock);
786 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
787 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
792 case ConfigureNotify:
793 g_mutex_unlock (xvimagesink->x_lock);
794 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
795 g_mutex_lock (xvimagesink->x_lock);
803 if (xvimagesink->handle_expose && (exposed || configured)) {
804 g_mutex_unlock (xvimagesink->x_lock);
805 g_mutex_unlock (xvimagesink->flow_lock);
807 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
809 g_mutex_lock (xvimagesink->flow_lock);
810 g_mutex_lock (xvimagesink->x_lock);
813 /* Handle Display events */
814 while (XPending (xvimagesink->xcontext->disp)) {
815 XNextEvent (xvimagesink->xcontext->disp, &e);
821 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
822 "WM_DELETE_WINDOW", True);
823 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
824 /* Handle window deletion by posting an error on the bus */
825 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
826 ("Output window was closed"), (NULL));
828 g_mutex_unlock (xvimagesink->x_lock);
829 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
830 xvimagesink->xwindow = NULL;
831 g_mutex_lock (xvimagesink->x_lock);
840 g_mutex_unlock (xvimagesink->x_lock);
841 g_mutex_unlock (xvimagesink->flow_lock);
845 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
846 XvAdaptorInfo * adaptors, int adaptor_no)
851 /* Do we support XvImageMask ? */
852 if (!(adaptors[adaptor_no].type & XvImageMask)) {
853 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
854 adaptors[adaptor_no].name);
858 /* We found such an adaptor, looking for an available port */
859 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
860 /* We try to grab the port */
861 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
862 if (Success == res) {
863 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
864 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
865 adaptors[adaptor_no].num_ports);
867 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
868 adaptors[adaptor_no].name, res);
873 /* This function generates a caps with all supported format by the first
874 Xv grabable port we find. We store each one of the supported formats in a
875 format list and append the format to a newly created caps that we return
876 If this function does not return NULL because of an error, it also grabs
877 the port via XvGrabPort */
879 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
880 GstXContext * xcontext)
883 XvAdaptorInfo *adaptors;
885 XvImageFormatValues *formats = NULL;
887 XvEncodingInfo *encodings = NULL;
888 gulong max_w = G_MAXINT, max_h = G_MAXINT;
889 GstCaps *caps = NULL;
890 GstCaps *rgb_caps = NULL;
892 g_return_val_if_fail (xcontext != NULL, NULL);
894 /* First let's check that XVideo extension is available */
895 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
896 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
897 ("Could not initialise Xv output"),
898 ("XVideo extension is not available"));
902 /* Then we get adaptors list */
903 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
904 &xcontext->nb_adaptors, &adaptors)) {
905 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
906 ("Could not initialise Xv output"),
907 ("Failed getting XV adaptors list"));
911 xcontext->xv_port_id = 0;
913 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
916 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
918 /* Now fill up our adaptor name array */
919 for (i = 0; i < xcontext->nb_adaptors; i++) {
920 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
923 if (xvimagesink->adaptor_no != -1 &&
924 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
925 /* Find xv port from user defined adaptor */
926 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
927 xvimagesink->adaptor_no);
930 if (!xcontext->xv_port_id) {
931 /* Now search for an adaptor that supports XvImageMask */
932 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
933 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
934 xvimagesink->adaptor_no = i;
938 XvFreeAdaptorInfo (adaptors);
940 if (!xcontext->xv_port_id) {
941 xvimagesink->adaptor_no = -1;
942 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
943 ("Could not initialise Xv output"), ("No port available"));
947 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
950 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
951 xcontext->xv_port_id, &count);
952 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
953 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
954 static const char colorkey[] = "XV_COLORKEY";
956 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
958 xvimagesink->have_autopaint_colorkey = FALSE;
959 xvimagesink->have_double_buffer = FALSE;
960 xvimagesink->have_colorkey = FALSE;
962 for (i = 0; ((i < count) && todo); i++)
963 if (!strcmp (attr[i].name, autopaint)) {
964 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
966 /* turn on autopaint colorkey */
967 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
968 (xvimagesink->autopaint_colorkey ? 1 : 0));
970 xvimagesink->have_autopaint_colorkey = TRUE;
971 } else if (!strcmp (attr[i].name, dbl_buffer)) {
972 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
974 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
975 (xvimagesink->double_buffer ? 1 : 0));
977 xvimagesink->have_double_buffer = TRUE;
978 } else if (!strcmp (attr[i].name, colorkey)) {
979 /* Set the colorkey, default is something that is dark but hopefully
980 * won't randomly appear on the screen elsewhere (ie not black or greys)
981 * can be overridden by setting "colorkey" property
983 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
985 gboolean set_attr = TRUE;
988 /* set a colorkey in the right format RGB565/RGB888
989 * We only handle these 2 cases, because they're the only types of
990 * devices we've encountered. If we don't recognise it, leave it alone
992 cr = (xvimagesink->colorkey >> 16);
993 cg = (xvimagesink->colorkey >> 8) & 0xFF;
994 cb = (xvimagesink->colorkey) & 0xFF;
995 switch (xcontext->depth) {
996 case 16: /* RGB 565 */
1000 ckey = (cr << 11) | (cg << 5) | cb;
1003 case 32: /* RGB 888 / ARGB 8888 */
1004 ckey = (cr << 16) | (cg << 8) | cb;
1007 GST_DEBUG_OBJECT (xvimagesink,
1008 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1015 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1016 (guint32) attr[i].max_value);
1017 GST_LOG_OBJECT (xvimagesink,
1018 "Setting color key for display depth %d to 0x%x",
1019 xcontext->depth, ckey);
1021 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1025 xvimagesink->have_colorkey = TRUE;
1031 /* Get the list of encodings supported by the adapter and look for the
1032 * XV_IMAGE encoding so we can determine the maximum width and height
1034 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1037 for (i = 0; i < nb_encodings; i++) {
1038 GST_LOG_OBJECT (xvimagesink,
1039 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1040 i, encodings[i].name, encodings[i].width, encodings[i].height,
1041 encodings[i].rate.numerator, encodings[i].rate.denominator);
1042 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1043 max_w = encodings[i].width;
1044 max_h = encodings[i].height;
1048 XvFreeEncodingInfo (encodings);
1050 /* We get all image formats supported by our port */
1051 formats = XvListImageFormats (xcontext->disp,
1052 xcontext->xv_port_id, &nb_formats);
1053 caps = gst_caps_new_empty ();
1054 for (i = 0; i < nb_formats; i++) {
1055 GstCaps *format_caps = NULL;
1056 gboolean is_rgb_format = FALSE;
1057 GstVideoFormat vformat;
1059 /* We set the image format of the xcontext to an existing one. This
1060 is just some valid image format for making our xshm calls check before
1061 caps negotiation really happens. */
1062 xcontext->im_format = formats[i].id;
1064 switch (formats[i].type) {
1067 XvImageFormatValues *fmt = &(formats[i]);
1071 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1073 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1074 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1075 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1078 format_caps = gst_caps_new_simple ("video/x-raw",
1079 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1080 "width", GST_TYPE_INT_RANGE, 1, max_w,
1081 "height", GST_TYPE_INT_RANGE, 1, max_h,
1082 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1084 is_rgb_format = TRUE;
1089 vformat = gst_video_format_from_fourcc (formats[i].id);
1090 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1093 format_caps = gst_caps_new_simple ("video/x-raw",
1094 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1095 "width", GST_TYPE_INT_RANGE, 1, max_w,
1096 "height", GST_TYPE_INT_RANGE, 1, max_h,
1097 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1101 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1102 g_assert_not_reached ();
1107 GstXvImageFormat *format = NULL;
1109 format = g_new0 (GstXvImageFormat, 1);
1111 format->format = formats[i].id;
1112 format->vformat = vformat;
1113 format->caps = gst_caps_copy (format_caps);
1114 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1117 if (is_rgb_format) {
1118 if (rgb_caps == NULL)
1119 rgb_caps = format_caps;
1121 gst_caps_append (rgb_caps, format_caps);
1123 gst_caps_append (caps, format_caps);
1127 /* Collected all caps into either the caps or rgb_caps structures.
1128 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1130 gst_caps_append (caps, rgb_caps);
1135 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1137 if (gst_caps_is_empty (caps)) {
1138 gst_caps_unref (caps);
1139 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1140 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1141 ("No supported format found"));
1149 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1151 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1153 GST_OBJECT_LOCK (xvimagesink);
1154 while (xvimagesink->running) {
1155 GST_OBJECT_UNLOCK (xvimagesink);
1157 if (xvimagesink->xwindow) {
1158 gst_xvimagesink_handle_xevents (xvimagesink);
1160 /* FIXME: do we want to align this with the framerate or anything else? */
1161 g_usleep (G_USEC_PER_SEC / 20);
1163 GST_OBJECT_LOCK (xvimagesink);
1165 GST_OBJECT_UNLOCK (xvimagesink);
1171 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1173 GThread *thread = NULL;
1175 /* don't start the thread too early */
1176 if (xvimagesink->xcontext == NULL) {
1180 GST_OBJECT_LOCK (xvimagesink);
1181 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1182 if (!xvimagesink->event_thread) {
1183 /* Setup our event listening thread */
1184 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1185 xvimagesink->handle_expose, xvimagesink->handle_events);
1186 xvimagesink->running = TRUE;
1187 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1188 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1191 if (xvimagesink->event_thread) {
1192 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1193 xvimagesink->handle_expose, xvimagesink->handle_events);
1194 xvimagesink->running = FALSE;
1195 /* grab thread and mark it as NULL */
1196 thread = xvimagesink->event_thread;
1197 xvimagesink->event_thread = NULL;
1200 GST_OBJECT_UNLOCK (xvimagesink);
1202 /* Wait for our event thread to finish */
1204 g_thread_join (thread);
1209 /* This function calculates the pixel aspect ratio based on the properties
1210 * in the xcontext structure and stores it there. */
1212 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1214 static const gint par[][2] = {
1215 {1, 1}, /* regular screen */
1216 {16, 15}, /* PAL TV */
1217 {11, 10}, /* 525 line Rec.601 video */
1218 {54, 59}, /* 625 line Rec.601 video */
1219 {64, 45}, /* 1280x1024 on 16:9 display */
1220 {5, 3}, /* 1280x1024 on 4:3 display */
1221 {4, 3} /* 800x600 on 16:9 display */
1228 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1230 /* first calculate the "real" ratio based on the X values;
1231 * which is the "physical" w/h divided by the w/h in pixels of the display */
1232 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1233 / (xcontext->heightmm * xcontext->width);
1235 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1237 if (xcontext->width == 720 && xcontext->height == 576) {
1238 ratio = 4.0 * 576 / (3.0 * 720);
1240 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1242 /* now find the one from par[][2] with the lowest delta to the real one */
1246 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1247 gdouble this_delta = DELTA (i);
1249 if (this_delta < delta) {
1255 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1256 par[index][0], par[index][1]);
1258 g_free (xcontext->par);
1259 xcontext->par = g_new0 (GValue, 1);
1260 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1261 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1262 GST_DEBUG ("set xcontext PAR to %d/%d",
1263 gst_value_get_fraction_numerator (xcontext->par),
1264 gst_value_get_fraction_denominator (xcontext->par));
1267 /* This function gets the X Display and global info about it. Everything is
1268 stored in our object and will be cleaned when the object is disposed. Note
1269 here that caps for supported format are generated without any window or
1271 static GstXContext *
1272 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1274 GstXContext *xcontext = NULL;
1275 XPixmapFormatValues *px_formats = NULL;
1276 gint nb_formats = 0, i, j, N_attr;
1277 XvAttribute *xv_attr;
1279 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1280 "XV_BRIGHTNESS", "XV_CONTRAST"
1283 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1285 xcontext = g_new0 (GstXContext, 1);
1286 xcontext->im_format = 0;
1288 g_mutex_lock (xvimagesink->x_lock);
1290 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1292 if (!xcontext->disp) {
1293 g_mutex_unlock (xvimagesink->x_lock);
1295 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1296 ("Could not initialise Xv output"), ("Could not open display"));
1300 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1301 xcontext->screen_num = DefaultScreen (xcontext->disp);
1302 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1303 xcontext->root = DefaultRootWindow (xcontext->disp);
1304 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1305 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1306 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1308 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1309 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1310 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1311 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1313 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1314 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1316 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1317 /* We get supported pixmap formats at supported depth */
1318 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1321 XCloseDisplay (xcontext->disp);
1322 g_mutex_unlock (xvimagesink->x_lock);
1323 g_free (xcontext->par);
1325 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1326 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1330 /* We get bpp value corresponding to our running depth */
1331 for (i = 0; i < nb_formats; i++) {
1332 if (px_formats[i].depth == xcontext->depth)
1333 xcontext->bpp = px_formats[i].bits_per_pixel;
1338 xcontext->endianness =
1339 (ImageByteOrder (xcontext->disp) ==
1340 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1342 /* our caps system handles 24/32bpp RGB as big-endian. */
1343 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1344 xcontext->endianness == G_LITTLE_ENDIAN) {
1345 xcontext->endianness = G_BIG_ENDIAN;
1346 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1347 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1348 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1349 if (xcontext->bpp == 24) {
1350 xcontext->visual->red_mask >>= 8;
1351 xcontext->visual->green_mask >>= 8;
1352 xcontext->visual->blue_mask >>= 8;
1356 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1358 /* Search for XShm extension support */
1360 if (XShmQueryExtension (xcontext->disp) &&
1361 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1362 xcontext->use_xshm = TRUE;
1363 GST_DEBUG ("xvimagesink is using XShm extension");
1365 #endif /* HAVE_XSHM */
1367 xcontext->use_xshm = FALSE;
1368 GST_DEBUG ("xvimagesink is not using XShm extension");
1371 if (!xcontext->caps) {
1372 XCloseDisplay (xcontext->disp);
1373 g_mutex_unlock (xvimagesink->x_lock);
1374 g_free (xcontext->par);
1376 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1380 xv_attr = XvQueryPortAttributes (xcontext->disp,
1381 xcontext->xv_port_id, &N_attr);
1384 /* Generate the channels list */
1385 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1386 XvAttribute *matching_attr = NULL;
1388 /* Retrieve the property atom if it exists. If it doesn't exist,
1389 * the attribute itself must not either, so we can skip */
1390 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1391 if (prop_atom == None)
1394 if (xv_attr != NULL) {
1395 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1396 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1397 matching_attr = xv_attr + j;
1400 if (matching_attr) {
1401 GstColorBalanceChannel *channel;
1403 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1404 channel->label = g_strdup (channels[i]);
1405 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1406 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1408 xcontext->channels_list = g_list_append (xcontext->channels_list,
1411 /* If the colorbalance settings have not been touched we get Xv values
1412 as defaults and update our internal variables */
1413 if (!xvimagesink->cb_changed) {
1416 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1418 /* Normalize val to [-1000, 1000] */
1419 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1420 (double) (channel->max_value - channel->min_value));
1422 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1423 xvimagesink->hue = val;
1424 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1425 xvimagesink->saturation = val;
1426 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1427 xvimagesink->brightness = val;
1428 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1429 xvimagesink->contrast = val;
1437 g_mutex_unlock (xvimagesink->x_lock);
1442 /* This function cleans the X context. Closing the Display, releasing the XV
1443 port and unrefing the caps for supported formats. */
1445 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1447 GList *formats_list, *channels_list;
1448 GstXContext *xcontext;
1451 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1453 GST_OBJECT_LOCK (xvimagesink);
1454 if (xvimagesink->xcontext == NULL) {
1455 GST_OBJECT_UNLOCK (xvimagesink);
1459 /* Take the XContext from the sink and clean it up */
1460 xcontext = xvimagesink->xcontext;
1461 xvimagesink->xcontext = NULL;
1463 GST_OBJECT_UNLOCK (xvimagesink);
1466 formats_list = xcontext->formats_list;
1468 while (formats_list) {
1469 GstXvImageFormat *format = formats_list->data;
1471 gst_caps_unref (format->caps);
1473 formats_list = g_list_next (formats_list);
1476 if (xcontext->formats_list)
1477 g_list_free (xcontext->formats_list);
1479 channels_list = xcontext->channels_list;
1481 while (channels_list) {
1482 GstColorBalanceChannel *channel = channels_list->data;
1484 g_object_unref (channel);
1485 channels_list = g_list_next (channels_list);
1488 if (xcontext->channels_list)
1489 g_list_free (xcontext->channels_list);
1491 gst_caps_unref (xcontext->caps);
1492 if (xcontext->last_caps)
1493 gst_caps_replace (&xcontext->last_caps, NULL);
1495 for (i = 0; i < xcontext->nb_adaptors; i++) {
1496 g_free (xcontext->adaptors[i]);
1499 g_free (xcontext->adaptors);
1501 g_free (xcontext->par);
1503 g_mutex_lock (xvimagesink->x_lock);
1505 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1507 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1509 XCloseDisplay (xcontext->disp);
1511 g_mutex_unlock (xvimagesink->x_lock);
1519 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1521 GstXvImageSink *xvimagesink;
1524 xvimagesink = GST_XVIMAGESINK (bsink);
1526 if (xvimagesink->xcontext) {
1528 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1529 GST_CAPS_INTERSECT_FIRST);
1531 return gst_caps_ref (xvimagesink->xcontext->caps);
1534 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1536 GstCaps *intersection;
1539 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1540 gst_caps_unref (caps);
1541 caps = intersection;
1547 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1549 GstXvImageSink *xvimagesink;
1550 GstStructure *structure;
1551 GstBufferPool *newpool, *oldpool;
1553 guint32 im_format = 0;
1554 gint video_par_n, video_par_d; /* video's PAR */
1555 gint display_par_n, display_par_d; /* display's PAR */
1558 static GstAllocationParams params = { 0, 15, 0, 0, };
1560 xvimagesink = GST_XVIMAGESINK (bsink);
1562 GST_DEBUG_OBJECT (xvimagesink,
1563 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1564 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1566 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1567 goto incompatible_caps;
1569 if (!gst_video_info_from_caps (&info, caps))
1570 goto invalid_format;
1572 xvimagesink->fps_n = info.fps_n;
1573 xvimagesink->fps_d = info.fps_d;
1575 xvimagesink->video_width = info.width;
1576 xvimagesink->video_height = info.height;
1578 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1579 if (im_format == -1)
1580 goto invalid_format;
1584 /* get aspect ratio from caps if it's present, and
1585 * convert video width and height to a display width and height
1586 * using wd / hd = wv / hv * PARv / PARd */
1588 /* get video's PAR */
1589 video_par_n = info.par_n;
1590 video_par_d = info.par_d;
1592 /* get display's PAR */
1593 if (xvimagesink->par) {
1594 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1595 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1601 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1602 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1605 GST_DEBUG_OBJECT (xvimagesink,
1606 "video width/height: %dx%d, calculated display ratio: %d/%d",
1607 info.width, info.height, num, den);
1609 /* now find a width x height that respects this display ratio.
1610 * prefer those that have one of w/h the same as the incoming video
1611 * using wd / hd = num / den */
1613 /* start with same height, because of interlaced video */
1614 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1615 if (info.height % den == 0) {
1616 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1617 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1618 gst_util_uint64_scale_int (info.height, num, den);
1619 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1620 } else if (info.width % num == 0) {
1621 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1622 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1623 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1624 gst_util_uint64_scale_int (info.width, den, num);
1626 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1627 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1628 gst_util_uint64_scale_int (info.height, num, den);
1629 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1631 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1632 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1634 /* Notify application to set xwindow id now */
1635 g_mutex_lock (xvimagesink->flow_lock);
1636 if (!xvimagesink->xwindow) {
1637 g_mutex_unlock (xvimagesink->flow_lock);
1638 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1640 g_mutex_unlock (xvimagesink->flow_lock);
1643 /* Creating our window and our image with the display size in pixels */
1644 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1645 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1646 goto no_display_size;
1648 g_mutex_lock (xvimagesink->flow_lock);
1649 if (!xvimagesink->xwindow) {
1650 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1651 GST_VIDEO_SINK_WIDTH (xvimagesink),
1652 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1655 xvimagesink->info = info;
1657 /* After a resize, we want to redraw the borders in case the new frame size
1658 * doesn't cover the same area */
1659 xvimagesink->redraw_border = TRUE;
1661 /* create a new pool for the new configuration */
1662 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1664 structure = gst_buffer_pool_get_config (newpool);
1665 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1666 gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms);
1667 if (!gst_buffer_pool_set_config (newpool, structure))
1670 oldpool = xvimagesink->pool;
1671 /* we don't activate the pool yet, this will be done by downstream after it
1672 * has configured the pool. If downstream does not want our pool we will
1673 * activate it when we render into it */
1674 xvimagesink->pool = newpool;
1675 g_mutex_unlock (xvimagesink->flow_lock);
1677 /* unref the old sink */
1679 /* we don't deactivate, some elements might still be using it, it will
1680 * be deactivated when the last ref is gone */
1681 gst_object_unref (oldpool);
1689 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1694 GST_DEBUG_OBJECT (xvimagesink,
1695 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1700 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1701 ("Error calculating the output display ratio of the video."));
1706 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1707 ("Error calculating the output display ratio of the video."));
1712 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1713 g_mutex_unlock (xvimagesink->flow_lock);
1718 static GstStateChangeReturn
1719 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1721 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1722 GstXvImageSink *xvimagesink;
1723 GstXContext *xcontext = NULL;
1725 xvimagesink = GST_XVIMAGESINK (element);
1727 switch (transition) {
1728 case GST_STATE_CHANGE_NULL_TO_READY:
1729 /* Initializing the XContext */
1730 if (xvimagesink->xcontext == NULL) {
1731 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1732 if (xcontext == NULL) {
1733 ret = GST_STATE_CHANGE_FAILURE;
1736 GST_OBJECT_LOCK (xvimagesink);
1738 xvimagesink->xcontext = xcontext;
1739 GST_OBJECT_UNLOCK (xvimagesink);
1742 /* update object's par with calculated one if not set yet */
1743 if (!xvimagesink->par) {
1744 xvimagesink->par = g_new0 (GValue, 1);
1745 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1746 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1748 /* call XSynchronize with the current value of synchronous */
1749 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1750 xvimagesink->synchronous ? "TRUE" : "FALSE");
1751 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1752 gst_xvimagesink_update_colorbalance (xvimagesink);
1753 gst_xvimagesink_manage_event_thread (xvimagesink);
1755 case GST_STATE_CHANGE_READY_TO_PAUSED:
1757 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1759 case GST_STATE_CHANGE_PAUSED_TO_READY:
1765 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1767 switch (transition) {
1768 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1770 case GST_STATE_CHANGE_PAUSED_TO_READY:
1771 xvimagesink->fps_n = 0;
1772 xvimagesink->fps_d = 1;
1773 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1774 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1775 g_mutex_lock (xvimagesink->flow_lock);
1776 if (xvimagesink->pool)
1777 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1778 g_mutex_unlock (xvimagesink->flow_lock);
1780 case GST_STATE_CHANGE_READY_TO_NULL:
1781 gst_xvimagesink_reset (xvimagesink);
1792 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1793 GstClockTime * start, GstClockTime * end)
1795 GstXvImageSink *xvimagesink;
1797 xvimagesink = GST_XVIMAGESINK (bsink);
1799 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1800 *start = GST_BUFFER_TIMESTAMP (buf);
1801 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1802 *end = *start + GST_BUFFER_DURATION (buf);
1804 if (xvimagesink->fps_n > 0) {
1806 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1807 xvimagesink->fps_n);
1813 static GstFlowReturn
1814 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1817 GstXvImageSink *xvimagesink;
1818 GstXvImageMeta *meta;
1821 xvimagesink = GST_XVIMAGESINK (vsink);
1823 meta = gst_buffer_get_xvimage_meta (buf);
1825 if (meta && meta->sink == xvimagesink) {
1826 /* If this buffer has been allocated using our buffer management we simply
1827 put the ximage which is in the PRIVATE pointer */
1828 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1833 GstVideoFrame src, dest;
1834 GstBufferPoolAcquireParams params = { 0, };
1836 /* Else we have to copy the data into our private image, */
1837 /* if we have one... */
1838 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1840 /* we should have a pool, configured in setcaps */
1841 if (xvimagesink->pool == NULL)
1844 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1845 goto activate_failed;
1847 /* take a buffer from our pool, if there is no buffer in the pool something
1848 * is seriously wrong, waiting for the pool here might deadlock when we try
1849 * to go to PAUSED because we never flush the pool then. */
1850 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1851 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
1852 if (res != GST_FLOW_OK)
1855 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1856 "slow copy into bufferpool buffer %p", to_put);
1858 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1859 goto invalid_buffer;
1861 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1862 gst_video_frame_unmap (&src);
1863 goto invalid_buffer;
1866 gst_video_frame_copy (&dest, &src);
1868 gst_video_frame_unmap (&dest);
1869 gst_video_frame_unmap (&src);
1872 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1877 gst_buffer_unref (to_put);
1884 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1885 ("Internal error: can't allocate images"),
1886 ("We don't have a bufferpool negotiated"));
1887 return GST_FLOW_ERROR;
1891 /* No image available. That's very bad ! */
1892 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1897 /* No Window available to put our image into */
1898 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1904 /* No Window available to put our image into */
1905 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1906 res = GST_FLOW_ERROR;
1911 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1912 res = GST_FLOW_ERROR;
1918 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1920 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1922 switch (GST_EVENT_TYPE (event)) {
1923 case GST_EVENT_TAG:{
1925 gchar *title = NULL;
1927 gst_event_parse_tag (event, &l);
1928 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1931 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1932 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1942 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1946 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1948 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1949 GstBufferPool *pool;
1950 GstStructure *config;
1955 gst_query_parse_allocation (query, &caps, &need_pool);
1960 g_mutex_lock (xvimagesink->flow_lock);
1961 if ((pool = xvimagesink->pool))
1962 gst_object_ref (pool);
1963 g_mutex_unlock (xvimagesink->flow_lock);
1968 /* we had a pool, check caps */
1969 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1970 config = gst_buffer_pool_get_config (pool);
1971 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1973 if (!gst_caps_is_equal (caps, pcaps)) {
1974 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1975 /* different caps, we can't use this pool */
1976 gst_object_unref (pool);
1979 gst_structure_free (config);
1981 if (pool == NULL && need_pool) {
1984 if (!gst_video_info_from_caps (&info, caps))
1987 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1988 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1990 /* the normal size of a frame */
1993 config = gst_buffer_pool_get_config (pool);
1994 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1995 if (!gst_buffer_pool_set_config (pool, config))
1999 /* we need at least 2 buffer because we hold on to the last one */
2000 gst_query_add_allocation_pool (query, pool, size, 2, 0);
2001 gst_object_unref (pool);
2004 /* we also support various metadata */
2005 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2006 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2013 GST_DEBUG_OBJECT (bsink, "no caps specified");
2018 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2023 GST_DEBUG_OBJECT (bsink, "failed setting config");
2024 gst_object_unref (pool);
2029 /* Interfaces stuff */
2031 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2032 GstStructure * structure)
2034 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2037 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2039 GstVideoRectangle src, dst, result;
2040 gdouble x, y, xscale = 1.0, yscale = 1.0;
2042 event = gst_event_new_navigation (structure);
2044 /* We take the flow_lock while we look at the window */
2045 g_mutex_lock (xvimagesink->flow_lock);
2047 if (!xvimagesink->xwindow) {
2048 g_mutex_unlock (xvimagesink->flow_lock);
2052 if (xvimagesink->keep_aspect) {
2053 /* We get the frame position using the calculated geometry from _setcaps
2054 that respect pixel aspect ratios */
2055 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2056 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2057 dst.w = xvimagesink->render_rect.w;
2058 dst.h = xvimagesink->render_rect.h;
2060 gst_video_sink_center_rect (src, dst, &result, TRUE);
2061 result.x += xvimagesink->render_rect.x;
2062 result.y += xvimagesink->render_rect.y;
2064 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2067 g_mutex_unlock (xvimagesink->flow_lock);
2069 /* We calculate scaling using the original video frames geometry to include
2070 pixel aspect ratio scaling. */
2071 xscale = (gdouble) xvimagesink->video_width / result.w;
2072 yscale = (gdouble) xvimagesink->video_height / result.h;
2074 /* Converting pointer coordinates to the non scaled geometry */
2075 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2076 x = MIN (x, result.x + result.w);
2077 x = MAX (x - result.x, 0);
2078 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2079 (gdouble) x * xscale, NULL);
2081 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2082 y = MIN (y, result.y + result.h);
2083 y = MAX (y - result.y, 0);
2084 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2085 (gdouble) y * yscale, NULL);
2088 gst_pad_send_event (peer, event);
2089 gst_object_unref (peer);
2094 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2096 iface->send_event = gst_xvimagesink_navigation_send_event;
2100 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2102 XID xwindow_id = id;
2103 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2104 GstXWindow *xwindow = NULL;
2106 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2108 g_mutex_lock (xvimagesink->flow_lock);
2110 /* If we already use that window return */
2111 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2112 g_mutex_unlock (xvimagesink->flow_lock);
2116 /* If the element has not initialized the X11 context try to do so */
2117 if (!xvimagesink->xcontext &&
2118 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2119 g_mutex_unlock (xvimagesink->flow_lock);
2120 /* we have thrown a GST_ELEMENT_ERROR now */
2124 gst_xvimagesink_update_colorbalance (xvimagesink);
2126 /* If a window is there already we destroy it */
2127 if (xvimagesink->xwindow) {
2128 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2129 xvimagesink->xwindow = NULL;
2132 /* If the xid is 0 we go back to an internal window */
2133 if (xwindow_id == 0) {
2134 /* If no width/height caps nego did not happen window will be created
2135 during caps nego then */
2136 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2137 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2139 gst_xvimagesink_xwindow_new (xvimagesink,
2140 GST_VIDEO_SINK_WIDTH (xvimagesink),
2141 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2144 XWindowAttributes attr;
2146 xwindow = g_new0 (GstXWindow, 1);
2147 xwindow->win = xwindow_id;
2149 /* Set the event we want to receive and create a GC */
2150 g_mutex_lock (xvimagesink->x_lock);
2152 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2154 xwindow->width = attr.width;
2155 xwindow->height = attr.height;
2156 xwindow->internal = FALSE;
2157 if (!xvimagesink->have_render_rect) {
2158 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2159 xvimagesink->render_rect.w = attr.width;
2160 xvimagesink->render_rect.h = attr.height;
2162 if (xvimagesink->handle_events) {
2163 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2164 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2168 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2169 xwindow->win, 0, NULL);
2170 g_mutex_unlock (xvimagesink->x_lock);
2174 xvimagesink->xwindow = xwindow;
2176 g_mutex_unlock (xvimagesink->flow_lock);
2180 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2182 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2184 GST_DEBUG ("doing expose");
2185 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2186 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2190 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2191 gboolean handle_events)
2193 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2195 xvimagesink->handle_events = handle_events;
2197 g_mutex_lock (xvimagesink->flow_lock);
2199 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2200 g_mutex_unlock (xvimagesink->flow_lock);
2204 g_mutex_lock (xvimagesink->x_lock);
2206 if (handle_events) {
2207 if (xvimagesink->xwindow->internal) {
2208 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2209 ExposureMask | StructureNotifyMask | PointerMotionMask |
2210 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2212 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2213 ExposureMask | StructureNotifyMask | PointerMotionMask |
2214 KeyPressMask | KeyReleaseMask);
2217 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2220 g_mutex_unlock (xvimagesink->x_lock);
2222 g_mutex_unlock (xvimagesink->flow_lock);
2226 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2227 gint width, gint height)
2229 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2231 /* FIXME: how about some locking? */
2232 if (width >= 0 && height >= 0) {
2233 xvimagesink->render_rect.x = x;
2234 xvimagesink->render_rect.y = y;
2235 xvimagesink->render_rect.w = width;
2236 xvimagesink->render_rect.h = height;
2237 xvimagesink->have_render_rect = TRUE;
2239 xvimagesink->render_rect.x = 0;
2240 xvimagesink->render_rect.y = 0;
2241 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2242 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2243 xvimagesink->have_render_rect = FALSE;
2248 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2250 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2251 iface->expose = gst_xvimagesink_expose;
2252 iface->handle_events = gst_xvimagesink_set_event_handling;
2253 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2256 static const GList *
2257 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2259 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2261 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2263 if (xvimagesink->xcontext)
2264 return xvimagesink->xcontext->channels_list;
2270 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2271 GstColorBalanceChannel * channel, gint value)
2273 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2275 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2276 g_return_if_fail (channel->label != NULL);
2278 xvimagesink->cb_changed = TRUE;
2280 /* Normalize val to [-1000, 1000] */
2281 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2282 (double) (channel->max_value - channel->min_value));
2284 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2285 xvimagesink->hue = value;
2286 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2287 xvimagesink->saturation = value;
2288 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2289 xvimagesink->contrast = value;
2290 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2291 xvimagesink->brightness = value;
2293 g_warning ("got an unknown channel %s", channel->label);
2297 gst_xvimagesink_update_colorbalance (xvimagesink);
2301 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2302 GstColorBalanceChannel * channel)
2304 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2307 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2308 g_return_val_if_fail (channel->label != NULL, 0);
2310 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2311 value = xvimagesink->hue;
2312 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2313 value = xvimagesink->saturation;
2314 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2315 value = xvimagesink->contrast;
2316 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2317 value = xvimagesink->brightness;
2319 g_warning ("got an unknown channel %s", channel->label);
2322 /* Normalize val to [channel->min_value, channel->max_value] */
2323 value = channel->min_value + (channel->max_value - channel->min_value) *
2324 (value + 1000) / 2000;
2329 static GstColorBalanceType
2330 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
2332 return GST_COLOR_BALANCE_HARDWARE;
2336 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2338 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2339 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2340 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2341 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
2345 static const GList *
2346 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2348 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2349 static GList *list = NULL;
2352 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2354 g_list_append (list, g_object_class_find_property (klass,
2355 "autopaint-colorkey"));
2357 g_list_append (list, g_object_class_find_property (klass,
2360 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2367 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2368 guint prop_id, const GParamSpec * pspec)
2370 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2374 case PROP_AUTOPAINT_COLORKEY:
2375 case PROP_DOUBLE_BUFFER:
2377 GST_DEBUG_OBJECT (xvimagesink,
2378 "probing device list and get capabilities");
2379 if (!xvimagesink->xcontext) {
2380 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2381 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2385 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2391 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2392 guint prop_id, const GParamSpec * pspec)
2394 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2395 gboolean ret = FALSE;
2399 case PROP_AUTOPAINT_COLORKEY:
2400 case PROP_DOUBLE_BUFFER:
2402 if (xvimagesink->xcontext != NULL) {
2409 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2416 static GValueArray *
2417 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2418 guint prop_id, const GParamSpec * pspec)
2420 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2421 GValueArray *array = NULL;
2423 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2424 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2433 GValue value = { 0 };
2435 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2436 g_value_init (&value, G_TYPE_STRING);
2438 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2439 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2441 g_value_set_string (&value, adaptor_id_s);
2442 g_value_array_append (array, &value);
2443 g_free (adaptor_id_s);
2445 g_value_unset (&value);
2448 case PROP_AUTOPAINT_COLORKEY:
2449 if (xvimagesink->have_autopaint_colorkey) {
2450 GValue value = { 0 };
2452 array = g_value_array_new (2);
2453 g_value_init (&value, G_TYPE_BOOLEAN);
2454 g_value_set_boolean (&value, FALSE);
2455 g_value_array_append (array, &value);
2456 g_value_set_boolean (&value, TRUE);
2457 g_value_array_append (array, &value);
2458 g_value_unset (&value);
2461 case PROP_DOUBLE_BUFFER:
2462 if (xvimagesink->have_double_buffer) {
2463 GValue value = { 0 };
2465 array = g_value_array_new (2);
2466 g_value_init (&value, G_TYPE_BOOLEAN);
2467 g_value_set_boolean (&value, FALSE);
2468 g_value_array_append (array, &value);
2469 g_value_set_boolean (&value, TRUE);
2470 g_value_array_append (array, &value);
2471 g_value_unset (&value);
2475 if (xvimagesink->have_colorkey) {
2476 GValue value = { 0 };
2478 array = g_value_array_new (1);
2479 g_value_init (&value, GST_TYPE_INT_RANGE);
2480 gst_value_set_int_range (&value, 0, 0xffffff);
2481 g_value_array_append (array, &value);
2482 g_value_unset (&value);
2486 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2495 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2498 iface->get_properties = gst_xvimagesink_probe_get_properties;
2499 iface->probe_property = gst_xvimagesink_probe_probe_property;
2500 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2501 iface->get_values = gst_xvimagesink_probe_get_values;
2505 /* =========================================== */
2507 /* Init & Class init */
2509 /* =========================================== */
2512 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2513 const GValue * value, GParamSpec * pspec)
2515 GstXvImageSink *xvimagesink;
2517 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2519 xvimagesink = GST_XVIMAGESINK (object);
2523 xvimagesink->hue = g_value_get_int (value);
2524 xvimagesink->cb_changed = TRUE;
2525 gst_xvimagesink_update_colorbalance (xvimagesink);
2528 xvimagesink->contrast = g_value_get_int (value);
2529 xvimagesink->cb_changed = TRUE;
2530 gst_xvimagesink_update_colorbalance (xvimagesink);
2532 case PROP_BRIGHTNESS:
2533 xvimagesink->brightness = g_value_get_int (value);
2534 xvimagesink->cb_changed = TRUE;
2535 gst_xvimagesink_update_colorbalance (xvimagesink);
2537 case PROP_SATURATION:
2538 xvimagesink->saturation = g_value_get_int (value);
2539 xvimagesink->cb_changed = TRUE;
2540 gst_xvimagesink_update_colorbalance (xvimagesink);
2543 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2545 case PROP_SYNCHRONOUS:
2546 xvimagesink->synchronous = g_value_get_boolean (value);
2547 if (xvimagesink->xcontext) {
2548 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2549 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2550 xvimagesink->synchronous ? "TRUE" : "FALSE");
2553 case PROP_PIXEL_ASPECT_RATIO:
2554 g_free (xvimagesink->par);
2555 xvimagesink->par = g_new0 (GValue, 1);
2556 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2557 if (!g_value_transform (value, xvimagesink->par)) {
2558 g_warning ("Could not transform string to aspect ratio");
2559 gst_value_set_fraction (xvimagesink->par, 1, 1);
2561 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2562 gst_value_get_fraction_numerator (xvimagesink->par),
2563 gst_value_get_fraction_denominator (xvimagesink->par));
2565 case PROP_FORCE_ASPECT_RATIO:
2566 xvimagesink->keep_aspect = g_value_get_boolean (value);
2568 case PROP_HANDLE_EVENTS:
2569 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2570 g_value_get_boolean (value));
2571 gst_xvimagesink_manage_event_thread (xvimagesink);
2574 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2576 case PROP_HANDLE_EXPOSE:
2577 xvimagesink->handle_expose = g_value_get_boolean (value);
2578 gst_xvimagesink_manage_event_thread (xvimagesink);
2580 case PROP_DOUBLE_BUFFER:
2581 xvimagesink->double_buffer = g_value_get_boolean (value);
2583 case PROP_AUTOPAINT_COLORKEY:
2584 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2587 xvimagesink->colorkey = g_value_get_int (value);
2589 case PROP_DRAW_BORDERS:
2590 xvimagesink->draw_borders = g_value_get_boolean (value);
2593 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2599 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2600 GValue * value, GParamSpec * pspec)
2602 GstXvImageSink *xvimagesink;
2604 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2606 xvimagesink = GST_XVIMAGESINK (object);
2610 g_value_set_int (value, xvimagesink->hue);
2613 g_value_set_int (value, xvimagesink->contrast);
2615 case PROP_BRIGHTNESS:
2616 g_value_set_int (value, xvimagesink->brightness);
2618 case PROP_SATURATION:
2619 g_value_set_int (value, xvimagesink->saturation);
2622 g_value_set_string (value, xvimagesink->display_name);
2624 case PROP_SYNCHRONOUS:
2625 g_value_set_boolean (value, xvimagesink->synchronous);
2627 case PROP_PIXEL_ASPECT_RATIO:
2628 if (xvimagesink->par)
2629 g_value_transform (xvimagesink->par, value);
2631 case PROP_FORCE_ASPECT_RATIO:
2632 g_value_set_boolean (value, xvimagesink->keep_aspect);
2634 case PROP_HANDLE_EVENTS:
2635 g_value_set_boolean (value, xvimagesink->handle_events);
2639 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2641 g_value_set_string (value, adaptor_no_s);
2642 g_free (adaptor_no_s);
2645 case PROP_DEVICE_NAME:
2646 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2647 g_value_set_string (value,
2648 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2650 g_value_set_string (value, NULL);
2653 case PROP_HANDLE_EXPOSE:
2654 g_value_set_boolean (value, xvimagesink->handle_expose);
2656 case PROP_DOUBLE_BUFFER:
2657 g_value_set_boolean (value, xvimagesink->double_buffer);
2659 case PROP_AUTOPAINT_COLORKEY:
2660 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2663 g_value_set_int (value, xvimagesink->colorkey);
2665 case PROP_DRAW_BORDERS:
2666 g_value_set_boolean (value, xvimagesink->draw_borders);
2668 case PROP_WINDOW_WIDTH:
2669 if (xvimagesink->xwindow)
2670 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2672 g_value_set_uint64 (value, 0);
2674 case PROP_WINDOW_HEIGHT:
2675 if (xvimagesink->xwindow)
2676 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2678 g_value_set_uint64 (value, 0);
2681 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2687 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2691 GST_OBJECT_LOCK (xvimagesink);
2692 xvimagesink->running = FALSE;
2693 /* grab thread and mark it as NULL */
2694 thread = xvimagesink->event_thread;
2695 xvimagesink->event_thread = NULL;
2696 GST_OBJECT_UNLOCK (xvimagesink);
2698 /* Wait for our event thread to finish before we clean up our stuff. */
2700 g_thread_join (thread);
2702 if (xvimagesink->cur_image) {
2703 gst_buffer_unref (xvimagesink->cur_image);
2704 xvimagesink->cur_image = NULL;
2707 g_mutex_lock (xvimagesink->flow_lock);
2709 if (xvimagesink->pool) {
2710 gst_object_unref (xvimagesink->pool);
2711 xvimagesink->pool = NULL;
2714 if (xvimagesink->xwindow) {
2715 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2716 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2717 xvimagesink->xwindow = NULL;
2719 g_mutex_unlock (xvimagesink->flow_lock);
2721 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2722 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2723 xvimagesink->have_render_rect = FALSE;
2725 gst_xvimagesink_xcontext_clear (xvimagesink);
2728 /* Finalize is called only once, dispose can be called multiple times.
2729 * We use mutexes and don't reset stuff to NULL here so let's register
2732 gst_xvimagesink_finalize (GObject * object)
2734 GstXvImageSink *xvimagesink;
2736 xvimagesink = GST_XVIMAGESINK (object);
2738 gst_xvimagesink_reset (xvimagesink);
2740 if (xvimagesink->display_name) {
2741 g_free (xvimagesink->display_name);
2742 xvimagesink->display_name = NULL;
2745 if (xvimagesink->par) {
2746 g_free (xvimagesink->par);
2747 xvimagesink->par = NULL;
2749 if (xvimagesink->x_lock) {
2750 g_mutex_free (xvimagesink->x_lock);
2751 xvimagesink->x_lock = NULL;
2753 if (xvimagesink->flow_lock) {
2754 g_mutex_free (xvimagesink->flow_lock);
2755 xvimagesink->flow_lock = NULL;
2758 g_free (xvimagesink->media_title);
2760 G_OBJECT_CLASS (parent_class)->finalize (object);
2764 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2766 xvimagesink->display_name = NULL;
2767 xvimagesink->adaptor_no = 0;
2768 xvimagesink->xcontext = NULL;
2769 xvimagesink->xwindow = NULL;
2770 xvimagesink->cur_image = NULL;
2772 xvimagesink->hue = xvimagesink->saturation = 0;
2773 xvimagesink->contrast = xvimagesink->brightness = 0;
2774 xvimagesink->cb_changed = FALSE;
2776 xvimagesink->fps_n = 0;
2777 xvimagesink->fps_d = 0;
2778 xvimagesink->video_width = 0;
2779 xvimagesink->video_height = 0;
2781 xvimagesink->x_lock = g_mutex_new ();
2782 xvimagesink->flow_lock = g_mutex_new ();
2784 xvimagesink->pool = NULL;
2786 xvimagesink->synchronous = FALSE;
2787 xvimagesink->double_buffer = TRUE;
2788 xvimagesink->running = FALSE;
2789 xvimagesink->keep_aspect = TRUE;
2790 xvimagesink->handle_events = TRUE;
2791 xvimagesink->par = NULL;
2792 xvimagesink->handle_expose = TRUE;
2793 xvimagesink->autopaint_colorkey = TRUE;
2795 /* on 16bit displays this becomes r,g,b = 1,2,3
2796 * on 24bit displays this becomes r,g,b = 8,8,16
2797 * as a port atom value
2799 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2800 xvimagesink->draw_borders = TRUE;
2804 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2806 GObjectClass *gobject_class;
2807 GstElementClass *gstelement_class;
2808 GstBaseSinkClass *gstbasesink_class;
2809 GstVideoSinkClass *videosink_class;
2811 gobject_class = (GObjectClass *) klass;
2812 gstelement_class = (GstElementClass *) klass;
2813 gstbasesink_class = (GstBaseSinkClass *) klass;
2814 videosink_class = (GstVideoSinkClass *) klass;
2816 parent_class = g_type_class_peek_parent (klass);
2818 gobject_class->set_property = gst_xvimagesink_set_property;
2819 gobject_class->get_property = gst_xvimagesink_get_property;
2821 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2822 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2823 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2824 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2825 g_param_spec_int ("brightness", "Brightness",
2826 "The brightness of the video", -1000, 1000, 0,
2827 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2828 g_object_class_install_property (gobject_class, PROP_HUE,
2829 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2830 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2831 g_object_class_install_property (gobject_class, PROP_SATURATION,
2832 g_param_spec_int ("saturation", "Saturation",
2833 "The saturation of the video", -1000, 1000, 0,
2834 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2835 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2836 g_param_spec_string ("display", "Display", "X Display name", NULL,
2837 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2838 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2839 g_param_spec_boolean ("synchronous", "Synchronous",
2840 "When enabled, runs the X display in synchronous mode. "
2841 "(unrelated to A/V sync, used only for debugging)", FALSE,
2842 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2843 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2844 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2845 "The pixel aspect ratio of the device", "1/1",
2846 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2847 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2848 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2849 "When enabled, scaling will respect original aspect ratio", TRUE,
2850 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2851 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2852 g_param_spec_boolean ("handle-events", "Handle XEvents",
2853 "When enabled, XEvents will be selected and handled", TRUE,
2854 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2855 g_object_class_install_property (gobject_class, PROP_DEVICE,
2856 g_param_spec_string ("device", "Adaptor number",
2857 "The number of the video adaptor", "0",
2858 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2859 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2860 g_param_spec_string ("device-name", "Adaptor name",
2861 "The name of the video adaptor", NULL,
2862 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2864 * GstXvImageSink:handle-expose
2866 * When enabled, the current frame will always be drawn in response to X
2871 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2872 g_param_spec_boolean ("handle-expose", "Handle expose",
2874 "the current frame will always be drawn in response to X Expose "
2875 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2878 * GstXvImageSink:double-buffer
2880 * Whether to double-buffer the output.
2884 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2885 g_param_spec_boolean ("double-buffer", "Double-buffer",
2886 "Whether to double-buffer the output", TRUE,
2887 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2889 * GstXvImageSink:autopaint-colorkey
2891 * Whether to autofill overlay with colorkey
2895 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2896 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2897 "Whether to autofill overlay with colorkey", TRUE,
2898 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2900 * GstXvImageSink:colorkey
2902 * Color to use for the overlay mask.
2906 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2907 g_param_spec_int ("colorkey", "Colorkey",
2908 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2909 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2912 * GstXvImageSink:draw-borders
2914 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2915 * unused parts of the video area.
2919 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2920 g_param_spec_boolean ("draw-borders", "Colorkey",
2921 "Draw black borders to fill unused area in force-aspect-ratio mode",
2922 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2925 * GstXvImageSink:window-width
2927 * Actual width of the video window.
2931 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2932 g_param_spec_uint64 ("window-width", "window-width",
2933 "Width of the window", 0, G_MAXUINT64, 0,
2934 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2937 * GstXvImageSink:window-height
2939 * Actual height of the video window.
2943 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2944 g_param_spec_uint64 ("window-height", "window-height",
2945 "Height of the window", 0, G_MAXUINT64, 0,
2946 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2948 gobject_class->finalize = gst_xvimagesink_finalize;
2950 gst_element_class_set_static_metadata (gstelement_class,
2951 "Video sink", "Sink/Video",
2952 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2954 gst_element_class_add_pad_template (gstelement_class,
2955 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2957 gstelement_class->change_state =
2958 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2960 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2961 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2962 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2963 gstbasesink_class->propose_allocation =
2964 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2965 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2967 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);