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., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, 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 #GstVideoOverlay interface and will then render
29 * video frames in this drawable. If no Window ID was provided by the
30 * application, the element will create its own internal window and render
34 * <title>Scaling</title>
36 * The XVideo extension, when it's available, handles hardware accelerated
37 * scaling of video frames. This means that the element will just accept
38 * incoming video frames no matter their geometry and will then put them to the
39 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
40 * property it is possible to enforce scaling with a constant aspect ratio,
41 * which means drawing black borders around the video frame.
45 * <title>Events</title>
47 * XvImageSink creates a thread to handle events coming from the drawable. There
48 * are several kind of events that can be grouped in 2 big categories: input
49 * events and window state related events. Input events will be translated to
50 * navigation events and pushed upstream for other elements to react on them.
51 * This includes events such as pointer moves, key press/release, clicks etc...
52 * Other events are used to handle the drawable appearance even when the data
53 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
54 * paused, it will receive expose events from the drawable and draw the latest
55 * frame with correct borders/aspect-ratio.
59 * <title>Pixel aspect ratio</title>
61 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
62 * the display specified in the #GstXvImageSink:display property or the
63 * default display if nothing specified. Once this connection is open it will
64 * inspect the display configuration including the physical display geometry and
65 * then calculate the pixel aspect ratio. When receiving video frames with a
66 * different pixel aspect ratio, XvImageSink will use hardware scaling to
67 * display the video frames correctly on display's pixel aspect ratio.
68 * Sometimes the calculated pixel aspect ratio can be wrong, it is
69 * then possible to enforce a specific pixel aspect ratio using the
70 * #GstXvImageSink:pixel-aspect-ratio property.
74 * <title>Examples</title>
76 * gst-launch -v videotestsrc ! xvimagesink
77 * ]| A pipeline to test hardware scaling.
78 * When the test video signal appears you can resize the window and see that
79 * video frames are scaled through hardware (no extra CPU cost).
81 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
82 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
83 * You can observe the borders drawn around the scaled image respecting aspect
86 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
87 * ]| A pipeline to test navigation events.
88 * While moving the mouse pointer over the test signal you will see a black box
89 * following the mouse pointer. If you press the mouse button somewhere on the
90 * video and release it somewhere else a green box will appear where you pressed
91 * the button and a red one where you released it. (The navigationtest element
92 * is part of gst-plugins-good.) You can observe here that even if the images
93 * are scaled through hardware the pointer coordinates are converted back to the
94 * original video frame geometry so that the box can be drawn to the correct
95 * position. This also handles borders correctly, limiting coordinates to the
98 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
99 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
100 * videotestsrc, in most cases the pixel aspect ratio of the display will be
101 * 1/1. This means that XvImageSink will have to do the scaling to convert
102 * incoming frames to a size that will match the display pixel aspect ratio
103 * (from 320x240 to 320x180 in this case). Note that you might have to escape
104 * some characters for your shell like '\(fraction\)'.
106 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
107 * ]| Demonstrates how to use the colorbalance interface.
111 /* for developers: there are two useful tools : xvinfo and xvattr */
118 #include <gst/video/navigation.h>
119 #include <gst/video/videooverlay.h>
120 #include <gst/video/colorbalance.h>
121 /* Helper functions */
122 #include <gst/video/gstvideometa.h>
125 #include "xvimagesink.h"
127 /* Debugging category */
128 #include <gst/gstinfo.h>
130 /* for XkbKeycodeToKeysym */
131 #include <X11/XKBlib.h>
133 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
134 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
135 #define GST_CAT_DEFAULT gst_debug_xvimagesink
140 unsigned long functions;
141 unsigned long decorations;
143 unsigned long status;
145 MotifWmHints, MwmHints;
147 #define MWM_HINTS_DECORATIONS (1L << 1)
149 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
150 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
152 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
154 /* Default template - initiated with class struct to allow gst-register to work
156 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
157 GST_STATIC_PAD_TEMPLATE ("sink",
160 GST_STATIC_CAPS ("video/x-raw, "
161 "framerate = (fraction) [ 0, MAX ], "
162 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
174 PROP_PIXEL_ASPECT_RATIO,
175 PROP_FORCE_ASPECT_RATIO,
181 PROP_AUTOPAINT_COLORKEY,
188 /* ============================================================= */
192 /* ============================================================= */
194 /* =========================================== */
196 /* Object typing & Creation */
198 /* =========================================== */
199 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
200 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
202 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
204 #define gst_xvimagesink_parent_class parent_class
205 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
206 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
207 gst_xvimagesink_navigation_init);
208 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
209 gst_xvimagesink_video_overlay_init);
210 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
211 gst_xvimagesink_colorbalance_init));
214 /* ============================================================= */
216 /* Private Methods */
218 /* ============================================================= */
221 /* We are called with the x_lock taken */
223 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
224 GstXWindow * xwindow, GstVideoRectangle rect)
228 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
229 g_return_if_fail (xwindow != NULL);
231 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
232 xvimagesink->xcontext->black);
235 if (rect.x > xvimagesink->render_rect.x) {
236 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
237 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
238 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
242 t1 = rect.x + rect.w;
243 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
245 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
246 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
250 if (rect.y > xvimagesink->render_rect.y) {
251 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
252 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
253 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
257 t1 = rect.y + rect.h;
258 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
260 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
261 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
265 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
266 * if no window was available */
268 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
270 GstXvImageMeta *meta;
271 GstVideoCropMeta *crop;
272 GstVideoRectangle result;
273 gboolean draw_border = FALSE;
274 GstVideoRectangle src, dst;
276 /* We take the flow_lock. If expose is in there we don't want to run
277 concurrently from the data flow thread */
278 g_mutex_lock (&xvimagesink->flow_lock);
280 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
281 g_mutex_unlock (&xvimagesink->flow_lock);
285 /* Draw borders when displaying the first frame. After this
286 draw borders only on expose event or after a size change. */
287 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
291 /* Store a reference to the last image we put, lose the previous one */
292 if (xvimage && xvimagesink->cur_image != xvimage) {
293 if (xvimagesink->cur_image) {
294 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
295 gst_buffer_unref (xvimagesink->cur_image);
297 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
298 xvimagesink->cur_image = gst_buffer_ref (xvimage);
301 /* Expose sends a NULL image, we take the latest frame */
303 if (xvimagesink->cur_image) {
305 xvimage = xvimagesink->cur_image;
307 g_mutex_unlock (&xvimagesink->flow_lock);
312 meta = gst_buffer_get_xvimage_meta (xvimage);
314 crop = gst_buffer_get_video_crop_meta (xvimage);
317 src.x = crop->x + meta->x;
318 src.y = crop->y + meta->y;
320 src.h = crop->height;
321 GST_LOG_OBJECT (xvimagesink,
322 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
327 src.h = meta->height;
330 if (xvimagesink->keep_aspect) {
333 /* We take the size of the source material as it was negotiated and
334 * corrected for DAR. This size can be different from the cropped size in
335 * which case the image will be scaled to fit the negotiated size. */
336 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
337 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
338 dst.w = xvimagesink->render_rect.w;
339 dst.h = xvimagesink->render_rect.h;
341 gst_video_sink_center_rect (s, dst, &result, TRUE);
342 result.x += xvimagesink->render_rect.x;
343 result.y += xvimagesink->render_rect.y;
345 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
348 g_mutex_lock (&xvimagesink->x_lock);
350 if (draw_border && xvimagesink->draw_borders) {
351 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
353 xvimagesink->redraw_border = FALSE;
356 if (xvimagesink->xcontext->use_xshm) {
357 GST_LOG_OBJECT (xvimagesink,
358 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
359 GST_PTR_FORMAT, meta->width, meta->height,
360 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
362 XvShmPutImage (xvimagesink->xcontext->disp,
363 xvimagesink->xcontext->xv_port_id,
364 xvimagesink->xwindow->win,
365 xvimagesink->xwindow->gc, meta->xvimage,
366 src.x, src.y, src.w, src.h,
367 result.x, result.y, result.w, result.h, FALSE);
369 #endif /* HAVE_XSHM */
371 XvPutImage (xvimagesink->xcontext->disp,
372 xvimagesink->xcontext->xv_port_id,
373 xvimagesink->xwindow->win,
374 xvimagesink->xwindow->gc, meta->xvimage,
375 src.x, src.y, src.w, src.h, result.x, result.y, result.w, result.h);
378 XSync (xvimagesink->xcontext->disp, FALSE);
380 g_mutex_unlock (&xvimagesink->x_lock);
382 g_mutex_unlock (&xvimagesink->flow_lock);
388 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
391 Atom hints_atom = None;
394 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
395 g_return_val_if_fail (window != NULL, FALSE);
397 g_mutex_lock (&xvimagesink->x_lock);
399 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
401 if (hints_atom == None) {
402 g_mutex_unlock (&xvimagesink->x_lock);
406 hints = g_malloc0 (sizeof (MotifWmHints));
408 hints->flags |= MWM_HINTS_DECORATIONS;
409 hints->decorations = 1 << 0;
411 XChangeProperty (xvimagesink->xcontext->disp, window->win,
412 hints_atom, hints_atom, 32, PropModeReplace,
413 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
415 XSync (xvimagesink->xcontext->disp, FALSE);
417 g_mutex_unlock (&xvimagesink->x_lock);
425 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
426 GstXWindow * xwindow, const gchar * media_title)
429 g_free (xvimagesink->media_title);
430 xvimagesink->media_title = g_strdup (media_title);
433 /* we have a window */
434 if (xwindow->internal) {
435 XTextProperty xproperty;
436 const gchar *app_name;
437 const gchar *title = NULL;
438 gchar *title_mem = NULL;
440 /* set application name as a title */
441 app_name = g_get_application_name ();
443 if (app_name && xvimagesink->media_title) {
444 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
446 } else if (app_name) {
448 } else if (xvimagesink->media_title) {
449 title = xvimagesink->media_title;
453 if ((XStringListToTextProperty (((char **) &title), 1,
455 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
456 XFree (xproperty.value);
465 /* This function handles a GstXWindow creation
466 * The width and height are the actual pixel size on the display */
468 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
469 gint width, gint height)
471 GstXWindow *xwindow = NULL;
474 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
476 xwindow = g_new0 (GstXWindow, 1);
478 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
479 xvimagesink->render_rect.w = width;
480 xvimagesink->render_rect.h = height;
482 xwindow->width = width;
483 xwindow->height = height;
484 xwindow->internal = TRUE;
486 g_mutex_lock (&xvimagesink->x_lock);
488 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
489 xvimagesink->xcontext->root,
490 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
492 /* We have to do that to prevent X from redrawing the background on
493 * ConfigureNotify. This takes away flickering of video when resizing. */
494 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
496 /* set application name as a title */
497 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
499 if (xvimagesink->handle_events) {
502 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
503 StructureNotifyMask | PointerMotionMask | KeyPressMask |
504 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
506 /* Tell the window manager we'd like delete client messages instead of
508 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
509 "WM_DELETE_WINDOW", True);
510 if (wm_delete != None) {
511 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
516 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
517 xwindow->win, 0, &values);
519 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
521 XSync (xvimagesink->xcontext->disp, FALSE);
523 g_mutex_unlock (&xvimagesink->x_lock);
525 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
527 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
533 /* This function destroys a GstXWindow */
535 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
536 GstXWindow * xwindow)
538 g_return_if_fail (xwindow != NULL);
539 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
541 g_mutex_lock (&xvimagesink->x_lock);
543 /* If we did not create that window we just free the GC and let it live */
544 if (xwindow->internal)
545 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
547 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
549 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
551 XSync (xvimagesink->xcontext->disp, FALSE);
553 g_mutex_unlock (&xvimagesink->x_lock);
559 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
561 XWindowAttributes attr;
563 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
565 /* Update the window geometry */
566 g_mutex_lock (&xvimagesink->x_lock);
567 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
568 g_mutex_unlock (&xvimagesink->x_lock);
572 XGetWindowAttributes (xvimagesink->xcontext->disp,
573 xvimagesink->xwindow->win, &attr);
575 xvimagesink->xwindow->width = attr.width;
576 xvimagesink->xwindow->height = attr.height;
578 if (!xvimagesink->have_render_rect) {
579 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
580 xvimagesink->render_rect.w = attr.width;
581 xvimagesink->render_rect.h = attr.height;
584 g_mutex_unlock (&xvimagesink->x_lock);
588 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
589 GstXWindow * xwindow)
591 g_return_if_fail (xwindow != NULL);
592 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
594 g_mutex_lock (&xvimagesink->x_lock);
596 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
599 XSync (xvimagesink->xcontext->disp, FALSE);
601 g_mutex_unlock (&xvimagesink->x_lock);
604 /* This function commits our internal colorbalance settings to our grabbed Xv
605 port. If the xcontext is not initialized yet it simply returns */
607 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
609 GList *channels = NULL;
611 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
613 /* If we haven't initialized the X context we can't update anything */
614 if (xvimagesink->xcontext == NULL)
617 /* Don't set the attributes if they haven't been changed, to avoid
618 * rounding errors changing the values */
619 if (!xvimagesink->cb_changed)
622 /* For each channel of the colorbalance we calculate the correct value
623 doing range conversion and then set the Xv port attribute to match our
625 channels = xvimagesink->xcontext->channels_list;
628 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
629 GstColorBalanceChannel *channel = NULL;
632 gdouble convert_coef;
634 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
635 g_object_ref (channel);
637 /* Our range conversion coef */
638 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
640 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
641 value = xvimagesink->hue;
642 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
643 value = xvimagesink->saturation;
644 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
645 value = xvimagesink->contrast;
646 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
647 value = xvimagesink->brightness;
649 g_warning ("got an unknown channel %s", channel->label);
650 g_object_unref (channel);
654 /* Committing to Xv port */
655 g_mutex_lock (&xvimagesink->x_lock);
657 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
658 if (prop_atom != None) {
661 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
662 XvSetPortAttribute (xvimagesink->xcontext->disp,
663 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
665 g_mutex_unlock (&xvimagesink->x_lock);
667 g_object_unref (channel);
669 channels = g_list_next (channels);
673 /* This function handles XEvents that might be in the queue. It generates
674 GstEvent that will be sent upstream in the pipeline to handle interactivity
675 and navigation. It will also listen for configure events on the window to
676 trigger caps renegotiation so on the fly software scaling can work. */
678 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
681 guint pointer_x = 0, pointer_y = 0;
682 gboolean pointer_moved = FALSE;
683 gboolean exposed = FALSE, configured = FALSE;
685 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
687 /* Handle Interaction, produces navigation events */
689 /* We get all pointer motion events, only the last position is
691 g_mutex_lock (&xvimagesink->flow_lock);
692 g_mutex_lock (&xvimagesink->x_lock);
693 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
694 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
695 g_mutex_unlock (&xvimagesink->x_lock);
696 g_mutex_unlock (&xvimagesink->flow_lock);
700 pointer_x = e.xmotion.x;
701 pointer_y = e.xmotion.y;
702 pointer_moved = TRUE;
707 g_mutex_lock (&xvimagesink->flow_lock);
708 g_mutex_lock (&xvimagesink->x_lock);
712 g_mutex_unlock (&xvimagesink->x_lock);
713 g_mutex_unlock (&xvimagesink->flow_lock);
715 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
716 pointer_x, pointer_y);
717 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
718 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
720 g_mutex_lock (&xvimagesink->flow_lock);
721 g_mutex_lock (&xvimagesink->x_lock);
724 /* We get all events on our window to throw them upstream */
725 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
726 xvimagesink->xwindow->win,
727 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
730 const char *key_str = NULL;
732 /* We lock only for the X function call */
733 g_mutex_unlock (&xvimagesink->x_lock);
734 g_mutex_unlock (&xvimagesink->flow_lock);
738 /* Mouse button pressed over our window. We send upstream
739 events for interactivity/navigation */
740 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
741 e.xbutton.button, e.xbutton.x, e.xbutton.y);
742 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
743 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
746 /* Mouse button released over our window. We send upstream
747 events for interactivity/navigation */
748 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
749 e.xbutton.button, e.xbutton.x, e.xbutton.y);
750 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
751 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
755 /* Key pressed/released over our window. We send upstream
756 events for interactivity/navigation */
757 g_mutex_lock (&xvimagesink->x_lock);
758 keysym = XkbKeycodeToKeysym (xvimagesink->xcontext->disp,
759 e.xkey.keycode, 0, 0);
760 if (keysym != NoSymbol) {
761 key_str = XKeysymToString (keysym);
765 g_mutex_unlock (&xvimagesink->x_lock);
766 GST_DEBUG_OBJECT (xvimagesink,
767 "key %d pressed over window at %d,%d (%s)",
768 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
769 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
770 e.type == KeyPress ? "key-press" : "key-release", key_str);
773 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
776 g_mutex_lock (&xvimagesink->flow_lock);
777 g_mutex_lock (&xvimagesink->x_lock);
781 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
782 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
787 case ConfigureNotify:
788 g_mutex_unlock (&xvimagesink->x_lock);
789 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
790 g_mutex_lock (&xvimagesink->x_lock);
798 if (xvimagesink->handle_expose && (exposed || configured)) {
799 g_mutex_unlock (&xvimagesink->x_lock);
800 g_mutex_unlock (&xvimagesink->flow_lock);
802 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
804 g_mutex_lock (&xvimagesink->flow_lock);
805 g_mutex_lock (&xvimagesink->x_lock);
808 /* Handle Display events */
809 while (XPending (xvimagesink->xcontext->disp)) {
810 XNextEvent (xvimagesink->xcontext->disp, &e);
816 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
817 "WM_DELETE_WINDOW", True);
818 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
819 /* Handle window deletion by posting an error on the bus */
820 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
821 ("Output window was closed"), (NULL));
823 g_mutex_unlock (&xvimagesink->x_lock);
824 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
825 xvimagesink->xwindow = NULL;
826 g_mutex_lock (&xvimagesink->x_lock);
835 g_mutex_unlock (&xvimagesink->x_lock);
836 g_mutex_unlock (&xvimagesink->flow_lock);
840 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
841 XvAdaptorInfo * adaptors, int adaptor_no)
846 /* Do we support XvImageMask ? */
847 if (!(adaptors[adaptor_no].type & XvImageMask)) {
848 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
849 adaptors[adaptor_no].name);
853 /* We found such an adaptor, looking for an available port */
854 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
855 /* We try to grab the port */
856 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
857 if (Success == res) {
858 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
859 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
860 adaptors[adaptor_no].num_ports);
862 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
863 adaptors[adaptor_no].name, res);
868 /* This function generates a caps with all supported format by the first
869 Xv grabable port we find. We store each one of the supported formats in a
870 format list and append the format to a newly created caps that we return
871 If this function does not return NULL because of an error, it also grabs
872 the port via XvGrabPort */
874 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
875 GstXContext * xcontext)
878 XvAdaptorInfo *adaptors;
880 XvImageFormatValues *formats = NULL;
882 XvEncodingInfo *encodings = NULL;
883 gulong max_w = G_MAXINT, max_h = G_MAXINT;
884 GstCaps *caps = NULL;
885 GstCaps *rgb_caps = NULL;
887 g_return_val_if_fail (xcontext != NULL, NULL);
889 /* First let's check that XVideo extension is available */
890 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
891 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
892 ("Could not initialise Xv output"),
893 ("XVideo extension is not available"));
897 /* Then we get adaptors list */
898 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
899 &xcontext->nb_adaptors, &adaptors)) {
900 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
901 ("Could not initialise Xv output"),
902 ("Failed getting XV adaptors list"));
906 xcontext->xv_port_id = 0;
908 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
911 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
913 /* Now fill up our adaptor name array */
914 for (i = 0; i < xcontext->nb_adaptors; i++) {
915 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
918 if (xvimagesink->adaptor_no != -1 &&
919 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
920 /* Find xv port from user defined adaptor */
921 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
922 xvimagesink->adaptor_no);
925 if (!xcontext->xv_port_id) {
926 /* Now search for an adaptor that supports XvImageMask */
927 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
928 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
929 xvimagesink->adaptor_no = i;
933 XvFreeAdaptorInfo (adaptors);
935 if (!xcontext->xv_port_id) {
936 xvimagesink->adaptor_no = -1;
937 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
938 ("Could not initialise Xv output"), ("No port available"));
942 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
945 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
946 xcontext->xv_port_id, &count);
947 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
948 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
949 static const char colorkey[] = "XV_COLORKEY";
951 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
953 xvimagesink->have_autopaint_colorkey = FALSE;
954 xvimagesink->have_double_buffer = FALSE;
955 xvimagesink->have_colorkey = FALSE;
957 for (i = 0; ((i < count) && todo); i++)
958 if (!strcmp (attr[i].name, autopaint)) {
959 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
961 /* turn on autopaint colorkey */
962 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
963 (xvimagesink->autopaint_colorkey ? 1 : 0));
965 xvimagesink->have_autopaint_colorkey = TRUE;
966 } else if (!strcmp (attr[i].name, dbl_buffer)) {
967 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
969 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
970 (xvimagesink->double_buffer ? 1 : 0));
972 xvimagesink->have_double_buffer = TRUE;
973 } else if (!strcmp (attr[i].name, colorkey)) {
974 /* Set the colorkey, default is something that is dark but hopefully
975 * won't randomly appear on the screen elsewhere (ie not black or greys)
976 * can be overridden by setting "colorkey" property
978 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
980 gboolean set_attr = TRUE;
983 /* set a colorkey in the right format RGB565/RGB888
984 * We only handle these 2 cases, because they're the only types of
985 * devices we've encountered. If we don't recognise it, leave it alone
987 cr = (xvimagesink->colorkey >> 16);
988 cg = (xvimagesink->colorkey >> 8) & 0xFF;
989 cb = (xvimagesink->colorkey) & 0xFF;
990 switch (xcontext->depth) {
991 case 16: /* RGB 565 */
995 ckey = (cr << 11) | (cg << 5) | cb;
998 case 32: /* RGB 888 / ARGB 8888 */
999 ckey = (cr << 16) | (cg << 8) | cb;
1002 GST_DEBUG_OBJECT (xvimagesink,
1003 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1010 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1011 (guint32) attr[i].max_value);
1012 GST_LOG_OBJECT (xvimagesink,
1013 "Setting color key for display depth %d to 0x%x",
1014 xcontext->depth, ckey);
1016 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1020 xvimagesink->have_colorkey = TRUE;
1026 /* Get the list of encodings supported by the adapter and look for the
1027 * XV_IMAGE encoding so we can determine the maximum width and height
1029 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1032 for (i = 0; i < nb_encodings; i++) {
1033 GST_LOG_OBJECT (xvimagesink,
1034 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1035 i, encodings[i].name, encodings[i].width, encodings[i].height,
1036 encodings[i].rate.numerator, encodings[i].rate.denominator);
1037 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1038 max_w = encodings[i].width;
1039 max_h = encodings[i].height;
1043 XvFreeEncodingInfo (encodings);
1045 /* We get all image formats supported by our port */
1046 formats = XvListImageFormats (xcontext->disp,
1047 xcontext->xv_port_id, &nb_formats);
1048 caps = gst_caps_new_empty ();
1049 for (i = 0; i < nb_formats; i++) {
1050 GstCaps *format_caps = NULL;
1051 gboolean is_rgb_format = FALSE;
1052 GstVideoFormat vformat;
1054 /* We set the image format of the xcontext to an existing one. This
1055 is just some valid image format for making our xshm calls check before
1056 caps negotiation really happens. */
1057 xcontext->im_format = formats[i].id;
1059 switch (formats[i].type) {
1062 XvImageFormatValues *fmt = &(formats[i]);
1066 (fmt->byte_order == LSBFirst ? G_LITTLE_ENDIAN : G_BIG_ENDIAN);
1068 vformat = gst_video_format_from_masks (fmt->depth, fmt->bits_per_pixel,
1069 endianness, fmt->red_mask, fmt->green_mask, fmt->blue_mask, 0);
1070 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1073 format_caps = gst_caps_new_simple ("video/x-raw",
1074 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1075 "width", GST_TYPE_INT_RANGE, 1, max_w,
1076 "height", GST_TYPE_INT_RANGE, 1, max_h,
1077 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1079 is_rgb_format = TRUE;
1084 vformat = gst_video_format_from_fourcc (formats[i].id);
1085 if (vformat == GST_VIDEO_FORMAT_UNKNOWN)
1088 format_caps = gst_caps_new_simple ("video/x-raw",
1089 "format", G_TYPE_STRING, gst_video_format_to_string (vformat),
1090 "width", GST_TYPE_INT_RANGE, 1, max_w,
1091 "height", GST_TYPE_INT_RANGE, 1, max_h,
1092 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1096 vformat = GST_VIDEO_FORMAT_UNKNOWN;
1097 g_assert_not_reached ();
1102 GstXvImageFormat *format = NULL;
1104 format = g_new0 (GstXvImageFormat, 1);
1106 format->format = formats[i].id;
1107 format->vformat = vformat;
1108 format->caps = gst_caps_copy (format_caps);
1109 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1112 if (is_rgb_format) {
1113 if (rgb_caps == NULL)
1114 rgb_caps = format_caps;
1116 gst_caps_append (rgb_caps, format_caps);
1118 gst_caps_append (caps, format_caps);
1122 /* Collected all caps into either the caps or rgb_caps structures.
1123 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1125 gst_caps_append (caps, rgb_caps);
1130 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1132 if (gst_caps_is_empty (caps)) {
1133 gst_caps_unref (caps);
1134 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1135 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1136 ("No supported format found"));
1144 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1146 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1148 GST_OBJECT_LOCK (xvimagesink);
1149 while (xvimagesink->running) {
1150 GST_OBJECT_UNLOCK (xvimagesink);
1152 if (xvimagesink->xwindow) {
1153 gst_xvimagesink_handle_xevents (xvimagesink);
1155 /* FIXME: do we want to align this with the framerate or anything else? */
1156 g_usleep (G_USEC_PER_SEC / 20);
1158 GST_OBJECT_LOCK (xvimagesink);
1160 GST_OBJECT_UNLOCK (xvimagesink);
1166 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1168 GThread *thread = NULL;
1170 /* don't start the thread too early */
1171 if (xvimagesink->xcontext == NULL) {
1175 GST_OBJECT_LOCK (xvimagesink);
1176 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1177 if (!xvimagesink->event_thread) {
1178 /* Setup our event listening thread */
1179 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1180 xvimagesink->handle_expose, xvimagesink->handle_events);
1181 xvimagesink->running = TRUE;
1182 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1183 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1186 if (xvimagesink->event_thread) {
1187 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1188 xvimagesink->handle_expose, xvimagesink->handle_events);
1189 xvimagesink->running = FALSE;
1190 /* grab thread and mark it as NULL */
1191 thread = xvimagesink->event_thread;
1192 xvimagesink->event_thread = NULL;
1195 GST_OBJECT_UNLOCK (xvimagesink);
1197 /* Wait for our event thread to finish */
1199 g_thread_join (thread);
1204 /* This function calculates the pixel aspect ratio based on the properties
1205 * in the xcontext structure and stores it there. */
1207 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1209 static const gint par[][2] = {
1210 {1, 1}, /* regular screen */
1211 {16, 15}, /* PAL TV */
1212 {11, 10}, /* 525 line Rec.601 video */
1213 {54, 59}, /* 625 line Rec.601 video */
1214 {64, 45}, /* 1280x1024 on 16:9 display */
1215 {5, 3}, /* 1280x1024 on 4:3 display */
1216 {4, 3} /* 800x600 on 16:9 display */
1223 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1225 /* first calculate the "real" ratio based on the X values;
1226 * which is the "physical" w/h divided by the w/h in pixels of the display */
1227 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1228 / (xcontext->heightmm * xcontext->width);
1230 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1232 if (xcontext->width == 720 && xcontext->height == 576) {
1233 ratio = 4.0 * 576 / (3.0 * 720);
1235 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1237 /* now find the one from par[][2] with the lowest delta to the real one */
1241 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1242 gdouble this_delta = DELTA (i);
1244 if (this_delta < delta) {
1250 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1251 par[index][0], par[index][1]);
1253 g_free (xcontext->par);
1254 xcontext->par = g_new0 (GValue, 1);
1255 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1256 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1257 GST_DEBUG ("set xcontext PAR to %d/%d",
1258 gst_value_get_fraction_numerator (xcontext->par),
1259 gst_value_get_fraction_denominator (xcontext->par));
1262 /* This function gets the X Display and global info about it. Everything is
1263 stored in our object and will be cleaned when the object is disposed. Note
1264 here that caps for supported format are generated without any window or
1266 static GstXContext *
1267 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1269 GstXContext *xcontext = NULL;
1270 XPixmapFormatValues *px_formats = NULL;
1271 gint nb_formats = 0, i, j, N_attr;
1272 XvAttribute *xv_attr;
1274 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1275 "XV_BRIGHTNESS", "XV_CONTRAST"
1278 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1280 xcontext = g_new0 (GstXContext, 1);
1281 xcontext->im_format = 0;
1283 g_mutex_lock (&xvimagesink->x_lock);
1285 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1287 if (!xcontext->disp) {
1288 g_mutex_unlock (&xvimagesink->x_lock);
1290 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1291 ("Could not initialise Xv output"), ("Could not open display"));
1295 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1296 xcontext->screen_num = DefaultScreen (xcontext->disp);
1297 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1298 xcontext->root = DefaultRootWindow (xcontext->disp);
1299 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1300 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1301 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1303 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1304 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1305 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1306 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1308 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1309 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1311 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1312 /* We get supported pixmap formats at supported depth */
1313 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1316 XCloseDisplay (xcontext->disp);
1317 g_mutex_unlock (&xvimagesink->x_lock);
1318 g_free (xcontext->par);
1320 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1321 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1325 /* We get bpp value corresponding to our running depth */
1326 for (i = 0; i < nb_formats; i++) {
1327 if (px_formats[i].depth == xcontext->depth)
1328 xcontext->bpp = px_formats[i].bits_per_pixel;
1333 xcontext->endianness =
1334 (ImageByteOrder (xcontext->disp) ==
1335 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1337 /* our caps system handles 24/32bpp RGB as big-endian. */
1338 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1339 xcontext->endianness == G_LITTLE_ENDIAN) {
1340 xcontext->endianness = G_BIG_ENDIAN;
1341 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1342 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1343 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1344 if (xcontext->bpp == 24) {
1345 xcontext->visual->red_mask >>= 8;
1346 xcontext->visual->green_mask >>= 8;
1347 xcontext->visual->blue_mask >>= 8;
1351 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1353 /* Search for XShm extension support */
1355 if (XShmQueryExtension (xcontext->disp) &&
1356 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1357 xcontext->use_xshm = TRUE;
1358 GST_DEBUG ("xvimagesink is using XShm extension");
1360 #endif /* HAVE_XSHM */
1362 xcontext->use_xshm = FALSE;
1363 GST_DEBUG ("xvimagesink is not using XShm extension");
1366 if (!xcontext->caps) {
1367 XCloseDisplay (xcontext->disp);
1368 g_mutex_unlock (&xvimagesink->x_lock);
1369 g_free (xcontext->par);
1371 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1375 xv_attr = XvQueryPortAttributes (xcontext->disp,
1376 xcontext->xv_port_id, &N_attr);
1379 /* Generate the channels list */
1380 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1381 XvAttribute *matching_attr = NULL;
1383 /* Retrieve the property atom if it exists. If it doesn't exist,
1384 * the attribute itself must not either, so we can skip */
1385 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1386 if (prop_atom == None)
1389 if (xv_attr != NULL) {
1390 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1391 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1392 matching_attr = xv_attr + j;
1395 if (matching_attr) {
1396 GstColorBalanceChannel *channel;
1398 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1399 channel->label = g_strdup (channels[i]);
1400 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1401 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1403 xcontext->channels_list = g_list_append (xcontext->channels_list,
1406 /* If the colorbalance settings have not been touched we get Xv values
1407 as defaults and update our internal variables */
1408 if (!xvimagesink->cb_changed) {
1411 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1413 /* Normalize val to [-1000, 1000] */
1414 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1415 (double) (channel->max_value - channel->min_value));
1417 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1418 xvimagesink->hue = val;
1419 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1420 xvimagesink->saturation = val;
1421 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1422 xvimagesink->brightness = val;
1423 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1424 xvimagesink->contrast = val;
1432 g_mutex_unlock (&xvimagesink->x_lock);
1437 /* This function cleans the X context. Closing the Display, releasing the XV
1438 port and unrefing the caps for supported formats. */
1440 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1442 GList *formats_list, *channels_list;
1443 GstXContext *xcontext;
1446 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1448 GST_OBJECT_LOCK (xvimagesink);
1449 if (xvimagesink->xcontext == NULL) {
1450 GST_OBJECT_UNLOCK (xvimagesink);
1454 /* Take the XContext from the sink and clean it up */
1455 xcontext = xvimagesink->xcontext;
1456 xvimagesink->xcontext = NULL;
1458 GST_OBJECT_UNLOCK (xvimagesink);
1461 formats_list = xcontext->formats_list;
1463 while (formats_list) {
1464 GstXvImageFormat *format = formats_list->data;
1466 gst_caps_unref (format->caps);
1468 formats_list = g_list_next (formats_list);
1471 if (xcontext->formats_list)
1472 g_list_free (xcontext->formats_list);
1474 channels_list = xcontext->channels_list;
1476 while (channels_list) {
1477 GstColorBalanceChannel *channel = channels_list->data;
1479 g_object_unref (channel);
1480 channels_list = g_list_next (channels_list);
1483 if (xcontext->channels_list)
1484 g_list_free (xcontext->channels_list);
1486 gst_caps_unref (xcontext->caps);
1487 if (xcontext->last_caps)
1488 gst_caps_replace (&xcontext->last_caps, NULL);
1490 for (i = 0; i < xcontext->nb_adaptors; i++) {
1491 g_free (xcontext->adaptors[i]);
1494 g_free (xcontext->adaptors);
1496 g_free (xcontext->par);
1498 g_mutex_lock (&xvimagesink->x_lock);
1500 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1502 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1504 XCloseDisplay (xcontext->disp);
1506 g_mutex_unlock (&xvimagesink->x_lock);
1514 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
1516 GstXvImageSink *xvimagesink;
1519 xvimagesink = GST_XVIMAGESINK (bsink);
1521 if (xvimagesink->xcontext) {
1523 return gst_caps_intersect_full (filter, xvimagesink->xcontext->caps,
1524 GST_CAPS_INTERSECT_FIRST);
1526 return gst_caps_ref (xvimagesink->xcontext->caps);
1529 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
1531 GstCaps *intersection;
1534 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
1535 gst_caps_unref (caps);
1536 caps = intersection;
1542 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1544 GstXvImageSink *xvimagesink;
1545 GstStructure *structure;
1546 GstBufferPool *newpool, *oldpool;
1548 guint32 im_format = 0;
1549 gint video_par_n, video_par_d; /* video's PAR */
1550 gint display_par_n, display_par_d; /* display's PAR */
1553 static GstAllocationParams params = { 0, 15, 0, 0, };
1555 xvimagesink = GST_XVIMAGESINK (bsink);
1557 GST_DEBUG_OBJECT (xvimagesink,
1558 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1559 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1561 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1562 goto incompatible_caps;
1564 if (!gst_video_info_from_caps (&info, caps))
1565 goto invalid_format;
1567 xvimagesink->fps_n = info.fps_n;
1568 xvimagesink->fps_d = info.fps_d;
1570 xvimagesink->video_width = info.width;
1571 xvimagesink->video_height = info.height;
1573 im_format = gst_xvimagesink_get_format_from_info (xvimagesink, &info);
1574 if (im_format == -1)
1575 goto invalid_format;
1579 /* get aspect ratio from caps if it's present, and
1580 * convert video width and height to a display width and height
1581 * using wd / hd = wv / hv * PARv / PARd */
1583 /* get video's PAR */
1584 video_par_n = info.par_n;
1585 video_par_d = info.par_d;
1587 /* get display's PAR */
1588 if (xvimagesink->par) {
1589 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1590 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1596 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
1597 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
1600 GST_DEBUG_OBJECT (xvimagesink,
1601 "video width/height: %dx%d, calculated display ratio: %d/%d",
1602 info.width, info.height, num, den);
1604 /* now find a width x height that respects this display ratio.
1605 * prefer those that have one of w/h the same as the incoming video
1606 * using wd / hd = num / den */
1608 /* start with same height, because of interlaced video */
1609 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1610 if (info.height % den == 0) {
1611 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1612 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1613 gst_util_uint64_scale_int (info.height, num, den);
1614 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1615 } else if (info.width % num == 0) {
1616 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1617 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
1618 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1619 gst_util_uint64_scale_int (info.width, den, num);
1621 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1622 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1623 gst_util_uint64_scale_int (info.height, num, den);
1624 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
1626 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1627 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1629 /* Notify application to set xwindow id now */
1630 g_mutex_lock (&xvimagesink->flow_lock);
1631 if (!xvimagesink->xwindow) {
1632 g_mutex_unlock (&xvimagesink->flow_lock);
1633 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
1635 g_mutex_unlock (&xvimagesink->flow_lock);
1638 /* Creating our window and our image with the display size in pixels */
1639 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1640 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1641 goto no_display_size;
1643 g_mutex_lock (&xvimagesink->flow_lock);
1644 if (!xvimagesink->xwindow) {
1645 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1646 GST_VIDEO_SINK_WIDTH (xvimagesink),
1647 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1650 xvimagesink->info = info;
1652 /* After a resize, we want to redraw the borders in case the new frame size
1653 * doesn't cover the same area */
1654 xvimagesink->redraw_border = TRUE;
1656 /* create a new pool for the new configuration */
1657 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1659 structure = gst_buffer_pool_get_config (newpool);
1660 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
1661 gst_buffer_pool_config_set_allocator (structure, NULL, ¶ms);
1662 if (!gst_buffer_pool_set_config (newpool, structure))
1665 oldpool = xvimagesink->pool;
1666 /* we don't activate the pool yet, this will be done by downstream after it
1667 * has configured the pool. If downstream does not want our pool we will
1668 * activate it when we render into it */
1669 xvimagesink->pool = newpool;
1670 g_mutex_unlock (&xvimagesink->flow_lock);
1672 /* unref the old sink */
1674 /* we don't deactivate, some elements might still be using it, it will
1675 * be deactivated when the last ref is gone */
1676 gst_object_unref (oldpool);
1684 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1689 GST_DEBUG_OBJECT (xvimagesink,
1690 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1695 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1696 ("Error calculating the output display ratio of the video."));
1701 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1702 ("Error calculating the output display ratio of the video."));
1707 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1708 g_mutex_unlock (&xvimagesink->flow_lock);
1713 static GstStateChangeReturn
1714 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1716 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1717 GstXvImageSink *xvimagesink;
1718 GstXContext *xcontext = NULL;
1720 xvimagesink = GST_XVIMAGESINK (element);
1722 switch (transition) {
1723 case GST_STATE_CHANGE_NULL_TO_READY:
1724 /* Initializing the XContext */
1725 if (xvimagesink->xcontext == NULL) {
1726 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1727 if (xcontext == NULL) {
1728 ret = GST_STATE_CHANGE_FAILURE;
1731 GST_OBJECT_LOCK (xvimagesink);
1733 xvimagesink->xcontext = xcontext;
1734 GST_OBJECT_UNLOCK (xvimagesink);
1737 /* update object's par with calculated one if not set yet */
1738 if (!xvimagesink->par) {
1739 xvimagesink->par = g_new0 (GValue, 1);
1740 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1741 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1743 /* call XSynchronize with the current value of synchronous */
1744 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1745 xvimagesink->synchronous ? "TRUE" : "FALSE");
1746 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1747 gst_xvimagesink_update_colorbalance (xvimagesink);
1748 gst_xvimagesink_manage_event_thread (xvimagesink);
1750 case GST_STATE_CHANGE_READY_TO_PAUSED:
1752 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1754 case GST_STATE_CHANGE_PAUSED_TO_READY:
1760 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1762 switch (transition) {
1763 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1765 case GST_STATE_CHANGE_PAUSED_TO_READY:
1766 xvimagesink->fps_n = 0;
1767 xvimagesink->fps_d = 1;
1768 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1769 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1770 g_mutex_lock (&xvimagesink->flow_lock);
1771 if (xvimagesink->pool)
1772 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1773 g_mutex_unlock (&xvimagesink->flow_lock);
1775 case GST_STATE_CHANGE_READY_TO_NULL:
1776 gst_xvimagesink_reset (xvimagesink);
1787 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1788 GstClockTime * start, GstClockTime * end)
1790 GstXvImageSink *xvimagesink;
1792 xvimagesink = GST_XVIMAGESINK (bsink);
1794 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1795 *start = GST_BUFFER_TIMESTAMP (buf);
1796 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1797 *end = *start + GST_BUFFER_DURATION (buf);
1799 if (xvimagesink->fps_n > 0) {
1801 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1802 xvimagesink->fps_n);
1808 static GstFlowReturn
1809 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1812 GstXvImageSink *xvimagesink;
1813 GstXvImageMeta *meta;
1816 xvimagesink = GST_XVIMAGESINK (vsink);
1818 meta = gst_buffer_get_xvimage_meta (buf);
1820 if (meta && meta->sink == xvimagesink) {
1821 /* If this buffer has been allocated using our buffer management we simply
1822 put the ximage which is in the PRIVATE pointer */
1823 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1828 GstVideoFrame src, dest;
1829 GstBufferPoolAcquireParams params = { 0, };
1831 /* Else we have to copy the data into our private image, */
1832 /* if we have one... */
1833 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1835 /* we should have a pool, configured in setcaps */
1836 if (xvimagesink->pool == NULL)
1839 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
1840 goto activate_failed;
1842 /* take a buffer from our pool, if there is no buffer in the pool something
1843 * is seriously wrong, waiting for the pool here might deadlock when we try
1844 * to go to PAUSED because we never flush the pool then. */
1845 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
1846 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
1847 if (res != GST_FLOW_OK)
1850 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1851 "slow copy into bufferpool buffer %p", to_put);
1853 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
1854 goto invalid_buffer;
1856 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
1857 gst_video_frame_unmap (&src);
1858 goto invalid_buffer;
1861 gst_video_frame_copy (&dest, &src);
1863 gst_video_frame_unmap (&dest);
1864 gst_video_frame_unmap (&src);
1867 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1872 gst_buffer_unref (to_put);
1879 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1880 ("Internal error: can't allocate images"),
1881 ("We don't have a bufferpool negotiated"));
1882 return GST_FLOW_ERROR;
1886 /* No image available. That's very bad ! */
1887 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1892 /* No Window available to put our image into */
1893 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1899 /* No Window available to put our image into */
1900 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1901 res = GST_FLOW_ERROR;
1906 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1907 res = GST_FLOW_ERROR;
1913 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1915 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1917 switch (GST_EVENT_TYPE (event)) {
1918 case GST_EVENT_TAG:{
1920 gchar *title = NULL;
1922 gst_event_parse_tag (event, &l);
1923 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1926 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1927 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1937 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1941 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1943 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1944 GstBufferPool *pool;
1945 GstStructure *config;
1950 gst_query_parse_allocation (query, &caps, &need_pool);
1955 g_mutex_lock (&xvimagesink->flow_lock);
1956 if ((pool = xvimagesink->pool))
1957 gst_object_ref (pool);
1958 g_mutex_unlock (&xvimagesink->flow_lock);
1963 /* we had a pool, check caps */
1964 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1965 config = gst_buffer_pool_get_config (pool);
1966 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1968 if (!gst_caps_is_equal (caps, pcaps)) {
1969 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1970 /* different caps, we can't use this pool */
1971 gst_object_unref (pool);
1974 gst_structure_free (config);
1976 if (pool == NULL && need_pool) {
1979 if (!gst_video_info_from_caps (&info, caps))
1982 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1983 pool = gst_xvimage_buffer_pool_new (xvimagesink);
1985 /* the normal size of a frame */
1988 config = gst_buffer_pool_get_config (pool);
1989 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1990 if (!gst_buffer_pool_set_config (pool, config))
1994 /* we need at least 2 buffer because we hold on to the last one */
1995 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1996 gst_object_unref (pool);
1999 /* we also support various metadata */
2000 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
2001 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
2008 GST_DEBUG_OBJECT (bsink, "no caps specified");
2013 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
2018 GST_DEBUG_OBJECT (bsink, "failed setting config");
2019 gst_object_unref (pool);
2024 /* Interfaces stuff */
2026 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2027 GstStructure * structure)
2029 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2032 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2034 GstVideoRectangle src, dst, result;
2035 gdouble x, y, xscale = 1.0, yscale = 1.0;
2037 event = gst_event_new_navigation (structure);
2039 /* We take the flow_lock while we look at the window */
2040 g_mutex_lock (&xvimagesink->flow_lock);
2042 if (!xvimagesink->xwindow) {
2043 g_mutex_unlock (&xvimagesink->flow_lock);
2047 if (xvimagesink->keep_aspect) {
2048 /* We get the frame position using the calculated geometry from _setcaps
2049 that respect pixel aspect ratios */
2050 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2051 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2052 dst.w = xvimagesink->render_rect.w;
2053 dst.h = xvimagesink->render_rect.h;
2055 gst_video_sink_center_rect (src, dst, &result, TRUE);
2056 result.x += xvimagesink->render_rect.x;
2057 result.y += xvimagesink->render_rect.y;
2059 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2062 g_mutex_unlock (&xvimagesink->flow_lock);
2064 /* We calculate scaling using the original video frames geometry to include
2065 pixel aspect ratio scaling. */
2066 xscale = (gdouble) xvimagesink->video_width / result.w;
2067 yscale = (gdouble) xvimagesink->video_height / result.h;
2069 /* Converting pointer coordinates to the non scaled geometry */
2070 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2071 x = MIN (x, result.x + result.w);
2072 x = MAX (x - result.x, 0);
2073 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2074 (gdouble) x * xscale, NULL);
2076 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2077 y = MIN (y, result.y + result.h);
2078 y = MAX (y - result.y, 0);
2079 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2080 (gdouble) y * yscale, NULL);
2083 gst_pad_send_event (peer, event);
2084 gst_object_unref (peer);
2089 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2091 iface->send_event = gst_xvimagesink_navigation_send_event;
2095 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
2097 XID xwindow_id = id;
2098 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2099 GstXWindow *xwindow = NULL;
2101 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2103 g_mutex_lock (&xvimagesink->flow_lock);
2105 /* If we already use that window return */
2106 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2107 g_mutex_unlock (&xvimagesink->flow_lock);
2111 /* If the element has not initialized the X11 context try to do so */
2112 if (!xvimagesink->xcontext &&
2113 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2114 g_mutex_unlock (&xvimagesink->flow_lock);
2115 /* we have thrown a GST_ELEMENT_ERROR now */
2119 gst_xvimagesink_update_colorbalance (xvimagesink);
2121 /* If a window is there already we destroy it */
2122 if (xvimagesink->xwindow) {
2123 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2124 xvimagesink->xwindow = NULL;
2127 /* If the xid is 0 we go back to an internal window */
2128 if (xwindow_id == 0) {
2129 /* If no width/height caps nego did not happen window will be created
2130 during caps nego then */
2131 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2132 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2134 gst_xvimagesink_xwindow_new (xvimagesink,
2135 GST_VIDEO_SINK_WIDTH (xvimagesink),
2136 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2139 XWindowAttributes attr;
2141 xwindow = g_new0 (GstXWindow, 1);
2142 xwindow->win = xwindow_id;
2144 /* Set the event we want to receive and create a GC */
2145 g_mutex_lock (&xvimagesink->x_lock);
2147 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2149 xwindow->width = attr.width;
2150 xwindow->height = attr.height;
2151 xwindow->internal = FALSE;
2152 if (!xvimagesink->have_render_rect) {
2153 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2154 xvimagesink->render_rect.w = attr.width;
2155 xvimagesink->render_rect.h = attr.height;
2157 if (xvimagesink->handle_events) {
2158 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2159 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2163 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2164 xwindow->win, 0, NULL);
2165 g_mutex_unlock (&xvimagesink->x_lock);
2169 xvimagesink->xwindow = xwindow;
2171 g_mutex_unlock (&xvimagesink->flow_lock);
2175 gst_xvimagesink_expose (GstVideoOverlay * overlay)
2177 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2179 GST_DEBUG ("doing expose");
2180 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2181 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2185 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
2186 gboolean handle_events)
2188 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2190 xvimagesink->handle_events = handle_events;
2192 g_mutex_lock (&xvimagesink->flow_lock);
2194 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2195 g_mutex_unlock (&xvimagesink->flow_lock);
2199 g_mutex_lock (&xvimagesink->x_lock);
2201 if (handle_events) {
2202 if (xvimagesink->xwindow->internal) {
2203 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2204 ExposureMask | StructureNotifyMask | PointerMotionMask |
2205 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2207 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2208 ExposureMask | StructureNotifyMask | PointerMotionMask |
2209 KeyPressMask | KeyReleaseMask);
2212 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2215 g_mutex_unlock (&xvimagesink->x_lock);
2217 g_mutex_unlock (&xvimagesink->flow_lock);
2221 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
2222 gint width, gint height)
2224 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2226 /* FIXME: how about some locking? */
2227 if (width >= 0 && height >= 0) {
2228 xvimagesink->render_rect.x = x;
2229 xvimagesink->render_rect.y = y;
2230 xvimagesink->render_rect.w = width;
2231 xvimagesink->render_rect.h = height;
2232 xvimagesink->have_render_rect = TRUE;
2234 xvimagesink->render_rect.x = 0;
2235 xvimagesink->render_rect.y = 0;
2236 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2237 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2238 xvimagesink->have_render_rect = FALSE;
2243 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
2245 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2246 iface->expose = gst_xvimagesink_expose;
2247 iface->handle_events = gst_xvimagesink_set_event_handling;
2248 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2251 static const GList *
2252 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2254 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2256 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2258 if (xvimagesink->xcontext)
2259 return xvimagesink->xcontext->channels_list;
2265 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2266 GstColorBalanceChannel * channel, gint value)
2268 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2270 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2271 g_return_if_fail (channel->label != NULL);
2273 xvimagesink->cb_changed = TRUE;
2275 /* Normalize val to [-1000, 1000] */
2276 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2277 (double) (channel->max_value - channel->min_value));
2279 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2280 xvimagesink->hue = value;
2281 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2282 xvimagesink->saturation = value;
2283 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2284 xvimagesink->contrast = value;
2285 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2286 xvimagesink->brightness = value;
2288 g_warning ("got an unknown channel %s", channel->label);
2292 gst_xvimagesink_update_colorbalance (xvimagesink);
2296 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2297 GstColorBalanceChannel * channel)
2299 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2302 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2303 g_return_val_if_fail (channel->label != NULL, 0);
2305 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2306 value = xvimagesink->hue;
2307 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2308 value = xvimagesink->saturation;
2309 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2310 value = xvimagesink->contrast;
2311 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2312 value = xvimagesink->brightness;
2314 g_warning ("got an unknown channel %s", channel->label);
2317 /* Normalize val to [channel->min_value, channel->max_value] */
2318 value = channel->min_value + (channel->max_value - channel->min_value) *
2319 (value + 1000) / 2000;
2324 static GstColorBalanceType
2325 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
2327 return GST_COLOR_BALANCE_HARDWARE;
2331 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
2333 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2334 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2335 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2336 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
2340 static const GList *
2341 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2343 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2344 static GList *list = NULL;
2347 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2349 g_list_append (list, g_object_class_find_property (klass,
2350 "autopaint-colorkey"));
2352 g_list_append (list, g_object_class_find_property (klass,
2355 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2362 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2363 guint prop_id, const GParamSpec * pspec)
2365 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2369 case PROP_AUTOPAINT_COLORKEY:
2370 case PROP_DOUBLE_BUFFER:
2372 GST_DEBUG_OBJECT (xvimagesink,
2373 "probing device list and get capabilities");
2374 if (!xvimagesink->xcontext) {
2375 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2376 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2380 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2386 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2387 guint prop_id, const GParamSpec * pspec)
2389 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2390 gboolean ret = FALSE;
2394 case PROP_AUTOPAINT_COLORKEY:
2395 case PROP_DOUBLE_BUFFER:
2397 if (xvimagesink->xcontext != NULL) {
2404 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2411 static GValueArray *
2412 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2413 guint prop_id, const GParamSpec * pspec)
2415 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2416 GValueArray *array = NULL;
2418 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2419 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2428 GValue value = { 0 };
2430 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2431 g_value_init (&value, G_TYPE_STRING);
2433 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2434 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2436 g_value_set_string (&value, adaptor_id_s);
2437 g_value_array_append (array, &value);
2438 g_free (adaptor_id_s);
2440 g_value_unset (&value);
2443 case PROP_AUTOPAINT_COLORKEY:
2444 if (xvimagesink->have_autopaint_colorkey) {
2445 GValue value = { 0 };
2447 array = g_value_array_new (2);
2448 g_value_init (&value, G_TYPE_BOOLEAN);
2449 g_value_set_boolean (&value, FALSE);
2450 g_value_array_append (array, &value);
2451 g_value_set_boolean (&value, TRUE);
2452 g_value_array_append (array, &value);
2453 g_value_unset (&value);
2456 case PROP_DOUBLE_BUFFER:
2457 if (xvimagesink->have_double_buffer) {
2458 GValue value = { 0 };
2460 array = g_value_array_new (2);
2461 g_value_init (&value, G_TYPE_BOOLEAN);
2462 g_value_set_boolean (&value, FALSE);
2463 g_value_array_append (array, &value);
2464 g_value_set_boolean (&value, TRUE);
2465 g_value_array_append (array, &value);
2466 g_value_unset (&value);
2470 if (xvimagesink->have_colorkey) {
2471 GValue value = { 0 };
2473 array = g_value_array_new (1);
2474 g_value_init (&value, GST_TYPE_INT_RANGE);
2475 gst_value_set_int_range (&value, 0, 0xffffff);
2476 g_value_array_append (array, &value);
2477 g_value_unset (&value);
2481 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2490 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2493 iface->get_properties = gst_xvimagesink_probe_get_properties;
2494 iface->probe_property = gst_xvimagesink_probe_probe_property;
2495 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2496 iface->get_values = gst_xvimagesink_probe_get_values;
2500 /* =========================================== */
2502 /* Init & Class init */
2504 /* =========================================== */
2507 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2508 const GValue * value, GParamSpec * pspec)
2510 GstXvImageSink *xvimagesink;
2512 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2514 xvimagesink = GST_XVIMAGESINK (object);
2518 xvimagesink->hue = g_value_get_int (value);
2519 xvimagesink->cb_changed = TRUE;
2520 gst_xvimagesink_update_colorbalance (xvimagesink);
2523 xvimagesink->contrast = g_value_get_int (value);
2524 xvimagesink->cb_changed = TRUE;
2525 gst_xvimagesink_update_colorbalance (xvimagesink);
2527 case PROP_BRIGHTNESS:
2528 xvimagesink->brightness = g_value_get_int (value);
2529 xvimagesink->cb_changed = TRUE;
2530 gst_xvimagesink_update_colorbalance (xvimagesink);
2532 case PROP_SATURATION:
2533 xvimagesink->saturation = g_value_get_int (value);
2534 xvimagesink->cb_changed = TRUE;
2535 gst_xvimagesink_update_colorbalance (xvimagesink);
2538 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2540 case PROP_SYNCHRONOUS:
2541 xvimagesink->synchronous = g_value_get_boolean (value);
2542 if (xvimagesink->xcontext) {
2543 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2544 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2545 xvimagesink->synchronous ? "TRUE" : "FALSE");
2548 case PROP_PIXEL_ASPECT_RATIO:
2549 g_free (xvimagesink->par);
2550 xvimagesink->par = g_new0 (GValue, 1);
2551 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2552 if (!g_value_transform (value, xvimagesink->par)) {
2553 g_warning ("Could not transform string to aspect ratio");
2554 gst_value_set_fraction (xvimagesink->par, 1, 1);
2556 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2557 gst_value_get_fraction_numerator (xvimagesink->par),
2558 gst_value_get_fraction_denominator (xvimagesink->par));
2560 case PROP_FORCE_ASPECT_RATIO:
2561 xvimagesink->keep_aspect = g_value_get_boolean (value);
2563 case PROP_HANDLE_EVENTS:
2564 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
2565 g_value_get_boolean (value));
2566 gst_xvimagesink_manage_event_thread (xvimagesink);
2569 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2571 case PROP_HANDLE_EXPOSE:
2572 xvimagesink->handle_expose = g_value_get_boolean (value);
2573 gst_xvimagesink_manage_event_thread (xvimagesink);
2575 case PROP_DOUBLE_BUFFER:
2576 xvimagesink->double_buffer = g_value_get_boolean (value);
2578 case PROP_AUTOPAINT_COLORKEY:
2579 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2582 xvimagesink->colorkey = g_value_get_int (value);
2584 case PROP_DRAW_BORDERS:
2585 xvimagesink->draw_borders = g_value_get_boolean (value);
2588 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2594 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2595 GValue * value, GParamSpec * pspec)
2597 GstXvImageSink *xvimagesink;
2599 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2601 xvimagesink = GST_XVIMAGESINK (object);
2605 g_value_set_int (value, xvimagesink->hue);
2608 g_value_set_int (value, xvimagesink->contrast);
2610 case PROP_BRIGHTNESS:
2611 g_value_set_int (value, xvimagesink->brightness);
2613 case PROP_SATURATION:
2614 g_value_set_int (value, xvimagesink->saturation);
2617 g_value_set_string (value, xvimagesink->display_name);
2619 case PROP_SYNCHRONOUS:
2620 g_value_set_boolean (value, xvimagesink->synchronous);
2622 case PROP_PIXEL_ASPECT_RATIO:
2623 if (xvimagesink->par)
2624 g_value_transform (xvimagesink->par, value);
2626 case PROP_FORCE_ASPECT_RATIO:
2627 g_value_set_boolean (value, xvimagesink->keep_aspect);
2629 case PROP_HANDLE_EVENTS:
2630 g_value_set_boolean (value, xvimagesink->handle_events);
2634 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2636 g_value_set_string (value, adaptor_no_s);
2637 g_free (adaptor_no_s);
2640 case PROP_DEVICE_NAME:
2641 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2642 g_value_set_string (value,
2643 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2645 g_value_set_string (value, NULL);
2648 case PROP_HANDLE_EXPOSE:
2649 g_value_set_boolean (value, xvimagesink->handle_expose);
2651 case PROP_DOUBLE_BUFFER:
2652 g_value_set_boolean (value, xvimagesink->double_buffer);
2654 case PROP_AUTOPAINT_COLORKEY:
2655 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2658 g_value_set_int (value, xvimagesink->colorkey);
2660 case PROP_DRAW_BORDERS:
2661 g_value_set_boolean (value, xvimagesink->draw_borders);
2663 case PROP_WINDOW_WIDTH:
2664 if (xvimagesink->xwindow)
2665 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2667 g_value_set_uint64 (value, 0);
2669 case PROP_WINDOW_HEIGHT:
2670 if (xvimagesink->xwindow)
2671 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2673 g_value_set_uint64 (value, 0);
2676 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2682 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2686 GST_OBJECT_LOCK (xvimagesink);
2687 xvimagesink->running = FALSE;
2688 /* grab thread and mark it as NULL */
2689 thread = xvimagesink->event_thread;
2690 xvimagesink->event_thread = NULL;
2691 GST_OBJECT_UNLOCK (xvimagesink);
2693 /* Wait for our event thread to finish before we clean up our stuff. */
2695 g_thread_join (thread);
2697 if (xvimagesink->cur_image) {
2698 gst_buffer_unref (xvimagesink->cur_image);
2699 xvimagesink->cur_image = NULL;
2702 g_mutex_lock (&xvimagesink->flow_lock);
2704 if (xvimagesink->pool) {
2705 gst_object_unref (xvimagesink->pool);
2706 xvimagesink->pool = NULL;
2709 if (xvimagesink->xwindow) {
2710 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2711 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2712 xvimagesink->xwindow = NULL;
2714 g_mutex_unlock (&xvimagesink->flow_lock);
2716 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2717 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2718 xvimagesink->have_render_rect = FALSE;
2720 gst_xvimagesink_xcontext_clear (xvimagesink);
2723 /* Finalize is called only once, dispose can be called multiple times.
2724 * We use mutexes and don't reset stuff to NULL here so let's register
2727 gst_xvimagesink_finalize (GObject * object)
2729 GstXvImageSink *xvimagesink;
2731 xvimagesink = GST_XVIMAGESINK (object);
2733 gst_xvimagesink_reset (xvimagesink);
2735 if (xvimagesink->display_name) {
2736 g_free (xvimagesink->display_name);
2737 xvimagesink->display_name = NULL;
2740 if (xvimagesink->par) {
2741 g_free (xvimagesink->par);
2742 xvimagesink->par = NULL;
2744 g_mutex_clear (&xvimagesink->x_lock);
2745 g_mutex_clear (&xvimagesink->flow_lock);
2746 g_free (xvimagesink->media_title);
2748 G_OBJECT_CLASS (parent_class)->finalize (object);
2752 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2754 xvimagesink->display_name = NULL;
2755 xvimagesink->adaptor_no = 0;
2756 xvimagesink->xcontext = NULL;
2757 xvimagesink->xwindow = NULL;
2758 xvimagesink->cur_image = NULL;
2760 xvimagesink->hue = xvimagesink->saturation = 0;
2761 xvimagesink->contrast = xvimagesink->brightness = 0;
2762 xvimagesink->cb_changed = FALSE;
2764 xvimagesink->fps_n = 0;
2765 xvimagesink->fps_d = 0;
2766 xvimagesink->video_width = 0;
2767 xvimagesink->video_height = 0;
2769 g_mutex_init (&xvimagesink->x_lock);
2770 g_mutex_init (&xvimagesink->flow_lock);
2772 xvimagesink->pool = NULL;
2774 xvimagesink->synchronous = FALSE;
2775 xvimagesink->double_buffer = TRUE;
2776 xvimagesink->running = FALSE;
2777 xvimagesink->keep_aspect = TRUE;
2778 xvimagesink->handle_events = TRUE;
2779 xvimagesink->par = NULL;
2780 xvimagesink->handle_expose = TRUE;
2781 xvimagesink->autopaint_colorkey = TRUE;
2783 /* on 16bit displays this becomes r,g,b = 1,2,3
2784 * on 24bit displays this becomes r,g,b = 8,8,16
2785 * as a port atom value
2787 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2788 xvimagesink->draw_borders = TRUE;
2792 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2794 GObjectClass *gobject_class;
2795 GstElementClass *gstelement_class;
2796 GstBaseSinkClass *gstbasesink_class;
2797 GstVideoSinkClass *videosink_class;
2799 gobject_class = (GObjectClass *) klass;
2800 gstelement_class = (GstElementClass *) klass;
2801 gstbasesink_class = (GstBaseSinkClass *) klass;
2802 videosink_class = (GstVideoSinkClass *) klass;
2804 parent_class = g_type_class_peek_parent (klass);
2806 gobject_class->set_property = gst_xvimagesink_set_property;
2807 gobject_class->get_property = gst_xvimagesink_get_property;
2809 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2810 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2811 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2812 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2813 g_param_spec_int ("brightness", "Brightness",
2814 "The brightness of the video", -1000, 1000, 0,
2815 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2816 g_object_class_install_property (gobject_class, PROP_HUE,
2817 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2818 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2819 g_object_class_install_property (gobject_class, PROP_SATURATION,
2820 g_param_spec_int ("saturation", "Saturation",
2821 "The saturation of the video", -1000, 1000, 0,
2822 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2823 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2824 g_param_spec_string ("display", "Display", "X Display name", NULL,
2825 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2826 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2827 g_param_spec_boolean ("synchronous", "Synchronous",
2828 "When enabled, runs the X display in synchronous mode. "
2829 "(unrelated to A/V sync, used only for debugging)", FALSE,
2830 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2831 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2832 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2833 "The pixel aspect ratio of the device", "1/1",
2834 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2835 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2836 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2837 "When enabled, scaling will respect original aspect ratio", TRUE,
2838 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2839 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2840 g_param_spec_boolean ("handle-events", "Handle XEvents",
2841 "When enabled, XEvents will be selected and handled", TRUE,
2842 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2843 g_object_class_install_property (gobject_class, PROP_DEVICE,
2844 g_param_spec_string ("device", "Adaptor number",
2845 "The number of the video adaptor", "0",
2846 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2847 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
2848 g_param_spec_string ("device-name", "Adaptor name",
2849 "The name of the video adaptor", NULL,
2850 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2852 * GstXvImageSink:handle-expose
2854 * When enabled, the current frame will always be drawn in response to X
2859 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2860 g_param_spec_boolean ("handle-expose", "Handle expose",
2862 "the current frame will always be drawn in response to X Expose "
2863 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2866 * GstXvImageSink:double-buffer
2868 * Whether to double-buffer the output.
2872 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
2873 g_param_spec_boolean ("double-buffer", "Double-buffer",
2874 "Whether to double-buffer the output", TRUE,
2875 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2877 * GstXvImageSink:autopaint-colorkey
2879 * Whether to autofill overlay with colorkey
2883 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
2884 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
2885 "Whether to autofill overlay with colorkey", TRUE,
2886 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2888 * GstXvImageSink:colorkey
2890 * Color to use for the overlay mask.
2894 g_object_class_install_property (gobject_class, PROP_COLORKEY,
2895 g_param_spec_int ("colorkey", "Colorkey",
2896 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
2897 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2900 * GstXvImageSink:draw-borders
2902 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2903 * unused parts of the video area.
2907 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2908 g_param_spec_boolean ("draw-borders", "Colorkey",
2909 "Draw black borders to fill unused area in force-aspect-ratio mode",
2910 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2913 * GstXvImageSink:window-width
2915 * Actual width of the video window.
2919 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2920 g_param_spec_uint64 ("window-width", "window-width",
2921 "Width of the window", 0, G_MAXUINT64, 0,
2922 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2925 * GstXvImageSink:window-height
2927 * Actual height of the video window.
2931 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2932 g_param_spec_uint64 ("window-height", "window-height",
2933 "Height of the window", 0, G_MAXUINT64, 0,
2934 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2936 gobject_class->finalize = gst_xvimagesink_finalize;
2938 gst_element_class_set_static_metadata (gstelement_class,
2939 "Video sink", "Sink/Video",
2940 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2942 gst_element_class_add_pad_template (gstelement_class,
2943 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2945 gstelement_class->change_state =
2946 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2948 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2949 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2950 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2951 gstbasesink_class->propose_allocation =
2952 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2953 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2955 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);