2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theorically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/videooverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/gstmetavideo.h>
125 #include "xvimagesink.h"
127 /* Debugging category */
128 #include <gst/gstinfo.h>
130 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
131 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
132 #define GST_CAT_DEFAULT gst_debug_xvimagesink
137 unsigned long functions;
138 unsigned long decorations;
140 unsigned long status;
142 MotifWmHints, MwmHints;
144 #define MWM_HINTS_DECORATIONS (1L << 1)
146 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
147 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
149 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
151 /* Default template - initiated with class struct to allow gst-register to work
153 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
154 GST_STATIC_PAD_TEMPLATE ("sink",
157 GST_STATIC_CAPS ("video/x-raw, "
158 "framerate = (fraction) [ 0, MAX ], "
159 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
171 PROP_PIXEL_ASPECT_RATIO,
172 PROP_FORCE_ASPECT_RATIO,
178 PROP_AUTOPAINT_COLORKEY,
185 /* ============================================================= */
189 /* ============================================================= */
191 /* =========================================== */
193 /* Object typing & Creation */
195 /* =========================================== */
196 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
197 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayIface * iface);
198 static void gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface);
200 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
202 #define gst_xvimagesink_parent_class parent_class
203 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
204 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
205 gst_xvimagesink_navigation_init);
206 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
207 gst_xvimagesink_video_overlay_init);
208 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
209 gst_xvimagesink_colorbalance_init);
210 G_IMPLEMENT_INTERFACE (GST_TYPE_PROPERTY_PROBE,
211 gst_xvimagesink_property_probe_interface_init));
214 /* ============================================================= */
216 /* Private Methods */
218 /* ============================================================= */
221 /* We are called with the x_lock taken */
223 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
224 GstXWindow * xwindow, GstVideoRectangle rect)
228 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
229 g_return_if_fail (xwindow != NULL);
231 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
232 xvimagesink->xcontext->black);
235 if (rect.x > xvimagesink->render_rect.x) {
236 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
237 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
238 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
242 t1 = rect.x + rect.w;
243 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
245 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
246 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
250 if (rect.y > xvimagesink->render_rect.y) {
251 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
252 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
253 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
257 t1 = rect.y + rect.h;
258 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
260 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
261 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
265 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
266 * if no window was available */
268 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
270 GstMetaXvImage *meta;
271 GstMetaVideoCrop *crop;
272 GstVideoRectangle result;
273 gboolean draw_border = FALSE;
274 GstVideoRectangle src, dst;
276 /* We take the flow_lock. If expose is in there we don't want to run
277 concurrently from the data flow thread */
278 g_mutex_lock (xvimagesink->flow_lock);
280 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
281 g_mutex_unlock (xvimagesink->flow_lock);
285 /* Draw borders when displaying the first frame. After this
286 draw borders only on expose event or after a size change. */
287 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
291 /* Store a reference to the last image we put, lose the previous one */
292 if (xvimage && xvimagesink->cur_image != xvimage) {
293 if (xvimagesink->cur_image) {
294 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
295 gst_buffer_unref (xvimagesink->cur_image);
297 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
298 xvimagesink->cur_image = gst_buffer_ref (xvimage);
301 /* Expose sends a NULL image, we take the latest frame */
303 if (xvimagesink->cur_image) {
305 xvimage = xvimagesink->cur_image;
307 g_mutex_unlock (xvimagesink->flow_lock);
312 meta = gst_buffer_get_meta_xvimage (xvimage);
314 crop = gst_buffer_get_meta_video_crop (xvimage);
317 src.x = crop->x + meta->x;
318 src.y = crop->y + meta->y;
320 src.h = crop->height;
325 src.h = meta->height;
328 if (xvimagesink->keep_aspect) {
329 dst.w = xvimagesink->render_rect.w;
330 dst.h = xvimagesink->render_rect.h;
332 gst_video_sink_center_rect (src, dst, &result, TRUE);
333 result.x += xvimagesink->render_rect.x;
334 result.y += xvimagesink->render_rect.y;
336 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
339 g_mutex_lock (xvimagesink->x_lock);
341 if (draw_border && xvimagesink->draw_borders) {
342 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
344 xvimagesink->redraw_border = FALSE;
347 if (xvimagesink->xcontext->use_xshm) {
348 GST_LOG_OBJECT (xvimagesink,
349 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
350 GST_PTR_FORMAT, meta->width, meta->height,
351 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
353 XvShmPutImage (xvimagesink->xcontext->disp,
354 xvimagesink->xcontext->xv_port_id,
355 xvimagesink->xwindow->win,
356 xvimagesink->xwindow->gc, meta->xvimage,
357 src.x, src.y, src.w, src.h,
358 result.x, result.y, result.w, result.h, FALSE);
360 #endif /* HAVE_XSHM */
362 XvPutImage (xvimagesink->xcontext->disp,
363 xvimagesink->xcontext->xv_port_id,
364 xvimagesink->xwindow->win,
365 xvimagesink->xwindow->gc, meta->xvimage,
366 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
369 XSync (xvimagesink->xcontext->disp, FALSE);
371 g_mutex_unlock (xvimagesink->x_lock);
373 g_mutex_unlock (xvimagesink->flow_lock);
379 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
382 Atom hints_atom = None;
385 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
386 g_return_val_if_fail (window != NULL, FALSE);
388 g_mutex_lock (xvimagesink->x_lock);
390 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
392 if (hints_atom == None) {
393 g_mutex_unlock (xvimagesink->x_lock);
397 hints = g_malloc0 (sizeof (MotifWmHints));
399 hints->flags |= MWM_HINTS_DECORATIONS;
400 hints->decorations = 1 << 0;
402 XChangeProperty (xvimagesink->xcontext->disp, window->win,
403 hints_atom, hints_atom, 32, PropModeReplace,
404 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
406 XSync (xvimagesink->xcontext->disp, FALSE);
408 g_mutex_unlock (xvimagesink->x_lock);
416 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
417 GstXWindow * xwindow, const gchar * media_title)
420 g_free (xvimagesink->media_title);
421 xvimagesink->media_title = g_strdup (media_title);
424 /* we have a window */
425 if (xwindow->internal) {
426 XTextProperty xproperty;
427 const gchar *app_name;
428 const gchar *title = NULL;
429 gchar *title_mem = NULL;
431 /* set application name as a title */
432 app_name = g_get_application_name ();
434 if (app_name && xvimagesink->media_title) {
435 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
437 } else if (app_name) {
439 } else if (xvimagesink->media_title) {
440 title = xvimagesink->media_title;
444 if ((XStringListToTextProperty (((char **) &title), 1,
446 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
447 XFree (xproperty.value);
456 /* This function handles a GstXWindow creation
457 * The width and height are the actual pixel size on the display */
459 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
460 gint width, gint height)
462 GstXWindow *xwindow = NULL;
465 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
467 xwindow = g_new0 (GstXWindow, 1);
469 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
470 xvimagesink->render_rect.w = width;
471 xvimagesink->render_rect.h = height;
473 xwindow->width = width;
474 xwindow->height = height;
475 xwindow->internal = TRUE;
477 g_mutex_lock (xvimagesink->x_lock);
479 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
480 xvimagesink->xcontext->root,
481 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
483 /* We have to do that to prevent X from redrawing the background on
484 * ConfigureNotify. This takes away flickering of video when resizing. */
485 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
487 /* set application name as a title */
488 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
490 if (xvimagesink->handle_events) {
493 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
494 StructureNotifyMask | PointerMotionMask | KeyPressMask |
495 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
497 /* Tell the window manager we'd like delete client messages instead of
499 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
500 "WM_DELETE_WINDOW", True);
501 if (wm_delete != None) {
502 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
507 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
508 xwindow->win, 0, &values);
510 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
512 XSync (xvimagesink->xcontext->disp, FALSE);
514 g_mutex_unlock (xvimagesink->x_lock);
516 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
518 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
524 /* This function destroys a GstXWindow */
526 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
527 GstXWindow * xwindow)
529 g_return_if_fail (xwindow != NULL);
530 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
532 g_mutex_lock (xvimagesink->x_lock);
534 /* If we did not create that window we just free the GC and let it live */
535 if (xwindow->internal)
536 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
538 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
540 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
542 XSync (xvimagesink->xcontext->disp, FALSE);
544 g_mutex_unlock (xvimagesink->x_lock);
550 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
552 XWindowAttributes attr;
554 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
556 /* Update the window geometry */
557 g_mutex_lock (xvimagesink->x_lock);
558 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
559 g_mutex_unlock (xvimagesink->x_lock);
563 XGetWindowAttributes (xvimagesink->xcontext->disp,
564 xvimagesink->xwindow->win, &attr);
566 xvimagesink->xwindow->width = attr.width;
567 xvimagesink->xwindow->height = attr.height;
569 if (!xvimagesink->have_render_rect) {
570 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
571 xvimagesink->render_rect.w = attr.width;
572 xvimagesink->render_rect.h = attr.height;
575 g_mutex_unlock (xvimagesink->x_lock);
579 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
580 GstXWindow * xwindow)
582 g_return_if_fail (xwindow != NULL);
583 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
585 g_mutex_lock (xvimagesink->x_lock);
587 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
590 XSync (xvimagesink->xcontext->disp, FALSE);
592 g_mutex_unlock (xvimagesink->x_lock);
595 /* This function commits our internal colorbalance settings to our grabbed Xv
596 port. If the xcontext is not initialized yet it simply returns */
598 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
600 GList *channels = NULL;
602 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
604 /* If we haven't initialized the X context we can't update anything */
605 if (xvimagesink->xcontext == NULL)
608 /* Don't set the attributes if they haven't been changed, to avoid
609 * rounding errors changing the values */
610 if (!xvimagesink->cb_changed)
613 /* For each channel of the colorbalance we calculate the correct value
614 doing range conversion and then set the Xv port attribute to match our
616 channels = xvimagesink->xcontext->channels_list;
619 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
620 GstColorBalanceChannel *channel = NULL;
623 gdouble convert_coef;
625 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
626 g_object_ref (channel);
628 /* Our range conversion coef */
629 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
631 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
632 value = xvimagesink->hue;
633 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
634 value = xvimagesink->saturation;
635 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
636 value = xvimagesink->contrast;
637 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
638 value = xvimagesink->brightness;
640 g_warning ("got an unknown channel %s", channel->label);
641 g_object_unref (channel);
645 /* Committing to Xv port */
646 g_mutex_lock (xvimagesink->x_lock);
648 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
649 if (prop_atom != None) {
652 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
653 XvSetPortAttribute (xvimagesink->xcontext->disp,
654 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
656 g_mutex_unlock (xvimagesink->x_lock);
658 g_object_unref (channel);
660 channels = g_list_next (channels);
664 /* This function handles XEvents that might be in the queue. It generates
665 GstEvent that will be sent upstream in the pipeline to handle interactivity
666 and navigation. It will also listen for configure events on the window to
667 trigger caps renegotiation so on the fly software scaling can work. */
669 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
672 guint pointer_x = 0, pointer_y = 0;
673 gboolean pointer_moved = FALSE;
674 gboolean exposed = FALSE, configured = FALSE;
676 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
678 /* Handle Interaction, produces navigation events */
680 /* We get all pointer motion events, only the last position is
682 g_mutex_lock (xvimagesink->flow_lock);
683 g_mutex_lock (xvimagesink->x_lock);
684 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
685 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
686 g_mutex_unlock (xvimagesink->x_lock);
687 g_mutex_unlock (xvimagesink->flow_lock);
691 pointer_x = e.xmotion.x;
692 pointer_y = e.xmotion.y;
693 pointer_moved = TRUE;
698 g_mutex_lock (xvimagesink->flow_lock);
699 g_mutex_lock (xvimagesink->x_lock);
703 g_mutex_unlock (xvimagesink->x_lock);
704 g_mutex_unlock (xvimagesink->flow_lock);
706 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
707 pointer_x, pointer_y);
708 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
709 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
711 g_mutex_lock (xvimagesink->flow_lock);
712 g_mutex_lock (xvimagesink->x_lock);
715 /* We get all events on our window to throw them upstream */
716 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
717 xvimagesink->xwindow->win,
718 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
722 /* We lock only for the X function call */
723 g_mutex_unlock (xvimagesink->x_lock);
724 g_mutex_unlock (xvimagesink->flow_lock);
728 /* Mouse button pressed over our window. We send upstream
729 events for interactivity/navigation */
730 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
731 e.xbutton.button, e.xbutton.x, e.xbutton.y);
732 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
733 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
736 /* Mouse button released over our window. We send upstream
737 events for interactivity/navigation */
738 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
739 e.xbutton.button, e.xbutton.x, e.xbutton.y);
740 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
741 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
745 /* Key pressed/released over our window. We send upstream
746 events for interactivity/navigation */
747 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
748 e.xkey.keycode, e.xkey.x, e.xkey.y);
749 g_mutex_lock (xvimagesink->x_lock);
750 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
752 g_mutex_unlock (xvimagesink->x_lock);
753 if (keysym != NoSymbol) {
754 char *key_str = NULL;
756 g_mutex_lock (xvimagesink->x_lock);
757 key_str = XKeysymToString (keysym);
758 g_mutex_unlock (xvimagesink->x_lock);
759 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
760 e.type == KeyPress ? "key-press" : "key-release", key_str);
762 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
763 e.type == KeyPress ? "key-press" : "key-release", "unknown");
767 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
770 g_mutex_lock (xvimagesink->flow_lock);
771 g_mutex_lock (xvimagesink->x_lock);
775 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
776 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
781 case ConfigureNotify:
782 g_mutex_unlock (xvimagesink->x_lock);
783 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
784 g_mutex_lock (xvimagesink->x_lock);
792 if (xvimagesink->handle_expose && (exposed || configured)) {
793 g_mutex_unlock (xvimagesink->x_lock);
794 g_mutex_unlock (xvimagesink->flow_lock);
796 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
798 g_mutex_lock (xvimagesink->flow_lock);
799 g_mutex_lock (xvimagesink->x_lock);
802 /* Handle Display events */
803 while (XPending (xvimagesink->xcontext->disp)) {
804 XNextEvent (xvimagesink->xcontext->disp, &e);
810 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
811 "WM_DELETE_WINDOW", True);
812 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
813 /* Handle window deletion by posting an error on the bus */
814 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
815 ("Output window was closed"), (NULL));
817 g_mutex_unlock (xvimagesink->x_lock);
818 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
819 xvimagesink->xwindow = NULL;
820 g_mutex_lock (xvimagesink->x_lock);
829 g_mutex_unlock (xvimagesink->x_lock);
830 g_mutex_unlock (xvimagesink->flow_lock);
834 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
835 XvAdaptorInfo * adaptors, int adaptor_no)
840 /* Do we support XvImageMask ? */
841 if (!(adaptors[adaptor_no].type & XvImageMask)) {
842 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
843 adaptors[adaptor_no].name);
847 /* We found such an adaptor, looking for an available port */
848 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
849 /* We try to grab the port */
850 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
851 if (Success == res) {
852 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
853 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
854 adaptors[adaptor_no].num_ports);
856 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
857 adaptors[adaptor_no].name, res);
862 /* This function generates a caps with all supported format by the first
863 Xv grabable port we find. We store each one of the supported formats in a
864 format list and append the format to a newly created caps that we return
865 If this function does not return NULL because of an error, it also grabs
866 the port via XvGrabPort */
868 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
869 GstXContext * xcontext)
872 XvAdaptorInfo *adaptors;
874 XvImageFormatValues *formats = NULL;
876 XvEncodingInfo *encodings = NULL;
877 gulong max_w = G_MAXINT, max_h = G_MAXINT;
878 GstCaps *caps = NULL;
879 GstCaps *rgb_caps = NULL;
881 g_return_val_if_fail (xcontext != NULL, NULL);
883 /* First let's check that XVideo extension is available */
884 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
885 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
886 ("Could not initialise Xv output"),
887 ("XVideo extension is not available"));
891 /* Then we get adaptors list */
892 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
893 &xcontext->nb_adaptors, &adaptors)) {
894 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
895 ("Could not initialise Xv output"),
896 ("Failed getting XV adaptors list"));
900 xcontext->xv_port_id = 0;
902 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
905 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
907 /* Now fill up our adaptor name array */
908 for (i = 0; i < xcontext->nb_adaptors; i++) {
909 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
912 if (xvimagesink->adaptor_no >= 0 &&
913 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
914 /* Find xv port from user defined adaptor */
915 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
916 xvimagesink->adaptor_no);
919 if (!xcontext->xv_port_id) {
920 /* Now search for an adaptor that supports XvImageMask */
921 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
922 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
923 xvimagesink->adaptor_no = i;
927 XvFreeAdaptorInfo (adaptors);
929 if (!xcontext->xv_port_id) {
930 xvimagesink->adaptor_no = -1;
931 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
932 ("Could not initialise Xv output"), ("No port available"));
936 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
939 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
940 xcontext->xv_port_id, &count);
941 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
942 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
943 static const char colorkey[] = "XV_COLORKEY";
945 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
947 xvimagesink->have_autopaint_colorkey = FALSE;
948 xvimagesink->have_double_buffer = FALSE;
949 xvimagesink->have_colorkey = FALSE;
951 for (i = 0; ((i < count) && todo); i++)
952 if (!strcmp (attr[i].name, autopaint)) {
953 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
955 /* turn on autopaint colorkey */
956 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
957 (xvimagesink->autopaint_colorkey ? 1 : 0));
959 xvimagesink->have_autopaint_colorkey = TRUE;
960 } else if (!strcmp (attr[i].name, dbl_buffer)) {
961 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
963 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
964 (xvimagesink->double_buffer ? 1 : 0));
966 xvimagesink->have_double_buffer = TRUE;
967 } else if (!strcmp (attr[i].name, colorkey)) {
968 /* Set the colorkey, default is something that is dark but hopefully
969 * won't randomly appear on the screen elsewhere (ie not black or greys)
970 * can be overridden by setting "colorkey" property
972 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
974 gboolean set_attr = TRUE;
977 /* set a colorkey in the right format RGB565/RGB888
978 * We only handle these 2 cases, because they're the only types of
979 * devices we've encountered. If we don't recognise it, leave it alone
981 cr = (xvimagesink->colorkey >> 16);
982 cg = (xvimagesink->colorkey >> 8) & 0xFF;
983 cb = (xvimagesink->colorkey) & 0xFF;
984 switch (xcontext->depth) {
985 case 16: /* RGB 565 */
989 ckey = (cr << 11) | (cg << 5) | cb;
992 case 32: /* RGB 888 / ARGB 8888 */
993 ckey = (cr << 16) | (cg << 8) | cb;
996 GST_DEBUG_OBJECT (xvimagesink,
997 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1004 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1005 (guint32) attr[i].max_value);
1006 GST_LOG_OBJECT (xvimagesink,
1007 "Setting color key for display depth %d to 0x%x",
1008 xcontext->depth, ckey);
1010 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1014 xvimagesink->have_colorkey = TRUE;
1020 /* Get the list of encodings supported by the adapter and look for the
1021 * XV_IMAGE encoding so we can determine the maximum width and height
1023 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1026 for (i = 0; i < nb_encodings; i++) {
1027 GST_LOG_OBJECT (xvimagesink,
1028 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1029 i, encodings[i].name, encodings[i].width, encodings[i].height,
1030 encodings[i].rate.numerator, encodings[i].rate.denominator);
1031 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1032 max_w = encodings[i].width;
1033 max_h = encodings[i].height;
1037 XvFreeEncodingInfo (encodings);
1039 /* We get all image formats supported by our port */
1040 formats = XvListImageFormats (xcontext->disp,
1041 xcontext->xv_port_id, &nb_formats);
1042 caps = gst_caps_new_empty ();
1043 for (i = 0; i < nb_formats; i++) {
1044 GstCaps *format_caps = NULL;
1045 gboolean is_rgb_format = FALSE;
1046 GstVideoFormat vformat;
1048 /* We set the image format of the xcontext to an existing one. This
1049 is just some valid image format for making our xshm calls check before
1050 caps negotiation really happens. */
1051 xcontext->im_format = formats[i].id;
1053 switch (formats[i].type) {
1056 XvImageFormatValues *fmt = &(formats[i]);
1060 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1062 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1063 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1064 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1067 format_caps = gst_caps_new_simple ("video/x-raw",
1068 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1069 "width", GST_TYPE_INT_RANGE, 1, max_w,
1070 "height", GST_TYPE_INT_RANGE, 1, max_h,
1071 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1073 is_rgb_format = TRUE;
1078 vformat = gst_video_format_from_fourcc (formats[i].id);
1079 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1082 format_caps = gst_caps_new_simple ("video/x-raw",
1083 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1084 "width", GST_TYPE_INT_RANGE, 1, max_w,
1085 "height", GST_TYPE_INT_RANGE, 1, max_h,
1086 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1090 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1091 g_assert_not_reached ();
1096 GstXvImageFormat *format = NULL;
1098 format = g_new0 (GstXvImageFormat, 1);
1100 format->format = formats[i].id;
1101 format->vformat = vformat;
1102 format->caps = gst_caps_copy (format_caps);
1103 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1106 if (is_rgb_format) {
1107 if (rgb_caps == NULL)
1108 rgb_caps = format_caps;
1110 gst_caps_append (rgb_caps, format_caps);
1112 gst_caps_append (caps, format_caps);
1116 /* Collected all caps into either the caps or rgb_caps structures.
1117 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1119 gst_caps_append (caps, rgb_caps);
1124 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1126 if (gst_caps_is_empty (caps)) {
1127 gst_caps_unref (caps);
1128 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1129 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1130 ("No supported format found"));
1138 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1140 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1142 GST_OBJECT_LOCK (xvimagesink);
1143 while (xvimagesink->running) {
1144 GST_OBJECT_UNLOCK (xvimagesink);
1146 if (xvimagesink->xwindow) {
1147 gst_xvimagesink_handle_xevents (xvimagesink);
1149 /* FIXME: do we want to align this with the framerate or anything else? */
1150 g_usleep (G_USEC_PER_SEC / 20);
1152 GST_OBJECT_LOCK (xvimagesink);
1154 GST_OBJECT_UNLOCK (xvimagesink);
1160 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1162 GThread *thread = NULL;
1164 /* don't start the thread too early */
1165 if (xvimagesink->xcontext == NULL) {
1169 GST_OBJECT_LOCK (xvimagesink);
1170 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1171 if (!xvimagesink->event_thread) {
1172 /* Setup our event listening thread */
1173 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1174 xvimagesink->handle_expose, xvimagesink->handle_events);
1175 xvimagesink->running = TRUE;
1176 xvimagesink->event_thread = g_thread_create (
1177 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1180 if (xvimagesink->event_thread) {
1181 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1182 xvimagesink->handle_expose, xvimagesink->handle_events);
1183 xvimagesink->running = FALSE;
1184 /* grab thread and mark it as NULL */
1185 thread = xvimagesink->event_thread;
1186 xvimagesink->event_thread = NULL;
1189 GST_OBJECT_UNLOCK (xvimagesink);
1191 /* Wait for our event thread to finish */
1193 g_thread_join (thread);
1198 /* This function calculates the pixel aspect ratio based on the properties
1199 * in the xcontext structure and stores it there. */
1201 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1203 static const gint par[][2] = {
1204 {1, 1}, /* regular screen */
1205 {16, 15}, /* PAL TV */
1206 {11, 10}, /* 525 line Rec.601 video */
1207 {54, 59}, /* 625 line Rec.601 video */
1208 {64, 45}, /* 1280x1024 on 16:9 display */
1209 {5, 3}, /* 1280x1024 on 4:3 display */
1210 {4, 3} /* 800x600 on 16:9 display */
1217 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1219 /* first calculate the "real" ratio based on the X values;
1220 * which is the "physical" w/h divided by the w/h in pixels of the display */
1221 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1222 / (xcontext->heightmm * xcontext->width);
1224 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1226 if (xcontext->width == 720 && xcontext->height == 576) {
1227 ratio = 4.0 * 576 / (3.0 * 720);
1229 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1231 /* now find the one from par[][2] with the lowest delta to the real one */
1235 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1236 gdouble this_delta = DELTA (i);
1238 if (this_delta < delta) {
1244 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1245 par[index][0], par[index][1]);
1247 g_free (xcontext->par);
1248 xcontext->par = g_new0 (GValue, 1);
1249 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1250 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1251 GST_DEBUG ("set xcontext PAR to %d/%d",
1252 gst_value_get_fraction_numerator (xcontext->par),
1253 gst_value_get_fraction_denominator (xcontext->par));
1256 /* This function gets the X Display and global info about it. Everything is
1257 stored in our object and will be cleaned when the object is disposed. Note
1258 here that caps for supported format are generated without any window or
1260 static GstXContext *
1261 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1263 GstXContext *xcontext = NULL;
1264 XPixmapFormatValues *px_formats = NULL;
1265 gint nb_formats = 0, i, j, N_attr;
1266 XvAttribute *xv_attr;
1268 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1269 "XV_BRIGHTNESS", "XV_CONTRAST"
1272 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1274 xcontext = g_new0 (GstXContext, 1);
1275 xcontext->im_format = 0;
1277 g_mutex_lock (xvimagesink->x_lock);
1279 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1281 if (!xcontext->disp) {
1282 g_mutex_unlock (xvimagesink->x_lock);
1284 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1285 ("Could not initialise Xv output"), ("Could not open display"));
1289 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1290 xcontext->screen_num = DefaultScreen (xcontext->disp);
1291 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1292 xcontext->root = DefaultRootWindow (xcontext->disp);
1293 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1294 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1295 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1297 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1298 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1299 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1300 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1302 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1303 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1305 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1306 /* We get supported pixmap formats at supported depth */
1307 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1310 XCloseDisplay (xcontext->disp);
1311 g_mutex_unlock (xvimagesink->x_lock);
1312 g_free (xcontext->par);
1314 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1315 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1319 /* We get bpp value corresponding to our running depth */
1320 for (i = 0; i < nb_formats; i++) {
1321 if (px_formats[i].depth == xcontext->depth)
1322 xcontext->bpp = px_formats[i].bits_per_pixel;
1327 xcontext->endianness =
1328 (ImageByteOrder (xcontext->disp) ==
1329 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1331 /* our caps system handles 24/32bpp RGB as big-endian. */
1332 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1333 xcontext->endianness == G_LITTLE_ENDIAN) {
1334 xcontext->endianness = G_BIG_ENDIAN;
1335 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1336 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1337 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1338 if (xcontext->bpp == 24) {
1339 xcontext->visual->red_mask >>= 8;
1340 xcontext->visual->green_mask >>= 8;
1341 xcontext->visual->blue_mask >>= 8;
1345 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1347 /* Search for XShm extension support */
1349 if (XShmQueryExtension (xcontext->disp) &&
1350 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1351 xcontext->use_xshm = TRUE;
1352 GST_DEBUG ("xvimagesink is using XShm extension");
1354 #endif /* HAVE_XSHM */
1356 xcontext->use_xshm = FALSE;
1357 GST_DEBUG ("xvimagesink is not using XShm extension");
1360 if (!xcontext->caps) {
1361 XCloseDisplay (xcontext->disp);
1362 g_mutex_unlock (xvimagesink->x_lock);
1363 g_free (xcontext->par);
1365 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1369 xv_attr = XvQueryPortAttributes (xcontext->disp,
1370 xcontext->xv_port_id, &N_attr);
1373 /* Generate the channels list */
1374 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1375 XvAttribute *matching_attr = NULL;
1377 /* Retrieve the property atom if it exists. If it doesn't exist,
1378 * the attribute itself must not either, so we can skip */
1379 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1380 if (prop_atom == None)
1383 if (xv_attr != NULL) {
1384 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1385 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1386 matching_attr = xv_attr + j;
1389 if (matching_attr) {
1390 GstColorBalanceChannel *channel;
1392 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1393 channel->label = g_strdup (channels[i]);
1394 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1395 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1397 xcontext->channels_list = g_list_append (xcontext->channels_list,
1400 /* If the colorbalance settings have not been touched we get Xv values
1401 as defaults and update our internal variables */
1402 if (!xvimagesink->cb_changed) {
1405 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1407 /* Normalize val to [-1000, 1000] */
1408 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1409 (double) (channel->max_value - channel->min_value));
1411 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1412 xvimagesink->hue = val;
1413 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1414 xvimagesink->saturation = val;
1415 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1416 xvimagesink->brightness = val;
1417 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1418 xvimagesink->contrast = val;
1426 g_mutex_unlock (xvimagesink->x_lock);
1431 /* This function cleans the X context. Closing the Display, releasing the XV
1432 port and unrefing the caps for supported formats. */
1434 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1436 GList *formats_list, *channels_list;
1437 GstXContext *xcontext;
1440 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1442 GST_OBJECT_LOCK (xvimagesink);
1443 if (xvimagesink->xcontext == NULL) {
1444 GST_OBJECT_UNLOCK (xvimagesink);
1448 /* Take the XContext from the sink and clean it up */
1449 xcontext = xvimagesink->xcontext;
1450 xvimagesink->xcontext = NULL;
1452 GST_OBJECT_UNLOCK (xvimagesink);
1455 formats_list = xcontext->formats_list;
1457 while (formats_list) {
1458 GstXvImageFormat *format = formats_list->data;
1460 gst_caps_unref (format->caps);
1462 formats_list = g_list_next (formats_list);
1465 if (xcontext->formats_list)
1466 g_list_free (xcontext->formats_list);
1468 channels_list = xcontext->channels_list;
1470 while (channels_list) {
1471 GstColorBalanceChannel *channel = channels_list->data;
1473 g_object_unref (channel);
1474 channels_list = g_list_next (channels_list);
1477 if (xcontext->channels_list)
1478 g_list_free (xcontext->channels_list);
1480 gst_caps_unref (xcontext->caps);
1481 if (xcontext->last_caps)
1482 gst_caps_replace (&xcontext->last_caps, NULL);
1484 for (i = 0; i < xcontext->nb_adaptors; i++) {
1485 g_free (xcontext->adaptors[i]);
1488 g_free (xcontext->adaptors);
1490 g_free (xcontext->par);
1492 g_mutex_lock (xvimagesink->x_lock);
1494 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1496 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1498 XCloseDisplay (xcontext->disp);
1500 g_mutex_unlock (xvimagesink->x_lock);
1508 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1510 GstXvImageSink *xvimagesink;
1513 xvimagesink = GST_XVIMAGESINK (bsink);
1515 if (xvimagesink->xcontext) {
1517 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1518 GST_CAPS_INTERSECT_FIRST);
1520 return gst_caps_ref (xvimagesink->xcontext->caps);
1523 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1525 GstCaps *intersection;
1528 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1529 gst_caps_unref (caps);
1530 caps = intersection;
1536 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1538 GstXvImageSink *xvimagesink;
1539 GstStructure *structure;
1540 GstBufferPool *newpool, *oldpool;
1542 guint32 im_format = 0;
1543 gint video_par_n, video_par_d; /* video's PAR */
1544 gint display_par_n, display_par_d; /* display's PAR */
1548 xvimagesink = GST_XVIMAGESINK (bsink);
1550 GST_DEBUG_OBJECT (xvimagesink,
1551 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1552 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1554 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1555 goto incompatible_caps;
1557 if (!gst_video_info_from_caps (&info, caps))
1558 goto invalid_format;
1560 structure = gst_caps_get_structure (caps, 0);
1562 xvimagesink->fps_n = info.fps_n;
1563 xvimagesink->fps_d = info.fps_d;
1565 xvimagesink->video_width = info.width;
1566 xvimagesink->video_height = info.height;
1568 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1569 if (im_format == -1)
1570 goto invalid_format;
1574 /* get aspect ratio from caps if it's present, and
1575 * convert video width and height to a display width and height
1576 * using wd / hd = wv / hv * PARv / PARd */
1578 /* get video's PAR */
1579 video_par_n = info.par_n;
1580 video_par_d = info.par_d;
1582 /* get display's PAR */
1583 if (xvimagesink->par) {
1584 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1585 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1591 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1592 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1595 GST_DEBUG_OBJECT (xvimagesink,
1596 "video width/height: %dx%d, calculated display ratio: %d/%d",
1597 info.width, info.height, num, den);
1599 /* now find a width x height that respects this display ratio.
1600 * prefer those that have one of w/h the same as the incoming video
1601 * using wd / hd = num / den */
1603 /* start with same height, because of interlaced video */
1604 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1605 if (info.height % den == 0) {
1606 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1607 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1608 gst_util_uint64_scale_int (info.height, num, den);
1609 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1610 } else if (info.width % num == 0) {
1611 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1612 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1613 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1614 gst_util_uint64_scale_int (info.width, den, num);
1616 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1617 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1618 gst_util_uint64_scale_int (info.height, num, den);
1619 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1621 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1622 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1624 /* Notify application to set xwindow id now */
1625 g_mutex_lock (xvimagesink->flow_lock);
1626 if (!xvimagesink->xwindow) {
1627 g_mutex_unlock (xvimagesink->flow_lock);
1628 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1630 g_mutex_unlock (xvimagesink->flow_lock);
1633 /* Creating our window and our image with the display size in pixels */
1634 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1635 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1636 goto no_display_size;
1638 g_mutex_lock (xvimagesink->flow_lock);
1639 if (!xvimagesink->xwindow) {
1640 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1641 GST_VIDEO_SINK_WIDTH (xvimagesink),
1642 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1645 xvimagesink->info = info;
1647 /* After a resize, we want to redraw the borders in case the new frame size
1648 * doesn't cover the same area */
1649 xvimagesink->redraw_border = TRUE;
1651 /* create a new pool for the new configuration */
1652 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1654 structure = gst_buffer_pool_get_config (newpool);
1655 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1656 if (!gst_buffer_pool_set_config (newpool, structure))
1659 oldpool = xvimagesink->pool;
1660 xvimagesink->pool = newpool;
1661 g_mutex_unlock (xvimagesink->flow_lock);
1663 /* unref the old sink */
1665 /* we don't deactivate, some elements might still be using it, it will
1666 * be deactivated when the last ref is gone */
1667 gst_object_unref (oldpool);
1675 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1680 GST_DEBUG_OBJECT (xvimagesink,
1681 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1686 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1687 ("Error calculating the output display ratio of the video."));
1692 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1693 ("Error calculating the output display ratio of the video."));
1698 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1699 g_mutex_unlock (xvimagesink->flow_lock);
1704 static GstStateChangeReturn
1705 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1707 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1708 GstXvImageSink *xvimagesink;
1709 GstXContext *xcontext = NULL;
1711 xvimagesink = GST_XVIMAGESINK (element);
1713 switch (transition) {
1714 case GST_STATE_CHANGE_NULL_TO_READY:
1715 /* Initializing the XContext */
1716 if (xvimagesink->xcontext == NULL) {
1717 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1718 if (xcontext == NULL) {
1719 ret = GST_STATE_CHANGE_FAILURE;
1722 GST_OBJECT_LOCK (xvimagesink);
1724 xvimagesink->xcontext = xcontext;
1725 GST_OBJECT_UNLOCK (xvimagesink);
1728 /* update object's par with calculated one if not set yet */
1729 if (!xvimagesink->par) {
1730 xvimagesink->par = g_new0 (GValue, 1);
1731 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1732 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1734 /* call XSynchronize with the current value of synchronous */
1735 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1736 xvimagesink->synchronous ? "TRUE" : "FALSE");
1737 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1738 gst_xvimagesink_update_colorbalance (xvimagesink);
1739 gst_xvimagesink_manage_event_thread (xvimagesink);
1741 case GST_STATE_CHANGE_READY_TO_PAUSED:
1743 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1745 case GST_STATE_CHANGE_PAUSED_TO_READY:
1751 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1753 switch (transition) {
1754 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1756 case GST_STATE_CHANGE_PAUSED_TO_READY:
1757 xvimagesink->fps_n = 0;
1758 xvimagesink->fps_d = 1;
1759 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1760 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1761 g_mutex_lock (xvimagesink->flow_lock);
1762 if (xvimagesink->pool)
1763 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1764 g_mutex_unlock (xvimagesink->flow_lock);
1766 case GST_STATE_CHANGE_READY_TO_NULL:
1767 gst_xvimagesink_reset (xvimagesink);
1778 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1779 GstClockTime * start, GstClockTime * end)
1781 GstXvImageSink *xvimagesink;
1783 xvimagesink = GST_XVIMAGESINK (bsink);
1785 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1786 *start = GST_BUFFER_TIMESTAMP (buf);
1787 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1788 *end = *start + GST_BUFFER_DURATION (buf);
1790 if (xvimagesink->fps_n > 0) {
1792 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1793 xvimagesink->fps_n);
1799 static GstFlowReturn
1800 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1803 GstXvImageSink *xvimagesink;
1804 GstMetaXvImage *meta;
1807 xvimagesink = GST_XVIMAGESINK (vsink);
1809 meta = gst_buffer_get_meta_xvimage (buf);
1811 if (meta && meta->sink == xvimagesink) {
1812 /* If this buffer has been allocated using our buffer management we simply
1813 put the ximage which is in the PRIVATE pointer */
1814 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1819 GstVideoFrame src, dest;
1821 /* Else we have to copy the data into our private image, */
1822 /* if we have one... */
1823 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1825 /* we should have a pool, configured in setcaps */
1826 if (xvimagesink->pool == NULL)
1829 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1830 goto activate_failed;
1832 /* take a buffer form our pool */
1833 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1834 if (res != GST_FLOW_OK)
1837 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1840 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1841 "slow copy into bufferpool buffer %p", to_put);
1843 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1844 goto invalid_buffer;
1846 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1847 gst_video_frame_unmap (&src);
1848 goto invalid_buffer;
1851 gst_video_frame_copy (&dest, &src);
1853 gst_video_frame_unmap (&dest);
1854 gst_video_frame_unmap (&src);
1857 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1862 gst_buffer_unref (to_put);
1869 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1870 ("Internal error: can't allocate images"),
1871 ("We don't have a bufferpool negotiated"));
1872 return GST_FLOW_ERROR;
1876 /* No image available. That's very bad ! */
1877 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1882 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1883 ("Failed to create output image buffer"),
1884 ("XServer allocated buffer size did not match input buffer %"
1885 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1886 gst_buffer_get_size (buf)));
1887 res = GST_FLOW_ERROR;
1892 /* No Window available to put our image into */
1893 GST_WARNING_OBJECT (xvimagesink, "could map image");
1899 /* No Window available to put our image into */
1900 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1901 res = GST_FLOW_ERROR;
1906 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1907 res = GST_FLOW_ERROR;
1913 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1915 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1917 switch (GST_EVENT_TYPE (event)) {
1918 case GST_EVENT_TAG:{
1920 gchar *title = NULL;
1922 gst_event_parse_tag (event, &l);
1923 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1926 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1927 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1937 if (GST_BASE_SINK_CLASS (parent_class)->event)
1938 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1944 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1946 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1947 GstBufferPool *pool;
1948 GstStructure *config;
1953 gst_query_parse_allocation (query, &caps, &need_pool);
1958 g_mutex_lock (xvimagesink->flow_lock);
1959 if ((pool = xvimagesink->pool))
1960 gst_object_ref (pool);
1961 g_mutex_unlock (xvimagesink->flow_lock);
1964 const GstCaps *pcaps;
1966 /* we had a pool, check caps */
1967 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1968 config = gst_buffer_pool_get_config (pool);
1969 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1971 if (!gst_caps_is_equal (caps, pcaps)) {
1972 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1973 /* different caps, we can't use this pool */
1974 gst_object_unref (pool);
1978 if (pool == NULL && need_pool) {
1981 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1982 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1984 if (!gst_video_info_from_caps (&info, caps))
1987 /* the normal size of a frame */
1990 config = gst_buffer_pool_get_config (pool);
1991 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1992 if (!gst_buffer_pool_set_config (pool, config))
1995 /* we need at least 2 buffer because we hold on to the last one */
1996 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1998 /* we also support various metadata */
1999 gst_query_add_allocation_meta (query, GST_META_API_VIDEO);
2000 gst_query_add_allocation_meta (query, GST_META_API_VIDEO_CROP);
2002 gst_object_unref (pool);
2009 GST_DEBUG_OBJECT (bsink, "no caps specified");
2014 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2019 GST_DEBUG_OBJECT (bsink, "failed setting config");
2024 /* Interfaces stuff */
2026 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2027 GstStructure * structure)
2029 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2032 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2034 GstVideoRectangle src, dst, result;
2035 gdouble x, y, xscale = 1.0, yscale = 1.0;
2037 event = gst_event_new_navigation (structure);
2039 /* We take the flow_lock while we look at the window */
2040 g_mutex_lock (xvimagesink->flow_lock);
2042 if (!xvimagesink->xwindow) {
2043 g_mutex_unlock (xvimagesink->flow_lock);
2047 if (xvimagesink->keep_aspect) {
2048 /* We get the frame position using the calculated geometry from _setcaps
2049 that respect pixel aspect ratios */
2050 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2051 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2052 dst.w = xvimagesink->render_rect.w;
2053 dst.h = xvimagesink->render_rect.h;
2055 gst_video_sink_center_rect (src, dst, &result, TRUE);
2056 result.x += xvimagesink->render_rect.x;
2057 result.y += xvimagesink->render_rect.y;
2059 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2062 g_mutex_unlock (xvimagesink->flow_lock);
2064 /* We calculate scaling using the original video frames geometry to include
2065 pixel aspect ratio scaling. */
2066 xscale = (gdouble) xvimagesink->video_width / result.w;
2067 yscale = (gdouble) xvimagesink->video_height / result.h;
2069 /* Converting pointer coordinates to the non scaled geometry */
2070 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2071 x = MIN (x, result.x + result.w);
2072 x = MAX (x - result.x, 0);
2073 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2074 (gdouble) x * xscale, NULL);
2076 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2077 y = MIN (y, result.y + result.h);
2078 y = MAX (y - result.y, 0);
2079 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2080 (gdouble) y * yscale, NULL);
2083 gst_pad_send_event (peer, event);
2084 gst_object_unref (peer);
2089 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2091 iface->send_event = gst_xvimagesink_navigation_send_event;
2095 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2097 XID xwindow_id = id;
2098 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2099 GstXWindow *xwindow = NULL;
2101 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2103 g_mutex_lock (xvimagesink->flow_lock);
2105 /* If we already use that window return */
2106 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2107 g_mutex_unlock (xvimagesink->flow_lock);
2111 /* If the element has not initialized the X11 context try to do so */
2112 if (!xvimagesink->xcontext &&
2113 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2114 g_mutex_unlock (xvimagesink->flow_lock);
2115 /* we have thrown a GST_ELEMENT_ERROR now */
2119 gst_xvimagesink_update_colorbalance (xvimagesink);
2121 /* If a window is there already we destroy it */
2122 if (xvimagesink->xwindow) {
2123 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2124 xvimagesink->xwindow = NULL;
2127 /* If the xid is 0 we go back to an internal window */
2128 if (xwindow_id == 0) {
2129 /* If no width/height caps nego did not happen window will be created
2130 during caps nego then */
2131 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2132 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2134 gst_xvimagesink_xwindow_new (xvimagesink,
2135 GST_VIDEO_SINK_WIDTH (xvimagesink),
2136 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2139 XWindowAttributes attr;
2141 xwindow = g_new0 (GstXWindow, 1);
2142 xwindow->win = xwindow_id;
2144 /* Set the event we want to receive and create a GC */
2145 g_mutex_lock (xvimagesink->x_lock);
2147 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2149 xwindow->width = attr.width;
2150 xwindow->height = attr.height;
2151 xwindow->internal = FALSE;
2152 if (!xvimagesink->have_render_rect) {
2153 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2154 xvimagesink->render_rect.w = attr.width;
2155 xvimagesink->render_rect.h = attr.height;
2157 if (xvimagesink->handle_events) {
2158 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2159 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2163 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2164 xwindow->win, 0, NULL);
2165 g_mutex_unlock (xvimagesink->x_lock);
2169 xvimagesink->xwindow = xwindow;
2171 g_mutex_unlock (xvimagesink->flow_lock);
2175 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2177 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2179 GST_DEBUG ("doing expose");
2180 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2181 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2185 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2186 gboolean handle_events)
2188 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2190 xvimagesink->handle_events = handle_events;
2192 g_mutex_lock (xvimagesink->flow_lock);
2194 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2195 g_mutex_unlock (xvimagesink->flow_lock);
2199 g_mutex_lock (xvimagesink->x_lock);
2201 if (handle_events) {
2202 if (xvimagesink->xwindow->internal) {
2203 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2204 ExposureMask | StructureNotifyMask | PointerMotionMask |
2205 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2207 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2208 ExposureMask | StructureNotifyMask | PointerMotionMask |
2209 KeyPressMask | KeyReleaseMask);
2212 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2215 g_mutex_unlock (xvimagesink->x_lock);
2217 g_mutex_unlock (xvimagesink->flow_lock);
2221 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2222 gint width, gint height)
2224 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2226 /* FIXME: how about some locking? */
2227 if (width >= 0 && height >= 0) {
2228 xvimagesink->render_rect.x = x;
2229 xvimagesink->render_rect.y = y;
2230 xvimagesink->render_rect.w = width;
2231 xvimagesink->render_rect.h = height;
2232 xvimagesink->have_render_rect = TRUE;
2234 xvimagesink->render_rect.x = 0;
2235 xvimagesink->render_rect.y = 0;
2236 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2237 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2238 xvimagesink->have_render_rect = FALSE;
2243 gst_xvimagesink_video_overlay_init (GstVideoOverlayIface * iface)
2245 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2246 iface->expose = gst_xvimagesink_expose;
2247 iface->handle_events = gst_xvimagesink_set_event_handling;
2248 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2251 static const GList *
2252 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2254 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2256 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2258 if (xvimagesink->xcontext)
2259 return xvimagesink->xcontext->channels_list;
2265 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2266 GstColorBalanceChannel * channel, gint value)
2268 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2270 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2271 g_return_if_fail (channel->label != NULL);
2273 xvimagesink->cb_changed = TRUE;
2275 /* Normalize val to [-1000, 1000] */
2276 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2277 (double) (channel->max_value - channel->min_value));
2279 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2280 xvimagesink->hue = value;
2281 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2282 xvimagesink->saturation = value;
2283 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2284 xvimagesink->contrast = value;
2285 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2286 xvimagesink->brightness = value;
2288 g_warning ("got an unknown channel %s", channel->label);
2292 gst_xvimagesink_update_colorbalance (xvimagesink);
2296 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2297 GstColorBalanceChannel * channel)
2299 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2302 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2303 g_return_val_if_fail (channel->label != NULL, 0);
2305 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2306 value = xvimagesink->hue;
2307 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2308 value = xvimagesink->saturation;
2309 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2310 value = xvimagesink->contrast;
2311 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2312 value = xvimagesink->brightness;
2314 g_warning ("got an unknown channel %s", channel->label);
2317 /* Normalize val to [channel->min_value, channel->max_value] */
2318 value = channel->min_value + (channel->max_value - channel->min_value) *
2319 (value + 1000) / 2000;
2325 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
2327 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2328 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2329 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2330 iface->get_value = gst_xvimagesink_colorbalance_get_value;
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;
2492 /* =========================================== */
2494 /* Init & Class init */
2496 /* =========================================== */
2499 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2500 const GValue * value, GParamSpec * pspec)
2502 GstXvImageSink *xvimagesink;
2504 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2506 xvimagesink = GST_XVIMAGESINK (object);
2510 xvimagesink->hue = g_value_get_int (value);
2511 xvimagesink->cb_changed = TRUE;
2512 gst_xvimagesink_update_colorbalance (xvimagesink);
2515 xvimagesink->contrast = g_value_get_int (value);
2516 xvimagesink->cb_changed = TRUE;
2517 gst_xvimagesink_update_colorbalance (xvimagesink);
2519 case PROP_BRIGHTNESS:
2520 xvimagesink->brightness = g_value_get_int (value);
2521 xvimagesink->cb_changed = TRUE;
2522 gst_xvimagesink_update_colorbalance (xvimagesink);
2524 case PROP_SATURATION:
2525 xvimagesink->saturation = g_value_get_int (value);
2526 xvimagesink->cb_changed = TRUE;
2527 gst_xvimagesink_update_colorbalance (xvimagesink);
2530 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2532 case PROP_SYNCHRONOUS:
2533 xvimagesink->synchronous = g_value_get_boolean (value);
2534 if (xvimagesink->xcontext) {
2535 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2536 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2537 xvimagesink->synchronous ? "TRUE" : "FALSE");
2540 case PROP_PIXEL_ASPECT_RATIO:
2541 g_free (xvimagesink->par);
2542 xvimagesink->par = g_new0 (GValue, 1);
2543 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2544 if (!g_value_transform (value, xvimagesink->par)) {
2545 g_warning ("Could not transform string to aspect ratio");
2546 gst_value_set_fraction (xvimagesink->par, 1, 1);
2548 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2549 gst_value_get_fraction_numerator (xvimagesink->par),
2550 gst_value_get_fraction_denominator (xvimagesink->par));
2552 case PROP_FORCE_ASPECT_RATIO:
2553 xvimagesink->keep_aspect = g_value_get_boolean (value);
2555 case PROP_HANDLE_EVENTS:
2556 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2557 g_value_get_boolean (value));
2558 gst_xvimagesink_manage_event_thread (xvimagesink);
2561 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2563 case PROP_HANDLE_EXPOSE:
2564 xvimagesink->handle_expose = g_value_get_boolean (value);
2565 gst_xvimagesink_manage_event_thread (xvimagesink);
2567 case PROP_DOUBLE_BUFFER:
2568 xvimagesink->double_buffer = g_value_get_boolean (value);
2570 case PROP_AUTOPAINT_COLORKEY:
2571 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2574 xvimagesink->colorkey = g_value_get_int (value);
2576 case PROP_DRAW_BORDERS:
2577 xvimagesink->draw_borders = g_value_get_boolean (value);
2580 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2586 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2587 GValue * value, GParamSpec * pspec)
2589 GstXvImageSink *xvimagesink;
2591 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2593 xvimagesink = GST_XVIMAGESINK (object);
2597 g_value_set_int (value, xvimagesink->hue);
2600 g_value_set_int (value, xvimagesink->contrast);
2602 case PROP_BRIGHTNESS:
2603 g_value_set_int (value, xvimagesink->brightness);
2605 case PROP_SATURATION:
2606 g_value_set_int (value, xvimagesink->saturation);
2609 g_value_set_string (value, xvimagesink->display_name);
2611 case PROP_SYNCHRONOUS:
2612 g_value_set_boolean (value, xvimagesink->synchronous);
2614 case PROP_PIXEL_ASPECT_RATIO:
2615 if (xvimagesink->par)
2616 g_value_transform (xvimagesink->par, value);
2618 case PROP_FORCE_ASPECT_RATIO:
2619 g_value_set_boolean (value, xvimagesink->keep_aspect);
2621 case PROP_HANDLE_EVENTS:
2622 g_value_set_boolean (value, xvimagesink->handle_events);
2626 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2628 g_value_set_string (value, adaptor_no_s);
2629 g_free (adaptor_no_s);
2632 case PROP_DEVICE_NAME:
2633 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2634 g_value_set_string (value,
2635 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2637 g_value_set_string (value, NULL);
2640 case PROP_HANDLE_EXPOSE:
2641 g_value_set_boolean (value, xvimagesink->handle_expose);
2643 case PROP_DOUBLE_BUFFER:
2644 g_value_set_boolean (value, xvimagesink->double_buffer);
2646 case PROP_AUTOPAINT_COLORKEY:
2647 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2650 g_value_set_int (value, xvimagesink->colorkey);
2652 case PROP_DRAW_BORDERS:
2653 g_value_set_boolean (value, xvimagesink->draw_borders);
2655 case PROP_WINDOW_WIDTH:
2656 if (xvimagesink->xwindow)
2657 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2659 g_value_set_uint64 (value, 0);
2661 case PROP_WINDOW_HEIGHT:
2662 if (xvimagesink->xwindow)
2663 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2665 g_value_set_uint64 (value, 0);
2668 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2674 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2678 GST_OBJECT_LOCK (xvimagesink);
2679 xvimagesink->running = FALSE;
2680 /* grab thread and mark it as NULL */
2681 thread = xvimagesink->event_thread;
2682 xvimagesink->event_thread = NULL;
2683 GST_OBJECT_UNLOCK (xvimagesink);
2685 /* Wait for our event thread to finish before we clean up our stuff. */
2687 g_thread_join (thread);
2689 if (xvimagesink->cur_image) {
2690 gst_buffer_unref (xvimagesink->cur_image);
2691 xvimagesink->cur_image = NULL;
2694 g_mutex_lock (xvimagesink->flow_lock);
2696 if (xvimagesink->pool) {
2697 gst_object_unref (xvimagesink->pool);
2698 xvimagesink->pool = NULL;
2701 if (xvimagesink->xwindow) {
2702 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2703 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2704 xvimagesink->xwindow = NULL;
2706 g_mutex_unlock (xvimagesink->flow_lock);
2708 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2709 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2710 xvimagesink->have_render_rect = FALSE;
2712 gst_xvimagesink_xcontext_clear (xvimagesink);
2715 /* Finalize is called only once, dispose can be called multiple times.
2716 * We use mutexes and don't reset stuff to NULL here so let's register
2719 gst_xvimagesink_finalize (GObject * object)
2721 GstXvImageSink *xvimagesink;
2723 xvimagesink = GST_XVIMAGESINK (object);
2725 gst_xvimagesink_reset (xvimagesink);
2727 if (xvimagesink->display_name) {
2728 g_free (xvimagesink->display_name);
2729 xvimagesink->display_name = NULL;
2732 if (xvimagesink->par) {
2733 g_free (xvimagesink->par);
2734 xvimagesink->par = NULL;
2736 if (xvimagesink->x_lock) {
2737 g_mutex_free (xvimagesink->x_lock);
2738 xvimagesink->x_lock = NULL;
2740 if (xvimagesink->flow_lock) {
2741 g_mutex_free (xvimagesink->flow_lock);
2742 xvimagesink->flow_lock = NULL;
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 xvimagesink->x_lock = g_mutex_new ();
2769 xvimagesink->flow_lock = g_mutex_new ();
2771 xvimagesink->pool = NULL;
2773 xvimagesink->synchronous = FALSE;
2774 xvimagesink->double_buffer = TRUE;
2775 xvimagesink->running = FALSE;
2776 xvimagesink->keep_aspect = FALSE;
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 "
2828 "the X display in synchronous mode. (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", FALSE,
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_details_simple (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);