2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theorically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/xoverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/video.h>
123 #include <gst/video/gstmetavideo.h>
126 #include "xvimagesink.h"
128 /* Debugging category */
129 #include <gst/gstinfo.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 (GstXOverlay * 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_interface_init (GstImplementsInterfaceClass *
199 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
200 static void gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface);
201 static void gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface);
203 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
205 #define gst_xvimagesink_parent_class parent_class
206 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
207 G_IMPLEMENT_INTERFACE (GST_TYPE_IMPLEMENTS_INTERFACE,
208 gst_xvimagesink_interface_init);
209 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
210 gst_xvimagesink_navigation_init);
211 G_IMPLEMENT_INTERFACE (GST_TYPE_X_OVERLAY, gst_xvimagesink_xoverlay_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
213 gst_xvimagesink_colorbalance_init);
214 G_IMPLEMENT_INTERFACE (GST_TYPE_PROPERTY_PROBE,
215 gst_xvimagesink_property_probe_interface_init));
218 /* ============================================================= */
220 /* Private Methods */
222 /* ============================================================= */
225 /* We are called with the x_lock taken */
227 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
228 GstXWindow * xwindow, GstVideoRectangle rect)
232 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
233 g_return_if_fail (xwindow != NULL);
235 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
236 xvimagesink->xcontext->black);
239 if (rect.x > xvimagesink->render_rect.x) {
240 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
241 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
242 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
246 t1 = rect.x + rect.w;
247 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
249 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
250 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
254 if (rect.y > xvimagesink->render_rect.y) {
255 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
256 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
257 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
261 t1 = rect.y + rect.h;
262 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
264 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
265 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
269 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
270 * if no window was available */
272 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
274 GstMetaXvImage *meta;
275 GstVideoRectangle result;
276 gboolean draw_border = FALSE;
278 /* We take the flow_lock. If expose is in there we don't want to run
279 concurrently from the data flow thread */
280 g_mutex_lock (xvimagesink->flow_lock);
282 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
283 g_mutex_unlock (xvimagesink->flow_lock);
287 /* Draw borders when displaying the first frame. After this
288 draw borders only on expose event or after a size change. */
289 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
293 /* Store a reference to the last image we put, lose the previous one */
294 if (xvimage && xvimagesink->cur_image != xvimage) {
295 if (xvimagesink->cur_image) {
296 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
297 gst_buffer_unref (xvimagesink->cur_image);
299 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
300 xvimagesink->cur_image = gst_buffer_ref (xvimage);
303 /* Expose sends a NULL image, we take the latest frame */
305 if (xvimagesink->cur_image) {
307 xvimage = xvimagesink->cur_image;
309 g_mutex_unlock (xvimagesink->flow_lock);
314 meta = gst_buffer_get_meta_xvimage (xvimage);
316 if (xvimagesink->keep_aspect) {
317 GstVideoRectangle src, dst;
319 /* We use the calculated geometry from _setcaps as a source to respect
320 source and screen pixel aspect ratios. */
321 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
322 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
323 dst.w = xvimagesink->render_rect.w;
324 dst.h = xvimagesink->render_rect.h;
326 gst_video_sink_center_rect (src, dst, &result, TRUE);
327 result.x += xvimagesink->render_rect.x;
328 result.y += xvimagesink->render_rect.y;
330 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
333 g_mutex_lock (xvimagesink->x_lock);
335 if (draw_border && xvimagesink->draw_borders) {
336 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
338 xvimagesink->redraw_border = FALSE;
341 if (xvimagesink->xcontext->use_xshm) {
342 GST_LOG_OBJECT (xvimagesink,
343 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
344 GST_PTR_FORMAT, meta->width, meta->height,
345 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
347 XvShmPutImage (xvimagesink->xcontext->disp,
348 xvimagesink->xcontext->xv_port_id,
349 xvimagesink->xwindow->win,
350 xvimagesink->xwindow->gc, meta->xvimage,
351 xvimagesink->disp_x, xvimagesink->disp_y,
352 xvimagesink->disp_width, xvimagesink->disp_height,
353 result.x, result.y, result.w, result.h, FALSE);
355 #endif /* HAVE_XSHM */
357 XvPutImage (xvimagesink->xcontext->disp,
358 xvimagesink->xcontext->xv_port_id,
359 xvimagesink->xwindow->win,
360 xvimagesink->xwindow->gc, meta->xvimage,
361 xvimagesink->disp_x, xvimagesink->disp_y,
362 xvimagesink->disp_width, xvimagesink->disp_height,
363 result.x, result.y, result.w, result.h);
366 XSync (xvimagesink->xcontext->disp, FALSE);
368 g_mutex_unlock (xvimagesink->x_lock);
370 g_mutex_unlock (xvimagesink->flow_lock);
376 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
379 Atom hints_atom = None;
382 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
383 g_return_val_if_fail (window != NULL, FALSE);
385 g_mutex_lock (xvimagesink->x_lock);
387 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
389 if (hints_atom == None) {
390 g_mutex_unlock (xvimagesink->x_lock);
394 hints = g_malloc0 (sizeof (MotifWmHints));
396 hints->flags |= MWM_HINTS_DECORATIONS;
397 hints->decorations = 1 << 0;
399 XChangeProperty (xvimagesink->xcontext->disp, window->win,
400 hints_atom, hints_atom, 32, PropModeReplace,
401 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
403 XSync (xvimagesink->xcontext->disp, FALSE);
405 g_mutex_unlock (xvimagesink->x_lock);
413 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
414 GstXWindow * xwindow, const gchar * media_title)
417 g_free (xvimagesink->media_title);
418 xvimagesink->media_title = g_strdup (media_title);
421 /* we have a window */
422 if (xwindow->internal) {
423 XTextProperty xproperty;
424 const gchar *app_name;
425 const gchar *title = NULL;
426 gchar *title_mem = NULL;
428 /* set application name as a title */
429 app_name = g_get_application_name ();
431 if (app_name && xvimagesink->media_title) {
432 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
434 } else if (app_name) {
436 } else if (xvimagesink->media_title) {
437 title = xvimagesink->media_title;
441 if ((XStringListToTextProperty (((char **) &title), 1,
443 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
444 XFree (xproperty.value);
453 /* This function handles a GstXWindow creation
454 * The width and height are the actual pixel size on the display */
456 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
457 gint width, gint height)
459 GstXWindow *xwindow = NULL;
462 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
464 xwindow = g_new0 (GstXWindow, 1);
466 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
467 xvimagesink->render_rect.w = width;
468 xvimagesink->render_rect.h = height;
470 xwindow->width = width;
471 xwindow->height = height;
472 xwindow->internal = TRUE;
474 g_mutex_lock (xvimagesink->x_lock);
476 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
477 xvimagesink->xcontext->root,
478 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
480 /* We have to do that to prevent X from redrawing the background on
481 * ConfigureNotify. This takes away flickering of video when resizing. */
482 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
484 /* set application name as a title */
485 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
487 if (xvimagesink->handle_events) {
490 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
491 StructureNotifyMask | PointerMotionMask | KeyPressMask |
492 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
494 /* Tell the window manager we'd like delete client messages instead of
496 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
497 "WM_DELETE_WINDOW", True);
498 if (wm_delete != None) {
499 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
504 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
505 xwindow->win, 0, &values);
507 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
509 XSync (xvimagesink->xcontext->disp, FALSE);
511 g_mutex_unlock (xvimagesink->x_lock);
513 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
515 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
520 /* This function destroys a GstXWindow */
522 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
523 GstXWindow * xwindow)
525 g_return_if_fail (xwindow != NULL);
526 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
528 g_mutex_lock (xvimagesink->x_lock);
530 /* If we did not create that window we just free the GC and let it live */
531 if (xwindow->internal)
532 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
534 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
536 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
538 XSync (xvimagesink->xcontext->disp, FALSE);
540 g_mutex_unlock (xvimagesink->x_lock);
546 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
548 XWindowAttributes attr;
550 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
552 /* Update the window geometry */
553 g_mutex_lock (xvimagesink->x_lock);
554 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
555 g_mutex_unlock (xvimagesink->x_lock);
559 XGetWindowAttributes (xvimagesink->xcontext->disp,
560 xvimagesink->xwindow->win, &attr);
562 xvimagesink->xwindow->width = attr.width;
563 xvimagesink->xwindow->height = attr.height;
565 if (!xvimagesink->have_render_rect) {
566 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
567 xvimagesink->render_rect.w = attr.width;
568 xvimagesink->render_rect.h = attr.height;
571 g_mutex_unlock (xvimagesink->x_lock);
575 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
576 GstXWindow * xwindow)
578 g_return_if_fail (xwindow != NULL);
579 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
581 g_mutex_lock (xvimagesink->x_lock);
583 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
586 XSync (xvimagesink->xcontext->disp, FALSE);
588 g_mutex_unlock (xvimagesink->x_lock);
591 /* This function commits our internal colorbalance settings to our grabbed Xv
592 port. If the xcontext is not initialized yet it simply returns */
594 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
596 GList *channels = NULL;
598 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
600 /* If we haven't initialized the X context we can't update anything */
601 if (xvimagesink->xcontext == NULL)
604 /* Don't set the attributes if they haven't been changed, to avoid
605 * rounding errors changing the values */
606 if (!xvimagesink->cb_changed)
609 /* For each channel of the colorbalance we calculate the correct value
610 doing range conversion and then set the Xv port attribute to match our
612 channels = xvimagesink->xcontext->channels_list;
615 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
616 GstColorBalanceChannel *channel = NULL;
619 gdouble convert_coef;
621 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
622 g_object_ref (channel);
624 /* Our range conversion coef */
625 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
627 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
628 value = xvimagesink->hue;
629 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
630 value = xvimagesink->saturation;
631 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
632 value = xvimagesink->contrast;
633 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
634 value = xvimagesink->brightness;
636 g_warning ("got an unknown channel %s", channel->label);
637 g_object_unref (channel);
641 /* Committing to Xv port */
642 g_mutex_lock (xvimagesink->x_lock);
644 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
645 if (prop_atom != None) {
648 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
649 XvSetPortAttribute (xvimagesink->xcontext->disp,
650 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
652 g_mutex_unlock (xvimagesink->x_lock);
654 g_object_unref (channel);
656 channels = g_list_next (channels);
660 /* This function handles XEvents that might be in the queue. It generates
661 GstEvent that will be sent upstream in the pipeline to handle interactivity
662 and navigation. It will also listen for configure events on the window to
663 trigger caps renegotiation so on the fly software scaling can work. */
665 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
668 guint pointer_x = 0, pointer_y = 0;
669 gboolean pointer_moved = FALSE;
670 gboolean exposed = FALSE, configured = FALSE;
672 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
674 /* Handle Interaction, produces navigation events */
676 /* We get all pointer motion events, only the last position is
678 g_mutex_lock (xvimagesink->flow_lock);
679 g_mutex_lock (xvimagesink->x_lock);
680 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
681 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
682 g_mutex_unlock (xvimagesink->x_lock);
683 g_mutex_unlock (xvimagesink->flow_lock);
687 pointer_x = e.xmotion.x;
688 pointer_y = e.xmotion.y;
689 pointer_moved = TRUE;
694 g_mutex_lock (xvimagesink->flow_lock);
695 g_mutex_lock (xvimagesink->x_lock);
699 g_mutex_unlock (xvimagesink->x_lock);
700 g_mutex_unlock (xvimagesink->flow_lock);
702 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
703 pointer_x, pointer_y);
704 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
705 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
707 g_mutex_lock (xvimagesink->flow_lock);
708 g_mutex_lock (xvimagesink->x_lock);
711 /* We get all events on our window to throw them upstream */
712 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
713 xvimagesink->xwindow->win,
714 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
718 /* We lock only for the X function call */
719 g_mutex_unlock (xvimagesink->x_lock);
720 g_mutex_unlock (xvimagesink->flow_lock);
724 /* Mouse button pressed over our window. We send upstream
725 events for interactivity/navigation */
726 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
727 e.xbutton.button, e.xbutton.x, e.xbutton.y);
728 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
729 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
732 /* Mouse button released over our window. We send upstream
733 events for interactivity/navigation */
734 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
735 e.xbutton.button, e.xbutton.x, e.xbutton.y);
736 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
737 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
741 /* Key pressed/released over our window. We send upstream
742 events for interactivity/navigation */
743 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
744 e.xkey.keycode, e.xkey.x, e.xkey.y);
745 g_mutex_lock (xvimagesink->x_lock);
746 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
748 g_mutex_unlock (xvimagesink->x_lock);
749 if (keysym != NoSymbol) {
750 char *key_str = NULL;
752 g_mutex_lock (xvimagesink->x_lock);
753 key_str = XKeysymToString (keysym);
754 g_mutex_unlock (xvimagesink->x_lock);
755 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
756 e.type == KeyPress ? "key-press" : "key-release", key_str);
758 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
759 e.type == KeyPress ? "key-press" : "key-release", "unknown");
763 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
766 g_mutex_lock (xvimagesink->flow_lock);
767 g_mutex_lock (xvimagesink->x_lock);
771 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
772 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
777 case ConfigureNotify:
778 g_mutex_unlock (xvimagesink->x_lock);
779 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
780 g_mutex_lock (xvimagesink->x_lock);
788 if (xvimagesink->handle_expose && (exposed || configured)) {
789 g_mutex_unlock (xvimagesink->x_lock);
790 g_mutex_unlock (xvimagesink->flow_lock);
792 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
794 g_mutex_lock (xvimagesink->flow_lock);
795 g_mutex_lock (xvimagesink->x_lock);
798 /* Handle Display events */
799 while (XPending (xvimagesink->xcontext->disp)) {
800 XNextEvent (xvimagesink->xcontext->disp, &e);
806 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
807 "WM_DELETE_WINDOW", True);
808 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
809 /* Handle window deletion by posting an error on the bus */
810 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
811 ("Output window was closed"), (NULL));
813 g_mutex_unlock (xvimagesink->x_lock);
814 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
815 xvimagesink->xwindow = NULL;
816 g_mutex_lock (xvimagesink->x_lock);
825 g_mutex_unlock (xvimagesink->x_lock);
826 g_mutex_unlock (xvimagesink->flow_lock);
830 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
831 XvAdaptorInfo * adaptors, int adaptor_no)
836 /* Do we support XvImageMask ? */
837 if (!(adaptors[adaptor_no].type & XvImageMask)) {
838 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
839 adaptors[adaptor_no].name);
843 /* We found such an adaptor, looking for an available port */
844 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
845 /* We try to grab the port */
846 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
847 if (Success == res) {
848 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
849 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
850 adaptors[adaptor_no].num_ports);
852 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
853 adaptors[adaptor_no].name, res);
858 /* This function generates a caps with all supported format by the first
859 Xv grabable port we find. We store each one of the supported formats in a
860 format list and append the format to a newly created caps that we return
861 If this function does not return NULL because of an error, it also grabs
862 the port via XvGrabPort */
864 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
865 GstXContext * xcontext)
868 XvAdaptorInfo *adaptors;
870 XvImageFormatValues *formats = NULL;
872 XvEncodingInfo *encodings = NULL;
873 gulong max_w = G_MAXINT, max_h = G_MAXINT;
874 GstCaps *caps = NULL;
875 GstCaps *rgb_caps = NULL;
877 g_return_val_if_fail (xcontext != NULL, NULL);
879 /* First let's check that XVideo extension is available */
880 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
881 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
882 ("Could not initialise Xv output"),
883 ("XVideo extension is not available"));
887 /* Then we get adaptors list */
888 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
889 &xcontext->nb_adaptors, &adaptors)) {
890 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
891 ("Could not initialise Xv output"),
892 ("Failed getting XV adaptors list"));
896 xcontext->xv_port_id = 0;
898 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
901 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
903 /* Now fill up our adaptor name array */
904 for (i = 0; i < xcontext->nb_adaptors; i++) {
905 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
908 if (xvimagesink->adaptor_no >= 0 &&
909 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
910 /* Find xv port from user defined adaptor */
911 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
912 xvimagesink->adaptor_no);
915 if (!xcontext->xv_port_id) {
916 /* Now search for an adaptor that supports XvImageMask */
917 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
918 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
919 xvimagesink->adaptor_no = i;
923 XvFreeAdaptorInfo (adaptors);
925 if (!xcontext->xv_port_id) {
926 xvimagesink->adaptor_no = -1;
927 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
928 ("Could not initialise Xv output"), ("No port available"));
932 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
935 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
936 xcontext->xv_port_id, &count);
937 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
938 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
939 static const char colorkey[] = "XV_COLORKEY";
941 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
943 xvimagesink->have_autopaint_colorkey = FALSE;
944 xvimagesink->have_double_buffer = FALSE;
945 xvimagesink->have_colorkey = FALSE;
947 for (i = 0; ((i < count) && todo); i++)
948 if (!strcmp (attr[i].name, autopaint)) {
949 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
951 /* turn on autopaint colorkey */
952 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
953 (xvimagesink->autopaint_colorkey ? 1 : 0));
955 xvimagesink->have_autopaint_colorkey = TRUE;
956 } else if (!strcmp (attr[i].name, dbl_buffer)) {
957 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
959 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
960 (xvimagesink->double_buffer ? 1 : 0));
962 xvimagesink->have_double_buffer = TRUE;
963 } else if (!strcmp (attr[i].name, colorkey)) {
964 /* Set the colorkey, default is something that is dark but hopefully
965 * won't randomly appear on the screen elsewhere (ie not black or greys)
966 * can be overridden by setting "colorkey" property
968 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
970 gboolean set_attr = TRUE;
973 /* set a colorkey in the right format RGB565/RGB888
974 * We only handle these 2 cases, because they're the only types of
975 * devices we've encountered. If we don't recognise it, leave it alone
977 cr = (xvimagesink->colorkey >> 16);
978 cg = (xvimagesink->colorkey >> 8) & 0xFF;
979 cb = (xvimagesink->colorkey) & 0xFF;
980 switch (xcontext->depth) {
981 case 16: /* RGB 565 */
985 ckey = (cr << 11) | (cg << 5) | cb;
988 case 32: /* RGB 888 / ARGB 8888 */
989 ckey = (cr << 16) | (cg << 8) | cb;
992 GST_DEBUG_OBJECT (xvimagesink,
993 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1000 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1001 (guint32) attr[i].max_value);
1002 GST_LOG_OBJECT (xvimagesink,
1003 "Setting color key for display depth %d to 0x%x",
1004 xcontext->depth, ckey);
1006 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1010 xvimagesink->have_colorkey = TRUE;
1016 /* Get the list of encodings supported by the adapter and look for the
1017 * XV_IMAGE encoding so we can determine the maximum width and height
1019 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1022 for (i = 0; i < nb_encodings; i++) {
1023 GST_LOG_OBJECT (xvimagesink,
1024 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1025 i, encodings[i].name, encodings[i].width, encodings[i].height,
1026 encodings[i].rate.numerator, encodings[i].rate.denominator);
1027 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1028 max_w = encodings[i].width;
1029 max_h = encodings[i].height;
1033 XvFreeEncodingInfo (encodings);
1035 /* We get all image formats supported by our port */
1036 formats = XvListImageFormats (xcontext->disp,
1037 xcontext->xv_port_id, &nb_formats);
1038 caps = gst_caps_new_empty ();
1039 for (i = 0; i < nb_formats; i++) {
1040 GstCaps *format_caps = NULL;
1041 gboolean is_rgb_format = FALSE;
1043 /* We set the image format of the xcontext to an existing one. This
1044 is just some valid image format for making our xshm calls check before
1045 caps negotiation really happens. */
1046 xcontext->im_format = formats[i].id;
1048 switch (formats[i].type) {
1051 XvImageFormatValues *fmt = &(formats[i]);
1053 GstVideoFormat vformat;
1056 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1058 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1059 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1061 format_caps = gst_caps_new_simple ("video/x-raw",
1062 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1063 "width", GST_TYPE_INT_RANGE, 1, max_w,
1064 "height", GST_TYPE_INT_RANGE, 1, max_h,
1065 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1067 is_rgb_format = TRUE;
1072 GstVideoFormat vformat;
1074 vformat = gst_video_format_from_fourcc (formats[i].id);
1076 format_caps = gst_caps_new_simple ("video/x-raw",
1077 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1078 "width", GST_TYPE_INT_RANGE, 1, max_w,
1079 "height", GST_TYPE_INT_RANGE, 1, max_h,
1080 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1084 g_assert_not_reached ();
1089 GstXvImageFormat *format = NULL;
1091 format = g_new0 (GstXvImageFormat, 1);
1093 format->format = formats[i].id;
1094 format->caps = gst_caps_copy (format_caps);
1095 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1098 if (is_rgb_format) {
1099 if (rgb_caps == NULL)
1100 rgb_caps = format_caps;
1102 gst_caps_append (rgb_caps, format_caps);
1104 gst_caps_append (caps, format_caps);
1108 /* Collected all caps into either the caps or rgb_caps structures.
1109 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1111 gst_caps_append (caps, rgb_caps);
1116 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1118 if (gst_caps_is_empty (caps)) {
1119 gst_caps_unref (caps);
1120 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1121 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1122 ("No supported format found"));
1130 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1132 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1134 GST_OBJECT_LOCK (xvimagesink);
1135 while (xvimagesink->running) {
1136 GST_OBJECT_UNLOCK (xvimagesink);
1138 if (xvimagesink->xwindow) {
1139 gst_xvimagesink_handle_xevents (xvimagesink);
1141 /* FIXME: do we want to align this with the framerate or anything else? */
1142 g_usleep (G_USEC_PER_SEC / 20);
1144 GST_OBJECT_LOCK (xvimagesink);
1146 GST_OBJECT_UNLOCK (xvimagesink);
1152 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1154 GThread *thread = NULL;
1156 /* don't start the thread too early */
1157 if (xvimagesink->xcontext == NULL) {
1161 GST_OBJECT_LOCK (xvimagesink);
1162 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1163 if (!xvimagesink->event_thread) {
1164 /* Setup our event listening thread */
1165 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1166 xvimagesink->handle_expose, xvimagesink->handle_events);
1167 xvimagesink->running = TRUE;
1168 xvimagesink->event_thread = g_thread_create (
1169 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1172 if (xvimagesink->event_thread) {
1173 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1174 xvimagesink->handle_expose, xvimagesink->handle_events);
1175 xvimagesink->running = FALSE;
1176 /* grab thread and mark it as NULL */
1177 thread = xvimagesink->event_thread;
1178 xvimagesink->event_thread = NULL;
1181 GST_OBJECT_UNLOCK (xvimagesink);
1183 /* Wait for our event thread to finish */
1185 g_thread_join (thread);
1190 /* This function calculates the pixel aspect ratio based on the properties
1191 * in the xcontext structure and stores it there. */
1193 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1195 static const gint par[][2] = {
1196 {1, 1}, /* regular screen */
1197 {16, 15}, /* PAL TV */
1198 {11, 10}, /* 525 line Rec.601 video */
1199 {54, 59}, /* 625 line Rec.601 video */
1200 {64, 45}, /* 1280x1024 on 16:9 display */
1201 {5, 3}, /* 1280x1024 on 4:3 display */
1202 {4, 3} /* 800x600 on 16:9 display */
1209 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1211 /* first calculate the "real" ratio based on the X values;
1212 * which is the "physical" w/h divided by the w/h in pixels of the display */
1213 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1214 / (xcontext->heightmm * xcontext->width);
1216 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1218 if (xcontext->width == 720 && xcontext->height == 576) {
1219 ratio = 4.0 * 576 / (3.0 * 720);
1221 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1223 /* now find the one from par[][2] with the lowest delta to the real one */
1227 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1228 gdouble this_delta = DELTA (i);
1230 if (this_delta < delta) {
1236 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1237 par[index][0], par[index][1]);
1239 g_free (xcontext->par);
1240 xcontext->par = g_new0 (GValue, 1);
1241 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1242 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1243 GST_DEBUG ("set xcontext PAR to %d/%d",
1244 gst_value_get_fraction_numerator (xcontext->par),
1245 gst_value_get_fraction_denominator (xcontext->par));
1248 /* This function gets the X Display and global info about it. Everything is
1249 stored in our object and will be cleaned when the object is disposed. Note
1250 here that caps for supported format are generated without any window or
1252 static GstXContext *
1253 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1255 GstXContext *xcontext = NULL;
1256 XPixmapFormatValues *px_formats = NULL;
1257 gint nb_formats = 0, i, j, N_attr;
1258 XvAttribute *xv_attr;
1260 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1261 "XV_BRIGHTNESS", "XV_CONTRAST"
1264 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1266 xcontext = g_new0 (GstXContext, 1);
1267 xcontext->im_format = 0;
1269 g_mutex_lock (xvimagesink->x_lock);
1271 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1273 if (!xcontext->disp) {
1274 g_mutex_unlock (xvimagesink->x_lock);
1276 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1277 ("Could not initialise Xv output"), ("Could not open display"));
1281 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1282 xcontext->screen_num = DefaultScreen (xcontext->disp);
1283 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1284 xcontext->root = DefaultRootWindow (xcontext->disp);
1285 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1286 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1287 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1289 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1290 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1291 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1292 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1294 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1295 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1297 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1298 /* We get supported pixmap formats at supported depth */
1299 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1302 XCloseDisplay (xcontext->disp);
1303 g_mutex_unlock (xvimagesink->x_lock);
1304 g_free (xcontext->par);
1306 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1307 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1311 /* We get bpp value corresponding to our running depth */
1312 for (i = 0; i < nb_formats; i++) {
1313 if (px_formats[i].depth == xcontext->depth)
1314 xcontext->bpp = px_formats[i].bits_per_pixel;
1319 xcontext->endianness =
1320 (ImageByteOrder (xcontext->disp) ==
1321 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1323 /* our caps system handles 24/32bpp RGB as big-endian. */
1324 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1325 xcontext->endianness == G_LITTLE_ENDIAN) {
1326 xcontext->endianness = G_BIG_ENDIAN;
1327 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1328 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1329 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1330 if (xcontext->bpp == 24) {
1331 xcontext->visual->red_mask >>= 8;
1332 xcontext->visual->green_mask >>= 8;
1333 xcontext->visual->blue_mask >>= 8;
1337 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1339 /* Search for XShm extension support */
1341 if (XShmQueryExtension (xcontext->disp) &&
1342 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1343 xcontext->use_xshm = TRUE;
1344 GST_DEBUG ("xvimagesink is using XShm extension");
1346 #endif /* HAVE_XSHM */
1348 xcontext->use_xshm = FALSE;
1349 GST_DEBUG ("xvimagesink is not using XShm extension");
1352 if (!xcontext->caps) {
1353 XCloseDisplay (xcontext->disp);
1354 g_mutex_unlock (xvimagesink->x_lock);
1355 g_free (xcontext->par);
1357 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1361 xv_attr = XvQueryPortAttributes (xcontext->disp,
1362 xcontext->xv_port_id, &N_attr);
1365 /* Generate the channels list */
1366 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1367 XvAttribute *matching_attr = NULL;
1369 /* Retrieve the property atom if it exists. If it doesn't exist,
1370 * the attribute itself must not either, so we can skip */
1371 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1372 if (prop_atom == None)
1375 if (xv_attr != NULL) {
1376 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1377 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1378 matching_attr = xv_attr + j;
1381 if (matching_attr) {
1382 GstColorBalanceChannel *channel;
1384 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1385 channel->label = g_strdup (channels[i]);
1386 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1387 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1389 xcontext->channels_list = g_list_append (xcontext->channels_list,
1392 /* If the colorbalance settings have not been touched we get Xv values
1393 as defaults and update our internal variables */
1394 if (!xvimagesink->cb_changed) {
1397 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1399 /* Normalize val to [-1000, 1000] */
1400 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1401 (double) (channel->max_value - channel->min_value));
1403 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1404 xvimagesink->hue = val;
1405 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1406 xvimagesink->saturation = val;
1407 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1408 xvimagesink->brightness = val;
1409 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1410 xvimagesink->contrast = val;
1418 g_mutex_unlock (xvimagesink->x_lock);
1423 /* This function cleans the X context. Closing the Display, releasing the XV
1424 port and unrefing the caps for supported formats. */
1426 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1428 GList *formats_list, *channels_list;
1429 GstXContext *xcontext;
1432 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1434 GST_OBJECT_LOCK (xvimagesink);
1435 if (xvimagesink->xcontext == NULL) {
1436 GST_OBJECT_UNLOCK (xvimagesink);
1440 /* Take the XContext from the sink and clean it up */
1441 xcontext = xvimagesink->xcontext;
1442 xvimagesink->xcontext = NULL;
1444 GST_OBJECT_UNLOCK (xvimagesink);
1447 formats_list = xcontext->formats_list;
1449 while (formats_list) {
1450 GstXvImageFormat *format = formats_list->data;
1452 gst_caps_unref (format->caps);
1454 formats_list = g_list_next (formats_list);
1457 if (xcontext->formats_list)
1458 g_list_free (xcontext->formats_list);
1460 channels_list = xcontext->channels_list;
1462 while (channels_list) {
1463 GstColorBalanceChannel *channel = channels_list->data;
1465 g_object_unref (channel);
1466 channels_list = g_list_next (channels_list);
1469 if (xcontext->channels_list)
1470 g_list_free (xcontext->channels_list);
1472 gst_caps_unref (xcontext->caps);
1473 if (xcontext->last_caps)
1474 gst_caps_replace (&xcontext->last_caps, NULL);
1476 for (i = 0; i < xcontext->nb_adaptors; i++) {
1477 g_free (xcontext->adaptors[i]);
1480 g_free (xcontext->adaptors);
1482 g_free (xcontext->par);
1484 g_mutex_lock (xvimagesink->x_lock);
1486 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1488 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1490 XCloseDisplay (xcontext->disp);
1492 g_mutex_unlock (xvimagesink->x_lock);
1500 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1502 GstXvImageSink *xvimagesink;
1505 xvimagesink = GST_XVIMAGESINK (bsink);
1507 if (xvimagesink->xcontext) {
1509 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1510 GST_CAPS_INTERSECT_FIRST);
1512 return gst_caps_ref (xvimagesink->xcontext->caps);
1515 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1517 GstCaps *intersection;
1520 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1521 gst_caps_unref (caps);
1522 caps = intersection;
1528 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1530 GstXvImageSink *xvimagesink;
1531 GstStructure *structure;
1532 GstBufferPool *newpool, *oldpool;
1534 guint32 im_format = 0;
1535 gint video_width, video_height;
1536 gint disp_x, disp_y;
1537 gint disp_width, disp_height;
1538 gint video_par_n, video_par_d; /* video's PAR */
1539 gint display_par_n, display_par_d; /* display's PAR */
1540 const GValue *caps_par;
1541 const GValue *caps_disp_reg;
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 structure = gst_caps_get_structure (caps, 0);
1556 ret = gst_structure_get_int (structure, "width", &video_width);
1557 ret &= gst_structure_get_int (structure, "height", &video_height);
1558 fps = gst_structure_get_value (structure, "framerate");
1559 ret &= (fps != NULL);
1562 goto incomplete_caps;
1564 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
1565 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
1567 xvimagesink->video_width = video_width;
1568 xvimagesink->video_height = video_height;
1570 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
1571 if (im_format == -1)
1572 goto invalid_format;
1574 if (!gst_video_get_size_from_caps (caps, &size))
1575 goto invalid_format;
1577 /* get aspect ratio from caps if it's present, and
1578 * convert video width and height to a display width and height
1579 * using wd / hd = wv / hv * PARv / PARd */
1581 /* get video's PAR */
1582 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1584 video_par_n = gst_value_get_fraction_numerator (caps_par);
1585 video_par_d = gst_value_get_fraction_denominator (caps_par);
1590 /* get display's PAR */
1591 if (xvimagesink->par) {
1592 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1593 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1599 /* get the display region */
1600 caps_disp_reg = gst_structure_get_value (structure, "display-region");
1601 if (caps_disp_reg) {
1602 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
1603 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
1604 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
1606 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
1608 disp_x = disp_y = 0;
1609 disp_width = video_width;
1610 disp_height = video_height;
1613 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
1614 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
1617 xvimagesink->disp_x = disp_x;
1618 xvimagesink->disp_y = disp_y;
1619 xvimagesink->disp_width = disp_width;
1620 xvimagesink->disp_height = disp_height;
1622 GST_DEBUG_OBJECT (xvimagesink,
1623 "video width/height: %dx%d, calculated display ratio: %d/%d",
1624 video_width, video_height, num, den);
1626 /* now find a width x height that respects this display ratio.
1627 * prefer those that have one of w/h the same as the incoming video
1628 * using wd / hd = num / den */
1630 /* start with same height, because of interlaced video */
1631 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1632 if (video_height % den == 0) {
1633 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1634 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1635 gst_util_uint64_scale_int (video_height, num, den);
1636 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
1637 } else if (video_width % num == 0) {
1638 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1639 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
1640 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1641 gst_util_uint64_scale_int (video_width, den, num);
1643 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1644 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1645 gst_util_uint64_scale_int (video_height, num, den);
1646 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
1648 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1649 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1651 /* Notify application to set xwindow id now */
1652 g_mutex_lock (xvimagesink->flow_lock);
1653 if (!xvimagesink->xwindow) {
1654 g_mutex_unlock (xvimagesink->flow_lock);
1655 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
1657 g_mutex_unlock (xvimagesink->flow_lock);
1660 /* Creating our window and our image with the display size in pixels */
1661 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1662 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1663 goto no_display_size;
1665 g_mutex_lock (xvimagesink->flow_lock);
1666 if (!xvimagesink->xwindow) {
1667 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1668 GST_VIDEO_SINK_WIDTH (xvimagesink),
1669 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1672 /* After a resize, we want to redraw the borders in case the new frame size
1673 * doesn't cover the same area */
1674 xvimagesink->redraw_border = TRUE;
1676 /* create a new pool for the new configuration */
1677 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1679 structure = gst_buffer_pool_get_config (newpool);
1680 gst_buffer_pool_config_set (structure, caps, size, 0, 0, 0, 15);
1681 if (!gst_buffer_pool_set_config (newpool, structure))
1684 if (!gst_buffer_pool_set_active (newpool, TRUE))
1685 goto activate_failed;
1687 oldpool = xvimagesink->pool;
1688 xvimagesink->pool = newpool;
1689 g_mutex_unlock (xvimagesink->flow_lock);
1691 /* unref the old sink */
1694 gst_buffer_pool_set_active (oldpool, FALSE);
1695 gst_object_unref (oldpool);
1703 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1708 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
1709 "height or framerate from intersected caps");
1714 GST_DEBUG_OBJECT (xvimagesink,
1715 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1720 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1721 ("Error calculating the output display ratio of the video."));
1726 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1727 ("Error calculating the output display ratio of the video."));
1732 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1733 g_mutex_unlock (xvimagesink->flow_lock);
1738 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1739 g_mutex_unlock (xvimagesink->flow_lock);
1740 gst_object_unref (newpool);
1745 static GstStateChangeReturn
1746 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1748 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1749 GstXvImageSink *xvimagesink;
1750 GstXContext *xcontext = NULL;
1752 xvimagesink = GST_XVIMAGESINK (element);
1754 switch (transition) {
1755 case GST_STATE_CHANGE_NULL_TO_READY:
1756 /* Initializing the XContext */
1757 if (xvimagesink->xcontext == NULL) {
1758 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1759 if (xcontext == NULL) {
1760 ret = GST_STATE_CHANGE_FAILURE;
1763 GST_OBJECT_LOCK (xvimagesink);
1765 xvimagesink->xcontext = xcontext;
1766 GST_OBJECT_UNLOCK (xvimagesink);
1769 /* update object's par with calculated one if not set yet */
1770 if (!xvimagesink->par) {
1771 xvimagesink->par = g_new0 (GValue, 1);
1772 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1773 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1775 /* call XSynchronize with the current value of synchronous */
1776 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1777 xvimagesink->synchronous ? "TRUE" : "FALSE");
1778 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1779 gst_xvimagesink_update_colorbalance (xvimagesink);
1780 gst_xvimagesink_manage_event_thread (xvimagesink);
1782 case GST_STATE_CHANGE_READY_TO_PAUSED:
1784 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1786 case GST_STATE_CHANGE_PAUSED_TO_READY:
1792 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1794 switch (transition) {
1795 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1797 case GST_STATE_CHANGE_PAUSED_TO_READY:
1798 xvimagesink->fps_n = 0;
1799 xvimagesink->fps_d = 1;
1800 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1801 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1802 g_mutex_lock (xvimagesink->flow_lock);
1803 if (xvimagesink->pool)
1804 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1805 g_mutex_unlock (xvimagesink->flow_lock);
1807 case GST_STATE_CHANGE_READY_TO_NULL:
1808 gst_xvimagesink_reset (xvimagesink);
1819 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1820 GstClockTime * start, GstClockTime * end)
1822 GstXvImageSink *xvimagesink;
1824 xvimagesink = GST_XVIMAGESINK (bsink);
1826 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1827 *start = GST_BUFFER_TIMESTAMP (buf);
1828 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1829 *end = *start + GST_BUFFER_DURATION (buf);
1831 if (xvimagesink->fps_n > 0) {
1833 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1834 xvimagesink->fps_n);
1840 static GstFlowReturn
1841 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1844 GstXvImageSink *xvimagesink;
1845 GstMetaXvImage *meta;
1848 xvimagesink = GST_XVIMAGESINK (vsink);
1850 meta = gst_buffer_get_meta_xvimage (buf);
1853 /* If this buffer has been allocated using our buffer management we simply
1854 put the ximage which is in the PRIVATE pointer */
1855 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1863 /* Else we have to copy the data into our private image, */
1864 /* if we have one... */
1865 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1867 /* we should have a pool, configured in setcaps */
1868 if (xvimagesink->pool == NULL)
1871 /* take a buffer form our pool */
1872 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1873 if (res != GST_FLOW_OK)
1876 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1879 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1880 "slow copy into bufferpool buffer %p", to_put);
1882 data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1883 gst_buffer_fill (to_put, 0, data, size);
1884 gst_buffer_unmap (buf, data, size);
1887 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1892 gst_buffer_unref (to_put);
1899 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1900 ("Internal error: can't allocate images"),
1901 ("We don't have a bufferpool negotiated"));
1902 return GST_FLOW_ERROR;
1906 /* No image available. That's very bad ! */
1907 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1912 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1913 ("Failed to create output image buffer"),
1914 ("XServer allocated buffer size did not match input buffer %"
1915 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1916 gst_buffer_get_size (buf)));
1917 res = GST_FLOW_ERROR;
1922 /* No Window available to put our image into */
1923 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1924 res = GST_FLOW_ERROR;
1930 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1932 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1934 switch (GST_EVENT_TYPE (event)) {
1935 case GST_EVENT_TAG:{
1937 gchar *title = NULL;
1939 gst_event_parse_tag (event, &l);
1940 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1943 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1944 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1954 if (GST_BASE_SINK_CLASS (parent_class)->event)
1955 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1961 gst_xvimagesink_sink_query (GstPad * sinkpad, GstQuery * query)
1963 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (GST_PAD_PARENT (sinkpad));
1964 gboolean res = TRUE;
1966 switch (GST_QUERY_TYPE (query)) {
1967 case GST_QUERY_ALLOCATION:
1969 GstBufferPool *pool;
1970 GstStructure *config;
1975 gst_query_parse_allocation (query, &caps, &need_pool);
1980 g_mutex_lock (xvimagesink->flow_lock);
1981 if ((pool = xvimagesink->pool))
1982 gst_object_ref (pool);
1983 g_mutex_unlock (xvimagesink->flow_lock);
1986 const GstCaps *pcaps;
1988 /* we had a pool, check caps */
1989 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1990 config = gst_buffer_pool_get_config (pool);
1991 gst_buffer_pool_config_get (config, &pcaps, &size, NULL, NULL, NULL,
1994 if (!gst_caps_is_equal (caps, pcaps)) {
1995 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1996 /* different caps, we can't use this pool */
1997 gst_object_unref (pool);
2001 if (pool == NULL && need_pool) {
2002 GstVideoFormat format;
2005 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
2006 pool = gst_xvimage_buffer_pool_new (xvimagesink);
2008 if (!gst_video_format_parse_caps (caps, &format, &width, &height))
2011 /* the normal size of a frame */
2012 size = gst_video_format_get_size (format, width, height);
2014 config = gst_buffer_pool_get_config (pool);
2015 gst_buffer_pool_config_set (config, caps, size, 0, 0, 0, 15);
2016 if (!gst_buffer_pool_set_config (pool, config))
2019 gst_query_set_allocation_params (query, size, 0, 0, 0, 15, pool);
2021 /* we also support various metadata */
2022 gst_query_add_allocation_meta (query, GST_META_API_VIDEO);
2024 gst_object_unref (pool);
2036 GST_DEBUG_OBJECT (sinkpad, "no caps specified");
2041 GST_DEBUG_OBJECT (sinkpad, "invalid caps specified");
2046 GST_DEBUG_OBJECT (sinkpad, "failed setting config");
2051 /* Interfaces stuff */
2054 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2056 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2057 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE)
2064 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2066 klass->supported = gst_xvimagesink_interface_supported;
2070 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2071 GstStructure * structure)
2073 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2076 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2078 GstVideoRectangle src, dst, result;
2079 gdouble x, y, xscale = 1.0, yscale = 1.0;
2081 event = gst_event_new_navigation (structure);
2083 /* We take the flow_lock while we look at the window */
2084 g_mutex_lock (xvimagesink->flow_lock);
2086 if (!xvimagesink->xwindow) {
2087 g_mutex_unlock (xvimagesink->flow_lock);
2091 if (xvimagesink->keep_aspect) {
2092 /* We get the frame position using the calculated geometry from _setcaps
2093 that respect pixel aspect ratios */
2094 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2095 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2096 dst.w = xvimagesink->render_rect.w;
2097 dst.h = xvimagesink->render_rect.h;
2099 gst_video_sink_center_rect (src, dst, &result, TRUE);
2100 result.x += xvimagesink->render_rect.x;
2101 result.y += xvimagesink->render_rect.y;
2103 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2106 g_mutex_unlock (xvimagesink->flow_lock);
2108 /* We calculate scaling using the original video frames geometry to include
2109 pixel aspect ratio scaling. */
2110 xscale = (gdouble) xvimagesink->video_width / result.w;
2111 yscale = (gdouble) xvimagesink->video_height / result.h;
2113 /* Converting pointer coordinates to the non scaled geometry */
2114 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2115 x = MIN (x, result.x + result.w);
2116 x = MAX (x - result.x, 0);
2117 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2118 (gdouble) x * xscale, NULL);
2120 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2121 y = MIN (y, result.y + result.h);
2122 y = MAX (y - result.y, 0);
2123 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2124 (gdouble) y * yscale, NULL);
2127 gst_pad_send_event (peer, event);
2128 gst_object_unref (peer);
2133 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2135 iface->send_event = gst_xvimagesink_navigation_send_event;
2139 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2141 XID xwindow_id = id;
2142 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2143 GstXWindow *xwindow = NULL;
2145 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2147 g_mutex_lock (xvimagesink->flow_lock);
2149 /* If we already use that window return */
2150 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2151 g_mutex_unlock (xvimagesink->flow_lock);
2155 /* If the element has not initialized the X11 context try to do so */
2156 if (!xvimagesink->xcontext &&
2157 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2158 g_mutex_unlock (xvimagesink->flow_lock);
2159 /* we have thrown a GST_ELEMENT_ERROR now */
2163 gst_xvimagesink_update_colorbalance (xvimagesink);
2165 /* If a window is there already we destroy it */
2166 if (xvimagesink->xwindow) {
2167 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2168 xvimagesink->xwindow = NULL;
2171 /* If the xid is 0 we go back to an internal window */
2172 if (xwindow_id == 0) {
2173 /* If no width/height caps nego did not happen window will be created
2174 during caps nego then */
2175 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2176 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2178 gst_xvimagesink_xwindow_new (xvimagesink,
2179 GST_VIDEO_SINK_WIDTH (xvimagesink),
2180 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2183 XWindowAttributes attr;
2185 xwindow = g_new0 (GstXWindow, 1);
2186 xwindow->win = xwindow_id;
2188 /* Set the event we want to receive and create a GC */
2189 g_mutex_lock (xvimagesink->x_lock);
2191 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2193 xwindow->width = attr.width;
2194 xwindow->height = attr.height;
2195 xwindow->internal = FALSE;
2196 if (!xvimagesink->have_render_rect) {
2197 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2198 xvimagesink->render_rect.w = attr.width;
2199 xvimagesink->render_rect.h = attr.height;
2201 if (xvimagesink->handle_events) {
2202 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2203 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2207 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2208 xwindow->win, 0, NULL);
2209 g_mutex_unlock (xvimagesink->x_lock);
2213 xvimagesink->xwindow = xwindow;
2215 g_mutex_unlock (xvimagesink->flow_lock);
2219 gst_xvimagesink_expose (GstXOverlay * overlay)
2221 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2223 GST_DEBUG ("doing expose");
2224 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2225 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2229 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2230 gboolean handle_events)
2232 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2234 xvimagesink->handle_events = handle_events;
2236 g_mutex_lock (xvimagesink->flow_lock);
2238 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2239 g_mutex_unlock (xvimagesink->flow_lock);
2243 g_mutex_lock (xvimagesink->x_lock);
2245 if (handle_events) {
2246 if (xvimagesink->xwindow->internal) {
2247 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2248 ExposureMask | StructureNotifyMask | PointerMotionMask |
2249 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2251 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2252 ExposureMask | StructureNotifyMask | PointerMotionMask |
2253 KeyPressMask | KeyReleaseMask);
2256 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2259 g_mutex_unlock (xvimagesink->x_lock);
2261 g_mutex_unlock (xvimagesink->flow_lock);
2265 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2266 gint width, gint height)
2268 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2270 /* FIXME: how about some locking? */
2271 if (width >= 0 && height >= 0) {
2272 xvimagesink->render_rect.x = x;
2273 xvimagesink->render_rect.y = y;
2274 xvimagesink->render_rect.w = width;
2275 xvimagesink->render_rect.h = height;
2276 xvimagesink->have_render_rect = TRUE;
2278 xvimagesink->render_rect.x = 0;
2279 xvimagesink->render_rect.y = 0;
2280 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2281 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2282 xvimagesink->have_render_rect = FALSE;
2287 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2289 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2290 iface->expose = gst_xvimagesink_expose;
2291 iface->handle_events = gst_xvimagesink_set_event_handling;
2292 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2295 static const GList *
2296 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2298 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2300 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2302 if (xvimagesink->xcontext)
2303 return xvimagesink->xcontext->channels_list;
2309 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2310 GstColorBalanceChannel * channel, gint value)
2312 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2314 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2315 g_return_if_fail (channel->label != NULL);
2317 xvimagesink->cb_changed = TRUE;
2319 /* Normalize val to [-1000, 1000] */
2320 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2321 (double) (channel->max_value - channel->min_value));
2323 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2324 xvimagesink->hue = value;
2325 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2326 xvimagesink->saturation = value;
2327 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2328 xvimagesink->contrast = value;
2329 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2330 xvimagesink->brightness = value;
2332 g_warning ("got an unknown channel %s", channel->label);
2336 gst_xvimagesink_update_colorbalance (xvimagesink);
2340 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2341 GstColorBalanceChannel * channel)
2343 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2346 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2347 g_return_val_if_fail (channel->label != NULL, 0);
2349 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2350 value = xvimagesink->hue;
2351 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2352 value = xvimagesink->saturation;
2353 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2354 value = xvimagesink->contrast;
2355 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2356 value = xvimagesink->brightness;
2358 g_warning ("got an unknown channel %s", channel->label);
2361 /* Normalize val to [channel->min_value, channel->max_value] */
2362 value = channel->min_value + (channel->max_value - channel->min_value) *
2363 (value + 1000) / 2000;
2369 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
2371 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2372 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2373 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2374 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2377 static const GList *
2378 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2380 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2381 static GList *list = NULL;
2384 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2386 g_list_append (list, g_object_class_find_property (klass,
2387 "autopaint-colorkey"));
2389 g_list_append (list, g_object_class_find_property (klass,
2392 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2399 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2400 guint prop_id, const GParamSpec * pspec)
2402 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2406 case PROP_AUTOPAINT_COLORKEY:
2407 case PROP_DOUBLE_BUFFER:
2409 GST_DEBUG_OBJECT (xvimagesink,
2410 "probing device list and get capabilities");
2411 if (!xvimagesink->xcontext) {
2412 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2413 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2417 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2423 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2424 guint prop_id, const GParamSpec * pspec)
2426 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2427 gboolean ret = FALSE;
2431 case PROP_AUTOPAINT_COLORKEY:
2432 case PROP_DOUBLE_BUFFER:
2434 if (xvimagesink->xcontext != NULL) {
2441 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2448 static GValueArray *
2449 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2450 guint prop_id, const GParamSpec * pspec)
2452 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2453 GValueArray *array = NULL;
2455 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2456 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2465 GValue value = { 0 };
2467 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2468 g_value_init (&value, G_TYPE_STRING);
2470 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2471 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2473 g_value_set_string (&value, adaptor_id_s);
2474 g_value_array_append (array, &value);
2475 g_free (adaptor_id_s);
2477 g_value_unset (&value);
2480 case PROP_AUTOPAINT_COLORKEY:
2481 if (xvimagesink->have_autopaint_colorkey) {
2482 GValue value = { 0 };
2484 array = g_value_array_new (2);
2485 g_value_init (&value, G_TYPE_BOOLEAN);
2486 g_value_set_boolean (&value, FALSE);
2487 g_value_array_append (array, &value);
2488 g_value_set_boolean (&value, TRUE);
2489 g_value_array_append (array, &value);
2490 g_value_unset (&value);
2493 case PROP_DOUBLE_BUFFER:
2494 if (xvimagesink->have_double_buffer) {
2495 GValue value = { 0 };
2497 array = g_value_array_new (2);
2498 g_value_init (&value, G_TYPE_BOOLEAN);
2499 g_value_set_boolean (&value, FALSE);
2500 g_value_array_append (array, &value);
2501 g_value_set_boolean (&value, TRUE);
2502 g_value_array_append (array, &value);
2503 g_value_unset (&value);
2507 if (xvimagesink->have_colorkey) {
2508 GValue value = { 0 };
2510 array = g_value_array_new (1);
2511 g_value_init (&value, GST_TYPE_INT_RANGE);
2512 gst_value_set_int_range (&value, 0, 0xffffff);
2513 g_value_array_append (array, &value);
2514 g_value_unset (&value);
2518 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2527 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2530 iface->get_properties = gst_xvimagesink_probe_get_properties;
2531 iface->probe_property = gst_xvimagesink_probe_probe_property;
2532 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2533 iface->get_values = gst_xvimagesink_probe_get_values;
2536 /* =========================================== */
2538 /* Init & Class init */
2540 /* =========================================== */
2543 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2544 const GValue * value, GParamSpec * pspec)
2546 GstXvImageSink *xvimagesink;
2548 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2550 xvimagesink = GST_XVIMAGESINK (object);
2554 xvimagesink->hue = g_value_get_int (value);
2555 xvimagesink->cb_changed = TRUE;
2556 gst_xvimagesink_update_colorbalance (xvimagesink);
2559 xvimagesink->contrast = g_value_get_int (value);
2560 xvimagesink->cb_changed = TRUE;
2561 gst_xvimagesink_update_colorbalance (xvimagesink);
2563 case PROP_BRIGHTNESS:
2564 xvimagesink->brightness = g_value_get_int (value);
2565 xvimagesink->cb_changed = TRUE;
2566 gst_xvimagesink_update_colorbalance (xvimagesink);
2568 case PROP_SATURATION:
2569 xvimagesink->saturation = g_value_get_int (value);
2570 xvimagesink->cb_changed = TRUE;
2571 gst_xvimagesink_update_colorbalance (xvimagesink);
2574 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2576 case PROP_SYNCHRONOUS:
2577 xvimagesink->synchronous = g_value_get_boolean (value);
2578 if (xvimagesink->xcontext) {
2579 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2580 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2581 xvimagesink->synchronous ? "TRUE" : "FALSE");
2584 case PROP_PIXEL_ASPECT_RATIO:
2585 g_free (xvimagesink->par);
2586 xvimagesink->par = g_new0 (GValue, 1);
2587 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2588 if (!g_value_transform (value, xvimagesink->par)) {
2589 g_warning ("Could not transform string to aspect ratio");
2590 gst_value_set_fraction (xvimagesink->par, 1, 1);
2592 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2593 gst_value_get_fraction_numerator (xvimagesink->par),
2594 gst_value_get_fraction_denominator (xvimagesink->par));
2596 case PROP_FORCE_ASPECT_RATIO:
2597 xvimagesink->keep_aspect = g_value_get_boolean (value);
2599 case PROP_HANDLE_EVENTS:
2600 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
2601 g_value_get_boolean (value));
2602 gst_xvimagesink_manage_event_thread (xvimagesink);
2605 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2607 case PROP_HANDLE_EXPOSE:
2608 xvimagesink->handle_expose = g_value_get_boolean (value);
2609 gst_xvimagesink_manage_event_thread (xvimagesink);
2611 case PROP_DOUBLE_BUFFER:
2612 xvimagesink->double_buffer = g_value_get_boolean (value);
2614 case PROP_AUTOPAINT_COLORKEY:
2615 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2618 xvimagesink->colorkey = g_value_get_int (value);
2620 case PROP_DRAW_BORDERS:
2621 xvimagesink->draw_borders = g_value_get_boolean (value);
2624 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2630 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2631 GValue * value, GParamSpec * pspec)
2633 GstXvImageSink *xvimagesink;
2635 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2637 xvimagesink = GST_XVIMAGESINK (object);
2641 g_value_set_int (value, xvimagesink->hue);
2644 g_value_set_int (value, xvimagesink->contrast);
2646 case PROP_BRIGHTNESS:
2647 g_value_set_int (value, xvimagesink->brightness);
2649 case PROP_SATURATION:
2650 g_value_set_int (value, xvimagesink->saturation);
2653 g_value_set_string (value, xvimagesink->display_name);
2655 case PROP_SYNCHRONOUS:
2656 g_value_set_boolean (value, xvimagesink->synchronous);
2658 case PROP_PIXEL_ASPECT_RATIO:
2659 if (xvimagesink->par)
2660 g_value_transform (xvimagesink->par, value);
2662 case PROP_FORCE_ASPECT_RATIO:
2663 g_value_set_boolean (value, xvimagesink->keep_aspect);
2665 case PROP_HANDLE_EVENTS:
2666 g_value_set_boolean (value, xvimagesink->handle_events);
2670 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2672 g_value_set_string (value, adaptor_no_s);
2673 g_free (adaptor_no_s);
2676 case PROP_DEVICE_NAME:
2677 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2678 g_value_set_string (value,
2679 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2681 g_value_set_string (value, NULL);
2684 case PROP_HANDLE_EXPOSE:
2685 g_value_set_boolean (value, xvimagesink->handle_expose);
2687 case PROP_DOUBLE_BUFFER:
2688 g_value_set_boolean (value, xvimagesink->double_buffer);
2690 case PROP_AUTOPAINT_COLORKEY:
2691 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2694 g_value_set_int (value, xvimagesink->colorkey);
2696 case PROP_DRAW_BORDERS:
2697 g_value_set_boolean (value, xvimagesink->draw_borders);
2699 case PROP_WINDOW_WIDTH:
2700 if (xvimagesink->xwindow)
2701 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2703 g_value_set_uint64 (value, 0);
2705 case PROP_WINDOW_HEIGHT:
2706 if (xvimagesink->xwindow)
2707 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2709 g_value_set_uint64 (value, 0);
2712 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2718 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2722 GST_OBJECT_LOCK (xvimagesink);
2723 xvimagesink->running = FALSE;
2724 /* grab thread and mark it as NULL */
2725 thread = xvimagesink->event_thread;
2726 xvimagesink->event_thread = NULL;
2727 GST_OBJECT_UNLOCK (xvimagesink);
2729 /* Wait for our event thread to finish before we clean up our stuff. */
2731 g_thread_join (thread);
2733 if (xvimagesink->cur_image) {
2734 gst_buffer_unref (xvimagesink->cur_image);
2735 xvimagesink->cur_image = NULL;
2738 g_mutex_lock (xvimagesink->flow_lock);
2740 if (xvimagesink->pool) {
2741 gst_object_unref (xvimagesink->pool);
2742 xvimagesink->pool = NULL;
2745 if (xvimagesink->xwindow) {
2746 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2747 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2748 xvimagesink->xwindow = NULL;
2750 g_mutex_unlock (xvimagesink->flow_lock);
2752 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2753 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2754 xvimagesink->have_render_rect = FALSE;
2756 gst_xvimagesink_xcontext_clear (xvimagesink);
2759 /* Finalize is called only once, dispose can be called multiple times.
2760 * We use mutexes and don't reset stuff to NULL here so let's register
2763 gst_xvimagesink_finalize (GObject * object)
2765 GstXvImageSink *xvimagesink;
2767 xvimagesink = GST_XVIMAGESINK (object);
2769 gst_xvimagesink_reset (xvimagesink);
2771 if (xvimagesink->display_name) {
2772 g_free (xvimagesink->display_name);
2773 xvimagesink->display_name = NULL;
2776 if (xvimagesink->par) {
2777 g_free (xvimagesink->par);
2778 xvimagesink->par = NULL;
2780 if (xvimagesink->x_lock) {
2781 g_mutex_free (xvimagesink->x_lock);
2782 xvimagesink->x_lock = NULL;
2784 if (xvimagesink->flow_lock) {
2785 g_mutex_free (xvimagesink->flow_lock);
2786 xvimagesink->flow_lock = NULL;
2789 g_free (xvimagesink->media_title);
2791 G_OBJECT_CLASS (parent_class)->finalize (object);
2795 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2797 /* for the ALLOCATION query */
2798 gst_pad_set_query_function (GST_BASE_SINK (xvimagesink)->sinkpad,
2799 gst_xvimagesink_sink_query);
2801 xvimagesink->display_name = NULL;
2802 xvimagesink->adaptor_no = 0;
2803 xvimagesink->xcontext = NULL;
2804 xvimagesink->xwindow = NULL;
2805 xvimagesink->cur_image = NULL;
2807 xvimagesink->hue = xvimagesink->saturation = 0;
2808 xvimagesink->contrast = xvimagesink->brightness = 0;
2809 xvimagesink->cb_changed = FALSE;
2811 xvimagesink->fps_n = 0;
2812 xvimagesink->fps_d = 0;
2813 xvimagesink->video_width = 0;
2814 xvimagesink->video_height = 0;
2816 xvimagesink->x_lock = g_mutex_new ();
2817 xvimagesink->flow_lock = g_mutex_new ();
2819 xvimagesink->pool = NULL;
2821 xvimagesink->synchronous = FALSE;
2822 xvimagesink->double_buffer = TRUE;
2823 xvimagesink->running = FALSE;
2824 xvimagesink->keep_aspect = FALSE;
2825 xvimagesink->handle_events = TRUE;
2826 xvimagesink->par = NULL;
2827 xvimagesink->handle_expose = TRUE;
2828 xvimagesink->autopaint_colorkey = TRUE;
2830 /* on 16bit displays this becomes r,g,b = 1,2,3
2831 * on 24bit displays this becomes r,g,b = 8,8,16
2832 * as a port atom value
2834 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2835 xvimagesink->draw_borders = TRUE;
2839 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2841 GObjectClass *gobject_class;
2842 GstElementClass *gstelement_class;
2843 GstBaseSinkClass *gstbasesink_class;
2844 GstVideoSinkClass *videosink_class;
2846 gobject_class = (GObjectClass *) klass;
2847 gstelement_class = (GstElementClass *) klass;
2848 gstbasesink_class = (GstBaseSinkClass *) klass;
2849 videosink_class = (GstVideoSinkClass *) klass;
2851 parent_class = g_type_class_peek_parent (klass);
2853 gobject_class->set_property = gst_xvimagesink_set_property;
2854 gobject_class->get_property = gst_xvimagesink_get_property;
2856 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2857 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2858 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2859 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2860 g_param_spec_int ("brightness", "Brightness",
2861 "The brightness of the video", -1000, 1000, 0,
2862 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2863 g_object_class_install_property (gobject_class, PROP_HUE,
2864 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2865 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2866 g_object_class_install_property (gobject_class, PROP_SATURATION,
2867 g_param_spec_int ("saturation", "Saturation",
2868 "The saturation of the video", -1000, 1000, 0,
2869 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2870 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2871 g_param_spec_string ("display", "Display", "X Display name", NULL,
2872 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2873 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2874 g_param_spec_boolean ("synchronous", "Synchronous",
2875 "When enabled, runs "
2876 "the X display in synchronous mode. (used only for debugging)", FALSE,
2877 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2878 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2879 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2880 "The pixel aspect ratio of the device", "1/1",
2881 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2882 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2883 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2884 "When enabled, scaling will respect original aspect ratio", FALSE,
2885 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2886 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2887 g_param_spec_boolean ("handle-events", "Handle XEvents",
2888 "When enabled, XEvents will be selected and handled", TRUE,
2889 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2890 g_object_class_install_property (gobject_class, PROP_DEVICE,
2891 g_param_spec_string ("device", "Adaptor number",
2892 "The number of the video adaptor", "0",
2893 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2894 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2895 g_param_spec_string ("device-name", "Adaptor name",
2896 "The name of the video adaptor", NULL,
2897 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2899 * GstXvImageSink:handle-expose
2901 * When enabled, the current frame will always be drawn in response to X
2906 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2907 g_param_spec_boolean ("handle-expose", "Handle expose",
2909 "the current frame will always be drawn in response to X Expose "
2910 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2913 * GstXvImageSink:double-buffer
2915 * Whether to double-buffer the output.
2919 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2920 g_param_spec_boolean ("double-buffer", "Double-buffer",
2921 "Whether to double-buffer the output", TRUE,
2922 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2924 * GstXvImageSink:autopaint-colorkey
2926 * Whether to autofill overlay with colorkey
2930 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2931 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2932 "Whether to autofill overlay with colorkey", TRUE,
2933 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2935 * GstXvImageSink:colorkey
2937 * Color to use for the overlay mask.
2941 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2942 g_param_spec_int ("colorkey", "Colorkey",
2943 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2944 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2947 * GstXvImageSink:draw-borders
2949 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2950 * unused parts of the video area.
2954 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2955 g_param_spec_boolean ("draw-borders", "Colorkey",
2956 "Draw black borders to fill unused area in force-aspect-ratio mode",
2957 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2960 * GstXvImageSink:window-width
2962 * Actual width of the video window.
2966 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2967 g_param_spec_uint64 ("window-width", "window-width",
2968 "Width of the window", 0, G_MAXUINT64, 0,
2969 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2972 * GstXvImageSink:window-height
2974 * Actual height of the video window.
2978 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2979 g_param_spec_uint64 ("window-height", "window-height",
2980 "Height of the window", 0, G_MAXUINT64, 0,
2981 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2983 gobject_class->finalize = gst_xvimagesink_finalize;
2985 gst_element_class_set_details_simple (gstelement_class,
2986 "Video sink", "Sink/Video",
2987 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2989 gst_element_class_add_pad_template (gstelement_class,
2990 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2992 gstelement_class->change_state =
2993 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2995 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2996 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2997 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2998 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3000 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);