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_xv_image_sink);
137 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
138 #define GST_CAT_DEFAULT gst_debug_xv_image_sink
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_xv_image_sink_open (GstXvImageSink * xvimagesink);
153 static void gst_xv_image_sink_close (GstXvImageSink * xvimagesink);
154 static void gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink *
156 static void gst_xv_image_sink_expose (GstVideoOverlay * overlay);
158 /* Default template - initiated with class struct to allow gst-register to work
160 static GstStaticPadTemplate gst_xv_image_sink_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_xv_image_sink_navigation_init (GstNavigationInterface * iface);
204 static void gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface *
206 static void gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface *
208 #define gst_xv_image_sink_parent_class parent_class
209 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xv_image_sink, GST_TYPE_VIDEO_SINK,
210 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
211 gst_xv_image_sink_navigation_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
213 gst_xv_image_sink_video_overlay_init);
214 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
215 gst_xv_image_sink_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_xv_image_sink_xvimage_put (GstXvImageSink * xvimagesink,
231 GstXvImageMemory *mem;
232 GstVideoCropMeta *crop;
233 GstVideoRectangle result;
234 gboolean draw_border = FALSE;
235 GstVideoRectangle src = { 0, };
236 GstVideoRectangle dst = { 0, };
237 GstVideoRectangle mem_crop;
240 /* We take the flow_lock. If expose is in there we don't want to run
241 concurrently from the data flow thread */
242 g_mutex_lock (&xvimagesink->flow_lock);
244 if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
245 g_mutex_unlock (&xvimagesink->flow_lock);
249 /* Draw borders when displaying the first frame. After this
250 draw borders only on expose event or after a size change. */
251 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
252 draw_border = xvimagesink->draw_borders;
253 xvimagesink->redraw_border = FALSE;
256 /* Store a reference to the last image we put, lose the previous one */
257 if (xvimage && xvimagesink->cur_image != xvimage) {
258 if (xvimagesink->cur_image) {
259 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
260 gst_buffer_unref (xvimagesink->cur_image);
262 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
263 xvimagesink->cur_image = gst_buffer_ref (xvimage);
266 /* Expose sends a NULL image, we take the latest frame */
268 if (xvimagesink->cur_image) {
270 xvimage = xvimagesink->cur_image;
272 g_mutex_unlock (&xvimagesink->flow_lock);
277 mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
278 gst_xvimage_memory_get_crop (mem, &mem_crop);
280 crop = gst_buffer_get_video_crop_meta (xvimage);
283 src.x = crop->x + mem_crop.x;
284 src.y = crop->y + mem_crop.y;
286 src.h = crop->height;
287 GST_LOG_OBJECT (xvimagesink,
288 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
293 if (xvimagesink->keep_aspect) {
296 /* We take the size of the source material as it was negotiated and
297 * corrected for DAR. This size can be different from the cropped size in
298 * which case the image will be scaled to fit the negotiated size. */
299 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
300 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
301 dst.w = xwindow->render_rect.w;
302 dst.h = xwindow->render_rect.h;
304 gst_video_sink_center_rect (s, dst, &result, TRUE);
305 result.x += xwindow->render_rect.x;
306 result.y += xwindow->render_rect.y;
308 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
311 gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
313 g_mutex_unlock (&xvimagesink->flow_lock);
319 gst_xv_image_sink_xwindow_set_title (GstXvImageSink * xvimagesink,
320 GstXWindow * xwindow, const gchar * media_title)
323 g_free (xvimagesink->media_title);
324 xvimagesink->media_title = g_strdup (media_title);
327 /* we have a window */
328 const gchar *app_name;
329 const gchar *title = NULL;
330 gchar *title_mem = NULL;
332 /* set application name as a title */
333 app_name = g_get_application_name ();
335 if (app_name && xvimagesink->media_title) {
336 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
338 } else if (app_name) {
340 } else if (xvimagesink->media_title) {
341 title = xvimagesink->media_title;
344 gst_xwindow_set_title (xwindow, title);
349 /* This function handles a GstXWindow creation
350 * The width and height are the actual pixel size on the display */
352 gst_xv_image_sink_xwindow_new (GstXvImageSink * xvimagesink,
353 gint width, gint height)
355 GstXWindow *xwindow = NULL;
356 GstXvContext *context;
358 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
360 context = xvimagesink->context;
362 xwindow = gst_xvcontext_create_xwindow (context, width, height);
364 /* set application name as a title */
365 gst_xv_image_sink_xwindow_set_title (xvimagesink, xwindow, NULL);
367 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
369 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
376 gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
378 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
380 /* Update the window geometry */
381 g_mutex_lock (&xvimagesink->flow_lock);
382 if (G_LIKELY (xvimagesink->xwindow))
383 gst_xwindow_update_geometry (xvimagesink->xwindow);
384 g_mutex_unlock (&xvimagesink->flow_lock);
387 /* This function commits our internal colorbalance settings to our grabbed Xv
388 port. If the context is not initialized yet it simply returns */
390 gst_xv_image_sink_update_colorbalance (GstXvImageSink * xvimagesink)
392 GstXvContext *context;
394 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
396 /* If we haven't initialized the X context we can't update anything */
397 if ((context = xvimagesink->context) == NULL)
400 gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
403 /* This function handles XEvents that might be in the queue. It generates
404 GstEvent that will be sent upstream in the pipeline to handle interactivity
405 and navigation. It will also listen for configure events on the window to
406 trigger caps renegotiation so on the fly software scaling can work. */
408 gst_xv_image_sink_handle_xevents (GstXvImageSink * xvimagesink)
411 guint pointer_x = 0, pointer_y = 0;
412 gboolean pointer_moved = FALSE;
413 gboolean exposed = FALSE, configured = FALSE;
415 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
417 /* Handle Interaction, produces navigation events */
419 /* We get all pointer motion events, only the last position is
421 g_mutex_lock (&xvimagesink->flow_lock);
422 g_mutex_lock (&xvimagesink->context->lock);
423 while (XCheckWindowEvent (xvimagesink->context->disp,
424 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
425 g_mutex_unlock (&xvimagesink->context->lock);
426 g_mutex_unlock (&xvimagesink->flow_lock);
430 pointer_x = e.xmotion.x;
431 pointer_y = e.xmotion.y;
432 pointer_moved = TRUE;
437 g_mutex_lock (&xvimagesink->flow_lock);
438 g_mutex_lock (&xvimagesink->context->lock);
442 g_mutex_unlock (&xvimagesink->context->lock);
443 g_mutex_unlock (&xvimagesink->flow_lock);
445 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
446 pointer_x, pointer_y);
447 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
448 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
450 g_mutex_lock (&xvimagesink->flow_lock);
451 g_mutex_lock (&xvimagesink->context->lock);
454 /* We get all events on our window to throw them upstream */
455 while (XCheckWindowEvent (xvimagesink->context->disp,
456 xvimagesink->xwindow->win,
457 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
460 const char *key_str = NULL;
462 /* We lock only for the X function call */
463 g_mutex_unlock (&xvimagesink->context->lock);
464 g_mutex_unlock (&xvimagesink->flow_lock);
468 /* Mouse button pressed over our window. We send upstream
469 events for interactivity/navigation */
470 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
471 e.xbutton.button, e.xbutton.x, e.xbutton.y);
472 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
473 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
476 /* Mouse button released over our window. We send upstream
477 events for interactivity/navigation */
478 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
479 e.xbutton.button, e.xbutton.x, e.xbutton.y);
480 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
481 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
485 /* Key pressed/released over our window. We send upstream
486 events for interactivity/navigation */
487 g_mutex_lock (&xvimagesink->context->lock);
488 keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
489 e.xkey.keycode, 0, 0);
490 if (keysym != NoSymbol) {
491 key_str = XKeysymToString (keysym);
495 g_mutex_unlock (&xvimagesink->context->lock);
496 GST_DEBUG_OBJECT (xvimagesink,
497 "key %d pressed over window at %d,%d (%s)",
498 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
499 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
500 e.type == KeyPress ? "key-press" : "key-release", key_str);
503 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
506 g_mutex_lock (&xvimagesink->flow_lock);
507 g_mutex_lock (&xvimagesink->context->lock);
511 while (XCheckWindowEvent (xvimagesink->context->disp,
512 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
517 case ConfigureNotify:
518 g_mutex_unlock (&xvimagesink->context->lock);
519 g_mutex_unlock (&xvimagesink->flow_lock);
521 gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
523 g_mutex_lock (&xvimagesink->flow_lock);
524 g_mutex_lock (&xvimagesink->context->lock);
532 if (xvimagesink->handle_expose && (exposed || configured)) {
533 g_mutex_unlock (&xvimagesink->context->lock);
534 g_mutex_unlock (&xvimagesink->flow_lock);
536 gst_xv_image_sink_expose (GST_VIDEO_OVERLAY (xvimagesink));
538 g_mutex_lock (&xvimagesink->flow_lock);
539 g_mutex_lock (&xvimagesink->context->lock);
542 /* Handle Display events */
543 while (XPending (xvimagesink->context->disp)) {
544 XNextEvent (xvimagesink->context->disp, &e);
550 wm_delete = XInternAtom (xvimagesink->context->disp,
551 "WM_DELETE_WINDOW", True);
552 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
553 /* Handle window deletion by posting an error on the bus */
554 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
555 ("Output window was closed"), (NULL));
557 g_mutex_unlock (&xvimagesink->context->lock);
558 gst_xwindow_destroy (xvimagesink->xwindow);
559 xvimagesink->xwindow = NULL;
560 g_mutex_lock (&xvimagesink->context->lock);
569 g_mutex_unlock (&xvimagesink->context->lock);
570 g_mutex_unlock (&xvimagesink->flow_lock);
574 gst_xv_image_sink_event_thread (GstXvImageSink * xvimagesink)
576 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
578 GST_OBJECT_LOCK (xvimagesink);
579 while (xvimagesink->running) {
580 GST_OBJECT_UNLOCK (xvimagesink);
582 if (xvimagesink->xwindow) {
583 gst_xv_image_sink_handle_xevents (xvimagesink);
585 /* FIXME: do we want to align this with the framerate or anything else? */
586 g_usleep (G_USEC_PER_SEC / 20);
588 GST_OBJECT_LOCK (xvimagesink);
590 GST_OBJECT_UNLOCK (xvimagesink);
596 gst_xv_image_sink_manage_event_thread (GstXvImageSink * xvimagesink)
598 GThread *thread = NULL;
600 /* don't start the thread too early */
601 if (xvimagesink->context == NULL) {
605 GST_OBJECT_LOCK (xvimagesink);
606 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
607 if (!xvimagesink->event_thread) {
608 /* Setup our event listening thread */
609 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
610 xvimagesink->handle_expose, xvimagesink->handle_events);
611 xvimagesink->running = TRUE;
612 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
613 (GThreadFunc) gst_xv_image_sink_event_thread, xvimagesink, NULL);
616 if (xvimagesink->event_thread) {
617 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
618 xvimagesink->handle_expose, xvimagesink->handle_events);
619 xvimagesink->running = FALSE;
620 /* grab thread and mark it as NULL */
621 thread = xvimagesink->event_thread;
622 xvimagesink->event_thread = NULL;
625 GST_OBJECT_UNLOCK (xvimagesink);
627 /* Wait for our event thread to finish */
629 g_thread_join (thread);
636 gst_xv_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
638 GstXvImageSink *xvimagesink;
641 xvimagesink = GST_XV_IMAGE_SINK (bsink);
643 if (xvimagesink->context) {
645 return gst_caps_intersect_full (filter, xvimagesink->context->caps,
646 GST_CAPS_INTERSECT_FIRST);
648 return gst_caps_ref (xvimagesink->context->caps);
651 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
653 GstCaps *intersection;
656 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
657 gst_caps_unref (caps);
663 static GstBufferPool *
664 gst_xv_image_sink_create_pool (GstXvImageSink * xvimagesink, GstCaps * caps,
665 gsize size, gint min)
668 GstStructure *config;
670 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
672 config = gst_buffer_pool_get_config (pool);
673 gst_buffer_pool_config_set_params (config, caps, size, min, 0);
675 if (!gst_buffer_pool_set_config (pool, config))
682 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
683 gst_object_unref (pool);
689 gst_xv_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
691 GstXvImageSink *xvimagesink;
692 GstXvContext *context;
693 GstBufferPool *newpool, *oldpool;
695 guint32 im_format = 0;
696 gint video_par_n, video_par_d; /* video's PAR */
697 gint display_par_n, display_par_d; /* display's PAR */
700 xvimagesink = GST_XV_IMAGE_SINK (bsink);
701 context = xvimagesink->context;
703 GST_DEBUG_OBJECT (xvimagesink,
704 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
705 GST_PTR_FORMAT, context->caps, caps);
707 if (!gst_caps_can_intersect (context->caps, caps))
708 goto incompatible_caps;
710 if (!gst_video_info_from_caps (&info, caps))
713 xvimagesink->fps_n = info.fps_n;
714 xvimagesink->fps_d = info.fps_d;
716 xvimagesink->video_width = info.width;
717 xvimagesink->video_height = info.height;
719 im_format = gst_xvcontext_get_format_from_info (context, &info);
723 gst_xvcontext_set_colorimetry (context, &info.colorimetry);
725 /* get aspect ratio from caps if it's present, and
726 * convert video width and height to a display width and height
727 * using wd / hd = wv / hv * PARv / PARd */
729 /* get video's PAR */
730 video_par_n = info.par_n;
731 video_par_d = info.par_d;
733 /* get display's PAR */
734 if (xvimagesink->par) {
735 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
736 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
742 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
743 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
746 GST_DEBUG_OBJECT (xvimagesink,
747 "video width/height: %dx%d, calculated display ratio: %d/%d",
748 info.width, info.height, num, den);
750 /* now find a width x height that respects this display ratio.
751 * prefer those that have one of w/h the same as the incoming video
752 * using wd / hd = num / den */
754 /* start with same height, because of interlaced video */
755 /* check hd / den is an integer scale factor, and scale wd with the PAR */
756 if (info.height % den == 0) {
757 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
758 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
759 gst_util_uint64_scale_int (info.height, num, den);
760 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
761 } else if (info.width % num == 0) {
762 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
763 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
764 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
765 gst_util_uint64_scale_int (info.width, den, num);
767 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
768 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
769 gst_util_uint64_scale_int (info.height, num, den);
770 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
772 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
773 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
775 /* Notify application to set xwindow id now */
776 g_mutex_lock (&xvimagesink->flow_lock);
777 if (!xvimagesink->xwindow) {
778 g_mutex_unlock (&xvimagesink->flow_lock);
779 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
781 g_mutex_unlock (&xvimagesink->flow_lock);
784 /* Creating our window and our image with the display size in pixels */
785 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
786 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
787 goto no_display_size;
789 g_mutex_lock (&xvimagesink->flow_lock);
790 if (!xvimagesink->xwindow) {
791 xvimagesink->xwindow = gst_xv_image_sink_xwindow_new (xvimagesink,
792 GST_VIDEO_SINK_WIDTH (xvimagesink),
793 GST_VIDEO_SINK_HEIGHT (xvimagesink));
796 xvimagesink->info = info;
798 /* After a resize, we want to redraw the borders in case the new frame size
799 * doesn't cover the same area */
800 xvimagesink->redraw_border = TRUE;
802 /* create a new pool for the new configuration */
803 newpool = gst_xv_image_sink_create_pool (xvimagesink, caps, info.size, 2);
805 /* we don't activate the internal pool yet as it may not be needed */
806 oldpool = xvimagesink->pool;
807 xvimagesink->pool = newpool;
808 g_mutex_unlock (&xvimagesink->flow_lock);
810 /* deactivate and unref the old internal pool */
812 gst_buffer_pool_set_active (oldpool, FALSE);
813 gst_object_unref (oldpool);
821 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
826 GST_DEBUG_OBJECT (xvimagesink,
827 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
832 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
833 ("Error calculating the output display ratio of the video."));
838 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
839 ("Error calculating the output display ratio of the video."));
844 static GstStateChangeReturn
845 gst_xv_image_sink_change_state (GstElement * element, GstStateChange transition)
847 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
848 GstXvImageSink *xvimagesink;
850 xvimagesink = GST_XV_IMAGE_SINK (element);
852 switch (transition) {
853 case GST_STATE_CHANGE_NULL_TO_READY:
854 if (!gst_xv_image_sink_open (xvimagesink))
857 case GST_STATE_CHANGE_READY_TO_PAUSED:
859 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
861 case GST_STATE_CHANGE_PAUSED_TO_READY:
867 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
869 switch (transition) {
870 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
872 case GST_STATE_CHANGE_PAUSED_TO_READY:
873 xvimagesink->fps_n = 0;
874 xvimagesink->fps_d = 1;
875 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
876 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
877 g_mutex_lock (&xvimagesink->flow_lock);
878 if (xvimagesink->pool)
879 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
880 g_mutex_unlock (&xvimagesink->flow_lock);
882 case GST_STATE_CHANGE_READY_TO_NULL:
883 gst_xv_image_sink_close (xvimagesink);
892 return GST_STATE_CHANGE_FAILURE;
897 gst_xv_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
898 GstClockTime * start, GstClockTime * end)
900 GstXvImageSink *xvimagesink;
902 xvimagesink = GST_XV_IMAGE_SINK (bsink);
904 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
905 *start = GST_BUFFER_TIMESTAMP (buf);
906 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
907 *end = *start + GST_BUFFER_DURATION (buf);
909 if (xvimagesink->fps_n > 0) {
911 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
919 gst_xv_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
922 GstXvImageSink *xvimagesink;
923 GstBuffer *to_put = NULL;
926 xvimagesink = GST_XV_IMAGE_SINK (vsink);
928 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
929 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
930 /* If this buffer has been allocated using our buffer management we simply
931 put the ximage which is in the PRIVATE pointer */
932 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
937 GstVideoFrame src, dest;
938 GstBufferPoolAcquireParams params = { 0, };
940 /* Else we have to copy the data into our private image, */
941 /* if we have one... */
942 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
944 /* we should have a pool, configured in setcaps */
945 if (xvimagesink->pool == NULL)
948 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
949 goto activate_failed;
951 /* take a buffer from our pool, if there is no buffer in the pool something
952 * is seriously wrong, waiting for the pool here might deadlock when we try
953 * to go to PAUSED because we never flush the pool then. */
954 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
955 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
956 if (res != GST_FLOW_OK)
959 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
960 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
962 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
965 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
966 gst_video_frame_unmap (&src);
970 gst_video_frame_copy (&dest, &src);
972 gst_video_frame_unmap (&dest);
973 gst_video_frame_unmap (&src);
976 if (!gst_xv_image_sink_xvimage_put (xvimagesink, to_put))
981 gst_buffer_unref (to_put);
988 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
989 ("Internal error: can't allocate images"),
990 ("We don't have a bufferpool negotiated"));
991 return GST_FLOW_ERROR;
995 /* No image available. That's very bad ! */
996 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1001 /* No Window available to put our image into */
1002 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1008 /* No Window available to put our image into */
1009 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1010 res = GST_FLOW_ERROR;
1015 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1016 res = GST_FLOW_ERROR;
1022 gst_xv_image_sink_event (GstBaseSink * sink, GstEvent * event)
1024 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (sink);
1026 switch (GST_EVENT_TYPE (event)) {
1027 case GST_EVENT_TAG:{
1029 gchar *title = NULL;
1031 gst_event_parse_tag (event, &l);
1032 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1035 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1036 gst_xv_image_sink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1046 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1050 gst_xv_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1052 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (bsink);
1053 GstBufferPool *pool = NULL;
1058 gst_query_parse_allocation (query, &caps, &need_pool);
1066 if (!gst_video_info_from_caps (&info, caps))
1069 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1070 pool = gst_xv_image_sink_create_pool (xvimagesink, caps, info.size, 0);
1072 /* the normal size of a frame */
1080 /* we need at least 2 buffer because we hold on to the last one */
1081 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1082 gst_object_unref (pool);
1085 /* we also support various metadata */
1086 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1087 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1094 GST_DEBUG_OBJECT (bsink, "no caps specified");
1099 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1104 /* Already warned in create_pool */
1109 /* Interfaces stuff */
1111 gst_xv_image_sink_navigation_send_event (GstNavigation * navigation,
1112 GstStructure * structure)
1114 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (navigation);
1116 gboolean handled = FALSE;
1117 GstEvent *event = NULL;
1119 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1120 GstVideoRectangle src = { 0, };
1121 GstVideoRectangle dst = { 0, };
1122 GstVideoRectangle result;
1123 gdouble x, y, xscale = 1.0, yscale = 1.0;
1124 GstXWindow *xwindow;
1126 /* We take the flow_lock while we look at the window */
1127 g_mutex_lock (&xvimagesink->flow_lock);
1129 if (!(xwindow = xvimagesink->xwindow)) {
1130 g_mutex_unlock (&xvimagesink->flow_lock);
1134 if (xvimagesink->keep_aspect) {
1135 /* We get the frame position using the calculated geometry from _setcaps
1136 that respect pixel aspect ratios */
1137 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1138 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1139 dst.w = xwindow->render_rect.w;
1140 dst.h = xwindow->render_rect.h;
1142 gst_video_sink_center_rect (src, dst, &result, TRUE);
1143 result.x += xwindow->render_rect.x;
1144 result.y += xwindow->render_rect.y;
1146 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1149 g_mutex_unlock (&xvimagesink->flow_lock);
1151 /* We calculate scaling using the original video frames geometry to include
1152 pixel aspect ratio scaling. */
1153 xscale = (gdouble) xvimagesink->video_width / result.w;
1154 yscale = (gdouble) xvimagesink->video_height / result.h;
1156 /* Converting pointer coordinates to the non scaled geometry */
1157 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1158 x = MIN (x, result.x + result.w);
1159 x = MAX (x - result.x, 0);
1160 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1161 (gdouble) x * xscale, NULL);
1163 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1164 y = MIN (y, result.y + result.h);
1165 y = MAX (y - result.y, 0);
1166 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1167 (gdouble) y * yscale, NULL);
1170 event = gst_event_new_navigation (structure);
1171 gst_event_ref (event);
1172 handled = gst_pad_send_event (peer, event);
1173 gst_object_unref (peer);
1176 if (!handled && event) {
1177 gst_element_post_message ((GstElement *) xvimagesink,
1178 gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1182 gst_event_unref (event);
1186 gst_xv_image_sink_navigation_init (GstNavigationInterface * iface)
1188 iface->send_event = gst_xv_image_sink_navigation_send_event;
1192 gst_xv_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1194 XID xwindow_id = id;
1195 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1196 GstXWindow *xwindow = NULL;
1197 GstXvContext *context;
1199 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1201 g_mutex_lock (&xvimagesink->flow_lock);
1203 /* If we already use that window return */
1204 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1205 g_mutex_unlock (&xvimagesink->flow_lock);
1209 /* If the element has not initialized the X11 context try to do so */
1210 if (!xvimagesink->context &&
1211 !(xvimagesink->context =
1212 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1213 g_mutex_unlock (&xvimagesink->flow_lock);
1214 /* we have thrown a GST_ELEMENT_ERROR now */
1218 context = xvimagesink->context;
1220 gst_xv_image_sink_update_colorbalance (xvimagesink);
1222 /* If a window is there already we destroy it */
1223 if (xvimagesink->xwindow) {
1224 gst_xwindow_destroy (xvimagesink->xwindow);
1225 xvimagesink->xwindow = NULL;
1228 /* If the xid is 0 we go back to an internal window */
1229 if (xwindow_id == 0) {
1230 /* If no width/height caps nego did not happen window will be created
1231 during caps nego then */
1232 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1233 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1235 gst_xv_image_sink_xwindow_new (xvimagesink,
1236 GST_VIDEO_SINK_WIDTH (xvimagesink),
1237 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1240 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1241 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1245 xvimagesink->xwindow = xwindow;
1247 g_mutex_unlock (&xvimagesink->flow_lock);
1251 gst_xv_image_sink_expose (GstVideoOverlay * overlay)
1253 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1255 GST_DEBUG ("doing expose");
1256 gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
1257 gst_xv_image_sink_xvimage_put (xvimagesink, NULL);
1261 gst_xv_image_sink_set_event_handling (GstVideoOverlay * overlay,
1262 gboolean handle_events)
1264 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1266 g_mutex_lock (&xvimagesink->flow_lock);
1267 xvimagesink->handle_events = handle_events;
1268 if (G_LIKELY (xvimagesink->xwindow))
1269 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1270 g_mutex_unlock (&xvimagesink->flow_lock);
1274 gst_xv_image_sink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
1275 gint y, gint width, gint height)
1277 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1279 g_mutex_lock (&xvimagesink->flow_lock);
1280 if (G_LIKELY (xvimagesink->xwindow))
1281 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1283 g_mutex_unlock (&xvimagesink->flow_lock);
1287 gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1289 iface->set_window_handle = gst_xv_image_sink_set_window_handle;
1290 iface->expose = gst_xv_image_sink_expose;
1291 iface->handle_events = gst_xv_image_sink_set_event_handling;
1292 iface->set_render_rectangle = gst_xv_image_sink_set_render_rectangle;
1295 static const GList *
1296 gst_xv_image_sink_colorbalance_list_channels (GstColorBalance * balance)
1298 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1300 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
1302 if (xvimagesink->context)
1303 return xvimagesink->context->channels_list;
1309 gst_xv_image_sink_colorbalance_set_value (GstColorBalance * balance,
1310 GstColorBalanceChannel * channel, gint value)
1312 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1314 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1315 g_return_if_fail (channel->label != NULL);
1317 xvimagesink->config.cb_changed = TRUE;
1319 /* Normalize val to [-1000, 1000] */
1320 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1321 (double) (channel->max_value - channel->min_value));
1323 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1324 xvimagesink->config.hue = value;
1325 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1326 xvimagesink->config.saturation = value;
1327 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1328 xvimagesink->config.contrast = value;
1329 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1330 xvimagesink->config.brightness = value;
1332 g_warning ("got an unknown channel %s", channel->label);
1336 gst_xv_image_sink_update_colorbalance (xvimagesink);
1340 gst_xv_image_sink_colorbalance_get_value (GstColorBalance * balance,
1341 GstColorBalanceChannel * channel)
1343 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1346 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), 0);
1347 g_return_val_if_fail (channel->label != NULL, 0);
1349 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1350 value = xvimagesink->config.hue;
1351 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1352 value = xvimagesink->config.saturation;
1353 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1354 value = xvimagesink->config.contrast;
1355 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1356 value = xvimagesink->config.brightness;
1358 g_warning ("got an unknown channel %s", channel->label);
1361 /* Normalize val to [channel->min_value, channel->max_value] */
1362 value = channel->min_value + (channel->max_value - channel->min_value) *
1363 (value + 1000) / 2000;
1368 static GstColorBalanceType
1369 gst_xv_image_sink_colorbalance_get_balance_type (GstColorBalance * balance)
1371 return GST_COLOR_BALANCE_HARDWARE;
1375 gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface * iface)
1377 iface->list_channels = gst_xv_image_sink_colorbalance_list_channels;
1378 iface->set_value = gst_xv_image_sink_colorbalance_set_value;
1379 iface->get_value = gst_xv_image_sink_colorbalance_get_value;
1380 iface->get_balance_type = gst_xv_image_sink_colorbalance_get_balance_type;
1384 static const GList *
1385 gst_xv_image_sink_probe_get_properties (GstPropertyProbe * probe)
1387 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1388 static GList *list = NULL;
1391 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1393 g_list_append (list, g_object_class_find_property (klass,
1394 "autopaint-colorkey"));
1396 g_list_append (list, g_object_class_find_property (klass,
1399 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1406 gst_xv_image_sink_probe_probe_property (GstPropertyProbe * probe,
1407 guint prop_id, const GParamSpec * pspec)
1409 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1413 case PROP_AUTOPAINT_COLORKEY:
1414 case PROP_DOUBLE_BUFFER:
1416 GST_DEBUG_OBJECT (xvimagesink,
1417 "probing device list and get capabilities");
1418 if (!xvimagesink->context) {
1419 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1420 xvimagesink->context = gst_xv_image_sink_context_get (xvimagesink);
1424 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1430 gst_xv_image_sink_probe_needs_probe (GstPropertyProbe * probe,
1431 guint prop_id, const GParamSpec * pspec)
1433 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1434 gboolean ret = FALSE;
1438 case PROP_AUTOPAINT_COLORKEY:
1439 case PROP_DOUBLE_BUFFER:
1441 if (xvimagesink->context != NULL) {
1448 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1455 static GValueArray *
1456 gst_xv_image_sink_probe_get_values (GstPropertyProbe * probe,
1457 guint prop_id, const GParamSpec * pspec)
1459 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1460 GValueArray *array = NULL;
1462 if (G_UNLIKELY (!xvimagesink->context)) {
1463 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1472 GValue value = { 0 };
1474 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1475 g_value_init (&value, G_TYPE_STRING);
1477 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1478 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1480 g_value_set_string (&value, adaptor_id_s);
1481 g_value_array_append (array, &value);
1482 g_free (adaptor_id_s);
1484 g_value_unset (&value);
1487 case PROP_AUTOPAINT_COLORKEY:
1488 if (xvimagesink->have_autopaint_colorkey) {
1489 GValue value = { 0 };
1491 array = g_value_array_new (2);
1492 g_value_init (&value, G_TYPE_BOOLEAN);
1493 g_value_set_boolean (&value, FALSE);
1494 g_value_array_append (array, &value);
1495 g_value_set_boolean (&value, TRUE);
1496 g_value_array_append (array, &value);
1497 g_value_unset (&value);
1500 case PROP_DOUBLE_BUFFER:
1501 if (xvimagesink->have_double_buffer) {
1502 GValue value = { 0 };
1504 array = g_value_array_new (2);
1505 g_value_init (&value, G_TYPE_BOOLEAN);
1506 g_value_set_boolean (&value, FALSE);
1507 g_value_array_append (array, &value);
1508 g_value_set_boolean (&value, TRUE);
1509 g_value_array_append (array, &value);
1510 g_value_unset (&value);
1514 if (xvimagesink->have_colorkey) {
1515 GValue value = { 0 };
1517 array = g_value_array_new (1);
1518 g_value_init (&value, GST_TYPE_INT_RANGE);
1519 gst_value_set_int_range (&value, 0, 0xffffff);
1520 g_value_array_append (array, &value);
1521 g_value_unset (&value);
1525 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1534 gst_xv_image_sink_property_probe_interface_init (GstPropertyProbeInterface *
1537 iface->get_properties = gst_xv_image_sink_probe_get_properties;
1538 iface->probe_property = gst_xv_image_sink_probe_probe_property;
1539 iface->needs_probe = gst_xv_image_sink_probe_needs_probe;
1540 iface->get_values = gst_xv_image_sink_probe_get_values;
1544 /* =========================================== */
1546 /* Init & Class init */
1548 /* =========================================== */
1551 gst_xv_image_sink_set_property (GObject * object, guint prop_id,
1552 const GValue * value, GParamSpec * pspec)
1554 GstXvImageSink *xvimagesink;
1556 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1558 xvimagesink = GST_XV_IMAGE_SINK (object);
1562 xvimagesink->config.hue = g_value_get_int (value);
1563 xvimagesink->config.cb_changed = TRUE;
1564 gst_xv_image_sink_update_colorbalance (xvimagesink);
1567 xvimagesink->config.contrast = g_value_get_int (value);
1568 xvimagesink->config.cb_changed = TRUE;
1569 gst_xv_image_sink_update_colorbalance (xvimagesink);
1571 case PROP_BRIGHTNESS:
1572 xvimagesink->config.brightness = g_value_get_int (value);
1573 xvimagesink->config.cb_changed = TRUE;
1574 gst_xv_image_sink_update_colorbalance (xvimagesink);
1576 case PROP_SATURATION:
1577 xvimagesink->config.saturation = g_value_get_int (value);
1578 xvimagesink->config.cb_changed = TRUE;
1579 gst_xv_image_sink_update_colorbalance (xvimagesink);
1582 g_free (xvimagesink->config.display_name);
1583 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1585 case PROP_SYNCHRONOUS:
1586 xvimagesink->synchronous = g_value_get_boolean (value);
1587 if (xvimagesink->context) {
1588 gst_xvcontext_set_synchronous (xvimagesink->context,
1589 xvimagesink->synchronous);
1592 case PROP_PIXEL_ASPECT_RATIO:
1593 g_free (xvimagesink->par);
1594 xvimagesink->par = g_new0 (GValue, 1);
1595 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1596 if (!g_value_transform (value, xvimagesink->par)) {
1597 g_warning ("Could not transform string to aspect ratio");
1598 gst_value_set_fraction (xvimagesink->par, 1, 1);
1600 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1601 gst_value_get_fraction_numerator (xvimagesink->par),
1602 gst_value_get_fraction_denominator (xvimagesink->par));
1604 case PROP_FORCE_ASPECT_RATIO:
1605 xvimagesink->keep_aspect = g_value_get_boolean (value);
1607 case PROP_HANDLE_EVENTS:
1608 gst_xv_image_sink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1609 g_value_get_boolean (value));
1610 gst_xv_image_sink_manage_event_thread (xvimagesink);
1613 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1615 case PROP_HANDLE_EXPOSE:
1616 xvimagesink->handle_expose = g_value_get_boolean (value);
1617 gst_xv_image_sink_manage_event_thread (xvimagesink);
1619 case PROP_DOUBLE_BUFFER:
1620 xvimagesink->double_buffer = g_value_get_boolean (value);
1622 case PROP_AUTOPAINT_COLORKEY:
1623 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1626 xvimagesink->config.colorkey = g_value_get_int (value);
1628 case PROP_DRAW_BORDERS:
1629 xvimagesink->draw_borders = g_value_get_boolean (value);
1632 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1638 gst_xv_image_sink_get_property (GObject * object, guint prop_id,
1639 GValue * value, GParamSpec * pspec)
1641 GstXvImageSink *xvimagesink;
1643 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1645 xvimagesink = GST_XV_IMAGE_SINK (object);
1649 g_value_set_int (value, xvimagesink->config.hue);
1652 g_value_set_int (value, xvimagesink->config.contrast);
1654 case PROP_BRIGHTNESS:
1655 g_value_set_int (value, xvimagesink->config.brightness);
1657 case PROP_SATURATION:
1658 g_value_set_int (value, xvimagesink->config.saturation);
1661 g_value_set_string (value, xvimagesink->config.display_name);
1663 case PROP_SYNCHRONOUS:
1664 g_value_set_boolean (value, xvimagesink->synchronous);
1666 case PROP_PIXEL_ASPECT_RATIO:
1667 if (xvimagesink->par)
1668 g_value_transform (xvimagesink->par, value);
1670 case PROP_FORCE_ASPECT_RATIO:
1671 g_value_set_boolean (value, xvimagesink->keep_aspect);
1673 case PROP_HANDLE_EVENTS:
1674 g_value_set_boolean (value, xvimagesink->handle_events);
1678 char *adaptor_nr_s =
1679 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1681 g_value_set_string (value, adaptor_nr_s);
1682 g_free (adaptor_nr_s);
1685 case PROP_DEVICE_NAME:
1686 if (xvimagesink->context && xvimagesink->context->adaptors) {
1687 g_value_set_string (value,
1688 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1690 g_value_set_string (value, NULL);
1693 case PROP_HANDLE_EXPOSE:
1694 g_value_set_boolean (value, xvimagesink->handle_expose);
1696 case PROP_DOUBLE_BUFFER:
1697 g_value_set_boolean (value, xvimagesink->double_buffer);
1699 case PROP_AUTOPAINT_COLORKEY:
1700 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1703 g_value_set_int (value, xvimagesink->config.colorkey);
1705 case PROP_DRAW_BORDERS:
1706 g_value_set_boolean (value, xvimagesink->draw_borders);
1708 case PROP_WINDOW_WIDTH:
1709 if (xvimagesink->xwindow)
1710 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1712 g_value_set_uint64 (value, 0);
1714 case PROP_WINDOW_HEIGHT:
1715 if (xvimagesink->xwindow)
1716 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1718 g_value_set_uint64 (value, 0);
1721 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1727 gst_xv_image_sink_open (GstXvImageSink * xvimagesink)
1729 GError *error = NULL;
1731 /* Initializing the XvContext unless already done through GstVideoOverlay */
1732 if (!xvimagesink->context) {
1733 GstXvContext *context;
1734 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1737 GST_OBJECT_LOCK (xvimagesink);
1738 xvimagesink->context = context;
1740 GST_OBJECT_LOCK (xvimagesink);
1741 /* make an allocator for this context */
1742 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1743 GST_OBJECT_UNLOCK (xvimagesink);
1745 /* update object's par with calculated one if not set yet */
1746 if (!xvimagesink->par) {
1747 xvimagesink->par = g_new0 (GValue, 1);
1748 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1749 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1751 /* call XSynchronize with the current value of synchronous */
1752 gst_xvcontext_set_synchronous (xvimagesink->context,
1753 xvimagesink->synchronous);
1754 gst_xv_image_sink_update_colorbalance (xvimagesink);
1755 gst_xv_image_sink_manage_event_thread (xvimagesink);
1761 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1762 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1763 error->message, __FILE__, GST_FUNCTION, __LINE__);
1769 gst_xv_image_sink_close (GstXvImageSink * xvimagesink)
1772 GstXvContext *context;
1774 GST_OBJECT_LOCK (xvimagesink);
1775 xvimagesink->running = FALSE;
1776 /* grab thread and mark it as NULL */
1777 thread = xvimagesink->event_thread;
1778 xvimagesink->event_thread = NULL;
1779 GST_OBJECT_UNLOCK (xvimagesink);
1781 /* Wait for our event thread to finish before we clean up our stuff. */
1783 g_thread_join (thread);
1785 if (xvimagesink->cur_image) {
1786 gst_buffer_unref (xvimagesink->cur_image);
1787 xvimagesink->cur_image = NULL;
1790 g_mutex_lock (&xvimagesink->flow_lock);
1792 if (xvimagesink->pool) {
1793 gst_object_unref (xvimagesink->pool);
1794 xvimagesink->pool = NULL;
1797 if (xvimagesink->xwindow) {
1798 gst_xwindow_clear (xvimagesink->xwindow);
1799 gst_xwindow_destroy (xvimagesink->xwindow);
1800 xvimagesink->xwindow = NULL;
1802 g_mutex_unlock (&xvimagesink->flow_lock);
1804 if (xvimagesink->allocator) {
1805 gst_object_unref (xvimagesink->allocator);
1806 xvimagesink->allocator = NULL;
1809 GST_OBJECT_LOCK (xvimagesink);
1810 /* grab context and mark it as NULL */
1811 context = xvimagesink->context;
1812 xvimagesink->context = NULL;
1813 GST_OBJECT_UNLOCK (xvimagesink);
1816 gst_xvcontext_unref (context);
1819 /* Finalize is called only once, dispose can be called multiple times.
1820 * We use mutexes and don't reset stuff to NULL here so let's register
1823 gst_xv_image_sink_finalize (GObject * object)
1825 GstXvImageSink *xvimagesink;
1827 xvimagesink = GST_XV_IMAGE_SINK (object);
1829 gst_xv_image_sink_close (xvimagesink);
1831 gst_xvcontext_config_clear (&xvimagesink->config);
1833 if (xvimagesink->par) {
1834 g_free (xvimagesink->par);
1835 xvimagesink->par = NULL;
1837 g_mutex_clear (&xvimagesink->flow_lock);
1838 g_free (xvimagesink->media_title);
1840 G_OBJECT_CLASS (parent_class)->finalize (object);
1844 gst_xv_image_sink_init (GstXvImageSink * xvimagesink)
1846 xvimagesink->config.display_name = NULL;
1847 xvimagesink->config.adaptor_nr = 0;
1848 xvimagesink->config.autopaint_colorkey = TRUE;
1849 xvimagesink->config.double_buffer = TRUE;
1850 /* on 16bit displays this becomes r,g,b = 1,2,3
1851 * on 24bit displays this becomes r,g,b = 8,8,16
1852 * as a port atom value */
1853 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1854 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1855 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1856 xvimagesink->config.cb_changed = FALSE;
1858 xvimagesink->context = NULL;
1859 xvimagesink->xwindow = NULL;
1860 xvimagesink->cur_image = NULL;
1862 xvimagesink->fps_n = 0;
1863 xvimagesink->fps_d = 0;
1864 xvimagesink->video_width = 0;
1865 xvimagesink->video_height = 0;
1867 g_mutex_init (&xvimagesink->flow_lock);
1869 xvimagesink->pool = NULL;
1871 xvimagesink->synchronous = FALSE;
1872 xvimagesink->running = FALSE;
1873 xvimagesink->keep_aspect = TRUE;
1874 xvimagesink->handle_events = TRUE;
1875 xvimagesink->par = NULL;
1876 xvimagesink->handle_expose = TRUE;
1878 xvimagesink->draw_borders = TRUE;
1882 gst_xv_image_sink_class_init (GstXvImageSinkClass * klass)
1884 GObjectClass *gobject_class;
1885 GstElementClass *gstelement_class;
1886 GstBaseSinkClass *gstbasesink_class;
1887 GstVideoSinkClass *videosink_class;
1889 gobject_class = (GObjectClass *) klass;
1890 gstelement_class = (GstElementClass *) klass;
1891 gstbasesink_class = (GstBaseSinkClass *) klass;
1892 videosink_class = (GstVideoSinkClass *) klass;
1894 parent_class = g_type_class_peek_parent (klass);
1896 gobject_class->set_property = gst_xv_image_sink_set_property;
1897 gobject_class->get_property = gst_xv_image_sink_get_property;
1899 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1900 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1901 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1902 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1903 g_param_spec_int ("brightness", "Brightness",
1904 "The brightness of the video", -1000, 1000, 0,
1905 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1906 g_object_class_install_property (gobject_class, PROP_HUE,
1907 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1908 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1909 g_object_class_install_property (gobject_class, PROP_SATURATION,
1910 g_param_spec_int ("saturation", "Saturation",
1911 "The saturation of the video", -1000, 1000, 0,
1912 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1913 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1914 g_param_spec_string ("display", "Display", "X Display name", NULL,
1915 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1916 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1917 g_param_spec_boolean ("synchronous", "Synchronous",
1918 "When enabled, runs the X display in synchronous mode. "
1919 "(unrelated to A/V sync, used only for debugging)", FALSE,
1920 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1921 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1922 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1923 "The pixel aspect ratio of the device", "1/1",
1924 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1925 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1926 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1927 "When enabled, scaling will respect original aspect ratio", TRUE,
1928 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1929 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1930 g_param_spec_boolean ("handle-events", "Handle XEvents",
1931 "When enabled, XEvents will be selected and handled", TRUE,
1932 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1933 g_object_class_install_property (gobject_class, PROP_DEVICE,
1934 g_param_spec_string ("device", "Adaptor number",
1935 "The number of the video adaptor", "0",
1936 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1937 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1938 g_param_spec_string ("device-name", "Adaptor name",
1939 "The name of the video adaptor", NULL,
1940 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1942 * GstXvImageSink:handle-expose
1944 * When enabled, the current frame will always be drawn in response to X
1947 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1948 g_param_spec_boolean ("handle-expose", "Handle expose",
1950 "the current frame will always be drawn in response to X Expose "
1951 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1954 * GstXvImageSink:double-buffer
1956 * Whether to double-buffer the output.
1958 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1959 g_param_spec_boolean ("double-buffer", "Double-buffer",
1960 "Whether to double-buffer the output", TRUE,
1961 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1963 * GstXvImageSink:autopaint-colorkey
1965 * Whether to autofill overlay with colorkey
1967 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1968 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1969 "Whether to autofill overlay with colorkey", TRUE,
1970 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1972 * GstXvImageSink:colorkey
1974 * Color to use for the overlay mask.
1976 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1977 g_param_spec_int ("colorkey", "Colorkey",
1978 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1979 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1982 * GstXvImageSink:draw-borders
1984 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1985 * unused parts of the video area.
1987 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
1988 g_param_spec_boolean ("draw-borders", "Draw Borders",
1989 "Draw black borders to fill unused area in force-aspect-ratio mode",
1990 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1993 * GstXvImageSink:window-width
1995 * Actual width of the video window.
1997 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1998 g_param_spec_uint64 ("window-width", "window-width",
1999 "Width of the window", 0, G_MAXUINT64, 0,
2000 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2003 * GstXvImageSink:window-height
2005 * Actual height of the video window.
2007 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2008 g_param_spec_uint64 ("window-height", "window-height",
2009 "Height of the window", 0, G_MAXUINT64, 0,
2010 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2012 gobject_class->finalize = gst_xv_image_sink_finalize;
2014 gst_element_class_set_static_metadata (gstelement_class,
2015 "Video sink", "Sink/Video",
2016 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2018 gst_element_class_add_pad_template (gstelement_class,
2019 gst_static_pad_template_get (&gst_xv_image_sink_sink_template_factory));
2021 gstelement_class->change_state =
2022 GST_DEBUG_FUNCPTR (gst_xv_image_sink_change_state);
2024 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_getcaps);
2025 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_setcaps);
2026 gstbasesink_class->get_times =
2027 GST_DEBUG_FUNCPTR (gst_xv_image_sink_get_times);
2028 gstbasesink_class->propose_allocation =
2029 GST_DEBUG_FUNCPTR (gst_xv_image_sink_propose_allocation);
2030 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xv_image_sink_event);
2032 videosink_class->show_frame =
2033 GST_DEBUG_FUNCPTR (gst_xv_image_sink_show_frame);