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);
1115 gboolean handled = FALSE;
1116 GstEvent *event = NULL;
1118 GstVideoRectangle src = { 0, };
1119 GstVideoRectangle dst = { 0, };
1120 GstVideoRectangle result;
1121 gdouble x, y, xscale = 1.0, yscale = 1.0;
1122 GstXWindow *xwindow;
1124 /* We take the flow_lock while we look at the window */
1125 g_mutex_lock (&xvimagesink->flow_lock);
1127 if (!(xwindow = xvimagesink->xwindow)) {
1128 g_mutex_unlock (&xvimagesink->flow_lock);
1132 if (xvimagesink->keep_aspect) {
1133 /* We get the frame position using the calculated geometry from _setcaps
1134 that respect pixel aspect ratios */
1135 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1136 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1137 dst.w = xwindow->render_rect.w;
1138 dst.h = xwindow->render_rect.h;
1140 gst_video_sink_center_rect (src, dst, &result, TRUE);
1141 result.x += xwindow->render_rect.x;
1142 result.y += xwindow->render_rect.y;
1144 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1147 g_mutex_unlock (&xvimagesink->flow_lock);
1149 /* We calculate scaling using the original video frames geometry to include
1150 pixel aspect ratio scaling. */
1151 xscale = (gdouble) xvimagesink->video_width / result.w;
1152 yscale = (gdouble) xvimagesink->video_height / result.h;
1154 /* Converting pointer coordinates to the non scaled geometry */
1155 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1156 x = MIN (x, result.x + result.w);
1157 x = MAX (x - result.x, 0);
1158 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1159 (gdouble) x * xscale, NULL);
1161 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1162 y = MIN (y, result.y + result.h);
1163 y = MAX (y - result.y, 0);
1164 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1165 (gdouble) y * yscale, NULL);
1168 event = gst_event_new_navigation (structure);
1170 gst_event_ref (event);
1171 handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (xvimagesink), event);
1174 gst_element_post_message ((GstElement *) xvimagesink,
1175 gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1177 gst_event_unref (event);
1182 gst_xv_image_sink_navigation_init (GstNavigationInterface * iface)
1184 iface->send_event = gst_xv_image_sink_navigation_send_event;
1188 gst_xv_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1190 XID xwindow_id = id;
1191 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1192 GstXWindow *xwindow = NULL;
1193 GstXvContext *context;
1195 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1197 g_mutex_lock (&xvimagesink->flow_lock);
1199 /* If we already use that window return */
1200 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1201 g_mutex_unlock (&xvimagesink->flow_lock);
1205 /* If the element has not initialized the X11 context try to do so */
1206 if (!xvimagesink->context &&
1207 !(xvimagesink->context =
1208 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1209 g_mutex_unlock (&xvimagesink->flow_lock);
1210 /* we have thrown a GST_ELEMENT_ERROR now */
1214 context = xvimagesink->context;
1216 gst_xv_image_sink_update_colorbalance (xvimagesink);
1218 /* If a window is there already we destroy it */
1219 if (xvimagesink->xwindow) {
1220 gst_xwindow_destroy (xvimagesink->xwindow);
1221 xvimagesink->xwindow = NULL;
1224 /* If the xid is 0 we go back to an internal window */
1225 if (xwindow_id == 0) {
1226 /* If no width/height caps nego did not happen window will be created
1227 during caps nego then */
1228 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1229 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1231 gst_xv_image_sink_xwindow_new (xvimagesink,
1232 GST_VIDEO_SINK_WIDTH (xvimagesink),
1233 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1236 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1237 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1241 xvimagesink->xwindow = xwindow;
1243 g_mutex_unlock (&xvimagesink->flow_lock);
1247 gst_xv_image_sink_expose (GstVideoOverlay * overlay)
1249 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1251 GST_DEBUG ("doing expose");
1252 gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
1253 gst_xv_image_sink_xvimage_put (xvimagesink, NULL);
1257 gst_xv_image_sink_set_event_handling (GstVideoOverlay * overlay,
1258 gboolean handle_events)
1260 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1262 g_mutex_lock (&xvimagesink->flow_lock);
1263 xvimagesink->handle_events = handle_events;
1264 if (G_LIKELY (xvimagesink->xwindow))
1265 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1266 g_mutex_unlock (&xvimagesink->flow_lock);
1270 gst_xv_image_sink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
1271 gint y, gint width, gint height)
1273 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1275 g_mutex_lock (&xvimagesink->flow_lock);
1276 if (G_LIKELY (xvimagesink->xwindow))
1277 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1279 g_mutex_unlock (&xvimagesink->flow_lock);
1283 gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1285 iface->set_window_handle = gst_xv_image_sink_set_window_handle;
1286 iface->expose = gst_xv_image_sink_expose;
1287 iface->handle_events = gst_xv_image_sink_set_event_handling;
1288 iface->set_render_rectangle = gst_xv_image_sink_set_render_rectangle;
1291 static const GList *
1292 gst_xv_image_sink_colorbalance_list_channels (GstColorBalance * balance)
1294 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1296 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
1298 if (xvimagesink->context)
1299 return xvimagesink->context->channels_list;
1305 gst_xv_image_sink_colorbalance_set_value (GstColorBalance * balance,
1306 GstColorBalanceChannel * channel, gint value)
1308 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1310 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1311 g_return_if_fail (channel->label != NULL);
1313 xvimagesink->config.cb_changed = TRUE;
1315 /* Normalize val to [-1000, 1000] */
1316 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1317 (double) (channel->max_value - channel->min_value));
1319 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1320 xvimagesink->config.hue = value;
1321 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1322 xvimagesink->config.saturation = value;
1323 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1324 xvimagesink->config.contrast = value;
1325 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1326 xvimagesink->config.brightness = value;
1328 g_warning ("got an unknown channel %s", channel->label);
1332 gst_xv_image_sink_update_colorbalance (xvimagesink);
1336 gst_xv_image_sink_colorbalance_get_value (GstColorBalance * balance,
1337 GstColorBalanceChannel * channel)
1339 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1342 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), 0);
1343 g_return_val_if_fail (channel->label != NULL, 0);
1345 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1346 value = xvimagesink->config.hue;
1347 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1348 value = xvimagesink->config.saturation;
1349 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1350 value = xvimagesink->config.contrast;
1351 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1352 value = xvimagesink->config.brightness;
1354 g_warning ("got an unknown channel %s", channel->label);
1357 /* Normalize val to [channel->min_value, channel->max_value] */
1358 value = channel->min_value + (channel->max_value - channel->min_value) *
1359 (value + 1000) / 2000;
1364 static GstColorBalanceType
1365 gst_xv_image_sink_colorbalance_get_balance_type (GstColorBalance * balance)
1367 return GST_COLOR_BALANCE_HARDWARE;
1371 gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface * iface)
1373 iface->list_channels = gst_xv_image_sink_colorbalance_list_channels;
1374 iface->set_value = gst_xv_image_sink_colorbalance_set_value;
1375 iface->get_value = gst_xv_image_sink_colorbalance_get_value;
1376 iface->get_balance_type = gst_xv_image_sink_colorbalance_get_balance_type;
1380 static const GList *
1381 gst_xv_image_sink_probe_get_properties (GstPropertyProbe * probe)
1383 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1384 static GList *list = NULL;
1387 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1389 g_list_append (list, g_object_class_find_property (klass,
1390 "autopaint-colorkey"));
1392 g_list_append (list, g_object_class_find_property (klass,
1395 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1402 gst_xv_image_sink_probe_probe_property (GstPropertyProbe * probe,
1403 guint prop_id, const GParamSpec * pspec)
1405 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1409 case PROP_AUTOPAINT_COLORKEY:
1410 case PROP_DOUBLE_BUFFER:
1412 GST_DEBUG_OBJECT (xvimagesink,
1413 "probing device list and get capabilities");
1414 if (!xvimagesink->context) {
1415 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1416 xvimagesink->context = gst_xv_image_sink_context_get (xvimagesink);
1420 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1426 gst_xv_image_sink_probe_needs_probe (GstPropertyProbe * probe,
1427 guint prop_id, const GParamSpec * pspec)
1429 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1430 gboolean ret = FALSE;
1434 case PROP_AUTOPAINT_COLORKEY:
1435 case PROP_DOUBLE_BUFFER:
1437 if (xvimagesink->context != NULL) {
1444 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1451 static GValueArray *
1452 gst_xv_image_sink_probe_get_values (GstPropertyProbe * probe,
1453 guint prop_id, const GParamSpec * pspec)
1455 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1456 GValueArray *array = NULL;
1458 if (G_UNLIKELY (!xvimagesink->context)) {
1459 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1468 GValue value = { 0 };
1470 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1471 g_value_init (&value, G_TYPE_STRING);
1473 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1474 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1476 g_value_set_string (&value, adaptor_id_s);
1477 g_value_array_append (array, &value);
1478 g_free (adaptor_id_s);
1480 g_value_unset (&value);
1483 case PROP_AUTOPAINT_COLORKEY:
1484 if (xvimagesink->have_autopaint_colorkey) {
1485 GValue value = { 0 };
1487 array = g_value_array_new (2);
1488 g_value_init (&value, G_TYPE_BOOLEAN);
1489 g_value_set_boolean (&value, FALSE);
1490 g_value_array_append (array, &value);
1491 g_value_set_boolean (&value, TRUE);
1492 g_value_array_append (array, &value);
1493 g_value_unset (&value);
1496 case PROP_DOUBLE_BUFFER:
1497 if (xvimagesink->have_double_buffer) {
1498 GValue value = { 0 };
1500 array = g_value_array_new (2);
1501 g_value_init (&value, G_TYPE_BOOLEAN);
1502 g_value_set_boolean (&value, FALSE);
1503 g_value_array_append (array, &value);
1504 g_value_set_boolean (&value, TRUE);
1505 g_value_array_append (array, &value);
1506 g_value_unset (&value);
1510 if (xvimagesink->have_colorkey) {
1511 GValue value = { 0 };
1513 array = g_value_array_new (1);
1514 g_value_init (&value, GST_TYPE_INT_RANGE);
1515 gst_value_set_int_range (&value, 0, 0xffffff);
1516 g_value_array_append (array, &value);
1517 g_value_unset (&value);
1521 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1530 gst_xv_image_sink_property_probe_interface_init (GstPropertyProbeInterface *
1533 iface->get_properties = gst_xv_image_sink_probe_get_properties;
1534 iface->probe_property = gst_xv_image_sink_probe_probe_property;
1535 iface->needs_probe = gst_xv_image_sink_probe_needs_probe;
1536 iface->get_values = gst_xv_image_sink_probe_get_values;
1540 /* =========================================== */
1542 /* Init & Class init */
1544 /* =========================================== */
1547 gst_xv_image_sink_set_property (GObject * object, guint prop_id,
1548 const GValue * value, GParamSpec * pspec)
1550 GstXvImageSink *xvimagesink;
1552 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1554 xvimagesink = GST_XV_IMAGE_SINK (object);
1558 xvimagesink->config.hue = g_value_get_int (value);
1559 xvimagesink->config.cb_changed = TRUE;
1560 gst_xv_image_sink_update_colorbalance (xvimagesink);
1563 xvimagesink->config.contrast = g_value_get_int (value);
1564 xvimagesink->config.cb_changed = TRUE;
1565 gst_xv_image_sink_update_colorbalance (xvimagesink);
1567 case PROP_BRIGHTNESS:
1568 xvimagesink->config.brightness = g_value_get_int (value);
1569 xvimagesink->config.cb_changed = TRUE;
1570 gst_xv_image_sink_update_colorbalance (xvimagesink);
1572 case PROP_SATURATION:
1573 xvimagesink->config.saturation = g_value_get_int (value);
1574 xvimagesink->config.cb_changed = TRUE;
1575 gst_xv_image_sink_update_colorbalance (xvimagesink);
1578 g_free (xvimagesink->config.display_name);
1579 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1581 case PROP_SYNCHRONOUS:
1582 xvimagesink->synchronous = g_value_get_boolean (value);
1583 if (xvimagesink->context) {
1584 gst_xvcontext_set_synchronous (xvimagesink->context,
1585 xvimagesink->synchronous);
1588 case PROP_PIXEL_ASPECT_RATIO:
1589 g_free (xvimagesink->par);
1590 xvimagesink->par = g_new0 (GValue, 1);
1591 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1592 if (!g_value_transform (value, xvimagesink->par)) {
1593 g_warning ("Could not transform string to aspect ratio");
1594 gst_value_set_fraction (xvimagesink->par, 1, 1);
1596 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1597 gst_value_get_fraction_numerator (xvimagesink->par),
1598 gst_value_get_fraction_denominator (xvimagesink->par));
1600 case PROP_FORCE_ASPECT_RATIO:
1601 xvimagesink->keep_aspect = g_value_get_boolean (value);
1603 case PROP_HANDLE_EVENTS:
1604 gst_xv_image_sink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1605 g_value_get_boolean (value));
1606 gst_xv_image_sink_manage_event_thread (xvimagesink);
1609 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1611 case PROP_HANDLE_EXPOSE:
1612 xvimagesink->handle_expose = g_value_get_boolean (value);
1613 gst_xv_image_sink_manage_event_thread (xvimagesink);
1615 case PROP_DOUBLE_BUFFER:
1616 xvimagesink->double_buffer = g_value_get_boolean (value);
1618 case PROP_AUTOPAINT_COLORKEY:
1619 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1622 xvimagesink->config.colorkey = g_value_get_int (value);
1624 case PROP_DRAW_BORDERS:
1625 xvimagesink->draw_borders = g_value_get_boolean (value);
1628 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1634 gst_xv_image_sink_get_property (GObject * object, guint prop_id,
1635 GValue * value, GParamSpec * pspec)
1637 GstXvImageSink *xvimagesink;
1639 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1641 xvimagesink = GST_XV_IMAGE_SINK (object);
1645 g_value_set_int (value, xvimagesink->config.hue);
1648 g_value_set_int (value, xvimagesink->config.contrast);
1650 case PROP_BRIGHTNESS:
1651 g_value_set_int (value, xvimagesink->config.brightness);
1653 case PROP_SATURATION:
1654 g_value_set_int (value, xvimagesink->config.saturation);
1657 g_value_set_string (value, xvimagesink->config.display_name);
1659 case PROP_SYNCHRONOUS:
1660 g_value_set_boolean (value, xvimagesink->synchronous);
1662 case PROP_PIXEL_ASPECT_RATIO:
1663 if (xvimagesink->par)
1664 g_value_transform (xvimagesink->par, value);
1666 case PROP_FORCE_ASPECT_RATIO:
1667 g_value_set_boolean (value, xvimagesink->keep_aspect);
1669 case PROP_HANDLE_EVENTS:
1670 g_value_set_boolean (value, xvimagesink->handle_events);
1674 char *adaptor_nr_s =
1675 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1677 g_value_set_string (value, adaptor_nr_s);
1678 g_free (adaptor_nr_s);
1681 case PROP_DEVICE_NAME:
1682 if (xvimagesink->context && xvimagesink->context->adaptors) {
1683 g_value_set_string (value,
1684 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1686 g_value_set_string (value, NULL);
1689 case PROP_HANDLE_EXPOSE:
1690 g_value_set_boolean (value, xvimagesink->handle_expose);
1692 case PROP_DOUBLE_BUFFER:
1693 g_value_set_boolean (value, xvimagesink->double_buffer);
1695 case PROP_AUTOPAINT_COLORKEY:
1696 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1699 g_value_set_int (value, xvimagesink->config.colorkey);
1701 case PROP_DRAW_BORDERS:
1702 g_value_set_boolean (value, xvimagesink->draw_borders);
1704 case PROP_WINDOW_WIDTH:
1705 if (xvimagesink->xwindow)
1706 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1708 g_value_set_uint64 (value, 0);
1710 case PROP_WINDOW_HEIGHT:
1711 if (xvimagesink->xwindow)
1712 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1714 g_value_set_uint64 (value, 0);
1717 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1723 gst_xv_image_sink_open (GstXvImageSink * xvimagesink)
1725 GError *error = NULL;
1727 /* Initializing the XvContext unless already done through GstVideoOverlay */
1728 if (!xvimagesink->context) {
1729 GstXvContext *context;
1730 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1733 GST_OBJECT_LOCK (xvimagesink);
1734 xvimagesink->context = context;
1736 GST_OBJECT_LOCK (xvimagesink);
1737 /* make an allocator for this context */
1738 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1739 GST_OBJECT_UNLOCK (xvimagesink);
1741 /* update object's par with calculated one if not set yet */
1742 if (!xvimagesink->par) {
1743 xvimagesink->par = g_new0 (GValue, 1);
1744 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1745 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1747 /* call XSynchronize with the current value of synchronous */
1748 gst_xvcontext_set_synchronous (xvimagesink->context,
1749 xvimagesink->synchronous);
1750 gst_xv_image_sink_update_colorbalance (xvimagesink);
1751 gst_xv_image_sink_manage_event_thread (xvimagesink);
1757 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1758 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1759 g_strdup (error->message), __FILE__, GST_FUNCTION, __LINE__);
1760 g_clear_error (&error);
1766 gst_xv_image_sink_close (GstXvImageSink * xvimagesink)
1769 GstXvContext *context;
1771 GST_OBJECT_LOCK (xvimagesink);
1772 xvimagesink->running = FALSE;
1773 /* grab thread and mark it as NULL */
1774 thread = xvimagesink->event_thread;
1775 xvimagesink->event_thread = NULL;
1776 GST_OBJECT_UNLOCK (xvimagesink);
1778 /* Wait for our event thread to finish before we clean up our stuff. */
1780 g_thread_join (thread);
1782 if (xvimagesink->cur_image) {
1783 gst_buffer_unref (xvimagesink->cur_image);
1784 xvimagesink->cur_image = NULL;
1787 g_mutex_lock (&xvimagesink->flow_lock);
1789 if (xvimagesink->pool) {
1790 gst_object_unref (xvimagesink->pool);
1791 xvimagesink->pool = NULL;
1794 if (xvimagesink->xwindow) {
1795 gst_xwindow_clear (xvimagesink->xwindow);
1796 gst_xwindow_destroy (xvimagesink->xwindow);
1797 xvimagesink->xwindow = NULL;
1799 g_mutex_unlock (&xvimagesink->flow_lock);
1801 if (xvimagesink->allocator) {
1802 gst_object_unref (xvimagesink->allocator);
1803 xvimagesink->allocator = NULL;
1806 GST_OBJECT_LOCK (xvimagesink);
1807 /* grab context and mark it as NULL */
1808 context = xvimagesink->context;
1809 xvimagesink->context = NULL;
1810 GST_OBJECT_UNLOCK (xvimagesink);
1813 gst_xvcontext_unref (context);
1816 /* Finalize is called only once, dispose can be called multiple times.
1817 * We use mutexes and don't reset stuff to NULL here so let's register
1820 gst_xv_image_sink_finalize (GObject * object)
1822 GstXvImageSink *xvimagesink;
1824 xvimagesink = GST_XV_IMAGE_SINK (object);
1826 gst_xv_image_sink_close (xvimagesink);
1828 gst_xvcontext_config_clear (&xvimagesink->config);
1830 if (xvimagesink->par) {
1831 g_free (xvimagesink->par);
1832 xvimagesink->par = NULL;
1834 g_mutex_clear (&xvimagesink->flow_lock);
1835 g_free (xvimagesink->media_title);
1837 G_OBJECT_CLASS (parent_class)->finalize (object);
1841 gst_xv_image_sink_init (GstXvImageSink * xvimagesink)
1843 xvimagesink->config.display_name = NULL;
1844 xvimagesink->config.adaptor_nr = 0;
1845 xvimagesink->config.autopaint_colorkey = TRUE;
1846 xvimagesink->config.double_buffer = TRUE;
1847 /* on 16bit displays this becomes r,g,b = 1,2,3
1848 * on 24bit displays this becomes r,g,b = 8,8,16
1849 * as a port atom value */
1850 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1851 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1852 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1853 xvimagesink->config.cb_changed = FALSE;
1855 xvimagesink->context = NULL;
1856 xvimagesink->xwindow = NULL;
1857 xvimagesink->cur_image = NULL;
1859 xvimagesink->fps_n = 0;
1860 xvimagesink->fps_d = 0;
1861 xvimagesink->video_width = 0;
1862 xvimagesink->video_height = 0;
1864 g_mutex_init (&xvimagesink->flow_lock);
1866 xvimagesink->pool = NULL;
1868 xvimagesink->synchronous = FALSE;
1869 xvimagesink->running = FALSE;
1870 xvimagesink->keep_aspect = TRUE;
1871 xvimagesink->handle_events = TRUE;
1872 xvimagesink->par = NULL;
1873 xvimagesink->handle_expose = TRUE;
1875 xvimagesink->draw_borders = TRUE;
1879 gst_xv_image_sink_class_init (GstXvImageSinkClass * klass)
1881 GObjectClass *gobject_class;
1882 GstElementClass *gstelement_class;
1883 GstBaseSinkClass *gstbasesink_class;
1884 GstVideoSinkClass *videosink_class;
1886 gobject_class = (GObjectClass *) klass;
1887 gstelement_class = (GstElementClass *) klass;
1888 gstbasesink_class = (GstBaseSinkClass *) klass;
1889 videosink_class = (GstVideoSinkClass *) klass;
1891 parent_class = g_type_class_peek_parent (klass);
1893 gobject_class->set_property = gst_xv_image_sink_set_property;
1894 gobject_class->get_property = gst_xv_image_sink_get_property;
1896 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1897 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1898 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1899 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1900 g_param_spec_int ("brightness", "Brightness",
1901 "The brightness of the video", -1000, 1000, 0,
1902 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1903 g_object_class_install_property (gobject_class, PROP_HUE,
1904 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1905 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1906 g_object_class_install_property (gobject_class, PROP_SATURATION,
1907 g_param_spec_int ("saturation", "Saturation",
1908 "The saturation of the video", -1000, 1000, 0,
1909 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1910 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1911 g_param_spec_string ("display", "Display", "X Display name", NULL,
1912 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1913 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1914 g_param_spec_boolean ("synchronous", "Synchronous",
1915 "When enabled, runs the X display in synchronous mode. "
1916 "(unrelated to A/V sync, used only for debugging)", FALSE,
1917 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1918 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1919 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1920 "The pixel aspect ratio of the device", "1/1",
1921 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1922 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1923 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1924 "When enabled, scaling will respect original aspect ratio", TRUE,
1925 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1926 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1927 g_param_spec_boolean ("handle-events", "Handle XEvents",
1928 "When enabled, XEvents will be selected and handled", TRUE,
1929 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1930 g_object_class_install_property (gobject_class, PROP_DEVICE,
1931 g_param_spec_string ("device", "Adaptor number",
1932 "The number of the video adaptor", "0",
1933 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1934 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1935 g_param_spec_string ("device-name", "Adaptor name",
1936 "The name of the video adaptor", NULL,
1937 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1939 * GstXvImageSink:handle-expose
1941 * When enabled, the current frame will always be drawn in response to X
1944 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1945 g_param_spec_boolean ("handle-expose", "Handle expose",
1947 "the current frame will always be drawn in response to X Expose "
1948 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1951 * GstXvImageSink:double-buffer
1953 * Whether to double-buffer the output.
1955 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1956 g_param_spec_boolean ("double-buffer", "Double-buffer",
1957 "Whether to double-buffer the output", TRUE,
1958 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1960 * GstXvImageSink:autopaint-colorkey
1962 * Whether to autofill overlay with colorkey
1964 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1965 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1966 "Whether to autofill overlay with colorkey", TRUE,
1967 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1969 * GstXvImageSink:colorkey
1971 * Color to use for the overlay mask.
1973 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1974 g_param_spec_int ("colorkey", "Colorkey",
1975 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1976 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1979 * GstXvImageSink:draw-borders
1981 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1982 * unused parts of the video area.
1984 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
1985 g_param_spec_boolean ("draw-borders", "Draw Borders",
1986 "Draw black borders to fill unused area in force-aspect-ratio mode",
1987 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1990 * GstXvImageSink:window-width
1992 * Actual width of the video window.
1994 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
1995 g_param_spec_uint64 ("window-width", "window-width",
1996 "Width of the window", 0, G_MAXUINT64, 0,
1997 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2000 * GstXvImageSink:window-height
2002 * Actual height of the video window.
2004 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2005 g_param_spec_uint64 ("window-height", "window-height",
2006 "Height of the window", 0, G_MAXUINT64, 0,
2007 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2009 gobject_class->finalize = gst_xv_image_sink_finalize;
2011 gst_element_class_set_static_metadata (gstelement_class,
2012 "Video sink", "Sink/Video",
2013 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2015 gst_element_class_add_pad_template (gstelement_class,
2016 gst_static_pad_template_get (&gst_xv_image_sink_sink_template_factory));
2018 gstelement_class->change_state =
2019 GST_DEBUG_FUNCPTR (gst_xv_image_sink_change_state);
2021 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_getcaps);
2022 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_setcaps);
2023 gstbasesink_class->get_times =
2024 GST_DEBUG_FUNCPTR (gst_xv_image_sink_get_times);
2025 gstbasesink_class->propose_allocation =
2026 GST_DEBUG_FUNCPTR (gst_xv_image_sink_propose_allocation);
2027 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xv_image_sink_event);
2029 videosink_class->show_frame =
2030 GST_DEBUG_FUNCPTR (gst_xv_image_sink_show_frame);