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) {
336 dst.w = xvimagesink->render_rect.w;
337 dst.h = xvimagesink->render_rect.h;
339 gst_video_sink_center_rect (src, dst, &result, TRUE);
340 result.x += xvimagesink->render_rect.x;
341 result.y += xvimagesink->render_rect.y;
343 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
346 g_mutex_lock (xvimagesink->x_lock);
348 if (draw_border && xvimagesink->draw_borders) {
349 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
351 xvimagesink->redraw_border = FALSE;
354 if (xvimagesink->xcontext->use_xshm) {
355 GST_LOG_OBJECT (xvimagesink,
356 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
357 GST_PTR_FORMAT, meta->width, meta->height,
358 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
360 XvShmPutImage (xvimagesink->xcontext->disp,
361 xvimagesink->xcontext->xv_port_id,
362 xvimagesink->xwindow->win,
363 xvimagesink->xwindow->gc, meta->xvimage,
364 src.x, src.y, src.w, src.h,
365 result.x, result.y, result.w, result.h, FALSE);
367 #endif /* HAVE_XSHM */
369 XvPutImage (xvimagesink->xcontext->disp,
370 xvimagesink->xcontext->xv_port_id,
371 xvimagesink->xwindow->win,
372 xvimagesink->xwindow->gc, meta->xvimage,
373 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
376 XSync (xvimagesink->xcontext->disp, FALSE);
378 g_mutex_unlock (xvimagesink->x_lock);
380 g_mutex_unlock (xvimagesink->flow_lock);
386 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
389 Atom hints_atom = None;
392 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
393 g_return_val_if_fail (window != NULL, FALSE);
395 g_mutex_lock (xvimagesink->x_lock);
397 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
399 if (hints_atom == None) {
400 g_mutex_unlock (xvimagesink->x_lock);
404 hints = g_malloc0 (sizeof (MotifWmHints));
406 hints->flags |= MWM_HINTS_DECORATIONS;
407 hints->decorations = 1 << 0;
409 XChangeProperty (xvimagesink->xcontext->disp, window->win,
410 hints_atom, hints_atom, 32, PropModeReplace,
411 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
413 XSync (xvimagesink->xcontext->disp, FALSE);
415 g_mutex_unlock (xvimagesink->x_lock);
423 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
424 GstXWindow * xwindow, const gchar * media_title)
427 g_free (xvimagesink->media_title);
428 xvimagesink->media_title = g_strdup (media_title);
431 /* we have a window */
432 if (xwindow->internal) {
433 XTextProperty xproperty;
434 const gchar *app_name;
435 const gchar *title = NULL;
436 gchar *title_mem = NULL;
438 /* set application name as a title */
439 app_name = g_get_application_name ();
441 if (app_name && xvimagesink->media_title) {
442 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
444 } else if (app_name) {
446 } else if (xvimagesink->media_title) {
447 title = xvimagesink->media_title;
451 if ((XStringListToTextProperty (((char **) &title), 1,
453 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
454 XFree (xproperty.value);
463 /* This function handles a GstXWindow creation
464 * The width and height are the actual pixel size on the display */
466 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
467 gint width, gint height)
469 GstXWindow *xwindow = NULL;
472 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
474 xwindow = g_new0 (GstXWindow, 1);
476 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
477 xvimagesink->render_rect.w = width;
478 xvimagesink->render_rect.h = height;
480 xwindow->width = width;
481 xwindow->height = height;
482 xwindow->internal = TRUE;
484 g_mutex_lock (xvimagesink->x_lock);
486 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
487 xvimagesink->xcontext->root,
488 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
490 /* We have to do that to prevent X from redrawing the background on
491 * ConfigureNotify. This takes away flickering of video when resizing. */
492 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
494 /* set application name as a title */
495 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
497 if (xvimagesink->handle_events) {
500 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
501 StructureNotifyMask | PointerMotionMask | KeyPressMask |
502 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
504 /* Tell the window manager we'd like delete client messages instead of
506 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
507 "WM_DELETE_WINDOW", True);
508 if (wm_delete != None) {
509 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
514 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
515 xwindow->win, 0, &values);
517 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
519 XSync (xvimagesink->xcontext->disp, FALSE);
521 g_mutex_unlock (xvimagesink->x_lock);
523 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
525 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
531 /* This function destroys a GstXWindow */
533 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
534 GstXWindow * xwindow)
536 g_return_if_fail (xwindow != NULL);
537 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
539 g_mutex_lock (xvimagesink->x_lock);
541 /* If we did not create that window we just free the GC and let it live */
542 if (xwindow->internal)
543 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
545 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
547 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
549 XSync (xvimagesink->xcontext->disp, FALSE);
551 g_mutex_unlock (xvimagesink->x_lock);
557 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
559 XWindowAttributes attr;
561 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
563 /* Update the window geometry */
564 g_mutex_lock (xvimagesink->x_lock);
565 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
566 g_mutex_unlock (xvimagesink->x_lock);
570 XGetWindowAttributes (xvimagesink->xcontext->disp,
571 xvimagesink->xwindow->win, &attr);
573 xvimagesink->xwindow->width = attr.width;
574 xvimagesink->xwindow->height = attr.height;
576 if (!xvimagesink->have_render_rect) {
577 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
578 xvimagesink->render_rect.w = attr.width;
579 xvimagesink->render_rect.h = attr.height;
582 g_mutex_unlock (xvimagesink->x_lock);
586 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
587 GstXWindow * xwindow)
589 g_return_if_fail (xwindow != NULL);
590 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
592 g_mutex_lock (xvimagesink->x_lock);
594 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
597 XSync (xvimagesink->xcontext->disp, FALSE);
599 g_mutex_unlock (xvimagesink->x_lock);
602 /* This function commits our internal colorbalance settings to our grabbed Xv
603 port. If the xcontext is not initialized yet it simply returns */
605 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
607 GList *channels = NULL;
609 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
611 /* If we haven't initialized the X context we can't update anything */
612 if (xvimagesink->xcontext == NULL)
615 /* Don't set the attributes if they haven't been changed, to avoid
616 * rounding errors changing the values */
617 if (!xvimagesink->cb_changed)
620 /* For each channel of the colorbalance we calculate the correct value
621 doing range conversion and then set the Xv port attribute to match our
623 channels = xvimagesink->xcontext->channels_list;
626 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
627 GstColorBalanceChannel *channel = NULL;
630 gdouble convert_coef;
632 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
633 g_object_ref (channel);
635 /* Our range conversion coef */
636 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
638 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
639 value = xvimagesink->hue;
640 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
641 value = xvimagesink->saturation;
642 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
643 value = xvimagesink->contrast;
644 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
645 value = xvimagesink->brightness;
647 g_warning ("got an unknown channel %s", channel->label);
648 g_object_unref (channel);
652 /* Committing to Xv port */
653 g_mutex_lock (xvimagesink->x_lock);
655 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
656 if (prop_atom != None) {
659 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
660 XvSetPortAttribute (xvimagesink->xcontext->disp,
661 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
663 g_mutex_unlock (xvimagesink->x_lock);
665 g_object_unref (channel);
667 channels = g_list_next (channels);
671 /* This function handles XEvents that might be in the queue. It generates
672 GstEvent that will be sent upstream in the pipeline to handle interactivity
673 and navigation. It will also listen for configure events on the window to
674 trigger caps renegotiation so on the fly software scaling can work. */
676 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
679 guint pointer_x = 0, pointer_y = 0;
680 gboolean pointer_moved = FALSE;
681 gboolean exposed = FALSE, configured = FALSE;
683 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
685 /* Handle Interaction, produces navigation events */
687 /* We get all pointer motion events, only the last position is
689 g_mutex_lock (xvimagesink->flow_lock);
690 g_mutex_lock (xvimagesink->x_lock);
691 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
692 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
693 g_mutex_unlock (xvimagesink->x_lock);
694 g_mutex_unlock (xvimagesink->flow_lock);
698 pointer_x = e.xmotion.x;
699 pointer_y = e.xmotion.y;
700 pointer_moved = TRUE;
705 g_mutex_lock (xvimagesink->flow_lock);
706 g_mutex_lock (xvimagesink->x_lock);
710 g_mutex_unlock (xvimagesink->x_lock);
711 g_mutex_unlock (xvimagesink->flow_lock);
713 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
714 pointer_x, pointer_y);
715 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
716 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
718 g_mutex_lock (xvimagesink->flow_lock);
719 g_mutex_lock (xvimagesink->x_lock);
722 /* We get all events on our window to throw them upstream */
723 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
724 xvimagesink->xwindow->win,
725 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
728 const char *key_str = NULL;
730 /* We lock only for the X function call */
731 g_mutex_unlock (xvimagesink->x_lock);
732 g_mutex_unlock (xvimagesink->flow_lock);
736 /* Mouse button pressed over our window. We send upstream
737 events for interactivity/navigation */
738 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
739 e.xbutton.button, e.xbutton.x, e.xbutton.y);
740 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
741 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
744 /* Mouse button released over our window. We send upstream
745 events for interactivity/navigation */
746 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
747 e.xbutton.button, e.xbutton.x, e.xbutton.y);
748 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
749 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
753 /* Key pressed/released over our window. We send upstream
754 events for interactivity/navigation */
755 g_mutex_lock (xvimagesink->x_lock);
756 keysym = XkbKeycodeToKeysym (xvimagesink->xcontext->disp,
757 e.xkey.keycode, 0, 0);
758 if (keysym != NoSymbol) {
759 key_str = XKeysymToString (keysym);
763 g_mutex_unlock (xvimagesink->x_lock);
764 GST_DEBUG_OBJECT (xvimagesink,
765 "key %d pressed over window at %d,%d (%s)",
766 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
767 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
768 e.type == KeyPress ? "key-press" : "key-release", key_str);
771 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
774 g_mutex_lock (xvimagesink->flow_lock);
775 g_mutex_lock (xvimagesink->x_lock);
779 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
780 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
785 case ConfigureNotify:
786 g_mutex_unlock (xvimagesink->x_lock);
787 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
788 g_mutex_lock (xvimagesink->x_lock);
796 if (xvimagesink->handle_expose && (exposed || configured)) {
797 g_mutex_unlock (xvimagesink->x_lock);
798 g_mutex_unlock (xvimagesink->flow_lock);
800 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
802 g_mutex_lock (xvimagesink->flow_lock);
803 g_mutex_lock (xvimagesink->x_lock);
806 /* Handle Display events */
807 while (XPending (xvimagesink->xcontext->disp)) {
808 XNextEvent (xvimagesink->xcontext->disp, &e);
814 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
815 "WM_DELETE_WINDOW", True);
816 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
817 /* Handle window deletion by posting an error on the bus */
818 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
819 ("Output window was closed"), (NULL));
821 g_mutex_unlock (xvimagesink->x_lock);
822 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
823 xvimagesink->xwindow = NULL;
824 g_mutex_lock (xvimagesink->x_lock);
833 g_mutex_unlock (xvimagesink->x_lock);
834 g_mutex_unlock (xvimagesink->flow_lock);
838 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
839 XvAdaptorInfo * adaptors, int adaptor_no)
844 /* Do we support XvImageMask ? */
845 if (!(adaptors[adaptor_no].type & XvImageMask)) {
846 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
847 adaptors[adaptor_no].name);
851 /* We found such an adaptor, looking for an available port */
852 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
853 /* We try to grab the port */
854 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
855 if (Success == res) {
856 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
857 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
858 adaptors[adaptor_no].num_ports);
860 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
861 adaptors[adaptor_no].name, res);
866 /* This function generates a caps with all supported format by the first
867 Xv grabable port we find. We store each one of the supported formats in a
868 format list and append the format to a newly created caps that we return
869 If this function does not return NULL because of an error, it also grabs
870 the port via XvGrabPort */
872 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
873 GstXContext * xcontext)
876 XvAdaptorInfo *adaptors;
878 XvImageFormatValues *formats = NULL;
880 XvEncodingInfo *encodings = NULL;
881 gulong max_w = G_MAXINT, max_h = G_MAXINT;
882 GstCaps *caps = NULL;
883 GstCaps *rgb_caps = NULL;
885 g_return_val_if_fail (xcontext != NULL, NULL);
887 /* First let's check that XVideo extension is available */
888 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
889 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
890 ("Could not initialise Xv output"),
891 ("XVideo extension is not available"));
895 /* Then we get adaptors list */
896 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
897 &xcontext->nb_adaptors, &adaptors)) {
898 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
899 ("Could not initialise Xv output"),
900 ("Failed getting XV adaptors list"));
904 xcontext->xv_port_id = 0;
906 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
909 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
911 /* Now fill up our adaptor name array */
912 for (i = 0; i < xcontext->nb_adaptors; i++) {
913 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
916 if (xvimagesink->adaptor_no != -1 &&
917 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
918 /* Find xv port from user defined adaptor */
919 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
920 xvimagesink->adaptor_no);
923 if (!xcontext->xv_port_id) {
924 /* Now search for an adaptor that supports XvImageMask */
925 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
926 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
927 xvimagesink->adaptor_no = i;
931 XvFreeAdaptorInfo (adaptors);
933 if (!xcontext->xv_port_id) {
934 xvimagesink->adaptor_no = -1;
935 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
936 ("Could not initialise Xv output"), ("No port available"));
940 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
943 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
944 xcontext->xv_port_id, &count);
945 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
946 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
947 static const char colorkey[] = "XV_COLORKEY";
949 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
951 xvimagesink->have_autopaint_colorkey = FALSE;
952 xvimagesink->have_double_buffer = FALSE;
953 xvimagesink->have_colorkey = FALSE;
955 for (i = 0; ((i < count) && todo); i++)
956 if (!strcmp (attr[i].name, autopaint)) {
957 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
959 /* turn on autopaint colorkey */
960 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
961 (xvimagesink->autopaint_colorkey ? 1 : 0));
963 xvimagesink->have_autopaint_colorkey = TRUE;
964 } else if (!strcmp (attr[i].name, dbl_buffer)) {
965 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
967 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
968 (xvimagesink->double_buffer ? 1 : 0));
970 xvimagesink->have_double_buffer = TRUE;
971 } else if (!strcmp (attr[i].name, colorkey)) {
972 /* Set the colorkey, default is something that is dark but hopefully
973 * won't randomly appear on the screen elsewhere (ie not black or greys)
974 * can be overridden by setting "colorkey" property
976 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
978 gboolean set_attr = TRUE;
981 /* set a colorkey in the right format RGB565/RGB888
982 * We only handle these 2 cases, because they're the only types of
983 * devices we've encountered. If we don't recognise it, leave it alone
985 cr = (xvimagesink->colorkey >> 16);
986 cg = (xvimagesink->colorkey >> 8) & 0xFF;
987 cb = (xvimagesink->colorkey) & 0xFF;
988 switch (xcontext->depth) {
989 case 16: /* RGB 565 */
993 ckey = (cr << 11) | (cg << 5) | cb;
996 case 32: /* RGB 888 / ARGB 8888 */
997 ckey = (cr << 16) | (cg << 8) | cb;
1000 GST_DEBUG_OBJECT (xvimagesink,
1001 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1008 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1009 (guint32) attr[i].max_value);
1010 GST_LOG_OBJECT (xvimagesink,
1011 "Setting color key for display depth %d to 0x%x",
1012 xcontext->depth, ckey);
1014 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1018 xvimagesink->have_colorkey = TRUE;
1024 /* Get the list of encodings supported by the adapter and look for the
1025 * XV_IMAGE encoding so we can determine the maximum width and height
1027 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1030 for (i = 0; i < nb_encodings; i++) {
1031 GST_LOG_OBJECT (xvimagesink,
1032 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1033 i, encodings[i].name, encodings[i].width, encodings[i].height,
1034 encodings[i].rate.numerator, encodings[i].rate.denominator);
1035 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1036 max_w = encodings[i].width;
1037 max_h = encodings[i].height;
1041 XvFreeEncodingInfo (encodings);
1043 /* We get all image formats supported by our port */
1044 formats = XvListImageFormats (xcontext->disp,
1045 xcontext->xv_port_id, &nb_formats);
1046 caps = gst_caps_new_empty ();
1047 for (i = 0; i < nb_formats; i++) {
1048 GstCaps *format_caps = NULL;
1049 gboolean is_rgb_format = FALSE;
1050 GstVideoFormat vformat;
1052 /* We set the image format of the xcontext to an existing one. This
1053 is just some valid image format for making our xshm calls check before
1054 caps negotiation really happens. */
1055 xcontext->im_format = formats[i].id;
1057 switch (formats[i].type) {
1060 XvImageFormatValues *fmt = &(formats[i]);
1064 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1066 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1067 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1068 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1071 format_caps = gst_caps_new_simple ("video/x-raw",
1072 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1073 "width", GST_TYPE_INT_RANGE, 1, max_w,
1074 "height", GST_TYPE_INT_RANGE, 1, max_h,
1075 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1077 is_rgb_format = TRUE;
1082 vformat = gst_video_format_from_fourcc (formats[i].id);
1083 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1086 format_caps = gst_caps_new_simple ("video/x-raw",
1087 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1088 "width", GST_TYPE_INT_RANGE, 1, max_w,
1089 "height", GST_TYPE_INT_RANGE, 1, max_h,
1090 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1094 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1095 g_assert_not_reached ();
1100 GstXvImageFormat *format = NULL;
1102 format = g_new0 (GstXvImageFormat, 1);
1104 format->format = formats[i].id;
1105 format->vformat = vformat;
1106 format->caps = gst_caps_copy (format_caps);
1107 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1110 if (is_rgb_format) {
1111 if (rgb_caps == NULL)
1112 rgb_caps = format_caps;
1114 gst_caps_append (rgb_caps, format_caps);
1116 gst_caps_append (caps, format_caps);
1120 /* Collected all caps into either the caps or rgb_caps structures.
1121 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1123 gst_caps_append (caps, rgb_caps);
1128 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1130 if (gst_caps_is_empty (caps)) {
1131 gst_caps_unref (caps);
1132 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1133 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1134 ("No supported format found"));
1142 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1144 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1146 GST_OBJECT_LOCK (xvimagesink);
1147 while (xvimagesink->running) {
1148 GST_OBJECT_UNLOCK (xvimagesink);
1150 if (xvimagesink->xwindow) {
1151 gst_xvimagesink_handle_xevents (xvimagesink);
1153 /* FIXME: do we want to align this with the framerate or anything else? */
1154 g_usleep (G_USEC_PER_SEC / 20);
1156 GST_OBJECT_LOCK (xvimagesink);
1158 GST_OBJECT_UNLOCK (xvimagesink);
1164 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1166 GThread *thread = NULL;
1168 /* don't start the thread too early */
1169 if (xvimagesink->xcontext == NULL) {
1173 GST_OBJECT_LOCK (xvimagesink);
1174 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1175 if (!xvimagesink->event_thread) {
1176 /* Setup our event listening thread */
1177 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1178 xvimagesink->handle_expose, xvimagesink->handle_events);
1179 xvimagesink->running = TRUE;
1180 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1181 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1184 if (xvimagesink->event_thread) {
1185 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1186 xvimagesink->handle_expose, xvimagesink->handle_events);
1187 xvimagesink->running = FALSE;
1188 /* grab thread and mark it as NULL */
1189 thread = xvimagesink->event_thread;
1190 xvimagesink->event_thread = NULL;
1193 GST_OBJECT_UNLOCK (xvimagesink);
1195 /* Wait for our event thread to finish */
1197 g_thread_join (thread);
1202 /* This function calculates the pixel aspect ratio based on the properties
1203 * in the xcontext structure and stores it there. */
1205 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1207 static const gint par[][2] = {
1208 {1, 1}, /* regular screen */
1209 {16, 15}, /* PAL TV */
1210 {11, 10}, /* 525 line Rec.601 video */
1211 {54, 59}, /* 625 line Rec.601 video */
1212 {64, 45}, /* 1280x1024 on 16:9 display */
1213 {5, 3}, /* 1280x1024 on 4:3 display */
1214 {4, 3} /* 800x600 on 16:9 display */
1221 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1223 /* first calculate the "real" ratio based on the X values;
1224 * which is the "physical" w/h divided by the w/h in pixels of the display */
1225 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1226 / (xcontext->heightmm * xcontext->width);
1228 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1230 if (xcontext->width == 720 && xcontext->height == 576) {
1231 ratio = 4.0 * 576 / (3.0 * 720);
1233 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1235 /* now find the one from par[][2] with the lowest delta to the real one */
1239 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1240 gdouble this_delta = DELTA (i);
1242 if (this_delta < delta) {
1248 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1249 par[index][0], par[index][1]);
1251 g_free (xcontext->par);
1252 xcontext->par = g_new0 (GValue, 1);
1253 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1254 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1255 GST_DEBUG ("set xcontext PAR to %d/%d",
1256 gst_value_get_fraction_numerator (xcontext->par),
1257 gst_value_get_fraction_denominator (xcontext->par));
1260 /* This function gets the X Display and global info about it. Everything is
1261 stored in our object and will be cleaned when the object is disposed. Note
1262 here that caps for supported format are generated without any window or
1264 static GstXContext *
1265 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1267 GstXContext *xcontext = NULL;
1268 XPixmapFormatValues *px_formats = NULL;
1269 gint nb_formats = 0, i, j, N_attr;
1270 XvAttribute *xv_attr;
1272 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1273 "XV_BRIGHTNESS", "XV_CONTRAST"
1276 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1278 xcontext = g_new0 (GstXContext, 1);
1279 xcontext->im_format = 0;
1281 g_mutex_lock (xvimagesink->x_lock);
1283 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1285 if (!xcontext->disp) {
1286 g_mutex_unlock (xvimagesink->x_lock);
1288 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1289 ("Could not initialise Xv output"), ("Could not open display"));
1293 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1294 xcontext->screen_num = DefaultScreen (xcontext->disp);
1295 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1296 xcontext->root = DefaultRootWindow (xcontext->disp);
1297 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1298 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1299 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1301 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1302 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1303 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1304 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1306 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1307 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1309 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1310 /* We get supported pixmap formats at supported depth */
1311 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1314 XCloseDisplay (xcontext->disp);
1315 g_mutex_unlock (xvimagesink->x_lock);
1316 g_free (xcontext->par);
1318 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1319 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1323 /* We get bpp value corresponding to our running depth */
1324 for (i = 0; i < nb_formats; i++) {
1325 if (px_formats[i].depth == xcontext->depth)
1326 xcontext->bpp = px_formats[i].bits_per_pixel;
1331 xcontext->endianness =
1332 (ImageByteOrder (xcontext->disp) ==
1333 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1335 /* our caps system handles 24/32bpp RGB as big-endian. */
1336 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1337 xcontext->endianness == G_LITTLE_ENDIAN) {
1338 xcontext->endianness = G_BIG_ENDIAN;
1339 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1340 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1341 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1342 if (xcontext->bpp == 24) {
1343 xcontext->visual->red_mask >>= 8;
1344 xcontext->visual->green_mask >>= 8;
1345 xcontext->visual->blue_mask >>= 8;
1349 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1351 /* Search for XShm extension support */
1353 if (XShmQueryExtension (xcontext->disp) &&
1354 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1355 xcontext->use_xshm = TRUE;
1356 GST_DEBUG ("xvimagesink is using XShm extension");
1358 #endif /* HAVE_XSHM */
1360 xcontext->use_xshm = FALSE;
1361 GST_DEBUG ("xvimagesink is not using XShm extension");
1364 if (!xcontext->caps) {
1365 XCloseDisplay (xcontext->disp);
1366 g_mutex_unlock (xvimagesink->x_lock);
1367 g_free (xcontext->par);
1369 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1373 xv_attr = XvQueryPortAttributes (xcontext->disp,
1374 xcontext->xv_port_id, &N_attr);
1377 /* Generate the channels list */
1378 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1379 XvAttribute *matching_attr = NULL;
1381 /* Retrieve the property atom if it exists. If it doesn't exist,
1382 * the attribute itself must not either, so we can skip */
1383 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1384 if (prop_atom == None)
1387 if (xv_attr != NULL) {
1388 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1389 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1390 matching_attr = xv_attr + j;
1393 if (matching_attr) {
1394 GstColorBalanceChannel *channel;
1396 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1397 channel->label = g_strdup (channels[i]);
1398 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1399 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1401 xcontext->channels_list = g_list_append (xcontext->channels_list,
1404 /* If the colorbalance settings have not been touched we get Xv values
1405 as defaults and update our internal variables */
1406 if (!xvimagesink->cb_changed) {
1409 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1411 /* Normalize val to [-1000, 1000] */
1412 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1413 (double) (channel->max_value - channel->min_value));
1415 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1416 xvimagesink->hue = val;
1417 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1418 xvimagesink->saturation = val;
1419 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1420 xvimagesink->brightness = val;
1421 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1422 xvimagesink->contrast = val;
1430 g_mutex_unlock (xvimagesink->x_lock);
1435 /* This function cleans the X context. Closing the Display, releasing the XV
1436 port and unrefing the caps for supported formats. */
1438 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1440 GList *formats_list, *channels_list;
1441 GstXContext *xcontext;
1444 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1446 GST_OBJECT_LOCK (xvimagesink);
1447 if (xvimagesink->xcontext == NULL) {
1448 GST_OBJECT_UNLOCK (xvimagesink);
1452 /* Take the XContext from the sink and clean it up */
1453 xcontext = xvimagesink->xcontext;
1454 xvimagesink->xcontext = NULL;
1456 GST_OBJECT_UNLOCK (xvimagesink);
1459 formats_list = xcontext->formats_list;
1461 while (formats_list) {
1462 GstXvImageFormat *format = formats_list->data;
1464 gst_caps_unref (format->caps);
1466 formats_list = g_list_next (formats_list);
1469 if (xcontext->formats_list)
1470 g_list_free (xcontext->formats_list);
1472 channels_list = xcontext->channels_list;
1474 while (channels_list) {
1475 GstColorBalanceChannel *channel = channels_list->data;
1477 g_object_unref (channel);
1478 channels_list = g_list_next (channels_list);
1481 if (xcontext->channels_list)
1482 g_list_free (xcontext->channels_list);
1484 gst_caps_unref (xcontext->caps);
1485 if (xcontext->last_caps)
1486 gst_caps_replace (&xcontext->last_caps, NULL);
1488 for (i = 0; i < xcontext->nb_adaptors; i++) {
1489 g_free (xcontext->adaptors[i]);
1492 g_free (xcontext->adaptors);
1494 g_free (xcontext->par);
1496 g_mutex_lock (xvimagesink->x_lock);
1498 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1500 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1502 XCloseDisplay (xcontext->disp);
1504 g_mutex_unlock (xvimagesink->x_lock);
1512 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1514 GstXvImageSink *xvimagesink;
1517 xvimagesink = GST_XVIMAGESINK (bsink);
1519 if (xvimagesink->xcontext) {
1521 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1522 GST_CAPS_INTERSECT_FIRST);
1524 return gst_caps_ref (xvimagesink->xcontext->caps);
1527 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1529 GstCaps *intersection;
1532 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1533 gst_caps_unref (caps);
1534 caps = intersection;
1540 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1542 GstXvImageSink *xvimagesink;
1543 GstStructure *structure;
1544 GstBufferPool *newpool, *oldpool;
1546 guint32 im_format = 0;
1547 gint video_par_n, video_par_d; /* video's PAR */
1548 gint display_par_n, display_par_d; /* display's PAR */
1551 static GstAllocationParams params = { 0, 0, 0, 15, };
1553 xvimagesink = GST_XVIMAGESINK (bsink);
1555 GST_DEBUG_OBJECT (xvimagesink,
1556 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1557 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1559 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1560 goto incompatible_caps;
1562 if (!gst_video_info_from_caps (&info, caps))
1563 goto invalid_format;
1565 xvimagesink->fps_n = info.fps_n;
1566 xvimagesink->fps_d = info.fps_d;
1568 xvimagesink->video_width = info.width;
1569 xvimagesink->video_height = info.height;
1571 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1572 if (im_format == -1)
1573 goto invalid_format;
1577 /* get aspect ratio from caps if it's present, and
1578 * convert video width and height to a display width and height
1579 * using wd / hd = wv / hv * PARv / PARd */
1581 /* get video's PAR */
1582 video_par_n = info.par_n;
1583 video_par_d = info.par_d;
1585 /* get display's PAR */
1586 if (xvimagesink->par) {
1587 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1588 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1594 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1595 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1598 GST_DEBUG_OBJECT (xvimagesink,
1599 "video width/height: %dx%d, calculated display ratio: %d/%d",
1600 info.width, info.height, num, den);
1602 /* now find a width x height that respects this display ratio.
1603 * prefer those that have one of w/h the same as the incoming video
1604 * using wd / hd = num / den */
1606 /* start with same height, because of interlaced video */
1607 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1608 if (info.height % den == 0) {
1609 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1610 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1611 gst_util_uint64_scale_int (info.height, num, den);
1612 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1613 } else if (info.width % num == 0) {
1614 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1615 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1616 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1617 gst_util_uint64_scale_int (info.width, den, num);
1619 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1620 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1621 gst_util_uint64_scale_int (info.height, num, den);
1622 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1624 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1625 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1627 /* Notify application to set xwindow id now */
1628 g_mutex_lock (xvimagesink->flow_lock);
1629 if (!xvimagesink->xwindow) {
1630 g_mutex_unlock (xvimagesink->flow_lock);
1631 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1633 g_mutex_unlock (xvimagesink->flow_lock);
1636 /* Creating our window and our image with the display size in pixels */
1637 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1638 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1639 goto no_display_size;
1641 g_mutex_lock (xvimagesink->flow_lock);
1642 if (!xvimagesink->xwindow) {
1643 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1644 GST_VIDEO_SINK_WIDTH (xvimagesink),
1645 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1648 xvimagesink->info = info;
1650 /* After a resize, we want to redraw the borders in case the new frame size
1651 * doesn't cover the same area */
1652 xvimagesink->redraw_border = TRUE;
1654 /* create a new pool for the new configuration */
1655 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1657 structure = gst_buffer_pool_get_config (newpool);
1658 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1659 gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms);
1660 if (!gst_buffer_pool_set_config (newpool, structure))
1663 oldpool = xvimagesink->pool;
1664 /* we don't activate the pool yet, this will be done by downstream after it
1665 * has configured the pool. If downstream does not want our pool we will
1666 * activate it when we render into it */
1667 xvimagesink->pool = newpool;
1668 g_mutex_unlock (xvimagesink->flow_lock);
1670 /* unref the old sink */
1672 /* we don't deactivate, some elements might still be using it, it will
1673 * be deactivated when the last ref is gone */
1674 gst_object_unref (oldpool);
1682 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1687 GST_DEBUG_OBJECT (xvimagesink,
1688 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1693 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1694 ("Error calculating the output display ratio of the video."));
1699 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1700 ("Error calculating the output display ratio of the video."));
1705 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1706 g_mutex_unlock (xvimagesink->flow_lock);
1711 static GstStateChangeReturn
1712 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1714 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1715 GstXvImageSink *xvimagesink;
1716 GstXContext *xcontext = NULL;
1718 xvimagesink = GST_XVIMAGESINK (element);
1720 switch (transition) {
1721 case GST_STATE_CHANGE_NULL_TO_READY:
1722 /* Initializing the XContext */
1723 if (xvimagesink->xcontext == NULL) {
1724 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1725 if (xcontext == NULL) {
1726 ret = GST_STATE_CHANGE_FAILURE;
1729 GST_OBJECT_LOCK (xvimagesink);
1731 xvimagesink->xcontext = xcontext;
1732 GST_OBJECT_UNLOCK (xvimagesink);
1735 /* update object's par with calculated one if not set yet */
1736 if (!xvimagesink->par) {
1737 xvimagesink->par = g_new0 (GValue, 1);
1738 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1739 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1741 /* call XSynchronize with the current value of synchronous */
1742 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1743 xvimagesink->synchronous ? "TRUE" : "FALSE");
1744 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1745 gst_xvimagesink_update_colorbalance (xvimagesink);
1746 gst_xvimagesink_manage_event_thread (xvimagesink);
1748 case GST_STATE_CHANGE_READY_TO_PAUSED:
1750 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1752 case GST_STATE_CHANGE_PAUSED_TO_READY:
1758 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1760 switch (transition) {
1761 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1763 case GST_STATE_CHANGE_PAUSED_TO_READY:
1764 xvimagesink->fps_n = 0;
1765 xvimagesink->fps_d = 1;
1766 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1767 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1768 g_mutex_lock (xvimagesink->flow_lock);
1769 if (xvimagesink->pool)
1770 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1771 g_mutex_unlock (xvimagesink->flow_lock);
1773 case GST_STATE_CHANGE_READY_TO_NULL:
1774 gst_xvimagesink_reset (xvimagesink);
1785 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1786 GstClockTime * start, GstClockTime * end)
1788 GstXvImageSink *xvimagesink;
1790 xvimagesink = GST_XVIMAGESINK (bsink);
1792 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1793 *start = GST_BUFFER_TIMESTAMP (buf);
1794 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1795 *end = *start + GST_BUFFER_DURATION (buf);
1797 if (xvimagesink->fps_n > 0) {
1799 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1800 xvimagesink->fps_n);
1806 static GstFlowReturn
1807 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1810 GstXvImageSink *xvimagesink;
1811 GstXvImageMeta *meta;
1814 xvimagesink = GST_XVIMAGESINK (vsink);
1816 meta = gst_buffer_get_xvimage_meta (buf);
1818 if (meta && meta->sink == xvimagesink) {
1819 /* If this buffer has been allocated using our buffer management we simply
1820 put the ximage which is in the PRIVATE pointer */
1821 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1826 GstVideoFrame src, dest;
1828 /* Else we have to copy the data into our private image, */
1829 /* if we have one... */
1830 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1832 /* we should have a pool, configured in setcaps */
1833 if (xvimagesink->pool == NULL)
1836 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1837 goto activate_failed;
1839 /* take a buffer from our pool */
1840 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1841 if (res != GST_FLOW_OK)
1844 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1845 "slow copy into bufferpool buffer %p", to_put);
1847 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1848 goto invalid_buffer;
1850 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1851 gst_video_frame_unmap (&src);
1852 goto invalid_buffer;
1855 gst_video_frame_copy (&dest, &src);
1857 gst_video_frame_unmap (&dest);
1858 gst_video_frame_unmap (&src);
1861 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1866 gst_buffer_unref (to_put);
1873 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1874 ("Internal error: can't allocate images"),
1875 ("We don't have a bufferpool negotiated"));
1876 return GST_FLOW_ERROR;
1880 /* No image available. That's very bad ! */
1881 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1886 /* No Window available to put our image into */
1887 GST_WARNING_OBJECT (xvimagesink, "could map image");
1893 /* No Window available to put our image into */
1894 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1895 res = GST_FLOW_ERROR;
1900 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1901 res = GST_FLOW_ERROR;
1907 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1909 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1911 switch (GST_EVENT_TYPE (event)) {
1912 case GST_EVENT_TAG:{
1914 gchar *title = NULL;
1916 gst_event_parse_tag (event, &l);
1917 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1920 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1921 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1931 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1935 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1937 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1938 GstBufferPool *pool;
1939 GstStructure *config;
1944 gst_query_parse_allocation (query, &caps, &need_pool);
1949 g_mutex_lock (xvimagesink->flow_lock);
1950 if ((pool = xvimagesink->pool))
1951 gst_object_ref (pool);
1952 g_mutex_unlock (xvimagesink->flow_lock);
1957 /* we had a pool, check caps */
1958 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1959 config = gst_buffer_pool_get_config (pool);
1960 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1962 if (!gst_caps_is_equal (caps, pcaps)) {
1963 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1964 /* different caps, we can't use this pool */
1965 gst_object_unref (pool);
1968 gst_structure_free (config);
1970 if (pool == NULL && need_pool) {
1973 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1974 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1976 if (!gst_video_info_from_caps (&info, caps))
1979 /* the normal size of a frame */
1982 config = gst_buffer_pool_get_config (pool);
1983 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1984 if (!gst_buffer_pool_set_config (pool, config))
1988 /* we need at least 2 buffer because we hold on to the last one */
1989 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1990 gst_object_unref (pool);
1993 /* we also support various metadata */
1994 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE);
1995 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE);
2002 GST_DEBUG_OBJECT (bsink, "no caps specified");
2007 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2012 GST_DEBUG_OBJECT (bsink, "failed setting config");
2017 /* Interfaces stuff */
2019 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2020 GstStructure * structure)
2022 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2025 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2027 GstVideoRectangle src, dst, result;
2028 gdouble x, y, xscale = 1.0, yscale = 1.0;
2030 event = gst_event_new_navigation (structure);
2032 /* We take the flow_lock while we look at the window */
2033 g_mutex_lock (xvimagesink->flow_lock);
2035 if (!xvimagesink->xwindow) {
2036 g_mutex_unlock (xvimagesink->flow_lock);
2040 if (xvimagesink->keep_aspect) {
2041 /* We get the frame position using the calculated geometry from _setcaps
2042 that respect pixel aspect ratios */
2043 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2044 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2045 dst.w = xvimagesink->render_rect.w;
2046 dst.h = xvimagesink->render_rect.h;
2048 gst_video_sink_center_rect (src, dst, &result, TRUE);
2049 result.x += xvimagesink->render_rect.x;
2050 result.y += xvimagesink->render_rect.y;
2052 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2055 g_mutex_unlock (xvimagesink->flow_lock);
2057 /* We calculate scaling using the original video frames geometry to include
2058 pixel aspect ratio scaling. */
2059 xscale = (gdouble) xvimagesink->video_width / result.w;
2060 yscale = (gdouble) xvimagesink->video_height / result.h;
2062 /* Converting pointer coordinates to the non scaled geometry */
2063 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2064 x = MIN (x, result.x + result.w);
2065 x = MAX (x - result.x, 0);
2066 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2067 (gdouble) x * xscale, NULL);
2069 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2070 y = MIN (y, result.y + result.h);
2071 y = MAX (y - result.y, 0);
2072 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2073 (gdouble) y * yscale, NULL);
2076 gst_pad_send_event (peer, event);
2077 gst_object_unref (peer);
2082 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2084 iface->send_event = gst_xvimagesink_navigation_send_event;
2088 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2090 XID xwindow_id = id;
2091 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2092 GstXWindow *xwindow = NULL;
2094 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2096 g_mutex_lock (xvimagesink->flow_lock);
2098 /* If we already use that window return */
2099 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2100 g_mutex_unlock (xvimagesink->flow_lock);
2104 /* If the element has not initialized the X11 context try to do so */
2105 if (!xvimagesink->xcontext &&
2106 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2107 g_mutex_unlock (xvimagesink->flow_lock);
2108 /* we have thrown a GST_ELEMENT_ERROR now */
2112 gst_xvimagesink_update_colorbalance (xvimagesink);
2114 /* If a window is there already we destroy it */
2115 if (xvimagesink->xwindow) {
2116 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2117 xvimagesink->xwindow = NULL;
2120 /* If the xid is 0 we go back to an internal window */
2121 if (xwindow_id == 0) {
2122 /* If no width/height caps nego did not happen window will be created
2123 during caps nego then */
2124 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2125 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2127 gst_xvimagesink_xwindow_new (xvimagesink,
2128 GST_VIDEO_SINK_WIDTH (xvimagesink),
2129 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2132 XWindowAttributes attr;
2134 xwindow = g_new0 (GstXWindow, 1);
2135 xwindow->win = xwindow_id;
2137 /* Set the event we want to receive and create a GC */
2138 g_mutex_lock (xvimagesink->x_lock);
2140 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2142 xwindow->width = attr.width;
2143 xwindow->height = attr.height;
2144 xwindow->internal = FALSE;
2145 if (!xvimagesink->have_render_rect) {
2146 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2147 xvimagesink->render_rect.w = attr.width;
2148 xvimagesink->render_rect.h = attr.height;
2150 if (xvimagesink->handle_events) {
2151 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2152 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2156 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2157 xwindow->win, 0, NULL);
2158 g_mutex_unlock (xvimagesink->x_lock);
2162 xvimagesink->xwindow = xwindow;
2164 g_mutex_unlock (xvimagesink->flow_lock);
2168 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2170 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2172 GST_DEBUG ("doing expose");
2173 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2174 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2178 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2179 gboolean handle_events)
2181 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2183 xvimagesink->handle_events = handle_events;
2185 g_mutex_lock (xvimagesink->flow_lock);
2187 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2188 g_mutex_unlock (xvimagesink->flow_lock);
2192 g_mutex_lock (xvimagesink->x_lock);
2194 if (handle_events) {
2195 if (xvimagesink->xwindow->internal) {
2196 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2197 ExposureMask | StructureNotifyMask | PointerMotionMask |
2198 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2200 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2201 ExposureMask | StructureNotifyMask | PointerMotionMask |
2202 KeyPressMask | KeyReleaseMask);
2205 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2208 g_mutex_unlock (xvimagesink->x_lock);
2210 g_mutex_unlock (xvimagesink->flow_lock);
2214 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2215 gint width, gint height)
2217 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2219 /* FIXME: how about some locking? */
2220 if (width >= 0 && height >= 0) {
2221 xvimagesink->render_rect.x = x;
2222 xvimagesink->render_rect.y = y;
2223 xvimagesink->render_rect.w = width;
2224 xvimagesink->render_rect.h = height;
2225 xvimagesink->have_render_rect = TRUE;
2227 xvimagesink->render_rect.x = 0;
2228 xvimagesink->render_rect.y = 0;
2229 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2230 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2231 xvimagesink->have_render_rect = FALSE;
2236 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2238 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2239 iface->expose = gst_xvimagesink_expose;
2240 iface->handle_events = gst_xvimagesink_set_event_handling;
2241 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2244 static const GList *
2245 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2247 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2249 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2251 if (xvimagesink->xcontext)
2252 return xvimagesink->xcontext->channels_list;
2258 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2259 GstColorBalanceChannel * channel, gint value)
2261 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2263 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2264 g_return_if_fail (channel->label != NULL);
2266 xvimagesink->cb_changed = TRUE;
2268 /* Normalize val to [-1000, 1000] */
2269 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2270 (double) (channel->max_value - channel->min_value));
2272 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2273 xvimagesink->hue = value;
2274 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2275 xvimagesink->saturation = value;
2276 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2277 xvimagesink->contrast = value;
2278 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2279 xvimagesink->brightness = value;
2281 g_warning ("got an unknown channel %s", channel->label);
2285 gst_xvimagesink_update_colorbalance (xvimagesink);
2289 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2290 GstColorBalanceChannel * channel)
2292 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2295 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2296 g_return_val_if_fail (channel->label != NULL, 0);
2298 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2299 value = xvimagesink->hue;
2300 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2301 value = xvimagesink->saturation;
2302 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2303 value = xvimagesink->contrast;
2304 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2305 value = xvimagesink->brightness;
2307 g_warning ("got an unknown channel %s", channel->label);
2310 /* Normalize val to [channel->min_value, channel->max_value] */
2311 value = channel->min_value + (channel->max_value - channel->min_value) *
2312 (value + 1000) / 2000;
2317 static GstColorBalanceType
2318 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
2320 return GST_COLOR_BALANCE_HARDWARE;
2324 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2326 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2327 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2328 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2329 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
2333 static const GList *
2334 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2336 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2337 static GList *list = NULL;
2340 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2342 g_list_append (list, g_object_class_find_property (klass,
2343 "autopaint-colorkey"));
2345 g_list_append (list, g_object_class_find_property (klass,
2348 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2355 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2356 guint prop_id, const GParamSpec * pspec)
2358 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2362 case PROP_AUTOPAINT_COLORKEY:
2363 case PROP_DOUBLE_BUFFER:
2365 GST_DEBUG_OBJECT (xvimagesink,
2366 "probing device list and get capabilities");
2367 if (!xvimagesink->xcontext) {
2368 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2369 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2373 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2379 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2380 guint prop_id, const GParamSpec * pspec)
2382 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2383 gboolean ret = FALSE;
2387 case PROP_AUTOPAINT_COLORKEY:
2388 case PROP_DOUBLE_BUFFER:
2390 if (xvimagesink->xcontext != NULL) {
2397 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2404 static GValueArray *
2405 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2406 guint prop_id, const GParamSpec * pspec)
2408 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2409 GValueArray *array = NULL;
2411 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2412 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2421 GValue value = { 0 };
2423 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2424 g_value_init (&value, G_TYPE_STRING);
2426 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2427 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2429 g_value_set_string (&value, adaptor_id_s);
2430 g_value_array_append (array, &value);
2431 g_free (adaptor_id_s);
2433 g_value_unset (&value);
2436 case PROP_AUTOPAINT_COLORKEY:
2437 if (xvimagesink->have_autopaint_colorkey) {
2438 GValue value = { 0 };
2440 array = g_value_array_new (2);
2441 g_value_init (&value, G_TYPE_BOOLEAN);
2442 g_value_set_boolean (&value, FALSE);
2443 g_value_array_append (array, &value);
2444 g_value_set_boolean (&value, TRUE);
2445 g_value_array_append (array, &value);
2446 g_value_unset (&value);
2449 case PROP_DOUBLE_BUFFER:
2450 if (xvimagesink->have_double_buffer) {
2451 GValue value = { 0 };
2453 array = g_value_array_new (2);
2454 g_value_init (&value, G_TYPE_BOOLEAN);
2455 g_value_set_boolean (&value, FALSE);
2456 g_value_array_append (array, &value);
2457 g_value_set_boolean (&value, TRUE);
2458 g_value_array_append (array, &value);
2459 g_value_unset (&value);
2463 if (xvimagesink->have_colorkey) {
2464 GValue value = { 0 };
2466 array = g_value_array_new (1);
2467 g_value_init (&value, GST_TYPE_INT_RANGE);
2468 gst_value_set_int_range (&value, 0, 0xffffff);
2469 g_value_array_append (array, &value);
2470 g_value_unset (&value);
2474 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2483 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2486 iface->get_properties = gst_xvimagesink_probe_get_properties;
2487 iface->probe_property = gst_xvimagesink_probe_probe_property;
2488 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2489 iface->get_values = gst_xvimagesink_probe_get_values;
2493 /* =========================================== */
2495 /* Init & Class init */
2497 /* =========================================== */
2500 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2501 const GValue * value, GParamSpec * pspec)
2503 GstXvImageSink *xvimagesink;
2505 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2507 xvimagesink = GST_XVIMAGESINK (object);
2511 xvimagesink->hue = g_value_get_int (value);
2512 xvimagesink->cb_changed = TRUE;
2513 gst_xvimagesink_update_colorbalance (xvimagesink);
2516 xvimagesink->contrast = g_value_get_int (value);
2517 xvimagesink->cb_changed = TRUE;
2518 gst_xvimagesink_update_colorbalance (xvimagesink);
2520 case PROP_BRIGHTNESS:
2521 xvimagesink->brightness = g_value_get_int (value);
2522 xvimagesink->cb_changed = TRUE;
2523 gst_xvimagesink_update_colorbalance (xvimagesink);
2525 case PROP_SATURATION:
2526 xvimagesink->saturation = g_value_get_int (value);
2527 xvimagesink->cb_changed = TRUE;
2528 gst_xvimagesink_update_colorbalance (xvimagesink);
2531 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2533 case PROP_SYNCHRONOUS:
2534 xvimagesink->synchronous = g_value_get_boolean (value);
2535 if (xvimagesink->xcontext) {
2536 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2537 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2538 xvimagesink->synchronous ? "TRUE" : "FALSE");
2541 case PROP_PIXEL_ASPECT_RATIO:
2542 g_free (xvimagesink->par);
2543 xvimagesink->par = g_new0 (GValue, 1);
2544 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2545 if (!g_value_transform (value, xvimagesink->par)) {
2546 g_warning ("Could not transform string to aspect ratio");
2547 gst_value_set_fraction (xvimagesink->par, 1, 1);
2549 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2550 gst_value_get_fraction_numerator (xvimagesink->par),
2551 gst_value_get_fraction_denominator (xvimagesink->par));
2553 case PROP_FORCE_ASPECT_RATIO:
2554 xvimagesink->keep_aspect = g_value_get_boolean (value);
2556 case PROP_HANDLE_EVENTS:
2557 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2558 g_value_get_boolean (value));
2559 gst_xvimagesink_manage_event_thread (xvimagesink);
2562 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2564 case PROP_HANDLE_EXPOSE:
2565 xvimagesink->handle_expose = g_value_get_boolean (value);
2566 gst_xvimagesink_manage_event_thread (xvimagesink);
2568 case PROP_DOUBLE_BUFFER:
2569 xvimagesink->double_buffer = g_value_get_boolean (value);
2571 case PROP_AUTOPAINT_COLORKEY:
2572 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2575 xvimagesink->colorkey = g_value_get_int (value);
2577 case PROP_DRAW_BORDERS:
2578 xvimagesink->draw_borders = g_value_get_boolean (value);
2581 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2587 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2588 GValue * value, GParamSpec * pspec)
2590 GstXvImageSink *xvimagesink;
2592 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2594 xvimagesink = GST_XVIMAGESINK (object);
2598 g_value_set_int (value, xvimagesink->hue);
2601 g_value_set_int (value, xvimagesink->contrast);
2603 case PROP_BRIGHTNESS:
2604 g_value_set_int (value, xvimagesink->brightness);
2606 case PROP_SATURATION:
2607 g_value_set_int (value, xvimagesink->saturation);
2610 g_value_set_string (value, xvimagesink->display_name);
2612 case PROP_SYNCHRONOUS:
2613 g_value_set_boolean (value, xvimagesink->synchronous);
2615 case PROP_PIXEL_ASPECT_RATIO:
2616 if (xvimagesink->par)
2617 g_value_transform (xvimagesink->par, value);
2619 case PROP_FORCE_ASPECT_RATIO:
2620 g_value_set_boolean (value, xvimagesink->keep_aspect);
2622 case PROP_HANDLE_EVENTS:
2623 g_value_set_boolean (value, xvimagesink->handle_events);
2627 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2629 g_value_set_string (value, adaptor_no_s);
2630 g_free (adaptor_no_s);
2633 case PROP_DEVICE_NAME:
2634 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2635 g_value_set_string (value,
2636 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2638 g_value_set_string (value, NULL);
2641 case PROP_HANDLE_EXPOSE:
2642 g_value_set_boolean (value, xvimagesink->handle_expose);
2644 case PROP_DOUBLE_BUFFER:
2645 g_value_set_boolean (value, xvimagesink->double_buffer);
2647 case PROP_AUTOPAINT_COLORKEY:
2648 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2651 g_value_set_int (value, xvimagesink->colorkey);
2653 case PROP_DRAW_BORDERS:
2654 g_value_set_boolean (value, xvimagesink->draw_borders);
2656 case PROP_WINDOW_WIDTH:
2657 if (xvimagesink->xwindow)
2658 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2660 g_value_set_uint64 (value, 0);
2662 case PROP_WINDOW_HEIGHT:
2663 if (xvimagesink->xwindow)
2664 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2666 g_value_set_uint64 (value, 0);
2669 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2675 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2679 GST_OBJECT_LOCK (xvimagesink);
2680 xvimagesink->running = FALSE;
2681 /* grab thread and mark it as NULL */
2682 thread = xvimagesink->event_thread;
2683 xvimagesink->event_thread = NULL;
2684 GST_OBJECT_UNLOCK (xvimagesink);
2686 /* Wait for our event thread to finish before we clean up our stuff. */
2688 g_thread_join (thread);
2690 if (xvimagesink->cur_image) {
2691 gst_buffer_unref (xvimagesink->cur_image);
2692 xvimagesink->cur_image = NULL;
2695 g_mutex_lock (xvimagesink->flow_lock);
2697 if (xvimagesink->pool) {
2698 gst_object_unref (xvimagesink->pool);
2699 xvimagesink->pool = NULL;
2702 if (xvimagesink->xwindow) {
2703 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2704 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2705 xvimagesink->xwindow = NULL;
2707 g_mutex_unlock (xvimagesink->flow_lock);
2709 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2710 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2711 xvimagesink->have_render_rect = FALSE;
2713 gst_xvimagesink_xcontext_clear (xvimagesink);
2716 /* Finalize is called only once, dispose can be called multiple times.
2717 * We use mutexes and don't reset stuff to NULL here so let's register
2720 gst_xvimagesink_finalize (GObject * object)
2722 GstXvImageSink *xvimagesink;
2724 xvimagesink = GST_XVIMAGESINK (object);
2726 gst_xvimagesink_reset (xvimagesink);
2728 if (xvimagesink->display_name) {
2729 g_free (xvimagesink->display_name);
2730 xvimagesink->display_name = NULL;
2733 if (xvimagesink->par) {
2734 g_free (xvimagesink->par);
2735 xvimagesink->par = NULL;
2737 if (xvimagesink->x_lock) {
2738 g_mutex_free (xvimagesink->x_lock);
2739 xvimagesink->x_lock = NULL;
2741 if (xvimagesink->flow_lock) {
2742 g_mutex_free (xvimagesink->flow_lock);
2743 xvimagesink->flow_lock = NULL;
2746 g_free (xvimagesink->media_title);
2748 G_OBJECT_CLASS (parent_class)->finalize (object);
2752 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2754 xvimagesink->display_name = NULL;
2755 xvimagesink->adaptor_no = 0;
2756 xvimagesink->xcontext = NULL;
2757 xvimagesink->xwindow = NULL;
2758 xvimagesink->cur_image = NULL;
2760 xvimagesink->hue = xvimagesink->saturation = 0;
2761 xvimagesink->contrast = xvimagesink->brightness = 0;
2762 xvimagesink->cb_changed = FALSE;
2764 xvimagesink->fps_n = 0;
2765 xvimagesink->fps_d = 0;
2766 xvimagesink->video_width = 0;
2767 xvimagesink->video_height = 0;
2769 xvimagesink->x_lock = g_mutex_new ();
2770 xvimagesink->flow_lock = g_mutex_new ();
2772 xvimagesink->pool = NULL;
2774 xvimagesink->synchronous = FALSE;
2775 xvimagesink->double_buffer = TRUE;
2776 xvimagesink->running = FALSE;
2777 xvimagesink->keep_aspect = FALSE;
2778 xvimagesink->handle_events = TRUE;
2779 xvimagesink->par = NULL;
2780 xvimagesink->handle_expose = TRUE;
2781 xvimagesink->autopaint_colorkey = TRUE;
2783 /* on 16bit displays this becomes r,g,b = 1,2,3
2784 * on 24bit displays this becomes r,g,b = 8,8,16
2785 * as a port atom value
2787 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2788 xvimagesink->draw_borders = TRUE;
2792 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2794 GObjectClass *gobject_class;
2795 GstElementClass *gstelement_class;
2796 GstBaseSinkClass *gstbasesink_class;
2797 GstVideoSinkClass *videosink_class;
2799 gobject_class = (GObjectClass *) klass;
2800 gstelement_class = (GstElementClass *) klass;
2801 gstbasesink_class = (GstBaseSinkClass *) klass;
2802 videosink_class = (GstVideoSinkClass *) klass;
2804 parent_class = g_type_class_peek_parent (klass);
2806 gobject_class->set_property = gst_xvimagesink_set_property;
2807 gobject_class->get_property = gst_xvimagesink_get_property;
2809 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2810 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2811 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2812 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2813 g_param_spec_int ("brightness", "Brightness",
2814 "The brightness of the video", -1000, 1000, 0,
2815 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2816 g_object_class_install_property (gobject_class, PROP_HUE,
2817 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2818 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2819 g_object_class_install_property (gobject_class, PROP_SATURATION,
2820 g_param_spec_int ("saturation", "Saturation",
2821 "The saturation of the video", -1000, 1000, 0,
2822 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2823 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2824 g_param_spec_string ("display", "Display", "X Display name", NULL,
2825 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2826 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2827 g_param_spec_boolean ("synchronous", "Synchronous",
2828 "When enabled, runs the X display in synchronous mode. "
2829 "(unrelated to A/V sync, used only for debugging)", FALSE,
2830 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2831 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2832 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2833 "The pixel aspect ratio of the device", "1/1",
2834 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2835 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2836 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2837 "When enabled, scaling will respect original aspect ratio", FALSE,
2838 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2839 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2840 g_param_spec_boolean ("handle-events", "Handle XEvents",
2841 "When enabled, XEvents will be selected and handled", TRUE,
2842 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2843 g_object_class_install_property (gobject_class, PROP_DEVICE,
2844 g_param_spec_string ("device", "Adaptor number",
2845 "The number of the video adaptor", "0",
2846 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2847 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2848 g_param_spec_string ("device-name", "Adaptor name",
2849 "The name of the video adaptor", NULL,
2850 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2852 * GstXvImageSink:handle-expose
2854 * When enabled, the current frame will always be drawn in response to X
2859 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2860 g_param_spec_boolean ("handle-expose", "Handle expose",
2862 "the current frame will always be drawn in response to X Expose "
2863 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2866 * GstXvImageSink:double-buffer
2868 * Whether to double-buffer the output.
2872 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2873 g_param_spec_boolean ("double-buffer", "Double-buffer",
2874 "Whether to double-buffer the output", TRUE,
2875 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2877 * GstXvImageSink:autopaint-colorkey
2879 * Whether to autofill overlay with colorkey
2883 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2884 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2885 "Whether to autofill overlay with colorkey", TRUE,
2886 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2888 * GstXvImageSink:colorkey
2890 * Color to use for the overlay mask.
2894 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2895 g_param_spec_int ("colorkey", "Colorkey",
2896 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2897 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2900 * GstXvImageSink:draw-borders
2902 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2903 * unused parts of the video area.
2907 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2908 g_param_spec_boolean ("draw-borders", "Colorkey",
2909 "Draw black borders to fill unused area in force-aspect-ratio mode",
2910 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2913 * GstXvImageSink:window-width
2915 * Actual width of the video window.
2919 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2920 g_param_spec_uint64 ("window-width", "window-width",
2921 "Width of the window", 0, G_MAXUINT64, 0,
2922 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2925 * GstXvImageSink:window-height
2927 * Actual height of the video window.
2931 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2932 g_param_spec_uint64 ("window-height", "window-height",
2933 "Height of the window", 0, G_MAXUINT64, 0,
2934 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2936 gobject_class->finalize = gst_xvimagesink_finalize;
2938 gst_element_class_set_static_metadata (gstelement_class,
2939 "Video sink", "Sink/Video",
2940 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2942 gst_element_class_add_pad_template (gstelement_class,
2943 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2945 gstelement_class->change_state =
2946 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2948 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2949 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2950 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2951 gstbasesink_class->propose_allocation =
2952 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2953 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2955 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);