2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/video/videooverlay.h>
119 #include <gst/video/colorbalance.h>
120 /* Helper functions */
121 #include <gst/video/gstvideometa.h>
124 #include "xvimagesink.h"
126 /* Debugging category */
127 #include <gst/gstinfo.h>
129 #include "gst/glib-compat-private.h"
131 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
132 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
133 #define GST_CAT_DEFAULT gst_debug_xvimagesink
138 unsigned long functions;
139 unsigned long decorations;
141 unsigned long status;
143 MotifWmHints, MwmHints;
145 #define MWM_HINTS_DECORATIONS (1L << 1)
147 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
148 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
150 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
152 /* Default template - initiated with class struct to allow gst-register to work
154 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
155 GST_STATIC_PAD_TEMPLATE ("sink",
158 GST_STATIC_CAPS ("video/x-raw, "
159 "framerate = (fraction) [ 0, MAX ], "
160 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
172 PROP_PIXEL_ASPECT_RATIO,
173 PROP_FORCE_ASPECT_RATIO,
179 PROP_AUTOPAINT_COLORKEY,
186 /* ============================================================= */
190 /* ============================================================= */
192 /* =========================================== */
194 /* Object typing & Creation */
196 /* =========================================== */
197 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
198 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
200 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
202 #define gst_xvimagesink_parent_class parent_class
203 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
204 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
205 gst_xvimagesink_navigation_init);
206 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
207 gst_xvimagesink_video_overlay_init);
208 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
209 gst_xvimagesink_colorbalance_init));
212 /* ============================================================= */
214 /* Private Methods */
216 /* ============================================================= */
219 /* We are called with the x_lock taken */
221 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
222 GstXWindow * xwindow, GstVideoRectangle rect)
226 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
227 g_return_if_fail (xwindow != NULL);
229 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
230 xvimagesink->xcontext->black);
233 if (rect.x > xvimagesink->render_rect.x) {
234 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
235 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
236 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
240 t1 = rect.x + rect.w;
241 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
243 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
244 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
248 if (rect.y > xvimagesink->render_rect.y) {
249 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
250 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
251 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
255 t1 = rect.y + rect.h;
256 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
258 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
259 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
263 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
264 * if no window was available */
266 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
268 GstXvImageMeta *meta;
269 GstVideoCropMeta *crop;
270 GstVideoRectangle result;
271 gboolean draw_border = FALSE;
272 GstVideoRectangle src, dst;
274 /* We take the flow_lock. If expose is in there we don't want to run
275 concurrently from the data flow thread */
276 g_mutex_lock (xvimagesink->flow_lock);
278 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
279 g_mutex_unlock (xvimagesink->flow_lock);
283 /* Draw borders when displaying the first frame. After this
284 draw borders only on expose event or after a size change. */
285 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
289 /* Store a reference to the last image we put, lose the previous one */
290 if (xvimage && xvimagesink->cur_image != xvimage) {
291 if (xvimagesink->cur_image) {
292 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
293 gst_buffer_unref (xvimagesink->cur_image);
295 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
296 xvimagesink->cur_image = gst_buffer_ref (xvimage);
299 /* Expose sends a NULL image, we take the latest frame */
301 if (xvimagesink->cur_image) {
303 xvimage = xvimagesink->cur_image;
305 g_mutex_unlock (xvimagesink->flow_lock);
310 meta = gst_buffer_get_xvimage_meta (xvimage);
312 crop = gst_buffer_get_video_crop_meta (xvimage);
315 src.x = crop->x + meta->x;
316 src.y = crop->y + meta->y;
318 src.h = crop->height;
323 src.h = meta->height;
326 if (xvimagesink->keep_aspect) {
327 dst.w = xvimagesink->render_rect.w;
328 dst.h = xvimagesink->render_rect.h;
330 gst_video_sink_center_rect (src, dst, &result, TRUE);
331 result.x += xvimagesink->render_rect.x;
332 result.y += xvimagesink->render_rect.y;
334 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
337 g_mutex_lock (xvimagesink->x_lock);
339 if (draw_border && xvimagesink->draw_borders) {
340 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
342 xvimagesink->redraw_border = FALSE;
345 if (xvimagesink->xcontext->use_xshm) {
346 GST_LOG_OBJECT (xvimagesink,
347 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
348 GST_PTR_FORMAT, meta->width, meta->height,
349 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
351 XvShmPutImage (xvimagesink->xcontext->disp,
352 xvimagesink->xcontext->xv_port_id,
353 xvimagesink->xwindow->win,
354 xvimagesink->xwindow->gc, meta->xvimage,
355 src.x, src.y, src.w, src.h,
356 result.x, result.y, result.w, result.h, FALSE);
358 #endif /* HAVE_XSHM */
360 XvPutImage (xvimagesink->xcontext->disp,
361 xvimagesink->xcontext->xv_port_id,
362 xvimagesink->xwindow->win,
363 xvimagesink->xwindow->gc, meta->xvimage,
364 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
367 XSync (xvimagesink->xcontext->disp, FALSE);
369 g_mutex_unlock (xvimagesink->x_lock);
371 g_mutex_unlock (xvimagesink->flow_lock);
377 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
380 Atom hints_atom = None;
383 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
384 g_return_val_if_fail (window != NULL, FALSE);
386 g_mutex_lock (xvimagesink->x_lock);
388 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
390 if (hints_atom == None) {
391 g_mutex_unlock (xvimagesink->x_lock);
395 hints = g_malloc0 (sizeof (MotifWmHints));
397 hints->flags |= MWM_HINTS_DECORATIONS;
398 hints->decorations = 1 << 0;
400 XChangeProperty (xvimagesink->xcontext->disp, window->win,
401 hints_atom, hints_atom, 32, PropModeReplace,
402 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
404 XSync (xvimagesink->xcontext->disp, FALSE);
406 g_mutex_unlock (xvimagesink->x_lock);
414 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
415 GstXWindow * xwindow, const gchar * media_title)
418 g_free (xvimagesink->media_title);
419 xvimagesink->media_title = g_strdup (media_title);
422 /* we have a window */
423 if (xwindow->internal) {
424 XTextProperty xproperty;
425 const gchar *app_name;
426 const gchar *title = NULL;
427 gchar *title_mem = NULL;
429 /* set application name as a title */
430 app_name = g_get_application_name ();
432 if (app_name && xvimagesink->media_title) {
433 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
435 } else if (app_name) {
437 } else if (xvimagesink->media_title) {
438 title = xvimagesink->media_title;
442 if ((XStringListToTextProperty (((char **) &title), 1,
444 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
445 XFree (xproperty.value);
454 /* This function handles a GstXWindow creation
455 * The width and height are the actual pixel size on the display */
457 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
458 gint width, gint height)
460 GstXWindow *xwindow = NULL;
463 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
465 xwindow = g_new0 (GstXWindow, 1);
467 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
468 xvimagesink->render_rect.w = width;
469 xvimagesink->render_rect.h = height;
471 xwindow->width = width;
472 xwindow->height = height;
473 xwindow->internal = TRUE;
475 g_mutex_lock (xvimagesink->x_lock);
477 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
478 xvimagesink->xcontext->root,
479 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
481 /* We have to do that to prevent X from redrawing the background on
482 * ConfigureNotify. This takes away flickering of video when resizing. */
483 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
485 /* set application name as a title */
486 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
488 if (xvimagesink->handle_events) {
491 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
492 StructureNotifyMask | PointerMotionMask | KeyPressMask |
493 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
495 /* Tell the window manager we'd like delete client messages instead of
497 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
498 "WM_DELETE_WINDOW", True);
499 if (wm_delete != None) {
500 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
505 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
506 xwindow->win, 0, &values);
508 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
510 XSync (xvimagesink->xcontext->disp, FALSE);
512 g_mutex_unlock (xvimagesink->x_lock);
514 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
516 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
522 /* This function destroys a GstXWindow */
524 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
525 GstXWindow * xwindow)
527 g_return_if_fail (xwindow != NULL);
528 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
530 g_mutex_lock (xvimagesink->x_lock);
532 /* If we did not create that window we just free the GC and let it live */
533 if (xwindow->internal)
534 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
536 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
538 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
540 XSync (xvimagesink->xcontext->disp, FALSE);
542 g_mutex_unlock (xvimagesink->x_lock);
548 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
550 XWindowAttributes attr;
552 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
554 /* Update the window geometry */
555 g_mutex_lock (xvimagesink->x_lock);
556 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
557 g_mutex_unlock (xvimagesink->x_lock);
561 XGetWindowAttributes (xvimagesink->xcontext->disp,
562 xvimagesink->xwindow->win, &attr);
564 xvimagesink->xwindow->width = attr.width;
565 xvimagesink->xwindow->height = attr.height;
567 if (!xvimagesink->have_render_rect) {
568 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
569 xvimagesink->render_rect.w = attr.width;
570 xvimagesink->render_rect.h = attr.height;
573 g_mutex_unlock (xvimagesink->x_lock);
577 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
578 GstXWindow * xwindow)
580 g_return_if_fail (xwindow != NULL);
581 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
583 g_mutex_lock (xvimagesink->x_lock);
585 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
588 XSync (xvimagesink->xcontext->disp, FALSE);
590 g_mutex_unlock (xvimagesink->x_lock);
593 /* This function commits our internal colorbalance settings to our grabbed Xv
594 port. If the xcontext is not initialized yet it simply returns */
596 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
598 GList *channels = NULL;
600 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
602 /* If we haven't initialized the X context we can't update anything */
603 if (xvimagesink->xcontext == NULL)
606 /* Don't set the attributes if they haven't been changed, to avoid
607 * rounding errors changing the values */
608 if (!xvimagesink->cb_changed)
611 /* For each channel of the colorbalance we calculate the correct value
612 doing range conversion and then set the Xv port attribute to match our
614 channels = xvimagesink->xcontext->channels_list;
617 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
618 GstColorBalanceChannel *channel = NULL;
621 gdouble convert_coef;
623 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
624 g_object_ref (channel);
626 /* Our range conversion coef */
627 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
629 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
630 value = xvimagesink->hue;
631 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
632 value = xvimagesink->saturation;
633 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
634 value = xvimagesink->contrast;
635 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
636 value = xvimagesink->brightness;
638 g_warning ("got an unknown channel %s", channel->label);
639 g_object_unref (channel);
643 /* Committing to Xv port */
644 g_mutex_lock (xvimagesink->x_lock);
646 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
647 if (prop_atom != None) {
650 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
651 XvSetPortAttribute (xvimagesink->xcontext->disp,
652 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
654 g_mutex_unlock (xvimagesink->x_lock);
656 g_object_unref (channel);
658 channels = g_list_next (channels);
662 /* This function handles XEvents that might be in the queue. It generates
663 GstEvent that will be sent upstream in the pipeline to handle interactivity
664 and navigation. It will also listen for configure events on the window to
665 trigger caps renegotiation so on the fly software scaling can work. */
667 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
670 guint pointer_x = 0, pointer_y = 0;
671 gboolean pointer_moved = FALSE;
672 gboolean exposed = FALSE, configured = FALSE;
674 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
676 /* Handle Interaction, produces navigation events */
678 /* We get all pointer motion events, only the last position is
680 g_mutex_lock (xvimagesink->flow_lock);
681 g_mutex_lock (xvimagesink->x_lock);
682 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
683 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
684 g_mutex_unlock (xvimagesink->x_lock);
685 g_mutex_unlock (xvimagesink->flow_lock);
689 pointer_x = e.xmotion.x;
690 pointer_y = e.xmotion.y;
691 pointer_moved = TRUE;
696 g_mutex_lock (xvimagesink->flow_lock);
697 g_mutex_lock (xvimagesink->x_lock);
701 g_mutex_unlock (xvimagesink->x_lock);
702 g_mutex_unlock (xvimagesink->flow_lock);
704 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
705 pointer_x, pointer_y);
706 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
707 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
709 g_mutex_lock (xvimagesink->flow_lock);
710 g_mutex_lock (xvimagesink->x_lock);
713 /* We get all events on our window to throw them upstream */
714 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
715 xvimagesink->xwindow->win,
716 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
720 /* We lock only for the X function call */
721 g_mutex_unlock (xvimagesink->x_lock);
722 g_mutex_unlock (xvimagesink->flow_lock);
726 /* Mouse button pressed over our window. We send upstream
727 events for interactivity/navigation */
728 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
729 e.xbutton.button, e.xbutton.x, e.xbutton.y);
730 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
731 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
734 /* Mouse button released over our window. We send upstream
735 events for interactivity/navigation */
736 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
737 e.xbutton.button, e.xbutton.x, e.xbutton.y);
738 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
739 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
743 /* Key pressed/released over our window. We send upstream
744 events for interactivity/navigation */
745 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
746 e.xkey.keycode, e.xkey.x, e.xkey.y);
747 g_mutex_lock (xvimagesink->x_lock);
748 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
750 g_mutex_unlock (xvimagesink->x_lock);
751 if (keysym != NoSymbol) {
752 char *key_str = NULL;
754 g_mutex_lock (xvimagesink->x_lock);
755 key_str = XKeysymToString (keysym);
756 g_mutex_unlock (xvimagesink->x_lock);
757 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
758 e.type == KeyPress ? "key-press" : "key-release", key_str);
760 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
761 e.type == KeyPress ? "key-press" : "key-release", "unknown");
765 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
768 g_mutex_lock (xvimagesink->flow_lock);
769 g_mutex_lock (xvimagesink->x_lock);
773 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
774 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
779 case ConfigureNotify:
780 g_mutex_unlock (xvimagesink->x_lock);
781 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
782 g_mutex_lock (xvimagesink->x_lock);
790 if (xvimagesink->handle_expose && (exposed || configured)) {
791 g_mutex_unlock (xvimagesink->x_lock);
792 g_mutex_unlock (xvimagesink->flow_lock);
794 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
796 g_mutex_lock (xvimagesink->flow_lock);
797 g_mutex_lock (xvimagesink->x_lock);
800 /* Handle Display events */
801 while (XPending (xvimagesink->xcontext->disp)) {
802 XNextEvent (xvimagesink->xcontext->disp, &e);
808 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
809 "WM_DELETE_WINDOW", True);
810 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
811 /* Handle window deletion by posting an error on the bus */
812 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
813 ("Output window was closed"), (NULL));
815 g_mutex_unlock (xvimagesink->x_lock);
816 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
817 xvimagesink->xwindow = NULL;
818 g_mutex_lock (xvimagesink->x_lock);
827 g_mutex_unlock (xvimagesink->x_lock);
828 g_mutex_unlock (xvimagesink->flow_lock);
832 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
833 XvAdaptorInfo * adaptors, int adaptor_no)
838 /* Do we support XvImageMask ? */
839 if (!(adaptors[adaptor_no].type & XvImageMask)) {
840 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
841 adaptors[adaptor_no].name);
845 /* We found such an adaptor, looking for an available port */
846 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
847 /* We try to grab the port */
848 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
849 if (Success == res) {
850 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
851 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
852 adaptors[adaptor_no].num_ports);
854 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
855 adaptors[adaptor_no].name, res);
860 /* This function generates a caps with all supported format by the first
861 Xv grabable port we find. We store each one of the supported formats in a
862 format list and append the format to a newly created caps that we return
863 If this function does not return NULL because of an error, it also grabs
864 the port via XvGrabPort */
866 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
867 GstXContext * xcontext)
870 XvAdaptorInfo *adaptors;
872 XvImageFormatValues *formats = NULL;
874 XvEncodingInfo *encodings = NULL;
875 gulong max_w = G_MAXINT, max_h = G_MAXINT;
876 GstCaps *caps = NULL;
877 GstCaps *rgb_caps = NULL;
879 g_return_val_if_fail (xcontext != NULL, NULL);
881 /* First let's check that XVideo extension is available */
882 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
883 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
884 ("Could not initialise Xv output"),
885 ("XVideo extension is not available"));
889 /* Then we get adaptors list */
890 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
891 &xcontext->nb_adaptors, &adaptors)) {
892 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
893 ("Could not initialise Xv output"),
894 ("Failed getting XV adaptors list"));
898 xcontext->xv_port_id = 0;
900 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
903 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
905 /* Now fill up our adaptor name array */
906 for (i = 0; i < xcontext->nb_adaptors; i++) {
907 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
910 if (xvimagesink->adaptor_no >= 0 &&
911 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
912 /* Find xv port from user defined adaptor */
913 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
914 xvimagesink->adaptor_no);
917 if (!xcontext->xv_port_id) {
918 /* Now search for an adaptor that supports XvImageMask */
919 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
920 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
921 xvimagesink->adaptor_no = i;
925 XvFreeAdaptorInfo (adaptors);
927 if (!xcontext->xv_port_id) {
928 xvimagesink->adaptor_no = -1;
929 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
930 ("Could not initialise Xv output"), ("No port available"));
934 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
937 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
938 xcontext->xv_port_id, &count);
939 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
940 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
941 static const char colorkey[] = "XV_COLORKEY";
943 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
945 xvimagesink->have_autopaint_colorkey = FALSE;
946 xvimagesink->have_double_buffer = FALSE;
947 xvimagesink->have_colorkey = FALSE;
949 for (i = 0; ((i < count) && todo); i++)
950 if (!strcmp (attr[i].name, autopaint)) {
951 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
953 /* turn on autopaint colorkey */
954 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
955 (xvimagesink->autopaint_colorkey ? 1 : 0));
957 xvimagesink->have_autopaint_colorkey = TRUE;
958 } else if (!strcmp (attr[i].name, dbl_buffer)) {
959 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
961 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
962 (xvimagesink->double_buffer ? 1 : 0));
964 xvimagesink->have_double_buffer = TRUE;
965 } else if (!strcmp (attr[i].name, colorkey)) {
966 /* Set the colorkey, default is something that is dark but hopefully
967 * won't randomly appear on the screen elsewhere (ie not black or greys)
968 * can be overridden by setting "colorkey" property
970 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
972 gboolean set_attr = TRUE;
975 /* set a colorkey in the right format RGB565/RGB888
976 * We only handle these 2 cases, because they're the only types of
977 * devices we've encountered. If we don't recognise it, leave it alone
979 cr = (xvimagesink->colorkey >> 16);
980 cg = (xvimagesink->colorkey >> 8) & 0xFF;
981 cb = (xvimagesink->colorkey) & 0xFF;
982 switch (xcontext->depth) {
983 case 16: /* RGB 565 */
987 ckey = (cr << 11) | (cg << 5) | cb;
990 case 32: /* RGB 888 / ARGB 8888 */
991 ckey = (cr << 16) | (cg << 8) | cb;
994 GST_DEBUG_OBJECT (xvimagesink,
995 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1002 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1003 (guint32) attr[i].max_value);
1004 GST_LOG_OBJECT (xvimagesink,
1005 "Setting color key for display depth %d to 0x%x",
1006 xcontext->depth, ckey);
1008 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1012 xvimagesink->have_colorkey = TRUE;
1018 /* Get the list of encodings supported by the adapter and look for the
1019 * XV_IMAGE encoding so we can determine the maximum width and height
1021 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1024 for (i = 0; i < nb_encodings; i++) {
1025 GST_LOG_OBJECT (xvimagesink,
1026 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1027 i, encodings[i].name, encodings[i].width, encodings[i].height,
1028 encodings[i].rate.numerator, encodings[i].rate.denominator);
1029 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1030 max_w = encodings[i].width;
1031 max_h = encodings[i].height;
1035 XvFreeEncodingInfo (encodings);
1037 /* We get all image formats supported by our port */
1038 formats = XvListImageFormats (xcontext->disp,
1039 xcontext->xv_port_id, &nb_formats);
1040 caps = gst_caps_new_empty ();
1041 for (i = 0; i < nb_formats; i++) {
1042 GstCaps *format_caps = NULL;
1043 gboolean is_rgb_format = FALSE;
1044 GstVideoFormat vformat;
1046 /* We set the image format of the xcontext to an existing one. This
1047 is just some valid image format for making our xshm calls check before
1048 caps negotiation really happens. */
1049 xcontext->im_format = formats[i].id;
1051 switch (formats[i].type) {
1054 XvImageFormatValues *fmt = &(formats[i]);
1058 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1060 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1061 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1062 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1065 format_caps = gst_caps_new_simple ("video/x-raw",
1066 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1067 "width", GST_TYPE_INT_RANGE, 1, max_w,
1068 "height", GST_TYPE_INT_RANGE, 1, max_h,
1069 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1071 is_rgb_format = TRUE;
1076 vformat = gst_video_format_from_fourcc (formats[i].id);
1077 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1080 format_caps = gst_caps_new_simple ("video/x-raw",
1081 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1082 "width", GST_TYPE_INT_RANGE, 1, max_w,
1083 "height", GST_TYPE_INT_RANGE, 1, max_h,
1084 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1088 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1089 g_assert_not_reached ();
1094 GstXvImageFormat *format = NULL;
1096 format = g_new0 (GstXvImageFormat, 1);
1098 format->format = formats[i].id;
1099 format->vformat = vformat;
1100 format->caps = gst_caps_copy (format_caps);
1101 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1104 if (is_rgb_format) {
1105 if (rgb_caps == NULL)
1106 rgb_caps = format_caps;
1108 gst_caps_append (rgb_caps, format_caps);
1110 gst_caps_append (caps, format_caps);
1114 /* Collected all caps into either the caps or rgb_caps structures.
1115 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1117 gst_caps_append (caps, rgb_caps);
1122 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1124 if (gst_caps_is_empty (caps)) {
1125 gst_caps_unref (caps);
1126 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1127 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1128 ("No supported format found"));
1136 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1138 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1140 GST_OBJECT_LOCK (xvimagesink);
1141 while (xvimagesink->running) {
1142 GST_OBJECT_UNLOCK (xvimagesink);
1144 if (xvimagesink->xwindow) {
1145 gst_xvimagesink_handle_xevents (xvimagesink);
1147 /* FIXME: do we want to align this with the framerate or anything else? */
1148 g_usleep (G_USEC_PER_SEC / 20);
1150 GST_OBJECT_LOCK (xvimagesink);
1152 GST_OBJECT_UNLOCK (xvimagesink);
1158 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1160 GThread *thread = NULL;
1162 /* don't start the thread too early */
1163 if (xvimagesink->xcontext == NULL) {
1167 GST_OBJECT_LOCK (xvimagesink);
1168 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1169 if (!xvimagesink->event_thread) {
1170 /* Setup our event listening thread */
1171 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1172 xvimagesink->handle_expose, xvimagesink->handle_events);
1173 xvimagesink->running = TRUE;
1174 #if !GLIB_CHECK_VERSION (2, 31, 0)
1175 xvimagesink->event_thread = g_thread_create (
1176 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1178 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1179 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1183 if (xvimagesink->event_thread) {
1184 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1185 xvimagesink->handle_expose, xvimagesink->handle_events);
1186 xvimagesink->running = FALSE;
1187 /* grab thread and mark it as NULL */
1188 thread = xvimagesink->event_thread;
1189 xvimagesink->event_thread = NULL;
1192 GST_OBJECT_UNLOCK (xvimagesink);
1194 /* Wait for our event thread to finish */
1196 g_thread_join (thread);
1201 /* This function calculates the pixel aspect ratio based on the properties
1202 * in the xcontext structure and stores it there. */
1204 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1206 static const gint par[][2] = {
1207 {1, 1}, /* regular screen */
1208 {16, 15}, /* PAL TV */
1209 {11, 10}, /* 525 line Rec.601 video */
1210 {54, 59}, /* 625 line Rec.601 video */
1211 {64, 45}, /* 1280x1024 on 16:9 display */
1212 {5, 3}, /* 1280x1024 on 4:3 display */
1213 {4, 3} /* 800x600 on 16:9 display */
1220 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1222 /* first calculate the "real" ratio based on the X values;
1223 * which is the "physical" w/h divided by the w/h in pixels of the display */
1224 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1225 / (xcontext->heightmm * xcontext->width);
1227 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1229 if (xcontext->width == 720 && xcontext->height == 576) {
1230 ratio = 4.0 * 576 / (3.0 * 720);
1232 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1234 /* now find the one from par[][2] with the lowest delta to the real one */
1238 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1239 gdouble this_delta = DELTA (i);
1241 if (this_delta < delta) {
1247 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1248 par[index][0], par[index][1]);
1250 g_free (xcontext->par);
1251 xcontext->par = g_new0 (GValue, 1);
1252 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1253 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1254 GST_DEBUG ("set xcontext PAR to %d/%d",
1255 gst_value_get_fraction_numerator (xcontext->par),
1256 gst_value_get_fraction_denominator (xcontext->par));
1259 /* This function gets the X Display and global info about it. Everything is
1260 stored in our object and will be cleaned when the object is disposed. Note
1261 here that caps for supported format are generated without any window or
1263 static GstXContext *
1264 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1266 GstXContext *xcontext = NULL;
1267 XPixmapFormatValues *px_formats = NULL;
1268 gint nb_formats = 0, i, j, N_attr;
1269 XvAttribute *xv_attr;
1271 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1272 "XV_BRIGHTNESS", "XV_CONTRAST"
1275 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1277 xcontext = g_new0 (GstXContext, 1);
1278 xcontext->im_format = 0;
1280 g_mutex_lock (xvimagesink->x_lock);
1282 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1284 if (!xcontext->disp) {
1285 g_mutex_unlock (xvimagesink->x_lock);
1287 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1288 ("Could not initialise Xv output"), ("Could not open display"));
1292 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1293 xcontext->screen_num = DefaultScreen (xcontext->disp);
1294 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1295 xcontext->root = DefaultRootWindow (xcontext->disp);
1296 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1297 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1298 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1300 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1301 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1302 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1303 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1305 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1306 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1308 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1309 /* We get supported pixmap formats at supported depth */
1310 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1313 XCloseDisplay (xcontext->disp);
1314 g_mutex_unlock (xvimagesink->x_lock);
1315 g_free (xcontext->par);
1317 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1318 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1322 /* We get bpp value corresponding to our running depth */
1323 for (i = 0; i < nb_formats; i++) {
1324 if (px_formats[i].depth == xcontext->depth)
1325 xcontext->bpp = px_formats[i].bits_per_pixel;
1330 xcontext->endianness =
1331 (ImageByteOrder (xcontext->disp) ==
1332 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1334 /* our caps system handles 24/32bpp RGB as big-endian. */
1335 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1336 xcontext->endianness == G_LITTLE_ENDIAN) {
1337 xcontext->endianness = G_BIG_ENDIAN;
1338 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1339 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1340 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1341 if (xcontext->bpp == 24) {
1342 xcontext->visual->red_mask >>= 8;
1343 xcontext->visual->green_mask >>= 8;
1344 xcontext->visual->blue_mask >>= 8;
1348 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1350 /* Search for XShm extension support */
1352 if (XShmQueryExtension (xcontext->disp) &&
1353 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1354 xcontext->use_xshm = TRUE;
1355 GST_DEBUG ("xvimagesink is using XShm extension");
1357 #endif /* HAVE_XSHM */
1359 xcontext->use_xshm = FALSE;
1360 GST_DEBUG ("xvimagesink is not using XShm extension");
1363 if (!xcontext->caps) {
1364 XCloseDisplay (xcontext->disp);
1365 g_mutex_unlock (xvimagesink->x_lock);
1366 g_free (xcontext->par);
1368 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1372 xv_attr = XvQueryPortAttributes (xcontext->disp,
1373 xcontext->xv_port_id, &N_attr);
1376 /* Generate the channels list */
1377 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1378 XvAttribute *matching_attr = NULL;
1380 /* Retrieve the property atom if it exists. If it doesn't exist,
1381 * the attribute itself must not either, so we can skip */
1382 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1383 if (prop_atom == None)
1386 if (xv_attr != NULL) {
1387 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1388 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1389 matching_attr = xv_attr + j;
1392 if (matching_attr) {
1393 GstColorBalanceChannel *channel;
1395 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1396 channel->label = g_strdup (channels[i]);
1397 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1398 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1400 xcontext->channels_list = g_list_append (xcontext->channels_list,
1403 /* If the colorbalance settings have not been touched we get Xv values
1404 as defaults and update our internal variables */
1405 if (!xvimagesink->cb_changed) {
1408 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1410 /* Normalize val to [-1000, 1000] */
1411 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1412 (double) (channel->max_value - channel->min_value));
1414 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1415 xvimagesink->hue = val;
1416 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1417 xvimagesink->saturation = val;
1418 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1419 xvimagesink->brightness = val;
1420 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1421 xvimagesink->contrast = val;
1429 g_mutex_unlock (xvimagesink->x_lock);
1434 /* This function cleans the X context. Closing the Display, releasing the XV
1435 port and unrefing the caps for supported formats. */
1437 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1439 GList *formats_list, *channels_list;
1440 GstXContext *xcontext;
1443 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1445 GST_OBJECT_LOCK (xvimagesink);
1446 if (xvimagesink->xcontext == NULL) {
1447 GST_OBJECT_UNLOCK (xvimagesink);
1451 /* Take the XContext from the sink and clean it up */
1452 xcontext = xvimagesink->xcontext;
1453 xvimagesink->xcontext = NULL;
1455 GST_OBJECT_UNLOCK (xvimagesink);
1458 formats_list = xcontext->formats_list;
1460 while (formats_list) {
1461 GstXvImageFormat *format = formats_list->data;
1463 gst_caps_unref (format->caps);
1465 formats_list = g_list_next (formats_list);
1468 if (xcontext->formats_list)
1469 g_list_free (xcontext->formats_list);
1471 channels_list = xcontext->channels_list;
1473 while (channels_list) {
1474 GstColorBalanceChannel *channel = channels_list->data;
1476 g_object_unref (channel);
1477 channels_list = g_list_next (channels_list);
1480 if (xcontext->channels_list)
1481 g_list_free (xcontext->channels_list);
1483 gst_caps_unref (xcontext->caps);
1484 if (xcontext->last_caps)
1485 gst_caps_replace (&xcontext->last_caps, NULL);
1487 for (i = 0; i < xcontext->nb_adaptors; i++) {
1488 g_free (xcontext->adaptors[i]);
1491 g_free (xcontext->adaptors);
1493 g_free (xcontext->par);
1495 g_mutex_lock (xvimagesink->x_lock);
1497 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1499 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1501 XCloseDisplay (xcontext->disp);
1503 g_mutex_unlock (xvimagesink->x_lock);
1511 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1513 GstXvImageSink *xvimagesink;
1516 xvimagesink = GST_XVIMAGESINK (bsink);
1518 if (xvimagesink->xcontext) {
1520 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1521 GST_CAPS_INTERSECT_FIRST);
1523 return gst_caps_ref (xvimagesink->xcontext->caps);
1526 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1528 GstCaps *intersection;
1531 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1532 gst_caps_unref (caps);
1533 caps = intersection;
1539 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1541 GstXvImageSink *xvimagesink;
1542 GstStructure *structure;
1543 GstBufferPool *newpool, *oldpool;
1545 guint32 im_format = 0;
1546 gint video_par_n, video_par_d; /* video's PAR */
1547 gint display_par_n, display_par_d; /* display's PAR */
1551 xvimagesink = GST_XVIMAGESINK (bsink);
1553 GST_DEBUG_OBJECT (xvimagesink,
1554 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1555 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1557 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1558 goto incompatible_caps;
1560 if (!gst_video_info_from_caps (&info, caps))
1561 goto invalid_format;
1563 structure = gst_caps_get_structure (caps, 0);
1565 xvimagesink->fps_n = info.fps_n;
1566 xvimagesink->fps_d = info.fps_d;
1568 xvimagesink->video_width = info.width;
1569 xvimagesink->video_height = info.height;
1571 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1572 if (im_format == -1)
1573 goto invalid_format;
1577 /* get aspect ratio from caps if it's present, and
1578 * convert video width and height to a display width and height
1579 * using wd / hd = wv / hv * PARv / PARd */
1581 /* get video's PAR */
1582 video_par_n = info.par_n;
1583 video_par_d = info.par_d;
1585 /* get display's PAR */
1586 if (xvimagesink->par) {
1587 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1588 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1594 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1595 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1598 GST_DEBUG_OBJECT (xvimagesink,
1599 "video width/height: %dx%d, calculated display ratio: %d/%d",
1600 info.width, info.height, num, den);
1602 /* now find a width x height that respects this display ratio.
1603 * prefer those that have one of w/h the same as the incoming video
1604 * using wd / hd = num / den */
1606 /* start with same height, because of interlaced video */
1607 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1608 if (info.height % den == 0) {
1609 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1610 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1611 gst_util_uint64_scale_int (info.height, num, den);
1612 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1613 } else if (info.width % num == 0) {
1614 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1615 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1616 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1617 gst_util_uint64_scale_int (info.width, den, num);
1619 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1620 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1621 gst_util_uint64_scale_int (info.height, num, den);
1622 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1624 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1625 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1627 /* Notify application to set xwindow id now */
1628 g_mutex_lock (xvimagesink->flow_lock);
1629 if (!xvimagesink->xwindow) {
1630 g_mutex_unlock (xvimagesink->flow_lock);
1631 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1633 g_mutex_unlock (xvimagesink->flow_lock);
1636 /* Creating our window and our image with the display size in pixels */
1637 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1638 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1639 goto no_display_size;
1641 g_mutex_lock (xvimagesink->flow_lock);
1642 if (!xvimagesink->xwindow) {
1643 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1644 GST_VIDEO_SINK_WIDTH (xvimagesink),
1645 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1648 xvimagesink->info = info;
1650 /* After a resize, we want to redraw the borders in case the new frame size
1651 * doesn't cover the same area */
1652 xvimagesink->redraw_border = TRUE;
1654 /* create a new pool for the new configuration */
1655 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1657 structure = gst_buffer_pool_get_config (newpool);
1658 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1659 if (!gst_buffer_pool_set_config (newpool, structure))
1662 oldpool = xvimagesink->pool;
1663 xvimagesink->pool = newpool;
1664 g_mutex_unlock (xvimagesink->flow_lock);
1666 /* unref the old sink */
1668 /* we don't deactivate, some elements might still be using it, it will
1669 * be deactivated when the last ref is gone */
1670 gst_object_unref (oldpool);
1678 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1683 GST_DEBUG_OBJECT (xvimagesink,
1684 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1689 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1690 ("Error calculating the output display ratio of the video."));
1695 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1696 ("Error calculating the output display ratio of the video."));
1701 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1702 g_mutex_unlock (xvimagesink->flow_lock);
1707 static GstStateChangeReturn
1708 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1710 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1711 GstXvImageSink *xvimagesink;
1712 GstXContext *xcontext = NULL;
1714 xvimagesink = GST_XVIMAGESINK (element);
1716 switch (transition) {
1717 case GST_STATE_CHANGE_NULL_TO_READY:
1718 /* Initializing the XContext */
1719 if (xvimagesink->xcontext == NULL) {
1720 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1721 if (xcontext == NULL) {
1722 ret = GST_STATE_CHANGE_FAILURE;
1725 GST_OBJECT_LOCK (xvimagesink);
1727 xvimagesink->xcontext = xcontext;
1728 GST_OBJECT_UNLOCK (xvimagesink);
1731 /* update object's par with calculated one if not set yet */
1732 if (!xvimagesink->par) {
1733 xvimagesink->par = g_new0 (GValue, 1);
1734 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1735 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1737 /* call XSynchronize with the current value of synchronous */
1738 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1739 xvimagesink->synchronous ? "TRUE" : "FALSE");
1740 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1741 gst_xvimagesink_update_colorbalance (xvimagesink);
1742 gst_xvimagesink_manage_event_thread (xvimagesink);
1744 case GST_STATE_CHANGE_READY_TO_PAUSED:
1746 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1748 case GST_STATE_CHANGE_PAUSED_TO_READY:
1754 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1756 switch (transition) {
1757 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1759 case GST_STATE_CHANGE_PAUSED_TO_READY:
1760 xvimagesink->fps_n = 0;
1761 xvimagesink->fps_d = 1;
1762 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1763 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1764 g_mutex_lock (xvimagesink->flow_lock);
1765 if (xvimagesink->pool)
1766 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1767 g_mutex_unlock (xvimagesink->flow_lock);
1769 case GST_STATE_CHANGE_READY_TO_NULL:
1770 gst_xvimagesink_reset (xvimagesink);
1781 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1782 GstClockTime * start, GstClockTime * end)
1784 GstXvImageSink *xvimagesink;
1786 xvimagesink = GST_XVIMAGESINK (bsink);
1788 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1789 *start = GST_BUFFER_TIMESTAMP (buf);
1790 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1791 *end = *start + GST_BUFFER_DURATION (buf);
1793 if (xvimagesink->fps_n > 0) {
1795 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1796 xvimagesink->fps_n);
1802 static GstFlowReturn
1803 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1806 GstXvImageSink *xvimagesink;
1807 GstXvImageMeta *meta;
1810 xvimagesink = GST_XVIMAGESINK (vsink);
1812 meta = gst_buffer_get_xvimage_meta (buf);
1814 if (meta && meta->sink == xvimagesink) {
1815 /* If this buffer has been allocated using our buffer management we simply
1816 put the ximage which is in the PRIVATE pointer */
1817 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1822 GstVideoFrame src, dest;
1824 /* Else we have to copy the data into our private image, */
1825 /* if we have one... */
1826 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1828 /* we should have a pool, configured in setcaps */
1829 if (xvimagesink->pool == NULL)
1832 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1833 goto activate_failed;
1835 /* take a buffer form our pool */
1836 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1837 if (res != GST_FLOW_OK)
1840 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1843 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1844 "slow copy into bufferpool buffer %p", to_put);
1846 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1847 goto invalid_buffer;
1849 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1850 gst_video_frame_unmap (&src);
1851 goto invalid_buffer;
1854 gst_video_frame_copy (&dest, &src);
1856 gst_video_frame_unmap (&dest);
1857 gst_video_frame_unmap (&src);
1860 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1865 gst_buffer_unref (to_put);
1872 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1873 ("Internal error: can't allocate images"),
1874 ("We don't have a bufferpool negotiated"));
1875 return GST_FLOW_ERROR;
1879 /* No image available. That's very bad ! */
1880 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1885 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1886 ("Failed to create output image buffer"),
1887 ("XServer allocated buffer size did not match input buffer %"
1888 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1889 gst_buffer_get_size (buf)));
1890 res = GST_FLOW_ERROR;
1895 /* No Window available to put our image into */
1896 GST_WARNING_OBJECT (xvimagesink, "could map image");
1902 /* No Window available to put our image into */
1903 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1904 res = GST_FLOW_ERROR;
1909 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1910 res = GST_FLOW_ERROR;
1916 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1918 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1920 switch (GST_EVENT_TYPE (event)) {
1921 case GST_EVENT_TAG:{
1923 gchar *title = NULL;
1925 gst_event_parse_tag (event, &l);
1926 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1929 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1930 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1940 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1944 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1946 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1947 GstBufferPool *pool;
1948 GstStructure *config;
1953 gst_query_parse_allocation (query, &caps, &need_pool);
1958 g_mutex_lock (xvimagesink->flow_lock);
1959 if ((pool = xvimagesink->pool))
1960 gst_object_ref (pool);
1961 g_mutex_unlock (xvimagesink->flow_lock);
1964 const GstCaps *pcaps;
1966 /* we had a pool, check caps */
1967 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1968 config = gst_buffer_pool_get_config (pool);
1969 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1971 if (!gst_caps_is_equal (caps, pcaps)) {
1972 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1973 /* different caps, we can't use this pool */
1974 gst_object_unref (pool);
1978 if (pool == NULL && need_pool) {
1981 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1982 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1984 if (!gst_video_info_from_caps (&info, caps))
1987 /* the normal size of a frame */
1990 config = gst_buffer_pool_get_config (pool);
1991 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1992 if (!gst_buffer_pool_set_config (pool, config))
1995 /* we need at least 2 buffer because we hold on to the last one */
1996 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1998 /* we also support various metadata */
1999 gst_query_add_allocation_meta (query, GST_VIDEO_META_API);
2000 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API);
2002 gst_object_unref (pool);
2009 GST_DEBUG_OBJECT (bsink, "no caps specified");
2014 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2019 GST_DEBUG_OBJECT (bsink, "failed setting config");
2024 /* Interfaces stuff */
2026 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2027 GstStructure * structure)
2029 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2032 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2034 GstVideoRectangle src, dst, result;
2035 gdouble x, y, xscale = 1.0, yscale = 1.0;
2037 event = gst_event_new_navigation (structure);
2039 /* We take the flow_lock while we look at the window */
2040 g_mutex_lock (xvimagesink->flow_lock);
2042 if (!xvimagesink->xwindow) {
2043 g_mutex_unlock (xvimagesink->flow_lock);
2047 if (xvimagesink->keep_aspect) {
2048 /* We get the frame position using the calculated geometry from _setcaps
2049 that respect pixel aspect ratios */
2050 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2051 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2052 dst.w = xvimagesink->render_rect.w;
2053 dst.h = xvimagesink->render_rect.h;
2055 gst_video_sink_center_rect (src, dst, &result, TRUE);
2056 result.x += xvimagesink->render_rect.x;
2057 result.y += xvimagesink->render_rect.y;
2059 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2062 g_mutex_unlock (xvimagesink->flow_lock);
2064 /* We calculate scaling using the original video frames geometry to include
2065 pixel aspect ratio scaling. */
2066 xscale = (gdouble) xvimagesink->video_width / result.w;
2067 yscale = (gdouble) xvimagesink->video_height / result.h;
2069 /* Converting pointer coordinates to the non scaled geometry */
2070 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2071 x = MIN (x, result.x + result.w);
2072 x = MAX (x - result.x, 0);
2073 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2074 (gdouble) x * xscale, NULL);
2076 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2077 y = MIN (y, result.y + result.h);
2078 y = MAX (y - result.y, 0);
2079 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2080 (gdouble) y * yscale, NULL);
2083 gst_pad_send_event (peer, event);
2084 gst_object_unref (peer);
2089 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2091 iface->send_event = gst_xvimagesink_navigation_send_event;
2095 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2097 XID xwindow_id = id;
2098 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2099 GstXWindow *xwindow = NULL;
2101 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2103 g_mutex_lock (xvimagesink->flow_lock);
2105 /* If we already use that window return */
2106 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2107 g_mutex_unlock (xvimagesink->flow_lock);
2111 /* If the element has not initialized the X11 context try to do so */
2112 if (!xvimagesink->xcontext &&
2113 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2114 g_mutex_unlock (xvimagesink->flow_lock);
2115 /* we have thrown a GST_ELEMENT_ERROR now */
2119 gst_xvimagesink_update_colorbalance (xvimagesink);
2121 /* If a window is there already we destroy it */
2122 if (xvimagesink->xwindow) {
2123 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2124 xvimagesink->xwindow = NULL;
2127 /* If the xid is 0 we go back to an internal window */
2128 if (xwindow_id == 0) {
2129 /* If no width/height caps nego did not happen window will be created
2130 during caps nego then */
2131 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2132 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2134 gst_xvimagesink_xwindow_new (xvimagesink,
2135 GST_VIDEO_SINK_WIDTH (xvimagesink),
2136 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2139 XWindowAttributes attr;
2141 xwindow = g_new0 (GstXWindow, 1);
2142 xwindow->win = xwindow_id;
2144 /* Set the event we want to receive and create a GC */
2145 g_mutex_lock (xvimagesink->x_lock);
2147 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2149 xwindow->width = attr.width;
2150 xwindow->height = attr.height;
2151 xwindow->internal = FALSE;
2152 if (!xvimagesink->have_render_rect) {
2153 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2154 xvimagesink->render_rect.w = attr.width;
2155 xvimagesink->render_rect.h = attr.height;
2157 if (xvimagesink->handle_events) {
2158 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2159 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2163 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2164 xwindow->win, 0, NULL);
2165 g_mutex_unlock (xvimagesink->x_lock);
2169 xvimagesink->xwindow = xwindow;
2171 g_mutex_unlock (xvimagesink->flow_lock);
2175 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2177 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2179 GST_DEBUG ("doing expose");
2180 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2181 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2185 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2186 gboolean handle_events)
2188 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2190 xvimagesink->handle_events = handle_events;
2192 g_mutex_lock (xvimagesink->flow_lock);
2194 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2195 g_mutex_unlock (xvimagesink->flow_lock);
2199 g_mutex_lock (xvimagesink->x_lock);
2201 if (handle_events) {
2202 if (xvimagesink->xwindow->internal) {
2203 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2204 ExposureMask | StructureNotifyMask | PointerMotionMask |
2205 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2207 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2208 ExposureMask | StructureNotifyMask | PointerMotionMask |
2209 KeyPressMask | KeyReleaseMask);
2212 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2215 g_mutex_unlock (xvimagesink->x_lock);
2217 g_mutex_unlock (xvimagesink->flow_lock);
2221 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2222 gint width, gint height)
2224 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2226 /* FIXME: how about some locking? */
2227 if (width >= 0 && height >= 0) {
2228 xvimagesink->render_rect.x = x;
2229 xvimagesink->render_rect.y = y;
2230 xvimagesink->render_rect.w = width;
2231 xvimagesink->render_rect.h = height;
2232 xvimagesink->have_render_rect = TRUE;
2234 xvimagesink->render_rect.x = 0;
2235 xvimagesink->render_rect.y = 0;
2236 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2237 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2238 xvimagesink->have_render_rect = FALSE;
2243 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2245 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2246 iface->expose = gst_xvimagesink_expose;
2247 iface->handle_events = gst_xvimagesink_set_event_handling;
2248 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2251 static const GList *
2252 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2254 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2256 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2258 if (xvimagesink->xcontext)
2259 return xvimagesink->xcontext->channels_list;
2265 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2266 GstColorBalanceChannel * channel, gint value)
2268 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2270 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2271 g_return_if_fail (channel->label != NULL);
2273 xvimagesink->cb_changed = TRUE;
2275 /* Normalize val to [-1000, 1000] */
2276 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2277 (double) (channel->max_value - channel->min_value));
2279 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2280 xvimagesink->hue = value;
2281 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2282 xvimagesink->saturation = value;
2283 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2284 xvimagesink->contrast = value;
2285 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2286 xvimagesink->brightness = value;
2288 g_warning ("got an unknown channel %s", channel->label);
2292 gst_xvimagesink_update_colorbalance (xvimagesink);
2296 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2297 GstColorBalanceChannel * channel)
2299 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2302 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2303 g_return_val_if_fail (channel->label != NULL, 0);
2305 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2306 value = xvimagesink->hue;
2307 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2308 value = xvimagesink->saturation;
2309 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2310 value = xvimagesink->contrast;
2311 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2312 value = xvimagesink->brightness;
2314 g_warning ("got an unknown channel %s", channel->label);
2317 /* Normalize val to [channel->min_value, channel->max_value] */
2318 value = channel->min_value + (channel->max_value - channel->min_value) *
2319 (value + 1000) / 2000;
2325 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2327 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2328 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2329 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2330 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2334 static const GList *
2335 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2337 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2338 static GList *list = NULL;
2341 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2343 g_list_append (list, g_object_class_find_property (klass,
2344 "autopaint-colorkey"));
2346 g_list_append (list, g_object_class_find_property (klass,
2349 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2356 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2357 guint prop_id, const GParamSpec * pspec)
2359 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2363 case PROP_AUTOPAINT_COLORKEY:
2364 case PROP_DOUBLE_BUFFER:
2366 GST_DEBUG_OBJECT (xvimagesink,
2367 "probing device list and get capabilities");
2368 if (!xvimagesink->xcontext) {
2369 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2370 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2374 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2380 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2381 guint prop_id, const GParamSpec * pspec)
2383 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2384 gboolean ret = FALSE;
2388 case PROP_AUTOPAINT_COLORKEY:
2389 case PROP_DOUBLE_BUFFER:
2391 if (xvimagesink->xcontext != NULL) {
2398 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2405 static GValueArray *
2406 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2407 guint prop_id, const GParamSpec * pspec)
2409 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2410 GValueArray *array = NULL;
2412 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2413 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2422 GValue value = { 0 };
2424 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2425 g_value_init (&value, G_TYPE_STRING);
2427 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2428 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2430 g_value_set_string (&value, adaptor_id_s);
2431 g_value_array_append (array, &value);
2432 g_free (adaptor_id_s);
2434 g_value_unset (&value);
2437 case PROP_AUTOPAINT_COLORKEY:
2438 if (xvimagesink->have_autopaint_colorkey) {
2439 GValue value = { 0 };
2441 array = g_value_array_new (2);
2442 g_value_init (&value, G_TYPE_BOOLEAN);
2443 g_value_set_boolean (&value, FALSE);
2444 g_value_array_append (array, &value);
2445 g_value_set_boolean (&value, TRUE);
2446 g_value_array_append (array, &value);
2447 g_value_unset (&value);
2450 case PROP_DOUBLE_BUFFER:
2451 if (xvimagesink->have_double_buffer) {
2452 GValue value = { 0 };
2454 array = g_value_array_new (2);
2455 g_value_init (&value, G_TYPE_BOOLEAN);
2456 g_value_set_boolean (&value, FALSE);
2457 g_value_array_append (array, &value);
2458 g_value_set_boolean (&value, TRUE);
2459 g_value_array_append (array, &value);
2460 g_value_unset (&value);
2464 if (xvimagesink->have_colorkey) {
2465 GValue value = { 0 };
2467 array = g_value_array_new (1);
2468 g_value_init (&value, GST_TYPE_INT_RANGE);
2469 gst_value_set_int_range (&value, 0, 0xffffff);
2470 g_value_array_append (array, &value);
2471 g_value_unset (&value);
2475 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2484 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2487 iface->get_properties = gst_xvimagesink_probe_get_properties;
2488 iface->probe_property = gst_xvimagesink_probe_probe_property;
2489 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2490 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);