2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
112 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
113 * with newer GLib versions (>= 2.31.0) */
114 #define GLIB_DISABLE_DEPRECATION_WARNINGS
121 #include <gst/video/navigation.h>
122 #include <gst/video/videooverlay.h>
123 #include <gst/video/colorbalance.h>
124 /* Helper functions */
125 #include <gst/video/gstvideometa.h>
128 #include "xvimagesink.h"
130 /* Debugging category */
131 #include <gst/gstinfo.h>
133 /* for XkbKeycodeToKeysym */
134 #include <X11/XKBlib.h>
136 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
137 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
138 #define GST_CAT_DEFAULT gst_debug_xvimagesink
143 unsigned long functions;
144 unsigned long decorations;
146 unsigned long status;
148 MotifWmHints, MwmHints;
150 #define MWM_HINTS_DECORATIONS (1L << 1)
152 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
153 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
155 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
157 /* Default template - initiated with class struct to allow gst-register to work
159 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
160 GST_STATIC_PAD_TEMPLATE ("sink",
163 GST_STATIC_CAPS ("video/x-raw, "
164 "framerate = (fraction) [ 0, MAX ], "
165 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
177 PROP_PIXEL_ASPECT_RATIO,
178 PROP_FORCE_ASPECT_RATIO,
184 PROP_AUTOPAINT_COLORKEY,
191 /* ============================================================= */
195 /* ============================================================= */
197 /* =========================================== */
199 /* Object typing & Creation */
201 /* =========================================== */
202 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
203 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
205 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
207 #define gst_xvimagesink_parent_class parent_class
208 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
209 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
210 gst_xvimagesink_navigation_init);
211 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
212 gst_xvimagesink_video_overlay_init);
213 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
214 gst_xvimagesink_colorbalance_init));
217 /* ============================================================= */
219 /* Private Methods */
221 /* ============================================================= */
224 /* We are called with the x_lock taken */
226 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
227 GstXWindow * xwindow, GstVideoRectangle rect)
231 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
232 g_return_if_fail (xwindow != NULL);
234 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
235 xvimagesink->xcontext->black);
238 if (rect.x > xvimagesink->render_rect.x) {
239 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
240 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
241 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
245 t1 = rect.x + rect.w;
246 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
248 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
249 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
253 if (rect.y > xvimagesink->render_rect.y) {
254 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
255 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
256 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
260 t1 = rect.y + rect.h;
261 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
263 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
264 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
268 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
269 * if no window was available */
271 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
273 GstXvImageMeta *meta;
274 GstVideoCropMeta *crop;
275 GstVideoRectangle result;
276 gboolean draw_border = FALSE;
277 GstVideoRectangle src, dst;
279 /* We take the flow_lock. If expose is in there we don't want to run
280 concurrently from the data flow thread */
281 g_mutex_lock (xvimagesink->flow_lock);
283 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
284 g_mutex_unlock (xvimagesink->flow_lock);
288 /* Draw borders when displaying the first frame. After this
289 draw borders only on expose event or after a size change. */
290 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
294 /* Store a reference to the last image we put, lose the previous one */
295 if (xvimage && xvimagesink->cur_image != xvimage) {
296 if (xvimagesink->cur_image) {
297 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
298 gst_buffer_unref (xvimagesink->cur_image);
300 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
301 xvimagesink->cur_image = gst_buffer_ref (xvimage);
304 /* Expose sends a NULL image, we take the latest frame */
306 if (xvimagesink->cur_image) {
308 xvimage = xvimagesink->cur_image;
310 g_mutex_unlock (xvimagesink->flow_lock);
315 meta = gst_buffer_get_xvimage_meta (xvimage);
317 crop = gst_buffer_get_video_crop_meta (xvimage);
320 src.x = crop->x + meta->x;
321 src.y = crop->y + meta->y;
323 src.h = crop->height;
324 GST_LOG_OBJECT (xvimagesink,
325 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
330 src.h = meta->height;
333 if (xvimagesink->keep_aspect) {
336 /* We take the size of the source material as it was negotiated and
337 * corrected for DAR. This size can be different from the cropped size in
338 * which case the image will be scaled to fit the negotiated size. */
339 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
340 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
341 dst.w = xvimagesink->render_rect.w;
342 dst.h = xvimagesink->render_rect.h;
344 gst_video_sink_center_rect (s, dst, &result, TRUE);
345 result.x += xvimagesink->render_rect.x;
346 result.y += xvimagesink->render_rect.y;
348 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
351 g_mutex_lock (xvimagesink->x_lock);
353 if (draw_border && xvimagesink->draw_borders) {
354 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
356 xvimagesink->redraw_border = FALSE;
359 if (xvimagesink->xcontext->use_xshm) {
360 GST_LOG_OBJECT (xvimagesink,
361 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
362 GST_PTR_FORMAT, meta->width, meta->height,
363 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
365 XvShmPutImage (xvimagesink->xcontext->disp,
366 xvimagesink->xcontext->xv_port_id,
367 xvimagesink->xwindow->win,
368 xvimagesink->xwindow->gc, meta->xvimage,
369 src.x, src.y, src.w, src.h,
370 result.x, result.y, result.w, result.h, FALSE);
372 #endif /* HAVE_XSHM */
374 XvPutImage (xvimagesink->xcontext->disp,
375 xvimagesink->xcontext->xv_port_id,
376 xvimagesink->xwindow->win,
377 xvimagesink->xwindow->gc, meta->xvimage,
378 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
381 XSync (xvimagesink->xcontext->disp, FALSE);
383 g_mutex_unlock (xvimagesink->x_lock);
385 g_mutex_unlock (xvimagesink->flow_lock);
391 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
394 Atom hints_atom = None;
397 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
398 g_return_val_if_fail (window != NULL, FALSE);
400 g_mutex_lock (xvimagesink->x_lock);
402 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
404 if (hints_atom == None) {
405 g_mutex_unlock (xvimagesink->x_lock);
409 hints = g_malloc0 (sizeof (MotifWmHints));
411 hints->flags |= MWM_HINTS_DECORATIONS;
412 hints->decorations = 1 << 0;
414 XChangeProperty (xvimagesink->xcontext->disp, window->win,
415 hints_atom, hints_atom, 32, PropModeReplace,
416 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
418 XSync (xvimagesink->xcontext->disp, FALSE);
420 g_mutex_unlock (xvimagesink->x_lock);
428 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
429 GstXWindow * xwindow, const gchar * media_title)
432 g_free (xvimagesink->media_title);
433 xvimagesink->media_title = g_strdup (media_title);
436 /* we have a window */
437 if (xwindow->internal) {
438 XTextProperty xproperty;
439 const gchar *app_name;
440 const gchar *title = NULL;
441 gchar *title_mem = NULL;
443 /* set application name as a title */
444 app_name = g_get_application_name ();
446 if (app_name && xvimagesink->media_title) {
447 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
449 } else if (app_name) {
451 } else if (xvimagesink->media_title) {
452 title = xvimagesink->media_title;
456 if ((XStringListToTextProperty (((char **) &title), 1,
458 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
459 XFree (xproperty.value);
468 /* This function handles a GstXWindow creation
469 * The width and height are the actual pixel size on the display */
471 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
472 gint width, gint height)
474 GstXWindow *xwindow = NULL;
477 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
479 xwindow = g_new0 (GstXWindow, 1);
481 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
482 xvimagesink->render_rect.w = width;
483 xvimagesink->render_rect.h = height;
485 xwindow->width = width;
486 xwindow->height = height;
487 xwindow->internal = TRUE;
489 g_mutex_lock (xvimagesink->x_lock);
491 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
492 xvimagesink->xcontext->root,
493 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
495 /* We have to do that to prevent X from redrawing the background on
496 * ConfigureNotify. This takes away flickering of video when resizing. */
497 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
499 /* set application name as a title */
500 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
502 if (xvimagesink->handle_events) {
505 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
506 StructureNotifyMask | PointerMotionMask | KeyPressMask |
507 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
509 /* Tell the window manager we'd like delete client messages instead of
511 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
512 "WM_DELETE_WINDOW", True);
513 if (wm_delete != None) {
514 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
519 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
520 xwindow->win, 0, &values);
522 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
524 XSync (xvimagesink->xcontext->disp, FALSE);
526 g_mutex_unlock (xvimagesink->x_lock);
528 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
530 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
536 /* This function destroys a GstXWindow */
538 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
539 GstXWindow * xwindow)
541 g_return_if_fail (xwindow != NULL);
542 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
544 g_mutex_lock (xvimagesink->x_lock);
546 /* If we did not create that window we just free the GC and let it live */
547 if (xwindow->internal)
548 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
550 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
552 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
554 XSync (xvimagesink->xcontext->disp, FALSE);
556 g_mutex_unlock (xvimagesink->x_lock);
562 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
564 XWindowAttributes attr;
566 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
568 /* Update the window geometry */
569 g_mutex_lock (xvimagesink->x_lock);
570 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
571 g_mutex_unlock (xvimagesink->x_lock);
575 XGetWindowAttributes (xvimagesink->xcontext->disp,
576 xvimagesink->xwindow->win, &attr);
578 xvimagesink->xwindow->width = attr.width;
579 xvimagesink->xwindow->height = attr.height;
581 if (!xvimagesink->have_render_rect) {
582 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
583 xvimagesink->render_rect.w = attr.width;
584 xvimagesink->render_rect.h = attr.height;
587 g_mutex_unlock (xvimagesink->x_lock);
591 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
592 GstXWindow * xwindow)
594 g_return_if_fail (xwindow != NULL);
595 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
597 g_mutex_lock (xvimagesink->x_lock);
599 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
602 XSync (xvimagesink->xcontext->disp, FALSE);
604 g_mutex_unlock (xvimagesink->x_lock);
607 /* This function commits our internal colorbalance settings to our grabbed Xv
608 port. If the xcontext is not initialized yet it simply returns */
610 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
612 GList *channels = NULL;
614 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
616 /* If we haven't initialized the X context we can't update anything */
617 if (xvimagesink->xcontext == NULL)
620 /* Don't set the attributes if they haven't been changed, to avoid
621 * rounding errors changing the values */
622 if (!xvimagesink->cb_changed)
625 /* For each channel of the colorbalance we calculate the correct value
626 doing range conversion and then set the Xv port attribute to match our
628 channels = xvimagesink->xcontext->channels_list;
631 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
632 GstColorBalanceChannel *channel = NULL;
635 gdouble convert_coef;
637 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
638 g_object_ref (channel);
640 /* Our range conversion coef */
641 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
643 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
644 value = xvimagesink->hue;
645 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
646 value = xvimagesink->saturation;
647 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
648 value = xvimagesink->contrast;
649 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
650 value = xvimagesink->brightness;
652 g_warning ("got an unknown channel %s", channel->label);
653 g_object_unref (channel);
657 /* Committing to Xv port */
658 g_mutex_lock (xvimagesink->x_lock);
660 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
661 if (prop_atom != None) {
664 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
665 XvSetPortAttribute (xvimagesink->xcontext->disp,
666 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
668 g_mutex_unlock (xvimagesink->x_lock);
670 g_object_unref (channel);
672 channels = g_list_next (channels);
676 /* This function handles XEvents that might be in the queue. It generates
677 GstEvent that will be sent upstream in the pipeline to handle interactivity
678 and navigation. It will also listen for configure events on the window to
679 trigger caps renegotiation so on the fly software scaling can work. */
681 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
684 guint pointer_x = 0, pointer_y = 0;
685 gboolean pointer_moved = FALSE;
686 gboolean exposed = FALSE, configured = FALSE;
688 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
690 /* Handle Interaction, produces navigation events */
692 /* We get all pointer motion events, only the last position is
694 g_mutex_lock (xvimagesink->flow_lock);
695 g_mutex_lock (xvimagesink->x_lock);
696 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
697 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
698 g_mutex_unlock (xvimagesink->x_lock);
699 g_mutex_unlock (xvimagesink->flow_lock);
703 pointer_x = e.xmotion.x;
704 pointer_y = e.xmotion.y;
705 pointer_moved = TRUE;
710 g_mutex_lock (xvimagesink->flow_lock);
711 g_mutex_lock (xvimagesink->x_lock);
715 g_mutex_unlock (xvimagesink->x_lock);
716 g_mutex_unlock (xvimagesink->flow_lock);
718 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
719 pointer_x, pointer_y);
720 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
721 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
723 g_mutex_lock (xvimagesink->flow_lock);
724 g_mutex_lock (xvimagesink->x_lock);
727 /* We get all events on our window to throw them upstream */
728 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
729 xvimagesink->xwindow->win,
730 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
733 const char *key_str = NULL;
735 /* We lock only for the X function call */
736 g_mutex_unlock (xvimagesink->x_lock);
737 g_mutex_unlock (xvimagesink->flow_lock);
741 /* Mouse button pressed over our window. We send upstream
742 events for interactivity/navigation */
743 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
744 e.xbutton.button, e.xbutton.x, e.xbutton.y);
745 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
746 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
749 /* Mouse button released over our window. We send upstream
750 events for interactivity/navigation */
751 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
752 e.xbutton.button, e.xbutton.x, e.xbutton.y);
753 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
754 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
758 /* Key pressed/released over our window. We send upstream
759 events for interactivity/navigation */
760 g_mutex_lock (xvimagesink->x_lock);
761 keysym = XkbKeycodeToKeysym (xvimagesink->xcontext->disp,
762 e.xkey.keycode, 0, 0);
763 if (keysym != NoSymbol) {
764 key_str = XKeysymToString (keysym);
768 g_mutex_unlock (xvimagesink->x_lock);
769 GST_DEBUG_OBJECT (xvimagesink,
770 "key %d pressed over window at %d,%d (%s)",
771 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
772 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
773 e.type == KeyPress ? "key-press" : "key-release", key_str);
776 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
779 g_mutex_lock (xvimagesink->flow_lock);
780 g_mutex_lock (xvimagesink->x_lock);
784 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
785 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
790 case ConfigureNotify:
791 g_mutex_unlock (xvimagesink->x_lock);
792 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
793 g_mutex_lock (xvimagesink->x_lock);
801 if (xvimagesink->handle_expose && (exposed || configured)) {
802 g_mutex_unlock (xvimagesink->x_lock);
803 g_mutex_unlock (xvimagesink->flow_lock);
805 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
807 g_mutex_lock (xvimagesink->flow_lock);
808 g_mutex_lock (xvimagesink->x_lock);
811 /* Handle Display events */
812 while (XPending (xvimagesink->xcontext->disp)) {
813 XNextEvent (xvimagesink->xcontext->disp, &e);
819 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
820 "WM_DELETE_WINDOW", True);
821 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
822 /* Handle window deletion by posting an error on the bus */
823 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
824 ("Output window was closed"), (NULL));
826 g_mutex_unlock (xvimagesink->x_lock);
827 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
828 xvimagesink->xwindow = NULL;
829 g_mutex_lock (xvimagesink->x_lock);
838 g_mutex_unlock (xvimagesink->x_lock);
839 g_mutex_unlock (xvimagesink->flow_lock);
843 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
844 XvAdaptorInfo * adaptors, int adaptor_no)
849 /* Do we support XvImageMask ? */
850 if (!(adaptors[adaptor_no].type & XvImageMask)) {
851 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
852 adaptors[adaptor_no].name);
856 /* We found such an adaptor, looking for an available port */
857 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
858 /* We try to grab the port */
859 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
860 if (Success == res) {
861 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
862 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
863 adaptors[adaptor_no].num_ports);
865 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
866 adaptors[adaptor_no].name, res);
871 /* This function generates a caps with all supported format by the first
872 Xv grabable port we find. We store each one of the supported formats in a
873 format list and append the format to a newly created caps that we return
874 If this function does not return NULL because of an error, it also grabs
875 the port via XvGrabPort */
877 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
878 GstXContext * xcontext)
881 XvAdaptorInfo *adaptors;
883 XvImageFormatValues *formats = NULL;
885 XvEncodingInfo *encodings = NULL;
886 gulong max_w = G_MAXINT, max_h = G_MAXINT;
887 GstCaps *caps = NULL;
888 GstCaps *rgb_caps = NULL;
890 g_return_val_if_fail (xcontext != NULL, NULL);
892 /* First let's check that XVideo extension is available */
893 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
894 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
895 ("Could not initialise Xv output"),
896 ("XVideo extension is not available"));
900 /* Then we get adaptors list */
901 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
902 &xcontext->nb_adaptors, &adaptors)) {
903 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
904 ("Could not initialise Xv output"),
905 ("Failed getting XV adaptors list"));
909 xcontext->xv_port_id = 0;
911 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
914 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
916 /* Now fill up our adaptor name array */
917 for (i = 0; i < xcontext->nb_adaptors; i++) {
918 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
921 if (xvimagesink->adaptor_no != -1 &&
922 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
923 /* Find xv port from user defined adaptor */
924 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
925 xvimagesink->adaptor_no);
928 if (!xcontext->xv_port_id) {
929 /* Now search for an adaptor that supports XvImageMask */
930 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
931 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
932 xvimagesink->adaptor_no = i;
936 XvFreeAdaptorInfo (adaptors);
938 if (!xcontext->xv_port_id) {
939 xvimagesink->adaptor_no = -1;
940 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
941 ("Could not initialise Xv output"), ("No port available"));
945 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
948 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
949 xcontext->xv_port_id, &count);
950 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
951 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
952 static const char colorkey[] = "XV_COLORKEY";
954 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
956 xvimagesink->have_autopaint_colorkey = FALSE;
957 xvimagesink->have_double_buffer = FALSE;
958 xvimagesink->have_colorkey = FALSE;
960 for (i = 0; ((i < count) && todo); i++)
961 if (!strcmp (attr[i].name, autopaint)) {
962 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
964 /* turn on autopaint colorkey */
965 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
966 (xvimagesink->autopaint_colorkey ? 1 : 0));
968 xvimagesink->have_autopaint_colorkey = TRUE;
969 } else if (!strcmp (attr[i].name, dbl_buffer)) {
970 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
972 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
973 (xvimagesink->double_buffer ? 1 : 0));
975 xvimagesink->have_double_buffer = TRUE;
976 } else if (!strcmp (attr[i].name, colorkey)) {
977 /* Set the colorkey, default is something that is dark but hopefully
978 * won't randomly appear on the screen elsewhere (ie not black or greys)
979 * can be overridden by setting "colorkey" property
981 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
983 gboolean set_attr = TRUE;
986 /* set a colorkey in the right format RGB565/RGB888
987 * We only handle these 2 cases, because they're the only types of
988 * devices we've encountered. If we don't recognise it, leave it alone
990 cr = (xvimagesink->colorkey >> 16);
991 cg = (xvimagesink->colorkey >> 8) & 0xFF;
992 cb = (xvimagesink->colorkey) & 0xFF;
993 switch (xcontext->depth) {
994 case 16: /* RGB 565 */
998 ckey = (cr << 11) | (cg << 5) | cb;
1001 case 32: /* RGB 888 / ARGB 8888 */
1002 ckey = (cr << 16) | (cg << 8) | cb;
1005 GST_DEBUG_OBJECT (xvimagesink,
1006 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1013 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1014 (guint32) attr[i].max_value);
1015 GST_LOG_OBJECT (xvimagesink,
1016 "Setting color key for display depth %d to 0x%x",
1017 xcontext->depth, ckey);
1019 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1023 xvimagesink->have_colorkey = TRUE;
1029 /* Get the list of encodings supported by the adapter and look for the
1030 * XV_IMAGE encoding so we can determine the maximum width and height
1032 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1035 for (i = 0; i < nb_encodings; i++) {
1036 GST_LOG_OBJECT (xvimagesink,
1037 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1038 i, encodings[i].name, encodings[i].width, encodings[i].height,
1039 encodings[i].rate.numerator, encodings[i].rate.denominator);
1040 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1041 max_w = encodings[i].width;
1042 max_h = encodings[i].height;
1046 XvFreeEncodingInfo (encodings);
1048 /* We get all image formats supported by our port */
1049 formats = XvListImageFormats (xcontext->disp,
1050 xcontext->xv_port_id, &nb_formats);
1051 caps = gst_caps_new_empty ();
1052 for (i = 0; i < nb_formats; i++) {
1053 GstCaps *format_caps = NULL;
1054 gboolean is_rgb_format = FALSE;
1055 GstVideoFormat vformat;
1057 /* We set the image format of the xcontext to an existing one. This
1058 is just some valid image format for making our xshm calls check before
1059 caps negotiation really happens. */
1060 xcontext->im_format = formats[i].id;
1062 switch (formats[i].type) {
1065 XvImageFormatValues *fmt = &(formats[i]);
1069 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1071 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1072 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1073 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
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);
1082 is_rgb_format = TRUE;
1087 vformat = gst_video_format_from_fourcc (formats[i].id);
1088 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1091 format_caps = gst_caps_new_simple ("video/x-raw",
1092 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1093 "width", GST_TYPE_INT_RANGE, 1, max_w,
1094 "height", GST_TYPE_INT_RANGE, 1, max_h,
1095 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1099 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1100 g_assert_not_reached ();
1105 GstXvImageFormat *format = NULL;
1107 format = g_new0 (GstXvImageFormat, 1);
1109 format->format = formats[i].id;
1110 format->vformat = vformat;
1111 format->caps = gst_caps_copy (format_caps);
1112 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1115 if (is_rgb_format) {
1116 if (rgb_caps == NULL)
1117 rgb_caps = format_caps;
1119 gst_caps_append (rgb_caps, format_caps);
1121 gst_caps_append (caps, format_caps);
1125 /* Collected all caps into either the caps or rgb_caps structures.
1126 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1128 gst_caps_append (caps, rgb_caps);
1133 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1135 if (gst_caps_is_empty (caps)) {
1136 gst_caps_unref (caps);
1137 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1138 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1139 ("No supported format found"));
1147 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1149 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1151 GST_OBJECT_LOCK (xvimagesink);
1152 while (xvimagesink->running) {
1153 GST_OBJECT_UNLOCK (xvimagesink);
1155 if (xvimagesink->xwindow) {
1156 gst_xvimagesink_handle_xevents (xvimagesink);
1158 /* FIXME: do we want to align this with the framerate or anything else? */
1159 g_usleep (G_USEC_PER_SEC / 20);
1161 GST_OBJECT_LOCK (xvimagesink);
1163 GST_OBJECT_UNLOCK (xvimagesink);
1169 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1171 GThread *thread = NULL;
1173 /* don't start the thread too early */
1174 if (xvimagesink->xcontext == NULL) {
1178 GST_OBJECT_LOCK (xvimagesink);
1179 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1180 if (!xvimagesink->event_thread) {
1181 /* Setup our event listening thread */
1182 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1183 xvimagesink->handle_expose, xvimagesink->handle_events);
1184 xvimagesink->running = TRUE;
1185 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1186 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1189 if (xvimagesink->event_thread) {
1190 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1191 xvimagesink->handle_expose, xvimagesink->handle_events);
1192 xvimagesink->running = FALSE;
1193 /* grab thread and mark it as NULL */
1194 thread = xvimagesink->event_thread;
1195 xvimagesink->event_thread = NULL;
1198 GST_OBJECT_UNLOCK (xvimagesink);
1200 /* Wait for our event thread to finish */
1202 g_thread_join (thread);
1207 /* This function calculates the pixel aspect ratio based on the properties
1208 * in the xcontext structure and stores it there. */
1210 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1212 static const gint par[][2] = {
1213 {1, 1}, /* regular screen */
1214 {16, 15}, /* PAL TV */
1215 {11, 10}, /* 525 line Rec.601 video */
1216 {54, 59}, /* 625 line Rec.601 video */
1217 {64, 45}, /* 1280x1024 on 16:9 display */
1218 {5, 3}, /* 1280x1024 on 4:3 display */
1219 {4, 3} /* 800x600 on 16:9 display */
1226 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1228 /* first calculate the "real" ratio based on the X values;
1229 * which is the "physical" w/h divided by the w/h in pixels of the display */
1230 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1231 / (xcontext->heightmm * xcontext->width);
1233 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1235 if (xcontext->width == 720 && xcontext->height == 576) {
1236 ratio = 4.0 * 576 / (3.0 * 720);
1238 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1240 /* now find the one from par[][2] with the lowest delta to the real one */
1244 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1245 gdouble this_delta = DELTA (i);
1247 if (this_delta < delta) {
1253 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1254 par[index][0], par[index][1]);
1256 g_free (xcontext->par);
1257 xcontext->par = g_new0 (GValue, 1);
1258 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1259 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1260 GST_DEBUG ("set xcontext PAR to %d/%d",
1261 gst_value_get_fraction_numerator (xcontext->par),
1262 gst_value_get_fraction_denominator (xcontext->par));
1265 /* This function gets the X Display and global info about it. Everything is
1266 stored in our object and will be cleaned when the object is disposed. Note
1267 here that caps for supported format are generated without any window or
1269 static GstXContext *
1270 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1272 GstXContext *xcontext = NULL;
1273 XPixmapFormatValues *px_formats = NULL;
1274 gint nb_formats = 0, i, j, N_attr;
1275 XvAttribute *xv_attr;
1277 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1278 "XV_BRIGHTNESS", "XV_CONTRAST"
1281 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1283 xcontext = g_new0 (GstXContext, 1);
1284 xcontext->im_format = 0;
1286 g_mutex_lock (xvimagesink->x_lock);
1288 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1290 if (!xcontext->disp) {
1291 g_mutex_unlock (xvimagesink->x_lock);
1293 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1294 ("Could not initialise Xv output"), ("Could not open display"));
1298 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1299 xcontext->screen_num = DefaultScreen (xcontext->disp);
1300 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1301 xcontext->root = DefaultRootWindow (xcontext->disp);
1302 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1303 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1304 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1306 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1307 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1308 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1309 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1311 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1312 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1314 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1315 /* We get supported pixmap formats at supported depth */
1316 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1319 XCloseDisplay (xcontext->disp);
1320 g_mutex_unlock (xvimagesink->x_lock);
1321 g_free (xcontext->par);
1323 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1324 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1328 /* We get bpp value corresponding to our running depth */
1329 for (i = 0; i < nb_formats; i++) {
1330 if (px_formats[i].depth == xcontext->depth)
1331 xcontext->bpp = px_formats[i].bits_per_pixel;
1336 xcontext->endianness =
1337 (ImageByteOrder (xcontext->disp) ==
1338 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1340 /* our caps system handles 24/32bpp RGB as big-endian. */
1341 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1342 xcontext->endianness == G_LITTLE_ENDIAN) {
1343 xcontext->endianness = G_BIG_ENDIAN;
1344 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1345 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1346 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1347 if (xcontext->bpp == 24) {
1348 xcontext->visual->red_mask >>= 8;
1349 xcontext->visual->green_mask >>= 8;
1350 xcontext->visual->blue_mask >>= 8;
1354 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1356 /* Search for XShm extension support */
1358 if (XShmQueryExtension (xcontext->disp) &&
1359 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1360 xcontext->use_xshm = TRUE;
1361 GST_DEBUG ("xvimagesink is using XShm extension");
1363 #endif /* HAVE_XSHM */
1365 xcontext->use_xshm = FALSE;
1366 GST_DEBUG ("xvimagesink is not using XShm extension");
1369 if (!xcontext->caps) {
1370 XCloseDisplay (xcontext->disp);
1371 g_mutex_unlock (xvimagesink->x_lock);
1372 g_free (xcontext->par);
1374 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1378 xv_attr = XvQueryPortAttributes (xcontext->disp,
1379 xcontext->xv_port_id, &N_attr);
1382 /* Generate the channels list */
1383 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1384 XvAttribute *matching_attr = NULL;
1386 /* Retrieve the property atom if it exists. If it doesn't exist,
1387 * the attribute itself must not either, so we can skip */
1388 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1389 if (prop_atom == None)
1392 if (xv_attr != NULL) {
1393 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1394 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1395 matching_attr = xv_attr + j;
1398 if (matching_attr) {
1399 GstColorBalanceChannel *channel;
1401 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1402 channel->label = g_strdup (channels[i]);
1403 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1404 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1406 xcontext->channels_list = g_list_append (xcontext->channels_list,
1409 /* If the colorbalance settings have not been touched we get Xv values
1410 as defaults and update our internal variables */
1411 if (!xvimagesink->cb_changed) {
1414 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1416 /* Normalize val to [-1000, 1000] */
1417 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1418 (double) (channel->max_value - channel->min_value));
1420 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1421 xvimagesink->hue = val;
1422 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1423 xvimagesink->saturation = val;
1424 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1425 xvimagesink->brightness = val;
1426 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1427 xvimagesink->contrast = val;
1435 g_mutex_unlock (xvimagesink->x_lock);
1440 /* This function cleans the X context. Closing the Display, releasing the XV
1441 port and unrefing the caps for supported formats. */
1443 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1445 GList *formats_list, *channels_list;
1446 GstXContext *xcontext;
1449 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1451 GST_OBJECT_LOCK (xvimagesink);
1452 if (xvimagesink->xcontext == NULL) {
1453 GST_OBJECT_UNLOCK (xvimagesink);
1457 /* Take the XContext from the sink and clean it up */
1458 xcontext = xvimagesink->xcontext;
1459 xvimagesink->xcontext = NULL;
1461 GST_OBJECT_UNLOCK (xvimagesink);
1464 formats_list = xcontext->formats_list;
1466 while (formats_list) {
1467 GstXvImageFormat *format = formats_list->data;
1469 gst_caps_unref (format->caps);
1471 formats_list = g_list_next (formats_list);
1474 if (xcontext->formats_list)
1475 g_list_free (xcontext->formats_list);
1477 channels_list = xcontext->channels_list;
1479 while (channels_list) {
1480 GstColorBalanceChannel *channel = channels_list->data;
1482 g_object_unref (channel);
1483 channels_list = g_list_next (channels_list);
1486 if (xcontext->channels_list)
1487 g_list_free (xcontext->channels_list);
1489 gst_caps_unref (xcontext->caps);
1490 if (xcontext->last_caps)
1491 gst_caps_replace (&xcontext->last_caps, NULL);
1493 for (i = 0; i < xcontext->nb_adaptors; i++) {
1494 g_free (xcontext->adaptors[i]);
1497 g_free (xcontext->adaptors);
1499 g_free (xcontext->par);
1501 g_mutex_lock (xvimagesink->x_lock);
1503 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1505 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1507 XCloseDisplay (xcontext->disp);
1509 g_mutex_unlock (xvimagesink->x_lock);
1517 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1519 GstXvImageSink *xvimagesink;
1522 xvimagesink = GST_XVIMAGESINK (bsink);
1524 if (xvimagesink->xcontext) {
1526 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1527 GST_CAPS_INTERSECT_FIRST);
1529 return gst_caps_ref (xvimagesink->xcontext->caps);
1532 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1534 GstCaps *intersection;
1537 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1538 gst_caps_unref (caps);
1539 caps = intersection;
1545 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1547 GstXvImageSink *xvimagesink;
1548 GstStructure *structure;
1549 GstBufferPool *newpool, *oldpool;
1551 guint32 im_format = 0;
1552 gint video_par_n, video_par_d; /* video's PAR */
1553 gint display_par_n, display_par_d; /* display's PAR */
1556 static GstAllocationParams params = { 0, 15, 0, 0, };
1558 xvimagesink = GST_XVIMAGESINK (bsink);
1560 GST_DEBUG_OBJECT (xvimagesink,
1561 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1562 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1564 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1565 goto incompatible_caps;
1567 if (!gst_video_info_from_caps (&info, caps))
1568 goto invalid_format;
1570 xvimagesink->fps_n = info.fps_n;
1571 xvimagesink->fps_d = info.fps_d;
1573 xvimagesink->video_width = info.width;
1574 xvimagesink->video_height = info.height;
1576 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1577 if (im_format == -1)
1578 goto invalid_format;
1582 /* get aspect ratio from caps if it's present, and
1583 * convert video width and height to a display width and height
1584 * using wd / hd = wv / hv * PARv / PARd */
1586 /* get video's PAR */
1587 video_par_n = info.par_n;
1588 video_par_d = info.par_d;
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 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1600 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1603 GST_DEBUG_OBJECT (xvimagesink,
1604 "video width/height: %dx%d, calculated display ratio: %d/%d",
1605 info.width, info.height, num, den);
1607 /* now find a width x height that respects this display ratio.
1608 * prefer those that have one of w/h the same as the incoming video
1609 * using wd / hd = num / den */
1611 /* start with same height, because of interlaced video */
1612 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1613 if (info.height % den == 0) {
1614 GST_DEBUG_OBJECT (xvimagesink, "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;
1618 } else if (info.width % num == 0) {
1619 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1620 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1621 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1622 gst_util_uint64_scale_int (info.width, den, num);
1624 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1625 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1626 gst_util_uint64_scale_int (info.height, num, den);
1627 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1629 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1630 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1632 /* Notify application to set xwindow id now */
1633 g_mutex_lock (xvimagesink->flow_lock);
1634 if (!xvimagesink->xwindow) {
1635 g_mutex_unlock (xvimagesink->flow_lock);
1636 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1638 g_mutex_unlock (xvimagesink->flow_lock);
1641 /* Creating our window and our image with the display size in pixels */
1642 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1643 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1644 goto no_display_size;
1646 g_mutex_lock (xvimagesink->flow_lock);
1647 if (!xvimagesink->xwindow) {
1648 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1649 GST_VIDEO_SINK_WIDTH (xvimagesink),
1650 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1653 xvimagesink->info = info;
1655 /* After a resize, we want to redraw the borders in case the new frame size
1656 * doesn't cover the same area */
1657 xvimagesink->redraw_border = TRUE;
1659 /* create a new pool for the new configuration */
1660 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1662 structure = gst_buffer_pool_get_config (newpool);
1663 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1664 gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms);
1665 if (!gst_buffer_pool_set_config (newpool, structure))
1668 oldpool = xvimagesink->pool;
1669 /* we don't activate the pool yet, this will be done by downstream after it
1670 * has configured the pool. If downstream does not want our pool we will
1671 * activate it when we render into it */
1672 xvimagesink->pool = newpool;
1673 g_mutex_unlock (xvimagesink->flow_lock);
1675 /* unref the old sink */
1677 /* we don't deactivate, some elements might still be using it, it will
1678 * be deactivated when the last ref is gone */
1679 gst_object_unref (oldpool);
1687 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1692 GST_DEBUG_OBJECT (xvimagesink,
1693 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1698 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1699 ("Error calculating the output display ratio of the video."));
1704 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1705 ("Error calculating the output display ratio of the video."));
1710 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1711 g_mutex_unlock (xvimagesink->flow_lock);
1716 static GstStateChangeReturn
1717 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1719 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1720 GstXvImageSink *xvimagesink;
1721 GstXContext *xcontext = NULL;
1723 xvimagesink = GST_XVIMAGESINK (element);
1725 switch (transition) {
1726 case GST_STATE_CHANGE_NULL_TO_READY:
1727 /* Initializing the XContext */
1728 if (xvimagesink->xcontext == NULL) {
1729 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1730 if (xcontext == NULL) {
1731 ret = GST_STATE_CHANGE_FAILURE;
1734 GST_OBJECT_LOCK (xvimagesink);
1736 xvimagesink->xcontext = xcontext;
1737 GST_OBJECT_UNLOCK (xvimagesink);
1740 /* update object's par with calculated one if not set yet */
1741 if (!xvimagesink->par) {
1742 xvimagesink->par = g_new0 (GValue, 1);
1743 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1744 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1746 /* call XSynchronize with the current value of synchronous */
1747 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1748 xvimagesink->synchronous ? "TRUE" : "FALSE");
1749 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1750 gst_xvimagesink_update_colorbalance (xvimagesink);
1751 gst_xvimagesink_manage_event_thread (xvimagesink);
1753 case GST_STATE_CHANGE_READY_TO_PAUSED:
1755 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1757 case GST_STATE_CHANGE_PAUSED_TO_READY:
1763 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1765 switch (transition) {
1766 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1768 case GST_STATE_CHANGE_PAUSED_TO_READY:
1769 xvimagesink->fps_n = 0;
1770 xvimagesink->fps_d = 1;
1771 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1772 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1773 g_mutex_lock (xvimagesink->flow_lock);
1774 if (xvimagesink->pool)
1775 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1776 g_mutex_unlock (xvimagesink->flow_lock);
1778 case GST_STATE_CHANGE_READY_TO_NULL:
1779 gst_xvimagesink_reset (xvimagesink);
1790 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1791 GstClockTime * start, GstClockTime * end)
1793 GstXvImageSink *xvimagesink;
1795 xvimagesink = GST_XVIMAGESINK (bsink);
1797 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1798 *start = GST_BUFFER_TIMESTAMP (buf);
1799 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1800 *end = *start + GST_BUFFER_DURATION (buf);
1802 if (xvimagesink->fps_n > 0) {
1804 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1805 xvimagesink->fps_n);
1811 static GstFlowReturn
1812 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1815 GstXvImageSink *xvimagesink;
1816 GstXvImageMeta *meta;
1819 xvimagesink = GST_XVIMAGESINK (vsink);
1821 meta = gst_buffer_get_xvimage_meta (buf);
1823 if (meta && meta->sink == xvimagesink) {
1824 /* If this buffer has been allocated using our buffer management we simply
1825 put the ximage which is in the PRIVATE pointer */
1826 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1831 GstVideoFrame src, dest;
1832 GstBufferPoolAcquireParams params = { 0, };
1834 /* Else we have to copy the data into our private image, */
1835 /* if we have one... */
1836 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1838 /* we should have a pool, configured in setcaps */
1839 if (xvimagesink->pool == NULL)
1842 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1843 goto activate_failed;
1845 /* take a buffer from our pool, if there is no buffer in the pool something
1846 * is seriously wrong, waiting for the pool here might deadlock when we try
1847 * to go to PAUSED because we never flush the pool then. */
1848 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1849 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
1850 if (res != GST_FLOW_OK)
1853 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1854 "slow copy into bufferpool buffer %p", to_put);
1856 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1857 goto invalid_buffer;
1859 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1860 gst_video_frame_unmap (&src);
1861 goto invalid_buffer;
1864 gst_video_frame_copy (&dest, &src);
1866 gst_video_frame_unmap (&dest);
1867 gst_video_frame_unmap (&src);
1870 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1875 gst_buffer_unref (to_put);
1882 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1883 ("Internal error: can't allocate images"),
1884 ("We don't have a bufferpool negotiated"));
1885 return GST_FLOW_ERROR;
1889 /* No image available. That's very bad ! */
1890 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1895 /* No Window available to put our image into */
1896 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1902 /* No Window available to put our image into */
1903 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1904 res = GST_FLOW_ERROR;
1909 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1910 res = GST_FLOW_ERROR;
1916 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1918 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1920 switch (GST_EVENT_TYPE (event)) {
1921 case GST_EVENT_TAG:{
1923 gchar *title = NULL;
1925 gst_event_parse_tag (event, &l);
1926 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1929 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1930 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1940 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1944 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1946 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1947 GstBufferPool *pool;
1948 GstStructure *config;
1953 gst_query_parse_allocation (query, &caps, &need_pool);
1958 g_mutex_lock (xvimagesink->flow_lock);
1959 if ((pool = xvimagesink->pool))
1960 gst_object_ref (pool);
1961 g_mutex_unlock (xvimagesink->flow_lock);
1966 /* we had a pool, check caps */
1967 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1968 config = gst_buffer_pool_get_config (pool);
1969 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1971 if (!gst_caps_is_equal (caps, pcaps)) {
1972 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1973 /* different caps, we can't use this pool */
1974 gst_object_unref (pool);
1977 gst_structure_free (config);
1979 if (pool == NULL && need_pool) {
1982 if (!gst_video_info_from_caps (&info, caps))
1985 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1986 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1988 /* the normal size of a frame */
1991 config = gst_buffer_pool_get_config (pool);
1992 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1993 if (!gst_buffer_pool_set_config (pool, config))
1997 /* we need at least 2 buffer because we hold on to the last one */
1998 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1999 gst_object_unref (pool);
2002 /* we also support various metadata */
2003 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2004 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2011 GST_DEBUG_OBJECT (bsink, "no caps specified");
2016 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2021 GST_DEBUG_OBJECT (bsink, "failed setting config");
2022 gst_object_unref (pool);
2027 /* Interfaces stuff */
2029 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2030 GstStructure * structure)
2032 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2035 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2037 GstVideoRectangle src, dst, result;
2038 gdouble x, y, xscale = 1.0, yscale = 1.0;
2040 event = gst_event_new_navigation (structure);
2042 /* We take the flow_lock while we look at the window */
2043 g_mutex_lock (xvimagesink->flow_lock);
2045 if (!xvimagesink->xwindow) {
2046 g_mutex_unlock (xvimagesink->flow_lock);
2050 if (xvimagesink->keep_aspect) {
2051 /* We get the frame position using the calculated geometry from _setcaps
2052 that respect pixel aspect ratios */
2053 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2054 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2055 dst.w = xvimagesink->render_rect.w;
2056 dst.h = xvimagesink->render_rect.h;
2058 gst_video_sink_center_rect (src, dst, &result, TRUE);
2059 result.x += xvimagesink->render_rect.x;
2060 result.y += xvimagesink->render_rect.y;
2062 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2065 g_mutex_unlock (xvimagesink->flow_lock);
2067 /* We calculate scaling using the original video frames geometry to include
2068 pixel aspect ratio scaling. */
2069 xscale = (gdouble) xvimagesink->video_width / result.w;
2070 yscale = (gdouble) xvimagesink->video_height / result.h;
2072 /* Converting pointer coordinates to the non scaled geometry */
2073 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2074 x = MIN (x, result.x + result.w);
2075 x = MAX (x - result.x, 0);
2076 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2077 (gdouble) x * xscale, NULL);
2079 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2080 y = MIN (y, result.y + result.h);
2081 y = MAX (y - result.y, 0);
2082 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2083 (gdouble) y * yscale, NULL);
2086 gst_pad_send_event (peer, event);
2087 gst_object_unref (peer);
2092 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2094 iface->send_event = gst_xvimagesink_navigation_send_event;
2098 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2100 XID xwindow_id = id;
2101 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2102 GstXWindow *xwindow = NULL;
2104 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2106 g_mutex_lock (xvimagesink->flow_lock);
2108 /* If we already use that window return */
2109 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2110 g_mutex_unlock (xvimagesink->flow_lock);
2114 /* If the element has not initialized the X11 context try to do so */
2115 if (!xvimagesink->xcontext &&
2116 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2117 g_mutex_unlock (xvimagesink->flow_lock);
2118 /* we have thrown a GST_ELEMENT_ERROR now */
2122 gst_xvimagesink_update_colorbalance (xvimagesink);
2124 /* If a window is there already we destroy it */
2125 if (xvimagesink->xwindow) {
2126 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2127 xvimagesink->xwindow = NULL;
2130 /* If the xid is 0 we go back to an internal window */
2131 if (xwindow_id == 0) {
2132 /* If no width/height caps nego did not happen window will be created
2133 during caps nego then */
2134 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2135 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2137 gst_xvimagesink_xwindow_new (xvimagesink,
2138 GST_VIDEO_SINK_WIDTH (xvimagesink),
2139 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2142 XWindowAttributes attr;
2144 xwindow = g_new0 (GstXWindow, 1);
2145 xwindow->win = xwindow_id;
2147 /* Set the event we want to receive and create a GC */
2148 g_mutex_lock (xvimagesink->x_lock);
2150 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2152 xwindow->width = attr.width;
2153 xwindow->height = attr.height;
2154 xwindow->internal = FALSE;
2155 if (!xvimagesink->have_render_rect) {
2156 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2157 xvimagesink->render_rect.w = attr.width;
2158 xvimagesink->render_rect.h = attr.height;
2160 if (xvimagesink->handle_events) {
2161 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2162 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2166 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2167 xwindow->win, 0, NULL);
2168 g_mutex_unlock (xvimagesink->x_lock);
2172 xvimagesink->xwindow = xwindow;
2174 g_mutex_unlock (xvimagesink->flow_lock);
2178 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2180 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2182 GST_DEBUG ("doing expose");
2183 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2184 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2188 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2189 gboolean handle_events)
2191 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2193 xvimagesink->handle_events = handle_events;
2195 g_mutex_lock (xvimagesink->flow_lock);
2197 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2198 g_mutex_unlock (xvimagesink->flow_lock);
2202 g_mutex_lock (xvimagesink->x_lock);
2204 if (handle_events) {
2205 if (xvimagesink->xwindow->internal) {
2206 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2207 ExposureMask | StructureNotifyMask | PointerMotionMask |
2208 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2210 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2211 ExposureMask | StructureNotifyMask | PointerMotionMask |
2212 KeyPressMask | KeyReleaseMask);
2215 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2218 g_mutex_unlock (xvimagesink->x_lock);
2220 g_mutex_unlock (xvimagesink->flow_lock);
2224 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2225 gint width, gint height)
2227 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2229 /* FIXME: how about some locking? */
2230 if (width >= 0 && height >= 0) {
2231 xvimagesink->render_rect.x = x;
2232 xvimagesink->render_rect.y = y;
2233 xvimagesink->render_rect.w = width;
2234 xvimagesink->render_rect.h = height;
2235 xvimagesink->have_render_rect = TRUE;
2237 xvimagesink->render_rect.x = 0;
2238 xvimagesink->render_rect.y = 0;
2239 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2240 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2241 xvimagesink->have_render_rect = FALSE;
2246 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2248 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2249 iface->expose = gst_xvimagesink_expose;
2250 iface->handle_events = gst_xvimagesink_set_event_handling;
2251 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2254 static const GList *
2255 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2257 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2259 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2261 if (xvimagesink->xcontext)
2262 return xvimagesink->xcontext->channels_list;
2268 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2269 GstColorBalanceChannel * channel, gint value)
2271 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2273 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2274 g_return_if_fail (channel->label != NULL);
2276 xvimagesink->cb_changed = TRUE;
2278 /* Normalize val to [-1000, 1000] */
2279 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2280 (double) (channel->max_value - channel->min_value));
2282 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2283 xvimagesink->hue = value;
2284 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2285 xvimagesink->saturation = value;
2286 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2287 xvimagesink->contrast = value;
2288 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2289 xvimagesink->brightness = value;
2291 g_warning ("got an unknown channel %s", channel->label);
2295 gst_xvimagesink_update_colorbalance (xvimagesink);
2299 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2300 GstColorBalanceChannel * channel)
2302 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2305 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2306 g_return_val_if_fail (channel->label != NULL, 0);
2308 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2309 value = xvimagesink->hue;
2310 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2311 value = xvimagesink->saturation;
2312 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2313 value = xvimagesink->contrast;
2314 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2315 value = xvimagesink->brightness;
2317 g_warning ("got an unknown channel %s", channel->label);
2320 /* Normalize val to [channel->min_value, channel->max_value] */
2321 value = channel->min_value + (channel->max_value - channel->min_value) *
2322 (value + 1000) / 2000;
2327 static GstColorBalanceType
2328 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
2330 return GST_COLOR_BALANCE_HARDWARE;
2334 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2336 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2337 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2338 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2339 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
2343 static const GList *
2344 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2346 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2347 static GList *list = NULL;
2350 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2352 g_list_append (list, g_object_class_find_property (klass,
2353 "autopaint-colorkey"));
2355 g_list_append (list, g_object_class_find_property (klass,
2358 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2365 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2366 guint prop_id, const GParamSpec * pspec)
2368 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2372 case PROP_AUTOPAINT_COLORKEY:
2373 case PROP_DOUBLE_BUFFER:
2375 GST_DEBUG_OBJECT (xvimagesink,
2376 "probing device list and get capabilities");
2377 if (!xvimagesink->xcontext) {
2378 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2379 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2383 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2389 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2390 guint prop_id, const GParamSpec * pspec)
2392 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2393 gboolean ret = FALSE;
2397 case PROP_AUTOPAINT_COLORKEY:
2398 case PROP_DOUBLE_BUFFER:
2400 if (xvimagesink->xcontext != NULL) {
2407 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2414 static GValueArray *
2415 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2416 guint prop_id, const GParamSpec * pspec)
2418 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2419 GValueArray *array = NULL;
2421 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2422 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2431 GValue value = { 0 };
2433 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2434 g_value_init (&value, G_TYPE_STRING);
2436 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2437 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2439 g_value_set_string (&value, adaptor_id_s);
2440 g_value_array_append (array, &value);
2441 g_free (adaptor_id_s);
2443 g_value_unset (&value);
2446 case PROP_AUTOPAINT_COLORKEY:
2447 if (xvimagesink->have_autopaint_colorkey) {
2448 GValue value = { 0 };
2450 array = g_value_array_new (2);
2451 g_value_init (&value, G_TYPE_BOOLEAN);
2452 g_value_set_boolean (&value, FALSE);
2453 g_value_array_append (array, &value);
2454 g_value_set_boolean (&value, TRUE);
2455 g_value_array_append (array, &value);
2456 g_value_unset (&value);
2459 case PROP_DOUBLE_BUFFER:
2460 if (xvimagesink->have_double_buffer) {
2461 GValue value = { 0 };
2463 array = g_value_array_new (2);
2464 g_value_init (&value, G_TYPE_BOOLEAN);
2465 g_value_set_boolean (&value, FALSE);
2466 g_value_array_append (array, &value);
2467 g_value_set_boolean (&value, TRUE);
2468 g_value_array_append (array, &value);
2469 g_value_unset (&value);
2473 if (xvimagesink->have_colorkey) {
2474 GValue value = { 0 };
2476 array = g_value_array_new (1);
2477 g_value_init (&value, GST_TYPE_INT_RANGE);
2478 gst_value_set_int_range (&value, 0, 0xffffff);
2479 g_value_array_append (array, &value);
2480 g_value_unset (&value);
2484 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2493 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2496 iface->get_properties = gst_xvimagesink_probe_get_properties;
2497 iface->probe_property = gst_xvimagesink_probe_probe_property;
2498 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2499 iface->get_values = gst_xvimagesink_probe_get_values;
2503 /* =========================================== */
2505 /* Init & Class init */
2507 /* =========================================== */
2510 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2511 const GValue * value, GParamSpec * pspec)
2513 GstXvImageSink *xvimagesink;
2515 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2517 xvimagesink = GST_XVIMAGESINK (object);
2521 xvimagesink->hue = g_value_get_int (value);
2522 xvimagesink->cb_changed = TRUE;
2523 gst_xvimagesink_update_colorbalance (xvimagesink);
2526 xvimagesink->contrast = g_value_get_int (value);
2527 xvimagesink->cb_changed = TRUE;
2528 gst_xvimagesink_update_colorbalance (xvimagesink);
2530 case PROP_BRIGHTNESS:
2531 xvimagesink->brightness = g_value_get_int (value);
2532 xvimagesink->cb_changed = TRUE;
2533 gst_xvimagesink_update_colorbalance (xvimagesink);
2535 case PROP_SATURATION:
2536 xvimagesink->saturation = g_value_get_int (value);
2537 xvimagesink->cb_changed = TRUE;
2538 gst_xvimagesink_update_colorbalance (xvimagesink);
2541 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2543 case PROP_SYNCHRONOUS:
2544 xvimagesink->synchronous = g_value_get_boolean (value);
2545 if (xvimagesink->xcontext) {
2546 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2547 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2548 xvimagesink->synchronous ? "TRUE" : "FALSE");
2551 case PROP_PIXEL_ASPECT_RATIO:
2552 g_free (xvimagesink->par);
2553 xvimagesink->par = g_new0 (GValue, 1);
2554 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2555 if (!g_value_transform (value, xvimagesink->par)) {
2556 g_warning ("Could not transform string to aspect ratio");
2557 gst_value_set_fraction (xvimagesink->par, 1, 1);
2559 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2560 gst_value_get_fraction_numerator (xvimagesink->par),
2561 gst_value_get_fraction_denominator (xvimagesink->par));
2563 case PROP_FORCE_ASPECT_RATIO:
2564 xvimagesink->keep_aspect = g_value_get_boolean (value);
2566 case PROP_HANDLE_EVENTS:
2567 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2568 g_value_get_boolean (value));
2569 gst_xvimagesink_manage_event_thread (xvimagesink);
2572 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2574 case PROP_HANDLE_EXPOSE:
2575 xvimagesink->handle_expose = g_value_get_boolean (value);
2576 gst_xvimagesink_manage_event_thread (xvimagesink);
2578 case PROP_DOUBLE_BUFFER:
2579 xvimagesink->double_buffer = g_value_get_boolean (value);
2581 case PROP_AUTOPAINT_COLORKEY:
2582 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2585 xvimagesink->colorkey = g_value_get_int (value);
2587 case PROP_DRAW_BORDERS:
2588 xvimagesink->draw_borders = g_value_get_boolean (value);
2591 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2597 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2598 GValue * value, GParamSpec * pspec)
2600 GstXvImageSink *xvimagesink;
2602 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2604 xvimagesink = GST_XVIMAGESINK (object);
2608 g_value_set_int (value, xvimagesink->hue);
2611 g_value_set_int (value, xvimagesink->contrast);
2613 case PROP_BRIGHTNESS:
2614 g_value_set_int (value, xvimagesink->brightness);
2616 case PROP_SATURATION:
2617 g_value_set_int (value, xvimagesink->saturation);
2620 g_value_set_string (value, xvimagesink->display_name);
2622 case PROP_SYNCHRONOUS:
2623 g_value_set_boolean (value, xvimagesink->synchronous);
2625 case PROP_PIXEL_ASPECT_RATIO:
2626 if (xvimagesink->par)
2627 g_value_transform (xvimagesink->par, value);
2629 case PROP_FORCE_ASPECT_RATIO:
2630 g_value_set_boolean (value, xvimagesink->keep_aspect);
2632 case PROP_HANDLE_EVENTS:
2633 g_value_set_boolean (value, xvimagesink->handle_events);
2637 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2639 g_value_set_string (value, adaptor_no_s);
2640 g_free (adaptor_no_s);
2643 case PROP_DEVICE_NAME:
2644 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2645 g_value_set_string (value,
2646 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2648 g_value_set_string (value, NULL);
2651 case PROP_HANDLE_EXPOSE:
2652 g_value_set_boolean (value, xvimagesink->handle_expose);
2654 case PROP_DOUBLE_BUFFER:
2655 g_value_set_boolean (value, xvimagesink->double_buffer);
2657 case PROP_AUTOPAINT_COLORKEY:
2658 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2661 g_value_set_int (value, xvimagesink->colorkey);
2663 case PROP_DRAW_BORDERS:
2664 g_value_set_boolean (value, xvimagesink->draw_borders);
2666 case PROP_WINDOW_WIDTH:
2667 if (xvimagesink->xwindow)
2668 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2670 g_value_set_uint64 (value, 0);
2672 case PROP_WINDOW_HEIGHT:
2673 if (xvimagesink->xwindow)
2674 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2676 g_value_set_uint64 (value, 0);
2679 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2685 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2689 GST_OBJECT_LOCK (xvimagesink);
2690 xvimagesink->running = FALSE;
2691 /* grab thread and mark it as NULL */
2692 thread = xvimagesink->event_thread;
2693 xvimagesink->event_thread = NULL;
2694 GST_OBJECT_UNLOCK (xvimagesink);
2696 /* Wait for our event thread to finish before we clean up our stuff. */
2698 g_thread_join (thread);
2700 if (xvimagesink->cur_image) {
2701 gst_buffer_unref (xvimagesink->cur_image);
2702 xvimagesink->cur_image = NULL;
2705 g_mutex_lock (xvimagesink->flow_lock);
2707 if (xvimagesink->pool) {
2708 gst_object_unref (xvimagesink->pool);
2709 xvimagesink->pool = NULL;
2712 if (xvimagesink->xwindow) {
2713 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2714 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2715 xvimagesink->xwindow = NULL;
2717 g_mutex_unlock (xvimagesink->flow_lock);
2719 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2720 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2721 xvimagesink->have_render_rect = FALSE;
2723 gst_xvimagesink_xcontext_clear (xvimagesink);
2726 /* Finalize is called only once, dispose can be called multiple times.
2727 * We use mutexes and don't reset stuff to NULL here so let's register
2730 gst_xvimagesink_finalize (GObject * object)
2732 GstXvImageSink *xvimagesink;
2734 xvimagesink = GST_XVIMAGESINK (object);
2736 gst_xvimagesink_reset (xvimagesink);
2738 if (xvimagesink->display_name) {
2739 g_free (xvimagesink->display_name);
2740 xvimagesink->display_name = NULL;
2743 if (xvimagesink->par) {
2744 g_free (xvimagesink->par);
2745 xvimagesink->par = NULL;
2747 if (xvimagesink->x_lock) {
2748 g_mutex_free (xvimagesink->x_lock);
2749 xvimagesink->x_lock = NULL;
2751 if (xvimagesink->flow_lock) {
2752 g_mutex_free (xvimagesink->flow_lock);
2753 xvimagesink->flow_lock = NULL;
2756 g_free (xvimagesink->media_title);
2758 G_OBJECT_CLASS (parent_class)->finalize (object);
2762 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2764 xvimagesink->display_name = NULL;
2765 xvimagesink->adaptor_no = 0;
2766 xvimagesink->xcontext = NULL;
2767 xvimagesink->xwindow = NULL;
2768 xvimagesink->cur_image = NULL;
2770 xvimagesink->hue = xvimagesink->saturation = 0;
2771 xvimagesink->contrast = xvimagesink->brightness = 0;
2772 xvimagesink->cb_changed = FALSE;
2774 xvimagesink->fps_n = 0;
2775 xvimagesink->fps_d = 0;
2776 xvimagesink->video_width = 0;
2777 xvimagesink->video_height = 0;
2779 xvimagesink->x_lock = g_mutex_new ();
2780 xvimagesink->flow_lock = g_mutex_new ();
2782 xvimagesink->pool = NULL;
2784 xvimagesink->synchronous = FALSE;
2785 xvimagesink->double_buffer = TRUE;
2786 xvimagesink->running = FALSE;
2787 xvimagesink->keep_aspect = TRUE;
2788 xvimagesink->handle_events = TRUE;
2789 xvimagesink->par = NULL;
2790 xvimagesink->handle_expose = TRUE;
2791 xvimagesink->autopaint_colorkey = TRUE;
2793 /* on 16bit displays this becomes r,g,b = 1,2,3
2794 * on 24bit displays this becomes r,g,b = 8,8,16
2795 * as a port atom value
2797 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2798 xvimagesink->draw_borders = TRUE;
2802 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2804 GObjectClass *gobject_class;
2805 GstElementClass *gstelement_class;
2806 GstBaseSinkClass *gstbasesink_class;
2807 GstVideoSinkClass *videosink_class;
2809 gobject_class = (GObjectClass *) klass;
2810 gstelement_class = (GstElementClass *) klass;
2811 gstbasesink_class = (GstBaseSinkClass *) klass;
2812 videosink_class = (GstVideoSinkClass *) klass;
2814 parent_class = g_type_class_peek_parent (klass);
2816 gobject_class->set_property = gst_xvimagesink_set_property;
2817 gobject_class->get_property = gst_xvimagesink_get_property;
2819 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2820 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2821 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2822 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2823 g_param_spec_int ("brightness", "Brightness",
2824 "The brightness of the video", -1000, 1000, 0,
2825 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2826 g_object_class_install_property (gobject_class, PROP_HUE,
2827 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2828 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2829 g_object_class_install_property (gobject_class, PROP_SATURATION,
2830 g_param_spec_int ("saturation", "Saturation",
2831 "The saturation of the video", -1000, 1000, 0,
2832 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2833 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2834 g_param_spec_string ("display", "Display", "X Display name", NULL,
2835 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2836 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2837 g_param_spec_boolean ("synchronous", "Synchronous",
2838 "When enabled, runs the X display in synchronous mode. "
2839 "(unrelated to A/V sync, used only for debugging)", FALSE,
2840 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2841 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2842 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2843 "The pixel aspect ratio of the device", "1/1",
2844 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2845 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2846 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2847 "When enabled, scaling will respect original aspect ratio", TRUE,
2848 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2849 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2850 g_param_spec_boolean ("handle-events", "Handle XEvents",
2851 "When enabled, XEvents will be selected and handled", TRUE,
2852 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2853 g_object_class_install_property (gobject_class, PROP_DEVICE,
2854 g_param_spec_string ("device", "Adaptor number",
2855 "The number of the video adaptor", "0",
2856 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2857 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2858 g_param_spec_string ("device-name", "Adaptor name",
2859 "The name of the video adaptor", NULL,
2860 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2862 * GstXvImageSink:handle-expose
2864 * When enabled, the current frame will always be drawn in response to X
2869 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2870 g_param_spec_boolean ("handle-expose", "Handle expose",
2872 "the current frame will always be drawn in response to X Expose "
2873 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2876 * GstXvImageSink:double-buffer
2878 * Whether to double-buffer the output.
2882 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2883 g_param_spec_boolean ("double-buffer", "Double-buffer",
2884 "Whether to double-buffer the output", TRUE,
2885 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2887 * GstXvImageSink:autopaint-colorkey
2889 * Whether to autofill overlay with colorkey
2893 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2894 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2895 "Whether to autofill overlay with colorkey", TRUE,
2896 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2898 * GstXvImageSink:colorkey
2900 * Color to use for the overlay mask.
2904 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2905 g_param_spec_int ("colorkey", "Colorkey",
2906 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2907 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2910 * GstXvImageSink:draw-borders
2912 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2913 * unused parts of the video area.
2917 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2918 g_param_spec_boolean ("draw-borders", "Colorkey",
2919 "Draw black borders to fill unused area in force-aspect-ratio mode",
2920 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2923 * GstXvImageSink:window-width
2925 * Actual width of the video window.
2929 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2930 g_param_spec_uint64 ("window-width", "window-width",
2931 "Width of the window", 0, G_MAXUINT64, 0,
2932 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2935 * GstXvImageSink:window-height
2937 * Actual height of the video window.
2941 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2942 g_param_spec_uint64 ("window-height", "window-height",
2943 "Height of the window", 0, G_MAXUINT64, 0,
2944 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2946 gobject_class->finalize = gst_xvimagesink_finalize;
2948 gst_element_class_set_static_metadata (gstelement_class,
2949 "Video sink", "Sink/Video",
2950 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2952 gst_element_class_add_pad_template (gstelement_class,
2953 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2955 gstelement_class->change_state =
2956 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2958 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2959 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2960 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2961 gstbasesink_class->propose_allocation =
2962 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2963 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2965 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);