2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/propertyprobe.h>
119 #include <gst/video/videooverlay.h>
120 #include <gst/video/colorbalance.h>
121 /* Helper functions */
122 #include <gst/video/gstvideometa.h>
125 #include "xvimagesink.h"
127 /* Debugging category */
128 #include <gst/gstinfo.h>
130 #include "gst/glib-compat-private.h"
132 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
133 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
134 #define GST_CAT_DEFAULT gst_debug_xvimagesink
139 unsigned long functions;
140 unsigned long decorations;
142 unsigned long status;
144 MotifWmHints, MwmHints;
146 #define MWM_HINTS_DECORATIONS (1L << 1)
148 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
149 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
151 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
153 /* Default template - initiated with class struct to allow gst-register to work
155 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
156 GST_STATIC_PAD_TEMPLATE ("sink",
159 GST_STATIC_CAPS ("video/x-raw, "
160 "framerate = (fraction) [ 0, MAX ], "
161 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
173 PROP_PIXEL_ASPECT_RATIO,
174 PROP_FORCE_ASPECT_RATIO,
180 PROP_AUTOPAINT_COLORKEY,
187 /* ============================================================= */
191 /* ============================================================= */
193 /* =========================================== */
195 /* Object typing & Creation */
197 /* =========================================== */
198 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
199 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
201 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
204 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
206 #define gst_xvimagesink_parent_class parent_class
207 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
208 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
209 gst_xvimagesink_navigation_init);
210 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
211 gst_xvimagesink_video_overlay_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
213 gst_xvimagesink_colorbalance_init);
214 G_IMPLEMENT_INTERFACE (GST_TYPE_PROPERTY_PROBE,
215 gst_xvimagesink_property_probe_interface_init));
218 /* ============================================================= */
220 /* Private Methods */
222 /* ============================================================= */
225 /* We are called with the x_lock taken */
227 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
228 GstXWindow * xwindow, GstVideoRectangle rect)
232 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
233 g_return_if_fail (xwindow != NULL);
235 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
236 xvimagesink->xcontext->black);
239 if (rect.x > xvimagesink->render_rect.x) {
240 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
241 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
242 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
246 t1 = rect.x + rect.w;
247 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
249 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
250 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
254 if (rect.y > xvimagesink->render_rect.y) {
255 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
256 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
257 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
261 t1 = rect.y + rect.h;
262 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
264 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
265 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
269 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
270 * if no window was available */
272 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
274 GstXvImageMeta *meta;
275 GstVideoCropMeta *crop;
276 GstVideoRectangle result;
277 gboolean draw_border = FALSE;
278 GstVideoRectangle src, dst;
280 /* We take the flow_lock. If expose is in there we don't want to run
281 concurrently from the data flow thread */
282 g_mutex_lock (xvimagesink->flow_lock);
284 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
285 g_mutex_unlock (xvimagesink->flow_lock);
289 /* Draw borders when displaying the first frame. After this
290 draw borders only on expose event or after a size change. */
291 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
295 /* Store a reference to the last image we put, lose the previous one */
296 if (xvimage && xvimagesink->cur_image != xvimage) {
297 if (xvimagesink->cur_image) {
298 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
299 gst_buffer_unref (xvimagesink->cur_image);
301 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
302 xvimagesink->cur_image = gst_buffer_ref (xvimage);
305 /* Expose sends a NULL image, we take the latest frame */
307 if (xvimagesink->cur_image) {
309 xvimage = xvimagesink->cur_image;
311 g_mutex_unlock (xvimagesink->flow_lock);
316 meta = gst_buffer_get_xvimage_meta (xvimage);
318 crop = gst_buffer_get_video_crop_meta (xvimage);
321 src.x = crop->x + meta->x;
322 src.y = crop->y + meta->y;
324 src.h = crop->height;
329 src.h = meta->height;
332 if (xvimagesink->keep_aspect) {
333 dst.w = xvimagesink->render_rect.w;
334 dst.h = xvimagesink->render_rect.h;
336 gst_video_sink_center_rect (src, dst, &result, TRUE);
337 result.x += xvimagesink->render_rect.x;
338 result.y += xvimagesink->render_rect.y;
340 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
343 g_mutex_lock (xvimagesink->x_lock);
345 if (draw_border && xvimagesink->draw_borders) {
346 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
348 xvimagesink->redraw_border = FALSE;
351 if (xvimagesink->xcontext->use_xshm) {
352 GST_LOG_OBJECT (xvimagesink,
353 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
354 GST_PTR_FORMAT, meta->width, meta->height,
355 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
357 XvShmPutImage (xvimagesink->xcontext->disp,
358 xvimagesink->xcontext->xv_port_id,
359 xvimagesink->xwindow->win,
360 xvimagesink->xwindow->gc, meta->xvimage,
361 src.x, src.y, src.w, src.h,
362 result.x, result.y, result.w, result.h, FALSE);
364 #endif /* HAVE_XSHM */
366 XvPutImage (xvimagesink->xcontext->disp,
367 xvimagesink->xcontext->xv_port_id,
368 xvimagesink->xwindow->win,
369 xvimagesink->xwindow->gc, meta->xvimage,
370 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
373 XSync (xvimagesink->xcontext->disp, FALSE);
375 g_mutex_unlock (xvimagesink->x_lock);
377 g_mutex_unlock (xvimagesink->flow_lock);
383 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
386 Atom hints_atom = None;
389 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
390 g_return_val_if_fail (window != NULL, FALSE);
392 g_mutex_lock (xvimagesink->x_lock);
394 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
396 if (hints_atom == None) {
397 g_mutex_unlock (xvimagesink->x_lock);
401 hints = g_malloc0 (sizeof (MotifWmHints));
403 hints->flags |= MWM_HINTS_DECORATIONS;
404 hints->decorations = 1 << 0;
406 XChangeProperty (xvimagesink->xcontext->disp, window->win,
407 hints_atom, hints_atom, 32, PropModeReplace,
408 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
410 XSync (xvimagesink->xcontext->disp, FALSE);
412 g_mutex_unlock (xvimagesink->x_lock);
420 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
421 GstXWindow * xwindow, const gchar * media_title)
424 g_free (xvimagesink->media_title);
425 xvimagesink->media_title = g_strdup (media_title);
428 /* we have a window */
429 if (xwindow->internal) {
430 XTextProperty xproperty;
431 const gchar *app_name;
432 const gchar *title = NULL;
433 gchar *title_mem = NULL;
435 /* set application name as a title */
436 app_name = g_get_application_name ();
438 if (app_name && xvimagesink->media_title) {
439 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
441 } else if (app_name) {
443 } else if (xvimagesink->media_title) {
444 title = xvimagesink->media_title;
448 if ((XStringListToTextProperty (((char **) &title), 1,
450 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
451 XFree (xproperty.value);
460 /* This function handles a GstXWindow creation
461 * The width and height are the actual pixel size on the display */
463 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
464 gint width, gint height)
466 GstXWindow *xwindow = NULL;
469 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
471 xwindow = g_new0 (GstXWindow, 1);
473 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
474 xvimagesink->render_rect.w = width;
475 xvimagesink->render_rect.h = height;
477 xwindow->width = width;
478 xwindow->height = height;
479 xwindow->internal = TRUE;
481 g_mutex_lock (xvimagesink->x_lock);
483 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
484 xvimagesink->xcontext->root,
485 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
487 /* We have to do that to prevent X from redrawing the background on
488 * ConfigureNotify. This takes away flickering of video when resizing. */
489 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
491 /* set application name as a title */
492 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
494 if (xvimagesink->handle_events) {
497 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
498 StructureNotifyMask | PointerMotionMask | KeyPressMask |
499 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
501 /* Tell the window manager we'd like delete client messages instead of
503 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
504 "WM_DELETE_WINDOW", True);
505 if (wm_delete != None) {
506 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
511 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
512 xwindow->win, 0, &values);
514 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
516 XSync (xvimagesink->xcontext->disp, FALSE);
518 g_mutex_unlock (xvimagesink->x_lock);
520 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
522 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
528 /* This function destroys a GstXWindow */
530 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
531 GstXWindow * xwindow)
533 g_return_if_fail (xwindow != NULL);
534 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
536 g_mutex_lock (xvimagesink->x_lock);
538 /* If we did not create that window we just free the GC and let it live */
539 if (xwindow->internal)
540 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
542 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
544 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
546 XSync (xvimagesink->xcontext->disp, FALSE);
548 g_mutex_unlock (xvimagesink->x_lock);
554 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
556 XWindowAttributes attr;
558 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
560 /* Update the window geometry */
561 g_mutex_lock (xvimagesink->x_lock);
562 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
563 g_mutex_unlock (xvimagesink->x_lock);
567 XGetWindowAttributes (xvimagesink->xcontext->disp,
568 xvimagesink->xwindow->win, &attr);
570 xvimagesink->xwindow->width = attr.width;
571 xvimagesink->xwindow->height = attr.height;
573 if (!xvimagesink->have_render_rect) {
574 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
575 xvimagesink->render_rect.w = attr.width;
576 xvimagesink->render_rect.h = attr.height;
579 g_mutex_unlock (xvimagesink->x_lock);
583 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
584 GstXWindow * xwindow)
586 g_return_if_fail (xwindow != NULL);
587 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
589 g_mutex_lock (xvimagesink->x_lock);
591 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
594 XSync (xvimagesink->xcontext->disp, FALSE);
596 g_mutex_unlock (xvimagesink->x_lock);
599 /* This function commits our internal colorbalance settings to our grabbed Xv
600 port. If the xcontext is not initialized yet it simply returns */
602 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
604 GList *channels = NULL;
606 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
608 /* If we haven't initialized the X context we can't update anything */
609 if (xvimagesink->xcontext == NULL)
612 /* Don't set the attributes if they haven't been changed, to avoid
613 * rounding errors changing the values */
614 if (!xvimagesink->cb_changed)
617 /* For each channel of the colorbalance we calculate the correct value
618 doing range conversion and then set the Xv port attribute to match our
620 channels = xvimagesink->xcontext->channels_list;
623 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
624 GstColorBalanceChannel *channel = NULL;
627 gdouble convert_coef;
629 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
630 g_object_ref (channel);
632 /* Our range conversion coef */
633 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
635 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
636 value = xvimagesink->hue;
637 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
638 value = xvimagesink->saturation;
639 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
640 value = xvimagesink->contrast;
641 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
642 value = xvimagesink->brightness;
644 g_warning ("got an unknown channel %s", channel->label);
645 g_object_unref (channel);
649 /* Committing to Xv port */
650 g_mutex_lock (xvimagesink->x_lock);
652 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
653 if (prop_atom != None) {
656 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
657 XvSetPortAttribute (xvimagesink->xcontext->disp,
658 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
660 g_mutex_unlock (xvimagesink->x_lock);
662 g_object_unref (channel);
664 channels = g_list_next (channels);
668 /* This function handles XEvents that might be in the queue. It generates
669 GstEvent that will be sent upstream in the pipeline to handle interactivity
670 and navigation. It will also listen for configure events on the window to
671 trigger caps renegotiation so on the fly software scaling can work. */
673 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
676 guint pointer_x = 0, pointer_y = 0;
677 gboolean pointer_moved = FALSE;
678 gboolean exposed = FALSE, configured = FALSE;
680 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
682 /* Handle Interaction, produces navigation events */
684 /* We get all pointer motion events, only the last position is
686 g_mutex_lock (xvimagesink->flow_lock);
687 g_mutex_lock (xvimagesink->x_lock);
688 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
689 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
690 g_mutex_unlock (xvimagesink->x_lock);
691 g_mutex_unlock (xvimagesink->flow_lock);
695 pointer_x = e.xmotion.x;
696 pointer_y = e.xmotion.y;
697 pointer_moved = TRUE;
702 g_mutex_lock (xvimagesink->flow_lock);
703 g_mutex_lock (xvimagesink->x_lock);
707 g_mutex_unlock (xvimagesink->x_lock);
708 g_mutex_unlock (xvimagesink->flow_lock);
710 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
711 pointer_x, pointer_y);
712 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
713 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
715 g_mutex_lock (xvimagesink->flow_lock);
716 g_mutex_lock (xvimagesink->x_lock);
719 /* We get all events on our window to throw them upstream */
720 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
721 xvimagesink->xwindow->win,
722 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
726 /* We lock only for the X function call */
727 g_mutex_unlock (xvimagesink->x_lock);
728 g_mutex_unlock (xvimagesink->flow_lock);
732 /* Mouse button pressed over our window. We send upstream
733 events for interactivity/navigation */
734 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
735 e.xbutton.button, e.xbutton.x, e.xbutton.y);
736 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
737 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
740 /* Mouse button released over our window. We send upstream
741 events for interactivity/navigation */
742 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
743 e.xbutton.button, e.xbutton.x, e.xbutton.y);
744 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
745 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
749 /* Key pressed/released over our window. We send upstream
750 events for interactivity/navigation */
751 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
752 e.xkey.keycode, e.xkey.x, e.xkey.y);
753 g_mutex_lock (xvimagesink->x_lock);
754 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
756 g_mutex_unlock (xvimagesink->x_lock);
757 if (keysym != NoSymbol) {
758 char *key_str = NULL;
760 g_mutex_lock (xvimagesink->x_lock);
761 key_str = XKeysymToString (keysym);
762 g_mutex_unlock (xvimagesink->x_lock);
763 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
764 e.type == KeyPress ? "key-press" : "key-release", key_str);
766 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
767 e.type == KeyPress ? "key-press" : "key-release", "unknown");
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 >= 0 &&
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 #if !GLIB_CHECK_VERSION (2, 31, 0)
1181 xvimagesink->event_thread = g_thread_create (
1182 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1184 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1185 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1189 if (xvimagesink->event_thread) {
1190 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1191 xvimagesink->handle_expose, xvimagesink->handle_events);
1192 xvimagesink->running = FALSE;
1193 /* grab thread and mark it as NULL */
1194 thread = xvimagesink->event_thread;
1195 xvimagesink->event_thread = NULL;
1198 GST_OBJECT_UNLOCK (xvimagesink);
1200 /* Wait for our event thread to finish */
1202 g_thread_join (thread);
1207 /* This function calculates the pixel aspect ratio based on the properties
1208 * in the xcontext structure and stores it there. */
1210 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1212 static const gint par[][2] = {
1213 {1, 1}, /* regular screen */
1214 {16, 15}, /* PAL TV */
1215 {11, 10}, /* 525 line Rec.601 video */
1216 {54, 59}, /* 625 line Rec.601 video */
1217 {64, 45}, /* 1280x1024 on 16:9 display */
1218 {5, 3}, /* 1280x1024 on 4:3 display */
1219 {4, 3} /* 800x600 on 16:9 display */
1226 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1228 /* first calculate the "real" ratio based on the X values;
1229 * which is the "physical" w/h divided by the w/h in pixels of the display */
1230 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1231 / (xcontext->heightmm * xcontext->width);
1233 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1235 if (xcontext->width == 720 && xcontext->height == 576) {
1236 ratio = 4.0 * 576 / (3.0 * 720);
1238 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1240 /* now find the one from par[][2] with the lowest delta to the real one */
1244 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1245 gdouble this_delta = DELTA (i);
1247 if (this_delta < delta) {
1253 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1254 par[index][0], par[index][1]);
1256 g_free (xcontext->par);
1257 xcontext->par = g_new0 (GValue, 1);
1258 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1259 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1260 GST_DEBUG ("set xcontext PAR to %d/%d",
1261 gst_value_get_fraction_numerator (xcontext->par),
1262 gst_value_get_fraction_denominator (xcontext->par));
1265 /* This function gets the X Display and global info about it. Everything is
1266 stored in our object and will be cleaned when the object is disposed. Note
1267 here that caps for supported format are generated without any window or
1269 static GstXContext *
1270 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1272 GstXContext *xcontext = NULL;
1273 XPixmapFormatValues *px_formats = NULL;
1274 gint nb_formats = 0, i, j, N_attr;
1275 XvAttribute *xv_attr;
1277 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1278 "XV_BRIGHTNESS", "XV_CONTRAST"
1281 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1283 xcontext = g_new0 (GstXContext, 1);
1284 xcontext->im_format = 0;
1286 g_mutex_lock (xvimagesink->x_lock);
1288 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1290 if (!xcontext->disp) {
1291 g_mutex_unlock (xvimagesink->x_lock);
1293 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1294 ("Could not initialise Xv output"), ("Could not open display"));
1298 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1299 xcontext->screen_num = DefaultScreen (xcontext->disp);
1300 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1301 xcontext->root = DefaultRootWindow (xcontext->disp);
1302 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1303 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1304 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1306 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1307 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1308 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1309 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1311 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1312 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1314 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1315 /* We get supported pixmap formats at supported depth */
1316 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1319 XCloseDisplay (xcontext->disp);
1320 g_mutex_unlock (xvimagesink->x_lock);
1321 g_free (xcontext->par);
1323 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1324 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1328 /* We get bpp value corresponding to our running depth */
1329 for (i = 0; i < nb_formats; i++) {
1330 if (px_formats[i].depth == xcontext->depth)
1331 xcontext->bpp = px_formats[i].bits_per_pixel;
1336 xcontext->endianness =
1337 (ImageByteOrder (xcontext->disp) ==
1338 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1340 /* our caps system handles 24/32bpp RGB as big-endian. */
1341 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1342 xcontext->endianness == G_LITTLE_ENDIAN) {
1343 xcontext->endianness = G_BIG_ENDIAN;
1344 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1345 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1346 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1347 if (xcontext->bpp == 24) {
1348 xcontext->visual->red_mask >>= 8;
1349 xcontext->visual->green_mask >>= 8;
1350 xcontext->visual->blue_mask >>= 8;
1354 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1356 /* Search for XShm extension support */
1358 if (XShmQueryExtension (xcontext->disp) &&
1359 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1360 xcontext->use_xshm = TRUE;
1361 GST_DEBUG ("xvimagesink is using XShm extension");
1363 #endif /* HAVE_XSHM */
1365 xcontext->use_xshm = FALSE;
1366 GST_DEBUG ("xvimagesink is not using XShm extension");
1369 if (!xcontext->caps) {
1370 XCloseDisplay (xcontext->disp);
1371 g_mutex_unlock (xvimagesink->x_lock);
1372 g_free (xcontext->par);
1374 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1378 xv_attr = XvQueryPortAttributes (xcontext->disp,
1379 xcontext->xv_port_id, &N_attr);
1382 /* Generate the channels list */
1383 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1384 XvAttribute *matching_attr = NULL;
1386 /* Retrieve the property atom if it exists. If it doesn't exist,
1387 * the attribute itself must not either, so we can skip */
1388 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1389 if (prop_atom == None)
1392 if (xv_attr != NULL) {
1393 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1394 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1395 matching_attr = xv_attr + j;
1398 if (matching_attr) {
1399 GstColorBalanceChannel *channel;
1401 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1402 channel->label = g_strdup (channels[i]);
1403 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1404 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1406 xcontext->channels_list = g_list_append (xcontext->channels_list,
1409 /* If the colorbalance settings have not been touched we get Xv values
1410 as defaults and update our internal variables */
1411 if (!xvimagesink->cb_changed) {
1414 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1416 /* Normalize val to [-1000, 1000] */
1417 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1418 (double) (channel->max_value - channel->min_value));
1420 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1421 xvimagesink->hue = val;
1422 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1423 xvimagesink->saturation = val;
1424 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1425 xvimagesink->brightness = val;
1426 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1427 xvimagesink->contrast = val;
1435 g_mutex_unlock (xvimagesink->x_lock);
1440 /* This function cleans the X context. Closing the Display, releasing the XV
1441 port and unrefing the caps for supported formats. */
1443 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1445 GList *formats_list, *channels_list;
1446 GstXContext *xcontext;
1449 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1451 GST_OBJECT_LOCK (xvimagesink);
1452 if (xvimagesink->xcontext == NULL) {
1453 GST_OBJECT_UNLOCK (xvimagesink);
1457 /* Take the XContext from the sink and clean it up */
1458 xcontext = xvimagesink->xcontext;
1459 xvimagesink->xcontext = NULL;
1461 GST_OBJECT_UNLOCK (xvimagesink);
1464 formats_list = xcontext->formats_list;
1466 while (formats_list) {
1467 GstXvImageFormat *format = formats_list->data;
1469 gst_caps_unref (format->caps);
1471 formats_list = g_list_next (formats_list);
1474 if (xcontext->formats_list)
1475 g_list_free (xcontext->formats_list);
1477 channels_list = xcontext->channels_list;
1479 while (channels_list) {
1480 GstColorBalanceChannel *channel = channels_list->data;
1482 g_object_unref (channel);
1483 channels_list = g_list_next (channels_list);
1486 if (xcontext->channels_list)
1487 g_list_free (xcontext->channels_list);
1489 gst_caps_unref (xcontext->caps);
1490 if (xcontext->last_caps)
1491 gst_caps_replace (&xcontext->last_caps, NULL);
1493 for (i = 0; i < xcontext->nb_adaptors; i++) {
1494 g_free (xcontext->adaptors[i]);
1497 g_free (xcontext->adaptors);
1499 g_free (xcontext->par);
1501 g_mutex_lock (xvimagesink->x_lock);
1503 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1505 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1507 XCloseDisplay (xcontext->disp);
1509 g_mutex_unlock (xvimagesink->x_lock);
1517 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1519 GstXvImageSink *xvimagesink;
1522 xvimagesink = GST_XVIMAGESINK (bsink);
1524 if (xvimagesink->xcontext) {
1526 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1527 GST_CAPS_INTERSECT_FIRST);
1529 return gst_caps_ref (xvimagesink->xcontext->caps);
1532 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1534 GstCaps *intersection;
1537 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1538 gst_caps_unref (caps);
1539 caps = intersection;
1545 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1547 GstXvImageSink *xvimagesink;
1548 GstStructure *structure;
1549 GstBufferPool *newpool, *oldpool;
1551 guint32 im_format = 0;
1552 gint video_par_n, video_par_d; /* video's PAR */
1553 gint display_par_n, display_par_d; /* display's PAR */
1557 xvimagesink = GST_XVIMAGESINK (bsink);
1559 GST_DEBUG_OBJECT (xvimagesink,
1560 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1561 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1563 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1564 goto incompatible_caps;
1566 if (!gst_video_info_from_caps (&info, caps))
1567 goto invalid_format;
1569 structure = gst_caps_get_structure (caps, 0);
1571 xvimagesink->fps_n = info.fps_n;
1572 xvimagesink->fps_d = info.fps_d;
1574 xvimagesink->video_width = info.width;
1575 xvimagesink->video_height = info.height;
1577 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1578 if (im_format == -1)
1579 goto invalid_format;
1583 /* get aspect ratio from caps if it's present, and
1584 * convert video width and height to a display width and height
1585 * using wd / hd = wv / hv * PARv / PARd */
1587 /* get video's PAR */
1588 video_par_n = info.par_n;
1589 video_par_d = info.par_d;
1591 /* get display's PAR */
1592 if (xvimagesink->par) {
1593 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1594 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1600 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1601 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1604 GST_DEBUG_OBJECT (xvimagesink,
1605 "video width/height: %dx%d, calculated display ratio: %d/%d",
1606 info.width, info.height, num, den);
1608 /* now find a width x height that respects this display ratio.
1609 * prefer those that have one of w/h the same as the incoming video
1610 * using wd / hd = num / den */
1612 /* start with same height, because of interlaced video */
1613 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1614 if (info.height % den == 0) {
1615 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1616 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1617 gst_util_uint64_scale_int (info.height, num, den);
1618 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1619 } else if (info.width % num == 0) {
1620 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1621 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1622 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1623 gst_util_uint64_scale_int (info.width, den, num);
1625 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1626 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1627 gst_util_uint64_scale_int (info.height, num, den);
1628 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1630 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1631 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1633 /* Notify application to set xwindow id now */
1634 g_mutex_lock (xvimagesink->flow_lock);
1635 if (!xvimagesink->xwindow) {
1636 g_mutex_unlock (xvimagesink->flow_lock);
1637 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1639 g_mutex_unlock (xvimagesink->flow_lock);
1642 /* Creating our window and our image with the display size in pixels */
1643 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1644 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1645 goto no_display_size;
1647 g_mutex_lock (xvimagesink->flow_lock);
1648 if (!xvimagesink->xwindow) {
1649 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1650 GST_VIDEO_SINK_WIDTH (xvimagesink),
1651 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1654 xvimagesink->info = info;
1656 /* After a resize, we want to redraw the borders in case the new frame size
1657 * doesn't cover the same area */
1658 xvimagesink->redraw_border = TRUE;
1660 /* create a new pool for the new configuration */
1661 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1663 structure = gst_buffer_pool_get_config (newpool);
1664 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1665 if (!gst_buffer_pool_set_config (newpool, structure))
1668 oldpool = xvimagesink->pool;
1669 xvimagesink->pool = newpool;
1670 g_mutex_unlock (xvimagesink->flow_lock);
1672 /* unref the old sink */
1674 /* we don't deactivate, some elements might still be using it, it will
1675 * be deactivated when the last ref is gone */
1676 gst_object_unref (oldpool);
1684 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1689 GST_DEBUG_OBJECT (xvimagesink,
1690 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1695 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1696 ("Error calculating the output display ratio of the video."));
1701 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1702 ("Error calculating the output display ratio of the video."));
1707 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1708 g_mutex_unlock (xvimagesink->flow_lock);
1713 static GstStateChangeReturn
1714 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1716 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1717 GstXvImageSink *xvimagesink;
1718 GstXContext *xcontext = NULL;
1720 xvimagesink = GST_XVIMAGESINK (element);
1722 switch (transition) {
1723 case GST_STATE_CHANGE_NULL_TO_READY:
1724 /* Initializing the XContext */
1725 if (xvimagesink->xcontext == NULL) {
1726 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1727 if (xcontext == NULL) {
1728 ret = GST_STATE_CHANGE_FAILURE;
1731 GST_OBJECT_LOCK (xvimagesink);
1733 xvimagesink->xcontext = xcontext;
1734 GST_OBJECT_UNLOCK (xvimagesink);
1737 /* update object's par with calculated one if not set yet */
1738 if (!xvimagesink->par) {
1739 xvimagesink->par = g_new0 (GValue, 1);
1740 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1741 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1743 /* call XSynchronize with the current value of synchronous */
1744 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1745 xvimagesink->synchronous ? "TRUE" : "FALSE");
1746 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1747 gst_xvimagesink_update_colorbalance (xvimagesink);
1748 gst_xvimagesink_manage_event_thread (xvimagesink);
1750 case GST_STATE_CHANGE_READY_TO_PAUSED:
1752 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1754 case GST_STATE_CHANGE_PAUSED_TO_READY:
1760 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1762 switch (transition) {
1763 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1765 case GST_STATE_CHANGE_PAUSED_TO_READY:
1766 xvimagesink->fps_n = 0;
1767 xvimagesink->fps_d = 1;
1768 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1769 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1770 g_mutex_lock (xvimagesink->flow_lock);
1771 if (xvimagesink->pool)
1772 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1773 g_mutex_unlock (xvimagesink->flow_lock);
1775 case GST_STATE_CHANGE_READY_TO_NULL:
1776 gst_xvimagesink_reset (xvimagesink);
1787 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1788 GstClockTime * start, GstClockTime * end)
1790 GstXvImageSink *xvimagesink;
1792 xvimagesink = GST_XVIMAGESINK (bsink);
1794 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1795 *start = GST_BUFFER_TIMESTAMP (buf);
1796 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1797 *end = *start + GST_BUFFER_DURATION (buf);
1799 if (xvimagesink->fps_n > 0) {
1801 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1802 xvimagesink->fps_n);
1808 static GstFlowReturn
1809 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1812 GstXvImageSink *xvimagesink;
1813 GstXvImageMeta *meta;
1816 xvimagesink = GST_XVIMAGESINK (vsink);
1818 meta = gst_buffer_get_xvimage_meta (buf);
1820 if (meta && meta->sink == xvimagesink) {
1821 /* If this buffer has been allocated using our buffer management we simply
1822 put the ximage which is in the PRIVATE pointer */
1823 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1828 GstVideoFrame src, dest;
1830 /* Else we have to copy the data into our private image, */
1831 /* if we have one... */
1832 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1834 /* we should have a pool, configured in setcaps */
1835 if (xvimagesink->pool == NULL)
1838 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1839 goto activate_failed;
1841 /* take a buffer form our pool */
1842 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1843 if (res != GST_FLOW_OK)
1846 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1849 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1850 "slow copy into bufferpool buffer %p", to_put);
1852 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1853 goto invalid_buffer;
1855 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1856 gst_video_frame_unmap (&src);
1857 goto invalid_buffer;
1860 gst_video_frame_copy (&dest, &src);
1862 gst_video_frame_unmap (&dest);
1863 gst_video_frame_unmap (&src);
1866 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1871 gst_buffer_unref (to_put);
1878 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1879 ("Internal error: can't allocate images"),
1880 ("We don't have a bufferpool negotiated"));
1881 return GST_FLOW_ERROR;
1885 /* No image available. That's very bad ! */
1886 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1891 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1892 ("Failed to create output image buffer"),
1893 ("XServer allocated buffer size did not match input buffer %"
1894 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1895 gst_buffer_get_size (buf)));
1896 res = GST_FLOW_ERROR;
1901 /* No Window available to put our image into */
1902 GST_WARNING_OBJECT (xvimagesink, "could map image");
1908 /* No Window available to put our image into */
1909 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1910 res = GST_FLOW_ERROR;
1915 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1916 res = GST_FLOW_ERROR;
1922 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1924 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1926 switch (GST_EVENT_TYPE (event)) {
1927 case GST_EVENT_TAG:{
1929 gchar *title = NULL;
1931 gst_event_parse_tag (event, &l);
1932 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1935 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1936 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1946 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1950 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1952 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1953 GstBufferPool *pool;
1954 GstStructure *config;
1959 gst_query_parse_allocation (query, &caps, &need_pool);
1964 g_mutex_lock (xvimagesink->flow_lock);
1965 if ((pool = xvimagesink->pool))
1966 gst_object_ref (pool);
1967 g_mutex_unlock (xvimagesink->flow_lock);
1970 const GstCaps *pcaps;
1972 /* we had a pool, check caps */
1973 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1974 config = gst_buffer_pool_get_config (pool);
1975 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1977 if (!gst_caps_is_equal (caps, pcaps)) {
1978 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1979 /* different caps, we can't use this pool */
1980 gst_object_unref (pool);
1984 if (pool == NULL && need_pool) {
1987 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1988 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1990 if (!gst_video_info_from_caps (&info, caps))
1993 /* the normal size of a frame */
1996 config = gst_buffer_pool_get_config (pool);
1997 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1998 if (!gst_buffer_pool_set_config (pool, config))
2001 /* we need at least 2 buffer because we hold on to the last one */
2002 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
2004 /* we also support various metadata */
2005 gst_query_add_allocation_meta (query, GST_VIDEO_META_API);
2006 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API);
2008 gst_object_unref (pool);
2015 GST_DEBUG_OBJECT (bsink, "no caps specified");
2020 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2025 GST_DEBUG_OBJECT (bsink, "failed setting config");
2030 /* Interfaces stuff */
2032 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2033 GstStructure * structure)
2035 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2038 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2040 GstVideoRectangle src, dst, result;
2041 gdouble x, y, xscale = 1.0, yscale = 1.0;
2043 event = gst_event_new_navigation (structure);
2045 /* We take the flow_lock while we look at the window */
2046 g_mutex_lock (xvimagesink->flow_lock);
2048 if (!xvimagesink->xwindow) {
2049 g_mutex_unlock (xvimagesink->flow_lock);
2053 if (xvimagesink->keep_aspect) {
2054 /* We get the frame position using the calculated geometry from _setcaps
2055 that respect pixel aspect ratios */
2056 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2057 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2058 dst.w = xvimagesink->render_rect.w;
2059 dst.h = xvimagesink->render_rect.h;
2061 gst_video_sink_center_rect (src, dst, &result, TRUE);
2062 result.x += xvimagesink->render_rect.x;
2063 result.y += xvimagesink->render_rect.y;
2065 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2068 g_mutex_unlock (xvimagesink->flow_lock);
2070 /* We calculate scaling using the original video frames geometry to include
2071 pixel aspect ratio scaling. */
2072 xscale = (gdouble) xvimagesink->video_width / result.w;
2073 yscale = (gdouble) xvimagesink->video_height / result.h;
2075 /* Converting pointer coordinates to the non scaled geometry */
2076 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2077 x = MIN (x, result.x + result.w);
2078 x = MAX (x - result.x, 0);
2079 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2080 (gdouble) x * xscale, NULL);
2082 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2083 y = MIN (y, result.y + result.h);
2084 y = MAX (y - result.y, 0);
2085 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2086 (gdouble) y * yscale, NULL);
2089 gst_pad_send_event (peer, event);
2090 gst_object_unref (peer);
2095 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2097 iface->send_event = gst_xvimagesink_navigation_send_event;
2101 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2103 XID xwindow_id = id;
2104 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2105 GstXWindow *xwindow = NULL;
2107 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2109 g_mutex_lock (xvimagesink->flow_lock);
2111 /* If we already use that window return */
2112 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2113 g_mutex_unlock (xvimagesink->flow_lock);
2117 /* If the element has not initialized the X11 context try to do so */
2118 if (!xvimagesink->xcontext &&
2119 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2120 g_mutex_unlock (xvimagesink->flow_lock);
2121 /* we have thrown a GST_ELEMENT_ERROR now */
2125 gst_xvimagesink_update_colorbalance (xvimagesink);
2127 /* If a window is there already we destroy it */
2128 if (xvimagesink->xwindow) {
2129 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2130 xvimagesink->xwindow = NULL;
2133 /* If the xid is 0 we go back to an internal window */
2134 if (xwindow_id == 0) {
2135 /* If no width/height caps nego did not happen window will be created
2136 during caps nego then */
2137 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2138 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2140 gst_xvimagesink_xwindow_new (xvimagesink,
2141 GST_VIDEO_SINK_WIDTH (xvimagesink),
2142 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2145 XWindowAttributes attr;
2147 xwindow = g_new0 (GstXWindow, 1);
2148 xwindow->win = xwindow_id;
2150 /* Set the event we want to receive and create a GC */
2151 g_mutex_lock (xvimagesink->x_lock);
2153 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2155 xwindow->width = attr.width;
2156 xwindow->height = attr.height;
2157 xwindow->internal = FALSE;
2158 if (!xvimagesink->have_render_rect) {
2159 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2160 xvimagesink->render_rect.w = attr.width;
2161 xvimagesink->render_rect.h = attr.height;
2163 if (xvimagesink->handle_events) {
2164 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2165 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2169 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2170 xwindow->win, 0, NULL);
2171 g_mutex_unlock (xvimagesink->x_lock);
2175 xvimagesink->xwindow = xwindow;
2177 g_mutex_unlock (xvimagesink->flow_lock);
2181 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2183 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2185 GST_DEBUG ("doing expose");
2186 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2187 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2191 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2192 gboolean handle_events)
2194 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2196 xvimagesink->handle_events = handle_events;
2198 g_mutex_lock (xvimagesink->flow_lock);
2200 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2201 g_mutex_unlock (xvimagesink->flow_lock);
2205 g_mutex_lock (xvimagesink->x_lock);
2207 if (handle_events) {
2208 if (xvimagesink->xwindow->internal) {
2209 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2210 ExposureMask | StructureNotifyMask | PointerMotionMask |
2211 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2213 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2214 ExposureMask | StructureNotifyMask | PointerMotionMask |
2215 KeyPressMask | KeyReleaseMask);
2218 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2221 g_mutex_unlock (xvimagesink->x_lock);
2223 g_mutex_unlock (xvimagesink->flow_lock);
2227 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2228 gint width, gint height)
2230 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2232 /* FIXME: how about some locking? */
2233 if (width >= 0 && height >= 0) {
2234 xvimagesink->render_rect.x = x;
2235 xvimagesink->render_rect.y = y;
2236 xvimagesink->render_rect.w = width;
2237 xvimagesink->render_rect.h = height;
2238 xvimagesink->have_render_rect = TRUE;
2240 xvimagesink->render_rect.x = 0;
2241 xvimagesink->render_rect.y = 0;
2242 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2243 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2244 xvimagesink->have_render_rect = FALSE;
2249 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2251 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2252 iface->expose = gst_xvimagesink_expose;
2253 iface->handle_events = gst_xvimagesink_set_event_handling;
2254 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2257 static const GList *
2258 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2260 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2262 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2264 if (xvimagesink->xcontext)
2265 return xvimagesink->xcontext->channels_list;
2271 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2272 GstColorBalanceChannel * channel, gint value)
2274 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2276 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2277 g_return_if_fail (channel->label != NULL);
2279 xvimagesink->cb_changed = TRUE;
2281 /* Normalize val to [-1000, 1000] */
2282 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2283 (double) (channel->max_value - channel->min_value));
2285 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2286 xvimagesink->hue = value;
2287 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2288 xvimagesink->saturation = value;
2289 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2290 xvimagesink->contrast = value;
2291 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2292 xvimagesink->brightness = value;
2294 g_warning ("got an unknown channel %s", channel->label);
2298 gst_xvimagesink_update_colorbalance (xvimagesink);
2302 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2303 GstColorBalanceChannel * channel)
2305 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2308 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2309 g_return_val_if_fail (channel->label != NULL, 0);
2311 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2312 value = xvimagesink->hue;
2313 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2314 value = xvimagesink->saturation;
2315 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2316 value = xvimagesink->contrast;
2317 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2318 value = xvimagesink->brightness;
2320 g_warning ("got an unknown channel %s", channel->label);
2323 /* Normalize val to [channel->min_value, channel->max_value] */
2324 value = channel->min_value + (channel->max_value - channel->min_value) *
2325 (value + 1000) / 2000;
2331 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2333 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2334 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2335 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2336 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2339 static const GList *
2340 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2342 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2343 static GList *list = NULL;
2346 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2348 g_list_append (list, g_object_class_find_property (klass,
2349 "autopaint-colorkey"));
2351 g_list_append (list, g_object_class_find_property (klass,
2354 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2361 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2362 guint prop_id, const GParamSpec * pspec)
2364 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2368 case PROP_AUTOPAINT_COLORKEY:
2369 case PROP_DOUBLE_BUFFER:
2371 GST_DEBUG_OBJECT (xvimagesink,
2372 "probing device list and get capabilities");
2373 if (!xvimagesink->xcontext) {
2374 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2375 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2379 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2385 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2386 guint prop_id, const GParamSpec * pspec)
2388 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2389 gboolean ret = FALSE;
2393 case PROP_AUTOPAINT_COLORKEY:
2394 case PROP_DOUBLE_BUFFER:
2396 if (xvimagesink->xcontext != NULL) {
2403 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2410 static GValueArray *
2411 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2412 guint prop_id, const GParamSpec * pspec)
2414 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2415 GValueArray *array = NULL;
2417 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2418 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2427 GValue value = { 0 };
2429 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2430 g_value_init (&value, G_TYPE_STRING);
2432 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2433 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2435 g_value_set_string (&value, adaptor_id_s);
2436 g_value_array_append (array, &value);
2437 g_free (adaptor_id_s);
2439 g_value_unset (&value);
2442 case PROP_AUTOPAINT_COLORKEY:
2443 if (xvimagesink->have_autopaint_colorkey) {
2444 GValue value = { 0 };
2446 array = g_value_array_new (2);
2447 g_value_init (&value, G_TYPE_BOOLEAN);
2448 g_value_set_boolean (&value, FALSE);
2449 g_value_array_append (array, &value);
2450 g_value_set_boolean (&value, TRUE);
2451 g_value_array_append (array, &value);
2452 g_value_unset (&value);
2455 case PROP_DOUBLE_BUFFER:
2456 if (xvimagesink->have_double_buffer) {
2457 GValue value = { 0 };
2459 array = g_value_array_new (2);
2460 g_value_init (&value, G_TYPE_BOOLEAN);
2461 g_value_set_boolean (&value, FALSE);
2462 g_value_array_append (array, &value);
2463 g_value_set_boolean (&value, TRUE);
2464 g_value_array_append (array, &value);
2465 g_value_unset (&value);
2469 if (xvimagesink->have_colorkey) {
2470 GValue value = { 0 };
2472 array = g_value_array_new (1);
2473 g_value_init (&value, GST_TYPE_INT_RANGE);
2474 gst_value_set_int_range (&value, 0, 0xffffff);
2475 g_value_array_append (array, &value);
2476 g_value_unset (&value);
2480 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2489 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2492 iface->get_properties = gst_xvimagesink_probe_get_properties;
2493 iface->probe_property = gst_xvimagesink_probe_probe_property;
2494 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2495 iface->get_values = gst_xvimagesink_probe_get_values;
2498 /* =========================================== */
2500 /* Init & Class init */
2502 /* =========================================== */
2505 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2506 const GValue * value, GParamSpec * pspec)
2508 GstXvImageSink *xvimagesink;
2510 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2512 xvimagesink = GST_XVIMAGESINK (object);
2516 xvimagesink->hue = g_value_get_int (value);
2517 xvimagesink->cb_changed = TRUE;
2518 gst_xvimagesink_update_colorbalance (xvimagesink);
2521 xvimagesink->contrast = g_value_get_int (value);
2522 xvimagesink->cb_changed = TRUE;
2523 gst_xvimagesink_update_colorbalance (xvimagesink);
2525 case PROP_BRIGHTNESS:
2526 xvimagesink->brightness = g_value_get_int (value);
2527 xvimagesink->cb_changed = TRUE;
2528 gst_xvimagesink_update_colorbalance (xvimagesink);
2530 case PROP_SATURATION:
2531 xvimagesink->saturation = g_value_get_int (value);
2532 xvimagesink->cb_changed = TRUE;
2533 gst_xvimagesink_update_colorbalance (xvimagesink);
2536 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2538 case PROP_SYNCHRONOUS:
2539 xvimagesink->synchronous = g_value_get_boolean (value);
2540 if (xvimagesink->xcontext) {
2541 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2542 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2543 xvimagesink->synchronous ? "TRUE" : "FALSE");
2546 case PROP_PIXEL_ASPECT_RATIO:
2547 g_free (xvimagesink->par);
2548 xvimagesink->par = g_new0 (GValue, 1);
2549 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2550 if (!g_value_transform (value, xvimagesink->par)) {
2551 g_warning ("Could not transform string to aspect ratio");
2552 gst_value_set_fraction (xvimagesink->par, 1, 1);
2554 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2555 gst_value_get_fraction_numerator (xvimagesink->par),
2556 gst_value_get_fraction_denominator (xvimagesink->par));
2558 case PROP_FORCE_ASPECT_RATIO:
2559 xvimagesink->keep_aspect = g_value_get_boolean (value);
2561 case PROP_HANDLE_EVENTS:
2562 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2563 g_value_get_boolean (value));
2564 gst_xvimagesink_manage_event_thread (xvimagesink);
2567 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2569 case PROP_HANDLE_EXPOSE:
2570 xvimagesink->handle_expose = g_value_get_boolean (value);
2571 gst_xvimagesink_manage_event_thread (xvimagesink);
2573 case PROP_DOUBLE_BUFFER:
2574 xvimagesink->double_buffer = g_value_get_boolean (value);
2576 case PROP_AUTOPAINT_COLORKEY:
2577 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2580 xvimagesink->colorkey = g_value_get_int (value);
2582 case PROP_DRAW_BORDERS:
2583 xvimagesink->draw_borders = g_value_get_boolean (value);
2586 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2592 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2593 GValue * value, GParamSpec * pspec)
2595 GstXvImageSink *xvimagesink;
2597 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2599 xvimagesink = GST_XVIMAGESINK (object);
2603 g_value_set_int (value, xvimagesink->hue);
2606 g_value_set_int (value, xvimagesink->contrast);
2608 case PROP_BRIGHTNESS:
2609 g_value_set_int (value, xvimagesink->brightness);
2611 case PROP_SATURATION:
2612 g_value_set_int (value, xvimagesink->saturation);
2615 g_value_set_string (value, xvimagesink->display_name);
2617 case PROP_SYNCHRONOUS:
2618 g_value_set_boolean (value, xvimagesink->synchronous);
2620 case PROP_PIXEL_ASPECT_RATIO:
2621 if (xvimagesink->par)
2622 g_value_transform (xvimagesink->par, value);
2624 case PROP_FORCE_ASPECT_RATIO:
2625 g_value_set_boolean (value, xvimagesink->keep_aspect);
2627 case PROP_HANDLE_EVENTS:
2628 g_value_set_boolean (value, xvimagesink->handle_events);
2632 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2634 g_value_set_string (value, adaptor_no_s);
2635 g_free (adaptor_no_s);
2638 case PROP_DEVICE_NAME:
2639 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2640 g_value_set_string (value,
2641 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2643 g_value_set_string (value, NULL);
2646 case PROP_HANDLE_EXPOSE:
2647 g_value_set_boolean (value, xvimagesink->handle_expose);
2649 case PROP_DOUBLE_BUFFER:
2650 g_value_set_boolean (value, xvimagesink->double_buffer);
2652 case PROP_AUTOPAINT_COLORKEY:
2653 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2656 g_value_set_int (value, xvimagesink->colorkey);
2658 case PROP_DRAW_BORDERS:
2659 g_value_set_boolean (value, xvimagesink->draw_borders);
2661 case PROP_WINDOW_WIDTH:
2662 if (xvimagesink->xwindow)
2663 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2665 g_value_set_uint64 (value, 0);
2667 case PROP_WINDOW_HEIGHT:
2668 if (xvimagesink->xwindow)
2669 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2671 g_value_set_uint64 (value, 0);
2674 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2680 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2684 GST_OBJECT_LOCK (xvimagesink);
2685 xvimagesink->running = FALSE;
2686 /* grab thread and mark it as NULL */
2687 thread = xvimagesink->event_thread;
2688 xvimagesink->event_thread = NULL;
2689 GST_OBJECT_UNLOCK (xvimagesink);
2691 /* Wait for our event thread to finish before we clean up our stuff. */
2693 g_thread_join (thread);
2695 if (xvimagesink->cur_image) {
2696 gst_buffer_unref (xvimagesink->cur_image);
2697 xvimagesink->cur_image = NULL;
2700 g_mutex_lock (xvimagesink->flow_lock);
2702 if (xvimagesink->pool) {
2703 gst_object_unref (xvimagesink->pool);
2704 xvimagesink->pool = NULL;
2707 if (xvimagesink->xwindow) {
2708 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2709 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2710 xvimagesink->xwindow = NULL;
2712 g_mutex_unlock (xvimagesink->flow_lock);
2714 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2715 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2716 xvimagesink->have_render_rect = FALSE;
2718 gst_xvimagesink_xcontext_clear (xvimagesink);
2721 /* Finalize is called only once, dispose can be called multiple times.
2722 * We use mutexes and don't reset stuff to NULL here so let's register
2725 gst_xvimagesink_finalize (GObject * object)
2727 GstXvImageSink *xvimagesink;
2729 xvimagesink = GST_XVIMAGESINK (object);
2731 gst_xvimagesink_reset (xvimagesink);
2733 if (xvimagesink->display_name) {
2734 g_free (xvimagesink->display_name);
2735 xvimagesink->display_name = NULL;
2738 if (xvimagesink->par) {
2739 g_free (xvimagesink->par);
2740 xvimagesink->par = NULL;
2742 if (xvimagesink->x_lock) {
2743 g_mutex_free (xvimagesink->x_lock);
2744 xvimagesink->x_lock = NULL;
2746 if (xvimagesink->flow_lock) {
2747 g_mutex_free (xvimagesink->flow_lock);
2748 xvimagesink->flow_lock = NULL;
2751 g_free (xvimagesink->media_title);
2753 G_OBJECT_CLASS (parent_class)->finalize (object);
2757 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2759 xvimagesink->display_name = NULL;
2760 xvimagesink->adaptor_no = 0;
2761 xvimagesink->xcontext = NULL;
2762 xvimagesink->xwindow = NULL;
2763 xvimagesink->cur_image = NULL;
2765 xvimagesink->hue = xvimagesink->saturation = 0;
2766 xvimagesink->contrast = xvimagesink->brightness = 0;
2767 xvimagesink->cb_changed = FALSE;
2769 xvimagesink->fps_n = 0;
2770 xvimagesink->fps_d = 0;
2771 xvimagesink->video_width = 0;
2772 xvimagesink->video_height = 0;
2774 xvimagesink->x_lock = g_mutex_new ();
2775 xvimagesink->flow_lock = g_mutex_new ();
2777 xvimagesink->pool = NULL;
2779 xvimagesink->synchronous = FALSE;
2780 xvimagesink->double_buffer = TRUE;
2781 xvimagesink->running = FALSE;
2782 xvimagesink->keep_aspect = FALSE;
2783 xvimagesink->handle_events = TRUE;
2784 xvimagesink->par = NULL;
2785 xvimagesink->handle_expose = TRUE;
2786 xvimagesink->autopaint_colorkey = TRUE;
2788 /* on 16bit displays this becomes r,g,b = 1,2,3
2789 * on 24bit displays this becomes r,g,b = 8,8,16
2790 * as a port atom value
2792 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2793 xvimagesink->draw_borders = TRUE;
2797 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2799 GObjectClass *gobject_class;
2800 GstElementClass *gstelement_class;
2801 GstBaseSinkClass *gstbasesink_class;
2802 GstVideoSinkClass *videosink_class;
2804 gobject_class = (GObjectClass *) klass;
2805 gstelement_class = (GstElementClass *) klass;
2806 gstbasesink_class = (GstBaseSinkClass *) klass;
2807 videosink_class = (GstVideoSinkClass *) klass;
2809 parent_class = g_type_class_peek_parent (klass);
2811 gobject_class->set_property = gst_xvimagesink_set_property;
2812 gobject_class->get_property = gst_xvimagesink_get_property;
2814 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2815 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2816 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2817 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2818 g_param_spec_int ("brightness", "Brightness",
2819 "The brightness of the video", -1000, 1000, 0,
2820 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2821 g_object_class_install_property (gobject_class, PROP_HUE,
2822 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2823 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2824 g_object_class_install_property (gobject_class, PROP_SATURATION,
2825 g_param_spec_int ("saturation", "Saturation",
2826 "The saturation of the video", -1000, 1000, 0,
2827 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2828 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2829 g_param_spec_string ("display", "Display", "X Display name", NULL,
2830 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2831 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2832 g_param_spec_boolean ("synchronous", "Synchronous",
2833 "When enabled, runs the X display in synchronous mode. "
2834 "(unrelated to A/V sync, used only for debugging)", FALSE,
2835 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2836 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2837 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2838 "The pixel aspect ratio of the device", "1/1",
2839 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2840 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2841 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2842 "When enabled, scaling will respect original aspect ratio", FALSE,
2843 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2844 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2845 g_param_spec_boolean ("handle-events", "Handle XEvents",
2846 "When enabled, XEvents will be selected and handled", TRUE,
2847 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2848 g_object_class_install_property (gobject_class, PROP_DEVICE,
2849 g_param_spec_string ("device", "Adaptor number",
2850 "The number of the video adaptor", "0",
2851 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2852 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2853 g_param_spec_string ("device-name", "Adaptor name",
2854 "The name of the video adaptor", NULL,
2855 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2857 * GstXvImageSink:handle-expose
2859 * When enabled, the current frame will always be drawn in response to X
2864 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2865 g_param_spec_boolean ("handle-expose", "Handle expose",
2867 "the current frame will always be drawn in response to X Expose "
2868 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2871 * GstXvImageSink:double-buffer
2873 * Whether to double-buffer the output.
2877 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2878 g_param_spec_boolean ("double-buffer", "Double-buffer",
2879 "Whether to double-buffer the output", TRUE,
2880 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2882 * GstXvImageSink:autopaint-colorkey
2884 * Whether to autofill overlay with colorkey
2888 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2889 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2890 "Whether to autofill overlay with colorkey", TRUE,
2891 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2893 * GstXvImageSink:colorkey
2895 * Color to use for the overlay mask.
2899 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2900 g_param_spec_int ("colorkey", "Colorkey",
2901 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2902 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2905 * GstXvImageSink:draw-borders
2907 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2908 * unused parts of the video area.
2912 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2913 g_param_spec_boolean ("draw-borders", "Colorkey",
2914 "Draw black borders to fill unused area in force-aspect-ratio mode",
2915 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2918 * GstXvImageSink:window-width
2920 * Actual width of the video window.
2924 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2925 g_param_spec_uint64 ("window-width", "window-width",
2926 "Width of the window", 0, G_MAXUINT64, 0,
2927 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2930 * GstXvImageSink:window-height
2932 * Actual height of the video window.
2936 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2937 g_param_spec_uint64 ("window-height", "window-height",
2938 "Height of the window", 0, G_MAXUINT64, 0,
2939 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2941 gobject_class->finalize = gst_xvimagesink_finalize;
2943 gst_element_class_set_details_simple (gstelement_class,
2944 "Video sink", "Sink/Video",
2945 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2947 gst_element_class_add_pad_template (gstelement_class,
2948 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2950 gstelement_class->change_state =
2951 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2953 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2954 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2955 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2956 gstbasesink_class->propose_allocation =
2957 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2958 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2960 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);