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/gstvideometa.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 (GstVideoOverlayInterface *
199 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
202 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
204 #define gst_xvimagesink_parent_class parent_class
205 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
206 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
207 gst_xvimagesink_navigation_init);
208 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
209 gst_xvimagesink_video_overlay_init);
210 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
211 gst_xvimagesink_colorbalance_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_PROPERTY_PROBE,
213 gst_xvimagesink_property_probe_interface_init));
216 /* ============================================================= */
218 /* Private Methods */
220 /* ============================================================= */
223 /* We are called with the x_lock taken */
225 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
226 GstXWindow * xwindow, GstVideoRectangle rect)
230 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
231 g_return_if_fail (xwindow != NULL);
233 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
234 xvimagesink->xcontext->black);
237 if (rect.x > xvimagesink->render_rect.x) {
238 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
239 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
240 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
244 t1 = rect.x + rect.w;
245 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
247 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
248 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
252 if (rect.y > xvimagesink->render_rect.y) {
253 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
254 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
255 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
259 t1 = rect.y + rect.h;
260 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
262 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
263 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
267 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
268 * if no window was available */
270 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
272 GstXvImageMeta *meta;
273 GstVideoCropMeta *crop;
274 GstVideoRectangle result;
275 gboolean draw_border = FALSE;
276 GstVideoRectangle src, dst;
278 /* We take the flow_lock. If expose is in there we don't want to run
279 concurrently from the data flow thread */
280 g_mutex_lock (xvimagesink->flow_lock);
282 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
283 g_mutex_unlock (xvimagesink->flow_lock);
287 /* Draw borders when displaying the first frame. After this
288 draw borders only on expose event or after a size change. */
289 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
293 /* Store a reference to the last image we put, lose the previous one */
294 if (xvimage && xvimagesink->cur_image != xvimage) {
295 if (xvimagesink->cur_image) {
296 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
297 gst_buffer_unref (xvimagesink->cur_image);
299 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
300 xvimagesink->cur_image = gst_buffer_ref (xvimage);
303 /* Expose sends a NULL image, we take the latest frame */
305 if (xvimagesink->cur_image) {
307 xvimage = xvimagesink->cur_image;
309 g_mutex_unlock (xvimagesink->flow_lock);
314 meta = gst_buffer_get_xvimage_meta (xvimage);
316 crop = gst_buffer_get_video_crop_meta (xvimage);
319 src.x = crop->x + meta->x;
320 src.y = crop->y + meta->y;
322 src.h = crop->height;
327 src.h = meta->height;
330 if (xvimagesink->keep_aspect) {
331 dst.w = xvimagesink->render_rect.w;
332 dst.h = xvimagesink->render_rect.h;
334 gst_video_sink_center_rect (src, dst, &result, TRUE);
335 result.x += xvimagesink->render_rect.x;
336 result.y += xvimagesink->render_rect.y;
338 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
341 g_mutex_lock (xvimagesink->x_lock);
343 if (draw_border && xvimagesink->draw_borders) {
344 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
346 xvimagesink->redraw_border = FALSE;
349 if (xvimagesink->xcontext->use_xshm) {
350 GST_LOG_OBJECT (xvimagesink,
351 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
352 GST_PTR_FORMAT, meta->width, meta->height,
353 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
355 XvShmPutImage (xvimagesink->xcontext->disp,
356 xvimagesink->xcontext->xv_port_id,
357 xvimagesink->xwindow->win,
358 xvimagesink->xwindow->gc, meta->xvimage,
359 src.x, src.y, src.w, src.h,
360 result.x, result.y, result.w, result.h, FALSE);
362 #endif /* HAVE_XSHM */
364 XvPutImage (xvimagesink->xcontext->disp,
365 xvimagesink->xcontext->xv_port_id,
366 xvimagesink->xwindow->win,
367 xvimagesink->xwindow->gc, meta->xvimage,
368 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
371 XSync (xvimagesink->xcontext->disp, FALSE);
373 g_mutex_unlock (xvimagesink->x_lock);
375 g_mutex_unlock (xvimagesink->flow_lock);
381 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
384 Atom hints_atom = None;
387 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
388 g_return_val_if_fail (window != NULL, FALSE);
390 g_mutex_lock (xvimagesink->x_lock);
392 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
394 if (hints_atom == None) {
395 g_mutex_unlock (xvimagesink->x_lock);
399 hints = g_malloc0 (sizeof (MotifWmHints));
401 hints->flags |= MWM_HINTS_DECORATIONS;
402 hints->decorations = 1 << 0;
404 XChangeProperty (xvimagesink->xcontext->disp, window->win,
405 hints_atom, hints_atom, 32, PropModeReplace,
406 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
408 XSync (xvimagesink->xcontext->disp, FALSE);
410 g_mutex_unlock (xvimagesink->x_lock);
418 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
419 GstXWindow * xwindow, const gchar * media_title)
422 g_free (xvimagesink->media_title);
423 xvimagesink->media_title = g_strdup (media_title);
426 /* we have a window */
427 if (xwindow->internal) {
428 XTextProperty xproperty;
429 const gchar *app_name;
430 const gchar *title = NULL;
431 gchar *title_mem = NULL;
433 /* set application name as a title */
434 app_name = g_get_application_name ();
436 if (app_name && xvimagesink->media_title) {
437 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
439 } else if (app_name) {
441 } else if (xvimagesink->media_title) {
442 title = xvimagesink->media_title;
446 if ((XStringListToTextProperty (((char **) &title), 1,
448 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
449 XFree (xproperty.value);
458 /* This function handles a GstXWindow creation
459 * The width and height are the actual pixel size on the display */
461 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
462 gint width, gint height)
464 GstXWindow *xwindow = NULL;
467 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
469 xwindow = g_new0 (GstXWindow, 1);
471 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
472 xvimagesink->render_rect.w = width;
473 xvimagesink->render_rect.h = height;
475 xwindow->width = width;
476 xwindow->height = height;
477 xwindow->internal = TRUE;
479 g_mutex_lock (xvimagesink->x_lock);
481 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
482 xvimagesink->xcontext->root,
483 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
485 /* We have to do that to prevent X from redrawing the background on
486 * ConfigureNotify. This takes away flickering of video when resizing. */
487 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
489 /* set application name as a title */
490 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
492 if (xvimagesink->handle_events) {
495 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
496 StructureNotifyMask | PointerMotionMask | KeyPressMask |
497 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
499 /* Tell the window manager we'd like delete client messages instead of
501 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
502 "WM_DELETE_WINDOW", True);
503 if (wm_delete != None) {
504 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
509 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
510 xwindow->win, 0, &values);
512 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
514 XSync (xvimagesink->xcontext->disp, FALSE);
516 g_mutex_unlock (xvimagesink->x_lock);
518 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
520 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
526 /* This function destroys a GstXWindow */
528 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
529 GstXWindow * xwindow)
531 g_return_if_fail (xwindow != NULL);
532 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
534 g_mutex_lock (xvimagesink->x_lock);
536 /* If we did not create that window we just free the GC and let it live */
537 if (xwindow->internal)
538 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
540 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
542 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
544 XSync (xvimagesink->xcontext->disp, FALSE);
546 g_mutex_unlock (xvimagesink->x_lock);
552 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
554 XWindowAttributes attr;
556 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
558 /* Update the window geometry */
559 g_mutex_lock (xvimagesink->x_lock);
560 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
561 g_mutex_unlock (xvimagesink->x_lock);
565 XGetWindowAttributes (xvimagesink->xcontext->disp,
566 xvimagesink->xwindow->win, &attr);
568 xvimagesink->xwindow->width = attr.width;
569 xvimagesink->xwindow->height = attr.height;
571 if (!xvimagesink->have_render_rect) {
572 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
573 xvimagesink->render_rect.w = attr.width;
574 xvimagesink->render_rect.h = attr.height;
577 g_mutex_unlock (xvimagesink->x_lock);
581 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
582 GstXWindow * xwindow)
584 g_return_if_fail (xwindow != NULL);
585 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
587 g_mutex_lock (xvimagesink->x_lock);
589 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
592 XSync (xvimagesink->xcontext->disp, FALSE);
594 g_mutex_unlock (xvimagesink->x_lock);
597 /* This function commits our internal colorbalance settings to our grabbed Xv
598 port. If the xcontext is not initialized yet it simply returns */
600 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
602 GList *channels = NULL;
604 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
606 /* If we haven't initialized the X context we can't update anything */
607 if (xvimagesink->xcontext == NULL)
610 /* Don't set the attributes if they haven't been changed, to avoid
611 * rounding errors changing the values */
612 if (!xvimagesink->cb_changed)
615 /* For each channel of the colorbalance we calculate the correct value
616 doing range conversion and then set the Xv port attribute to match our
618 channels = xvimagesink->xcontext->channels_list;
621 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
622 GstColorBalanceChannel *channel = NULL;
625 gdouble convert_coef;
627 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
628 g_object_ref (channel);
630 /* Our range conversion coef */
631 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
633 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
634 value = xvimagesink->hue;
635 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
636 value = xvimagesink->saturation;
637 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
638 value = xvimagesink->contrast;
639 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
640 value = xvimagesink->brightness;
642 g_warning ("got an unknown channel %s", channel->label);
643 g_object_unref (channel);
647 /* Committing to Xv port */
648 g_mutex_lock (xvimagesink->x_lock);
650 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
651 if (prop_atom != None) {
654 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
655 XvSetPortAttribute (xvimagesink->xcontext->disp,
656 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
658 g_mutex_unlock (xvimagesink->x_lock);
660 g_object_unref (channel);
662 channels = g_list_next (channels);
666 /* This function handles XEvents that might be in the queue. It generates
667 GstEvent that will be sent upstream in the pipeline to handle interactivity
668 and navigation. It will also listen for configure events on the window to
669 trigger caps renegotiation so on the fly software scaling can work. */
671 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
674 guint pointer_x = 0, pointer_y = 0;
675 gboolean pointer_moved = FALSE;
676 gboolean exposed = FALSE, configured = FALSE;
678 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
680 /* Handle Interaction, produces navigation events */
682 /* We get all pointer motion events, only the last position is
684 g_mutex_lock (xvimagesink->flow_lock);
685 g_mutex_lock (xvimagesink->x_lock);
686 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
687 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
688 g_mutex_unlock (xvimagesink->x_lock);
689 g_mutex_unlock (xvimagesink->flow_lock);
693 pointer_x = e.xmotion.x;
694 pointer_y = e.xmotion.y;
695 pointer_moved = TRUE;
700 g_mutex_lock (xvimagesink->flow_lock);
701 g_mutex_lock (xvimagesink->x_lock);
705 g_mutex_unlock (xvimagesink->x_lock);
706 g_mutex_unlock (xvimagesink->flow_lock);
708 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
709 pointer_x, pointer_y);
710 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
711 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
713 g_mutex_lock (xvimagesink->flow_lock);
714 g_mutex_lock (xvimagesink->x_lock);
717 /* We get all events on our window to throw them upstream */
718 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
719 xvimagesink->xwindow->win,
720 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
724 /* We lock only for the X function call */
725 g_mutex_unlock (xvimagesink->x_lock);
726 g_mutex_unlock (xvimagesink->flow_lock);
730 /* Mouse button pressed over our window. We send upstream
731 events for interactivity/navigation */
732 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
733 e.xbutton.button, e.xbutton.x, e.xbutton.y);
734 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
735 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
738 /* Mouse button released over our window. We send upstream
739 events for interactivity/navigation */
740 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
741 e.xbutton.button, e.xbutton.x, e.xbutton.y);
742 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
743 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
747 /* Key pressed/released over our window. We send upstream
748 events for interactivity/navigation */
749 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
750 e.xkey.keycode, e.xkey.x, e.xkey.y);
751 g_mutex_lock (xvimagesink->x_lock);
752 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
754 g_mutex_unlock (xvimagesink->x_lock);
755 if (keysym != NoSymbol) {
756 char *key_str = NULL;
758 g_mutex_lock (xvimagesink->x_lock);
759 key_str = XKeysymToString (keysym);
760 g_mutex_unlock (xvimagesink->x_lock);
761 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
762 e.type == KeyPress ? "key-press" : "key-release", key_str);
764 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
765 e.type == KeyPress ? "key-press" : "key-release", "unknown");
769 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
772 g_mutex_lock (xvimagesink->flow_lock);
773 g_mutex_lock (xvimagesink->x_lock);
777 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
778 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
783 case ConfigureNotify:
784 g_mutex_unlock (xvimagesink->x_lock);
785 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
786 g_mutex_lock (xvimagesink->x_lock);
794 if (xvimagesink->handle_expose && (exposed || configured)) {
795 g_mutex_unlock (xvimagesink->x_lock);
796 g_mutex_unlock (xvimagesink->flow_lock);
798 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
800 g_mutex_lock (xvimagesink->flow_lock);
801 g_mutex_lock (xvimagesink->x_lock);
804 /* Handle Display events */
805 while (XPending (xvimagesink->xcontext->disp)) {
806 XNextEvent (xvimagesink->xcontext->disp, &e);
812 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
813 "WM_DELETE_WINDOW", True);
814 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
815 /* Handle window deletion by posting an error on the bus */
816 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
817 ("Output window was closed"), (NULL));
819 g_mutex_unlock (xvimagesink->x_lock);
820 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
821 xvimagesink->xwindow = NULL;
822 g_mutex_lock (xvimagesink->x_lock);
831 g_mutex_unlock (xvimagesink->x_lock);
832 g_mutex_unlock (xvimagesink->flow_lock);
836 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
837 XvAdaptorInfo * adaptors, int adaptor_no)
842 /* Do we support XvImageMask ? */
843 if (!(adaptors[adaptor_no].type & XvImageMask)) {
844 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
845 adaptors[adaptor_no].name);
849 /* We found such an adaptor, looking for an available port */
850 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
851 /* We try to grab the port */
852 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
853 if (Success == res) {
854 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
855 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
856 adaptors[adaptor_no].num_ports);
858 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
859 adaptors[adaptor_no].name, res);
864 /* This function generates a caps with all supported format by the first
865 Xv grabable port we find. We store each one of the supported formats in a
866 format list and append the format to a newly created caps that we return
867 If this function does not return NULL because of an error, it also grabs
868 the port via XvGrabPort */
870 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
871 GstXContext * xcontext)
874 XvAdaptorInfo *adaptors;
876 XvImageFormatValues *formats = NULL;
878 XvEncodingInfo *encodings = NULL;
879 gulong max_w = G_MAXINT, max_h = G_MAXINT;
880 GstCaps *caps = NULL;
881 GstCaps *rgb_caps = NULL;
883 g_return_val_if_fail (xcontext != NULL, NULL);
885 /* First let's check that XVideo extension is available */
886 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
887 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
888 ("Could not initialise Xv output"),
889 ("XVideo extension is not available"));
893 /* Then we get adaptors list */
894 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
895 &xcontext->nb_adaptors, &adaptors)) {
896 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
897 ("Could not initialise Xv output"),
898 ("Failed getting XV adaptors list"));
902 xcontext->xv_port_id = 0;
904 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
907 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
909 /* Now fill up our adaptor name array */
910 for (i = 0; i < xcontext->nb_adaptors; i++) {
911 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
914 if (xvimagesink->adaptor_no >= 0 &&
915 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
916 /* Find xv port from user defined adaptor */
917 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
918 xvimagesink->adaptor_no);
921 if (!xcontext->xv_port_id) {
922 /* Now search for an adaptor that supports XvImageMask */
923 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
924 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
925 xvimagesink->adaptor_no = i;
929 XvFreeAdaptorInfo (adaptors);
931 if (!xcontext->xv_port_id) {
932 xvimagesink->adaptor_no = -1;
933 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
934 ("Could not initialise Xv output"), ("No port available"));
938 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
941 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
942 xcontext->xv_port_id, &count);
943 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
944 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
945 static const char colorkey[] = "XV_COLORKEY";
947 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
949 xvimagesink->have_autopaint_colorkey = FALSE;
950 xvimagesink->have_double_buffer = FALSE;
951 xvimagesink->have_colorkey = FALSE;
953 for (i = 0; ((i < count) && todo); i++)
954 if (!strcmp (attr[i].name, autopaint)) {
955 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
957 /* turn on autopaint colorkey */
958 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
959 (xvimagesink->autopaint_colorkey ? 1 : 0));
961 xvimagesink->have_autopaint_colorkey = TRUE;
962 } else if (!strcmp (attr[i].name, dbl_buffer)) {
963 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
965 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
966 (xvimagesink->double_buffer ? 1 : 0));
968 xvimagesink->have_double_buffer = TRUE;
969 } else if (!strcmp (attr[i].name, colorkey)) {
970 /* Set the colorkey, default is something that is dark but hopefully
971 * won't randomly appear on the screen elsewhere (ie not black or greys)
972 * can be overridden by setting "colorkey" property
974 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
976 gboolean set_attr = TRUE;
979 /* set a colorkey in the right format RGB565/RGB888
980 * We only handle these 2 cases, because they're the only types of
981 * devices we've encountered. If we don't recognise it, leave it alone
983 cr = (xvimagesink->colorkey >> 16);
984 cg = (xvimagesink->colorkey >> 8) & 0xFF;
985 cb = (xvimagesink->colorkey) & 0xFF;
986 switch (xcontext->depth) {
987 case 16: /* RGB 565 */
991 ckey = (cr << 11) | (cg << 5) | cb;
994 case 32: /* RGB 888 / ARGB 8888 */
995 ckey = (cr << 16) | (cg << 8) | cb;
998 GST_DEBUG_OBJECT (xvimagesink,
999 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1006 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1007 (guint32) attr[i].max_value);
1008 GST_LOG_OBJECT (xvimagesink,
1009 "Setting color key for display depth %d to 0x%x",
1010 xcontext->depth, ckey);
1012 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1016 xvimagesink->have_colorkey = TRUE;
1022 /* Get the list of encodings supported by the adapter and look for the
1023 * XV_IMAGE encoding so we can determine the maximum width and height
1025 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1028 for (i = 0; i < nb_encodings; i++) {
1029 GST_LOG_OBJECT (xvimagesink,
1030 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1031 i, encodings[i].name, encodings[i].width, encodings[i].height,
1032 encodings[i].rate.numerator, encodings[i].rate.denominator);
1033 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1034 max_w = encodings[i].width;
1035 max_h = encodings[i].height;
1039 XvFreeEncodingInfo (encodings);
1041 /* We get all image formats supported by our port */
1042 formats = XvListImageFormats (xcontext->disp,
1043 xcontext->xv_port_id, &nb_formats);
1044 caps = gst_caps_new_empty ();
1045 for (i = 0; i < nb_formats; i++) {
1046 GstCaps *format_caps = NULL;
1047 gboolean is_rgb_format = FALSE;
1048 GstVideoFormat vformat;
1050 /* We set the image format of the xcontext to an existing one. This
1051 is just some valid image format for making our xshm calls check before
1052 caps negotiation really happens. */
1053 xcontext->im_format = formats[i].id;
1055 switch (formats[i].type) {
1058 XvImageFormatValues *fmt = &(formats[i]);
1062 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1064 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1065 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1066 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1069 format_caps = gst_caps_new_simple ("video/x-raw",
1070 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1071 "width", GST_TYPE_INT_RANGE, 1, max_w,
1072 "height", GST_TYPE_INT_RANGE, 1, max_h,
1073 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1075 is_rgb_format = TRUE;
1080 vformat = gst_video_format_from_fourcc (formats[i].id);
1081 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1084 format_caps = gst_caps_new_simple ("video/x-raw",
1085 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1086 "width", GST_TYPE_INT_RANGE, 1, max_w,
1087 "height", GST_TYPE_INT_RANGE, 1, max_h,
1088 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1092 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1093 g_assert_not_reached ();
1098 GstXvImageFormat *format = NULL;
1100 format = g_new0 (GstXvImageFormat, 1);
1102 format->format = formats[i].id;
1103 format->vformat = vformat;
1104 format->caps = gst_caps_copy (format_caps);
1105 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1108 if (is_rgb_format) {
1109 if (rgb_caps == NULL)
1110 rgb_caps = format_caps;
1112 gst_caps_append (rgb_caps, format_caps);
1114 gst_caps_append (caps, format_caps);
1118 /* Collected all caps into either the caps or rgb_caps structures.
1119 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1121 gst_caps_append (caps, rgb_caps);
1126 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1128 if (gst_caps_is_empty (caps)) {
1129 gst_caps_unref (caps);
1130 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1131 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1132 ("No supported format found"));
1140 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1142 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1144 GST_OBJECT_LOCK (xvimagesink);
1145 while (xvimagesink->running) {
1146 GST_OBJECT_UNLOCK (xvimagesink);
1148 if (xvimagesink->xwindow) {
1149 gst_xvimagesink_handle_xevents (xvimagesink);
1151 /* FIXME: do we want to align this with the framerate or anything else? */
1152 g_usleep (G_USEC_PER_SEC / 20);
1154 GST_OBJECT_LOCK (xvimagesink);
1156 GST_OBJECT_UNLOCK (xvimagesink);
1162 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1164 GThread *thread = NULL;
1166 /* don't start the thread too early */
1167 if (xvimagesink->xcontext == NULL) {
1171 GST_OBJECT_LOCK (xvimagesink);
1172 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1173 if (!xvimagesink->event_thread) {
1174 /* Setup our event listening thread */
1175 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1176 xvimagesink->handle_expose, xvimagesink->handle_events);
1177 xvimagesink->running = TRUE;
1178 xvimagesink->event_thread = g_thread_create (
1179 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1182 if (xvimagesink->event_thread) {
1183 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1184 xvimagesink->handle_expose, xvimagesink->handle_events);
1185 xvimagesink->running = FALSE;
1186 /* grab thread and mark it as NULL */
1187 thread = xvimagesink->event_thread;
1188 xvimagesink->event_thread = NULL;
1191 GST_OBJECT_UNLOCK (xvimagesink);
1193 /* Wait for our event thread to finish */
1195 g_thread_join (thread);
1200 /* This function calculates the pixel aspect ratio based on the properties
1201 * in the xcontext structure and stores it there. */
1203 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1205 static const gint par[][2] = {
1206 {1, 1}, /* regular screen */
1207 {16, 15}, /* PAL TV */
1208 {11, 10}, /* 525 line Rec.601 video */
1209 {54, 59}, /* 625 line Rec.601 video */
1210 {64, 45}, /* 1280x1024 on 16:9 display */
1211 {5, 3}, /* 1280x1024 on 4:3 display */
1212 {4, 3} /* 800x600 on 16:9 display */
1219 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1221 /* first calculate the "real" ratio based on the X values;
1222 * which is the "physical" w/h divided by the w/h in pixels of the display */
1223 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1224 / (xcontext->heightmm * xcontext->width);
1226 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1228 if (xcontext->width == 720 && xcontext->height == 576) {
1229 ratio = 4.0 * 576 / (3.0 * 720);
1231 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1233 /* now find the one from par[][2] with the lowest delta to the real one */
1237 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1238 gdouble this_delta = DELTA (i);
1240 if (this_delta < delta) {
1246 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1247 par[index][0], par[index][1]);
1249 g_free (xcontext->par);
1250 xcontext->par = g_new0 (GValue, 1);
1251 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1252 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1253 GST_DEBUG ("set xcontext PAR to %d/%d",
1254 gst_value_get_fraction_numerator (xcontext->par),
1255 gst_value_get_fraction_denominator (xcontext->par));
1258 /* This function gets the X Display and global info about it. Everything is
1259 stored in our object and will be cleaned when the object is disposed. Note
1260 here that caps for supported format are generated without any window or
1262 static GstXContext *
1263 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1265 GstXContext *xcontext = NULL;
1266 XPixmapFormatValues *px_formats = NULL;
1267 gint nb_formats = 0, i, j, N_attr;
1268 XvAttribute *xv_attr;
1270 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1271 "XV_BRIGHTNESS", "XV_CONTRAST"
1274 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1276 xcontext = g_new0 (GstXContext, 1);
1277 xcontext->im_format = 0;
1279 g_mutex_lock (xvimagesink->x_lock);
1281 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1283 if (!xcontext->disp) {
1284 g_mutex_unlock (xvimagesink->x_lock);
1286 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1287 ("Could not initialise Xv output"), ("Could not open display"));
1291 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1292 xcontext->screen_num = DefaultScreen (xcontext->disp);
1293 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1294 xcontext->root = DefaultRootWindow (xcontext->disp);
1295 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1296 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1297 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1299 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1300 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1301 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1302 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1304 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1305 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1307 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1308 /* We get supported pixmap formats at supported depth */
1309 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1312 XCloseDisplay (xcontext->disp);
1313 g_mutex_unlock (xvimagesink->x_lock);
1314 g_free (xcontext->par);
1316 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1317 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1321 /* We get bpp value corresponding to our running depth */
1322 for (i = 0; i < nb_formats; i++) {
1323 if (px_formats[i].depth == xcontext->depth)
1324 xcontext->bpp = px_formats[i].bits_per_pixel;
1329 xcontext->endianness =
1330 (ImageByteOrder (xcontext->disp) ==
1331 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1333 /* our caps system handles 24/32bpp RGB as big-endian. */
1334 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1335 xcontext->endianness == G_LITTLE_ENDIAN) {
1336 xcontext->endianness = G_BIG_ENDIAN;
1337 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1338 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1339 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1340 if (xcontext->bpp == 24) {
1341 xcontext->visual->red_mask >>= 8;
1342 xcontext->visual->green_mask >>= 8;
1343 xcontext->visual->blue_mask >>= 8;
1347 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1349 /* Search for XShm extension support */
1351 if (XShmQueryExtension (xcontext->disp) &&
1352 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1353 xcontext->use_xshm = TRUE;
1354 GST_DEBUG ("xvimagesink is using XShm extension");
1356 #endif /* HAVE_XSHM */
1358 xcontext->use_xshm = FALSE;
1359 GST_DEBUG ("xvimagesink is not using XShm extension");
1362 if (!xcontext->caps) {
1363 XCloseDisplay (xcontext->disp);
1364 g_mutex_unlock (xvimagesink->x_lock);
1365 g_free (xcontext->par);
1367 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1371 xv_attr = XvQueryPortAttributes (xcontext->disp,
1372 xcontext->xv_port_id, &N_attr);
1375 /* Generate the channels list */
1376 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1377 XvAttribute *matching_attr = NULL;
1379 /* Retrieve the property atom if it exists. If it doesn't exist,
1380 * the attribute itself must not either, so we can skip */
1381 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1382 if (prop_atom == None)
1385 if (xv_attr != NULL) {
1386 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1387 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1388 matching_attr = xv_attr + j;
1391 if (matching_attr) {
1392 GstColorBalanceChannel *channel;
1394 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1395 channel->label = g_strdup (channels[i]);
1396 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1397 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1399 xcontext->channels_list = g_list_append (xcontext->channels_list,
1402 /* If the colorbalance settings have not been touched we get Xv values
1403 as defaults and update our internal variables */
1404 if (!xvimagesink->cb_changed) {
1407 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1409 /* Normalize val to [-1000, 1000] */
1410 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1411 (double) (channel->max_value - channel->min_value));
1413 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1414 xvimagesink->hue = val;
1415 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1416 xvimagesink->saturation = val;
1417 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1418 xvimagesink->brightness = val;
1419 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1420 xvimagesink->contrast = val;
1428 g_mutex_unlock (xvimagesink->x_lock);
1433 /* This function cleans the X context. Closing the Display, releasing the XV
1434 port and unrefing the caps for supported formats. */
1436 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1438 GList *formats_list, *channels_list;
1439 GstXContext *xcontext;
1442 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1444 GST_OBJECT_LOCK (xvimagesink);
1445 if (xvimagesink->xcontext == NULL) {
1446 GST_OBJECT_UNLOCK (xvimagesink);
1450 /* Take the XContext from the sink and clean it up */
1451 xcontext = xvimagesink->xcontext;
1452 xvimagesink->xcontext = NULL;
1454 GST_OBJECT_UNLOCK (xvimagesink);
1457 formats_list = xcontext->formats_list;
1459 while (formats_list) {
1460 GstXvImageFormat *format = formats_list->data;
1462 gst_caps_unref (format->caps);
1464 formats_list = g_list_next (formats_list);
1467 if (xcontext->formats_list)
1468 g_list_free (xcontext->formats_list);
1470 channels_list = xcontext->channels_list;
1472 while (channels_list) {
1473 GstColorBalanceChannel *channel = channels_list->data;
1475 g_object_unref (channel);
1476 channels_list = g_list_next (channels_list);
1479 if (xcontext->channels_list)
1480 g_list_free (xcontext->channels_list);
1482 gst_caps_unref (xcontext->caps);
1483 if (xcontext->last_caps)
1484 gst_caps_replace (&xcontext->last_caps, NULL);
1486 for (i = 0; i < xcontext->nb_adaptors; i++) {
1487 g_free (xcontext->adaptors[i]);
1490 g_free (xcontext->adaptors);
1492 g_free (xcontext->par);
1494 g_mutex_lock (xvimagesink->x_lock);
1496 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1498 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1500 XCloseDisplay (xcontext->disp);
1502 g_mutex_unlock (xvimagesink->x_lock);
1510 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1512 GstXvImageSink *xvimagesink;
1515 xvimagesink = GST_XVIMAGESINK (bsink);
1517 if (xvimagesink->xcontext) {
1519 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1520 GST_CAPS_INTERSECT_FIRST);
1522 return gst_caps_ref (xvimagesink->xcontext->caps);
1525 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1527 GstCaps *intersection;
1530 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1531 gst_caps_unref (caps);
1532 caps = intersection;
1538 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1540 GstXvImageSink *xvimagesink;
1541 GstStructure *structure;
1542 GstBufferPool *newpool, *oldpool;
1544 guint32 im_format = 0;
1545 gint video_par_n, video_par_d; /* video's PAR */
1546 gint display_par_n, display_par_d; /* display's PAR */
1550 xvimagesink = GST_XVIMAGESINK (bsink);
1552 GST_DEBUG_OBJECT (xvimagesink,
1553 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1554 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1556 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1557 goto incompatible_caps;
1559 if (!gst_video_info_from_caps (&info, caps))
1560 goto invalid_format;
1562 structure = gst_caps_get_structure (caps, 0);
1564 xvimagesink->fps_n = info.fps_n;
1565 xvimagesink->fps_d = info.fps_d;
1567 xvimagesink->video_width = info.width;
1568 xvimagesink->video_height = info.height;
1570 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1571 if (im_format == -1)
1572 goto invalid_format;
1576 /* get aspect ratio from caps if it's present, and
1577 * convert video width and height to a display width and height
1578 * using wd / hd = wv / hv * PARv / PARd */
1580 /* get video's PAR */
1581 video_par_n = info.par_n;
1582 video_par_d = info.par_d;
1584 /* get display's PAR */
1585 if (xvimagesink->par) {
1586 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1587 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1593 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1594 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1597 GST_DEBUG_OBJECT (xvimagesink,
1598 "video width/height: %dx%d, calculated display ratio: %d/%d",
1599 info.width, info.height, num, den);
1601 /* now find a width x height that respects this display ratio.
1602 * prefer those that have one of w/h the same as the incoming video
1603 * using wd / hd = num / den */
1605 /* start with same height, because of interlaced video */
1606 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1607 if (info.height % den == 0) {
1608 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1609 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1610 gst_util_uint64_scale_int (info.height, num, den);
1611 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1612 } else if (info.width % num == 0) {
1613 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1614 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1615 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1616 gst_util_uint64_scale_int (info.width, den, num);
1618 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1619 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1620 gst_util_uint64_scale_int (info.height, num, den);
1621 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1623 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1624 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1626 /* Notify application to set xwindow id now */
1627 g_mutex_lock (xvimagesink->flow_lock);
1628 if (!xvimagesink->xwindow) {
1629 g_mutex_unlock (xvimagesink->flow_lock);
1630 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1632 g_mutex_unlock (xvimagesink->flow_lock);
1635 /* Creating our window and our image with the display size in pixels */
1636 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1637 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1638 goto no_display_size;
1640 g_mutex_lock (xvimagesink->flow_lock);
1641 if (!xvimagesink->xwindow) {
1642 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1643 GST_VIDEO_SINK_WIDTH (xvimagesink),
1644 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1647 xvimagesink->info = info;
1649 /* After a resize, we want to redraw the borders in case the new frame size
1650 * doesn't cover the same area */
1651 xvimagesink->redraw_border = TRUE;
1653 /* create a new pool for the new configuration */
1654 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1656 structure = gst_buffer_pool_get_config (newpool);
1657 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1658 if (!gst_buffer_pool_set_config (newpool, structure))
1661 oldpool = xvimagesink->pool;
1662 xvimagesink->pool = newpool;
1663 g_mutex_unlock (xvimagesink->flow_lock);
1665 /* unref the old sink */
1667 /* we don't deactivate, some elements might still be using it, it will
1668 * be deactivated when the last ref is gone */
1669 gst_object_unref (oldpool);
1677 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1682 GST_DEBUG_OBJECT (xvimagesink,
1683 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1688 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1689 ("Error calculating the output display ratio of the video."));
1694 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1695 ("Error calculating the output display ratio of the video."));
1700 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1701 g_mutex_unlock (xvimagesink->flow_lock);
1706 static GstStateChangeReturn
1707 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1709 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1710 GstXvImageSink *xvimagesink;
1711 GstXContext *xcontext = NULL;
1713 xvimagesink = GST_XVIMAGESINK (element);
1715 switch (transition) {
1716 case GST_STATE_CHANGE_NULL_TO_READY:
1717 /* Initializing the XContext */
1718 if (xvimagesink->xcontext == NULL) {
1719 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1720 if (xcontext == NULL) {
1721 ret = GST_STATE_CHANGE_FAILURE;
1724 GST_OBJECT_LOCK (xvimagesink);
1726 xvimagesink->xcontext = xcontext;
1727 GST_OBJECT_UNLOCK (xvimagesink);
1730 /* update object's par with calculated one if not set yet */
1731 if (!xvimagesink->par) {
1732 xvimagesink->par = g_new0 (GValue, 1);
1733 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1734 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1736 /* call XSynchronize with the current value of synchronous */
1737 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1738 xvimagesink->synchronous ? "TRUE" : "FALSE");
1739 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1740 gst_xvimagesink_update_colorbalance (xvimagesink);
1741 gst_xvimagesink_manage_event_thread (xvimagesink);
1743 case GST_STATE_CHANGE_READY_TO_PAUSED:
1745 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1747 case GST_STATE_CHANGE_PAUSED_TO_READY:
1753 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1755 switch (transition) {
1756 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1758 case GST_STATE_CHANGE_PAUSED_TO_READY:
1759 xvimagesink->fps_n = 0;
1760 xvimagesink->fps_d = 1;
1761 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1762 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1763 g_mutex_lock (xvimagesink->flow_lock);
1764 if (xvimagesink->pool)
1765 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1766 g_mutex_unlock (xvimagesink->flow_lock);
1768 case GST_STATE_CHANGE_READY_TO_NULL:
1769 gst_xvimagesink_reset (xvimagesink);
1780 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1781 GstClockTime * start, GstClockTime * end)
1783 GstXvImageSink *xvimagesink;
1785 xvimagesink = GST_XVIMAGESINK (bsink);
1787 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1788 *start = GST_BUFFER_TIMESTAMP (buf);
1789 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1790 *end = *start + GST_BUFFER_DURATION (buf);
1792 if (xvimagesink->fps_n > 0) {
1794 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1795 xvimagesink->fps_n);
1801 static GstFlowReturn
1802 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1805 GstXvImageSink *xvimagesink;
1806 GstXvImageMeta *meta;
1809 xvimagesink = GST_XVIMAGESINK (vsink);
1811 meta = gst_buffer_get_xvimage_meta (buf);
1813 if (meta && meta->sink == xvimagesink) {
1814 /* If this buffer has been allocated using our buffer management we simply
1815 put the ximage which is in the PRIVATE pointer */
1816 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1821 GstVideoFrame src, dest;
1823 /* Else we have to copy the data into our private image, */
1824 /* if we have one... */
1825 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1827 /* we should have a pool, configured in setcaps */
1828 if (xvimagesink->pool == NULL)
1831 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1832 goto activate_failed;
1834 /* take a buffer form our pool */
1835 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1836 if (res != GST_FLOW_OK)
1839 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1842 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1843 "slow copy into bufferpool buffer %p", to_put);
1845 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1846 goto invalid_buffer;
1848 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1849 gst_video_frame_unmap (&src);
1850 goto invalid_buffer;
1853 gst_video_frame_copy (&dest, &src);
1855 gst_video_frame_unmap (&dest);
1856 gst_video_frame_unmap (&src);
1859 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1864 gst_buffer_unref (to_put);
1871 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1872 ("Internal error: can't allocate images"),
1873 ("We don't have a bufferpool negotiated"));
1874 return GST_FLOW_ERROR;
1878 /* No image available. That's very bad ! */
1879 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1884 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1885 ("Failed to create output image buffer"),
1886 ("XServer allocated buffer size did not match input buffer %"
1887 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1888 gst_buffer_get_size (buf)));
1889 res = GST_FLOW_ERROR;
1894 /* No Window available to put our image into */
1895 GST_WARNING_OBJECT (xvimagesink, "could map image");
1901 /* No Window available to put our image into */
1902 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1903 res = GST_FLOW_ERROR;
1908 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1909 res = GST_FLOW_ERROR;
1915 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1917 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1919 switch (GST_EVENT_TYPE (event)) {
1920 case GST_EVENT_TAG:{
1922 gchar *title = NULL;
1924 gst_event_parse_tag (event, &l);
1925 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1928 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1929 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1939 if (GST_BASE_SINK_CLASS (parent_class)->event)
1940 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1946 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1948 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1949 GstBufferPool *pool;
1950 GstStructure *config;
1955 gst_query_parse_allocation (query, &caps, &need_pool);
1960 g_mutex_lock (xvimagesink->flow_lock);
1961 if ((pool = xvimagesink->pool))
1962 gst_object_ref (pool);
1963 g_mutex_unlock (xvimagesink->flow_lock);
1966 const GstCaps *pcaps;
1968 /* we had a pool, check caps */
1969 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1970 config = gst_buffer_pool_get_config (pool);
1971 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1973 if (!gst_caps_is_equal (caps, pcaps)) {
1974 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1975 /* different caps, we can't use this pool */
1976 gst_object_unref (pool);
1980 if (pool == NULL && need_pool) {
1983 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1984 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1986 if (!gst_video_info_from_caps (&info, caps))
1989 /* the normal size of a frame */
1992 config = gst_buffer_pool_get_config (pool);
1993 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1994 if (!gst_buffer_pool_set_config (pool, config))
1997 /* we need at least 2 buffer because we hold on to the last one */
1998 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
2000 /* we also support various metadata */
2001 gst_query_add_allocation_meta (query, GST_VIDEO_META_API);
2002 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API);
2004 gst_object_unref (pool);
2011 GST_DEBUG_OBJECT (bsink, "no caps specified");
2016 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2021 GST_DEBUG_OBJECT (bsink, "failed setting config");
2026 /* Interfaces stuff */
2028 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2029 GstStructure * structure)
2031 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2034 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2036 GstVideoRectangle src, dst, result;
2037 gdouble x, y, xscale = 1.0, yscale = 1.0;
2039 event = gst_event_new_navigation (structure);
2041 /* We take the flow_lock while we look at the window */
2042 g_mutex_lock (xvimagesink->flow_lock);
2044 if (!xvimagesink->xwindow) {
2045 g_mutex_unlock (xvimagesink->flow_lock);
2049 if (xvimagesink->keep_aspect) {
2050 /* We get the frame position using the calculated geometry from _setcaps
2051 that respect pixel aspect ratios */
2052 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2053 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2054 dst.w = xvimagesink->render_rect.w;
2055 dst.h = xvimagesink->render_rect.h;
2057 gst_video_sink_center_rect (src, dst, &result, TRUE);
2058 result.x += xvimagesink->render_rect.x;
2059 result.y += xvimagesink->render_rect.y;
2061 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2064 g_mutex_unlock (xvimagesink->flow_lock);
2066 /* We calculate scaling using the original video frames geometry to include
2067 pixel aspect ratio scaling. */
2068 xscale = (gdouble) xvimagesink->video_width / result.w;
2069 yscale = (gdouble) xvimagesink->video_height / result.h;
2071 /* Converting pointer coordinates to the non scaled geometry */
2072 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2073 x = MIN (x, result.x + result.w);
2074 x = MAX (x - result.x, 0);
2075 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2076 (gdouble) x * xscale, NULL);
2078 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2079 y = MIN (y, result.y + result.h);
2080 y = MAX (y - result.y, 0);
2081 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2082 (gdouble) y * yscale, NULL);
2085 gst_pad_send_event (peer, event);
2086 gst_object_unref (peer);
2091 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2093 iface->send_event = gst_xvimagesink_navigation_send_event;
2097 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2099 XID xwindow_id = id;
2100 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2101 GstXWindow *xwindow = NULL;
2103 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2105 g_mutex_lock (xvimagesink->flow_lock);
2107 /* If we already use that window return */
2108 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2109 g_mutex_unlock (xvimagesink->flow_lock);
2113 /* If the element has not initialized the X11 context try to do so */
2114 if (!xvimagesink->xcontext &&
2115 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2116 g_mutex_unlock (xvimagesink->flow_lock);
2117 /* we have thrown a GST_ELEMENT_ERROR now */
2121 gst_xvimagesink_update_colorbalance (xvimagesink);
2123 /* If a window is there already we destroy it */
2124 if (xvimagesink->xwindow) {
2125 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2126 xvimagesink->xwindow = NULL;
2129 /* If the xid is 0 we go back to an internal window */
2130 if (xwindow_id == 0) {
2131 /* If no width/height caps nego did not happen window will be created
2132 during caps nego then */
2133 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2134 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2136 gst_xvimagesink_xwindow_new (xvimagesink,
2137 GST_VIDEO_SINK_WIDTH (xvimagesink),
2138 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2141 XWindowAttributes attr;
2143 xwindow = g_new0 (GstXWindow, 1);
2144 xwindow->win = xwindow_id;
2146 /* Set the event we want to receive and create a GC */
2147 g_mutex_lock (xvimagesink->x_lock);
2149 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2151 xwindow->width = attr.width;
2152 xwindow->height = attr.height;
2153 xwindow->internal = FALSE;
2154 if (!xvimagesink->have_render_rect) {
2155 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2156 xvimagesink->render_rect.w = attr.width;
2157 xvimagesink->render_rect.h = attr.height;
2159 if (xvimagesink->handle_events) {
2160 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2161 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2165 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2166 xwindow->win, 0, NULL);
2167 g_mutex_unlock (xvimagesink->x_lock);
2171 xvimagesink->xwindow = xwindow;
2173 g_mutex_unlock (xvimagesink->flow_lock);
2177 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2179 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2181 GST_DEBUG ("doing expose");
2182 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2183 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2187 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2188 gboolean handle_events)
2190 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2192 xvimagesink->handle_events = handle_events;
2194 g_mutex_lock (xvimagesink->flow_lock);
2196 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2197 g_mutex_unlock (xvimagesink->flow_lock);
2201 g_mutex_lock (xvimagesink->x_lock);
2203 if (handle_events) {
2204 if (xvimagesink->xwindow->internal) {
2205 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2206 ExposureMask | StructureNotifyMask | PointerMotionMask |
2207 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2209 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2210 ExposureMask | StructureNotifyMask | PointerMotionMask |
2211 KeyPressMask | KeyReleaseMask);
2214 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2217 g_mutex_unlock (xvimagesink->x_lock);
2219 g_mutex_unlock (xvimagesink->flow_lock);
2223 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2224 gint width, gint height)
2226 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2228 /* FIXME: how about some locking? */
2229 if (width >= 0 && height >= 0) {
2230 xvimagesink->render_rect.x = x;
2231 xvimagesink->render_rect.y = y;
2232 xvimagesink->render_rect.w = width;
2233 xvimagesink->render_rect.h = height;
2234 xvimagesink->have_render_rect = TRUE;
2236 xvimagesink->render_rect.x = 0;
2237 xvimagesink->render_rect.y = 0;
2238 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2239 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2240 xvimagesink->have_render_rect = FALSE;
2245 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2247 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2248 iface->expose = gst_xvimagesink_expose;
2249 iface->handle_events = gst_xvimagesink_set_event_handling;
2250 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2253 static const GList *
2254 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2256 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2258 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2260 if (xvimagesink->xcontext)
2261 return xvimagesink->xcontext->channels_list;
2267 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2268 GstColorBalanceChannel * channel, gint value)
2270 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2272 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2273 g_return_if_fail (channel->label != NULL);
2275 xvimagesink->cb_changed = TRUE;
2277 /* Normalize val to [-1000, 1000] */
2278 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2279 (double) (channel->max_value - channel->min_value));
2281 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2282 xvimagesink->hue = value;
2283 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2284 xvimagesink->saturation = value;
2285 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2286 xvimagesink->contrast = value;
2287 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2288 xvimagesink->brightness = value;
2290 g_warning ("got an unknown channel %s", channel->label);
2294 gst_xvimagesink_update_colorbalance (xvimagesink);
2298 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2299 GstColorBalanceChannel * channel)
2301 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2304 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2305 g_return_val_if_fail (channel->label != NULL, 0);
2307 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2308 value = xvimagesink->hue;
2309 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2310 value = xvimagesink->saturation;
2311 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2312 value = xvimagesink->contrast;
2313 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2314 value = xvimagesink->brightness;
2316 g_warning ("got an unknown channel %s", channel->label);
2319 /* Normalize val to [channel->min_value, channel->max_value] */
2320 value = channel->min_value + (channel->max_value - channel->min_value) *
2321 (value + 1000) / 2000;
2327 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2329 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2330 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2331 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2332 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2335 static const GList *
2336 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2338 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2339 static GList *list = NULL;
2342 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2344 g_list_append (list, g_object_class_find_property (klass,
2345 "autopaint-colorkey"));
2347 g_list_append (list, g_object_class_find_property (klass,
2350 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2357 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2358 guint prop_id, const GParamSpec * pspec)
2360 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2364 case PROP_AUTOPAINT_COLORKEY:
2365 case PROP_DOUBLE_BUFFER:
2367 GST_DEBUG_OBJECT (xvimagesink,
2368 "probing device list and get capabilities");
2369 if (!xvimagesink->xcontext) {
2370 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2371 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2375 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2381 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2382 guint prop_id, const GParamSpec * pspec)
2384 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2385 gboolean ret = FALSE;
2389 case PROP_AUTOPAINT_COLORKEY:
2390 case PROP_DOUBLE_BUFFER:
2392 if (xvimagesink->xcontext != NULL) {
2399 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2406 static GValueArray *
2407 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2408 guint prop_id, const GParamSpec * pspec)
2410 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2411 GValueArray *array = NULL;
2413 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2414 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2423 GValue value = { 0 };
2425 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2426 g_value_init (&value, G_TYPE_STRING);
2428 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2429 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2431 g_value_set_string (&value, adaptor_id_s);
2432 g_value_array_append (array, &value);
2433 g_free (adaptor_id_s);
2435 g_value_unset (&value);
2438 case PROP_AUTOPAINT_COLORKEY:
2439 if (xvimagesink->have_autopaint_colorkey) {
2440 GValue value = { 0 };
2442 array = g_value_array_new (2);
2443 g_value_init (&value, G_TYPE_BOOLEAN);
2444 g_value_set_boolean (&value, FALSE);
2445 g_value_array_append (array, &value);
2446 g_value_set_boolean (&value, TRUE);
2447 g_value_array_append (array, &value);
2448 g_value_unset (&value);
2451 case PROP_DOUBLE_BUFFER:
2452 if (xvimagesink->have_double_buffer) {
2453 GValue value = { 0 };
2455 array = g_value_array_new (2);
2456 g_value_init (&value, G_TYPE_BOOLEAN);
2457 g_value_set_boolean (&value, FALSE);
2458 g_value_array_append (array, &value);
2459 g_value_set_boolean (&value, TRUE);
2460 g_value_array_append (array, &value);
2461 g_value_unset (&value);
2465 if (xvimagesink->have_colorkey) {
2466 GValue value = { 0 };
2468 array = g_value_array_new (1);
2469 g_value_init (&value, GST_TYPE_INT_RANGE);
2470 gst_value_set_int_range (&value, 0, 0xffffff);
2471 g_value_array_append (array, &value);
2472 g_value_unset (&value);
2476 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2485 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2488 iface->get_properties = gst_xvimagesink_probe_get_properties;
2489 iface->probe_property = gst_xvimagesink_probe_probe_property;
2490 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2491 iface->get_values = gst_xvimagesink_probe_get_values;
2494 /* =========================================== */
2496 /* Init & Class init */
2498 /* =========================================== */
2501 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2502 const GValue * value, GParamSpec * pspec)
2504 GstXvImageSink *xvimagesink;
2506 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2508 xvimagesink = GST_XVIMAGESINK (object);
2512 xvimagesink->hue = g_value_get_int (value);
2513 xvimagesink->cb_changed = TRUE;
2514 gst_xvimagesink_update_colorbalance (xvimagesink);
2517 xvimagesink->contrast = g_value_get_int (value);
2518 xvimagesink->cb_changed = TRUE;
2519 gst_xvimagesink_update_colorbalance (xvimagesink);
2521 case PROP_BRIGHTNESS:
2522 xvimagesink->brightness = g_value_get_int (value);
2523 xvimagesink->cb_changed = TRUE;
2524 gst_xvimagesink_update_colorbalance (xvimagesink);
2526 case PROP_SATURATION:
2527 xvimagesink->saturation = g_value_get_int (value);
2528 xvimagesink->cb_changed = TRUE;
2529 gst_xvimagesink_update_colorbalance (xvimagesink);
2532 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2534 case PROP_SYNCHRONOUS:
2535 xvimagesink->synchronous = g_value_get_boolean (value);
2536 if (xvimagesink->xcontext) {
2537 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2538 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2539 xvimagesink->synchronous ? "TRUE" : "FALSE");
2542 case PROP_PIXEL_ASPECT_RATIO:
2543 g_free (xvimagesink->par);
2544 xvimagesink->par = g_new0 (GValue, 1);
2545 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2546 if (!g_value_transform (value, xvimagesink->par)) {
2547 g_warning ("Could not transform string to aspect ratio");
2548 gst_value_set_fraction (xvimagesink->par, 1, 1);
2550 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2551 gst_value_get_fraction_numerator (xvimagesink->par),
2552 gst_value_get_fraction_denominator (xvimagesink->par));
2554 case PROP_FORCE_ASPECT_RATIO:
2555 xvimagesink->keep_aspect = g_value_get_boolean (value);
2557 case PROP_HANDLE_EVENTS:
2558 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2559 g_value_get_boolean (value));
2560 gst_xvimagesink_manage_event_thread (xvimagesink);
2563 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2565 case PROP_HANDLE_EXPOSE:
2566 xvimagesink->handle_expose = g_value_get_boolean (value);
2567 gst_xvimagesink_manage_event_thread (xvimagesink);
2569 case PROP_DOUBLE_BUFFER:
2570 xvimagesink->double_buffer = g_value_get_boolean (value);
2572 case PROP_AUTOPAINT_COLORKEY:
2573 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2576 xvimagesink->colorkey = g_value_get_int (value);
2578 case PROP_DRAW_BORDERS:
2579 xvimagesink->draw_borders = g_value_get_boolean (value);
2582 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2588 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2589 GValue * value, GParamSpec * pspec)
2591 GstXvImageSink *xvimagesink;
2593 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2595 xvimagesink = GST_XVIMAGESINK (object);
2599 g_value_set_int (value, xvimagesink->hue);
2602 g_value_set_int (value, xvimagesink->contrast);
2604 case PROP_BRIGHTNESS:
2605 g_value_set_int (value, xvimagesink->brightness);
2607 case PROP_SATURATION:
2608 g_value_set_int (value, xvimagesink->saturation);
2611 g_value_set_string (value, xvimagesink->display_name);
2613 case PROP_SYNCHRONOUS:
2614 g_value_set_boolean (value, xvimagesink->synchronous);
2616 case PROP_PIXEL_ASPECT_RATIO:
2617 if (xvimagesink->par)
2618 g_value_transform (xvimagesink->par, value);
2620 case PROP_FORCE_ASPECT_RATIO:
2621 g_value_set_boolean (value, xvimagesink->keep_aspect);
2623 case PROP_HANDLE_EVENTS:
2624 g_value_set_boolean (value, xvimagesink->handle_events);
2628 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2630 g_value_set_string (value, adaptor_no_s);
2631 g_free (adaptor_no_s);
2634 case PROP_DEVICE_NAME:
2635 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2636 g_value_set_string (value,
2637 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2639 g_value_set_string (value, NULL);
2642 case PROP_HANDLE_EXPOSE:
2643 g_value_set_boolean (value, xvimagesink->handle_expose);
2645 case PROP_DOUBLE_BUFFER:
2646 g_value_set_boolean (value, xvimagesink->double_buffer);
2648 case PROP_AUTOPAINT_COLORKEY:
2649 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2652 g_value_set_int (value, xvimagesink->colorkey);
2654 case PROP_DRAW_BORDERS:
2655 g_value_set_boolean (value, xvimagesink->draw_borders);
2657 case PROP_WINDOW_WIDTH:
2658 if (xvimagesink->xwindow)
2659 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2661 g_value_set_uint64 (value, 0);
2663 case PROP_WINDOW_HEIGHT:
2664 if (xvimagesink->xwindow)
2665 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2667 g_value_set_uint64 (value, 0);
2670 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2676 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2680 GST_OBJECT_LOCK (xvimagesink);
2681 xvimagesink->running = FALSE;
2682 /* grab thread and mark it as NULL */
2683 thread = xvimagesink->event_thread;
2684 xvimagesink->event_thread = NULL;
2685 GST_OBJECT_UNLOCK (xvimagesink);
2687 /* Wait for our event thread to finish before we clean up our stuff. */
2689 g_thread_join (thread);
2691 if (xvimagesink->cur_image) {
2692 gst_buffer_unref (xvimagesink->cur_image);
2693 xvimagesink->cur_image = NULL;
2696 g_mutex_lock (xvimagesink->flow_lock);
2698 if (xvimagesink->pool) {
2699 gst_object_unref (xvimagesink->pool);
2700 xvimagesink->pool = NULL;
2703 if (xvimagesink->xwindow) {
2704 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2705 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2706 xvimagesink->xwindow = NULL;
2708 g_mutex_unlock (xvimagesink->flow_lock);
2710 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2711 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2712 xvimagesink->have_render_rect = FALSE;
2714 gst_xvimagesink_xcontext_clear (xvimagesink);
2717 /* Finalize is called only once, dispose can be called multiple times.
2718 * We use mutexes and don't reset stuff to NULL here so let's register
2721 gst_xvimagesink_finalize (GObject * object)
2723 GstXvImageSink *xvimagesink;
2725 xvimagesink = GST_XVIMAGESINK (object);
2727 gst_xvimagesink_reset (xvimagesink);
2729 if (xvimagesink->display_name) {
2730 g_free (xvimagesink->display_name);
2731 xvimagesink->display_name = NULL;
2734 if (xvimagesink->par) {
2735 g_free (xvimagesink->par);
2736 xvimagesink->par = NULL;
2738 if (xvimagesink->x_lock) {
2739 g_mutex_free (xvimagesink->x_lock);
2740 xvimagesink->x_lock = NULL;
2742 if (xvimagesink->flow_lock) {
2743 g_mutex_free (xvimagesink->flow_lock);
2744 xvimagesink->flow_lock = NULL;
2747 g_free (xvimagesink->media_title);
2749 G_OBJECT_CLASS (parent_class)->finalize (object);
2753 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2755 xvimagesink->display_name = NULL;
2756 xvimagesink->adaptor_no = 0;
2757 xvimagesink->xcontext = NULL;
2758 xvimagesink->xwindow = NULL;
2759 xvimagesink->cur_image = NULL;
2761 xvimagesink->hue = xvimagesink->saturation = 0;
2762 xvimagesink->contrast = xvimagesink->brightness = 0;
2763 xvimagesink->cb_changed = FALSE;
2765 xvimagesink->fps_n = 0;
2766 xvimagesink->fps_d = 0;
2767 xvimagesink->video_width = 0;
2768 xvimagesink->video_height = 0;
2770 xvimagesink->x_lock = g_mutex_new ();
2771 xvimagesink->flow_lock = g_mutex_new ();
2773 xvimagesink->pool = NULL;
2775 xvimagesink->synchronous = FALSE;
2776 xvimagesink->double_buffer = TRUE;
2777 xvimagesink->running = FALSE;
2778 xvimagesink->keep_aspect = FALSE;
2779 xvimagesink->handle_events = TRUE;
2780 xvimagesink->par = NULL;
2781 xvimagesink->handle_expose = TRUE;
2782 xvimagesink->autopaint_colorkey = TRUE;
2784 /* on 16bit displays this becomes r,g,b = 1,2,3
2785 * on 24bit displays this becomes r,g,b = 8,8,16
2786 * as a port atom value
2788 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2789 xvimagesink->draw_borders = TRUE;
2793 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2795 GObjectClass *gobject_class;
2796 GstElementClass *gstelement_class;
2797 GstBaseSinkClass *gstbasesink_class;
2798 GstVideoSinkClass *videosink_class;
2800 gobject_class = (GObjectClass *) klass;
2801 gstelement_class = (GstElementClass *) klass;
2802 gstbasesink_class = (GstBaseSinkClass *) klass;
2803 videosink_class = (GstVideoSinkClass *) klass;
2805 parent_class = g_type_class_peek_parent (klass);
2807 gobject_class->set_property = gst_xvimagesink_set_property;
2808 gobject_class->get_property = gst_xvimagesink_get_property;
2810 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2811 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2812 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2813 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2814 g_param_spec_int ("brightness", "Brightness",
2815 "The brightness of the video", -1000, 1000, 0,
2816 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2817 g_object_class_install_property (gobject_class, PROP_HUE,
2818 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2819 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2820 g_object_class_install_property (gobject_class, PROP_SATURATION,
2821 g_param_spec_int ("saturation", "Saturation",
2822 "The saturation of the video", -1000, 1000, 0,
2823 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2824 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2825 g_param_spec_string ("display", "Display", "X Display name", NULL,
2826 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2827 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2828 g_param_spec_boolean ("synchronous", "Synchronous",
2829 "When enabled, runs the X display in synchronous mode. "
2830 "(unrelated to A/V sync, used only for debugging)", FALSE,
2831 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2832 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2833 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2834 "The pixel aspect ratio of the device", "1/1",
2835 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2836 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2837 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2838 "When enabled, scaling will respect original aspect ratio", FALSE,
2839 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2840 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2841 g_param_spec_boolean ("handle-events", "Handle XEvents",
2842 "When enabled, XEvents will be selected and handled", TRUE,
2843 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2844 g_object_class_install_property (gobject_class, PROP_DEVICE,
2845 g_param_spec_string ("device", "Adaptor number",
2846 "The number of the video adaptor", "0",
2847 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2848 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2849 g_param_spec_string ("device-name", "Adaptor name",
2850 "The name of the video adaptor", NULL,
2851 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2853 * GstXvImageSink:handle-expose
2855 * When enabled, the current frame will always be drawn in response to X
2860 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2861 g_param_spec_boolean ("handle-expose", "Handle expose",
2863 "the current frame will always be drawn in response to X Expose "
2864 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2867 * GstXvImageSink:double-buffer
2869 * Whether to double-buffer the output.
2873 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2874 g_param_spec_boolean ("double-buffer", "Double-buffer",
2875 "Whether to double-buffer the output", TRUE,
2876 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2878 * GstXvImageSink:autopaint-colorkey
2880 * Whether to autofill overlay with colorkey
2884 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2885 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2886 "Whether to autofill overlay with colorkey", TRUE,
2887 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2889 * GstXvImageSink:colorkey
2891 * Color to use for the overlay mask.
2895 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2896 g_param_spec_int ("colorkey", "Colorkey",
2897 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2898 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2901 * GstXvImageSink:draw-borders
2903 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2904 * unused parts of the video area.
2908 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2909 g_param_spec_boolean ("draw-borders", "Colorkey",
2910 "Draw black borders to fill unused area in force-aspect-ratio mode",
2911 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2914 * GstXvImageSink:window-width
2916 * Actual width of the video window.
2920 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2921 g_param_spec_uint64 ("window-width", "window-width",
2922 "Width of the window", 0, G_MAXUINT64, 0,
2923 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2926 * GstXvImageSink:window-height
2928 * Actual height of the video window.
2932 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2933 g_param_spec_uint64 ("window-height", "window-height",
2934 "Height of the window", 0, G_MAXUINT64, 0,
2935 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2937 gobject_class->finalize = gst_xvimagesink_finalize;
2939 gst_element_class_set_details_simple (gstelement_class,
2940 "Video sink", "Sink/Video",
2941 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2943 gst_element_class_add_pad_template (gstelement_class,
2944 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2946 gstelement_class->change_state =
2947 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2949 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2950 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2951 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2952 gstbasesink_class->propose_allocation =
2953 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2954 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2956 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);