2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the #GstVideoOverlay interface and will then render
29 * video frames in this drawable. If no Window ID was provided by the
30 * application, the element will create its own internal window and render
34 * <title>Scaling</title>
36 * The XVideo extension, when it's available, handles hardware accelerated
37 * scaling of video frames. This means that the element will just accept
38 * incoming video frames no matter their geometry and will then put them to the
39 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
40 * property it is possible to enforce scaling with a constant aspect ratio,
41 * which means drawing black borders around the video frame.
45 * <title>Events</title>
47 * XvImageSink creates a thread to handle events coming from the drawable. There
48 * are several kind of events that can be grouped in 2 big categories: input
49 * events and window state related events. Input events will be translated to
50 * navigation events and pushed upstream for other elements to react on them.
51 * This includes events such as pointer moves, key press/release, clicks etc...
52 * Other events are used to handle the drawable appearance even when the data
53 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
54 * paused, it will receive expose events from the drawable and draw the latest
55 * frame with correct borders/aspect-ratio.
59 * <title>Pixel aspect ratio</title>
61 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
62 * the display specified in the #GstXvImageSink:display property or the
63 * default display if nothing specified. Once this connection is open it will
64 * inspect the display configuration including the physical display geometry and
65 * then calculate the pixel aspect ratio. When receiving video frames with a
66 * different pixel aspect ratio, XvImageSink will use hardware scaling to
67 * display the video frames correctly on display's pixel aspect ratio.
68 * Sometimes the calculated pixel aspect ratio can be wrong, it is
69 * then possible to enforce a specific pixel aspect ratio using the
70 * #GstXvImageSink:pixel-aspect-ratio property.
74 * <title>Examples</title>
76 * gst-launch-1.0 -v videotestsrc ! xvimagesink
77 * ]| A pipeline to test hardware scaling.
78 * When the test video signal appears you can resize the window and see that
79 * video frames are scaled through hardware (no extra CPU cost). By default
80 * the image will never be distorted when scaled, instead black borders will
83 * gst-launch-1.0 -v videotestsrc ! xvimagesink force-aspect-ratio=false
84 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to
85 * false. You can observe that no borders are drawn around the scaled image
86 * now and it will be distorted to fill the entire frame instead of respecting
89 * gst-launch-1.0 -v videotestsrc ! navigationtest ! xvimagesink
90 * ]| A pipeline to test navigation events.
91 * While moving the mouse pointer over the test signal you will see a black box
92 * following the mouse pointer. If you press the mouse button somewhere on the
93 * video and release it somewhere else a green box will appear where you pressed
94 * the button and a red one where you released it. (The navigationtest element
95 * is part of gst-plugins-good.) You can observe here that even if the images
96 * are scaled through hardware the pointer coordinates are converted back to the
97 * original video frame geometry so that the box can be drawn to the correct
98 * position. This also handles borders correctly, limiting coordinates to the
101 * gst-launch-1.0 -v videotestsrc ! video/x-raw, pixel-aspect-ratio=4/3 ! xvimagesink
102 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
103 * videotestsrc, in most cases the pixel aspect ratio of the display will be
104 * 1/1. This means that XvImageSink will have to do the scaling to convert
105 * incoming frames to a size that will match the display pixel aspect ratio
106 * (from 320x240 to 320x180 in this case).
108 * gst-launch-1.0 -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
109 * ]| Demonstrates how to use the colorbalance interface.
113 /* for developers: there are two useful tools : xvinfo and xvattr */
120 #include <gst/video/navigation.h>
121 #include <gst/video/videooverlay.h>
122 #include <gst/video/colorbalance.h>
123 /* Helper functions */
124 #include <gst/video/gstvideometa.h>
127 #include "xvimagesink.h"
128 #include "xvimageallocator.h"
130 /* Debugging category */
131 #include <gst/gstinfo.h>
133 /* for XkbKeycodeToKeysym */
134 #include <X11/XKBlib.h>
136 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
137 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
138 #define GST_CAT_DEFAULT gst_debug_xvimagesink
143 unsigned long functions;
144 unsigned long decorations;
146 unsigned long status;
148 MotifWmHints, MwmHints;
150 #define MWM_HINTS_DECORATIONS (1L << 1)
152 static gboolean gst_xvimagesink_open (GstXvImageSink * xvimagesink);
153 static void gst_xvimagesink_close (GstXvImageSink * xvimagesink);
154 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
156 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
158 /* Default template - initiated with class struct to allow gst-register to work
160 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
161 GST_STATIC_PAD_TEMPLATE ("sink",
164 GST_STATIC_CAPS ("video/x-raw, "
165 "framerate = (fraction) [ 0, MAX ], "
166 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
178 PROP_PIXEL_ASPECT_RATIO,
179 PROP_FORCE_ASPECT_RATIO,
185 PROP_AUTOPAINT_COLORKEY,
192 /* ============================================================= */
196 /* ============================================================= */
198 /* =========================================== */
200 /* Object typing & Creation */
202 /* =========================================== */
203 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
204 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
206 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
208 #define gst_xvimagesink_parent_class parent_class
209 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
210 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
211 gst_xvimagesink_navigation_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
213 gst_xvimagesink_video_overlay_init);
214 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
215 gst_xvimagesink_colorbalance_init));
218 /* ============================================================= */
220 /* Private Methods */
222 /* ============================================================= */
225 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
226 * if no window was available */
228 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
230 GstXvImageMemory *mem;
231 GstVideoCropMeta *crop;
232 GstVideoRectangle result;
233 gboolean draw_border = FALSE;
234 GstVideoRectangle src = { 0, };
235 GstVideoRectangle dst = { 0, };
236 GstVideoRectangle mem_crop;
239 /* We take the flow_lock. If expose is in there we don't want to run
240 concurrently from the data flow thread */
241 g_mutex_lock (&xvimagesink->flow_lock);
243 if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
244 g_mutex_unlock (&xvimagesink->flow_lock);
248 /* Draw borders when displaying the first frame. After this
249 draw borders only on expose event or after a size change. */
250 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
251 draw_border = xvimagesink->draw_borders;
252 xvimagesink->redraw_border = FALSE;
255 /* Store a reference to the last image we put, lose the previous one */
256 if (xvimage && xvimagesink->cur_image != xvimage) {
257 if (xvimagesink->cur_image) {
258 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
259 gst_buffer_unref (xvimagesink->cur_image);
261 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
262 xvimagesink->cur_image = gst_buffer_ref (xvimage);
265 /* Expose sends a NULL image, we take the latest frame */
267 if (xvimagesink->cur_image) {
269 xvimage = xvimagesink->cur_image;
271 g_mutex_unlock (&xvimagesink->flow_lock);
276 mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
277 gst_xvimage_memory_get_crop (mem, &mem_crop);
279 crop = gst_buffer_get_video_crop_meta (xvimage);
282 src.x = crop->x + mem_crop.x;
283 src.y = crop->y + mem_crop.y;
285 src.h = crop->height;
286 GST_LOG_OBJECT (xvimagesink,
287 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
292 if (xvimagesink->keep_aspect) {
295 /* We take the size of the source material as it was negotiated and
296 * corrected for DAR. This size can be different from the cropped size in
297 * which case the image will be scaled to fit the negotiated size. */
298 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
299 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
300 dst.w = xwindow->render_rect.w;
301 dst.h = xwindow->render_rect.h;
303 gst_video_sink_center_rect (s, dst, &result, TRUE);
304 result.x += xwindow->render_rect.x;
305 result.y += xwindow->render_rect.y;
307 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
310 gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
312 g_mutex_unlock (&xvimagesink->flow_lock);
318 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
319 GstXWindow * xwindow, const gchar * media_title)
322 g_free (xvimagesink->media_title);
323 xvimagesink->media_title = g_strdup (media_title);
326 /* we have a window */
327 const gchar *app_name;
328 const gchar *title = NULL;
329 gchar *title_mem = NULL;
331 /* set application name as a title */
332 app_name = g_get_application_name ();
334 if (app_name && xvimagesink->media_title) {
335 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
337 } else if (app_name) {
339 } else if (xvimagesink->media_title) {
340 title = xvimagesink->media_title;
343 gst_xwindow_set_title (xwindow, title);
348 /* This function handles a GstXWindow creation
349 * The width and height are the actual pixel size on the display */
351 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
352 gint width, gint height)
354 GstXWindow *xwindow = NULL;
355 GstXvContext *context;
357 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
359 context = xvimagesink->context;
361 xwindow = gst_xvcontext_create_xwindow (context, width, height);
363 /* set application name as a title */
364 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
366 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
368 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
375 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
377 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
379 /* Update the window geometry */
380 g_mutex_lock (&xvimagesink->flow_lock);
381 if (G_LIKELY (xvimagesink->xwindow))
382 gst_xwindow_update_geometry (xvimagesink->xwindow);
383 g_mutex_unlock (&xvimagesink->flow_lock);
386 /* This function commits our internal colorbalance settings to our grabbed Xv
387 port. If the context is not initialized yet it simply returns */
389 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
391 GstXvContext *context;
393 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
395 /* If we haven't initialized the X context we can't update anything */
396 if ((context = xvimagesink->context) == NULL)
399 gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
402 /* This function handles XEvents that might be in the queue. It generates
403 GstEvent that will be sent upstream in the pipeline to handle interactivity
404 and navigation. It will also listen for configure events on the window to
405 trigger caps renegotiation so on the fly software scaling can work. */
407 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
410 guint pointer_x = 0, pointer_y = 0;
411 gboolean pointer_moved = FALSE;
412 gboolean exposed = FALSE, configured = FALSE;
414 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
416 /* Handle Interaction, produces navigation events */
418 /* We get all pointer motion events, only the last position is
420 g_mutex_lock (&xvimagesink->flow_lock);
421 g_mutex_lock (&xvimagesink->context->lock);
422 while (XCheckWindowEvent (xvimagesink->context->disp,
423 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
424 g_mutex_unlock (&xvimagesink->context->lock);
425 g_mutex_unlock (&xvimagesink->flow_lock);
429 pointer_x = e.xmotion.x;
430 pointer_y = e.xmotion.y;
431 pointer_moved = TRUE;
436 g_mutex_lock (&xvimagesink->flow_lock);
437 g_mutex_lock (&xvimagesink->context->lock);
441 g_mutex_unlock (&xvimagesink->context->lock);
442 g_mutex_unlock (&xvimagesink->flow_lock);
444 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
445 pointer_x, pointer_y);
446 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
447 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
449 g_mutex_lock (&xvimagesink->flow_lock);
450 g_mutex_lock (&xvimagesink->context->lock);
453 /* We get all events on our window to throw them upstream */
454 while (XCheckWindowEvent (xvimagesink->context->disp,
455 xvimagesink->xwindow->win,
456 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
459 const char *key_str = NULL;
461 /* We lock only for the X function call */
462 g_mutex_unlock (&xvimagesink->context->lock);
463 g_mutex_unlock (&xvimagesink->flow_lock);
467 /* Mouse button pressed over our window. We send upstream
468 events for interactivity/navigation */
469 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
470 e.xbutton.button, e.xbutton.x, e.xbutton.y);
471 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
472 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
475 /* Mouse button released over our window. We send upstream
476 events for interactivity/navigation */
477 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
478 e.xbutton.button, e.xbutton.x, e.xbutton.y);
479 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
480 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
484 /* Key pressed/released over our window. We send upstream
485 events for interactivity/navigation */
486 g_mutex_lock (&xvimagesink->context->lock);
487 keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
488 e.xkey.keycode, 0, 0);
489 if (keysym != NoSymbol) {
490 key_str = XKeysymToString (keysym);
494 g_mutex_unlock (&xvimagesink->context->lock);
495 GST_DEBUG_OBJECT (xvimagesink,
496 "key %d pressed over window at %d,%d (%s)",
497 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
498 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
499 e.type == KeyPress ? "key-press" : "key-release", key_str);
502 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
505 g_mutex_lock (&xvimagesink->flow_lock);
506 g_mutex_lock (&xvimagesink->context->lock);
510 while (XCheckWindowEvent (xvimagesink->context->disp,
511 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
516 case ConfigureNotify:
517 g_mutex_unlock (&xvimagesink->context->lock);
518 g_mutex_unlock (&xvimagesink->flow_lock);
520 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
522 g_mutex_lock (&xvimagesink->flow_lock);
523 g_mutex_lock (&xvimagesink->context->lock);
531 if (xvimagesink->handle_expose && (exposed || configured)) {
532 g_mutex_unlock (&xvimagesink->context->lock);
533 g_mutex_unlock (&xvimagesink->flow_lock);
535 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
537 g_mutex_lock (&xvimagesink->flow_lock);
538 g_mutex_lock (&xvimagesink->context->lock);
541 /* Handle Display events */
542 while (XPending (xvimagesink->context->disp)) {
543 XNextEvent (xvimagesink->context->disp, &e);
549 wm_delete = XInternAtom (xvimagesink->context->disp,
550 "WM_DELETE_WINDOW", True);
551 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
552 /* Handle window deletion by posting an error on the bus */
553 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
554 ("Output window was closed"), (NULL));
556 g_mutex_unlock (&xvimagesink->context->lock);
557 gst_xwindow_destroy (xvimagesink->xwindow);
558 xvimagesink->xwindow = NULL;
559 g_mutex_lock (&xvimagesink->context->lock);
568 g_mutex_unlock (&xvimagesink->context->lock);
569 g_mutex_unlock (&xvimagesink->flow_lock);
573 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
575 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
577 GST_OBJECT_LOCK (xvimagesink);
578 while (xvimagesink->running) {
579 GST_OBJECT_UNLOCK (xvimagesink);
581 if (xvimagesink->xwindow) {
582 gst_xvimagesink_handle_xevents (xvimagesink);
584 /* FIXME: do we want to align this with the framerate or anything else? */
585 g_usleep (G_USEC_PER_SEC / 20);
587 GST_OBJECT_LOCK (xvimagesink);
589 GST_OBJECT_UNLOCK (xvimagesink);
595 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
597 GThread *thread = NULL;
599 /* don't start the thread too early */
600 if (xvimagesink->context == NULL) {
604 GST_OBJECT_LOCK (xvimagesink);
605 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
606 if (!xvimagesink->event_thread) {
607 /* Setup our event listening thread */
608 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
609 xvimagesink->handle_expose, xvimagesink->handle_events);
610 xvimagesink->running = TRUE;
611 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
612 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
615 if (xvimagesink->event_thread) {
616 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
617 xvimagesink->handle_expose, xvimagesink->handle_events);
618 xvimagesink->running = FALSE;
619 /* grab thread and mark it as NULL */
620 thread = xvimagesink->event_thread;
621 xvimagesink->event_thread = NULL;
624 GST_OBJECT_UNLOCK (xvimagesink);
626 /* Wait for our event thread to finish */
628 g_thread_join (thread);
635 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
637 GstXvImageSink *xvimagesink;
640 xvimagesink = GST_XVIMAGESINK (bsink);
642 if (xvimagesink->context) {
644 return gst_caps_intersect_full (filter, xvimagesink->context->caps,
645 GST_CAPS_INTERSECT_FIRST);
647 return gst_caps_ref (xvimagesink->context->caps);
650 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
652 GstCaps *intersection;
655 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
656 gst_caps_unref (caps);
662 static GstBufferPool *
663 gst_xvimagesink_create_pool (GstXvImageSink * xvimagesink, GstCaps * caps,
664 gsize size, gint min)
667 GstStructure *config;
669 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
671 config = gst_buffer_pool_get_config (pool);
672 gst_buffer_pool_config_set_params (config, caps, size, min, 0);
674 if (!gst_buffer_pool_set_config (pool, config))
681 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
682 gst_object_unref (pool);
688 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
690 GstXvImageSink *xvimagesink;
691 GstXvContext *context;
692 GstBufferPool *newpool, *oldpool;
694 guint32 im_format = 0;
695 gint video_par_n, video_par_d; /* video's PAR */
696 gint display_par_n, display_par_d; /* display's PAR */
699 xvimagesink = GST_XVIMAGESINK (bsink);
700 context = xvimagesink->context;
702 GST_DEBUG_OBJECT (xvimagesink,
703 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
704 GST_PTR_FORMAT, context->caps, caps);
706 if (!gst_caps_can_intersect (context->caps, caps))
707 goto incompatible_caps;
709 if (!gst_video_info_from_caps (&info, caps))
712 xvimagesink->fps_n = info.fps_n;
713 xvimagesink->fps_d = info.fps_d;
715 xvimagesink->video_width = info.width;
716 xvimagesink->video_height = info.height;
718 im_format = gst_xvcontext_get_format_from_info (context, &info);
722 gst_xvcontext_set_colorimetry (context, &info.colorimetry);
724 /* get aspect ratio from caps if it's present, and
725 * convert video width and height to a display width and height
726 * using wd / hd = wv / hv * PARv / PARd */
728 /* get video's PAR */
729 video_par_n = info.par_n;
730 video_par_d = info.par_d;
732 /* get display's PAR */
733 if (xvimagesink->par) {
734 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
735 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
741 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
742 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
745 GST_DEBUG_OBJECT (xvimagesink,
746 "video width/height: %dx%d, calculated display ratio: %d/%d",
747 info.width, info.height, num, den);
749 /* now find a width x height that respects this display ratio.
750 * prefer those that have one of w/h the same as the incoming video
751 * using wd / hd = num / den */
753 /* start with same height, because of interlaced video */
754 /* check hd / den is an integer scale factor, and scale wd with the PAR */
755 if (info.height % den == 0) {
756 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
757 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
758 gst_util_uint64_scale_int (info.height, num, den);
759 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
760 } else if (info.width % num == 0) {
761 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
762 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
763 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
764 gst_util_uint64_scale_int (info.width, den, num);
766 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
767 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
768 gst_util_uint64_scale_int (info.height, num, den);
769 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
771 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
772 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
774 /* Notify application to set xwindow id now */
775 g_mutex_lock (&xvimagesink->flow_lock);
776 if (!xvimagesink->xwindow) {
777 g_mutex_unlock (&xvimagesink->flow_lock);
778 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
780 g_mutex_unlock (&xvimagesink->flow_lock);
783 /* Creating our window and our image with the display size in pixels */
784 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
785 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
786 goto no_display_size;
788 g_mutex_lock (&xvimagesink->flow_lock);
789 if (!xvimagesink->xwindow) {
790 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
791 GST_VIDEO_SINK_WIDTH (xvimagesink),
792 GST_VIDEO_SINK_HEIGHT (xvimagesink));
795 xvimagesink->info = info;
797 /* After a resize, we want to redraw the borders in case the new frame size
798 * doesn't cover the same area */
799 xvimagesink->redraw_border = TRUE;
801 /* create a new pool for the new configuration */
802 newpool = gst_xvimagesink_create_pool (xvimagesink, caps, info.size, 2);
804 /* we don't activate the internal pool yet as it may not be needed */
805 oldpool = xvimagesink->pool;
806 xvimagesink->pool = newpool;
807 g_mutex_unlock (&xvimagesink->flow_lock);
809 /* deactivate and unref the old internal pool */
811 gst_buffer_pool_set_active (oldpool, FALSE);
812 gst_object_unref (oldpool);
820 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
825 GST_DEBUG_OBJECT (xvimagesink,
826 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
831 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
832 ("Error calculating the output display ratio of the video."));
837 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
838 ("Error calculating the output display ratio of the video."));
843 static GstStateChangeReturn
844 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
846 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
847 GstXvImageSink *xvimagesink;
849 xvimagesink = GST_XVIMAGESINK (element);
851 switch (transition) {
852 case GST_STATE_CHANGE_NULL_TO_READY:
853 if (!gst_xvimagesink_open (xvimagesink))
856 case GST_STATE_CHANGE_READY_TO_PAUSED:
858 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
860 case GST_STATE_CHANGE_PAUSED_TO_READY:
866 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
868 switch (transition) {
869 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
871 case GST_STATE_CHANGE_PAUSED_TO_READY:
872 xvimagesink->fps_n = 0;
873 xvimagesink->fps_d = 1;
874 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
875 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
876 g_mutex_lock (&xvimagesink->flow_lock);
877 if (xvimagesink->pool)
878 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
879 g_mutex_unlock (&xvimagesink->flow_lock);
881 case GST_STATE_CHANGE_READY_TO_NULL:
882 gst_xvimagesink_close (xvimagesink);
891 return GST_STATE_CHANGE_FAILURE;
896 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
897 GstClockTime * start, GstClockTime * end)
899 GstXvImageSink *xvimagesink;
901 xvimagesink = GST_XVIMAGESINK (bsink);
903 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
904 *start = GST_BUFFER_TIMESTAMP (buf);
905 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
906 *end = *start + GST_BUFFER_DURATION (buf);
908 if (xvimagesink->fps_n > 0) {
910 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
918 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
921 GstXvImageSink *xvimagesink;
922 GstBuffer *to_put = NULL;
925 xvimagesink = GST_XVIMAGESINK (vsink);
927 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
928 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
929 /* If this buffer has been allocated using our buffer management we simply
930 put the ximage which is in the PRIVATE pointer */
931 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
936 GstVideoFrame src, dest;
937 GstBufferPoolAcquireParams params = { 0, };
939 /* Else we have to copy the data into our private image, */
940 /* if we have one... */
941 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
943 /* we should have a pool, configured in setcaps */
944 if (xvimagesink->pool == NULL)
947 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
948 goto activate_failed;
950 /* take a buffer from our pool, if there is no buffer in the pool something
951 * is seriously wrong, waiting for the pool here might deadlock when we try
952 * to go to PAUSED because we never flush the pool then. */
953 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
954 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
955 if (res != GST_FLOW_OK)
958 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
959 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
961 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
964 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
965 gst_video_frame_unmap (&src);
969 gst_video_frame_copy (&dest, &src);
971 gst_video_frame_unmap (&dest);
972 gst_video_frame_unmap (&src);
975 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
980 gst_buffer_unref (to_put);
987 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
988 ("Internal error: can't allocate images"),
989 ("We don't have a bufferpool negotiated"));
990 return GST_FLOW_ERROR;
994 /* No image available. That's very bad ! */
995 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1000 /* No Window available to put our image into */
1001 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1007 /* No Window available to put our image into */
1008 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1009 res = GST_FLOW_ERROR;
1014 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1015 res = GST_FLOW_ERROR;
1021 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1023 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1025 switch (GST_EVENT_TYPE (event)) {
1026 case GST_EVENT_TAG:{
1028 gchar *title = NULL;
1030 gst_event_parse_tag (event, &l);
1031 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1034 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1035 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1045 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1049 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1051 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1052 GstBufferPool *pool = NULL;
1057 gst_query_parse_allocation (query, &caps, &need_pool);
1065 if (!gst_video_info_from_caps (&info, caps))
1068 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1069 pool = gst_xvimagesink_create_pool (xvimagesink, caps, info.size, 0);
1071 /* the normal size of a frame */
1079 /* we need at least 2 buffer because we hold on to the last one */
1080 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1081 gst_object_unref (pool);
1084 /* we also support various metadata */
1085 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1086 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1093 GST_DEBUG_OBJECT (bsink, "no caps specified");
1098 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1103 /* Already warned in create_pool */
1108 /* Interfaces stuff */
1110 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1111 GstStructure * structure)
1113 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1115 gboolean handled = FALSE;
1116 GstEvent *event = NULL;
1118 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1119 GstVideoRectangle src = { 0, };
1120 GstVideoRectangle dst = { 0, };
1121 GstVideoRectangle result;
1122 gdouble x, y, xscale = 1.0, yscale = 1.0;
1123 GstXWindow *xwindow;
1125 /* We take the flow_lock while we look at the window */
1126 g_mutex_lock (&xvimagesink->flow_lock);
1128 if (!(xwindow = xvimagesink->xwindow)) {
1129 g_mutex_unlock (&xvimagesink->flow_lock);
1133 if (xvimagesink->keep_aspect) {
1134 /* We get the frame position using the calculated geometry from _setcaps
1135 that respect pixel aspect ratios */
1136 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1137 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1138 dst.w = xwindow->render_rect.w;
1139 dst.h = xwindow->render_rect.h;
1141 gst_video_sink_center_rect (src, dst, &result, TRUE);
1142 result.x += xwindow->render_rect.x;
1143 result.y += xwindow->render_rect.y;
1145 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1148 g_mutex_unlock (&xvimagesink->flow_lock);
1150 /* We calculate scaling using the original video frames geometry to include
1151 pixel aspect ratio scaling. */
1152 xscale = (gdouble) xvimagesink->video_width / result.w;
1153 yscale = (gdouble) xvimagesink->video_height / result.h;
1155 /* Converting pointer coordinates to the non scaled geometry */
1156 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1157 x = MIN (x, result.x + result.w);
1158 x = MAX (x - result.x, 0);
1159 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1160 (gdouble) x * xscale, NULL);
1162 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1163 y = MIN (y, result.y + result.h);
1164 y = MAX (y - result.y, 0);
1165 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1166 (gdouble) y * yscale, NULL);
1169 event = gst_event_new_navigation (structure);
1170 gst_event_ref (event);
1171 handled = gst_pad_send_event (peer, event);
1172 gst_object_unref (peer);
1175 if (!handled && event) {
1176 gst_element_post_message ((GstElement *) xvimagesink,
1177 gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1181 gst_event_unref (event);
1185 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1187 iface->send_event = gst_xvimagesink_navigation_send_event;
1191 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1193 XID xwindow_id = id;
1194 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1195 GstXWindow *xwindow = NULL;
1196 GstXvContext *context;
1198 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1200 g_mutex_lock (&xvimagesink->flow_lock);
1202 /* If we already use that window return */
1203 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1204 g_mutex_unlock (&xvimagesink->flow_lock);
1208 /* If the element has not initialized the X11 context try to do so */
1209 if (!xvimagesink->context &&
1210 !(xvimagesink->context =
1211 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1212 g_mutex_unlock (&xvimagesink->flow_lock);
1213 /* we have thrown a GST_ELEMENT_ERROR now */
1217 context = xvimagesink->context;
1219 gst_xvimagesink_update_colorbalance (xvimagesink);
1221 /* If a window is there already we destroy it */
1222 if (xvimagesink->xwindow) {
1223 gst_xwindow_destroy (xvimagesink->xwindow);
1224 xvimagesink->xwindow = NULL;
1227 /* If the xid is 0 we go back to an internal window */
1228 if (xwindow_id == 0) {
1229 /* If no width/height caps nego did not happen window will be created
1230 during caps nego then */
1231 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1232 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1234 gst_xvimagesink_xwindow_new (xvimagesink,
1235 GST_VIDEO_SINK_WIDTH (xvimagesink),
1236 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1239 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1240 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1244 xvimagesink->xwindow = xwindow;
1246 g_mutex_unlock (&xvimagesink->flow_lock);
1250 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1252 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1254 GST_DEBUG ("doing expose");
1255 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1256 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1260 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1261 gboolean handle_events)
1263 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1265 g_mutex_lock (&xvimagesink->flow_lock);
1266 xvimagesink->handle_events = handle_events;
1267 if (G_LIKELY (xvimagesink->xwindow))
1268 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1269 g_mutex_unlock (&xvimagesink->flow_lock);
1273 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1274 gint width, gint height)
1276 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1278 g_mutex_lock (&xvimagesink->flow_lock);
1279 if (G_LIKELY (xvimagesink->xwindow))
1280 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1282 g_mutex_unlock (&xvimagesink->flow_lock);
1286 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1288 iface->set_window_handle = gst_xvimagesink_set_window_handle;
1289 iface->expose = gst_xvimagesink_expose;
1290 iface->handle_events = gst_xvimagesink_set_event_handling;
1291 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1294 static const GList *
1295 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1297 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1299 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1301 if (xvimagesink->context)
1302 return xvimagesink->context->channels_list;
1308 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1309 GstColorBalanceChannel * channel, gint value)
1311 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1313 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1314 g_return_if_fail (channel->label != NULL);
1316 xvimagesink->config.cb_changed = TRUE;
1318 /* Normalize val to [-1000, 1000] */
1319 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1320 (double) (channel->max_value - channel->min_value));
1322 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1323 xvimagesink->config.hue = value;
1324 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1325 xvimagesink->config.saturation = value;
1326 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1327 xvimagesink->config.contrast = value;
1328 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1329 xvimagesink->config.brightness = value;
1331 g_warning ("got an unknown channel %s", channel->label);
1335 gst_xvimagesink_update_colorbalance (xvimagesink);
1339 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1340 GstColorBalanceChannel * channel)
1342 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1345 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1346 g_return_val_if_fail (channel->label != NULL, 0);
1348 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1349 value = xvimagesink->config.hue;
1350 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1351 value = xvimagesink->config.saturation;
1352 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1353 value = xvimagesink->config.contrast;
1354 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1355 value = xvimagesink->config.brightness;
1357 g_warning ("got an unknown channel %s", channel->label);
1360 /* Normalize val to [channel->min_value, channel->max_value] */
1361 value = channel->min_value + (channel->max_value - channel->min_value) *
1362 (value + 1000) / 2000;
1367 static GstColorBalanceType
1368 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1370 return GST_COLOR_BALANCE_HARDWARE;
1374 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1376 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1377 iface->set_value = gst_xvimagesink_colorbalance_set_value;
1378 iface->get_value = gst_xvimagesink_colorbalance_get_value;
1379 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1383 static const GList *
1384 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1386 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1387 static GList *list = NULL;
1390 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1392 g_list_append (list, g_object_class_find_property (klass,
1393 "autopaint-colorkey"));
1395 g_list_append (list, g_object_class_find_property (klass,
1398 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1405 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1406 guint prop_id, const GParamSpec * pspec)
1408 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1412 case PROP_AUTOPAINT_COLORKEY:
1413 case PROP_DOUBLE_BUFFER:
1415 GST_DEBUG_OBJECT (xvimagesink,
1416 "probing device list and get capabilities");
1417 if (!xvimagesink->context) {
1418 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1419 xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1423 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1429 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1430 guint prop_id, const GParamSpec * pspec)
1432 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1433 gboolean ret = FALSE;
1437 case PROP_AUTOPAINT_COLORKEY:
1438 case PROP_DOUBLE_BUFFER:
1440 if (xvimagesink->context != NULL) {
1447 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1454 static GValueArray *
1455 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1456 guint prop_id, const GParamSpec * pspec)
1458 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1459 GValueArray *array = NULL;
1461 if (G_UNLIKELY (!xvimagesink->context)) {
1462 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1471 GValue value = { 0 };
1473 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1474 g_value_init (&value, G_TYPE_STRING);
1476 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1477 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1479 g_value_set_string (&value, adaptor_id_s);
1480 g_value_array_append (array, &value);
1481 g_free (adaptor_id_s);
1483 g_value_unset (&value);
1486 case PROP_AUTOPAINT_COLORKEY:
1487 if (xvimagesink->have_autopaint_colorkey) {
1488 GValue value = { 0 };
1490 array = g_value_array_new (2);
1491 g_value_init (&value, G_TYPE_BOOLEAN);
1492 g_value_set_boolean (&value, FALSE);
1493 g_value_array_append (array, &value);
1494 g_value_set_boolean (&value, TRUE);
1495 g_value_array_append (array, &value);
1496 g_value_unset (&value);
1499 case PROP_DOUBLE_BUFFER:
1500 if (xvimagesink->have_double_buffer) {
1501 GValue value = { 0 };
1503 array = g_value_array_new (2);
1504 g_value_init (&value, G_TYPE_BOOLEAN);
1505 g_value_set_boolean (&value, FALSE);
1506 g_value_array_append (array, &value);
1507 g_value_set_boolean (&value, TRUE);
1508 g_value_array_append (array, &value);
1509 g_value_unset (&value);
1513 if (xvimagesink->have_colorkey) {
1514 GValue value = { 0 };
1516 array = g_value_array_new (1);
1517 g_value_init (&value, GST_TYPE_INT_RANGE);
1518 gst_value_set_int_range (&value, 0, 0xffffff);
1519 g_value_array_append (array, &value);
1520 g_value_unset (&value);
1524 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1533 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1536 iface->get_properties = gst_xvimagesink_probe_get_properties;
1537 iface->probe_property = gst_xvimagesink_probe_probe_property;
1538 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1539 iface->get_values = gst_xvimagesink_probe_get_values;
1543 /* =========================================== */
1545 /* Init & Class init */
1547 /* =========================================== */
1550 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1551 const GValue * value, GParamSpec * pspec)
1553 GstXvImageSink *xvimagesink;
1555 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1557 xvimagesink = GST_XVIMAGESINK (object);
1561 xvimagesink->config.hue = g_value_get_int (value);
1562 xvimagesink->config.cb_changed = TRUE;
1563 gst_xvimagesink_update_colorbalance (xvimagesink);
1566 xvimagesink->config.contrast = g_value_get_int (value);
1567 xvimagesink->config.cb_changed = TRUE;
1568 gst_xvimagesink_update_colorbalance (xvimagesink);
1570 case PROP_BRIGHTNESS:
1571 xvimagesink->config.brightness = g_value_get_int (value);
1572 xvimagesink->config.cb_changed = TRUE;
1573 gst_xvimagesink_update_colorbalance (xvimagesink);
1575 case PROP_SATURATION:
1576 xvimagesink->config.saturation = g_value_get_int (value);
1577 xvimagesink->config.cb_changed = TRUE;
1578 gst_xvimagesink_update_colorbalance (xvimagesink);
1581 g_free (xvimagesink->config.display_name);
1582 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1584 case PROP_SYNCHRONOUS:
1585 xvimagesink->synchronous = g_value_get_boolean (value);
1586 if (xvimagesink->context) {
1587 gst_xvcontext_set_synchronous (xvimagesink->context,
1588 xvimagesink->synchronous);
1591 case PROP_PIXEL_ASPECT_RATIO:
1592 g_free (xvimagesink->par);
1593 xvimagesink->par = g_new0 (GValue, 1);
1594 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1595 if (!g_value_transform (value, xvimagesink->par)) {
1596 g_warning ("Could not transform string to aspect ratio");
1597 gst_value_set_fraction (xvimagesink->par, 1, 1);
1599 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1600 gst_value_get_fraction_numerator (xvimagesink->par),
1601 gst_value_get_fraction_denominator (xvimagesink->par));
1603 case PROP_FORCE_ASPECT_RATIO:
1604 xvimagesink->keep_aspect = g_value_get_boolean (value);
1606 case PROP_HANDLE_EVENTS:
1607 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1608 g_value_get_boolean (value));
1609 gst_xvimagesink_manage_event_thread (xvimagesink);
1612 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1614 case PROP_HANDLE_EXPOSE:
1615 xvimagesink->handle_expose = g_value_get_boolean (value);
1616 gst_xvimagesink_manage_event_thread (xvimagesink);
1618 case PROP_DOUBLE_BUFFER:
1619 xvimagesink->double_buffer = g_value_get_boolean (value);
1621 case PROP_AUTOPAINT_COLORKEY:
1622 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1625 xvimagesink->config.colorkey = g_value_get_int (value);
1627 case PROP_DRAW_BORDERS:
1628 xvimagesink->draw_borders = g_value_get_boolean (value);
1631 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1637 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1638 GValue * value, GParamSpec * pspec)
1640 GstXvImageSink *xvimagesink;
1642 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1644 xvimagesink = GST_XVIMAGESINK (object);
1648 g_value_set_int (value, xvimagesink->config.hue);
1651 g_value_set_int (value, xvimagesink->config.contrast);
1653 case PROP_BRIGHTNESS:
1654 g_value_set_int (value, xvimagesink->config.brightness);
1656 case PROP_SATURATION:
1657 g_value_set_int (value, xvimagesink->config.saturation);
1660 g_value_set_string (value, xvimagesink->config.display_name);
1662 case PROP_SYNCHRONOUS:
1663 g_value_set_boolean (value, xvimagesink->synchronous);
1665 case PROP_PIXEL_ASPECT_RATIO:
1666 if (xvimagesink->par)
1667 g_value_transform (xvimagesink->par, value);
1669 case PROP_FORCE_ASPECT_RATIO:
1670 g_value_set_boolean (value, xvimagesink->keep_aspect);
1672 case PROP_HANDLE_EVENTS:
1673 g_value_set_boolean (value, xvimagesink->handle_events);
1677 char *adaptor_nr_s =
1678 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1680 g_value_set_string (value, adaptor_nr_s);
1681 g_free (adaptor_nr_s);
1684 case PROP_DEVICE_NAME:
1685 if (xvimagesink->context && xvimagesink->context->adaptors) {
1686 g_value_set_string (value,
1687 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1689 g_value_set_string (value, NULL);
1692 case PROP_HANDLE_EXPOSE:
1693 g_value_set_boolean (value, xvimagesink->handle_expose);
1695 case PROP_DOUBLE_BUFFER:
1696 g_value_set_boolean (value, xvimagesink->double_buffer);
1698 case PROP_AUTOPAINT_COLORKEY:
1699 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1702 g_value_set_int (value, xvimagesink->config.colorkey);
1704 case PROP_DRAW_BORDERS:
1705 g_value_set_boolean (value, xvimagesink->draw_borders);
1707 case PROP_WINDOW_WIDTH:
1708 if (xvimagesink->xwindow)
1709 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1711 g_value_set_uint64 (value, 0);
1713 case PROP_WINDOW_HEIGHT:
1714 if (xvimagesink->xwindow)
1715 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1717 g_value_set_uint64 (value, 0);
1720 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1726 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1728 GError *error = NULL;
1730 /* Initializing the XvContext unless already done through GstVideoOverlay */
1731 if (!xvimagesink->context) {
1732 GstXvContext *context;
1733 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1736 GST_OBJECT_LOCK (xvimagesink);
1737 xvimagesink->context = context;
1739 GST_OBJECT_LOCK (xvimagesink);
1740 /* make an allocator for this context */
1741 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1742 GST_OBJECT_UNLOCK (xvimagesink);
1744 /* update object's par with calculated one if not set yet */
1745 if (!xvimagesink->par) {
1746 xvimagesink->par = g_new0 (GValue, 1);
1747 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1748 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1750 /* call XSynchronize with the current value of synchronous */
1751 gst_xvcontext_set_synchronous (xvimagesink->context,
1752 xvimagesink->synchronous);
1753 gst_xvimagesink_update_colorbalance (xvimagesink);
1754 gst_xvimagesink_manage_event_thread (xvimagesink);
1760 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1761 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1762 error->message, __FILE__, GST_FUNCTION, __LINE__);
1768 gst_xvimagesink_close (GstXvImageSink * xvimagesink)
1771 GstXvContext *context;
1773 GST_OBJECT_LOCK (xvimagesink);
1774 xvimagesink->running = FALSE;
1775 /* grab thread and mark it as NULL */
1776 thread = xvimagesink->event_thread;
1777 xvimagesink->event_thread = NULL;
1778 GST_OBJECT_UNLOCK (xvimagesink);
1780 /* Wait for our event thread to finish before we clean up our stuff. */
1782 g_thread_join (thread);
1784 if (xvimagesink->cur_image) {
1785 gst_buffer_unref (xvimagesink->cur_image);
1786 xvimagesink->cur_image = NULL;
1789 g_mutex_lock (&xvimagesink->flow_lock);
1791 if (xvimagesink->pool) {
1792 gst_object_unref (xvimagesink->pool);
1793 xvimagesink->pool = NULL;
1796 if (xvimagesink->xwindow) {
1797 gst_xwindow_clear (xvimagesink->xwindow);
1798 gst_xwindow_destroy (xvimagesink->xwindow);
1799 xvimagesink->xwindow = NULL;
1801 g_mutex_unlock (&xvimagesink->flow_lock);
1803 if (xvimagesink->allocator) {
1804 gst_object_unref (xvimagesink->allocator);
1805 xvimagesink->allocator = NULL;
1808 GST_OBJECT_LOCK (xvimagesink);
1809 /* grab context and mark it as NULL */
1810 context = xvimagesink->context;
1811 xvimagesink->context = NULL;
1812 GST_OBJECT_UNLOCK (xvimagesink);
1815 gst_xvcontext_unref (context);
1818 /* Finalize is called only once, dispose can be called multiple times.
1819 * We use mutexes and don't reset stuff to NULL here so let's register
1822 gst_xvimagesink_finalize (GObject * object)
1824 GstXvImageSink *xvimagesink;
1826 xvimagesink = GST_XVIMAGESINK (object);
1828 gst_xvimagesink_close (xvimagesink);
1830 gst_xvcontext_config_clear (&xvimagesink->config);
1832 if (xvimagesink->par) {
1833 g_free (xvimagesink->par);
1834 xvimagesink->par = NULL;
1836 g_mutex_clear (&xvimagesink->flow_lock);
1837 g_free (xvimagesink->media_title);
1839 G_OBJECT_CLASS (parent_class)->finalize (object);
1843 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1845 xvimagesink->config.display_name = NULL;
1846 xvimagesink->config.adaptor_nr = 0;
1847 xvimagesink->config.autopaint_colorkey = TRUE;
1848 xvimagesink->config.double_buffer = TRUE;
1849 /* on 16bit displays this becomes r,g,b = 1,2,3
1850 * on 24bit displays this becomes r,g,b = 8,8,16
1851 * as a port atom value */
1852 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1853 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1854 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1855 xvimagesink->config.cb_changed = FALSE;
1857 xvimagesink->context = NULL;
1858 xvimagesink->xwindow = NULL;
1859 xvimagesink->cur_image = NULL;
1861 xvimagesink->fps_n = 0;
1862 xvimagesink->fps_d = 0;
1863 xvimagesink->video_width = 0;
1864 xvimagesink->video_height = 0;
1866 g_mutex_init (&xvimagesink->flow_lock);
1868 xvimagesink->pool = NULL;
1870 xvimagesink->synchronous = FALSE;
1871 xvimagesink->running = FALSE;
1872 xvimagesink->keep_aspect = TRUE;
1873 xvimagesink->handle_events = TRUE;
1874 xvimagesink->par = NULL;
1875 xvimagesink->handle_expose = TRUE;
1877 xvimagesink->draw_borders = TRUE;
1881 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1883 GObjectClass *gobject_class;
1884 GstElementClass *gstelement_class;
1885 GstBaseSinkClass *gstbasesink_class;
1886 GstVideoSinkClass *videosink_class;
1888 gobject_class = (GObjectClass *) klass;
1889 gstelement_class = (GstElementClass *) klass;
1890 gstbasesink_class = (GstBaseSinkClass *) klass;
1891 videosink_class = (GstVideoSinkClass *) klass;
1893 parent_class = g_type_class_peek_parent (klass);
1895 gobject_class->set_property = gst_xvimagesink_set_property;
1896 gobject_class->get_property = gst_xvimagesink_get_property;
1898 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1899 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1900 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1901 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1902 g_param_spec_int ("brightness", "Brightness",
1903 "The brightness of the video", -1000, 1000, 0,
1904 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1905 g_object_class_install_property (gobject_class, PROP_HUE,
1906 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1907 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1908 g_object_class_install_property (gobject_class, PROP_SATURATION,
1909 g_param_spec_int ("saturation", "Saturation",
1910 "The saturation of the video", -1000, 1000, 0,
1911 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1912 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1913 g_param_spec_string ("display", "Display", "X Display name", NULL,
1914 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1915 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1916 g_param_spec_boolean ("synchronous", "Synchronous",
1917 "When enabled, runs the X display in synchronous mode. "
1918 "(unrelated to A/V sync, used only for debugging)", FALSE,
1919 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1920 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1921 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1922 "The pixel aspect ratio of the device", "1/1",
1923 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1924 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1925 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1926 "When enabled, scaling will respect original aspect ratio", TRUE,
1927 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1928 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1929 g_param_spec_boolean ("handle-events", "Handle XEvents",
1930 "When enabled, XEvents will be selected and handled", TRUE,
1931 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932 g_object_class_install_property (gobject_class, PROP_DEVICE,
1933 g_param_spec_string ("device", "Adaptor number",
1934 "The number of the video adaptor", "0",
1935 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1936 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1937 g_param_spec_string ("device-name", "Adaptor name",
1938 "The name of the video adaptor", NULL,
1939 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1941 * GstXvImageSink:handle-expose
1943 * When enabled, the current frame will always be drawn in response to X
1946 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1947 g_param_spec_boolean ("handle-expose", "Handle expose",
1949 "the current frame will always be drawn in response to X Expose "
1950 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1953 * GstXvImageSink:double-buffer
1955 * Whether to double-buffer the output.
1957 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1958 g_param_spec_boolean ("double-buffer", "Double-buffer",
1959 "Whether to double-buffer the output", TRUE,
1960 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1962 * GstXvImageSink:autopaint-colorkey
1964 * Whether to autofill overlay with colorkey
1966 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1967 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1968 "Whether to autofill overlay with colorkey", TRUE,
1969 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1971 * GstXvImageSink:colorkey
1973 * Color to use for the overlay mask.
1975 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1976 g_param_spec_int ("colorkey", "Colorkey",
1977 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1978 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1981 * GstXvImageSink:draw-borders
1983 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1984 * unused parts of the video area.
1986 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
1987 g_param_spec_boolean ("draw-borders", "Draw Borders",
1988 "Draw black borders to fill unused area in force-aspect-ratio mode",
1989 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1992 * GstXvImageSink:window-width
1994 * Actual width of the video window.
1996 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1997 g_param_spec_uint64 ("window-width", "window-width",
1998 "Width of the window", 0, G_MAXUINT64, 0,
1999 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2002 * GstXvImageSink:window-height
2004 * Actual height of the video window.
2006 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2007 g_param_spec_uint64 ("window-height", "window-height",
2008 "Height of the window", 0, G_MAXUINT64, 0,
2009 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2011 gobject_class->finalize = gst_xvimagesink_finalize;
2013 gst_element_class_set_static_metadata (gstelement_class,
2014 "Video sink", "Sink/Video",
2015 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2017 gst_element_class_add_pad_template (gstelement_class,
2018 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2020 gstelement_class->change_state =
2021 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2023 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2024 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2025 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2026 gstbasesink_class->propose_allocation =
2027 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2028 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2030 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);