2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theorically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/xoverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/video.h>
125 #include "xvimagesink.h"
127 /* Debugging category */
128 #include <gst/gstinfo.h>
130 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
131 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
132 #define GST_CAT_DEFAULT gst_debug_xvimagesink
137 unsigned long functions;
138 unsigned long decorations;
140 unsigned long status;
142 MotifWmHints, MwmHints;
144 #define MWM_HINTS_DECORATIONS (1L << 1)
146 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
147 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
149 static void gst_xvimagesink_expose (GstXOverlay * overlay);
151 /* Default template - initiated with class struct to allow gst-register to work
153 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
154 GST_STATIC_PAD_TEMPLATE ("sink",
157 GST_STATIC_CAPS ("video/x-raw-rgb, "
158 "framerate = (fraction) [ 0, MAX ], "
159 "width = (int) [ 1, MAX ], "
160 "height = (int) [ 1, MAX ]; "
162 "framerate = (fraction) [ 0, MAX ], "
163 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
175 PROP_PIXEL_ASPECT_RATIO,
176 PROP_FORCE_ASPECT_RATIO,
182 PROP_AUTOPAINT_COLORKEY,
189 static GstVideoSinkClass *parent_class = NULL;
191 /* ============================================================= */
193 /* Private Methods */
195 /* ============================================================= */
198 /* We are called with the x_lock taken */
200 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
201 GstXWindow * xwindow, GstVideoRectangle rect)
205 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
206 g_return_if_fail (xwindow != NULL);
208 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
209 xvimagesink->xcontext->black);
212 if (rect.x > xvimagesink->render_rect.x) {
213 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
214 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
215 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
219 t1 = rect.x + rect.w;
220 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
222 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
223 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
227 if (rect.y > xvimagesink->render_rect.y) {
228 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
229 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
230 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
234 t1 = rect.y + rect.h;
235 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
237 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
238 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
242 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
243 * if no window was available */
245 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
247 GstMetaXvImage *meta;
248 GstVideoRectangle result;
249 gboolean draw_border = FALSE;
251 /* We take the flow_lock. If expose is in there we don't want to run
252 concurrently from the data flow thread */
253 g_mutex_lock (xvimagesink->flow_lock);
255 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
256 g_mutex_unlock (xvimagesink->flow_lock);
260 /* Draw borders when displaying the first frame. After this
261 draw borders only on expose event or after a size change. */
262 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
266 /* Store a reference to the last image we put, lose the previous one */
267 if (xvimage && xvimagesink->cur_image != xvimage) {
268 if (xvimagesink->cur_image) {
269 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
270 gst_buffer_unref (xvimagesink->cur_image);
272 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
273 xvimagesink->cur_image = gst_buffer_ref (xvimage);
276 /* Expose sends a NULL image, we take the latest frame */
278 if (xvimagesink->cur_image) {
280 xvimage = xvimagesink->cur_image;
282 g_mutex_unlock (xvimagesink->flow_lock);
287 meta = gst_buffer_get_meta_xvimage (xvimage);
289 if (xvimagesink->keep_aspect) {
290 GstVideoRectangle src, dst;
292 /* We use the calculated geometry from _setcaps as a source to respect
293 source and screen pixel aspect ratios. */
294 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
295 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
296 dst.w = xvimagesink->render_rect.w;
297 dst.h = xvimagesink->render_rect.h;
299 gst_video_sink_center_rect (src, dst, &result, TRUE);
300 result.x += xvimagesink->render_rect.x;
301 result.y += xvimagesink->render_rect.y;
303 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
306 g_mutex_lock (xvimagesink->x_lock);
308 if (draw_border && xvimagesink->draw_borders) {
309 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
311 xvimagesink->redraw_border = FALSE;
314 if (xvimagesink->xcontext->use_xshm) {
315 GST_LOG_OBJECT (xvimagesink,
316 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
317 GST_PTR_FORMAT, meta->width, meta->height,
318 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
320 XvShmPutImage (xvimagesink->xcontext->disp,
321 xvimagesink->xcontext->xv_port_id,
322 xvimagesink->xwindow->win,
323 xvimagesink->xwindow->gc, meta->xvimage,
324 xvimagesink->disp_x, xvimagesink->disp_y,
325 xvimagesink->disp_width, xvimagesink->disp_height,
326 result.x, result.y, result.w, result.h, FALSE);
328 #endif /* HAVE_XSHM */
330 XvPutImage (xvimagesink->xcontext->disp,
331 xvimagesink->xcontext->xv_port_id,
332 xvimagesink->xwindow->win,
333 xvimagesink->xwindow->gc, meta->xvimage,
334 xvimagesink->disp_x, xvimagesink->disp_y,
335 xvimagesink->disp_width, xvimagesink->disp_height,
336 result.x, result.y, result.w, result.h);
339 XSync (xvimagesink->xcontext->disp, FALSE);
341 g_mutex_unlock (xvimagesink->x_lock);
343 g_mutex_unlock (xvimagesink->flow_lock);
349 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
352 Atom hints_atom = None;
355 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
356 g_return_val_if_fail (window != NULL, FALSE);
358 g_mutex_lock (xvimagesink->x_lock);
360 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
362 if (hints_atom == None) {
363 g_mutex_unlock (xvimagesink->x_lock);
367 hints = g_malloc0 (sizeof (MotifWmHints));
369 hints->flags |= MWM_HINTS_DECORATIONS;
370 hints->decorations = 1 << 0;
372 XChangeProperty (xvimagesink->xcontext->disp, window->win,
373 hints_atom, hints_atom, 32, PropModeReplace,
374 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
376 XSync (xvimagesink->xcontext->disp, FALSE);
378 g_mutex_unlock (xvimagesink->x_lock);
386 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
387 GstXWindow * xwindow, const gchar * media_title)
390 g_free (xvimagesink->media_title);
391 xvimagesink->media_title = g_strdup (media_title);
394 /* we have a window */
395 if (xwindow->internal) {
396 XTextProperty xproperty;
397 const gchar *app_name;
398 const gchar *title = NULL;
399 gchar *title_mem = NULL;
401 /* set application name as a title */
402 app_name = g_get_application_name ();
404 if (app_name && xvimagesink->media_title) {
405 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
407 } else if (app_name) {
409 } else if (xvimagesink->media_title) {
410 title = xvimagesink->media_title;
414 if ((XStringListToTextProperty (((char **) &title), 1,
416 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
417 XFree (xproperty.value);
426 /* This function handles a GstXWindow creation
427 * The width and height are the actual pixel size on the display */
429 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
430 gint width, gint height)
432 GstXWindow *xwindow = NULL;
435 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
437 xwindow = g_new0 (GstXWindow, 1);
439 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
440 xvimagesink->render_rect.w = width;
441 xvimagesink->render_rect.h = height;
443 xwindow->width = width;
444 xwindow->height = height;
445 xwindow->internal = TRUE;
447 g_mutex_lock (xvimagesink->x_lock);
449 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
450 xvimagesink->xcontext->root,
451 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
453 /* We have to do that to prevent X from redrawing the background on
454 * ConfigureNotify. This takes away flickering of video when resizing. */
455 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
457 /* set application name as a title */
458 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
460 if (xvimagesink->handle_events) {
463 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
464 StructureNotifyMask | PointerMotionMask | KeyPressMask |
465 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
467 /* Tell the window manager we'd like delete client messages instead of
469 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
470 "WM_DELETE_WINDOW", True);
471 if (wm_delete != None) {
472 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
477 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
478 xwindow->win, 0, &values);
480 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
482 XSync (xvimagesink->xcontext->disp, FALSE);
484 g_mutex_unlock (xvimagesink->x_lock);
486 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
488 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
493 /* This function destroys a GstXWindow */
495 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
496 GstXWindow * xwindow)
498 g_return_if_fail (xwindow != NULL);
499 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
501 g_mutex_lock (xvimagesink->x_lock);
503 /* If we did not create that window we just free the GC and let it live */
504 if (xwindow->internal)
505 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
507 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
509 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
511 XSync (xvimagesink->xcontext->disp, FALSE);
513 g_mutex_unlock (xvimagesink->x_lock);
519 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
521 XWindowAttributes attr;
523 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
525 /* Update the window geometry */
526 g_mutex_lock (xvimagesink->x_lock);
527 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
528 g_mutex_unlock (xvimagesink->x_lock);
532 XGetWindowAttributes (xvimagesink->xcontext->disp,
533 xvimagesink->xwindow->win, &attr);
535 xvimagesink->xwindow->width = attr.width;
536 xvimagesink->xwindow->height = attr.height;
538 if (!xvimagesink->have_render_rect) {
539 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
540 xvimagesink->render_rect.w = attr.width;
541 xvimagesink->render_rect.h = attr.height;
544 g_mutex_unlock (xvimagesink->x_lock);
548 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
549 GstXWindow * xwindow)
551 g_return_if_fail (xwindow != NULL);
552 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
554 g_mutex_lock (xvimagesink->x_lock);
556 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
559 XSync (xvimagesink->xcontext->disp, FALSE);
561 g_mutex_unlock (xvimagesink->x_lock);
564 /* This function commits our internal colorbalance settings to our grabbed Xv
565 port. If the xcontext is not initialized yet it simply returns */
567 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
569 GList *channels = NULL;
571 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
573 /* If we haven't initialized the X context we can't update anything */
574 if (xvimagesink->xcontext == NULL)
577 /* Don't set the attributes if they haven't been changed, to avoid
578 * rounding errors changing the values */
579 if (!xvimagesink->cb_changed)
582 /* For each channel of the colorbalance we calculate the correct value
583 doing range conversion and then set the Xv port attribute to match our
585 channels = xvimagesink->xcontext->channels_list;
588 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
589 GstColorBalanceChannel *channel = NULL;
592 gdouble convert_coef;
594 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
595 g_object_ref (channel);
597 /* Our range conversion coef */
598 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
600 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
601 value = xvimagesink->hue;
602 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
603 value = xvimagesink->saturation;
604 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
605 value = xvimagesink->contrast;
606 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
607 value = xvimagesink->brightness;
609 g_warning ("got an unknown channel %s", channel->label);
610 g_object_unref (channel);
614 /* Committing to Xv port */
615 g_mutex_lock (xvimagesink->x_lock);
617 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
618 if (prop_atom != None) {
621 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
622 XvSetPortAttribute (xvimagesink->xcontext->disp,
623 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
625 g_mutex_unlock (xvimagesink->x_lock);
627 g_object_unref (channel);
629 channels = g_list_next (channels);
633 /* This function handles XEvents that might be in the queue. It generates
634 GstEvent that will be sent upstream in the pipeline to handle interactivity
635 and navigation. It will also listen for configure events on the window to
636 trigger caps renegotiation so on the fly software scaling can work. */
638 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
641 guint pointer_x = 0, pointer_y = 0;
642 gboolean pointer_moved = FALSE;
643 gboolean exposed = FALSE, configured = FALSE;
645 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
647 /* Handle Interaction, produces navigation events */
649 /* We get all pointer motion events, only the last position is
651 g_mutex_lock (xvimagesink->flow_lock);
652 g_mutex_lock (xvimagesink->x_lock);
653 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
654 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
655 g_mutex_unlock (xvimagesink->x_lock);
656 g_mutex_unlock (xvimagesink->flow_lock);
660 pointer_x = e.xmotion.x;
661 pointer_y = e.xmotion.y;
662 pointer_moved = TRUE;
667 g_mutex_lock (xvimagesink->flow_lock);
668 g_mutex_lock (xvimagesink->x_lock);
672 g_mutex_unlock (xvimagesink->x_lock);
673 g_mutex_unlock (xvimagesink->flow_lock);
675 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
676 pointer_x, pointer_y);
677 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
678 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
680 g_mutex_lock (xvimagesink->flow_lock);
681 g_mutex_lock (xvimagesink->x_lock);
684 /* We get all events on our window to throw them upstream */
685 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
686 xvimagesink->xwindow->win,
687 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
691 /* We lock only for the X function call */
692 g_mutex_unlock (xvimagesink->x_lock);
693 g_mutex_unlock (xvimagesink->flow_lock);
697 /* Mouse button pressed over our window. We send upstream
698 events for interactivity/navigation */
699 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
700 e.xbutton.button, e.xbutton.x, e.xbutton.y);
701 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
702 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
705 /* Mouse button released over our window. We send upstream
706 events for interactivity/navigation */
707 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
708 e.xbutton.button, e.xbutton.x, e.xbutton.y);
709 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
710 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
714 /* Key pressed/released over our window. We send upstream
715 events for interactivity/navigation */
716 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
717 e.xkey.keycode, e.xkey.x, e.xkey.y);
718 g_mutex_lock (xvimagesink->x_lock);
719 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
721 g_mutex_unlock (xvimagesink->x_lock);
722 if (keysym != NoSymbol) {
723 char *key_str = NULL;
725 g_mutex_lock (xvimagesink->x_lock);
726 key_str = XKeysymToString (keysym);
727 g_mutex_unlock (xvimagesink->x_lock);
728 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
729 e.type == KeyPress ? "key-press" : "key-release", key_str);
731 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
732 e.type == KeyPress ? "key-press" : "key-release", "unknown");
736 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
739 g_mutex_lock (xvimagesink->flow_lock);
740 g_mutex_lock (xvimagesink->x_lock);
744 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
745 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
750 case ConfigureNotify:
751 g_mutex_unlock (xvimagesink->x_lock);
752 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
753 g_mutex_lock (xvimagesink->x_lock);
761 if (xvimagesink->handle_expose && (exposed || configured)) {
762 g_mutex_unlock (xvimagesink->x_lock);
763 g_mutex_unlock (xvimagesink->flow_lock);
765 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
767 g_mutex_lock (xvimagesink->flow_lock);
768 g_mutex_lock (xvimagesink->x_lock);
771 /* Handle Display events */
772 while (XPending (xvimagesink->xcontext->disp)) {
773 XNextEvent (xvimagesink->xcontext->disp, &e);
779 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
780 "WM_DELETE_WINDOW", True);
781 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
782 /* Handle window deletion by posting an error on the bus */
783 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
784 ("Output window was closed"), (NULL));
786 g_mutex_unlock (xvimagesink->x_lock);
787 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
788 xvimagesink->xwindow = NULL;
789 g_mutex_lock (xvimagesink->x_lock);
798 g_mutex_unlock (xvimagesink->x_lock);
799 g_mutex_unlock (xvimagesink->flow_lock);
803 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
804 XvAdaptorInfo * adaptors, int adaptor_no)
809 /* Do we support XvImageMask ? */
810 if (!(adaptors[adaptor_no].type & XvImageMask)) {
811 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
812 adaptors[adaptor_no].name);
816 /* We found such an adaptor, looking for an available port */
817 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
818 /* We try to grab the port */
819 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
820 if (Success == res) {
821 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
822 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
823 adaptors[adaptor_no].num_ports);
825 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
826 adaptors[adaptor_no].name, res);
831 /* This function generates a caps with all supported format by the first
832 Xv grabable port we find. We store each one of the supported formats in a
833 format list and append the format to a newly created caps that we return
834 If this function does not return NULL because of an error, it also grabs
835 the port via XvGrabPort */
837 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
838 GstXContext * xcontext)
841 XvAdaptorInfo *adaptors;
843 XvImageFormatValues *formats = NULL;
845 XvEncodingInfo *encodings = NULL;
846 gulong max_w = G_MAXINT, max_h = G_MAXINT;
847 GstCaps *caps = NULL;
848 GstCaps *rgb_caps = NULL;
850 g_return_val_if_fail (xcontext != NULL, NULL);
852 /* First let's check that XVideo extension is available */
853 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
854 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
855 ("Could not initialise Xv output"),
856 ("XVideo extension is not available"));
860 /* Then we get adaptors list */
861 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
862 &xcontext->nb_adaptors, &adaptors)) {
863 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
864 ("Could not initialise Xv output"),
865 ("Failed getting XV adaptors list"));
869 xcontext->xv_port_id = 0;
871 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
874 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
876 /* Now fill up our adaptor name array */
877 for (i = 0; i < xcontext->nb_adaptors; i++) {
878 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
881 if (xvimagesink->adaptor_no >= 0 &&
882 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
883 /* Find xv port from user defined adaptor */
884 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
885 xvimagesink->adaptor_no);
888 if (!xcontext->xv_port_id) {
889 /* Now search for an adaptor that supports XvImageMask */
890 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
891 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
892 xvimagesink->adaptor_no = i;
896 XvFreeAdaptorInfo (adaptors);
898 if (!xcontext->xv_port_id) {
899 xvimagesink->adaptor_no = -1;
900 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
901 ("Could not initialise Xv output"), ("No port available"));
905 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
908 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
909 xcontext->xv_port_id, &count);
910 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
911 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
912 static const char colorkey[] = "XV_COLORKEY";
914 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
916 xvimagesink->have_autopaint_colorkey = FALSE;
917 xvimagesink->have_double_buffer = FALSE;
918 xvimagesink->have_colorkey = FALSE;
920 for (i = 0; ((i < count) && todo); i++)
921 if (!strcmp (attr[i].name, autopaint)) {
922 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
924 /* turn on autopaint colorkey */
925 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
926 (xvimagesink->autopaint_colorkey ? 1 : 0));
928 xvimagesink->have_autopaint_colorkey = TRUE;
929 } else if (!strcmp (attr[i].name, dbl_buffer)) {
930 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
932 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
933 (xvimagesink->double_buffer ? 1 : 0));
935 xvimagesink->have_double_buffer = TRUE;
936 } else if (!strcmp (attr[i].name, colorkey)) {
937 /* Set the colorkey, default is something that is dark but hopefully
938 * won't randomly appear on the screen elsewhere (ie not black or greys)
939 * can be overridden by setting "colorkey" property
941 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
943 gboolean set_attr = TRUE;
946 /* set a colorkey in the right format RGB565/RGB888
947 * We only handle these 2 cases, because they're the only types of
948 * devices we've encountered. If we don't recognise it, leave it alone
950 cr = (xvimagesink->colorkey >> 16);
951 cg = (xvimagesink->colorkey >> 8) & 0xFF;
952 cb = (xvimagesink->colorkey) & 0xFF;
953 switch (xcontext->depth) {
954 case 16: /* RGB 565 */
958 ckey = (cr << 11) | (cg << 5) | cb;
961 case 32: /* RGB 888 / ARGB 8888 */
962 ckey = (cr << 16) | (cg << 8) | cb;
965 GST_DEBUG_OBJECT (xvimagesink,
966 "Unknown bit depth %d for Xv Colorkey - not adjusting",
973 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
974 (guint32) attr[i].max_value);
975 GST_LOG_OBJECT (xvimagesink,
976 "Setting color key for display depth %d to 0x%x",
977 xcontext->depth, ckey);
979 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
983 xvimagesink->have_colorkey = TRUE;
989 /* Get the list of encodings supported by the adapter and look for the
990 * XV_IMAGE encoding so we can determine the maximum width and height
992 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
995 for (i = 0; i < nb_encodings; i++) {
996 GST_LOG_OBJECT (xvimagesink,
997 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
998 i, encodings[i].name, encodings[i].width, encodings[i].height,
999 encodings[i].rate.numerator, encodings[i].rate.denominator);
1000 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1001 max_w = encodings[i].width;
1002 max_h = encodings[i].height;
1006 XvFreeEncodingInfo (encodings);
1008 /* We get all image formats supported by our port */
1009 formats = XvListImageFormats (xcontext->disp,
1010 xcontext->xv_port_id, &nb_formats);
1011 caps = gst_caps_new_empty ();
1012 for (i = 0; i < nb_formats; i++) {
1013 GstCaps *format_caps = NULL;
1014 gboolean is_rgb_format = FALSE;
1016 /* We set the image format of the xcontext to an existing one. This
1017 is just some valid image format for making our xshm calls check before
1018 caps negotiation really happens. */
1019 xcontext->im_format = formats[i].id;
1021 switch (formats[i].type) {
1024 XvImageFormatValues *fmt = &(formats[i]);
1025 gint endianness = G_BIG_ENDIAN;
1027 if (fmt->byte_order == LSBFirst) {
1028 /* our caps system handles 24/32bpp RGB as big-endian. */
1029 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1030 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1031 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1032 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1034 if (fmt->bits_per_pixel == 24) {
1035 fmt->red_mask >>= 8;
1036 fmt->green_mask >>= 8;
1037 fmt->blue_mask >>= 8;
1040 endianness = G_LITTLE_ENDIAN;
1043 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1044 "endianness", G_TYPE_INT, endianness,
1045 "depth", G_TYPE_INT, fmt->depth,
1046 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1047 "red_mask", G_TYPE_INT, fmt->red_mask,
1048 "green_mask", G_TYPE_INT, fmt->green_mask,
1049 "blue_mask", G_TYPE_INT, fmt->blue_mask,
1050 "width", GST_TYPE_INT_RANGE, 1, max_w,
1051 "height", GST_TYPE_INT_RANGE, 1, max_h,
1052 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1054 is_rgb_format = TRUE;
1058 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1059 "format", GST_TYPE_FOURCC, formats[i].id,
1060 "width", GST_TYPE_INT_RANGE, 1, max_w,
1061 "height", GST_TYPE_INT_RANGE, 1, max_h,
1062 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1065 g_assert_not_reached ();
1070 GstXvImageFormat *format = NULL;
1072 format = g_new0 (GstXvImageFormat, 1);
1074 format->format = formats[i].id;
1075 format->caps = gst_caps_copy (format_caps);
1076 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1079 if (is_rgb_format) {
1080 if (rgb_caps == NULL)
1081 rgb_caps = format_caps;
1083 gst_caps_append (rgb_caps, format_caps);
1085 gst_caps_append (caps, format_caps);
1089 /* Collected all caps into either the caps or rgb_caps structures.
1090 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1092 gst_caps_append (caps, rgb_caps);
1097 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1099 if (gst_caps_is_empty (caps)) {
1100 gst_caps_unref (caps);
1101 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1102 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1103 ("No supported format found"));
1111 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1113 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1115 GST_OBJECT_LOCK (xvimagesink);
1116 while (xvimagesink->running) {
1117 GST_OBJECT_UNLOCK (xvimagesink);
1119 if (xvimagesink->xwindow) {
1120 gst_xvimagesink_handle_xevents (xvimagesink);
1122 /* FIXME: do we want to align this with the framerate or anything else? */
1123 g_usleep (G_USEC_PER_SEC / 20);
1125 GST_OBJECT_LOCK (xvimagesink);
1127 GST_OBJECT_UNLOCK (xvimagesink);
1133 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1135 GThread *thread = NULL;
1137 /* don't start the thread too early */
1138 if (xvimagesink->xcontext == NULL) {
1142 GST_OBJECT_LOCK (xvimagesink);
1143 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1144 if (!xvimagesink->event_thread) {
1145 /* Setup our event listening thread */
1146 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1147 xvimagesink->handle_expose, xvimagesink->handle_events);
1148 xvimagesink->running = TRUE;
1149 xvimagesink->event_thread = g_thread_create (
1150 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1153 if (xvimagesink->event_thread) {
1154 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1155 xvimagesink->handle_expose, xvimagesink->handle_events);
1156 xvimagesink->running = FALSE;
1157 /* grab thread and mark it as NULL */
1158 thread = xvimagesink->event_thread;
1159 xvimagesink->event_thread = NULL;
1162 GST_OBJECT_UNLOCK (xvimagesink);
1164 /* Wait for our event thread to finish */
1166 g_thread_join (thread);
1171 /* This function calculates the pixel aspect ratio based on the properties
1172 * in the xcontext structure and stores it there. */
1174 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1176 static const gint par[][2] = {
1177 {1, 1}, /* regular screen */
1178 {16, 15}, /* PAL TV */
1179 {11, 10}, /* 525 line Rec.601 video */
1180 {54, 59}, /* 625 line Rec.601 video */
1181 {64, 45}, /* 1280x1024 on 16:9 display */
1182 {5, 3}, /* 1280x1024 on 4:3 display */
1183 {4, 3} /* 800x600 on 16:9 display */
1190 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1192 /* first calculate the "real" ratio based on the X values;
1193 * which is the "physical" w/h divided by the w/h in pixels of the display */
1194 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1195 / (xcontext->heightmm * xcontext->width);
1197 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1199 if (xcontext->width == 720 && xcontext->height == 576) {
1200 ratio = 4.0 * 576 / (3.0 * 720);
1202 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1204 /* now find the one from par[][2] with the lowest delta to the real one */
1208 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1209 gdouble this_delta = DELTA (i);
1211 if (this_delta < delta) {
1217 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1218 par[index][0], par[index][1]);
1220 g_free (xcontext->par);
1221 xcontext->par = g_new0 (GValue, 1);
1222 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1223 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1224 GST_DEBUG ("set xcontext PAR to %d/%d",
1225 gst_value_get_fraction_numerator (xcontext->par),
1226 gst_value_get_fraction_denominator (xcontext->par));
1229 /* This function gets the X Display and global info about it. Everything is
1230 stored in our object and will be cleaned when the object is disposed. Note
1231 here that caps for supported format are generated without any window or
1233 static GstXContext *
1234 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1236 GstXContext *xcontext = NULL;
1237 XPixmapFormatValues *px_formats = NULL;
1238 gint nb_formats = 0, i, j, N_attr;
1239 XvAttribute *xv_attr;
1241 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1242 "XV_BRIGHTNESS", "XV_CONTRAST"
1245 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1247 xcontext = g_new0 (GstXContext, 1);
1248 xcontext->im_format = 0;
1250 g_mutex_lock (xvimagesink->x_lock);
1252 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1254 if (!xcontext->disp) {
1255 g_mutex_unlock (xvimagesink->x_lock);
1257 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1258 ("Could not initialise Xv output"), ("Could not open display"));
1262 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1263 xcontext->screen_num = DefaultScreen (xcontext->disp);
1264 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1265 xcontext->root = DefaultRootWindow (xcontext->disp);
1266 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1267 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1268 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1270 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1271 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1272 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1273 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1275 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1276 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1278 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1279 /* We get supported pixmap formats at supported depth */
1280 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1283 XCloseDisplay (xcontext->disp);
1284 g_mutex_unlock (xvimagesink->x_lock);
1285 g_free (xcontext->par);
1287 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1288 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1292 /* We get bpp value corresponding to our running depth */
1293 for (i = 0; i < nb_formats; i++) {
1294 if (px_formats[i].depth == xcontext->depth)
1295 xcontext->bpp = px_formats[i].bits_per_pixel;
1300 xcontext->endianness =
1301 (ImageByteOrder (xcontext->disp) ==
1302 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1304 /* our caps system handles 24/32bpp RGB as big-endian. */
1305 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1306 xcontext->endianness == G_LITTLE_ENDIAN) {
1307 xcontext->endianness = G_BIG_ENDIAN;
1308 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1309 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1310 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1311 if (xcontext->bpp == 24) {
1312 xcontext->visual->red_mask >>= 8;
1313 xcontext->visual->green_mask >>= 8;
1314 xcontext->visual->blue_mask >>= 8;
1318 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1320 /* Search for XShm extension support */
1322 if (XShmQueryExtension (xcontext->disp) &&
1323 gst_xvimagesink_check_xshm_calls (xvimagesink, xcontext)) {
1324 xcontext->use_xshm = TRUE;
1325 GST_DEBUG ("xvimagesink is using XShm extension");
1327 #endif /* HAVE_XSHM */
1329 xcontext->use_xshm = FALSE;
1330 GST_DEBUG ("xvimagesink is not using XShm extension");
1333 if (!xcontext->caps) {
1334 XCloseDisplay (xcontext->disp);
1335 g_mutex_unlock (xvimagesink->x_lock);
1336 g_free (xcontext->par);
1338 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1342 xv_attr = XvQueryPortAttributes (xcontext->disp,
1343 xcontext->xv_port_id, &N_attr);
1346 /* Generate the channels list */
1347 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1348 XvAttribute *matching_attr = NULL;
1350 /* Retrieve the property atom if it exists. If it doesn't exist,
1351 * the attribute itself must not either, so we can skip */
1352 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1353 if (prop_atom == None)
1356 if (xv_attr != NULL) {
1357 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1358 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1359 matching_attr = xv_attr + j;
1362 if (matching_attr) {
1363 GstColorBalanceChannel *channel;
1365 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1366 channel->label = g_strdup (channels[i]);
1367 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1368 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1370 xcontext->channels_list = g_list_append (xcontext->channels_list,
1373 /* If the colorbalance settings have not been touched we get Xv values
1374 as defaults and update our internal variables */
1375 if (!xvimagesink->cb_changed) {
1378 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1380 /* Normalize val to [-1000, 1000] */
1381 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1382 (double) (channel->max_value - channel->min_value));
1384 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1385 xvimagesink->hue = val;
1386 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1387 xvimagesink->saturation = val;
1388 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1389 xvimagesink->brightness = val;
1390 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1391 xvimagesink->contrast = val;
1399 g_mutex_unlock (xvimagesink->x_lock);
1404 /* This function cleans the X context. Closing the Display, releasing the XV
1405 port and unrefing the caps for supported formats. */
1407 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1409 GList *formats_list, *channels_list;
1410 GstXContext *xcontext;
1413 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1415 GST_OBJECT_LOCK (xvimagesink);
1416 if (xvimagesink->xcontext == NULL) {
1417 GST_OBJECT_UNLOCK (xvimagesink);
1421 /* Take the XContext from the sink and clean it up */
1422 xcontext = xvimagesink->xcontext;
1423 xvimagesink->xcontext = NULL;
1425 GST_OBJECT_UNLOCK (xvimagesink);
1428 formats_list = xcontext->formats_list;
1430 while (formats_list) {
1431 GstXvImageFormat *format = formats_list->data;
1433 gst_caps_unref (format->caps);
1435 formats_list = g_list_next (formats_list);
1438 if (xcontext->formats_list)
1439 g_list_free (xcontext->formats_list);
1441 channels_list = xcontext->channels_list;
1443 while (channels_list) {
1444 GstColorBalanceChannel *channel = channels_list->data;
1446 g_object_unref (channel);
1447 channels_list = g_list_next (channels_list);
1450 if (xcontext->channels_list)
1451 g_list_free (xcontext->channels_list);
1453 gst_caps_unref (xcontext->caps);
1454 if (xcontext->last_caps)
1455 gst_caps_replace (&xcontext->last_caps, NULL);
1457 for (i = 0; i < xcontext->nb_adaptors; i++) {
1458 g_free (xcontext->adaptors[i]);
1461 g_free (xcontext->adaptors);
1463 g_free (xcontext->par);
1465 g_mutex_lock (xvimagesink->x_lock);
1467 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1469 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1471 XCloseDisplay (xcontext->disp);
1473 g_mutex_unlock (xvimagesink->x_lock);
1481 gst_xvimagesink_getcaps (GstBaseSink * bsink)
1483 GstXvImageSink *xvimagesink;
1485 xvimagesink = GST_XVIMAGESINK (bsink);
1487 if (xvimagesink->xcontext)
1488 return gst_caps_ref (xvimagesink->xcontext->caps);
1491 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
1496 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1498 GstXvImageSink *xvimagesink;
1499 GstStructure *structure;
1500 GstBufferPool *newpool, *oldpool;
1502 guint32 im_format = 0;
1503 gint video_width, video_height;
1504 gint disp_x, disp_y;
1505 gint disp_width, disp_height;
1506 gint video_par_n, video_par_d; /* video's PAR */
1507 gint display_par_n, display_par_d; /* display's PAR */
1508 const GValue *caps_par;
1509 const GValue *caps_disp_reg;
1513 xvimagesink = GST_XVIMAGESINK (bsink);
1515 GST_DEBUG_OBJECT (xvimagesink,
1516 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1517 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1519 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
1520 goto incompatible_caps;
1522 structure = gst_caps_get_structure (caps, 0);
1523 ret = gst_structure_get_int (structure, "width", &video_width);
1524 ret &= gst_structure_get_int (structure, "height", &video_height);
1525 fps = gst_structure_get_value (structure, "framerate");
1526 ret &= (fps != NULL);
1529 goto incomplete_caps;
1531 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
1532 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
1534 xvimagesink->video_width = video_width;
1535 xvimagesink->video_height = video_height;
1537 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
1538 if (im_format == -1)
1539 goto invalid_format;
1541 /* get aspect ratio from caps if it's present, and
1542 * convert video width and height to a display width and height
1543 * using wd / hd = wv / hv * PARv / PARd */
1545 /* get video's PAR */
1546 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1548 video_par_n = gst_value_get_fraction_numerator (caps_par);
1549 video_par_d = gst_value_get_fraction_denominator (caps_par);
1554 /* get display's PAR */
1555 if (xvimagesink->par) {
1556 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
1557 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
1563 /* get the display region */
1564 caps_disp_reg = gst_structure_get_value (structure, "display-region");
1565 if (caps_disp_reg) {
1566 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
1567 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
1568 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
1570 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
1572 disp_x = disp_y = 0;
1573 disp_width = video_width;
1574 disp_height = video_height;
1577 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
1578 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
1581 xvimagesink->disp_x = disp_x;
1582 xvimagesink->disp_y = disp_y;
1583 xvimagesink->disp_width = disp_width;
1584 xvimagesink->disp_height = disp_height;
1586 GST_DEBUG_OBJECT (xvimagesink,
1587 "video width/height: %dx%d, calculated display ratio: %d/%d",
1588 video_width, video_height, num, den);
1590 /* now find a width x height that respects this display ratio.
1591 * prefer those that have one of w/h the same as the incoming video
1592 * using wd / hd = num / den */
1594 /* start with same height, because of interlaced video */
1595 /* check hd / den is an integer scale factor, and scale wd with the PAR */
1596 if (video_height % den == 0) {
1597 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
1598 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1599 gst_util_uint64_scale_int (video_height, num, den);
1600 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
1601 } else if (video_width % num == 0) {
1602 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
1603 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
1604 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
1605 gst_util_uint64_scale_int (video_width, den, num);
1607 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
1608 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
1609 gst_util_uint64_scale_int (video_height, num, den);
1610 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
1612 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
1613 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
1615 /* Notify application to set xwindow id now */
1616 g_mutex_lock (xvimagesink->flow_lock);
1617 if (!xvimagesink->xwindow) {
1618 g_mutex_unlock (xvimagesink->flow_lock);
1619 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
1621 g_mutex_unlock (xvimagesink->flow_lock);
1624 /* Creating our window and our image with the display size in pixels */
1625 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
1626 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
1627 goto no_display_size;
1629 g_mutex_lock (xvimagesink->flow_lock);
1630 if (!xvimagesink->xwindow) {
1631 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
1632 GST_VIDEO_SINK_WIDTH (xvimagesink),
1633 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1636 /* After a resize, we want to redraw the borders in case the new frame size
1637 * doesn't cover the same area */
1638 xvimagesink->redraw_border = TRUE;
1640 /* create a new pool for the new configuration */
1641 newpool = gst_xvimage_buffer_pool_new (xvimagesink);
1643 structure = gst_buffer_pool_get_config (newpool);
1644 gst_buffer_pool_config_set (structure, caps, 0, 0, 0, 0, 0, 16);
1645 if (!gst_buffer_pool_set_config (newpool, structure))
1648 if (!gst_buffer_pool_set_active (newpool, TRUE))
1649 goto activate_failed;
1651 oldpool = xvimagesink->pool;
1652 xvimagesink->pool = newpool;
1654 /* unref the old sink */
1657 gst_buffer_pool_set_active (oldpool, FALSE);
1658 gst_object_unref (oldpool);
1660 g_mutex_unlock (xvimagesink->flow_lock);
1667 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
1672 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
1673 "height or framerate from intersected caps");
1678 GST_DEBUG_OBJECT (xvimagesink,
1679 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1684 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1685 ("Error calculating the output display ratio of the video."));
1690 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
1691 ("Error calculating the output display ratio of the video."));
1696 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
1697 g_mutex_unlock (xvimagesink->flow_lock);
1702 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1703 g_mutex_unlock (xvimagesink->flow_lock);
1708 static GstStateChangeReturn
1709 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
1711 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1712 GstXvImageSink *xvimagesink;
1713 GstXContext *xcontext = NULL;
1715 xvimagesink = GST_XVIMAGESINK (element);
1717 switch (transition) {
1718 case GST_STATE_CHANGE_NULL_TO_READY:
1719 /* Initializing the XContext */
1720 if (xvimagesink->xcontext == NULL) {
1721 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
1722 if (xcontext == NULL) {
1723 ret = GST_STATE_CHANGE_FAILURE;
1726 GST_OBJECT_LOCK (xvimagesink);
1728 xvimagesink->xcontext = xcontext;
1729 GST_OBJECT_UNLOCK (xvimagesink);
1732 /* update object's par with calculated one if not set yet */
1733 if (!xvimagesink->par) {
1734 xvimagesink->par = g_new0 (GValue, 1);
1735 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
1736 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1738 /* call XSynchronize with the current value of synchronous */
1739 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
1740 xvimagesink->synchronous ? "TRUE" : "FALSE");
1741 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
1742 gst_xvimagesink_update_colorbalance (xvimagesink);
1743 gst_xvimagesink_manage_event_thread (xvimagesink);
1745 case GST_STATE_CHANGE_READY_TO_PAUSED:
1747 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1749 case GST_STATE_CHANGE_PAUSED_TO_READY:
1755 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1757 switch (transition) {
1758 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1760 case GST_STATE_CHANGE_PAUSED_TO_READY:
1761 xvimagesink->fps_n = 0;
1762 xvimagesink->fps_d = 1;
1763 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
1764 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
1765 g_mutex_lock (xvimagesink->flow_lock);
1766 if (xvimagesink->pool)
1767 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
1768 g_mutex_unlock (xvimagesink->flow_lock);
1770 case GST_STATE_CHANGE_READY_TO_NULL:
1771 gst_xvimagesink_reset (xvimagesink);
1782 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1783 GstClockTime * start, GstClockTime * end)
1785 GstXvImageSink *xvimagesink;
1787 xvimagesink = GST_XVIMAGESINK (bsink);
1789 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1790 *start = GST_BUFFER_TIMESTAMP (buf);
1791 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1792 *end = *start + GST_BUFFER_DURATION (buf);
1794 if (xvimagesink->fps_n > 0) {
1796 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
1797 xvimagesink->fps_n);
1803 static GstFlowReturn
1804 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
1807 GstXvImageSink *xvimagesink;
1808 GstMetaXvImage *meta;
1811 xvimagesink = GST_XVIMAGESINK (vsink);
1813 meta = gst_buffer_get_meta_xvimage (buf);
1816 /* If this buffer has been allocated using our buffer management we simply
1817 put the ximage which is in the PRIVATE pointer */
1818 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
1826 /* Else we have to copy the data into our private image, */
1827 /* if we have one... */
1828 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
1830 /* we should have a pool, configured in setcaps */
1831 if (xvimagesink->pool == NULL)
1834 /* take a buffer form our pool */
1835 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, NULL);
1836 if (res != GST_FLOW_OK)
1839 if (gst_buffer_get_size (to_put) < gst_buffer_get_size (buf))
1842 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
1843 "slow copy into bufferpool buffer %p", to_put);
1845 data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
1846 gst_buffer_fill (to_put, 0, data, size);
1847 gst_buffer_unmap (buf, data, size);
1850 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
1855 gst_buffer_unref (to_put);
1862 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1863 ("Internal error: can't allocate images"),
1864 ("We don't have a bufferpool negotiated"));
1865 return GST_FLOW_ERROR;
1869 /* No image available. That's very bad ! */
1870 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1875 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1876 ("Failed to create output image buffer"),
1877 ("XServer allocated buffer size did not match input buffer %"
1878 G_GSIZE_FORMAT " - %" G_GSIZE_FORMAT, gst_buffer_get_size (to_put),
1879 gst_buffer_get_size (buf)));
1880 res = GST_FLOW_ERROR;
1885 /* No Window available to put our image into */
1886 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1887 res = GST_FLOW_ERROR;
1893 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1895 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1897 switch (GST_EVENT_TYPE (event)) {
1898 case GST_EVENT_TAG:{
1900 gchar *title = NULL;
1902 gst_event_parse_tag (event, &l);
1903 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1906 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1907 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1917 if (GST_BASE_SINK_CLASS (parent_class)->event)
1918 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1923 /* Buffer management */
1925 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
1928 GstCaps *intersection;
1932 gint par_n = 1, par_d = 1;
1936 new_caps = gst_caps_copy (caps);
1938 s = gst_caps_get_structure (new_caps, 0);
1940 gst_structure_get_int (s, "width", &width);
1941 gst_structure_get_int (s, "height", &height);
1942 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
1944 gst_structure_remove_field (s, "width");
1945 gst_structure_remove_field (s, "height");
1946 gst_structure_remove_field (s, "pixel-aspect-ratio");
1948 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
1949 gst_caps_unref (new_caps);
1951 if (gst_caps_is_empty (intersection))
1952 return intersection;
1954 s = gst_caps_get_structure (intersection, 0);
1956 gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
1958 /* xvimagesink supports all PARs */
1960 gst_structure_fixate_field_nearest_int (s, "width", width);
1961 gst_structure_fixate_field_nearest_int (s, "height", height);
1962 gst_structure_get_int (s, "width", &w);
1963 gst_structure_get_int (s, "height", &h);
1965 gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
1966 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
1969 return intersection;
1972 static GstFlowReturn
1973 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1974 GstCaps * caps, GstBuffer ** buf)
1976 GstXvImageSink *xvimagesink;
1977 GstBuffer *xvimage = NULL;
1978 GstStructure *structure = NULL;
1979 GstFlowReturn ret = GST_FLOW_OK;
1980 GstCaps *intersection = NULL;
1981 gint width, height, image_format;
1983 xvimagesink = GST_XVIMAGESINK (bsink);
1985 if (G_LIKELY (xvimagesink->xcontext->last_caps &&
1986 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
1987 GST_LOG_OBJECT (xvimagesink,
1988 "buffer alloc for same last_caps, reusing caps");
1989 intersection = gst_caps_ref (caps);
1990 image_format = xvimagesink->xcontext->last_format;
1991 width = xvimagesink->xcontext->last_width;
1992 height = xvimagesink->xcontext->last_height;
1994 goto reuse_last_caps;
1997 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
1998 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
1999 caps, xvimagesink->xcontext->caps);
2001 /* Check the caps against our xcontext */
2002 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2004 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2005 GST_PTR_FORMAT, intersection);
2007 if (gst_caps_is_empty (intersection)) {
2010 gst_caps_unref (intersection);
2012 /* So we don't support this kind of buffer, let's define one we'd like */
2013 new_caps = gst_caps_copy (caps);
2015 structure = gst_caps_get_structure (new_caps, 0);
2016 if (!gst_structure_has_field (structure, "width") ||
2017 !gst_structure_has_field (structure, "height")) {
2018 gst_caps_unref (new_caps);
2022 /* Try different dimensions */
2024 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2026 if (gst_caps_is_empty (intersection)) {
2027 /* Try with different YUV formats first */
2028 gst_structure_set_name (structure, "video/x-raw-yuv");
2030 /* Remove format specific fields */
2031 gst_structure_remove_field (structure, "format");
2032 gst_structure_remove_field (structure, "endianness");
2033 gst_structure_remove_field (structure, "depth");
2034 gst_structure_remove_field (structure, "bpp");
2035 gst_structure_remove_field (structure, "red_mask");
2036 gst_structure_remove_field (structure, "green_mask");
2037 gst_structure_remove_field (structure, "blue_mask");
2038 gst_structure_remove_field (structure, "alpha_mask");
2040 /* Reuse intersection with Xcontext */
2041 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2044 if (gst_caps_is_empty (intersection)) {
2045 /* Try with different dimensions and YUV formats */
2047 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2050 if (gst_caps_is_empty (intersection)) {
2051 /* Now try with RGB */
2052 gst_structure_set_name (structure, "video/x-raw-rgb");
2053 /* And interset again */
2054 gst_caps_unref (intersection);
2055 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2058 if (gst_caps_is_empty (intersection)) {
2059 /* Try with different dimensions and RGB formats */
2061 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2064 /* Clean this copy */
2065 gst_caps_unref (new_caps);
2067 if (gst_caps_is_empty (intersection))
2071 /* Ensure the returned caps are fixed */
2072 gst_caps_truncate (intersection);
2074 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2075 GST_PTR_FORMAT, intersection);
2076 if (gst_caps_is_equal (intersection, caps)) {
2077 /* Things work better if we return a buffer with the same caps ptr
2078 * as was asked for when we can */
2079 gst_caps_replace (&intersection, caps);
2082 /* Get image format from caps */
2083 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2086 /* Get geometry from caps */
2087 structure = gst_caps_get_structure (intersection, 0);
2088 if (!gst_structure_get_int (structure, "width", &width) ||
2089 !gst_structure_get_int (structure, "height", &height) ||
2093 /* Store our caps and format as the last_caps to avoid expensive
2094 * caps intersection next time */
2095 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2096 xvimagesink->xcontext->last_format = image_format;
2097 xvimagesink->xcontext->last_width = width;
2098 xvimagesink->xcontext->last_height = height;
2102 if (gst_caps_is_equal (GST_PAD_CAPS (bsink->sinkpad), caps)) {
2103 /* we negotiated this format before, use the pool */
2104 if (xvimagesink->pool) {
2107 GST_LOG_OBJECT (xvimagesink, "retrieving buffer from pool");
2108 ret = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &tmp, NULL);
2113 if (xvimage == NULL) {
2114 /* Something new make a new image a new one */
2115 GST_LOG_OBJECT (xvimagesink, "allocating new image");
2116 xvimage = gst_xvimage_buffer_new (xvimagesink, width, height, image_format);
2120 /* Make sure the buffer is cleared of any previously used flags */
2121 GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
2122 gst_buffer_set_caps (xvimage, intersection);
2129 gst_caps_unref (intersection);
2137 GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
2138 ret = GST_FLOW_WRONG_STATE;
2143 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2144 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2145 " are completely incompatible with those caps", caps,
2146 xvimagesink->xcontext->caps);
2147 ret = GST_FLOW_NOT_NEGOTIATED;
2152 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2153 GST_PTR_FORMAT, intersection);
2154 ret = GST_FLOW_NOT_NEGOTIATED;
2159 /* Interfaces stuff */
2162 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2164 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2165 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE);
2170 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2172 klass->supported = gst_xvimagesink_interface_supported;
2176 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2177 GstStructure * structure)
2179 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2182 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2184 GstVideoRectangle src, dst, result;
2185 gdouble x, y, xscale = 1.0, yscale = 1.0;
2187 event = gst_event_new_navigation (structure);
2189 /* We take the flow_lock while we look at the window */
2190 g_mutex_lock (xvimagesink->flow_lock);
2192 if (!xvimagesink->xwindow) {
2193 g_mutex_unlock (xvimagesink->flow_lock);
2197 if (xvimagesink->keep_aspect) {
2198 /* We get the frame position using the calculated geometry from _setcaps
2199 that respect pixel aspect ratios */
2200 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2201 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2202 dst.w = xvimagesink->render_rect.w;
2203 dst.h = xvimagesink->render_rect.h;
2205 gst_video_sink_center_rect (src, dst, &result, TRUE);
2206 result.x += xvimagesink->render_rect.x;
2207 result.y += xvimagesink->render_rect.y;
2209 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2212 g_mutex_unlock (xvimagesink->flow_lock);
2214 /* We calculate scaling using the original video frames geometry to include
2215 pixel aspect ratio scaling. */
2216 xscale = (gdouble) xvimagesink->video_width / result.w;
2217 yscale = (gdouble) xvimagesink->video_height / result.h;
2219 /* Converting pointer coordinates to the non scaled geometry */
2220 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2221 x = MIN (x, result.x + result.w);
2222 x = MAX (x - result.x, 0);
2223 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2224 (gdouble) x * xscale, NULL);
2226 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2227 y = MIN (y, result.y + result.h);
2228 y = MAX (y - result.y, 0);
2229 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2230 (gdouble) y * yscale, NULL);
2233 gst_pad_send_event (peer, event);
2234 gst_object_unref (peer);
2239 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2241 iface->send_event = gst_xvimagesink_navigation_send_event;
2245 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2247 XID xwindow_id = id;
2248 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2249 GstXWindow *xwindow = NULL;
2251 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2253 g_mutex_lock (xvimagesink->flow_lock);
2255 /* If we already use that window return */
2256 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2257 g_mutex_unlock (xvimagesink->flow_lock);
2261 /* If the element has not initialized the X11 context try to do so */
2262 if (!xvimagesink->xcontext &&
2263 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2264 g_mutex_unlock (xvimagesink->flow_lock);
2265 /* we have thrown a GST_ELEMENT_ERROR now */
2269 gst_xvimagesink_update_colorbalance (xvimagesink);
2271 /* If a window is there already we destroy it */
2272 if (xvimagesink->xwindow) {
2273 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2274 xvimagesink->xwindow = NULL;
2277 /* If the xid is 0 we go back to an internal window */
2278 if (xwindow_id == 0) {
2279 /* If no width/height caps nego did not happen window will be created
2280 during caps nego then */
2281 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2282 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2284 gst_xvimagesink_xwindow_new (xvimagesink,
2285 GST_VIDEO_SINK_WIDTH (xvimagesink),
2286 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2289 XWindowAttributes attr;
2291 xwindow = g_new0 (GstXWindow, 1);
2292 xwindow->win = xwindow_id;
2294 /* Set the event we want to receive and create a GC */
2295 g_mutex_lock (xvimagesink->x_lock);
2297 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2299 xwindow->width = attr.width;
2300 xwindow->height = attr.height;
2301 xwindow->internal = FALSE;
2302 if (!xvimagesink->have_render_rect) {
2303 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2304 xvimagesink->render_rect.w = attr.width;
2305 xvimagesink->render_rect.h = attr.height;
2307 if (xvimagesink->handle_events) {
2308 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2309 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2313 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2314 xwindow->win, 0, NULL);
2315 g_mutex_unlock (xvimagesink->x_lock);
2319 xvimagesink->xwindow = xwindow;
2321 g_mutex_unlock (xvimagesink->flow_lock);
2325 gst_xvimagesink_expose (GstXOverlay * overlay)
2327 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2329 GST_DEBUG ("doing expose");
2330 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2331 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2335 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2336 gboolean handle_events)
2338 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2340 xvimagesink->handle_events = handle_events;
2342 g_mutex_lock (xvimagesink->flow_lock);
2344 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2345 g_mutex_unlock (xvimagesink->flow_lock);
2349 g_mutex_lock (xvimagesink->x_lock);
2351 if (handle_events) {
2352 if (xvimagesink->xwindow->internal) {
2353 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2354 ExposureMask | StructureNotifyMask | PointerMotionMask |
2355 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2357 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2358 ExposureMask | StructureNotifyMask | PointerMotionMask |
2359 KeyPressMask | KeyReleaseMask);
2362 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2365 g_mutex_unlock (xvimagesink->x_lock);
2367 g_mutex_unlock (xvimagesink->flow_lock);
2371 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2372 gint width, gint height)
2374 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2376 /* FIXME: how about some locking? */
2377 if (width >= 0 && height >= 0) {
2378 xvimagesink->render_rect.x = x;
2379 xvimagesink->render_rect.y = y;
2380 xvimagesink->render_rect.w = width;
2381 xvimagesink->render_rect.h = height;
2382 xvimagesink->have_render_rect = TRUE;
2384 xvimagesink->render_rect.x = 0;
2385 xvimagesink->render_rect.y = 0;
2386 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2387 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2388 xvimagesink->have_render_rect = FALSE;
2393 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2395 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2396 iface->expose = gst_xvimagesink_expose;
2397 iface->handle_events = gst_xvimagesink_set_event_handling;
2398 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2401 static const GList *
2402 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2404 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2406 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2408 if (xvimagesink->xcontext)
2409 return xvimagesink->xcontext->channels_list;
2415 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2416 GstColorBalanceChannel * channel, gint value)
2418 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2420 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2421 g_return_if_fail (channel->label != NULL);
2423 xvimagesink->cb_changed = TRUE;
2425 /* Normalize val to [-1000, 1000] */
2426 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2427 (double) (channel->max_value - channel->min_value));
2429 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2430 xvimagesink->hue = value;
2431 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2432 xvimagesink->saturation = value;
2433 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2434 xvimagesink->contrast = value;
2435 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2436 xvimagesink->brightness = value;
2438 g_warning ("got an unknown channel %s", channel->label);
2442 gst_xvimagesink_update_colorbalance (xvimagesink);
2446 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2447 GstColorBalanceChannel * channel)
2449 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2452 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2453 g_return_val_if_fail (channel->label != NULL, 0);
2455 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2456 value = xvimagesink->hue;
2457 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2458 value = xvimagesink->saturation;
2459 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2460 value = xvimagesink->contrast;
2461 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2462 value = xvimagesink->brightness;
2464 g_warning ("got an unknown channel %s", channel->label);
2467 /* Normalize val to [channel->min_value, channel->max_value] */
2468 value = channel->min_value + (channel->max_value - channel->min_value) *
2469 (value + 1000) / 2000;
2475 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
2477 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2478 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2479 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2480 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2483 static const GList *
2484 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2486 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2487 static GList *list = NULL;
2490 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2492 g_list_append (list, g_object_class_find_property (klass,
2493 "autopaint-colorkey"));
2495 g_list_append (list, g_object_class_find_property (klass,
2498 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
2505 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2506 guint prop_id, const GParamSpec * pspec)
2508 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2512 case PROP_AUTOPAINT_COLORKEY:
2513 case PROP_DOUBLE_BUFFER:
2515 GST_DEBUG_OBJECT (xvimagesink,
2516 "probing device list and get capabilities");
2517 if (!xvimagesink->xcontext) {
2518 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2519 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2523 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2529 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2530 guint prop_id, const GParamSpec * pspec)
2532 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2533 gboolean ret = FALSE;
2537 case PROP_AUTOPAINT_COLORKEY:
2538 case PROP_DOUBLE_BUFFER:
2540 if (xvimagesink->xcontext != NULL) {
2547 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2554 static GValueArray *
2555 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2556 guint prop_id, const GParamSpec * pspec)
2558 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2559 GValueArray *array = NULL;
2561 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2562 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2571 GValue value = { 0 };
2573 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2574 g_value_init (&value, G_TYPE_STRING);
2576 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2577 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2579 g_value_set_string (&value, adaptor_id_s);
2580 g_value_array_append (array, &value);
2581 g_free (adaptor_id_s);
2583 g_value_unset (&value);
2586 case PROP_AUTOPAINT_COLORKEY:
2587 if (xvimagesink->have_autopaint_colorkey) {
2588 GValue value = { 0 };
2590 array = g_value_array_new (2);
2591 g_value_init (&value, G_TYPE_BOOLEAN);
2592 g_value_set_boolean (&value, FALSE);
2593 g_value_array_append (array, &value);
2594 g_value_set_boolean (&value, TRUE);
2595 g_value_array_append (array, &value);
2596 g_value_unset (&value);
2599 case PROP_DOUBLE_BUFFER:
2600 if (xvimagesink->have_double_buffer) {
2601 GValue value = { 0 };
2603 array = g_value_array_new (2);
2604 g_value_init (&value, G_TYPE_BOOLEAN);
2605 g_value_set_boolean (&value, FALSE);
2606 g_value_array_append (array, &value);
2607 g_value_set_boolean (&value, TRUE);
2608 g_value_array_append (array, &value);
2609 g_value_unset (&value);
2613 if (xvimagesink->have_colorkey) {
2614 GValue value = { 0 };
2616 array = g_value_array_new (1);
2617 g_value_init (&value, GST_TYPE_INT_RANGE);
2618 gst_value_set_int_range (&value, 0, 0xffffff);
2619 g_value_array_append (array, &value);
2620 g_value_unset (&value);
2624 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2633 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2636 iface->get_properties = gst_xvimagesink_probe_get_properties;
2637 iface->probe_property = gst_xvimagesink_probe_probe_property;
2638 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2639 iface->get_values = gst_xvimagesink_probe_get_values;
2642 /* =========================================== */
2644 /* Init & Class init */
2646 /* =========================================== */
2649 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2650 const GValue * value, GParamSpec * pspec)
2652 GstXvImageSink *xvimagesink;
2654 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2656 xvimagesink = GST_XVIMAGESINK (object);
2660 xvimagesink->hue = g_value_get_int (value);
2661 xvimagesink->cb_changed = TRUE;
2662 gst_xvimagesink_update_colorbalance (xvimagesink);
2665 xvimagesink->contrast = g_value_get_int (value);
2666 xvimagesink->cb_changed = TRUE;
2667 gst_xvimagesink_update_colorbalance (xvimagesink);
2669 case PROP_BRIGHTNESS:
2670 xvimagesink->brightness = g_value_get_int (value);
2671 xvimagesink->cb_changed = TRUE;
2672 gst_xvimagesink_update_colorbalance (xvimagesink);
2674 case PROP_SATURATION:
2675 xvimagesink->saturation = g_value_get_int (value);
2676 xvimagesink->cb_changed = TRUE;
2677 gst_xvimagesink_update_colorbalance (xvimagesink);
2680 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2682 case PROP_SYNCHRONOUS:
2683 xvimagesink->synchronous = g_value_get_boolean (value);
2684 if (xvimagesink->xcontext) {
2685 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2686 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2687 xvimagesink->synchronous ? "TRUE" : "FALSE");
2690 case PROP_PIXEL_ASPECT_RATIO:
2691 g_free (xvimagesink->par);
2692 xvimagesink->par = g_new0 (GValue, 1);
2693 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2694 if (!g_value_transform (value, xvimagesink->par)) {
2695 g_warning ("Could not transform string to aspect ratio");
2696 gst_value_set_fraction (xvimagesink->par, 1, 1);
2698 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2699 gst_value_get_fraction_numerator (xvimagesink->par),
2700 gst_value_get_fraction_denominator (xvimagesink->par));
2702 case PROP_FORCE_ASPECT_RATIO:
2703 xvimagesink->keep_aspect = g_value_get_boolean (value);
2705 case PROP_HANDLE_EVENTS:
2706 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
2707 g_value_get_boolean (value));
2708 gst_xvimagesink_manage_event_thread (xvimagesink);
2711 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2713 case PROP_HANDLE_EXPOSE:
2714 xvimagesink->handle_expose = g_value_get_boolean (value);
2715 gst_xvimagesink_manage_event_thread (xvimagesink);
2717 case PROP_DOUBLE_BUFFER:
2718 xvimagesink->double_buffer = g_value_get_boolean (value);
2720 case PROP_AUTOPAINT_COLORKEY:
2721 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
2724 xvimagesink->colorkey = g_value_get_int (value);
2726 case PROP_DRAW_BORDERS:
2727 xvimagesink->draw_borders = g_value_get_boolean (value);
2730 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2736 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2737 GValue * value, GParamSpec * pspec)
2739 GstXvImageSink *xvimagesink;
2741 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2743 xvimagesink = GST_XVIMAGESINK (object);
2747 g_value_set_int (value, xvimagesink->hue);
2750 g_value_set_int (value, xvimagesink->contrast);
2752 case PROP_BRIGHTNESS:
2753 g_value_set_int (value, xvimagesink->brightness);
2755 case PROP_SATURATION:
2756 g_value_set_int (value, xvimagesink->saturation);
2759 g_value_set_string (value, xvimagesink->display_name);
2761 case PROP_SYNCHRONOUS:
2762 g_value_set_boolean (value, xvimagesink->synchronous);
2764 case PROP_PIXEL_ASPECT_RATIO:
2765 if (xvimagesink->par)
2766 g_value_transform (xvimagesink->par, value);
2768 case PROP_FORCE_ASPECT_RATIO:
2769 g_value_set_boolean (value, xvimagesink->keep_aspect);
2771 case PROP_HANDLE_EVENTS:
2772 g_value_set_boolean (value, xvimagesink->handle_events);
2776 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2778 g_value_set_string (value, adaptor_no_s);
2779 g_free (adaptor_no_s);
2782 case PROP_DEVICE_NAME:
2783 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2784 g_value_set_string (value,
2785 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2787 g_value_set_string (value, NULL);
2790 case PROP_HANDLE_EXPOSE:
2791 g_value_set_boolean (value, xvimagesink->handle_expose);
2793 case PROP_DOUBLE_BUFFER:
2794 g_value_set_boolean (value, xvimagesink->double_buffer);
2796 case PROP_AUTOPAINT_COLORKEY:
2797 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
2800 g_value_set_int (value, xvimagesink->colorkey);
2802 case PROP_DRAW_BORDERS:
2803 g_value_set_boolean (value, xvimagesink->draw_borders);
2805 case PROP_WINDOW_WIDTH:
2806 if (xvimagesink->xwindow)
2807 g_value_set_uint64 (value, xvimagesink->xwindow->width);
2809 g_value_set_uint64 (value, 0);
2811 case PROP_WINDOW_HEIGHT:
2812 if (xvimagesink->xwindow)
2813 g_value_set_uint64 (value, xvimagesink->xwindow->height);
2815 g_value_set_uint64 (value, 0);
2818 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2824 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2828 GST_OBJECT_LOCK (xvimagesink);
2829 xvimagesink->running = FALSE;
2830 /* grab thread and mark it as NULL */
2831 thread = xvimagesink->event_thread;
2832 xvimagesink->event_thread = NULL;
2833 GST_OBJECT_UNLOCK (xvimagesink);
2835 /* Wait for our event thread to finish before we clean up our stuff. */
2837 g_thread_join (thread);
2839 if (xvimagesink->cur_image) {
2840 gst_buffer_unref (xvimagesink->cur_image);
2841 xvimagesink->cur_image = NULL;
2844 g_mutex_lock (xvimagesink->flow_lock);
2846 if (xvimagesink->pool) {
2847 gst_object_unref (xvimagesink->pool);
2848 xvimagesink->pool = NULL;
2851 if (xvimagesink->xwindow) {
2852 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2853 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2854 xvimagesink->xwindow = NULL;
2856 g_mutex_unlock (xvimagesink->flow_lock);
2858 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
2859 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
2860 xvimagesink->have_render_rect = FALSE;
2862 gst_xvimagesink_xcontext_clear (xvimagesink);
2865 /* Finalize is called only once, dispose can be called multiple times.
2866 * We use mutexes and don't reset stuff to NULL here so let's register
2869 gst_xvimagesink_finalize (GObject * object)
2871 GstXvImageSink *xvimagesink;
2873 xvimagesink = GST_XVIMAGESINK (object);
2875 gst_xvimagesink_reset (xvimagesink);
2877 if (xvimagesink->display_name) {
2878 g_free (xvimagesink->display_name);
2879 xvimagesink->display_name = NULL;
2882 if (xvimagesink->par) {
2883 g_free (xvimagesink->par);
2884 xvimagesink->par = NULL;
2886 if (xvimagesink->x_lock) {
2887 g_mutex_free (xvimagesink->x_lock);
2888 xvimagesink->x_lock = NULL;
2890 if (xvimagesink->flow_lock) {
2891 g_mutex_free (xvimagesink->flow_lock);
2892 xvimagesink->flow_lock = NULL;
2895 g_free (xvimagesink->media_title);
2897 G_OBJECT_CLASS (parent_class)->finalize (object);
2901 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
2903 xvimagesink->display_name = NULL;
2904 xvimagesink->adaptor_no = 0;
2905 xvimagesink->xcontext = NULL;
2906 xvimagesink->xwindow = NULL;
2907 xvimagesink->cur_image = NULL;
2909 xvimagesink->hue = xvimagesink->saturation = 0;
2910 xvimagesink->contrast = xvimagesink->brightness = 0;
2911 xvimagesink->cb_changed = FALSE;
2913 xvimagesink->fps_n = 0;
2914 xvimagesink->fps_d = 0;
2915 xvimagesink->video_width = 0;
2916 xvimagesink->video_height = 0;
2918 xvimagesink->x_lock = g_mutex_new ();
2919 xvimagesink->flow_lock = g_mutex_new ();
2921 xvimagesink->pool = NULL;
2923 xvimagesink->synchronous = FALSE;
2924 xvimagesink->double_buffer = TRUE;
2925 xvimagesink->running = FALSE;
2926 xvimagesink->keep_aspect = FALSE;
2927 xvimagesink->handle_events = TRUE;
2928 xvimagesink->par = NULL;
2929 xvimagesink->handle_expose = TRUE;
2930 xvimagesink->autopaint_colorkey = TRUE;
2932 /* on 16bit displays this becomes r,g,b = 1,2,3
2933 * on 24bit displays this becomes r,g,b = 8,8,16
2934 * as a port atom value
2936 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
2937 xvimagesink->draw_borders = TRUE;
2941 gst_xvimagesink_base_init (gpointer g_class)
2943 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2945 gst_element_class_set_details_simple (element_class,
2946 "Video sink", "Sink/Video",
2947 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2949 gst_element_class_add_pad_template (element_class,
2950 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2954 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
2956 GObjectClass *gobject_class;
2957 GstElementClass *gstelement_class;
2958 GstBaseSinkClass *gstbasesink_class;
2959 GstVideoSinkClass *videosink_class;
2961 gobject_class = (GObjectClass *) klass;
2962 gstelement_class = (GstElementClass *) klass;
2963 gstbasesink_class = (GstBaseSinkClass *) klass;
2964 videosink_class = (GstVideoSinkClass *) klass;
2966 parent_class = g_type_class_peek_parent (klass);
2968 gobject_class->set_property = gst_xvimagesink_set_property;
2969 gobject_class->get_property = gst_xvimagesink_get_property;
2971 g_object_class_install_property (gobject_class, PROP_CONTRAST,
2972 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
2973 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2974 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
2975 g_param_spec_int ("brightness", "Brightness",
2976 "The brightness of the video", -1000, 1000, 0,
2977 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2978 g_object_class_install_property (gobject_class, PROP_HUE,
2979 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
2980 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2981 g_object_class_install_property (gobject_class, PROP_SATURATION,
2982 g_param_spec_int ("saturation", "Saturation",
2983 "The saturation of the video", -1000, 1000, 0,
2984 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2985 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2986 g_param_spec_string ("display", "Display", "X Display name", NULL,
2987 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2988 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2989 g_param_spec_boolean ("synchronous", "Synchronous",
2990 "When enabled, runs "
2991 "the X display in synchronous mode. (used only for debugging)", FALSE,
2992 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2993 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2994 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2995 "The pixel aspect ratio of the device", "1/1",
2996 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2997 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2998 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2999 "When enabled, scaling will respect original aspect ratio", FALSE,
3000 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3001 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
3002 g_param_spec_boolean ("handle-events", "Handle XEvents",
3003 "When enabled, XEvents will be selected and handled", TRUE,
3004 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3005 g_object_class_install_property (gobject_class, PROP_DEVICE,
3006 g_param_spec_string ("device", "Adaptor number",
3007 "The number of the video adaptor", "0",
3008 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3009 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
3010 g_param_spec_string ("device-name", "Adaptor name",
3011 "The name of the video adaptor", NULL,
3012 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3014 * GstXvImageSink:handle-expose
3016 * When enabled, the current frame will always be drawn in response to X
3021 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
3022 g_param_spec_boolean ("handle-expose", "Handle expose",
3024 "the current frame will always be drawn in response to X Expose "
3025 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3028 * GstXvImageSink:double-buffer
3030 * Whether to double-buffer the output.
3034 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
3035 g_param_spec_boolean ("double-buffer", "Double-buffer",
3036 "Whether to double-buffer the output", TRUE,
3037 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3039 * GstXvImageSink:autopaint-colorkey
3041 * Whether to autofill overlay with colorkey
3045 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
3046 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
3047 "Whether to autofill overlay with colorkey", TRUE,
3048 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3050 * GstXvImageSink:colorkey
3052 * Color to use for the overlay mask.
3056 g_object_class_install_property (gobject_class, PROP_COLORKEY,
3057 g_param_spec_int ("colorkey", "Colorkey",
3058 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
3059 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3062 * GstXvImageSink:draw-borders
3064 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
3065 * unused parts of the video area.
3069 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
3070 g_param_spec_boolean ("draw-borders", "Colorkey",
3071 "Draw black borders to fill unused area in force-aspect-ratio mode",
3072 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3075 * GstXvImageSink:window-width
3077 * Actual width of the video window.
3081 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
3082 g_param_spec_uint64 ("window-width", "window-width",
3083 "Width of the window", 0, G_MAXUINT64, 0,
3084 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3087 * GstXvImageSink:window-height
3089 * Actual height of the video window.
3093 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
3094 g_param_spec_uint64 ("window-height", "window-height",
3095 "Height of the window", 0, G_MAXUINT64, 0,
3096 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3098 gobject_class->finalize = gst_xvimagesink_finalize;
3100 gstelement_class->change_state =
3101 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3103 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3104 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3105 gstbasesink_class->buffer_alloc =
3106 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3107 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3108 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3110 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3113 /* ============================================================= */
3115 /* Public Methods */
3117 /* ============================================================= */
3119 /* =========================================== */
3121 /* Object typing & Creation */
3123 /* =========================================== */
3126 gst_xvimagesink_get_type (void)
3128 static GType xvimagesink_type = 0;
3130 if (!xvimagesink_type) {
3131 static const GTypeInfo xvimagesink_info = {
3132 sizeof (GstXvImageSinkClass),
3133 gst_xvimagesink_base_init,
3135 (GClassInitFunc) gst_xvimagesink_class_init,
3138 sizeof (GstXvImageSink),
3140 (GInstanceInitFunc) gst_xvimagesink_init,
3142 static const GInterfaceInfo iface_info = {
3143 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3147 static const GInterfaceInfo navigation_info = {
3148 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3152 static const GInterfaceInfo overlay_info = {
3153 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3157 static const GInterfaceInfo colorbalance_info = {
3158 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3162 static const GInterfaceInfo propertyprobe_info = {
3163 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3167 xvimagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
3168 "GstXvImageSink", &xvimagesink_info, 0);
3170 g_type_add_interface_static (xvimagesink_type,
3171 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3172 g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
3174 g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY,
3176 g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE,
3177 &colorbalance_info);
3178 g_type_add_interface_static (xvimagesink_type, GST_TYPE_PROPERTY_PROBE,
3179 &propertyprobe_info);
3180 /* register type and create class in a more safe place instead of at
3181 * runtime since the type registration and class creation is not
3183 g_type_class_ref (gst_xvimage_buffer_pool_get_type ());
3186 return xvimagesink_type;