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 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1175 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1178 if (xvimagesink->event_thread) {
1179 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1180 xvimagesink->handle_expose, xvimagesink->handle_events);
1181 xvimagesink->running = FALSE;
1182 /* grab thread and mark it as NULL */
1183 thread = xvimagesink->event_thread;
1184 xvimagesink->event_thread = NULL;
1187 GST_OBJECT_UNLOCK (xvimagesink);
1189 /* Wait for our event thread to finish */
1191 g_thread_join (thread);
1196 /* This function calculates the pixel aspect ratio based on the properties
1197 * in the xcontext structure and stores it there. */
1199 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1201 static const gint par[][2] = {
1202 {1, 1}, /* regular screen */
1203 {16, 15}, /* PAL TV */
1204 {11, 10}, /* 525 line Rec.601 video */
1205 {54, 59}, /* 625 line Rec.601 video */
1206 {64, 45}, /* 1280x1024 on 16:9 display */
1207 {5, 3}, /* 1280x1024 on 4:3 display */
1208 {4, 3} /* 800x600 on 16:9 display */
1215 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1217 /* first calculate the "real" ratio based on the X values;
1218 * which is the "physical" w/h divided by the w/h in pixels of the display */
1219 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1220 / (xcontext->heightmm * xcontext->width);
1222 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1224 if (xcontext->width == 720 && xcontext->height == 576) {
1225 ratio = 4.0 * 576 / (3.0 * 720);
1227 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1229 /* now find the one from par[][2] with the lowest delta to the real one */
1233 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1234 gdouble this_delta = DELTA (i);
1236 if (this_delta < delta) {
1242 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1243 par[index][0], par[index][1]);
1245 g_free (xcontext->par);
1246 xcontext->par = g_new0 (GValue, 1);
1247 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1248 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1249 GST_DEBUG ("set xcontext PAR to %d/%d",
1250 gst_value_get_fraction_numerator (xcontext->par),
1251 gst_value_get_fraction_denominator (xcontext->par));
1254 /* This function gets the X Display and global info about it. Everything is
1255 stored in our object and will be cleaned when the object is disposed. Note
1256 here that caps for supported format are generated without any window or
1258 static GstXContext *
1259 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1261 GstXContext *xcontext = NULL;
1262 XPixmapFormatValues *px_formats = NULL;
1263 gint nb_formats = 0, i, j, N_attr;
1264 XvAttribute *xv_attr;
1266 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1267 "XV_BRIGHTNESS", "XV_CONTRAST"
1270 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1272 xcontext = g_new0 (GstXContext, 1);
1273 xcontext->im_format = 0;
1275 g_mutex_lock (xvimagesink->x_lock);
1277 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1279 if (!xcontext->disp) {
1280 g_mutex_unlock (xvimagesink->x_lock);
1282 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1283 ("Could not initialise Xv output"), ("Could not open display"));
1287 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1288 xcontext->screen_num = DefaultScreen (xcontext->disp);
1289 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1290 xcontext->root = DefaultRootWindow (xcontext->disp);
1291 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1292 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1293 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1295 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1296 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1297 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1298 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1300 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1301 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1303 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1304 /* We get supported pixmap formats at supported depth */
1305 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1308 XCloseDisplay (xcontext->disp);
1309 g_mutex_unlock (xvimagesink->x_lock);
1310 g_free (xcontext->par);
1312 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1313 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1317 /* We get bpp value corresponding to our running depth */
1318 for (i = 0; i < nb_formats; i++) {
1319 if (px_formats[i].depth == xcontext->depth)
1320 xcontext->bpp = px_formats[i].bits_per_pixel;
1325 xcontext->endianness =
1326 (ImageByteOrder (xcontext->disp) ==
1327 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1329 /* our caps system handles 24/32bpp RGB as big-endian. */
1330 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1331 xcontext->endianness == G_LITTLE_ENDIAN) {
1332 xcontext->endianness = G_BIG_ENDIAN;
1333 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1334 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1335 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1336 if (xcontext->bpp == 24) {
1337 xcontext->visual->red_mask >>= 8;
1338 xcontext->visual->green_mask >>= 8;
1339 xcontext->visual->blue_mask >>= 8;
1343 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1345 /* Search for XShm extension support */
1347 if (XShmQueryExtension (xcontext->disp) &&
1348 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1349 xcontext->use_xshm = TRUE;
1350 GST_DEBUG ("xvimagesink is using XShm extension");
1352 #endif /* HAVE_XSHM */
1354 xcontext->use_xshm = FALSE;
1355 GST_DEBUG ("xvimagesink is not using XShm extension");
1358 if (!xcontext->caps) {
1359 XCloseDisplay (xcontext->disp);
1360 g_mutex_unlock (xvimagesink->x_lock);
1361 g_free (xcontext->par);
1363 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1367 xv_attr = XvQueryPortAttributes (xcontext->disp,
1368 xcontext->xv_port_id, &N_attr);
1371 /* Generate the channels list */
1372 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1373 XvAttribute *matching_attr = NULL;
1375 /* Retrieve the property atom if it exists. If it doesn't exist,
1376 * the attribute itself must not either, so we can skip */
1377 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1378 if (prop_atom == None)
1381 if (xv_attr != NULL) {
1382 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1383 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1384 matching_attr = xv_attr + j;
1387 if (matching_attr) {
1388 GstColorBalanceChannel *channel;
1390 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1391 channel->label = g_strdup (channels[i]);
1392 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1393 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1395 xcontext->channels_list = g_list_append (xcontext->channels_list,
1398 /* If the colorbalance settings have not been touched we get Xv values
1399 as defaults and update our internal variables */
1400 if (!xvimagesink->cb_changed) {
1403 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1405 /* Normalize val to [-1000, 1000] */
1406 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1407 (double) (channel->max_value - channel->min_value));
1409 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1410 xvimagesink->hue = val;
1411 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1412 xvimagesink->saturation = val;
1413 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1414 xvimagesink->brightness = val;
1415 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1416 xvimagesink->contrast = val;
1424 g_mutex_unlock (xvimagesink->x_lock);
1429 /* This function cleans the X context. Closing the Display, releasing the XV
1430 port and unrefing the caps for supported formats. */
1432 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1434 GList *formats_list, *channels_list;
1435 GstXContext *xcontext;
1438 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1440 GST_OBJECT_LOCK (xvimagesink);
1441 if (xvimagesink->xcontext == NULL) {
1442 GST_OBJECT_UNLOCK (xvimagesink);
1446 /* Take the XContext from the sink and clean it up */
1447 xcontext = xvimagesink->xcontext;
1448 xvimagesink->xcontext = NULL;
1450 GST_OBJECT_UNLOCK (xvimagesink);
1453 formats_list = xcontext->formats_list;
1455 while (formats_list) {
1456 GstXvImageFormat *format = formats_list->data;
1458 gst_caps_unref (format->caps);
1460 formats_list = g_list_next (formats_list);
1463 if (xcontext->formats_list)
1464 g_list_free (xcontext->formats_list);
1466 channels_list = xcontext->channels_list;
1468 while (channels_list) {
1469 GstColorBalanceChannel *channel = channels_list->data;
1471 g_object_unref (channel);
1472 channels_list = g_list_next (channels_list);
1475 if (xcontext->channels_list)
1476 g_list_free (xcontext->channels_list);
1478 gst_caps_unref (xcontext->caps);
1479 if (xcontext->last_caps)
1480 gst_caps_replace (&xcontext->last_caps, NULL);
1482 for (i = 0; i < xcontext->nb_adaptors; i++) {
1483 g_free (xcontext->adaptors[i]);
1486 g_free (xcontext->adaptors);
1488 g_free (xcontext->par);
1490 g_mutex_lock (xvimagesink->x_lock);
1492 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1494 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1496 XCloseDisplay (xcontext->disp);
1498 g_mutex_unlock (xvimagesink->x_lock);
1506 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1508 GstXvImageSink *xvimagesink;
1511 xvimagesink = GST_XVIMAGESINK (bsink);
1513 if (xvimagesink->xcontext) {
1515 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1516 GST_CAPS_INTERSECT_FIRST);
1518 return gst_caps_ref (xvimagesink->xcontext->caps);
1521 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1523 GstCaps *intersection;
1526 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1527 gst_caps_unref (caps);
1528 caps = intersection;
1534 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1536 GstXvImageSink *xvimagesink;
1537 GstStructure *structure;
1538 GstBufferPool *newpool, *oldpool;
1540 guint32 im_format = 0;
1541 gint video_par_n, video_par_d; /* video's PAR */
1542 gint display_par_n, display_par_d; /* display's PAR */
1546 xvimagesink = GST_XVIMAGESINK (bsink);
1548 GST_DEBUG_OBJECT (xvimagesink,
1549 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1550 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1552 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1553 goto incompatible_caps;
1555 if (!gst_video_info_from_caps (&info, caps))
1556 goto invalid_format;
1558 structure = gst_caps_get_structure (caps, 0);
1560 xvimagesink->fps_n = info.fps_n;
1561 xvimagesink->fps_d = info.fps_d;
1563 xvimagesink->video_width = info.width;
1564 xvimagesink->video_height = info.height;
1566 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1567 if (im_format == -1)
1568 goto invalid_format;
1572 /* get aspect ratio from caps if it's present, and
1573 * convert video width and height to a display width and height
1574 * using wd / hd = wv / hv * PARv / PARd */
1576 /* get video's PAR */
1577 video_par_n = info.par_n;
1578 video_par_d = info.par_d;
1580 /* get display's PAR */
1581 if (xvimagesink->par) {
1582 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1583 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1589 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1590 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1593 GST_DEBUG_OBJECT (xvimagesink,
1594 "video width/height: %dx%d, calculated display ratio: %d/%d",
1595 info.width, info.height, num, den);
1597 /* now find a width x height that respects this display ratio.
1598 * prefer those that have one of w/h the same as the incoming video
1599 * using wd / hd = num / den */
1601 /* start with same height, because of interlaced video */
1602 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1603 if (info.height % den == 0) {
1604 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1605 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1606 gst_util_uint64_scale_int (info.height, num, den);
1607 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1608 } else if (info.width % num == 0) {
1609 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1610 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1611 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1612 gst_util_uint64_scale_int (info.width, den, num);
1614 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1615 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1616 gst_util_uint64_scale_int (info.height, num, den);
1617 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1619 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1620 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1622 /* Notify application to set xwindow id now */
1623 g_mutex_lock (xvimagesink->flow_lock);
1624 if (!xvimagesink->xwindow) {
1625 g_mutex_unlock (xvimagesink->flow_lock);
1626 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1628 g_mutex_unlock (xvimagesink->flow_lock);
1631 /* Creating our window and our image with the display size in pixels */
1632 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1633 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1634 goto no_display_size;
1636 g_mutex_lock (xvimagesink->flow_lock);
1637 if (!xvimagesink->xwindow) {
1638 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1639 GST_VIDEO_SINK_WIDTH (xvimagesink),
1640 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1643 xvimagesink->info = info;
1645 /* After a resize, we want to redraw the borders in case the new frame size
1646 * doesn't cover the same area */
1647 xvimagesink->redraw_border = TRUE;
1649 /* create a new pool for the new configuration */
1650 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1652 structure = gst_buffer_pool_get_config (newpool);
1653 gst_buffer_pool_config_set (structure, caps, size, 2, 0, 0, 15);
1654 if (!gst_buffer_pool_set_config (newpool, structure))
1657 oldpool = xvimagesink->pool;
1658 xvimagesink->pool = newpool;
1659 g_mutex_unlock (xvimagesink->flow_lock);
1661 /* unref the old sink */
1663 /* we don't deactivate, some elements might still be using it, it will
1664 * be deactivated when the last ref is gone */
1665 gst_object_unref (oldpool);
1673 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1678 GST_DEBUG_OBJECT (xvimagesink,
1679 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1684 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1685 ("Error calculating the output display ratio of the video."));
1690 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1691 ("Error calculating the output display ratio of the video."));
1696 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1697 g_mutex_unlock (xvimagesink->flow_lock);
1702 static GstStateChangeReturn
1703 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1705 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1706 GstXvImageSink *xvimagesink;
1707 GstXContext *xcontext = NULL;
1709 xvimagesink = GST_XVIMAGESINK (element);
1711 switch (transition) {
1712 case GST_STATE_CHANGE_NULL_TO_READY:
1713 /* Initializing the XContext */
1714 if (xvimagesink->xcontext == NULL) {
1715 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1716 if (xcontext == NULL) {
1717 ret = GST_STATE_CHANGE_FAILURE;
1720 GST_OBJECT_LOCK (xvimagesink);
1722 xvimagesink->xcontext = xcontext;
1723 GST_OBJECT_UNLOCK (xvimagesink);
1726 /* update object's par with calculated one if not set yet */
1727 if (!xvimagesink->par) {
1728 xvimagesink->par = g_new0 (GValue, 1);
1729 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1730 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1732 /* call XSynchronize with the current value of synchronous */
1733 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1734 xvimagesink->synchronous ? "TRUE" : "FALSE");
1735 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1736 gst_xvimagesink_update_colorbalance (xvimagesink);
1737 gst_xvimagesink_manage_event_thread (xvimagesink);
1739 case GST_STATE_CHANGE_READY_TO_PAUSED:
1741 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1743 case GST_STATE_CHANGE_PAUSED_TO_READY:
1749 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1751 switch (transition) {
1752 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1754 case GST_STATE_CHANGE_PAUSED_TO_READY:
1755 xvimagesink->fps_n = 0;
1756 xvimagesink->fps_d = 1;
1757 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1758 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1759 g_mutex_lock (xvimagesink->flow_lock);
1760 if (xvimagesink->pool)
1761 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1762 g_mutex_unlock (xvimagesink->flow_lock);
1764 case GST_STATE_CHANGE_READY_TO_NULL:
1765 gst_xvimagesink_reset (xvimagesink);
1776 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1777 GstClockTime * start, GstClockTime * end)
1779 GstXvImageSink *xvimagesink;
1781 xvimagesink = GST_XVIMAGESINK (bsink);
1783 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1784 *start = GST_BUFFER_TIMESTAMP (buf);
1785 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1786 *end = *start + GST_BUFFER_DURATION (buf);
1788 if (xvimagesink->fps_n > 0) {
1790 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1791 xvimagesink->fps_n);
1797 static GstFlowReturn
1798 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1801 GstXvImageSink *xvimagesink;
1802 GstXvImageMeta *meta;
1805 xvimagesink = GST_XVIMAGESINK (vsink);
1807 meta = gst_buffer_get_xvimage_meta (buf);
1809 if (meta && meta->sink == xvimagesink) {
1810 /* If this buffer has been allocated using our buffer management we simply
1811 put the ximage which is in the PRIVATE pointer */
1812 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1817 GstVideoFrame src, dest;
1819 /* Else we have to copy the data into our private image, */
1820 /* if we have one... */
1821 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1823 /* we should have a pool, configured in setcaps */
1824 if (xvimagesink->pool == NULL)
1827 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1828 goto activate_failed;
1830 /* take a buffer form our pool */
1831 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1832 if (res != GST_FLOW_OK)
1835 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1836 "slow copy into bufferpool buffer %p", to_put);
1838 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1839 goto invalid_buffer;
1841 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1842 gst_video_frame_unmap (&src);
1843 goto invalid_buffer;
1846 gst_video_frame_copy (&dest, &src);
1848 gst_video_frame_unmap (&dest);
1849 gst_video_frame_unmap (&src);
1852 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1857 gst_buffer_unref (to_put);
1864 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1865 ("Internal error: can't allocate images"),
1866 ("We don't have a bufferpool negotiated"));
1867 return GST_FLOW_ERROR;
1871 /* No image available. That's very bad ! */
1872 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1877 /* No Window available to put our image into */
1878 GST_WARNING_OBJECT (xvimagesink, "could map image");
1884 /* No Window available to put our image into */
1885 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1886 res = GST_FLOW_ERROR;
1891 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1892 res = GST_FLOW_ERROR;
1898 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1900 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1902 switch (GST_EVENT_TYPE (event)) {
1903 case GST_EVENT_TAG:{
1905 gchar *title = NULL;
1907 gst_event_parse_tag (event, &l);
1908 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1911 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1912 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1922 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1926 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1928 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1929 GstBufferPool *pool;
1930 GstStructure *config;
1935 gst_query_parse_allocation (query, &caps, &need_pool);
1940 g_mutex_lock (xvimagesink->flow_lock);
1941 if ((pool = xvimagesink->pool))
1942 gst_object_ref (pool);
1943 g_mutex_unlock (xvimagesink->flow_lock);
1946 const GstCaps *pcaps;
1948 /* we had a pool, check caps */
1949 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1950 config = gst_buffer_pool_get_config (pool);
1951 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1953 if (!gst_caps_is_equal (caps, pcaps)) {
1954 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1955 /* different caps, we can't use this pool */
1956 gst_object_unref (pool);
1960 if (pool == NULL && need_pool) {
1963 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1964 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1966 if (!gst_video_info_from_caps (&info, caps))
1969 /* the normal size of a frame */
1972 config = gst_buffer_pool_get_config (pool);
1973 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1974 if (!gst_buffer_pool_set_config (pool, config))
1977 /* we need at least 2 buffer because we hold on to the last one */
1978 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1980 /* we also support various metadata */
1981 gst_query_add_allocation_meta (query, GST_VIDEO_META_API);
1982 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API);
1984 gst_object_unref (pool);
1991 GST_DEBUG_OBJECT (bsink, "no caps specified");
1996 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2001 GST_DEBUG_OBJECT (bsink, "failed setting config");
2006 /* Interfaces stuff */
2008 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2009 GstStructure * structure)
2011 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2014 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2016 GstVideoRectangle src, dst, result;
2017 gdouble x, y, xscale = 1.0, yscale = 1.0;
2019 event = gst_event_new_navigation (structure);
2021 /* We take the flow_lock while we look at the window */
2022 g_mutex_lock (xvimagesink->flow_lock);
2024 if (!xvimagesink->xwindow) {
2025 g_mutex_unlock (xvimagesink->flow_lock);
2029 if (xvimagesink->keep_aspect) {
2030 /* We get the frame position using the calculated geometry from _setcaps
2031 that respect pixel aspect ratios */
2032 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2033 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2034 dst.w = xvimagesink->render_rect.w;
2035 dst.h = xvimagesink->render_rect.h;
2037 gst_video_sink_center_rect (src, dst, &result, TRUE);
2038 result.x += xvimagesink->render_rect.x;
2039 result.y += xvimagesink->render_rect.y;
2041 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2044 g_mutex_unlock (xvimagesink->flow_lock);
2046 /* We calculate scaling using the original video frames geometry to include
2047 pixel aspect ratio scaling. */
2048 xscale = (gdouble) xvimagesink->video_width / result.w;
2049 yscale = (gdouble) xvimagesink->video_height / result.h;
2051 /* Converting pointer coordinates to the non scaled geometry */
2052 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2053 x = MIN (x, result.x + result.w);
2054 x = MAX (x - result.x, 0);
2055 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2056 (gdouble) x * xscale, NULL);
2058 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2059 y = MIN (y, result.y + result.h);
2060 y = MAX (y - result.y, 0);
2061 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2062 (gdouble) y * yscale, NULL);
2065 gst_pad_send_event (peer, event);
2066 gst_object_unref (peer);
2071 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2073 iface->send_event = gst_xvimagesink_navigation_send_event;
2077 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2079 XID xwindow_id = id;
2080 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2081 GstXWindow *xwindow = NULL;
2083 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2085 g_mutex_lock (xvimagesink->flow_lock);
2087 /* If we already use that window return */
2088 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2089 g_mutex_unlock (xvimagesink->flow_lock);
2093 /* If the element has not initialized the X11 context try to do so */
2094 if (!xvimagesink->xcontext &&
2095 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2096 g_mutex_unlock (xvimagesink->flow_lock);
2097 /* we have thrown a GST_ELEMENT_ERROR now */
2101 gst_xvimagesink_update_colorbalance (xvimagesink);
2103 /* If a window is there already we destroy it */
2104 if (xvimagesink->xwindow) {
2105 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2106 xvimagesink->xwindow = NULL;
2109 /* If the xid is 0 we go back to an internal window */
2110 if (xwindow_id == 0) {
2111 /* If no width/height caps nego did not happen window will be created
2112 during caps nego then */
2113 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2114 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2116 gst_xvimagesink_xwindow_new (xvimagesink,
2117 GST_VIDEO_SINK_WIDTH (xvimagesink),
2118 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2121 XWindowAttributes attr;
2123 xwindow = g_new0 (GstXWindow, 1);
2124 xwindow->win = xwindow_id;
2126 /* Set the event we want to receive and create a GC */
2127 g_mutex_lock (xvimagesink->x_lock);
2129 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2131 xwindow->width = attr.width;
2132 xwindow->height = attr.height;
2133 xwindow->internal = FALSE;
2134 if (!xvimagesink->have_render_rect) {
2135 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2136 xvimagesink->render_rect.w = attr.width;
2137 xvimagesink->render_rect.h = attr.height;
2139 if (xvimagesink->handle_events) {
2140 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2141 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2145 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2146 xwindow->win, 0, NULL);
2147 g_mutex_unlock (xvimagesink->x_lock);
2151 xvimagesink->xwindow = xwindow;
2153 g_mutex_unlock (xvimagesink->flow_lock);
2157 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2159 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2161 GST_DEBUG ("doing expose");
2162 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2163 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2167 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2168 gboolean handle_events)
2170 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2172 xvimagesink->handle_events = handle_events;
2174 g_mutex_lock (xvimagesink->flow_lock);
2176 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2177 g_mutex_unlock (xvimagesink->flow_lock);
2181 g_mutex_lock (xvimagesink->x_lock);
2183 if (handle_events) {
2184 if (xvimagesink->xwindow->internal) {
2185 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2186 ExposureMask | StructureNotifyMask | PointerMotionMask |
2187 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2189 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2190 ExposureMask | StructureNotifyMask | PointerMotionMask |
2191 KeyPressMask | KeyReleaseMask);
2194 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2197 g_mutex_unlock (xvimagesink->x_lock);
2199 g_mutex_unlock (xvimagesink->flow_lock);
2203 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2204 gint width, gint height)
2206 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2208 /* FIXME: how about some locking? */
2209 if (width >= 0 && height >= 0) {
2210 xvimagesink->render_rect.x = x;
2211 xvimagesink->render_rect.y = y;
2212 xvimagesink->render_rect.w = width;
2213 xvimagesink->render_rect.h = height;
2214 xvimagesink->have_render_rect = TRUE;
2216 xvimagesink->render_rect.x = 0;
2217 xvimagesink->render_rect.y = 0;
2218 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2219 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2220 xvimagesink->have_render_rect = FALSE;
2225 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2227 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2228 iface->expose = gst_xvimagesink_expose;
2229 iface->handle_events = gst_xvimagesink_set_event_handling;
2230 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2233 static const GList *
2234 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2236 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2238 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2240 if (xvimagesink->xcontext)
2241 return xvimagesink->xcontext->channels_list;
2247 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2248 GstColorBalanceChannel * channel, gint value)
2250 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2252 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2253 g_return_if_fail (channel->label != NULL);
2255 xvimagesink->cb_changed = TRUE;
2257 /* Normalize val to [-1000, 1000] */
2258 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2259 (double) (channel->max_value - channel->min_value));
2261 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2262 xvimagesink->hue = value;
2263 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2264 xvimagesink->saturation = value;
2265 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2266 xvimagesink->contrast = value;
2267 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2268 xvimagesink->brightness = value;
2270 g_warning ("got an unknown channel %s", channel->label);
2274 gst_xvimagesink_update_colorbalance (xvimagesink);
2278 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2279 GstColorBalanceChannel * channel)
2281 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2284 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2285 g_return_val_if_fail (channel->label != NULL, 0);
2287 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2288 value = xvimagesink->hue;
2289 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2290 value = xvimagesink->saturation;
2291 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2292 value = xvimagesink->contrast;
2293 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2294 value = xvimagesink->brightness;
2296 g_warning ("got an unknown channel %s", channel->label);
2299 /* Normalize val to [channel->min_value, channel->max_value] */
2300 value = channel->min_value + (channel->max_value - channel->min_value) *
2301 (value + 1000) / 2000;
2307 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2309 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2310 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2311 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2312 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2316 static const GList *
2317 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2319 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2320 static GList *list = NULL;
2323 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2325 g_list_append (list, g_object_class_find_property (klass,
2326 "autopaint-colorkey"));
2328 g_list_append (list, g_object_class_find_property (klass,
2331 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2338 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2339 guint prop_id, const GParamSpec * pspec)
2341 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2345 case PROP_AUTOPAINT_COLORKEY:
2346 case PROP_DOUBLE_BUFFER:
2348 GST_DEBUG_OBJECT (xvimagesink,
2349 "probing device list and get capabilities");
2350 if (!xvimagesink->xcontext) {
2351 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2352 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2356 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2362 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2363 guint prop_id, const GParamSpec * pspec)
2365 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2366 gboolean ret = FALSE;
2370 case PROP_AUTOPAINT_COLORKEY:
2371 case PROP_DOUBLE_BUFFER:
2373 if (xvimagesink->xcontext != NULL) {
2380 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2387 static GValueArray *
2388 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2389 guint prop_id, const GParamSpec * pspec)
2391 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2392 GValueArray *array = NULL;
2394 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2395 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2404 GValue value = { 0 };
2406 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2407 g_value_init (&value, G_TYPE_STRING);
2409 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2410 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2412 g_value_set_string (&value, adaptor_id_s);
2413 g_value_array_append (array, &value);
2414 g_free (adaptor_id_s);
2416 g_value_unset (&value);
2419 case PROP_AUTOPAINT_COLORKEY:
2420 if (xvimagesink->have_autopaint_colorkey) {
2421 GValue value = { 0 };
2423 array = g_value_array_new (2);
2424 g_value_init (&value, G_TYPE_BOOLEAN);
2425 g_value_set_boolean (&value, FALSE);
2426 g_value_array_append (array, &value);
2427 g_value_set_boolean (&value, TRUE);
2428 g_value_array_append (array, &value);
2429 g_value_unset (&value);
2432 case PROP_DOUBLE_BUFFER:
2433 if (xvimagesink->have_double_buffer) {
2434 GValue value = { 0 };
2436 array = g_value_array_new (2);
2437 g_value_init (&value, G_TYPE_BOOLEAN);
2438 g_value_set_boolean (&value, FALSE);
2439 g_value_array_append (array, &value);
2440 g_value_set_boolean (&value, TRUE);
2441 g_value_array_append (array, &value);
2442 g_value_unset (&value);
2446 if (xvimagesink->have_colorkey) {
2447 GValue value = { 0 };
2449 array = g_value_array_new (1);
2450 g_value_init (&value, GST_TYPE_INT_RANGE);
2451 gst_value_set_int_range (&value, 0, 0xffffff);
2452 g_value_array_append (array, &value);
2453 g_value_unset (&value);
2457 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2466 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2469 iface->get_properties = gst_xvimagesink_probe_get_properties;
2470 iface->probe_property = gst_xvimagesink_probe_probe_property;
2471 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2472 iface->get_values = gst_xvimagesink_probe_get_values;
2476 /* =========================================== */
2478 /* Init & Class init */
2480 /* =========================================== */
2483 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2484 const GValue * value, GParamSpec * pspec)
2486 GstXvImageSink *xvimagesink;
2488 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2490 xvimagesink = GST_XVIMAGESINK (object);
2494 xvimagesink->hue = g_value_get_int (value);
2495 xvimagesink->cb_changed = TRUE;
2496 gst_xvimagesink_update_colorbalance (xvimagesink);
2499 xvimagesink->contrast = g_value_get_int (value);
2500 xvimagesink->cb_changed = TRUE;
2501 gst_xvimagesink_update_colorbalance (xvimagesink);
2503 case PROP_BRIGHTNESS:
2504 xvimagesink->brightness = g_value_get_int (value);
2505 xvimagesink->cb_changed = TRUE;
2506 gst_xvimagesink_update_colorbalance (xvimagesink);
2508 case PROP_SATURATION:
2509 xvimagesink->saturation = g_value_get_int (value);
2510 xvimagesink->cb_changed = TRUE;
2511 gst_xvimagesink_update_colorbalance (xvimagesink);
2514 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2516 case PROP_SYNCHRONOUS:
2517 xvimagesink->synchronous = g_value_get_boolean (value);
2518 if (xvimagesink->xcontext) {
2519 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2520 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2521 xvimagesink->synchronous ? "TRUE" : "FALSE");
2524 case PROP_PIXEL_ASPECT_RATIO:
2525 g_free (xvimagesink->par);
2526 xvimagesink->par = g_new0 (GValue, 1);
2527 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2528 if (!g_value_transform (value, xvimagesink->par)) {
2529 g_warning ("Could not transform string to aspect ratio");
2530 gst_value_set_fraction (xvimagesink->par, 1, 1);
2532 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2533 gst_value_get_fraction_numerator (xvimagesink->par),
2534 gst_value_get_fraction_denominator (xvimagesink->par));
2536 case PROP_FORCE_ASPECT_RATIO:
2537 xvimagesink->keep_aspect = g_value_get_boolean (value);
2539 case PROP_HANDLE_EVENTS:
2540 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2541 g_value_get_boolean (value));
2542 gst_xvimagesink_manage_event_thread (xvimagesink);
2545 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2547 case PROP_HANDLE_EXPOSE:
2548 xvimagesink->handle_expose = g_value_get_boolean (value);
2549 gst_xvimagesink_manage_event_thread (xvimagesink);
2551 case PROP_DOUBLE_BUFFER:
2552 xvimagesink->double_buffer = g_value_get_boolean (value);
2554 case PROP_AUTOPAINT_COLORKEY:
2555 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2558 xvimagesink->colorkey = g_value_get_int (value);
2560 case PROP_DRAW_BORDERS:
2561 xvimagesink->draw_borders = g_value_get_boolean (value);
2564 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2570 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2571 GValue * value, GParamSpec * pspec)
2573 GstXvImageSink *xvimagesink;
2575 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2577 xvimagesink = GST_XVIMAGESINK (object);
2581 g_value_set_int (value, xvimagesink->hue);
2584 g_value_set_int (value, xvimagesink->contrast);
2586 case PROP_BRIGHTNESS:
2587 g_value_set_int (value, xvimagesink->brightness);
2589 case PROP_SATURATION:
2590 g_value_set_int (value, xvimagesink->saturation);
2593 g_value_set_string (value, xvimagesink->display_name);
2595 case PROP_SYNCHRONOUS:
2596 g_value_set_boolean (value, xvimagesink->synchronous);
2598 case PROP_PIXEL_ASPECT_RATIO:
2599 if (xvimagesink->par)
2600 g_value_transform (xvimagesink->par, value);
2602 case PROP_FORCE_ASPECT_RATIO:
2603 g_value_set_boolean (value, xvimagesink->keep_aspect);
2605 case PROP_HANDLE_EVENTS:
2606 g_value_set_boolean (value, xvimagesink->handle_events);
2610 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2612 g_value_set_string (value, adaptor_no_s);
2613 g_free (adaptor_no_s);
2616 case PROP_DEVICE_NAME:
2617 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2618 g_value_set_string (value,
2619 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2621 g_value_set_string (value, NULL);
2624 case PROP_HANDLE_EXPOSE:
2625 g_value_set_boolean (value, xvimagesink->handle_expose);
2627 case PROP_DOUBLE_BUFFER:
2628 g_value_set_boolean (value, xvimagesink->double_buffer);
2630 case PROP_AUTOPAINT_COLORKEY:
2631 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2634 g_value_set_int (value, xvimagesink->colorkey);
2636 case PROP_DRAW_BORDERS:
2637 g_value_set_boolean (value, xvimagesink->draw_borders);
2639 case PROP_WINDOW_WIDTH:
2640 if (xvimagesink->xwindow)
2641 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2643 g_value_set_uint64 (value, 0);
2645 case PROP_WINDOW_HEIGHT:
2646 if (xvimagesink->xwindow)
2647 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2649 g_value_set_uint64 (value, 0);
2652 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2658 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2662 GST_OBJECT_LOCK (xvimagesink);
2663 xvimagesink->running = FALSE;
2664 /* grab thread and mark it as NULL */
2665 thread = xvimagesink->event_thread;
2666 xvimagesink->event_thread = NULL;
2667 GST_OBJECT_UNLOCK (xvimagesink);
2669 /* Wait for our event thread to finish before we clean up our stuff. */
2671 g_thread_join (thread);
2673 if (xvimagesink->cur_image) {
2674 gst_buffer_unref (xvimagesink->cur_image);
2675 xvimagesink->cur_image = NULL;
2678 g_mutex_lock (xvimagesink->flow_lock);
2680 if (xvimagesink->pool) {
2681 gst_object_unref (xvimagesink->pool);
2682 xvimagesink->pool = NULL;
2685 if (xvimagesink->xwindow) {
2686 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2687 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2688 xvimagesink->xwindow = NULL;
2690 g_mutex_unlock (xvimagesink->flow_lock);
2692 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2693 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2694 xvimagesink->have_render_rect = FALSE;
2696 gst_xvimagesink_xcontext_clear (xvimagesink);
2699 /* Finalize is called only once, dispose can be called multiple times.
2700 * We use mutexes and don't reset stuff to NULL here so let's register
2703 gst_xvimagesink_finalize (GObject * object)
2705 GstXvImageSink *xvimagesink;
2707 xvimagesink = GST_XVIMAGESINK (object);
2709 gst_xvimagesink_reset (xvimagesink);
2711 if (xvimagesink->display_name) {
2712 g_free (xvimagesink->display_name);
2713 xvimagesink->display_name = NULL;
2716 if (xvimagesink->par) {
2717 g_free (xvimagesink->par);
2718 xvimagesink->par = NULL;
2720 if (xvimagesink->x_lock) {
2721 g_mutex_free (xvimagesink->x_lock);
2722 xvimagesink->x_lock = NULL;
2724 if (xvimagesink->flow_lock) {
2725 g_mutex_free (xvimagesink->flow_lock);
2726 xvimagesink->flow_lock = NULL;
2729 g_free (xvimagesink->media_title);
2731 G_OBJECT_CLASS (parent_class)->finalize (object);
2735 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2737 xvimagesink->display_name = NULL;
2738 xvimagesink->adaptor_no = 0;
2739 xvimagesink->xcontext = NULL;
2740 xvimagesink->xwindow = NULL;
2741 xvimagesink->cur_image = NULL;
2743 xvimagesink->hue = xvimagesink->saturation = 0;
2744 xvimagesink->contrast = xvimagesink->brightness = 0;
2745 xvimagesink->cb_changed = FALSE;
2747 xvimagesink->fps_n = 0;
2748 xvimagesink->fps_d = 0;
2749 xvimagesink->video_width = 0;
2750 xvimagesink->video_height = 0;
2752 xvimagesink->x_lock = g_mutex_new ();
2753 xvimagesink->flow_lock = g_mutex_new ();
2755 xvimagesink->pool = NULL;
2757 xvimagesink->synchronous = FALSE;
2758 xvimagesink->double_buffer = TRUE;
2759 xvimagesink->running = FALSE;
2760 xvimagesink->keep_aspect = FALSE;
2761 xvimagesink->handle_events = TRUE;
2762 xvimagesink->par = NULL;
2763 xvimagesink->handle_expose = TRUE;
2764 xvimagesink->autopaint_colorkey = TRUE;
2766 /* on 16bit displays this becomes r,g,b = 1,2,3
2767 * on 24bit displays this becomes r,g,b = 8,8,16
2768 * as a port atom value
2770 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2771 xvimagesink->draw_borders = TRUE;
2775 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2777 GObjectClass *gobject_class;
2778 GstElementClass *gstelement_class;
2779 GstBaseSinkClass *gstbasesink_class;
2780 GstVideoSinkClass *videosink_class;
2782 gobject_class = (GObjectClass *) klass;
2783 gstelement_class = (GstElementClass *) klass;
2784 gstbasesink_class = (GstBaseSinkClass *) klass;
2785 videosink_class = (GstVideoSinkClass *) klass;
2787 parent_class = g_type_class_peek_parent (klass);
2789 gobject_class->set_property = gst_xvimagesink_set_property;
2790 gobject_class->get_property = gst_xvimagesink_get_property;
2792 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2793 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2794 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2795 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2796 g_param_spec_int ("brightness", "Brightness",
2797 "The brightness of the video", -1000, 1000, 0,
2798 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2799 g_object_class_install_property (gobject_class, PROP_HUE,
2800 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2801 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2802 g_object_class_install_property (gobject_class, PROP_SATURATION,
2803 g_param_spec_int ("saturation", "Saturation",
2804 "The saturation of the video", -1000, 1000, 0,
2805 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2806 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2807 g_param_spec_string ("display", "Display", "X Display name", NULL,
2808 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2809 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2810 g_param_spec_boolean ("synchronous", "Synchronous",
2811 "When enabled, runs the X display in synchronous mode. "
2812 "(unrelated to A/V sync, used only for debugging)", FALSE,
2813 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2814 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2815 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2816 "The pixel aspect ratio of the device", "1/1",
2817 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2818 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2819 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2820 "When enabled, scaling will respect original aspect ratio", FALSE,
2821 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2822 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2823 g_param_spec_boolean ("handle-events", "Handle XEvents",
2824 "When enabled, XEvents will be selected and handled", TRUE,
2825 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2826 g_object_class_install_property (gobject_class, PROP_DEVICE,
2827 g_param_spec_string ("device", "Adaptor number",
2828 "The number of the video adaptor", "0",
2829 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2830 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2831 g_param_spec_string ("device-name", "Adaptor name",
2832 "The name of the video adaptor", NULL,
2833 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2835 * GstXvImageSink:handle-expose
2837 * When enabled, the current frame will always be drawn in response to X
2842 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2843 g_param_spec_boolean ("handle-expose", "Handle expose",
2845 "the current frame will always be drawn in response to X Expose "
2846 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2849 * GstXvImageSink:double-buffer
2851 * Whether to double-buffer the output.
2855 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2856 g_param_spec_boolean ("double-buffer", "Double-buffer",
2857 "Whether to double-buffer the output", TRUE,
2858 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2860 * GstXvImageSink:autopaint-colorkey
2862 * Whether to autofill overlay with colorkey
2866 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2867 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2868 "Whether to autofill overlay with colorkey", TRUE,
2869 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2871 * GstXvImageSink:colorkey
2873 * Color to use for the overlay mask.
2877 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2878 g_param_spec_int ("colorkey", "Colorkey",
2879 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2880 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2883 * GstXvImageSink:draw-borders
2885 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2886 * unused parts of the video area.
2890 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2891 g_param_spec_boolean ("draw-borders", "Colorkey",
2892 "Draw black borders to fill unused area in force-aspect-ratio mode",
2893 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2896 * GstXvImageSink:window-width
2898 * Actual width of the video window.
2902 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2903 g_param_spec_uint64 ("window-width", "window-width",
2904 "Width of the window", 0, G_MAXUINT64, 0,
2905 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2908 * GstXvImageSink:window-height
2910 * Actual height of the video window.
2914 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2915 g_param_spec_uint64 ("window-height", "window-height",
2916 "Height of the window", 0, G_MAXUINT64, 0,
2917 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2919 gobject_class->finalize = gst_xvimagesink_finalize;
2921 gst_element_class_set_details_simple (gstelement_class,
2922 "Video sink", "Sink/Video",
2923 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2925 gst_element_class_add_pad_template (gstelement_class,
2926 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2928 gstelement_class->change_state =
2929 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2931 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2932 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2933 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2934 gstbasesink_class->propose_allocation =
2935 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2936 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2938 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);