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 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1838 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1839 "slow copy into bufferpool buffer %p", to_put);
1841 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1842 goto invalid_buffer;
1844 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1845 gst_video_frame_unmap (&src);
1846 goto invalid_buffer;
1849 gst_video_frame_copy (&dest, &src);
1851 gst_video_frame_unmap (&dest);
1852 gst_video_frame_unmap (&src);
1855 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1860 gst_buffer_unref (to_put);
1867 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1868 ("Internal error: can't allocate images"),
1869 ("We don't have a bufferpool negotiated"));
1870 return GST_FLOW_ERROR;
1874 /* No image available. That's very bad ! */
1875 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1880 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1881 ("Failed to create output image buffer"),
1882 ("XServer allocated buffer size did not match input buffer %"
1883 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1884 gst_buffer_get_size (buf)));
1885 res = GST_FLOW_ERROR;
1890 /* No Window available to put our image into */
1891 GST_WARNING_OBJECT (xvimagesink, "could map image");
1897 /* No Window available to put our image into */
1898 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1899 res = GST_FLOW_ERROR;
1904 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1905 res = GST_FLOW_ERROR;
1911 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1913 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1915 switch (GST_EVENT_TYPE (event)) {
1916 case GST_EVENT_TAG:{
1918 gchar *title = NULL;
1920 gst_event_parse_tag (event, &l);
1921 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1924 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1925 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1935 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1939 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1941 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1942 GstBufferPool *pool;
1943 GstStructure *config;
1948 gst_query_parse_allocation (query, &caps, &need_pool);
1953 g_mutex_lock (xvimagesink->flow_lock);
1954 if ((pool = xvimagesink->pool))
1955 gst_object_ref (pool);
1956 g_mutex_unlock (xvimagesink->flow_lock);
1959 const GstCaps *pcaps;
1961 /* we had a pool, check caps */
1962 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1963 config = gst_buffer_pool_get_config (pool);
1964 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL, NULL);
1966 if (!gst_caps_is_equal (caps, pcaps)) {
1967 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1968 /* different caps, we can't use this pool */
1969 gst_object_unref (pool);
1973 if (pool == NULL && need_pool) {
1976 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1977 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1979 if (!gst_video_info_from_caps (&info, caps))
1982 /* the normal size of a frame */
1985 config = gst_buffer_pool_get_config (pool);
1986 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 0);
1987 if (!gst_buffer_pool_set_config (pool, config))
1990 /* we need at least 2 buffer because we hold on to the last one */
1991 gst_query_set_allocation_params (query, size, 2, 0, 0, 0, pool);
1993 /* we also support various metadata */
1994 gst_query_add_allocation_meta (query, GST_VIDEO_META_API);
1995 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API);
1997 gst_object_unref (pool);
2004 GST_DEBUG_OBJECT (bsink, "no caps specified");
2009 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2014 GST_DEBUG_OBJECT (bsink, "failed setting config");
2019 /* Interfaces stuff */
2021 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2022 GstStructure * structure)
2024 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2027 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2029 GstVideoRectangle src, dst, result;
2030 gdouble x, y, xscale = 1.0, yscale = 1.0;
2032 event = gst_event_new_navigation (structure);
2034 /* We take the flow_lock while we look at the window */
2035 g_mutex_lock (xvimagesink->flow_lock);
2037 if (!xvimagesink->xwindow) {
2038 g_mutex_unlock (xvimagesink->flow_lock);
2042 if (xvimagesink->keep_aspect) {
2043 /* We get the frame position using the calculated geometry from _setcaps
2044 that respect pixel aspect ratios */
2045 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2046 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2047 dst.w = xvimagesink->render_rect.w;
2048 dst.h = xvimagesink->render_rect.h;
2050 gst_video_sink_center_rect (src, dst, &result, TRUE);
2051 result.x += xvimagesink->render_rect.x;
2052 result.y += xvimagesink->render_rect.y;
2054 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2057 g_mutex_unlock (xvimagesink->flow_lock);
2059 /* We calculate scaling using the original video frames geometry to include
2060 pixel aspect ratio scaling. */
2061 xscale = (gdouble) xvimagesink->video_width / result.w;
2062 yscale = (gdouble) xvimagesink->video_height / result.h;
2064 /* Converting pointer coordinates to the non scaled geometry */
2065 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2066 x = MIN (x, result.x + result.w);
2067 x = MAX (x - result.x, 0);
2068 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2069 (gdouble) x * xscale, NULL);
2071 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2072 y = MIN (y, result.y + result.h);
2073 y = MAX (y - result.y, 0);
2074 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2075 (gdouble) y * yscale, NULL);
2078 gst_pad_send_event (peer, event);
2079 gst_object_unref (peer);
2084 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2086 iface->send_event = gst_xvimagesink_navigation_send_event;
2090 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2092 XID xwindow_id = id;
2093 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2094 GstXWindow *xwindow = NULL;
2096 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2098 g_mutex_lock (xvimagesink->flow_lock);
2100 /* If we already use that window return */
2101 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2102 g_mutex_unlock (xvimagesink->flow_lock);
2106 /* If the element has not initialized the X11 context try to do so */
2107 if (!xvimagesink->xcontext &&
2108 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2109 g_mutex_unlock (xvimagesink->flow_lock);
2110 /* we have thrown a GST_ELEMENT_ERROR now */
2114 gst_xvimagesink_update_colorbalance (xvimagesink);
2116 /* If a window is there already we destroy it */
2117 if (xvimagesink->xwindow) {
2118 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2119 xvimagesink->xwindow = NULL;
2122 /* If the xid is 0 we go back to an internal window */
2123 if (xwindow_id == 0) {
2124 /* If no width/height caps nego did not happen window will be created
2125 during caps nego then */
2126 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2127 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2129 gst_xvimagesink_xwindow_new (xvimagesink,
2130 GST_VIDEO_SINK_WIDTH (xvimagesink),
2131 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2134 XWindowAttributes attr;
2136 xwindow = g_new0 (GstXWindow, 1);
2137 xwindow->win = xwindow_id;
2139 /* Set the event we want to receive and create a GC */
2140 g_mutex_lock (xvimagesink->x_lock);
2142 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2144 xwindow->width = attr.width;
2145 xwindow->height = attr.height;
2146 xwindow->internal = FALSE;
2147 if (!xvimagesink->have_render_rect) {
2148 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2149 xvimagesink->render_rect.w = attr.width;
2150 xvimagesink->render_rect.h = attr.height;
2152 if (xvimagesink->handle_events) {
2153 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2154 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2158 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2159 xwindow->win, 0, NULL);
2160 g_mutex_unlock (xvimagesink->x_lock);
2164 xvimagesink->xwindow = xwindow;
2166 g_mutex_unlock (xvimagesink->flow_lock);
2170 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2172 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2174 GST_DEBUG ("doing expose");
2175 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2176 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2180 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2181 gboolean handle_events)
2183 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2185 xvimagesink->handle_events = handle_events;
2187 g_mutex_lock (xvimagesink->flow_lock);
2189 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2190 g_mutex_unlock (xvimagesink->flow_lock);
2194 g_mutex_lock (xvimagesink->x_lock);
2196 if (handle_events) {
2197 if (xvimagesink->xwindow->internal) {
2198 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2199 ExposureMask | StructureNotifyMask | PointerMotionMask |
2200 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2202 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2203 ExposureMask | StructureNotifyMask | PointerMotionMask |
2204 KeyPressMask | KeyReleaseMask);
2207 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2210 g_mutex_unlock (xvimagesink->x_lock);
2212 g_mutex_unlock (xvimagesink->flow_lock);
2216 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2217 gint width, gint height)
2219 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2221 /* FIXME: how about some locking? */
2222 if (width >= 0 && height >= 0) {
2223 xvimagesink->render_rect.x = x;
2224 xvimagesink->render_rect.y = y;
2225 xvimagesink->render_rect.w = width;
2226 xvimagesink->render_rect.h = height;
2227 xvimagesink->have_render_rect = TRUE;
2229 xvimagesink->render_rect.x = 0;
2230 xvimagesink->render_rect.y = 0;
2231 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2232 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2233 xvimagesink->have_render_rect = FALSE;
2238 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2240 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2241 iface->expose = gst_xvimagesink_expose;
2242 iface->handle_events = gst_xvimagesink_set_event_handling;
2243 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2246 static const GList *
2247 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2249 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2251 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2253 if (xvimagesink->xcontext)
2254 return xvimagesink->xcontext->channels_list;
2260 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2261 GstColorBalanceChannel * channel, gint value)
2263 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2265 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2266 g_return_if_fail (channel->label != NULL);
2268 xvimagesink->cb_changed = TRUE;
2270 /* Normalize val to [-1000, 1000] */
2271 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2272 (double) (channel->max_value - channel->min_value));
2274 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2275 xvimagesink->hue = value;
2276 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2277 xvimagesink->saturation = value;
2278 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2279 xvimagesink->contrast = value;
2280 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2281 xvimagesink->brightness = value;
2283 g_warning ("got an unknown channel %s", channel->label);
2287 gst_xvimagesink_update_colorbalance (xvimagesink);
2291 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2292 GstColorBalanceChannel * channel)
2294 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2297 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2298 g_return_val_if_fail (channel->label != NULL, 0);
2300 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2301 value = xvimagesink->hue;
2302 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2303 value = xvimagesink->saturation;
2304 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2305 value = xvimagesink->contrast;
2306 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2307 value = xvimagesink->brightness;
2309 g_warning ("got an unknown channel %s", channel->label);
2312 /* Normalize val to [channel->min_value, channel->max_value] */
2313 value = channel->min_value + (channel->max_value - channel->min_value) *
2314 (value + 1000) / 2000;
2320 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2322 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2323 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2324 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2325 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2329 static const GList *
2330 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2332 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2333 static GList *list = NULL;
2336 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2338 g_list_append (list, g_object_class_find_property (klass,
2339 "autopaint-colorkey"));
2341 g_list_append (list, g_object_class_find_property (klass,
2344 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2351 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2352 guint prop_id, const GParamSpec * pspec)
2354 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2358 case PROP_AUTOPAINT_COLORKEY:
2359 case PROP_DOUBLE_BUFFER:
2361 GST_DEBUG_OBJECT (xvimagesink,
2362 "probing device list and get capabilities");
2363 if (!xvimagesink->xcontext) {
2364 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2365 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2369 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2375 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2376 guint prop_id, const GParamSpec * pspec)
2378 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2379 gboolean ret = FALSE;
2383 case PROP_AUTOPAINT_COLORKEY:
2384 case PROP_DOUBLE_BUFFER:
2386 if (xvimagesink->xcontext != NULL) {
2393 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2400 static GValueArray *
2401 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2402 guint prop_id, const GParamSpec * pspec)
2404 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2405 GValueArray *array = NULL;
2407 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2408 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2417 GValue value = { 0 };
2419 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2420 g_value_init (&value, G_TYPE_STRING);
2422 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2423 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2425 g_value_set_string (&value, adaptor_id_s);
2426 g_value_array_append (array, &value);
2427 g_free (adaptor_id_s);
2429 g_value_unset (&value);
2432 case PROP_AUTOPAINT_COLORKEY:
2433 if (xvimagesink->have_autopaint_colorkey) {
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);
2445 case PROP_DOUBLE_BUFFER:
2446 if (xvimagesink->have_double_buffer) {
2447 GValue value = { 0 };
2449 array = g_value_array_new (2);
2450 g_value_init (&value, G_TYPE_BOOLEAN);
2451 g_value_set_boolean (&value, FALSE);
2452 g_value_array_append (array, &value);
2453 g_value_set_boolean (&value, TRUE);
2454 g_value_array_append (array, &value);
2455 g_value_unset (&value);
2459 if (xvimagesink->have_colorkey) {
2460 GValue value = { 0 };
2462 array = g_value_array_new (1);
2463 g_value_init (&value, GST_TYPE_INT_RANGE);
2464 gst_value_set_int_range (&value, 0, 0xffffff);
2465 g_value_array_append (array, &value);
2466 g_value_unset (&value);
2470 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2479 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2482 iface->get_properties = gst_xvimagesink_probe_get_properties;
2483 iface->probe_property = gst_xvimagesink_probe_probe_property;
2484 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2485 iface->get_values = gst_xvimagesink_probe_get_values;
2489 /* =========================================== */
2491 /* Init & Class init */
2493 /* =========================================== */
2496 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2497 const GValue * value, GParamSpec * pspec)
2499 GstXvImageSink *xvimagesink;
2501 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2503 xvimagesink = GST_XVIMAGESINK (object);
2507 xvimagesink->hue = g_value_get_int (value);
2508 xvimagesink->cb_changed = TRUE;
2509 gst_xvimagesink_update_colorbalance (xvimagesink);
2512 xvimagesink->contrast = g_value_get_int (value);
2513 xvimagesink->cb_changed = TRUE;
2514 gst_xvimagesink_update_colorbalance (xvimagesink);
2516 case PROP_BRIGHTNESS:
2517 xvimagesink->brightness = g_value_get_int (value);
2518 xvimagesink->cb_changed = TRUE;
2519 gst_xvimagesink_update_colorbalance (xvimagesink);
2521 case PROP_SATURATION:
2522 xvimagesink->saturation = g_value_get_int (value);
2523 xvimagesink->cb_changed = TRUE;
2524 gst_xvimagesink_update_colorbalance (xvimagesink);
2527 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2529 case PROP_SYNCHRONOUS:
2530 xvimagesink->synchronous = g_value_get_boolean (value);
2531 if (xvimagesink->xcontext) {
2532 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2533 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2534 xvimagesink->synchronous ? "TRUE" : "FALSE");
2537 case PROP_PIXEL_ASPECT_RATIO:
2538 g_free (xvimagesink->par);
2539 xvimagesink->par = g_new0 (GValue, 1);
2540 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2541 if (!g_value_transform (value, xvimagesink->par)) {
2542 g_warning ("Could not transform string to aspect ratio");
2543 gst_value_set_fraction (xvimagesink->par, 1, 1);
2545 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2546 gst_value_get_fraction_numerator (xvimagesink->par),
2547 gst_value_get_fraction_denominator (xvimagesink->par));
2549 case PROP_FORCE_ASPECT_RATIO:
2550 xvimagesink->keep_aspect = g_value_get_boolean (value);
2552 case PROP_HANDLE_EVENTS:
2553 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2554 g_value_get_boolean (value));
2555 gst_xvimagesink_manage_event_thread (xvimagesink);
2558 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2560 case PROP_HANDLE_EXPOSE:
2561 xvimagesink->handle_expose = g_value_get_boolean (value);
2562 gst_xvimagesink_manage_event_thread (xvimagesink);
2564 case PROP_DOUBLE_BUFFER:
2565 xvimagesink->double_buffer = g_value_get_boolean (value);
2567 case PROP_AUTOPAINT_COLORKEY:
2568 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2571 xvimagesink->colorkey = g_value_get_int (value);
2573 case PROP_DRAW_BORDERS:
2574 xvimagesink->draw_borders = g_value_get_boolean (value);
2577 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2583 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2584 GValue * value, GParamSpec * pspec)
2586 GstXvImageSink *xvimagesink;
2588 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2590 xvimagesink = GST_XVIMAGESINK (object);
2594 g_value_set_int (value, xvimagesink->hue);
2597 g_value_set_int (value, xvimagesink->contrast);
2599 case PROP_BRIGHTNESS:
2600 g_value_set_int (value, xvimagesink->brightness);
2602 case PROP_SATURATION:
2603 g_value_set_int (value, xvimagesink->saturation);
2606 g_value_set_string (value, xvimagesink->display_name);
2608 case PROP_SYNCHRONOUS:
2609 g_value_set_boolean (value, xvimagesink->synchronous);
2611 case PROP_PIXEL_ASPECT_RATIO:
2612 if (xvimagesink->par)
2613 g_value_transform (xvimagesink->par, value);
2615 case PROP_FORCE_ASPECT_RATIO:
2616 g_value_set_boolean (value, xvimagesink->keep_aspect);
2618 case PROP_HANDLE_EVENTS:
2619 g_value_set_boolean (value, xvimagesink->handle_events);
2623 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2625 g_value_set_string (value, adaptor_no_s);
2626 g_free (adaptor_no_s);
2629 case PROP_DEVICE_NAME:
2630 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2631 g_value_set_string (value,
2632 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2634 g_value_set_string (value, NULL);
2637 case PROP_HANDLE_EXPOSE:
2638 g_value_set_boolean (value, xvimagesink->handle_expose);
2640 case PROP_DOUBLE_BUFFER:
2641 g_value_set_boolean (value, xvimagesink->double_buffer);
2643 case PROP_AUTOPAINT_COLORKEY:
2644 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2647 g_value_set_int (value, xvimagesink->colorkey);
2649 case PROP_DRAW_BORDERS:
2650 g_value_set_boolean (value, xvimagesink->draw_borders);
2652 case PROP_WINDOW_WIDTH:
2653 if (xvimagesink->xwindow)
2654 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2656 g_value_set_uint64 (value, 0);
2658 case PROP_WINDOW_HEIGHT:
2659 if (xvimagesink->xwindow)
2660 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2662 g_value_set_uint64 (value, 0);
2665 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2671 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2675 GST_OBJECT_LOCK (xvimagesink);
2676 xvimagesink->running = FALSE;
2677 /* grab thread and mark it as NULL */
2678 thread = xvimagesink->event_thread;
2679 xvimagesink->event_thread = NULL;
2680 GST_OBJECT_UNLOCK (xvimagesink);
2682 /* Wait for our event thread to finish before we clean up our stuff. */
2684 g_thread_join (thread);
2686 if (xvimagesink->cur_image) {
2687 gst_buffer_unref (xvimagesink->cur_image);
2688 xvimagesink->cur_image = NULL;
2691 g_mutex_lock (xvimagesink->flow_lock);
2693 if (xvimagesink->pool) {
2694 gst_object_unref (xvimagesink->pool);
2695 xvimagesink->pool = NULL;
2698 if (xvimagesink->xwindow) {
2699 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2700 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2701 xvimagesink->xwindow = NULL;
2703 g_mutex_unlock (xvimagesink->flow_lock);
2705 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2706 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2707 xvimagesink->have_render_rect = FALSE;
2709 gst_xvimagesink_xcontext_clear (xvimagesink);
2712 /* Finalize is called only once, dispose can be called multiple times.
2713 * We use mutexes and don't reset stuff to NULL here so let's register
2716 gst_xvimagesink_finalize (GObject * object)
2718 GstXvImageSink *xvimagesink;
2720 xvimagesink = GST_XVIMAGESINK (object);
2722 gst_xvimagesink_reset (xvimagesink);
2724 if (xvimagesink->display_name) {
2725 g_free (xvimagesink->display_name);
2726 xvimagesink->display_name = NULL;
2729 if (xvimagesink->par) {
2730 g_free (xvimagesink->par);
2731 xvimagesink->par = NULL;
2733 if (xvimagesink->x_lock) {
2734 g_mutex_free (xvimagesink->x_lock);
2735 xvimagesink->x_lock = NULL;
2737 if (xvimagesink->flow_lock) {
2738 g_mutex_free (xvimagesink->flow_lock);
2739 xvimagesink->flow_lock = NULL;
2742 g_free (xvimagesink->media_title);
2744 G_OBJECT_CLASS (parent_class)->finalize (object);
2748 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2750 xvimagesink->display_name = NULL;
2751 xvimagesink->adaptor_no = 0;
2752 xvimagesink->xcontext = NULL;
2753 xvimagesink->xwindow = NULL;
2754 xvimagesink->cur_image = NULL;
2756 xvimagesink->hue = xvimagesink->saturation = 0;
2757 xvimagesink->contrast = xvimagesink->brightness = 0;
2758 xvimagesink->cb_changed = FALSE;
2760 xvimagesink->fps_n = 0;
2761 xvimagesink->fps_d = 0;
2762 xvimagesink->video_width = 0;
2763 xvimagesink->video_height = 0;
2765 xvimagesink->x_lock = g_mutex_new ();
2766 xvimagesink->flow_lock = g_mutex_new ();
2768 xvimagesink->pool = NULL;
2770 xvimagesink->synchronous = FALSE;
2771 xvimagesink->double_buffer = TRUE;
2772 xvimagesink->running = FALSE;
2773 xvimagesink->keep_aspect = FALSE;
2774 xvimagesink->handle_events = TRUE;
2775 xvimagesink->par = NULL;
2776 xvimagesink->handle_expose = TRUE;
2777 xvimagesink->autopaint_colorkey = TRUE;
2779 /* on 16bit displays this becomes r,g,b = 1,2,3
2780 * on 24bit displays this becomes r,g,b = 8,8,16
2781 * as a port atom value
2783 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2784 xvimagesink->draw_borders = TRUE;
2788 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2790 GObjectClass *gobject_class;
2791 GstElementClass *gstelement_class;
2792 GstBaseSinkClass *gstbasesink_class;
2793 GstVideoSinkClass *videosink_class;
2795 gobject_class = (GObjectClass *) klass;
2796 gstelement_class = (GstElementClass *) klass;
2797 gstbasesink_class = (GstBaseSinkClass *) klass;
2798 videosink_class = (GstVideoSinkClass *) klass;
2800 parent_class = g_type_class_peek_parent (klass);
2802 gobject_class->set_property = gst_xvimagesink_set_property;
2803 gobject_class->get_property = gst_xvimagesink_get_property;
2805 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2806 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2807 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2808 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2809 g_param_spec_int ("brightness", "Brightness",
2810 "The brightness of the video", -1000, 1000, 0,
2811 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2812 g_object_class_install_property (gobject_class, PROP_HUE,
2813 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2814 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2815 g_object_class_install_property (gobject_class, PROP_SATURATION,
2816 g_param_spec_int ("saturation", "Saturation",
2817 "The saturation of the video", -1000, 1000, 0,
2818 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2819 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2820 g_param_spec_string ("display", "Display", "X Display name", NULL,
2821 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2822 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2823 g_param_spec_boolean ("synchronous", "Synchronous",
2824 "When enabled, runs the X display in synchronous mode. "
2825 "(unrelated to A/V sync, used only for debugging)", FALSE,
2826 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2827 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2828 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2829 "The pixel aspect ratio of the device", "1/1",
2830 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2831 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2832 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2833 "When enabled, scaling will respect original aspect ratio", FALSE,
2834 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2835 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2836 g_param_spec_boolean ("handle-events", "Handle XEvents",
2837 "When enabled, XEvents will be selected and handled", TRUE,
2838 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2839 g_object_class_install_property (gobject_class, PROP_DEVICE,
2840 g_param_spec_string ("device", "Adaptor number",
2841 "The number of the video adaptor", "0",
2842 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2843 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2844 g_param_spec_string ("device-name", "Adaptor name",
2845 "The name of the video adaptor", NULL,
2846 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2848 * GstXvImageSink:handle-expose
2850 * When enabled, the current frame will always be drawn in response to X
2855 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2856 g_param_spec_boolean ("handle-expose", "Handle expose",
2858 "the current frame will always be drawn in response to X Expose "
2859 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2862 * GstXvImageSink:double-buffer
2864 * Whether to double-buffer the output.
2868 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2869 g_param_spec_boolean ("double-buffer", "Double-buffer",
2870 "Whether to double-buffer the output", TRUE,
2871 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2873 * GstXvImageSink:autopaint-colorkey
2875 * Whether to autofill overlay with colorkey
2879 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2880 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2881 "Whether to autofill overlay with colorkey", TRUE,
2882 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2884 * GstXvImageSink:colorkey
2886 * Color to use for the overlay mask.
2890 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2891 g_param_spec_int ("colorkey", "Colorkey",
2892 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2893 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2896 * GstXvImageSink:draw-borders
2898 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2899 * unused parts of the video area.
2903 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2904 g_param_spec_boolean ("draw-borders", "Colorkey",
2905 "Draw black borders to fill unused area in force-aspect-ratio mode",
2906 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2909 * GstXvImageSink:window-width
2911 * Actual width of the video window.
2915 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2916 g_param_spec_uint64 ("window-width", "window-width",
2917 "Width of the window", 0, G_MAXUINT64, 0,
2918 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2921 * GstXvImageSink:window-height
2923 * Actual height of the video window.
2927 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2928 g_param_spec_uint64 ("window-height", "window-height",
2929 "Height of the window", 0, G_MAXUINT64, 0,
2930 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2932 gobject_class->finalize = gst_xvimagesink_finalize;
2934 gst_element_class_set_details_simple (gstelement_class,
2935 "Video sink", "Sink/Video",
2936 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2938 gst_element_class_add_pad_template (gstelement_class,
2939 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2941 gstelement_class->change_state =
2942 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2944 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2945 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2946 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2947 gstbasesink_class->propose_allocation =
2948 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2949 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2951 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);