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 */
117 #include <gst/video/navigation.h>
118 #include <gst/video/videooverlay.h>
119 #include <gst/video/colorbalance.h>
120 /* Helper functions */
121 #include <gst/video/gstvideometa.h>
124 #include "xvimagesink.h"
126 /* Debugging category */
127 #include <gst/gstinfo.h>
129 /* for XkbKeycodeToKeysym */
130 #include <X11/XKBlib.h>
132 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
133 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
134 #define GST_CAT_DEFAULT gst_debug_xvimagesink
139 unsigned long functions;
140 unsigned long decorations;
142 unsigned long status;
144 MotifWmHints, MwmHints;
146 #define MWM_HINTS_DECORATIONS (1L << 1)
148 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
149 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
151 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
153 /* Default template - initiated with class struct to allow gst-register to work
155 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
156 GST_STATIC_PAD_TEMPLATE ("sink",
159 GST_STATIC_CAPS ("video/x-raw, "
160 "framerate = (fraction) [ 0, MAX ], "
161 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
173 PROP_PIXEL_ASPECT_RATIO,
174 PROP_FORCE_ASPECT_RATIO,
180 PROP_AUTOPAINT_COLORKEY,
187 /* ============================================================= */
191 /* ============================================================= */
193 /* =========================================== */
195 /* Object typing & Creation */
197 /* =========================================== */
198 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
199 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
201 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
203 #define gst_xvimagesink_parent_class parent_class
204 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
205 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
206 gst_xvimagesink_navigation_init);
207 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
208 gst_xvimagesink_video_overlay_init);
209 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
210 gst_xvimagesink_colorbalance_init));
213 /* ============================================================= */
215 /* Private Methods */
217 /* ============================================================= */
220 /* We are called with the x_lock taken */
222 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
223 GstXWindow * xwindow, GstVideoRectangle rect)
227 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
228 g_return_if_fail (xwindow != NULL);
230 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
231 xvimagesink->xcontext->black);
234 if (rect.x > xvimagesink->render_rect.x) {
235 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
236 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
237 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
241 t1 = rect.x + rect.w;
242 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
244 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
245 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
249 if (rect.y > xvimagesink->render_rect.y) {
250 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
251 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
252 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
256 t1 = rect.y + rect.h;
257 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
259 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
260 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
264 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
265 * if no window was available */
267 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
269 GstXvImageMeta *meta;
270 GstVideoCropMeta *crop;
271 GstVideoRectangle result;
272 gboolean draw_border = FALSE;
273 GstVideoRectangle src, dst;
275 /* We take the flow_lock. If expose is in there we don't want to run
276 concurrently from the data flow thread */
277 g_mutex_lock (&xvimagesink->flow_lock);
279 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
280 g_mutex_unlock (&xvimagesink->flow_lock);
284 /* Draw borders when displaying the first frame. After this
285 draw borders only on expose event or after a size change. */
286 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
290 /* Store a reference to the last image we put, lose the previous one */
291 if (xvimage && xvimagesink->cur_image != xvimage) {
292 if (xvimagesink->cur_image) {
293 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
294 gst_buffer_unref (xvimagesink->cur_image);
296 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
297 xvimagesink->cur_image = gst_buffer_ref (xvimage);
300 /* Expose sends a NULL image, we take the latest frame */
302 if (xvimagesink->cur_image) {
304 xvimage = xvimagesink->cur_image;
306 g_mutex_unlock (&xvimagesink->flow_lock);
311 meta = gst_buffer_get_xvimage_meta (xvimage);
313 crop = gst_buffer_get_video_crop_meta (xvimage);
316 src.x = crop->x + meta->x;
317 src.y = crop->y + meta->y;
319 src.h = crop->height;
320 GST_LOG_OBJECT (xvimagesink,
321 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
326 src.h = meta->height;
329 if (xvimagesink->keep_aspect) {
332 /* We take the size of the source material as it was negotiated and
333 * corrected for DAR. This size can be different from the cropped size in
334 * which case the image will be scaled to fit the negotiated size. */
335 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
336 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
337 dst.w = xvimagesink->render_rect.w;
338 dst.h = xvimagesink->render_rect.h;
340 gst_video_sink_center_rect (s, dst, &result, TRUE);
341 result.x += xvimagesink->render_rect.x;
342 result.y += xvimagesink->render_rect.y;
344 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
347 g_mutex_lock (&xvimagesink->x_lock);
349 if (draw_border && xvimagesink->draw_borders) {
350 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
352 xvimagesink->redraw_border = FALSE;
355 if (xvimagesink->xcontext->use_xshm) {
356 GST_LOG_OBJECT (xvimagesink,
357 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
358 GST_PTR_FORMAT, meta->width, meta->height,
359 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
361 XvShmPutImage (xvimagesink->xcontext->disp,
362 xvimagesink->xcontext->xv_port_id,
363 xvimagesink->xwindow->win,
364 xvimagesink->xwindow->gc, meta->xvimage,
365 src.x, src.y, src.w, src.h,
366 result.x, result.y, result.w, result.h, FALSE);
368 #endif /* HAVE_XSHM */
370 XvPutImage (xvimagesink->xcontext->disp,
371 xvimagesink->xcontext->xv_port_id,
372 xvimagesink->xwindow->win,
373 xvimagesink->xwindow->gc, meta->xvimage,
374 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
377 XSync (xvimagesink->xcontext->disp, FALSE);
379 g_mutex_unlock (&xvimagesink->x_lock);
381 g_mutex_unlock (&xvimagesink->flow_lock);
387 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
390 Atom hints_atom = None;
393 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
394 g_return_val_if_fail (window != NULL, FALSE);
396 g_mutex_lock (&xvimagesink->x_lock);
398 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
400 if (hints_atom == None) {
401 g_mutex_unlock (&xvimagesink->x_lock);
405 hints = g_malloc0 (sizeof (MotifWmHints));
407 hints->flags |= MWM_HINTS_DECORATIONS;
408 hints->decorations = 1 << 0;
410 XChangeProperty (xvimagesink->xcontext->disp, window->win,
411 hints_atom, hints_atom, 32, PropModeReplace,
412 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
414 XSync (xvimagesink->xcontext->disp, FALSE);
416 g_mutex_unlock (&xvimagesink->x_lock);
424 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
425 GstXWindow * xwindow, const gchar * media_title)
428 g_free (xvimagesink->media_title);
429 xvimagesink->media_title = g_strdup (media_title);
432 /* we have a window */
433 if (xwindow->internal) {
434 XTextProperty xproperty;
435 const gchar *app_name;
436 const gchar *title = NULL;
437 gchar *title_mem = NULL;
439 /* set application name as a title */
440 app_name = g_get_application_name ();
442 if (app_name && xvimagesink->media_title) {
443 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
445 } else if (app_name) {
447 } else if (xvimagesink->media_title) {
448 title = xvimagesink->media_title;
452 if ((XStringListToTextProperty (((char **) &title), 1,
454 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
455 XFree (xproperty.value);
464 /* This function handles a GstXWindow creation
465 * The width and height are the actual pixel size on the display */
467 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
468 gint width, gint height)
470 GstXWindow *xwindow = NULL;
473 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
475 xwindow = g_new0 (GstXWindow, 1);
477 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
478 xvimagesink->render_rect.w = width;
479 xvimagesink->render_rect.h = height;
481 xwindow->width = width;
482 xwindow->height = height;
483 xwindow->internal = TRUE;
485 g_mutex_lock (&xvimagesink->x_lock);
487 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
488 xvimagesink->xcontext->root,
489 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
491 /* We have to do that to prevent X from redrawing the background on
492 * ConfigureNotify. This takes away flickering of video when resizing. */
493 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
495 /* set application name as a title */
496 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
498 if (xvimagesink->handle_events) {
501 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
502 StructureNotifyMask | PointerMotionMask | KeyPressMask |
503 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
505 /* Tell the window manager we'd like delete client messages instead of
507 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
508 "WM_DELETE_WINDOW", True);
509 if (wm_delete != None) {
510 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
515 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
516 xwindow->win, 0, &values);
518 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
520 XSync (xvimagesink->xcontext->disp, FALSE);
522 g_mutex_unlock (&xvimagesink->x_lock);
524 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
526 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
532 /* This function destroys a GstXWindow */
534 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
535 GstXWindow * xwindow)
537 g_return_if_fail (xwindow != NULL);
538 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
540 g_mutex_lock (&xvimagesink->x_lock);
542 /* If we did not create that window we just free the GC and let it live */
543 if (xwindow->internal)
544 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
546 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
548 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
550 XSync (xvimagesink->xcontext->disp, FALSE);
552 g_mutex_unlock (&xvimagesink->x_lock);
558 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
560 XWindowAttributes attr;
562 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
564 /* Update the window geometry */
565 g_mutex_lock (&xvimagesink->x_lock);
566 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
567 g_mutex_unlock (&xvimagesink->x_lock);
571 XGetWindowAttributes (xvimagesink->xcontext->disp,
572 xvimagesink->xwindow->win, &attr);
574 xvimagesink->xwindow->width = attr.width;
575 xvimagesink->xwindow->height = attr.height;
577 if (!xvimagesink->have_render_rect) {
578 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
579 xvimagesink->render_rect.w = attr.width;
580 xvimagesink->render_rect.h = attr.height;
583 g_mutex_unlock (&xvimagesink->x_lock);
587 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
588 GstXWindow * xwindow)
590 g_return_if_fail (xwindow != NULL);
591 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
593 g_mutex_lock (&xvimagesink->x_lock);
595 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
598 XSync (xvimagesink->xcontext->disp, FALSE);
600 g_mutex_unlock (&xvimagesink->x_lock);
603 /* This function commits our internal colorbalance settings to our grabbed Xv
604 port. If the xcontext is not initialized yet it simply returns */
606 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
608 GList *channels = NULL;
610 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
612 /* If we haven't initialized the X context we can't update anything */
613 if (xvimagesink->xcontext == NULL)
616 /* Don't set the attributes if they haven't been changed, to avoid
617 * rounding errors changing the values */
618 if (!xvimagesink->cb_changed)
621 /* For each channel of the colorbalance we calculate the correct value
622 doing range conversion and then set the Xv port attribute to match our
624 channels = xvimagesink->xcontext->channels_list;
627 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
628 GstColorBalanceChannel *channel = NULL;
631 gdouble convert_coef;
633 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
634 g_object_ref (channel);
636 /* Our range conversion coef */
637 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
639 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
640 value = xvimagesink->hue;
641 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
642 value = xvimagesink->saturation;
643 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
644 value = xvimagesink->contrast;
645 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
646 value = xvimagesink->brightness;
648 g_warning ("got an unknown channel %s", channel->label);
649 g_object_unref (channel);
653 /* Committing to Xv port */
654 g_mutex_lock (&xvimagesink->x_lock);
656 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
657 if (prop_atom != None) {
660 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
661 XvSetPortAttribute (xvimagesink->xcontext->disp,
662 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
664 g_mutex_unlock (&xvimagesink->x_lock);
666 g_object_unref (channel);
668 channels = g_list_next (channels);
672 /* This function handles XEvents that might be in the queue. It generates
673 GstEvent that will be sent upstream in the pipeline to handle interactivity
674 and navigation. It will also listen for configure events on the window to
675 trigger caps renegotiation so on the fly software scaling can work. */
677 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
680 guint pointer_x = 0, pointer_y = 0;
681 gboolean pointer_moved = FALSE;
682 gboolean exposed = FALSE, configured = FALSE;
684 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
686 /* Handle Interaction, produces navigation events */
688 /* We get all pointer motion events, only the last position is
690 g_mutex_lock (&xvimagesink->flow_lock);
691 g_mutex_lock (&xvimagesink->x_lock);
692 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
693 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
694 g_mutex_unlock (&xvimagesink->x_lock);
695 g_mutex_unlock (&xvimagesink->flow_lock);
699 pointer_x = e.xmotion.x;
700 pointer_y = e.xmotion.y;
701 pointer_moved = TRUE;
706 g_mutex_lock (&xvimagesink->flow_lock);
707 g_mutex_lock (&xvimagesink->x_lock);
711 g_mutex_unlock (&xvimagesink->x_lock);
712 g_mutex_unlock (&xvimagesink->flow_lock);
714 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
715 pointer_x, pointer_y);
716 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
717 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
719 g_mutex_lock (&xvimagesink->flow_lock);
720 g_mutex_lock (&xvimagesink->x_lock);
723 /* We get all events on our window to throw them upstream */
724 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
725 xvimagesink->xwindow->win,
726 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
729 const char *key_str = NULL;
731 /* We lock only for the X function call */
732 g_mutex_unlock (&xvimagesink->x_lock);
733 g_mutex_unlock (&xvimagesink->flow_lock);
737 /* Mouse button pressed over our window. We send upstream
738 events for interactivity/navigation */
739 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
740 e.xbutton.button, e.xbutton.x, e.xbutton.y);
741 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
742 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
745 /* Mouse button released over our window. We send upstream
746 events for interactivity/navigation */
747 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
748 e.xbutton.button, e.xbutton.x, e.xbutton.y);
749 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
750 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
754 /* Key pressed/released over our window. We send upstream
755 events for interactivity/navigation */
756 g_mutex_lock (&xvimagesink->x_lock);
757 keysym = XkbKeycodeToKeysym (xvimagesink->xcontext->disp,
758 e.xkey.keycode, 0, 0);
759 if (keysym != NoSymbol) {
760 key_str = XKeysymToString (keysym);
764 g_mutex_unlock (&xvimagesink->x_lock);
765 GST_DEBUG_OBJECT (xvimagesink,
766 "key %d pressed over window at %d,%d (%s)",
767 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
768 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
769 e.type == KeyPress ? "key-press" : "key-release", key_str);
772 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
775 g_mutex_lock (&xvimagesink->flow_lock);
776 g_mutex_lock (&xvimagesink->x_lock);
780 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
781 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
786 case ConfigureNotify:
787 g_mutex_unlock (&xvimagesink->x_lock);
788 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
789 g_mutex_lock (&xvimagesink->x_lock);
797 if (xvimagesink->handle_expose && (exposed || configured)) {
798 g_mutex_unlock (&xvimagesink->x_lock);
799 g_mutex_unlock (&xvimagesink->flow_lock);
801 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
803 g_mutex_lock (&xvimagesink->flow_lock);
804 g_mutex_lock (&xvimagesink->x_lock);
807 /* Handle Display events */
808 while (XPending (xvimagesink->xcontext->disp)) {
809 XNextEvent (xvimagesink->xcontext->disp, &e);
815 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
816 "WM_DELETE_WINDOW", True);
817 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
818 /* Handle window deletion by posting an error on the bus */
819 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
820 ("Output window was closed"), (NULL));
822 g_mutex_unlock (&xvimagesink->x_lock);
823 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
824 xvimagesink->xwindow = NULL;
825 g_mutex_lock (&xvimagesink->x_lock);
834 g_mutex_unlock (&xvimagesink->x_lock);
835 g_mutex_unlock (&xvimagesink->flow_lock);
839 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
840 XvAdaptorInfo * adaptors, int adaptor_no)
845 /* Do we support XvImageMask ? */
846 if (!(adaptors[adaptor_no].type & XvImageMask)) {
847 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
848 adaptors[adaptor_no].name);
852 /* We found such an adaptor, looking for an available port */
853 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
854 /* We try to grab the port */
855 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
856 if (Success == res) {
857 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
858 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
859 adaptors[adaptor_no].num_ports);
861 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
862 adaptors[adaptor_no].name, res);
867 /* This function generates a caps with all supported format by the first
868 Xv grabable port we find. We store each one of the supported formats in a
869 format list and append the format to a newly created caps that we return
870 If this function does not return NULL because of an error, it also grabs
871 the port via XvGrabPort */
873 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
874 GstXContext * xcontext)
877 XvAdaptorInfo *adaptors;
879 XvImageFormatValues *formats = NULL;
881 XvEncodingInfo *encodings = NULL;
882 gulong max_w = G_MAXINT, max_h = G_MAXINT;
883 GstCaps *caps = NULL;
884 GstCaps *rgb_caps = NULL;
886 g_return_val_if_fail (xcontext != NULL, NULL);
888 /* First let's check that XVideo extension is available */
889 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
890 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
891 ("Could not initialise Xv output"),
892 ("XVideo extension is not available"));
896 /* Then we get adaptors list */
897 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
898 &xcontext->nb_adaptors, &adaptors)) {
899 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
900 ("Could not initialise Xv output"),
901 ("Failed getting XV adaptors list"));
905 xcontext->xv_port_id = 0;
907 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
910 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
912 /* Now fill up our adaptor name array */
913 for (i = 0; i < xcontext->nb_adaptors; i++) {
914 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
917 if (xvimagesink->adaptor_no != -1 &&
918 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
919 /* Find xv port from user defined adaptor */
920 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
921 xvimagesink->adaptor_no);
924 if (!xcontext->xv_port_id) {
925 /* Now search for an adaptor that supports XvImageMask */
926 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
927 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
928 xvimagesink->adaptor_no = i;
932 XvFreeAdaptorInfo (adaptors);
934 if (!xcontext->xv_port_id) {
935 xvimagesink->adaptor_no = -1;
936 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
937 ("Could not initialise Xv output"), ("No port available"));
941 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
944 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
945 xcontext->xv_port_id, &count);
946 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
947 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
948 static const char colorkey[] = "XV_COLORKEY";
950 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
952 xvimagesink->have_autopaint_colorkey = FALSE;
953 xvimagesink->have_double_buffer = FALSE;
954 xvimagesink->have_colorkey = FALSE;
956 for (i = 0; ((i < count) && todo); i++)
957 if (!strcmp (attr[i].name, autopaint)) {
958 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
960 /* turn on autopaint colorkey */
961 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
962 (xvimagesink->autopaint_colorkey ? 1 : 0));
964 xvimagesink->have_autopaint_colorkey = TRUE;
965 } else if (!strcmp (attr[i].name, dbl_buffer)) {
966 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
968 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
969 (xvimagesink->double_buffer ? 1 : 0));
971 xvimagesink->have_double_buffer = TRUE;
972 } else if (!strcmp (attr[i].name, colorkey)) {
973 /* Set the colorkey, default is something that is dark but hopefully
974 * won't randomly appear on the screen elsewhere (ie not black or greys)
975 * can be overridden by setting "colorkey" property
977 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
979 gboolean set_attr = TRUE;
982 /* set a colorkey in the right format RGB565/RGB888
983 * We only handle these 2 cases, because they're the only types of
984 * devices we've encountered. If we don't recognise it, leave it alone
986 cr = (xvimagesink->colorkey >> 16);
987 cg = (xvimagesink->colorkey >> 8) & 0xFF;
988 cb = (xvimagesink->colorkey) & 0xFF;
989 switch (xcontext->depth) {
990 case 16: /* RGB 565 */
994 ckey = (cr << 11) | (cg << 5) | cb;
997 case 32: /* RGB 888 / ARGB 8888 */
998 ckey = (cr << 16) | (cg << 8) | cb;
1001 GST_DEBUG_OBJECT (xvimagesink,
1002 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1009 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1010 (guint32) attr[i].max_value);
1011 GST_LOG_OBJECT (xvimagesink,
1012 "Setting color key for display depth %d to 0x%x",
1013 xcontext->depth, ckey);
1015 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1019 xvimagesink->have_colorkey = TRUE;
1025 /* Get the list of encodings supported by the adapter and look for the
1026 * XV_IMAGE encoding so we can determine the maximum width and height
1028 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1031 for (i = 0; i < nb_encodings; i++) {
1032 GST_LOG_OBJECT (xvimagesink,
1033 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1034 i, encodings[i].name, encodings[i].width, encodings[i].height,
1035 encodings[i].rate.numerator, encodings[i].rate.denominator);
1036 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1037 max_w = encodings[i].width;
1038 max_h = encodings[i].height;
1042 XvFreeEncodingInfo (encodings);
1044 /* We get all image formats supported by our port */
1045 formats = XvListImageFormats (xcontext->disp,
1046 xcontext->xv_port_id, &nb_formats);
1047 caps = gst_caps_new_empty ();
1048 for (i = 0; i < nb_formats; i++) {
1049 GstCaps *format_caps = NULL;
1050 gboolean is_rgb_format = FALSE;
1051 GstVideoFormat vformat;
1053 /* We set the image format of the xcontext to an existing one. This
1054 is just some valid image format for making our xshm calls check before
1055 caps negotiation really happens. */
1056 xcontext->im_format = formats[i].id;
1058 switch (formats[i].type) {
1061 XvImageFormatValues *fmt = &(formats[i]);
1065 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1067 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1068 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1069 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1072 format_caps = gst_caps_new_simple ("video/x-raw",
1073 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1074 "width", GST_TYPE_INT_RANGE, 1, max_w,
1075 "height", GST_TYPE_INT_RANGE, 1, max_h,
1076 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1078 is_rgb_format = TRUE;
1083 vformat = gst_video_format_from_fourcc (formats[i].id);
1084 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1087 format_caps = gst_caps_new_simple ("video/x-raw",
1088 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1089 "width", GST_TYPE_INT_RANGE, 1, max_w,
1090 "height", GST_TYPE_INT_RANGE, 1, max_h,
1091 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1095 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1096 g_assert_not_reached ();
1101 GstXvImageFormat *format = NULL;
1103 format = g_new0 (GstXvImageFormat, 1);
1105 format->format = formats[i].id;
1106 format->vformat = vformat;
1107 format->caps = gst_caps_copy (format_caps);
1108 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1111 if (is_rgb_format) {
1112 if (rgb_caps == NULL)
1113 rgb_caps = format_caps;
1115 gst_caps_append (rgb_caps, format_caps);
1117 gst_caps_append (caps, format_caps);
1121 /* Collected all caps into either the caps or rgb_caps structures.
1122 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1124 gst_caps_append (caps, rgb_caps);
1129 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1131 if (gst_caps_is_empty (caps)) {
1132 gst_caps_unref (caps);
1133 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1134 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1135 ("No supported format found"));
1143 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1145 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1147 GST_OBJECT_LOCK (xvimagesink);
1148 while (xvimagesink->running) {
1149 GST_OBJECT_UNLOCK (xvimagesink);
1151 if (xvimagesink->xwindow) {
1152 gst_xvimagesink_handle_xevents (xvimagesink);
1154 /* FIXME: do we want to align this with the framerate or anything else? */
1155 g_usleep (G_USEC_PER_SEC / 20);
1157 GST_OBJECT_LOCK (xvimagesink);
1159 GST_OBJECT_UNLOCK (xvimagesink);
1165 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1167 GThread *thread = NULL;
1169 /* don't start the thread too early */
1170 if (xvimagesink->xcontext == NULL) {
1174 GST_OBJECT_LOCK (xvimagesink);
1175 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1176 if (!xvimagesink->event_thread) {
1177 /* Setup our event listening thread */
1178 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1179 xvimagesink->handle_expose, xvimagesink->handle_events);
1180 xvimagesink->running = TRUE;
1181 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1182 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1185 if (xvimagesink->event_thread) {
1186 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1187 xvimagesink->handle_expose, xvimagesink->handle_events);
1188 xvimagesink->running = FALSE;
1189 /* grab thread and mark it as NULL */
1190 thread = xvimagesink->event_thread;
1191 xvimagesink->event_thread = NULL;
1194 GST_OBJECT_UNLOCK (xvimagesink);
1196 /* Wait for our event thread to finish */
1198 g_thread_join (thread);
1203 /* This function calculates the pixel aspect ratio based on the properties
1204 * in the xcontext structure and stores it there. */
1206 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1208 static const gint par[][2] = {
1209 {1, 1}, /* regular screen */
1210 {16, 15}, /* PAL TV */
1211 {11, 10}, /* 525 line Rec.601 video */
1212 {54, 59}, /* 625 line Rec.601 video */
1213 {64, 45}, /* 1280x1024 on 16:9 display */
1214 {5, 3}, /* 1280x1024 on 4:3 display */
1215 {4, 3} /* 800x600 on 16:9 display */
1222 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1224 /* first calculate the "real" ratio based on the X values;
1225 * which is the "physical" w/h divided by the w/h in pixels of the display */
1226 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1227 / (xcontext->heightmm * xcontext->width);
1229 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1231 if (xcontext->width == 720 && xcontext->height == 576) {
1232 ratio = 4.0 * 576 / (3.0 * 720);
1234 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1236 /* now find the one from par[][2] with the lowest delta to the real one */
1240 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1241 gdouble this_delta = DELTA (i);
1243 if (this_delta < delta) {
1249 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1250 par[index][0], par[index][1]);
1252 g_free (xcontext->par);
1253 xcontext->par = g_new0 (GValue, 1);
1254 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1255 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1256 GST_DEBUG ("set xcontext PAR to %d/%d",
1257 gst_value_get_fraction_numerator (xcontext->par),
1258 gst_value_get_fraction_denominator (xcontext->par));
1261 /* This function gets the X Display and global info about it. Everything is
1262 stored in our object and will be cleaned when the object is disposed. Note
1263 here that caps for supported format are generated without any window or
1265 static GstXContext *
1266 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1268 GstXContext *xcontext = NULL;
1269 XPixmapFormatValues *px_formats = NULL;
1270 gint nb_formats = 0, i, j, N_attr;
1271 XvAttribute *xv_attr;
1273 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1274 "XV_BRIGHTNESS", "XV_CONTRAST"
1277 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1279 xcontext = g_new0 (GstXContext, 1);
1280 xcontext->im_format = 0;
1282 g_mutex_lock (&xvimagesink->x_lock);
1284 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1286 if (!xcontext->disp) {
1287 g_mutex_unlock (&xvimagesink->x_lock);
1289 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1290 ("Could not initialise Xv output"), ("Could not open display"));
1294 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1295 xcontext->screen_num = DefaultScreen (xcontext->disp);
1296 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1297 xcontext->root = DefaultRootWindow (xcontext->disp);
1298 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1299 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1300 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1302 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1303 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1304 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1305 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1307 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1308 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1310 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1311 /* We get supported pixmap formats at supported depth */
1312 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1315 XCloseDisplay (xcontext->disp);
1316 g_mutex_unlock (&xvimagesink->x_lock);
1317 g_free (xcontext->par);
1319 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1320 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1324 /* We get bpp value corresponding to our running depth */
1325 for (i = 0; i < nb_formats; i++) {
1326 if (px_formats[i].depth == xcontext->depth)
1327 xcontext->bpp = px_formats[i].bits_per_pixel;
1332 xcontext->endianness =
1333 (ImageByteOrder (xcontext->disp) ==
1334 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1336 /* our caps system handles 24/32bpp RGB as big-endian. */
1337 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1338 xcontext->endianness == G_LITTLE_ENDIAN) {
1339 xcontext->endianness = G_BIG_ENDIAN;
1340 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1341 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1342 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1343 if (xcontext->bpp == 24) {
1344 xcontext->visual->red_mask >>= 8;
1345 xcontext->visual->green_mask >>= 8;
1346 xcontext->visual->blue_mask >>= 8;
1350 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1352 /* Search for XShm extension support */
1354 if (XShmQueryExtension (xcontext->disp) &&
1355 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1356 xcontext->use_xshm = TRUE;
1357 GST_DEBUG ("xvimagesink is using XShm extension");
1359 #endif /* HAVE_XSHM */
1361 xcontext->use_xshm = FALSE;
1362 GST_DEBUG ("xvimagesink is not using XShm extension");
1365 if (!xcontext->caps) {
1366 XCloseDisplay (xcontext->disp);
1367 g_mutex_unlock (&xvimagesink->x_lock);
1368 g_free (xcontext->par);
1370 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1374 xv_attr = XvQueryPortAttributes (xcontext->disp,
1375 xcontext->xv_port_id, &N_attr);
1378 /* Generate the channels list */
1379 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1380 XvAttribute *matching_attr = NULL;
1382 /* Retrieve the property atom if it exists. If it doesn't exist,
1383 * the attribute itself must not either, so we can skip */
1384 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1385 if (prop_atom == None)
1388 if (xv_attr != NULL) {
1389 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1390 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1391 matching_attr = xv_attr + j;
1394 if (matching_attr) {
1395 GstColorBalanceChannel *channel;
1397 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1398 channel->label = g_strdup (channels[i]);
1399 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1400 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1402 xcontext->channels_list = g_list_append (xcontext->channels_list,
1405 /* If the colorbalance settings have not been touched we get Xv values
1406 as defaults and update our internal variables */
1407 if (!xvimagesink->cb_changed) {
1410 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1412 /* Normalize val to [-1000, 1000] */
1413 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1414 (double) (channel->max_value - channel->min_value));
1416 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1417 xvimagesink->hue = val;
1418 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1419 xvimagesink->saturation = val;
1420 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1421 xvimagesink->brightness = val;
1422 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1423 xvimagesink->contrast = val;
1431 g_mutex_unlock (&xvimagesink->x_lock);
1436 /* This function cleans the X context. Closing the Display, releasing the XV
1437 port and unrefing the caps for supported formats. */
1439 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1441 GList *formats_list, *channels_list;
1442 GstXContext *xcontext;
1445 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1447 GST_OBJECT_LOCK (xvimagesink);
1448 if (xvimagesink->xcontext == NULL) {
1449 GST_OBJECT_UNLOCK (xvimagesink);
1453 /* Take the XContext from the sink and clean it up */
1454 xcontext = xvimagesink->xcontext;
1455 xvimagesink->xcontext = NULL;
1457 GST_OBJECT_UNLOCK (xvimagesink);
1460 formats_list = xcontext->formats_list;
1462 while (formats_list) {
1463 GstXvImageFormat *format = formats_list->data;
1465 gst_caps_unref (format->caps);
1467 formats_list = g_list_next (formats_list);
1470 if (xcontext->formats_list)
1471 g_list_free (xcontext->formats_list);
1473 channels_list = xcontext->channels_list;
1475 while (channels_list) {
1476 GstColorBalanceChannel *channel = channels_list->data;
1478 g_object_unref (channel);
1479 channels_list = g_list_next (channels_list);
1482 if (xcontext->channels_list)
1483 g_list_free (xcontext->channels_list);
1485 gst_caps_unref (xcontext->caps);
1486 if (xcontext->last_caps)
1487 gst_caps_replace (&xcontext->last_caps, NULL);
1489 for (i = 0; i < xcontext->nb_adaptors; i++) {
1490 g_free (xcontext->adaptors[i]);
1493 g_free (xcontext->adaptors);
1495 g_free (xcontext->par);
1497 g_mutex_lock (&xvimagesink->x_lock);
1499 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1501 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1503 XCloseDisplay (xcontext->disp);
1505 g_mutex_unlock (&xvimagesink->x_lock);
1513 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1515 GstXvImageSink *xvimagesink;
1518 xvimagesink = GST_XVIMAGESINK (bsink);
1520 if (xvimagesink->xcontext) {
1522 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1523 GST_CAPS_INTERSECT_FIRST);
1525 return gst_caps_ref (xvimagesink->xcontext->caps);
1528 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1530 GstCaps *intersection;
1533 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1534 gst_caps_unref (caps);
1535 caps = intersection;
1541 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1543 GstXvImageSink *xvimagesink;
1544 GstStructure *structure;
1545 GstBufferPool *newpool, *oldpool;
1547 guint32 im_format = 0;
1548 gint video_par_n, video_par_d; /* video's PAR */
1549 gint display_par_n, display_par_d; /* display's PAR */
1552 static GstAllocationParams params = { 0, 15, 0, 0, };
1554 xvimagesink = GST_XVIMAGESINK (bsink);
1556 GST_DEBUG_OBJECT (xvimagesink,
1557 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1558 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1560 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1561 goto incompatible_caps;
1563 if (!gst_video_info_from_caps (&info, caps))
1564 goto invalid_format;
1566 xvimagesink->fps_n = info.fps_n;
1567 xvimagesink->fps_d = info.fps_d;
1569 xvimagesink->video_width = info.width;
1570 xvimagesink->video_height = info.height;
1572 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1573 if (im_format == -1)
1574 goto invalid_format;
1578 /* get aspect ratio from caps if it's present, and
1579 * convert video width and height to a display width and height
1580 * using wd / hd = wv / hv * PARv / PARd */
1582 /* get video's PAR */
1583 video_par_n = info.par_n;
1584 video_par_d = info.par_d;
1586 /* get display's PAR */
1587 if (xvimagesink->par) {
1588 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1589 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1595 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1596 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1599 GST_DEBUG_OBJECT (xvimagesink,
1600 "video width/height: %dx%d, calculated display ratio: %d/%d",
1601 info.width, info.height, num, den);
1603 /* now find a width x height that respects this display ratio.
1604 * prefer those that have one of w/h the same as the incoming video
1605 * using wd / hd = num / den */
1607 /* start with same height, because of interlaced video */
1608 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1609 if (info.height % den == 0) {
1610 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1611 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1612 gst_util_uint64_scale_int (info.height, num, den);
1613 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1614 } else if (info.width % num == 0) {
1615 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1616 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1617 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1618 gst_util_uint64_scale_int (info.width, den, num);
1620 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1621 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1622 gst_util_uint64_scale_int (info.height, num, den);
1623 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1625 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1626 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1628 /* Notify application to set xwindow id now */
1629 g_mutex_lock (&xvimagesink->flow_lock);
1630 if (!xvimagesink->xwindow) {
1631 g_mutex_unlock (&xvimagesink->flow_lock);
1632 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1634 g_mutex_unlock (&xvimagesink->flow_lock);
1637 /* Creating our window and our image with the display size in pixels */
1638 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1639 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1640 goto no_display_size;
1642 g_mutex_lock (&xvimagesink->flow_lock);
1643 if (!xvimagesink->xwindow) {
1644 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1645 GST_VIDEO_SINK_WIDTH (xvimagesink),
1646 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1649 xvimagesink->info = info;
1651 /* After a resize, we want to redraw the borders in case the new frame size
1652 * doesn't cover the same area */
1653 xvimagesink->redraw_border = TRUE;
1655 /* create a new pool for the new configuration */
1656 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1658 structure = gst_buffer_pool_get_config (newpool);
1659 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1660 gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms);
1661 if (!gst_buffer_pool_set_config (newpool, structure))
1664 oldpool = xvimagesink->pool;
1665 /* we don't activate the pool yet, this will be done by downstream after it
1666 * has configured the pool. If downstream does not want our pool we will
1667 * activate it when we render into it */
1668 xvimagesink->pool = newpool;
1669 g_mutex_unlock (&xvimagesink->flow_lock);
1671 /* unref the old sink */
1673 /* we don't deactivate, some elements might still be using it, it will
1674 * be deactivated when the last ref is gone */
1675 gst_object_unref (oldpool);
1683 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1688 GST_DEBUG_OBJECT (xvimagesink,
1689 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1694 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1695 ("Error calculating the output display ratio of the video."));
1700 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1701 ("Error calculating the output display ratio of the video."));
1706 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1707 g_mutex_unlock (&xvimagesink->flow_lock);
1712 static GstStateChangeReturn
1713 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1715 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1716 GstXvImageSink *xvimagesink;
1717 GstXContext *xcontext = NULL;
1719 xvimagesink = GST_XVIMAGESINK (element);
1721 switch (transition) {
1722 case GST_STATE_CHANGE_NULL_TO_READY:
1723 /* Initializing the XContext */
1724 if (xvimagesink->xcontext == NULL) {
1725 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1726 if (xcontext == NULL) {
1727 ret = GST_STATE_CHANGE_FAILURE;
1730 GST_OBJECT_LOCK (xvimagesink);
1732 xvimagesink->xcontext = xcontext;
1733 GST_OBJECT_UNLOCK (xvimagesink);
1736 /* update object's par with calculated one if not set yet */
1737 if (!xvimagesink->par) {
1738 xvimagesink->par = g_new0 (GValue, 1);
1739 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1740 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1742 /* call XSynchronize with the current value of synchronous */
1743 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1744 xvimagesink->synchronous ? "TRUE" : "FALSE");
1745 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1746 gst_xvimagesink_update_colorbalance (xvimagesink);
1747 gst_xvimagesink_manage_event_thread (xvimagesink);
1749 case GST_STATE_CHANGE_READY_TO_PAUSED:
1751 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1753 case GST_STATE_CHANGE_PAUSED_TO_READY:
1759 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1761 switch (transition) {
1762 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1764 case GST_STATE_CHANGE_PAUSED_TO_READY:
1765 xvimagesink->fps_n = 0;
1766 xvimagesink->fps_d = 1;
1767 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1768 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1769 g_mutex_lock (&xvimagesink->flow_lock);
1770 if (xvimagesink->pool)
1771 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1772 g_mutex_unlock (&xvimagesink->flow_lock);
1774 case GST_STATE_CHANGE_READY_TO_NULL:
1775 gst_xvimagesink_reset (xvimagesink);
1786 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1787 GstClockTime * start, GstClockTime * end)
1789 GstXvImageSink *xvimagesink;
1791 xvimagesink = GST_XVIMAGESINK (bsink);
1793 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1794 *start = GST_BUFFER_TIMESTAMP (buf);
1795 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1796 *end = *start + GST_BUFFER_DURATION (buf);
1798 if (xvimagesink->fps_n > 0) {
1800 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1801 xvimagesink->fps_n);
1807 static GstFlowReturn
1808 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1811 GstXvImageSink *xvimagesink;
1812 GstXvImageMeta *meta;
1815 xvimagesink = GST_XVIMAGESINK (vsink);
1817 meta = gst_buffer_get_xvimage_meta (buf);
1819 if (meta && meta->sink == xvimagesink) {
1820 /* If this buffer has been allocated using our buffer management we simply
1821 put the ximage which is in the PRIVATE pointer */
1822 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1827 GstVideoFrame src, dest;
1828 GstBufferPoolAcquireParams params = { 0, };
1830 /* Else we have to copy the data into our private image, */
1831 /* if we have one... */
1832 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1834 /* we should have a pool, configured in setcaps */
1835 if (xvimagesink->pool == NULL)
1838 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1839 goto activate_failed;
1841 /* take a buffer from our pool, if there is no buffer in the pool something
1842 * is seriously wrong, waiting for the pool here might deadlock when we try
1843 * to go to PAUSED because we never flush the pool then. */
1844 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1845 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
1846 if (res != GST_FLOW_OK)
1849 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1850 "slow copy into bufferpool buffer %p", to_put);
1852 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1853 goto invalid_buffer;
1855 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1856 gst_video_frame_unmap (&src);
1857 goto invalid_buffer;
1860 gst_video_frame_copy (&dest, &src);
1862 gst_video_frame_unmap (&dest);
1863 gst_video_frame_unmap (&src);
1866 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1871 gst_buffer_unref (to_put);
1878 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1879 ("Internal error: can't allocate images"),
1880 ("We don't have a bufferpool negotiated"));
1881 return GST_FLOW_ERROR;
1885 /* No image available. That's very bad ! */
1886 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1891 /* No Window available to put our image into */
1892 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1898 /* No Window available to put our image into */
1899 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1900 res = GST_FLOW_ERROR;
1905 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1906 res = GST_FLOW_ERROR;
1912 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1914 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1916 switch (GST_EVENT_TYPE (event)) {
1917 case GST_EVENT_TAG:{
1919 gchar *title = NULL;
1921 gst_event_parse_tag (event, &l);
1922 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1925 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1926 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1936 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1940 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1942 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1943 GstBufferPool *pool;
1944 GstStructure *config;
1949 gst_query_parse_allocation (query, &caps, &need_pool);
1954 g_mutex_lock (&xvimagesink->flow_lock);
1955 if ((pool = xvimagesink->pool))
1956 gst_object_ref (pool);
1957 g_mutex_unlock (&xvimagesink->flow_lock);
1962 /* we had a pool, check caps */
1963 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1964 config = gst_buffer_pool_get_config (pool);
1965 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1967 if (!gst_caps_is_equal (caps, pcaps)) {
1968 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1969 /* different caps, we can't use this pool */
1970 gst_object_unref (pool);
1973 gst_structure_free (config);
1975 if (pool == NULL && need_pool) {
1978 if (!gst_video_info_from_caps (&info, caps))
1981 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1982 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1984 /* the normal size of a frame */
1987 config = gst_buffer_pool_get_config (pool);
1988 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1989 if (!gst_buffer_pool_set_config (pool, config))
1993 /* we need at least 2 buffer because we hold on to the last one */
1994 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1995 gst_object_unref (pool);
1998 /* we also support various metadata */
1999 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2000 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2007 GST_DEBUG_OBJECT (bsink, "no caps specified");
2012 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2017 GST_DEBUG_OBJECT (bsink, "failed setting config");
2018 gst_object_unref (pool);
2023 /* Interfaces stuff */
2025 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2026 GstStructure * structure)
2028 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2031 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2033 GstVideoRectangle src, dst, result;
2034 gdouble x, y, xscale = 1.0, yscale = 1.0;
2036 event = gst_event_new_navigation (structure);
2038 /* We take the flow_lock while we look at the window */
2039 g_mutex_lock (&xvimagesink->flow_lock);
2041 if (!xvimagesink->xwindow) {
2042 g_mutex_unlock (&xvimagesink->flow_lock);
2046 if (xvimagesink->keep_aspect) {
2047 /* We get the frame position using the calculated geometry from _setcaps
2048 that respect pixel aspect ratios */
2049 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2050 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2051 dst.w = xvimagesink->render_rect.w;
2052 dst.h = xvimagesink->render_rect.h;
2054 gst_video_sink_center_rect (src, dst, &result, TRUE);
2055 result.x += xvimagesink->render_rect.x;
2056 result.y += xvimagesink->render_rect.y;
2058 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2061 g_mutex_unlock (&xvimagesink->flow_lock);
2063 /* We calculate scaling using the original video frames geometry to include
2064 pixel aspect ratio scaling. */
2065 xscale = (gdouble) xvimagesink->video_width / result.w;
2066 yscale = (gdouble) xvimagesink->video_height / result.h;
2068 /* Converting pointer coordinates to the non scaled geometry */
2069 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2070 x = MIN (x, result.x + result.w);
2071 x = MAX (x - result.x, 0);
2072 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2073 (gdouble) x * xscale, NULL);
2075 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2076 y = MIN (y, result.y + result.h);
2077 y = MAX (y - result.y, 0);
2078 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2079 (gdouble) y * yscale, NULL);
2082 gst_pad_send_event (peer, event);
2083 gst_object_unref (peer);
2088 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2090 iface->send_event = gst_xvimagesink_navigation_send_event;
2094 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2096 XID xwindow_id = id;
2097 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2098 GstXWindow *xwindow = NULL;
2100 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2102 g_mutex_lock (&xvimagesink->flow_lock);
2104 /* If we already use that window return */
2105 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2106 g_mutex_unlock (&xvimagesink->flow_lock);
2110 /* If the element has not initialized the X11 context try to do so */
2111 if (!xvimagesink->xcontext &&
2112 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2113 g_mutex_unlock (&xvimagesink->flow_lock);
2114 /* we have thrown a GST_ELEMENT_ERROR now */
2118 gst_xvimagesink_update_colorbalance (xvimagesink);
2120 /* If a window is there already we destroy it */
2121 if (xvimagesink->xwindow) {
2122 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2123 xvimagesink->xwindow = NULL;
2126 /* If the xid is 0 we go back to an internal window */
2127 if (xwindow_id == 0) {
2128 /* If no width/height caps nego did not happen window will be created
2129 during caps nego then */
2130 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2131 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2133 gst_xvimagesink_xwindow_new (xvimagesink,
2134 GST_VIDEO_SINK_WIDTH (xvimagesink),
2135 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2138 XWindowAttributes attr;
2140 xwindow = g_new0 (GstXWindow, 1);
2141 xwindow->win = xwindow_id;
2143 /* Set the event we want to receive and create a GC */
2144 g_mutex_lock (&xvimagesink->x_lock);
2146 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2148 xwindow->width = attr.width;
2149 xwindow->height = attr.height;
2150 xwindow->internal = FALSE;
2151 if (!xvimagesink->have_render_rect) {
2152 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2153 xvimagesink->render_rect.w = attr.width;
2154 xvimagesink->render_rect.h = attr.height;
2156 if (xvimagesink->handle_events) {
2157 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2158 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2162 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2163 xwindow->win, 0, NULL);
2164 g_mutex_unlock (&xvimagesink->x_lock);
2168 xvimagesink->xwindow = xwindow;
2170 g_mutex_unlock (&xvimagesink->flow_lock);
2174 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2176 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2178 GST_DEBUG ("doing expose");
2179 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2180 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2184 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2185 gboolean handle_events)
2187 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2189 xvimagesink->handle_events = handle_events;
2191 g_mutex_lock (&xvimagesink->flow_lock);
2193 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2194 g_mutex_unlock (&xvimagesink->flow_lock);
2198 g_mutex_lock (&xvimagesink->x_lock);
2200 if (handle_events) {
2201 if (xvimagesink->xwindow->internal) {
2202 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2203 ExposureMask | StructureNotifyMask | PointerMotionMask |
2204 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2206 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2207 ExposureMask | StructureNotifyMask | PointerMotionMask |
2208 KeyPressMask | KeyReleaseMask);
2211 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2214 g_mutex_unlock (&xvimagesink->x_lock);
2216 g_mutex_unlock (&xvimagesink->flow_lock);
2220 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2221 gint width, gint height)
2223 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2225 /* FIXME: how about some locking? */
2226 if (width >= 0 && height >= 0) {
2227 xvimagesink->render_rect.x = x;
2228 xvimagesink->render_rect.y = y;
2229 xvimagesink->render_rect.w = width;
2230 xvimagesink->render_rect.h = height;
2231 xvimagesink->have_render_rect = TRUE;
2233 xvimagesink->render_rect.x = 0;
2234 xvimagesink->render_rect.y = 0;
2235 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2236 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2237 xvimagesink->have_render_rect = FALSE;
2242 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2244 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2245 iface->expose = gst_xvimagesink_expose;
2246 iface->handle_events = gst_xvimagesink_set_event_handling;
2247 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2250 static const GList *
2251 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2253 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2255 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2257 if (xvimagesink->xcontext)
2258 return xvimagesink->xcontext->channels_list;
2264 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2265 GstColorBalanceChannel * channel, gint value)
2267 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2269 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2270 g_return_if_fail (channel->label != NULL);
2272 xvimagesink->cb_changed = TRUE;
2274 /* Normalize val to [-1000, 1000] */
2275 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2276 (double) (channel->max_value - channel->min_value));
2278 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2279 xvimagesink->hue = value;
2280 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2281 xvimagesink->saturation = value;
2282 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2283 xvimagesink->contrast = value;
2284 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2285 xvimagesink->brightness = value;
2287 g_warning ("got an unknown channel %s", channel->label);
2291 gst_xvimagesink_update_colorbalance (xvimagesink);
2295 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2296 GstColorBalanceChannel * channel)
2298 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2301 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2302 g_return_val_if_fail (channel->label != NULL, 0);
2304 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2305 value = xvimagesink->hue;
2306 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2307 value = xvimagesink->saturation;
2308 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2309 value = xvimagesink->contrast;
2310 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2311 value = xvimagesink->brightness;
2313 g_warning ("got an unknown channel %s", channel->label);
2316 /* Normalize val to [channel->min_value, channel->max_value] */
2317 value = channel->min_value + (channel->max_value - channel->min_value) *
2318 (value + 1000) / 2000;
2323 static GstColorBalanceType
2324 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
2326 return GST_COLOR_BALANCE_HARDWARE;
2330 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2332 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2333 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2334 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2335 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
2339 static const GList *
2340 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2342 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2343 static GList *list = NULL;
2346 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2348 g_list_append (list, g_object_class_find_property (klass,
2349 "autopaint-colorkey"));
2351 g_list_append (list, g_object_class_find_property (klass,
2354 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2361 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2362 guint prop_id, const GParamSpec * pspec)
2364 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2368 case PROP_AUTOPAINT_COLORKEY:
2369 case PROP_DOUBLE_BUFFER:
2371 GST_DEBUG_OBJECT (xvimagesink,
2372 "probing device list and get capabilities");
2373 if (!xvimagesink->xcontext) {
2374 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2375 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2379 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2385 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2386 guint prop_id, const GParamSpec * pspec)
2388 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2389 gboolean ret = FALSE;
2393 case PROP_AUTOPAINT_COLORKEY:
2394 case PROP_DOUBLE_BUFFER:
2396 if (xvimagesink->xcontext != NULL) {
2403 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2410 static GValueArray *
2411 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2412 guint prop_id, const GParamSpec * pspec)
2414 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2415 GValueArray *array = NULL;
2417 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2418 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2427 GValue value = { 0 };
2429 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2430 g_value_init (&value, G_TYPE_STRING);
2432 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2433 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2435 g_value_set_string (&value, adaptor_id_s);
2436 g_value_array_append (array, &value);
2437 g_free (adaptor_id_s);
2439 g_value_unset (&value);
2442 case PROP_AUTOPAINT_COLORKEY:
2443 if (xvimagesink->have_autopaint_colorkey) {
2444 GValue value = { 0 };
2446 array = g_value_array_new (2);
2447 g_value_init (&value, G_TYPE_BOOLEAN);
2448 g_value_set_boolean (&value, FALSE);
2449 g_value_array_append (array, &value);
2450 g_value_set_boolean (&value, TRUE);
2451 g_value_array_append (array, &value);
2452 g_value_unset (&value);
2455 case PROP_DOUBLE_BUFFER:
2456 if (xvimagesink->have_double_buffer) {
2457 GValue value = { 0 };
2459 array = g_value_array_new (2);
2460 g_value_init (&value, G_TYPE_BOOLEAN);
2461 g_value_set_boolean (&value, FALSE);
2462 g_value_array_append (array, &value);
2463 g_value_set_boolean (&value, TRUE);
2464 g_value_array_append (array, &value);
2465 g_value_unset (&value);
2469 if (xvimagesink->have_colorkey) {
2470 GValue value = { 0 };
2472 array = g_value_array_new (1);
2473 g_value_init (&value, GST_TYPE_INT_RANGE);
2474 gst_value_set_int_range (&value, 0, 0xffffff);
2475 g_value_array_append (array, &value);
2476 g_value_unset (&value);
2480 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2489 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2492 iface->get_properties = gst_xvimagesink_probe_get_properties;
2493 iface->probe_property = gst_xvimagesink_probe_probe_property;
2494 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2495 iface->get_values = gst_xvimagesink_probe_get_values;
2499 /* =========================================== */
2501 /* Init & Class init */
2503 /* =========================================== */
2506 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2507 const GValue * value, GParamSpec * pspec)
2509 GstXvImageSink *xvimagesink;
2511 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2513 xvimagesink = GST_XVIMAGESINK (object);
2517 xvimagesink->hue = g_value_get_int (value);
2518 xvimagesink->cb_changed = TRUE;
2519 gst_xvimagesink_update_colorbalance (xvimagesink);
2522 xvimagesink->contrast = g_value_get_int (value);
2523 xvimagesink->cb_changed = TRUE;
2524 gst_xvimagesink_update_colorbalance (xvimagesink);
2526 case PROP_BRIGHTNESS:
2527 xvimagesink->brightness = g_value_get_int (value);
2528 xvimagesink->cb_changed = TRUE;
2529 gst_xvimagesink_update_colorbalance (xvimagesink);
2531 case PROP_SATURATION:
2532 xvimagesink->saturation = g_value_get_int (value);
2533 xvimagesink->cb_changed = TRUE;
2534 gst_xvimagesink_update_colorbalance (xvimagesink);
2537 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2539 case PROP_SYNCHRONOUS:
2540 xvimagesink->synchronous = g_value_get_boolean (value);
2541 if (xvimagesink->xcontext) {
2542 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2543 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2544 xvimagesink->synchronous ? "TRUE" : "FALSE");
2547 case PROP_PIXEL_ASPECT_RATIO:
2548 g_free (xvimagesink->par);
2549 xvimagesink->par = g_new0 (GValue, 1);
2550 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2551 if (!g_value_transform (value, xvimagesink->par)) {
2552 g_warning ("Could not transform string to aspect ratio");
2553 gst_value_set_fraction (xvimagesink->par, 1, 1);
2555 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2556 gst_value_get_fraction_numerator (xvimagesink->par),
2557 gst_value_get_fraction_denominator (xvimagesink->par));
2559 case PROP_FORCE_ASPECT_RATIO:
2560 xvimagesink->keep_aspect = g_value_get_boolean (value);
2562 case PROP_HANDLE_EVENTS:
2563 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2564 g_value_get_boolean (value));
2565 gst_xvimagesink_manage_event_thread (xvimagesink);
2568 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2570 case PROP_HANDLE_EXPOSE:
2571 xvimagesink->handle_expose = g_value_get_boolean (value);
2572 gst_xvimagesink_manage_event_thread (xvimagesink);
2574 case PROP_DOUBLE_BUFFER:
2575 xvimagesink->double_buffer = g_value_get_boolean (value);
2577 case PROP_AUTOPAINT_COLORKEY:
2578 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2581 xvimagesink->colorkey = g_value_get_int (value);
2583 case PROP_DRAW_BORDERS:
2584 xvimagesink->draw_borders = g_value_get_boolean (value);
2587 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2593 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2594 GValue * value, GParamSpec * pspec)
2596 GstXvImageSink *xvimagesink;
2598 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2600 xvimagesink = GST_XVIMAGESINK (object);
2604 g_value_set_int (value, xvimagesink->hue);
2607 g_value_set_int (value, xvimagesink->contrast);
2609 case PROP_BRIGHTNESS:
2610 g_value_set_int (value, xvimagesink->brightness);
2612 case PROP_SATURATION:
2613 g_value_set_int (value, xvimagesink->saturation);
2616 g_value_set_string (value, xvimagesink->display_name);
2618 case PROP_SYNCHRONOUS:
2619 g_value_set_boolean (value, xvimagesink->synchronous);
2621 case PROP_PIXEL_ASPECT_RATIO:
2622 if (xvimagesink->par)
2623 g_value_transform (xvimagesink->par, value);
2625 case PROP_FORCE_ASPECT_RATIO:
2626 g_value_set_boolean (value, xvimagesink->keep_aspect);
2628 case PROP_HANDLE_EVENTS:
2629 g_value_set_boolean (value, xvimagesink->handle_events);
2633 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2635 g_value_set_string (value, adaptor_no_s);
2636 g_free (adaptor_no_s);
2639 case PROP_DEVICE_NAME:
2640 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2641 g_value_set_string (value,
2642 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2644 g_value_set_string (value, NULL);
2647 case PROP_HANDLE_EXPOSE:
2648 g_value_set_boolean (value, xvimagesink->handle_expose);
2650 case PROP_DOUBLE_BUFFER:
2651 g_value_set_boolean (value, xvimagesink->double_buffer);
2653 case PROP_AUTOPAINT_COLORKEY:
2654 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2657 g_value_set_int (value, xvimagesink->colorkey);
2659 case PROP_DRAW_BORDERS:
2660 g_value_set_boolean (value, xvimagesink->draw_borders);
2662 case PROP_WINDOW_WIDTH:
2663 if (xvimagesink->xwindow)
2664 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2666 g_value_set_uint64 (value, 0);
2668 case PROP_WINDOW_HEIGHT:
2669 if (xvimagesink->xwindow)
2670 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2672 g_value_set_uint64 (value, 0);
2675 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2681 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2685 GST_OBJECT_LOCK (xvimagesink);
2686 xvimagesink->running = FALSE;
2687 /* grab thread and mark it as NULL */
2688 thread = xvimagesink->event_thread;
2689 xvimagesink->event_thread = NULL;
2690 GST_OBJECT_UNLOCK (xvimagesink);
2692 /* Wait for our event thread to finish before we clean up our stuff. */
2694 g_thread_join (thread);
2696 if (xvimagesink->cur_image) {
2697 gst_buffer_unref (xvimagesink->cur_image);
2698 xvimagesink->cur_image = NULL;
2701 g_mutex_lock (&xvimagesink->flow_lock);
2703 if (xvimagesink->pool) {
2704 gst_object_unref (xvimagesink->pool);
2705 xvimagesink->pool = NULL;
2708 if (xvimagesink->xwindow) {
2709 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2710 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2711 xvimagesink->xwindow = NULL;
2713 g_mutex_unlock (&xvimagesink->flow_lock);
2715 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2716 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2717 xvimagesink->have_render_rect = FALSE;
2719 gst_xvimagesink_xcontext_clear (xvimagesink);
2722 /* Finalize is called only once, dispose can be called multiple times.
2723 * We use mutexes and don't reset stuff to NULL here so let's register
2726 gst_xvimagesink_finalize (GObject * object)
2728 GstXvImageSink *xvimagesink;
2730 xvimagesink = GST_XVIMAGESINK (object);
2732 gst_xvimagesink_reset (xvimagesink);
2734 if (xvimagesink->display_name) {
2735 g_free (xvimagesink->display_name);
2736 xvimagesink->display_name = NULL;
2739 if (xvimagesink->par) {
2740 g_free (xvimagesink->par);
2741 xvimagesink->par = NULL;
2743 g_mutex_clear (&xvimagesink->x_lock);
2744 g_mutex_clear (&xvimagesink->flow_lock);
2745 g_free (xvimagesink->media_title);
2747 G_OBJECT_CLASS (parent_class)->finalize (object);
2751 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2753 xvimagesink->display_name = NULL;
2754 xvimagesink->adaptor_no = 0;
2755 xvimagesink->xcontext = NULL;
2756 xvimagesink->xwindow = NULL;
2757 xvimagesink->cur_image = NULL;
2759 xvimagesink->hue = xvimagesink->saturation = 0;
2760 xvimagesink->contrast = xvimagesink->brightness = 0;
2761 xvimagesink->cb_changed = FALSE;
2763 xvimagesink->fps_n = 0;
2764 xvimagesink->fps_d = 0;
2765 xvimagesink->video_width = 0;
2766 xvimagesink->video_height = 0;
2768 g_mutex_init (&xvimagesink->x_lock);
2769 g_mutex_init (&xvimagesink->flow_lock);
2771 xvimagesink->pool = NULL;
2773 xvimagesink->synchronous = FALSE;
2774 xvimagesink->double_buffer = TRUE;
2775 xvimagesink->running = FALSE;
2776 xvimagesink->keep_aspect = TRUE;
2777 xvimagesink->handle_events = TRUE;
2778 xvimagesink->par = NULL;
2779 xvimagesink->handle_expose = TRUE;
2780 xvimagesink->autopaint_colorkey = TRUE;
2782 /* on 16bit displays this becomes r,g,b = 1,2,3
2783 * on 24bit displays this becomes r,g,b = 8,8,16
2784 * as a port atom value
2786 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2787 xvimagesink->draw_borders = TRUE;
2791 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2793 GObjectClass *gobject_class;
2794 GstElementClass *gstelement_class;
2795 GstBaseSinkClass *gstbasesink_class;
2796 GstVideoSinkClass *videosink_class;
2798 gobject_class = (GObjectClass *) klass;
2799 gstelement_class = (GstElementClass *) klass;
2800 gstbasesink_class = (GstBaseSinkClass *) klass;
2801 videosink_class = (GstVideoSinkClass *) klass;
2803 parent_class = g_type_class_peek_parent (klass);
2805 gobject_class->set_property = gst_xvimagesink_set_property;
2806 gobject_class->get_property = gst_xvimagesink_get_property;
2808 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2809 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2810 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2811 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2812 g_param_spec_int ("brightness", "Brightness",
2813 "The brightness of the video", -1000, 1000, 0,
2814 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2815 g_object_class_install_property (gobject_class, PROP_HUE,
2816 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2817 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2818 g_object_class_install_property (gobject_class, PROP_SATURATION,
2819 g_param_spec_int ("saturation", "Saturation",
2820 "The saturation of the video", -1000, 1000, 0,
2821 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2822 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2823 g_param_spec_string ("display", "Display", "X Display name", NULL,
2824 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2825 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2826 g_param_spec_boolean ("synchronous", "Synchronous",
2827 "When enabled, runs the X display in synchronous mode. "
2828 "(unrelated to A/V sync, used only for debugging)", FALSE,
2829 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2830 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2831 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2832 "The pixel aspect ratio of the device", "1/1",
2833 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2834 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2835 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2836 "When enabled, scaling will respect original aspect ratio", TRUE,
2837 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2838 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2839 g_param_spec_boolean ("handle-events", "Handle XEvents",
2840 "When enabled, XEvents will be selected and handled", TRUE,
2841 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2842 g_object_class_install_property (gobject_class, PROP_DEVICE,
2843 g_param_spec_string ("device", "Adaptor number",
2844 "The number of the video adaptor", "0",
2845 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2846 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2847 g_param_spec_string ("device-name", "Adaptor name",
2848 "The name of the video adaptor", NULL,
2849 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2851 * GstXvImageSink:handle-expose
2853 * When enabled, the current frame will always be drawn in response to X
2858 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2859 g_param_spec_boolean ("handle-expose", "Handle expose",
2861 "the current frame will always be drawn in response to X Expose "
2862 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2865 * GstXvImageSink:double-buffer
2867 * Whether to double-buffer the output.
2871 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2872 g_param_spec_boolean ("double-buffer", "Double-buffer",
2873 "Whether to double-buffer the output", TRUE,
2874 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2876 * GstXvImageSink:autopaint-colorkey
2878 * Whether to autofill overlay with colorkey
2882 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2883 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2884 "Whether to autofill overlay with colorkey", TRUE,
2885 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2887 * GstXvImageSink:colorkey
2889 * Color to use for the overlay mask.
2893 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2894 g_param_spec_int ("colorkey", "Colorkey",
2895 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2896 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2899 * GstXvImageSink:draw-borders
2901 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2902 * unused parts of the video area.
2906 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2907 g_param_spec_boolean ("draw-borders", "Colorkey",
2908 "Draw black borders to fill unused area in force-aspect-ratio mode",
2909 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2912 * GstXvImageSink:window-width
2914 * Actual width of the video window.
2918 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2919 g_param_spec_uint64 ("window-width", "window-width",
2920 "Width of the window", 0, G_MAXUINT64, 0,
2921 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2924 * GstXvImageSink:window-height
2926 * Actual height of the video window.
2930 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2931 g_param_spec_uint64 ("window-height", "window-height",
2932 "Height of the window", 0, G_MAXUINT64, 0,
2933 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2935 gobject_class->finalize = gst_xvimagesink_finalize;
2937 gst_element_class_set_static_metadata (gstelement_class,
2938 "Video sink", "Sink/Video",
2939 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2941 gst_element_class_add_pad_template (gstelement_class,
2942 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2944 gstelement_class->change_state =
2945 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2947 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2948 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2949 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2950 gstbasesink_class->propose_allocation =
2951 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2952 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2954 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);