2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
112 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
113 * with newer GLib versions (>= 2.31.0) */
114 #define GLIB_DISABLE_DEPRECATION_WARNINGS
121 #include <gst/interfaces/navigation.h>
122 #include <gst/video/videooverlay.h>
123 #include <gst/video/colorbalance.h>
124 /* Helper functions */
125 #include <gst/video/gstvideometa.h>
128 #include "xvimagesink.h"
130 /* Debugging category */
131 #include <gst/gstinfo.h>
133 #include "gst/glib-compat-private.h"
135 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
136 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
137 #define GST_CAT_DEFAULT gst_debug_xvimagesink
142 unsigned long functions;
143 unsigned long decorations;
145 unsigned long status;
147 MotifWmHints, MwmHints;
149 #define MWM_HINTS_DECORATIONS (1L << 1)
151 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
152 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
154 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
156 /* Default template - initiated with class struct to allow gst-register to work
158 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
159 GST_STATIC_PAD_TEMPLATE ("sink",
162 GST_STATIC_CAPS ("video/x-raw, "
163 "framerate = (fraction) [ 0, MAX ], "
164 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
176 PROP_PIXEL_ASPECT_RATIO,
177 PROP_FORCE_ASPECT_RATIO,
183 PROP_AUTOPAINT_COLORKEY,
190 /* ============================================================= */
194 /* ============================================================= */
196 /* =========================================== */
198 /* Object typing & Creation */
200 /* =========================================== */
201 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
202 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
204 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
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));
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;
323 GST_LOG_OBJECT (xvimagesink,
324 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, 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 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1181 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1184 if (xvimagesink->event_thread) {
1185 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1186 xvimagesink->handle_expose, xvimagesink->handle_events);
1187 xvimagesink->running = FALSE;
1188 /* grab thread and mark it as NULL */
1189 thread = xvimagesink->event_thread;
1190 xvimagesink->event_thread = NULL;
1193 GST_OBJECT_UNLOCK (xvimagesink);
1195 /* Wait for our event thread to finish */
1197 g_thread_join (thread);
1202 /* This function calculates the pixel aspect ratio based on the properties
1203 * in the xcontext structure and stores it there. */
1205 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1207 static const gint par[][2] = {
1208 {1, 1}, /* regular screen */
1209 {16, 15}, /* PAL TV */
1210 {11, 10}, /* 525 line Rec.601 video */
1211 {54, 59}, /* 625 line Rec.601 video */
1212 {64, 45}, /* 1280x1024 on 16:9 display */
1213 {5, 3}, /* 1280x1024 on 4:3 display */
1214 {4, 3} /* 800x600 on 16:9 display */
1221 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1223 /* first calculate the "real" ratio based on the X values;
1224 * which is the "physical" w/h divided by the w/h in pixels of the display */
1225 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1226 / (xcontext->heightmm * xcontext->width);
1228 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1230 if (xcontext->width == 720 && xcontext->height == 576) {
1231 ratio = 4.0 * 576 / (3.0 * 720);
1233 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1235 /* now find the one from par[][2] with the lowest delta to the real one */
1239 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1240 gdouble this_delta = DELTA (i);
1242 if (this_delta < delta) {
1248 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1249 par[index][0], par[index][1]);
1251 g_free (xcontext->par);
1252 xcontext->par = g_new0 (GValue, 1);
1253 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1254 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1255 GST_DEBUG ("set xcontext PAR to %d/%d",
1256 gst_value_get_fraction_numerator (xcontext->par),
1257 gst_value_get_fraction_denominator (xcontext->par));
1260 /* This function gets the X Display and global info about it. Everything is
1261 stored in our object and will be cleaned when the object is disposed. Note
1262 here that caps for supported format are generated without any window or
1264 static GstXContext *
1265 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1267 GstXContext *xcontext = NULL;
1268 XPixmapFormatValues *px_formats = NULL;
1269 gint nb_formats = 0, i, j, N_attr;
1270 XvAttribute *xv_attr;
1272 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1273 "XV_BRIGHTNESS", "XV_CONTRAST"
1276 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1278 xcontext = g_new0 (GstXContext, 1);
1279 xcontext->im_format = 0;
1281 g_mutex_lock (xvimagesink->x_lock);
1283 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1285 if (!xcontext->disp) {
1286 g_mutex_unlock (xvimagesink->x_lock);
1288 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1289 ("Could not initialise Xv output"), ("Could not open display"));
1293 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1294 xcontext->screen_num = DefaultScreen (xcontext->disp);
1295 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1296 xcontext->root = DefaultRootWindow (xcontext->disp);
1297 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1298 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1299 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1301 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1302 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1303 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1304 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1306 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1307 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1309 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1310 /* We get supported pixmap formats at supported depth */
1311 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1314 XCloseDisplay (xcontext->disp);
1315 g_mutex_unlock (xvimagesink->x_lock);
1316 g_free (xcontext->par);
1318 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1319 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1323 /* We get bpp value corresponding to our running depth */
1324 for (i = 0; i < nb_formats; i++) {
1325 if (px_formats[i].depth == xcontext->depth)
1326 xcontext->bpp = px_formats[i].bits_per_pixel;
1331 xcontext->endianness =
1332 (ImageByteOrder (xcontext->disp) ==
1333 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1335 /* our caps system handles 24/32bpp RGB as big-endian. */
1336 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1337 xcontext->endianness == G_LITTLE_ENDIAN) {
1338 xcontext->endianness = G_BIG_ENDIAN;
1339 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1340 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1341 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1342 if (xcontext->bpp == 24) {
1343 xcontext->visual->red_mask >>= 8;
1344 xcontext->visual->green_mask >>= 8;
1345 xcontext->visual->blue_mask >>= 8;
1349 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1351 /* Search for XShm extension support */
1353 if (XShmQueryExtension (xcontext->disp) &&
1354 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1355 xcontext->use_xshm = TRUE;
1356 GST_DEBUG ("xvimagesink is using XShm extension");
1358 #endif /* HAVE_XSHM */
1360 xcontext->use_xshm = FALSE;
1361 GST_DEBUG ("xvimagesink is not using XShm extension");
1364 if (!xcontext->caps) {
1365 XCloseDisplay (xcontext->disp);
1366 g_mutex_unlock (xvimagesink->x_lock);
1367 g_free (xcontext->par);
1369 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1373 xv_attr = XvQueryPortAttributes (xcontext->disp,
1374 xcontext->xv_port_id, &N_attr);
1377 /* Generate the channels list */
1378 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1379 XvAttribute *matching_attr = NULL;
1381 /* Retrieve the property atom if it exists. If it doesn't exist,
1382 * the attribute itself must not either, so we can skip */
1383 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1384 if (prop_atom == None)
1387 if (xv_attr != NULL) {
1388 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1389 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1390 matching_attr = xv_attr + j;
1393 if (matching_attr) {
1394 GstColorBalanceChannel *channel;
1396 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1397 channel->label = g_strdup (channels[i]);
1398 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1399 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1401 xcontext->channels_list = g_list_append (xcontext->channels_list,
1404 /* If the colorbalance settings have not been touched we get Xv values
1405 as defaults and update our internal variables */
1406 if (!xvimagesink->cb_changed) {
1409 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1411 /* Normalize val to [-1000, 1000] */
1412 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1413 (double) (channel->max_value - channel->min_value));
1415 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1416 xvimagesink->hue = val;
1417 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1418 xvimagesink->saturation = val;
1419 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1420 xvimagesink->brightness = val;
1421 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1422 xvimagesink->contrast = val;
1430 g_mutex_unlock (xvimagesink->x_lock);
1435 /* This function cleans the X context. Closing the Display, releasing the XV
1436 port and unrefing the caps for supported formats. */
1438 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1440 GList *formats_list, *channels_list;
1441 GstXContext *xcontext;
1444 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1446 GST_OBJECT_LOCK (xvimagesink);
1447 if (xvimagesink->xcontext == NULL) {
1448 GST_OBJECT_UNLOCK (xvimagesink);
1452 /* Take the XContext from the sink and clean it up */
1453 xcontext = xvimagesink->xcontext;
1454 xvimagesink->xcontext = NULL;
1456 GST_OBJECT_UNLOCK (xvimagesink);
1459 formats_list = xcontext->formats_list;
1461 while (formats_list) {
1462 GstXvImageFormat *format = formats_list->data;
1464 gst_caps_unref (format->caps);
1466 formats_list = g_list_next (formats_list);
1469 if (xcontext->formats_list)
1470 g_list_free (xcontext->formats_list);
1472 channels_list = xcontext->channels_list;
1474 while (channels_list) {
1475 GstColorBalanceChannel *channel = channels_list->data;
1477 g_object_unref (channel);
1478 channels_list = g_list_next (channels_list);
1481 if (xcontext->channels_list)
1482 g_list_free (xcontext->channels_list);
1484 gst_caps_unref (xcontext->caps);
1485 if (xcontext->last_caps)
1486 gst_caps_replace (&xcontext->last_caps, NULL);
1488 for (i = 0; i < xcontext->nb_adaptors; i++) {
1489 g_free (xcontext->adaptors[i]);
1492 g_free (xcontext->adaptors);
1494 g_free (xcontext->par);
1496 g_mutex_lock (xvimagesink->x_lock);
1498 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1500 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1502 XCloseDisplay (xcontext->disp);
1504 g_mutex_unlock (xvimagesink->x_lock);
1512 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1514 GstXvImageSink *xvimagesink;
1517 xvimagesink = GST_XVIMAGESINK (bsink);
1519 if (xvimagesink->xcontext) {
1521 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1522 GST_CAPS_INTERSECT_FIRST);
1524 return gst_caps_ref (xvimagesink->xcontext->caps);
1527 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1529 GstCaps *intersection;
1532 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1533 gst_caps_unref (caps);
1534 caps = intersection;
1540 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1542 GstXvImageSink *xvimagesink;
1543 GstStructure *structure;
1544 GstBufferPool *newpool, *oldpool;
1546 guint32 im_format = 0;
1547 gint video_par_n, video_par_d; /* video's PAR */
1548 gint display_par_n, display_par_d; /* display's PAR */
1552 xvimagesink = GST_XVIMAGESINK (bsink);
1554 GST_DEBUG_OBJECT (xvimagesink,
1555 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1556 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1558 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1559 goto incompatible_caps;
1561 if (!gst_video_info_from_caps (&info, caps))
1562 goto invalid_format;
1564 structure = gst_caps_get_structure (caps, 0);
1566 xvimagesink->fps_n = info.fps_n;
1567 xvimagesink->fps_d = info.fps_d;
1569 xvimagesink->video_width = info.width;
1570 xvimagesink->video_height = info.height;
1572 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1573 if (im_format == -1)
1574 goto invalid_format;
1578 /* get aspect ratio from caps if it's present, and
1579 * convert video width and height to a display width and height
1580 * using wd / hd = wv / hv * PARv / PARd */
1582 /* get video's PAR */
1583 video_par_n = info.par_n;
1584 video_par_d = info.par_d;
1586 /* get display's PAR */
1587 if (xvimagesink->par) {
1588 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1589 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1595 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1596 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1599 GST_DEBUG_OBJECT (xvimagesink,
1600 "video width/height: %dx%d, calculated display ratio: %d/%d",
1601 info.width, info.height, num, den);
1603 /* now find a width x height that respects this display ratio.
1604 * prefer those that have one of w/h the same as the incoming video
1605 * using wd / hd = num / den */
1607 /* start with same height, because of interlaced video */
1608 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1609 if (info.height % den == 0) {
1610 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1611 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1612 gst_util_uint64_scale_int (info.height, num, den);
1613 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1614 } else if (info.width % num == 0) {
1615 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1616 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1617 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1618 gst_util_uint64_scale_int (info.width, den, num);
1620 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1621 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1622 gst_util_uint64_scale_int (info.height, num, den);
1623 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1625 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1626 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1628 /* Notify application to set xwindow id now */
1629 g_mutex_lock (xvimagesink->flow_lock);
1630 if (!xvimagesink->xwindow) {
1631 g_mutex_unlock (xvimagesink->flow_lock);
1632 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1634 g_mutex_unlock (xvimagesink->flow_lock);
1637 /* Creating our window and our image with the display size in pixels */
1638 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1639 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1640 goto no_display_size;
1642 g_mutex_lock (xvimagesink->flow_lock);
1643 if (!xvimagesink->xwindow) {
1644 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1645 GST_VIDEO_SINK_WIDTH (xvimagesink),
1646 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1649 xvimagesink->info = info;
1651 /* After a resize, we want to redraw the borders in case the new frame size
1652 * doesn't cover the same area */
1653 xvimagesink->redraw_border = TRUE;
1655 /* create a new pool for the new configuration */
1656 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1658 structure = gst_buffer_pool_get_config (newpool);
1659 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1660 if (!gst_buffer_pool_set_config (newpool, structure))
1663 oldpool = xvimagesink->pool;
1664 xvimagesink->pool = newpool;
1665 g_mutex_unlock (xvimagesink->flow_lock);
1667 /* unref the old sink */
1669 /* we don't deactivate, some elements might still be using it, it will
1670 * be deactivated when the last ref is gone */
1671 gst_object_unref (oldpool);
1679 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1684 GST_DEBUG_OBJECT (xvimagesink,
1685 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1690 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1691 ("Error calculating the output display ratio of the video."));
1696 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1697 ("Error calculating the output display ratio of the video."));
1702 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1703 g_mutex_unlock (xvimagesink->flow_lock);
1708 static GstStateChangeReturn
1709 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1711 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1712 GstXvImageSink *xvimagesink;
1713 GstXContext *xcontext = NULL;
1715 xvimagesink = GST_XVIMAGESINK (element);
1717 switch (transition) {
1718 case GST_STATE_CHANGE_NULL_TO_READY:
1719 /* Initializing the XContext */
1720 if (xvimagesink->xcontext == NULL) {
1721 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1722 if (xcontext == NULL) {
1723 ret = GST_STATE_CHANGE_FAILURE;
1726 GST_OBJECT_LOCK (xvimagesink);
1728 xvimagesink->xcontext = xcontext;
1729 GST_OBJECT_UNLOCK (xvimagesink);
1732 /* update object's par with calculated one if not set yet */
1733 if (!xvimagesink->par) {
1734 xvimagesink->par = g_new0 (GValue, 1);
1735 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1736 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1738 /* call XSynchronize with the current value of synchronous */
1739 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1740 xvimagesink->synchronous ? "TRUE" : "FALSE");
1741 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1742 gst_xvimagesink_update_colorbalance (xvimagesink);
1743 gst_xvimagesink_manage_event_thread (xvimagesink);
1745 case GST_STATE_CHANGE_READY_TO_PAUSED:
1747 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1749 case GST_STATE_CHANGE_PAUSED_TO_READY:
1755 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1757 switch (transition) {
1758 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1760 case GST_STATE_CHANGE_PAUSED_TO_READY:
1761 xvimagesink->fps_n = 0;
1762 xvimagesink->fps_d = 1;
1763 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1764 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1765 g_mutex_lock (xvimagesink->flow_lock);
1766 if (xvimagesink->pool)
1767 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1768 g_mutex_unlock (xvimagesink->flow_lock);
1770 case GST_STATE_CHANGE_READY_TO_NULL:
1771 gst_xvimagesink_reset (xvimagesink);
1782 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1783 GstClockTime * start, GstClockTime * end)
1785 GstXvImageSink *xvimagesink;
1787 xvimagesink = GST_XVIMAGESINK (bsink);
1789 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1790 *start = GST_BUFFER_TIMESTAMP (buf);
1791 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1792 *end = *start + GST_BUFFER_DURATION (buf);
1794 if (xvimagesink->fps_n > 0) {
1796 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1797 xvimagesink->fps_n);
1803 static GstFlowReturn
1804 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1807 GstXvImageSink *xvimagesink;
1808 GstXvImageMeta *meta;
1811 xvimagesink = GST_XVIMAGESINK (vsink);
1813 meta = gst_buffer_get_xvimage_meta (buf);
1815 if (meta && meta->sink == xvimagesink) {
1816 /* If this buffer has been allocated using our buffer management we simply
1817 put the ximage which is in the PRIVATE pointer */
1818 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1823 GstVideoFrame src, dest;
1825 /* Else we have to copy the data into our private image, */
1826 /* if we have one... */
1827 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1829 /* we should have a pool, configured in setcaps */
1830 if (xvimagesink->pool == NULL)
1833 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1834 goto activate_failed;
1836 /* take a buffer form our pool */
1837 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1838 if (res != GST_FLOW_OK)
1841 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1842 "slow copy into bufferpool buffer %p", to_put);
1844 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1845 goto invalid_buffer;
1847 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1848 gst_video_frame_unmap (&src);
1849 goto invalid_buffer;
1852 gst_video_frame_copy (&dest, &src);
1854 gst_video_frame_unmap (&dest);
1855 gst_video_frame_unmap (&src);
1858 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1863 gst_buffer_unref (to_put);
1870 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1871 ("Internal error: can't allocate images"),
1872 ("We don't have a bufferpool negotiated"));
1873 return GST_FLOW_ERROR;
1877 /* No image available. That's very bad ! */
1878 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1883 /* No Window available to put our image into */
1884 GST_WARNING_OBJECT (xvimagesink, "could map image");
1890 /* No Window available to put our image into */
1891 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1892 res = GST_FLOW_ERROR;
1897 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1898 res = GST_FLOW_ERROR;
1904 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1906 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1908 switch (GST_EVENT_TYPE (event)) {
1909 case GST_EVENT_TAG:{
1911 gchar *title = NULL;
1913 gst_event_parse_tag (event, &l);
1914 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1917 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1918 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1928 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1932 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1934 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1935 GstBufferPool *pool;
1936 GstStructure *config;
1941 gst_query_parse_allocation (query, &caps, &need_pool);
1946 g_mutex_lock (xvimagesink->flow_lock);
1947 if ((pool = xvimagesink->pool))
1948 gst_object_ref (pool);
1949 g_mutex_unlock (xvimagesink->flow_lock);
1952 const GstCaps *pcaps;
1954 /* we had a pool, check caps */
1955 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1956 config = gst_buffer_pool_get_config (pool);
1957 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1959 if (!gst_caps_is_equal (caps, pcaps)) {
1960 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1961 /* different caps, we can't use this pool */
1962 gst_object_unref (pool);
1966 if (pool == NULL && need_pool) {
1969 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1970 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1972 if (!gst_video_info_from_caps (&info, caps))
1975 /* the normal size of a frame */
1978 config = gst_buffer_pool_get_config (pool);
1979 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1980 if (!gst_buffer_pool_set_config (pool, config))
1983 /* we need at least 2 buffer because we hold on to the last one */
1984 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1986 /* we also support various metadata */
1987 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE);
1988 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE);
1990 gst_object_unref (pool);
1997 GST_DEBUG_OBJECT (bsink, "no caps specified");
2002 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2007 GST_DEBUG_OBJECT (bsink, "failed setting config");
2012 /* Interfaces stuff */
2014 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2015 GstStructure * structure)
2017 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2020 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2022 GstVideoRectangle src, dst, result;
2023 gdouble x, y, xscale = 1.0, yscale = 1.0;
2025 event = gst_event_new_navigation (structure);
2027 /* We take the flow_lock while we look at the window */
2028 g_mutex_lock (xvimagesink->flow_lock);
2030 if (!xvimagesink->xwindow) {
2031 g_mutex_unlock (xvimagesink->flow_lock);
2035 if (xvimagesink->keep_aspect) {
2036 /* We get the frame position using the calculated geometry from _setcaps
2037 that respect pixel aspect ratios */
2038 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2039 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2040 dst.w = xvimagesink->render_rect.w;
2041 dst.h = xvimagesink->render_rect.h;
2043 gst_video_sink_center_rect (src, dst, &result, TRUE);
2044 result.x += xvimagesink->render_rect.x;
2045 result.y += xvimagesink->render_rect.y;
2047 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2050 g_mutex_unlock (xvimagesink->flow_lock);
2052 /* We calculate scaling using the original video frames geometry to include
2053 pixel aspect ratio scaling. */
2054 xscale = (gdouble) xvimagesink->video_width / result.w;
2055 yscale = (gdouble) xvimagesink->video_height / result.h;
2057 /* Converting pointer coordinates to the non scaled geometry */
2058 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2059 x = MIN (x, result.x + result.w);
2060 x = MAX (x - result.x, 0);
2061 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2062 (gdouble) x * xscale, NULL);
2064 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2065 y = MIN (y, result.y + result.h);
2066 y = MAX (y - result.y, 0);
2067 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2068 (gdouble) y * yscale, NULL);
2071 gst_pad_send_event (peer, event);
2072 gst_object_unref (peer);
2077 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2079 iface->send_event = gst_xvimagesink_navigation_send_event;
2083 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2085 XID xwindow_id = id;
2086 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2087 GstXWindow *xwindow = NULL;
2089 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2091 g_mutex_lock (xvimagesink->flow_lock);
2093 /* If we already use that window return */
2094 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2095 g_mutex_unlock (xvimagesink->flow_lock);
2099 /* If the element has not initialized the X11 context try to do so */
2100 if (!xvimagesink->xcontext &&
2101 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2102 g_mutex_unlock (xvimagesink->flow_lock);
2103 /* we have thrown a GST_ELEMENT_ERROR now */
2107 gst_xvimagesink_update_colorbalance (xvimagesink);
2109 /* If a window is there already we destroy it */
2110 if (xvimagesink->xwindow) {
2111 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2112 xvimagesink->xwindow = NULL;
2115 /* If the xid is 0 we go back to an internal window */
2116 if (xwindow_id == 0) {
2117 /* If no width/height caps nego did not happen window will be created
2118 during caps nego then */
2119 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2120 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2122 gst_xvimagesink_xwindow_new (xvimagesink,
2123 GST_VIDEO_SINK_WIDTH (xvimagesink),
2124 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2127 XWindowAttributes attr;
2129 xwindow = g_new0 (GstXWindow, 1);
2130 xwindow->win = xwindow_id;
2132 /* Set the event we want to receive and create a GC */
2133 g_mutex_lock (xvimagesink->x_lock);
2135 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2137 xwindow->width = attr.width;
2138 xwindow->height = attr.height;
2139 xwindow->internal = FALSE;
2140 if (!xvimagesink->have_render_rect) {
2141 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2142 xvimagesink->render_rect.w = attr.width;
2143 xvimagesink->render_rect.h = attr.height;
2145 if (xvimagesink->handle_events) {
2146 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2147 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2151 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2152 xwindow->win, 0, NULL);
2153 g_mutex_unlock (xvimagesink->x_lock);
2157 xvimagesink->xwindow = xwindow;
2159 g_mutex_unlock (xvimagesink->flow_lock);
2163 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2165 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2167 GST_DEBUG ("doing expose");
2168 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2169 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2173 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2174 gboolean handle_events)
2176 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2178 xvimagesink->handle_events = handle_events;
2180 g_mutex_lock (xvimagesink->flow_lock);
2182 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2183 g_mutex_unlock (xvimagesink->flow_lock);
2187 g_mutex_lock (xvimagesink->x_lock);
2189 if (handle_events) {
2190 if (xvimagesink->xwindow->internal) {
2191 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2192 ExposureMask | StructureNotifyMask | PointerMotionMask |
2193 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2195 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2196 ExposureMask | StructureNotifyMask | PointerMotionMask |
2197 KeyPressMask | KeyReleaseMask);
2200 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2203 g_mutex_unlock (xvimagesink->x_lock);
2205 g_mutex_unlock (xvimagesink->flow_lock);
2209 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2210 gint width, gint height)
2212 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2214 /* FIXME: how about some locking? */
2215 if (width >= 0 && height >= 0) {
2216 xvimagesink->render_rect.x = x;
2217 xvimagesink->render_rect.y = y;
2218 xvimagesink->render_rect.w = width;
2219 xvimagesink->render_rect.h = height;
2220 xvimagesink->have_render_rect = TRUE;
2222 xvimagesink->render_rect.x = 0;
2223 xvimagesink->render_rect.y = 0;
2224 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2225 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2226 xvimagesink->have_render_rect = FALSE;
2231 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2233 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2234 iface->expose = gst_xvimagesink_expose;
2235 iface->handle_events = gst_xvimagesink_set_event_handling;
2236 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2239 static const GList *
2240 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2242 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2244 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2246 if (xvimagesink->xcontext)
2247 return xvimagesink->xcontext->channels_list;
2253 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2254 GstColorBalanceChannel * channel, gint value)
2256 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2258 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2259 g_return_if_fail (channel->label != NULL);
2261 xvimagesink->cb_changed = TRUE;
2263 /* Normalize val to [-1000, 1000] */
2264 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2265 (double) (channel->max_value - channel->min_value));
2267 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2268 xvimagesink->hue = value;
2269 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2270 xvimagesink->saturation = value;
2271 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2272 xvimagesink->contrast = value;
2273 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2274 xvimagesink->brightness = value;
2276 g_warning ("got an unknown channel %s", channel->label);
2280 gst_xvimagesink_update_colorbalance (xvimagesink);
2284 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2285 GstColorBalanceChannel * channel)
2287 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2290 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2291 g_return_val_if_fail (channel->label != NULL, 0);
2293 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2294 value = xvimagesink->hue;
2295 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2296 value = xvimagesink->saturation;
2297 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2298 value = xvimagesink->contrast;
2299 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2300 value = xvimagesink->brightness;
2302 g_warning ("got an unknown channel %s", channel->label);
2305 /* Normalize val to [channel->min_value, channel->max_value] */
2306 value = channel->min_value + (channel->max_value - channel->min_value) *
2307 (value + 1000) / 2000;
2313 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2315 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2316 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2317 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2318 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2322 static const GList *
2323 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2325 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2326 static GList *list = NULL;
2329 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2331 g_list_append (list, g_object_class_find_property (klass,
2332 "autopaint-colorkey"));
2334 g_list_append (list, g_object_class_find_property (klass,
2337 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2344 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2345 guint prop_id, const GParamSpec * pspec)
2347 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2351 case PROP_AUTOPAINT_COLORKEY:
2352 case PROP_DOUBLE_BUFFER:
2354 GST_DEBUG_OBJECT (xvimagesink,
2355 "probing device list and get capabilities");
2356 if (!xvimagesink->xcontext) {
2357 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2358 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2362 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2368 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2369 guint prop_id, const GParamSpec * pspec)
2371 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2372 gboolean ret = FALSE;
2376 case PROP_AUTOPAINT_COLORKEY:
2377 case PROP_DOUBLE_BUFFER:
2379 if (xvimagesink->xcontext != NULL) {
2386 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2393 static GValueArray *
2394 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2395 guint prop_id, const GParamSpec * pspec)
2397 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2398 GValueArray *array = NULL;
2400 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2401 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2410 GValue value = { 0 };
2412 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2413 g_value_init (&value, G_TYPE_STRING);
2415 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2416 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2418 g_value_set_string (&value, adaptor_id_s);
2419 g_value_array_append (array, &value);
2420 g_free (adaptor_id_s);
2422 g_value_unset (&value);
2425 case PROP_AUTOPAINT_COLORKEY:
2426 if (xvimagesink->have_autopaint_colorkey) {
2427 GValue value = { 0 };
2429 array = g_value_array_new (2);
2430 g_value_init (&value, G_TYPE_BOOLEAN);
2431 g_value_set_boolean (&value, FALSE);
2432 g_value_array_append (array, &value);
2433 g_value_set_boolean (&value, TRUE);
2434 g_value_array_append (array, &value);
2435 g_value_unset (&value);
2438 case PROP_DOUBLE_BUFFER:
2439 if (xvimagesink->have_double_buffer) {
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);
2452 if (xvimagesink->have_colorkey) {
2453 GValue value = { 0 };
2455 array = g_value_array_new (1);
2456 g_value_init (&value, GST_TYPE_INT_RANGE);
2457 gst_value_set_int_range (&value, 0, 0xffffff);
2458 g_value_array_append (array, &value);
2459 g_value_unset (&value);
2463 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2472 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2475 iface->get_properties = gst_xvimagesink_probe_get_properties;
2476 iface->probe_property = gst_xvimagesink_probe_probe_property;
2477 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2478 iface->get_values = gst_xvimagesink_probe_get_values;
2482 /* =========================================== */
2484 /* Init & Class init */
2486 /* =========================================== */
2489 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2490 const GValue * value, GParamSpec * pspec)
2492 GstXvImageSink *xvimagesink;
2494 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2496 xvimagesink = GST_XVIMAGESINK (object);
2500 xvimagesink->hue = g_value_get_int (value);
2501 xvimagesink->cb_changed = TRUE;
2502 gst_xvimagesink_update_colorbalance (xvimagesink);
2505 xvimagesink->contrast = g_value_get_int (value);
2506 xvimagesink->cb_changed = TRUE;
2507 gst_xvimagesink_update_colorbalance (xvimagesink);
2509 case PROP_BRIGHTNESS:
2510 xvimagesink->brightness = g_value_get_int (value);
2511 xvimagesink->cb_changed = TRUE;
2512 gst_xvimagesink_update_colorbalance (xvimagesink);
2514 case PROP_SATURATION:
2515 xvimagesink->saturation = g_value_get_int (value);
2516 xvimagesink->cb_changed = TRUE;
2517 gst_xvimagesink_update_colorbalance (xvimagesink);
2520 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2522 case PROP_SYNCHRONOUS:
2523 xvimagesink->synchronous = g_value_get_boolean (value);
2524 if (xvimagesink->xcontext) {
2525 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2526 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2527 xvimagesink->synchronous ? "TRUE" : "FALSE");
2530 case PROP_PIXEL_ASPECT_RATIO:
2531 g_free (xvimagesink->par);
2532 xvimagesink->par = g_new0 (GValue, 1);
2533 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2534 if (!g_value_transform (value, xvimagesink->par)) {
2535 g_warning ("Could not transform string to aspect ratio");
2536 gst_value_set_fraction (xvimagesink->par, 1, 1);
2538 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2539 gst_value_get_fraction_numerator (xvimagesink->par),
2540 gst_value_get_fraction_denominator (xvimagesink->par));
2542 case PROP_FORCE_ASPECT_RATIO:
2543 xvimagesink->keep_aspect = g_value_get_boolean (value);
2545 case PROP_HANDLE_EVENTS:
2546 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2547 g_value_get_boolean (value));
2548 gst_xvimagesink_manage_event_thread (xvimagesink);
2551 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2553 case PROP_HANDLE_EXPOSE:
2554 xvimagesink->handle_expose = g_value_get_boolean (value);
2555 gst_xvimagesink_manage_event_thread (xvimagesink);
2557 case PROP_DOUBLE_BUFFER:
2558 xvimagesink->double_buffer = g_value_get_boolean (value);
2560 case PROP_AUTOPAINT_COLORKEY:
2561 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2564 xvimagesink->colorkey = g_value_get_int (value);
2566 case PROP_DRAW_BORDERS:
2567 xvimagesink->draw_borders = g_value_get_boolean (value);
2570 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2576 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2577 GValue * value, GParamSpec * pspec)
2579 GstXvImageSink *xvimagesink;
2581 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2583 xvimagesink = GST_XVIMAGESINK (object);
2587 g_value_set_int (value, xvimagesink->hue);
2590 g_value_set_int (value, xvimagesink->contrast);
2592 case PROP_BRIGHTNESS:
2593 g_value_set_int (value, xvimagesink->brightness);
2595 case PROP_SATURATION:
2596 g_value_set_int (value, xvimagesink->saturation);
2599 g_value_set_string (value, xvimagesink->display_name);
2601 case PROP_SYNCHRONOUS:
2602 g_value_set_boolean (value, xvimagesink->synchronous);
2604 case PROP_PIXEL_ASPECT_RATIO:
2605 if (xvimagesink->par)
2606 g_value_transform (xvimagesink->par, value);
2608 case PROP_FORCE_ASPECT_RATIO:
2609 g_value_set_boolean (value, xvimagesink->keep_aspect);
2611 case PROP_HANDLE_EVENTS:
2612 g_value_set_boolean (value, xvimagesink->handle_events);
2616 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2618 g_value_set_string (value, adaptor_no_s);
2619 g_free (adaptor_no_s);
2622 case PROP_DEVICE_NAME:
2623 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2624 g_value_set_string (value,
2625 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2627 g_value_set_string (value, NULL);
2630 case PROP_HANDLE_EXPOSE:
2631 g_value_set_boolean (value, xvimagesink->handle_expose);
2633 case PROP_DOUBLE_BUFFER:
2634 g_value_set_boolean (value, xvimagesink->double_buffer);
2636 case PROP_AUTOPAINT_COLORKEY:
2637 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2640 g_value_set_int (value, xvimagesink->colorkey);
2642 case PROP_DRAW_BORDERS:
2643 g_value_set_boolean (value, xvimagesink->draw_borders);
2645 case PROP_WINDOW_WIDTH:
2646 if (xvimagesink->xwindow)
2647 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2649 g_value_set_uint64 (value, 0);
2651 case PROP_WINDOW_HEIGHT:
2652 if (xvimagesink->xwindow)
2653 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2655 g_value_set_uint64 (value, 0);
2658 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2664 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2668 GST_OBJECT_LOCK (xvimagesink);
2669 xvimagesink->running = FALSE;
2670 /* grab thread and mark it as NULL */
2671 thread = xvimagesink->event_thread;
2672 xvimagesink->event_thread = NULL;
2673 GST_OBJECT_UNLOCK (xvimagesink);
2675 /* Wait for our event thread to finish before we clean up our stuff. */
2677 g_thread_join (thread);
2679 if (xvimagesink->cur_image) {
2680 gst_buffer_unref (xvimagesink->cur_image);
2681 xvimagesink->cur_image = NULL;
2684 g_mutex_lock (xvimagesink->flow_lock);
2686 if (xvimagesink->pool) {
2687 gst_object_unref (xvimagesink->pool);
2688 xvimagesink->pool = NULL;
2691 if (xvimagesink->xwindow) {
2692 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2693 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2694 xvimagesink->xwindow = NULL;
2696 g_mutex_unlock (xvimagesink->flow_lock);
2698 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2699 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2700 xvimagesink->have_render_rect = FALSE;
2702 gst_xvimagesink_xcontext_clear (xvimagesink);
2705 /* Finalize is called only once, dispose can be called multiple times.
2706 * We use mutexes and don't reset stuff to NULL here so let's register
2709 gst_xvimagesink_finalize (GObject * object)
2711 GstXvImageSink *xvimagesink;
2713 xvimagesink = GST_XVIMAGESINK (object);
2715 gst_xvimagesink_reset (xvimagesink);
2717 if (xvimagesink->display_name) {
2718 g_free (xvimagesink->display_name);
2719 xvimagesink->display_name = NULL;
2722 if (xvimagesink->par) {
2723 g_free (xvimagesink->par);
2724 xvimagesink->par = NULL;
2726 if (xvimagesink->x_lock) {
2727 g_mutex_free (xvimagesink->x_lock);
2728 xvimagesink->x_lock = NULL;
2730 if (xvimagesink->flow_lock) {
2731 g_mutex_free (xvimagesink->flow_lock);
2732 xvimagesink->flow_lock = NULL;
2735 g_free (xvimagesink->media_title);
2737 G_OBJECT_CLASS (parent_class)->finalize (object);
2741 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2743 xvimagesink->display_name = NULL;
2744 xvimagesink->adaptor_no = 0;
2745 xvimagesink->xcontext = NULL;
2746 xvimagesink->xwindow = NULL;
2747 xvimagesink->cur_image = NULL;
2749 xvimagesink->hue = xvimagesink->saturation = 0;
2750 xvimagesink->contrast = xvimagesink->brightness = 0;
2751 xvimagesink->cb_changed = FALSE;
2753 xvimagesink->fps_n = 0;
2754 xvimagesink->fps_d = 0;
2755 xvimagesink->video_width = 0;
2756 xvimagesink->video_height = 0;
2758 xvimagesink->x_lock = g_mutex_new ();
2759 xvimagesink->flow_lock = g_mutex_new ();
2761 xvimagesink->pool = NULL;
2763 xvimagesink->synchronous = FALSE;
2764 xvimagesink->double_buffer = TRUE;
2765 xvimagesink->running = FALSE;
2766 xvimagesink->keep_aspect = FALSE;
2767 xvimagesink->handle_events = TRUE;
2768 xvimagesink->par = NULL;
2769 xvimagesink->handle_expose = TRUE;
2770 xvimagesink->autopaint_colorkey = TRUE;
2772 /* on 16bit displays this becomes r,g,b = 1,2,3
2773 * on 24bit displays this becomes r,g,b = 8,8,16
2774 * as a port atom value
2776 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2777 xvimagesink->draw_borders = TRUE;
2781 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2783 GObjectClass *gobject_class;
2784 GstElementClass *gstelement_class;
2785 GstBaseSinkClass *gstbasesink_class;
2786 GstVideoSinkClass *videosink_class;
2788 gobject_class = (GObjectClass *) klass;
2789 gstelement_class = (GstElementClass *) klass;
2790 gstbasesink_class = (GstBaseSinkClass *) klass;
2791 videosink_class = (GstVideoSinkClass *) klass;
2793 parent_class = g_type_class_peek_parent (klass);
2795 gobject_class->set_property = gst_xvimagesink_set_property;
2796 gobject_class->get_property = gst_xvimagesink_get_property;
2798 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2799 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2800 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2801 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2802 g_param_spec_int ("brightness", "Brightness",
2803 "The brightness of the video", -1000, 1000, 0,
2804 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2805 g_object_class_install_property (gobject_class, PROP_HUE,
2806 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2807 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2808 g_object_class_install_property (gobject_class, PROP_SATURATION,
2809 g_param_spec_int ("saturation", "Saturation",
2810 "The saturation of the video", -1000, 1000, 0,
2811 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2812 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2813 g_param_spec_string ("display", "Display", "X Display name", NULL,
2814 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2815 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2816 g_param_spec_boolean ("synchronous", "Synchronous",
2817 "When enabled, runs the X display in synchronous mode. "
2818 "(unrelated to A/V sync, used only for debugging)", FALSE,
2819 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2820 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2821 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2822 "The pixel aspect ratio of the device", "1/1",
2823 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2824 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2825 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2826 "When enabled, scaling will respect original aspect ratio", FALSE,
2827 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2828 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2829 g_param_spec_boolean ("handle-events", "Handle XEvents",
2830 "When enabled, XEvents will be selected and handled", TRUE,
2831 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2832 g_object_class_install_property (gobject_class, PROP_DEVICE,
2833 g_param_spec_string ("device", "Adaptor number",
2834 "The number of the video adaptor", "0",
2835 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2836 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2837 g_param_spec_string ("device-name", "Adaptor name",
2838 "The name of the video adaptor", NULL,
2839 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2841 * GstXvImageSink:handle-expose
2843 * When enabled, the current frame will always be drawn in response to X
2848 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2849 g_param_spec_boolean ("handle-expose", "Handle expose",
2851 "the current frame will always be drawn in response to X Expose "
2852 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2855 * GstXvImageSink:double-buffer
2857 * Whether to double-buffer the output.
2861 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2862 g_param_spec_boolean ("double-buffer", "Double-buffer",
2863 "Whether to double-buffer the output", TRUE,
2864 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2866 * GstXvImageSink:autopaint-colorkey
2868 * Whether to autofill overlay with colorkey
2872 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2873 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2874 "Whether to autofill overlay with colorkey", TRUE,
2875 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2877 * GstXvImageSink:colorkey
2879 * Color to use for the overlay mask.
2883 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2884 g_param_spec_int ("colorkey", "Colorkey",
2885 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2886 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2889 * GstXvImageSink:draw-borders
2891 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2892 * unused parts of the video area.
2896 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2897 g_param_spec_boolean ("draw-borders", "Colorkey",
2898 "Draw black borders to fill unused area in force-aspect-ratio mode",
2899 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2902 * GstXvImageSink:window-width
2904 * Actual width of the video window.
2908 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2909 g_param_spec_uint64 ("window-width", "window-width",
2910 "Width of the window", 0, G_MAXUINT64, 0,
2911 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2914 * GstXvImageSink:window-height
2916 * Actual height of the video window.
2920 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2921 g_param_spec_uint64 ("window-height", "window-height",
2922 "Height of the window", 0, G_MAXUINT64, 0,
2923 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2925 gobject_class->finalize = gst_xvimagesink_finalize;
2927 gst_element_class_set_details_simple (gstelement_class,
2928 "Video sink", "Sink/Video",
2929 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2931 gst_element_class_add_pad_template (gstelement_class,
2932 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2934 gstelement_class->change_state =
2935 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2937 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2938 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2939 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2940 gstbasesink_class->propose_allocation =
2941 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2942 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2944 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);