2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the #GstVideoOverlay interface and will then render
29 * video frames in this drawable. If no Window ID was provided by the
30 * application, the element will create its own internal window and render
34 * <title>Scaling</title>
36 * The XVideo extension, when it's available, handles hardware accelerated
37 * scaling of video frames. This means that the element will just accept
38 * incoming video frames no matter their geometry and will then put them to the
39 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
40 * property it is possible to enforce scaling with a constant aspect ratio,
41 * which means drawing black borders around the video frame.
45 * <title>Events</title>
47 * XvImageSink creates a thread to handle events coming from the drawable. There
48 * are several kind of events that can be grouped in 2 big categories: input
49 * events and window state related events. Input events will be translated to
50 * navigation events and pushed upstream for other elements to react on them.
51 * This includes events such as pointer moves, key press/release, clicks etc...
52 * Other events are used to handle the drawable appearance even when the data
53 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
54 * paused, it will receive expose events from the drawable and draw the latest
55 * frame with correct borders/aspect-ratio.
59 * <title>Pixel aspect ratio</title>
61 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
62 * the display specified in the #GstXvImageSink:display property or the
63 * default display if nothing specified. Once this connection is open it will
64 * inspect the display configuration including the physical display geometry and
65 * then calculate the pixel aspect ratio. When receiving video frames with a
66 * different pixel aspect ratio, XvImageSink will use hardware scaling to
67 * display the video frames correctly on display's pixel aspect ratio.
68 * Sometimes the calculated pixel aspect ratio can be wrong, it is
69 * then possible to enforce a specific pixel aspect ratio using the
70 * #GstXvImageSink:pixel-aspect-ratio property.
74 * <title>Examples</title>
76 * gst-launch -v videotestsrc ! xvimagesink
77 * ]| A pipeline to test hardware scaling.
78 * When the test video signal appears you can resize the window and see that
79 * video frames are scaled through hardware (no extra CPU cost).
81 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
82 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
83 * You can observe the borders drawn around the scaled image respecting aspect
86 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
87 * ]| A pipeline to test navigation events.
88 * While moving the mouse pointer over the test signal you will see a black box
89 * following the mouse pointer. If you press the mouse button somewhere on the
90 * video and release it somewhere else a green box will appear where you pressed
91 * the button and a red one where you released it. (The navigationtest element
92 * is part of gst-plugins-good.) You can observe here that even if the images
93 * are scaled through hardware the pointer coordinates are converted back to the
94 * original video frame geometry so that the box can be drawn to the correct
95 * position. This also handles borders correctly, limiting coordinates to the
98 * gst-launch -v videotestsrc ! video/x-raw, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
99 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
100 * videotestsrc, in most cases the pixel aspect ratio of the display will be
101 * 1/1. This means that XvImageSink will have to do the scaling to convert
102 * incoming frames to a size that will match the display pixel aspect ratio
103 * (from 320x240 to 320x180 in this case). Note that you might have to escape
104 * some characters for your shell like '\(fraction\)'.
106 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
107 * ]| Demonstrates how to use the colorbalance interface.
111 /* for developers: there are two useful tools : xvinfo and xvattr */
118 #include <gst/video/navigation.h>
119 #include <gst/video/videooverlay.h>
120 #include <gst/video/colorbalance.h>
121 /* Helper functions */
122 #include <gst/video/gstvideometa.h>
125 #include "xvimagesink.h"
126 #include "xvimageallocator.h"
128 /* Debugging category */
129 #include <gst/gstinfo.h>
131 /* for XkbKeycodeToKeysym */
132 #include <X11/XKBlib.h>
134 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
135 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
136 #define GST_CAT_DEFAULT gst_debug_xvimagesink
141 unsigned long functions;
142 unsigned long decorations;
144 unsigned long status;
146 MotifWmHints, MwmHints;
148 #define MWM_HINTS_DECORATIONS (1L << 1)
150 static gboolean gst_xvimagesink_open (GstXvImageSink * xvimagesink);
151 static void gst_xvimagesink_close (GstXvImageSink * xvimagesink);
152 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
154 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
156 /* Default template - initiated with class struct to allow gst-register to work
158 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
159 GST_STATIC_PAD_TEMPLATE ("sink",
162 GST_STATIC_CAPS ("video/x-raw, "
163 "framerate = (fraction) [ 0, MAX ], "
164 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
176 PROP_PIXEL_ASPECT_RATIO,
177 PROP_FORCE_ASPECT_RATIO,
183 PROP_AUTOPAINT_COLORKEY,
190 /* ============================================================= */
194 /* ============================================================= */
196 /* =========================================== */
198 /* Object typing & Creation */
200 /* =========================================== */
201 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
202 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
204 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
206 #define gst_xvimagesink_parent_class parent_class
207 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
208 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
209 gst_xvimagesink_navigation_init);
210 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
211 gst_xvimagesink_video_overlay_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
213 gst_xvimagesink_colorbalance_init));
216 /* ============================================================= */
218 /* Private Methods */
220 /* ============================================================= */
223 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
224 * if no window was available */
226 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
228 GstXvImageMemory *mem;
229 GstVideoCropMeta *crop;
230 GstVideoRectangle result;
231 gboolean draw_border = FALSE;
232 GstVideoRectangle src = { 0, };
233 GstVideoRectangle dst = { 0, };
234 GstVideoRectangle mem_crop;
237 /* We take the flow_lock. If expose is in there we don't want to run
238 concurrently from the data flow thread */
239 g_mutex_lock (&xvimagesink->flow_lock);
241 if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
242 g_mutex_unlock (&xvimagesink->flow_lock);
246 /* Draw borders when displaying the first frame. After this
247 draw borders only on expose event or after a size change. */
248 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
249 draw_border = xvimagesink->draw_borders;
250 xvimagesink->redraw_border = FALSE;
253 /* Store a reference to the last image we put, lose the previous one */
254 if (xvimage && xvimagesink->cur_image != xvimage) {
255 if (xvimagesink->cur_image) {
256 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
257 gst_buffer_unref (xvimagesink->cur_image);
259 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
260 xvimagesink->cur_image = gst_buffer_ref (xvimage);
263 /* Expose sends a NULL image, we take the latest frame */
265 if (xvimagesink->cur_image) {
267 xvimage = xvimagesink->cur_image;
269 g_mutex_unlock (&xvimagesink->flow_lock);
274 mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
275 gst_xvimage_memory_get_crop (mem, &mem_crop);
277 crop = gst_buffer_get_video_crop_meta (xvimage);
280 src.x = crop->x + mem_crop.x;
281 src.y = crop->y + mem_crop.y;
283 src.h = crop->height;
284 GST_LOG_OBJECT (xvimagesink,
285 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
290 if (xvimagesink->keep_aspect) {
293 /* We take the size of the source material as it was negotiated and
294 * corrected for DAR. This size can be different from the cropped size in
295 * which case the image will be scaled to fit the negotiated size. */
296 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
297 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
298 dst.w = xwindow->render_rect.w;
299 dst.h = xwindow->render_rect.h;
301 gst_video_sink_center_rect (s, dst, &result, TRUE);
302 result.x += xwindow->render_rect.x;
303 result.y += xwindow->render_rect.y;
305 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
308 gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
310 g_mutex_unlock (&xvimagesink->flow_lock);
316 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
317 GstXWindow * xwindow, const gchar * media_title)
320 g_free (xvimagesink->media_title);
321 xvimagesink->media_title = g_strdup (media_title);
324 /* we have a window */
325 const gchar *app_name;
326 const gchar *title = NULL;
327 gchar *title_mem = NULL;
329 /* set application name as a title */
330 app_name = g_get_application_name ();
332 if (app_name && xvimagesink->media_title) {
333 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
335 } else if (app_name) {
337 } else if (xvimagesink->media_title) {
338 title = xvimagesink->media_title;
341 gst_xwindow_set_title (xwindow, title);
346 /* This function handles a GstXWindow creation
347 * The width and height are the actual pixel size on the display */
349 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
350 gint width, gint height)
352 GstXWindow *xwindow = NULL;
353 GstXvContext *context;
355 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
357 context = xvimagesink->context;
359 xwindow = gst_xvcontext_create_xwindow (context, width, height);
361 /* set application name as a title */
362 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
364 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
366 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
373 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
375 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
377 /* Update the window geometry */
378 g_mutex_lock (&xvimagesink->flow_lock);
379 if (G_LIKELY (xvimagesink->xwindow))
380 gst_xwindow_update_geometry (xvimagesink->xwindow);
381 g_mutex_unlock (&xvimagesink->flow_lock);
384 /* This function commits our internal colorbalance settings to our grabbed Xv
385 port. If the context is not initialized yet it simply returns */
387 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
389 GstXvContext *context;
391 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
393 /* If we haven't initialized the X context we can't update anything */
394 if ((context = xvimagesink->context) == NULL)
397 gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
400 /* This function handles XEvents that might be in the queue. It generates
401 GstEvent that will be sent upstream in the pipeline to handle interactivity
402 and navigation. It will also listen for configure events on the window to
403 trigger caps renegotiation so on the fly software scaling can work. */
405 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
408 guint pointer_x = 0, pointer_y = 0;
409 gboolean pointer_moved = FALSE;
410 gboolean exposed = FALSE, configured = FALSE;
412 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
414 /* Handle Interaction, produces navigation events */
416 /* We get all pointer motion events, only the last position is
418 g_mutex_lock (&xvimagesink->flow_lock);
419 g_mutex_lock (&xvimagesink->context->lock);
420 while (XCheckWindowEvent (xvimagesink->context->disp,
421 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
422 g_mutex_unlock (&xvimagesink->context->lock);
423 g_mutex_unlock (&xvimagesink->flow_lock);
427 pointer_x = e.xmotion.x;
428 pointer_y = e.xmotion.y;
429 pointer_moved = TRUE;
434 g_mutex_lock (&xvimagesink->flow_lock);
435 g_mutex_lock (&xvimagesink->context->lock);
439 g_mutex_unlock (&xvimagesink->context->lock);
440 g_mutex_unlock (&xvimagesink->flow_lock);
442 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
443 pointer_x, pointer_y);
444 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
445 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
447 g_mutex_lock (&xvimagesink->flow_lock);
448 g_mutex_lock (&xvimagesink->context->lock);
451 /* We get all events on our window to throw them upstream */
452 while (XCheckWindowEvent (xvimagesink->context->disp,
453 xvimagesink->xwindow->win,
454 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
457 const char *key_str = NULL;
459 /* We lock only for the X function call */
460 g_mutex_unlock (&xvimagesink->context->lock);
461 g_mutex_unlock (&xvimagesink->flow_lock);
465 /* Mouse button pressed over our window. We send upstream
466 events for interactivity/navigation */
467 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
468 e.xbutton.button, e.xbutton.x, e.xbutton.y);
469 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
470 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
473 /* Mouse button released over our window. We send upstream
474 events for interactivity/navigation */
475 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
476 e.xbutton.button, e.xbutton.x, e.xbutton.y);
477 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
478 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
482 /* Key pressed/released over our window. We send upstream
483 events for interactivity/navigation */
484 g_mutex_lock (&xvimagesink->context->lock);
485 keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
486 e.xkey.keycode, 0, 0);
487 if (keysym != NoSymbol) {
488 key_str = XKeysymToString (keysym);
492 g_mutex_unlock (&xvimagesink->context->lock);
493 GST_DEBUG_OBJECT (xvimagesink,
494 "key %d pressed over window at %d,%d (%s)",
495 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
496 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
497 e.type == KeyPress ? "key-press" : "key-release", key_str);
500 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
503 g_mutex_lock (&xvimagesink->flow_lock);
504 g_mutex_lock (&xvimagesink->context->lock);
508 while (XCheckWindowEvent (xvimagesink->context->disp,
509 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
514 case ConfigureNotify:
515 g_mutex_unlock (&xvimagesink->context->lock);
516 g_mutex_unlock (&xvimagesink->flow_lock);
518 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
520 g_mutex_lock (&xvimagesink->flow_lock);
521 g_mutex_lock (&xvimagesink->context->lock);
529 if (xvimagesink->handle_expose && (exposed || configured)) {
530 g_mutex_unlock (&xvimagesink->context->lock);
531 g_mutex_unlock (&xvimagesink->flow_lock);
533 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
535 g_mutex_lock (&xvimagesink->flow_lock);
536 g_mutex_lock (&xvimagesink->context->lock);
539 /* Handle Display events */
540 while (XPending (xvimagesink->context->disp)) {
541 XNextEvent (xvimagesink->context->disp, &e);
547 wm_delete = XInternAtom (xvimagesink->context->disp,
548 "WM_DELETE_WINDOW", True);
549 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
550 /* Handle window deletion by posting an error on the bus */
551 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
552 ("Output window was closed"), (NULL));
554 g_mutex_unlock (&xvimagesink->context->lock);
555 gst_xwindow_destroy (xvimagesink->xwindow);
556 xvimagesink->xwindow = NULL;
557 g_mutex_lock (&xvimagesink->context->lock);
566 g_mutex_unlock (&xvimagesink->context->lock);
567 g_mutex_unlock (&xvimagesink->flow_lock);
571 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
573 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
575 GST_OBJECT_LOCK (xvimagesink);
576 while (xvimagesink->running) {
577 GST_OBJECT_UNLOCK (xvimagesink);
579 if (xvimagesink->xwindow) {
580 gst_xvimagesink_handle_xevents (xvimagesink);
582 /* FIXME: do we want to align this with the framerate or anything else? */
583 g_usleep (G_USEC_PER_SEC / 20);
585 GST_OBJECT_LOCK (xvimagesink);
587 GST_OBJECT_UNLOCK (xvimagesink);
593 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
595 GThread *thread = NULL;
597 /* don't start the thread too early */
598 if (xvimagesink->context == NULL) {
602 GST_OBJECT_LOCK (xvimagesink);
603 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
604 if (!xvimagesink->event_thread) {
605 /* Setup our event listening thread */
606 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
607 xvimagesink->handle_expose, xvimagesink->handle_events);
608 xvimagesink->running = TRUE;
609 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
610 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
613 if (xvimagesink->event_thread) {
614 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
615 xvimagesink->handle_expose, xvimagesink->handle_events);
616 xvimagesink->running = FALSE;
617 /* grab thread and mark it as NULL */
618 thread = xvimagesink->event_thread;
619 xvimagesink->event_thread = NULL;
622 GST_OBJECT_UNLOCK (xvimagesink);
624 /* Wait for our event thread to finish */
626 g_thread_join (thread);
633 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
635 GstXvImageSink *xvimagesink;
638 xvimagesink = GST_XVIMAGESINK (bsink);
640 if (xvimagesink->context) {
642 return gst_caps_intersect_full (filter, xvimagesink->context->caps,
643 GST_CAPS_INTERSECT_FIRST);
645 return gst_caps_ref (xvimagesink->context->caps);
648 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
650 GstCaps *intersection;
653 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
654 gst_caps_unref (caps);
661 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
663 GstXvImageSink *xvimagesink;
664 GstXvContext *context;
665 GstStructure *structure;
666 GstBufferPool *newpool, *oldpool;
668 guint32 im_format = 0;
669 gint video_par_n, video_par_d; /* video's PAR */
670 gint display_par_n, display_par_d; /* display's PAR */
674 xvimagesink = GST_XVIMAGESINK (bsink);
675 context = xvimagesink->context;
677 GST_DEBUG_OBJECT (xvimagesink,
678 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
679 GST_PTR_FORMAT, context->caps, caps);
681 if (!gst_caps_can_intersect (context->caps, caps))
682 goto incompatible_caps;
684 if (!gst_video_info_from_caps (&info, caps))
687 xvimagesink->fps_n = info.fps_n;
688 xvimagesink->fps_d = info.fps_d;
690 xvimagesink->video_width = info.width;
691 xvimagesink->video_height = info.height;
693 im_format = gst_xvcontext_get_format_from_info (context, &info);
697 gst_xvcontext_set_colorimetry (context, &info.colorimetry);
701 /* get aspect ratio from caps if it's present, and
702 * convert video width and height to a display width and height
703 * using wd / hd = wv / hv * PARv / PARd */
705 /* get video's PAR */
706 video_par_n = info.par_n;
707 video_par_d = info.par_d;
709 /* get display's PAR */
710 if (xvimagesink->par) {
711 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
712 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
718 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
719 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
722 GST_DEBUG_OBJECT (xvimagesink,
723 "video width/height: %dx%d, calculated display ratio: %d/%d",
724 info.width, info.height, num, den);
726 /* now find a width x height that respects this display ratio.
727 * prefer those that have one of w/h the same as the incoming video
728 * using wd / hd = num / den */
730 /* start with same height, because of interlaced video */
731 /* check hd / den is an integer scale factor, and scale wd with the PAR */
732 if (info.height % den == 0) {
733 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
734 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
735 gst_util_uint64_scale_int (info.height, num, den);
736 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
737 } else if (info.width % num == 0) {
738 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
739 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
740 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
741 gst_util_uint64_scale_int (info.width, den, num);
743 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
744 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
745 gst_util_uint64_scale_int (info.height, num, den);
746 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
748 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
749 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
751 /* Notify application to set xwindow id now */
752 g_mutex_lock (&xvimagesink->flow_lock);
753 if (!xvimagesink->xwindow) {
754 g_mutex_unlock (&xvimagesink->flow_lock);
755 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
757 g_mutex_unlock (&xvimagesink->flow_lock);
760 /* Creating our window and our image with the display size in pixels */
761 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
762 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
763 goto no_display_size;
765 g_mutex_lock (&xvimagesink->flow_lock);
766 if (!xvimagesink->xwindow) {
767 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
768 GST_VIDEO_SINK_WIDTH (xvimagesink),
769 GST_VIDEO_SINK_HEIGHT (xvimagesink));
772 xvimagesink->info = info;
774 /* After a resize, we want to redraw the borders in case the new frame size
775 * doesn't cover the same area */
776 xvimagesink->redraw_border = TRUE;
778 /* create a new pool for the new configuration */
779 newpool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
781 structure = gst_buffer_pool_get_config (newpool);
782 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
783 if (!gst_buffer_pool_set_config (newpool, structure))
786 oldpool = xvimagesink->pool;
787 /* we don't activate the pool yet, this will be done by downstream after it
788 * has configured the pool. If downstream does not want our pool we will
789 * activate it when we render into it */
790 xvimagesink->pool = newpool;
791 g_mutex_unlock (&xvimagesink->flow_lock);
793 /* unref the old sink */
795 /* we don't deactivate, some elements might still be using it, it will
796 * be deactivated when the last ref is gone */
797 gst_object_unref (oldpool);
805 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
810 GST_DEBUG_OBJECT (xvimagesink,
811 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
816 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
817 ("Error calculating the output display ratio of the video."));
822 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
823 ("Error calculating the output display ratio of the video."));
828 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
829 g_mutex_unlock (&xvimagesink->flow_lock);
834 static GstStateChangeReturn
835 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
837 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
838 GstXvImageSink *xvimagesink;
840 xvimagesink = GST_XVIMAGESINK (element);
842 switch (transition) {
843 case GST_STATE_CHANGE_NULL_TO_READY:
844 if (!gst_xvimagesink_open (xvimagesink))
847 case GST_STATE_CHANGE_READY_TO_PAUSED:
849 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
851 case GST_STATE_CHANGE_PAUSED_TO_READY:
857 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
859 switch (transition) {
860 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
862 case GST_STATE_CHANGE_PAUSED_TO_READY:
863 xvimagesink->fps_n = 0;
864 xvimagesink->fps_d = 1;
865 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
866 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
867 g_mutex_lock (&xvimagesink->flow_lock);
868 if (xvimagesink->pool)
869 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
870 g_mutex_unlock (&xvimagesink->flow_lock);
872 case GST_STATE_CHANGE_READY_TO_NULL:
873 gst_xvimagesink_close (xvimagesink);
882 return GST_STATE_CHANGE_FAILURE;
887 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
888 GstClockTime * start, GstClockTime * end)
890 GstXvImageSink *xvimagesink;
892 xvimagesink = GST_XVIMAGESINK (bsink);
894 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
895 *start = GST_BUFFER_TIMESTAMP (buf);
896 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
897 *end = *start + GST_BUFFER_DURATION (buf);
899 if (xvimagesink->fps_n > 0) {
901 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
909 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
912 GstXvImageSink *xvimagesink;
913 GstBuffer *to_put = NULL;
916 xvimagesink = GST_XVIMAGESINK (vsink);
918 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
919 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
920 /* If this buffer has been allocated using our buffer management we simply
921 put the ximage which is in the PRIVATE pointer */
922 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
927 GstVideoFrame src, dest;
928 GstBufferPoolAcquireParams params = { 0, };
930 /* Else we have to copy the data into our private image, */
931 /* if we have one... */
932 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
934 /* we should have a pool, configured in setcaps */
935 if (xvimagesink->pool == NULL)
938 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
939 goto activate_failed;
941 /* take a buffer from our pool, if there is no buffer in the pool something
942 * is seriously wrong, waiting for the pool here might deadlock when we try
943 * to go to PAUSED because we never flush the pool then. */
944 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
945 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
946 if (res != GST_FLOW_OK)
949 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
950 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
952 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
955 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
956 gst_video_frame_unmap (&src);
960 gst_video_frame_copy (&dest, &src);
962 gst_video_frame_unmap (&dest);
963 gst_video_frame_unmap (&src);
966 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
971 gst_buffer_unref (to_put);
978 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
979 ("Internal error: can't allocate images"),
980 ("We don't have a bufferpool negotiated"));
981 return GST_FLOW_ERROR;
985 /* No image available. That's very bad ! */
986 GST_WARNING_OBJECT (xvimagesink, "could not create image");
991 /* No Window available to put our image into */
992 GST_WARNING_OBJECT (xvimagesink, "could not map image");
998 /* No Window available to put our image into */
999 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1000 res = GST_FLOW_ERROR;
1005 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1006 res = GST_FLOW_ERROR;
1012 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1014 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1016 switch (GST_EVENT_TYPE (event)) {
1017 case GST_EVENT_TAG:{
1019 gchar *title = NULL;
1021 gst_event_parse_tag (event, &l);
1022 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1025 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1026 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1036 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1040 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1042 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1043 GstBufferPool *pool;
1044 GstStructure *config;
1049 gst_query_parse_allocation (query, &caps, &need_pool);
1054 g_mutex_lock (&xvimagesink->flow_lock);
1055 if ((pool = xvimagesink->pool))
1056 gst_object_ref (pool);
1057 g_mutex_unlock (&xvimagesink->flow_lock);
1062 /* we had a pool, check caps */
1063 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1064 config = gst_buffer_pool_get_config (pool);
1065 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1067 if (!gst_caps_is_equal (caps, pcaps)) {
1068 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1069 /* different caps, we can't use this pool */
1070 gst_object_unref (pool);
1073 gst_structure_free (config);
1075 if (pool == NULL && need_pool) {
1078 if (!gst_video_info_from_caps (&info, caps))
1081 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1082 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
1084 /* the normal size of a frame */
1087 config = gst_buffer_pool_get_config (pool);
1088 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1089 if (!gst_buffer_pool_set_config (pool, config))
1093 /* we need at least 2 buffer because we hold on to the last one */
1094 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1095 gst_object_unref (pool);
1098 /* we also support various metadata */
1099 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1100 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1107 GST_DEBUG_OBJECT (bsink, "no caps specified");
1112 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1117 GST_DEBUG_OBJECT (bsink, "failed setting config");
1118 gst_object_unref (pool);
1123 /* Interfaces stuff */
1125 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1126 GstStructure * structure)
1128 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1130 gboolean handled = FALSE;
1131 GstEvent *event = NULL;
1133 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1134 GstVideoRectangle src = { 0, };
1135 GstVideoRectangle dst = { 0, };
1136 GstVideoRectangle result;
1137 gdouble x, y, xscale = 1.0, yscale = 1.0;
1138 GstXWindow *xwindow;
1140 event = gst_event_new_navigation (structure);
1142 /* We take the flow_lock while we look at the window */
1143 g_mutex_lock (&xvimagesink->flow_lock);
1145 if (!(xwindow = xvimagesink->xwindow)) {
1146 g_mutex_unlock (&xvimagesink->flow_lock);
1150 if (xvimagesink->keep_aspect) {
1151 /* We get the frame position using the calculated geometry from _setcaps
1152 that respect pixel aspect ratios */
1153 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1154 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1155 dst.w = xwindow->render_rect.w;
1156 dst.h = xwindow->render_rect.h;
1158 gst_video_sink_center_rect (src, dst, &result, TRUE);
1159 result.x += xwindow->render_rect.x;
1160 result.y += xwindow->render_rect.y;
1162 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1165 g_mutex_unlock (&xvimagesink->flow_lock);
1167 /* We calculate scaling using the original video frames geometry to include
1168 pixel aspect ratio scaling. */
1169 xscale = (gdouble) xvimagesink->video_width / result.w;
1170 yscale = (gdouble) xvimagesink->video_height / result.h;
1172 /* Converting pointer coordinates to the non scaled geometry */
1173 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1174 x = MIN (x, result.x + result.w);
1175 x = MAX (x - result.x, 0);
1176 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1177 (gdouble) x * xscale, NULL);
1179 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1180 y = MIN (y, result.y + result.h);
1181 y = MAX (y - result.y, 0);
1182 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1183 (gdouble) y * yscale, NULL);
1186 gst_event_ref (event);
1187 handled = gst_pad_send_event (peer, event);
1188 gst_object_unref (peer);
1191 if (!handled && event) {
1192 gst_element_post_message ((GstElement *) xvimagesink,
1193 gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1197 gst_event_unref (event);
1201 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1203 iface->send_event = gst_xvimagesink_navigation_send_event;
1207 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1209 XID xwindow_id = id;
1210 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1211 GstXWindow *xwindow = NULL;
1212 GstXvContext *context;
1214 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1216 g_mutex_lock (&xvimagesink->flow_lock);
1218 /* If we already use that window return */
1219 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1220 g_mutex_unlock (&xvimagesink->flow_lock);
1224 /* If the element has not initialized the X11 context try to do so */
1225 if (!xvimagesink->context &&
1226 !(xvimagesink->context =
1227 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1228 g_mutex_unlock (&xvimagesink->flow_lock);
1229 /* we have thrown a GST_ELEMENT_ERROR now */
1233 context = xvimagesink->context;
1235 gst_xvimagesink_update_colorbalance (xvimagesink);
1237 /* If a window is there already we destroy it */
1238 if (xvimagesink->xwindow) {
1239 gst_xwindow_destroy (xvimagesink->xwindow);
1240 xvimagesink->xwindow = NULL;
1243 /* If the xid is 0 we go back to an internal window */
1244 if (xwindow_id == 0) {
1245 /* If no width/height caps nego did not happen window will be created
1246 during caps nego then */
1247 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1248 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1250 gst_xvimagesink_xwindow_new (xvimagesink,
1251 GST_VIDEO_SINK_WIDTH (xvimagesink),
1252 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1255 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1256 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1260 xvimagesink->xwindow = xwindow;
1262 g_mutex_unlock (&xvimagesink->flow_lock);
1266 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1268 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1270 GST_DEBUG ("doing expose");
1271 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1272 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1276 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1277 gboolean handle_events)
1279 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1281 g_mutex_lock (&xvimagesink->flow_lock);
1282 xvimagesink->handle_events = handle_events;
1283 if (G_LIKELY (xvimagesink->xwindow))
1284 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1285 g_mutex_unlock (&xvimagesink->flow_lock);
1289 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1290 gint width, gint height)
1292 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1294 g_mutex_lock (&xvimagesink->flow_lock);
1295 if (G_LIKELY (xvimagesink->xwindow))
1296 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1298 g_mutex_unlock (&xvimagesink->flow_lock);
1302 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1304 iface->set_window_handle = gst_xvimagesink_set_window_handle;
1305 iface->expose = gst_xvimagesink_expose;
1306 iface->handle_events = gst_xvimagesink_set_event_handling;
1307 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1310 static const GList *
1311 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1313 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1315 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1317 if (xvimagesink->context)
1318 return xvimagesink->context->channels_list;
1324 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1325 GstColorBalanceChannel * channel, gint value)
1327 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1329 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1330 g_return_if_fail (channel->label != NULL);
1332 xvimagesink->config.cb_changed = TRUE;
1334 /* Normalize val to [-1000, 1000] */
1335 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1336 (double) (channel->max_value - channel->min_value));
1338 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1339 xvimagesink->config.hue = value;
1340 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1341 xvimagesink->config.saturation = value;
1342 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1343 xvimagesink->config.contrast = value;
1344 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1345 xvimagesink->config.brightness = value;
1347 g_warning ("got an unknown channel %s", channel->label);
1351 gst_xvimagesink_update_colorbalance (xvimagesink);
1355 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1356 GstColorBalanceChannel * channel)
1358 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1361 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1362 g_return_val_if_fail (channel->label != NULL, 0);
1364 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1365 value = xvimagesink->config.hue;
1366 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1367 value = xvimagesink->config.saturation;
1368 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1369 value = xvimagesink->config.contrast;
1370 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1371 value = xvimagesink->config.brightness;
1373 g_warning ("got an unknown channel %s", channel->label);
1376 /* Normalize val to [channel->min_value, channel->max_value] */
1377 value = channel->min_value + (channel->max_value - channel->min_value) *
1378 (value + 1000) / 2000;
1383 static GstColorBalanceType
1384 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1386 return GST_COLOR_BALANCE_HARDWARE;
1390 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1392 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1393 iface->set_value = gst_xvimagesink_colorbalance_set_value;
1394 iface->get_value = gst_xvimagesink_colorbalance_get_value;
1395 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1399 static const GList *
1400 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1402 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1403 static GList *list = NULL;
1406 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1408 g_list_append (list, g_object_class_find_property (klass,
1409 "autopaint-colorkey"));
1411 g_list_append (list, g_object_class_find_property (klass,
1414 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1421 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1422 guint prop_id, const GParamSpec * pspec)
1424 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1428 case PROP_AUTOPAINT_COLORKEY:
1429 case PROP_DOUBLE_BUFFER:
1431 GST_DEBUG_OBJECT (xvimagesink,
1432 "probing device list and get capabilities");
1433 if (!xvimagesink->context) {
1434 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1435 xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1439 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1445 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1446 guint prop_id, const GParamSpec * pspec)
1448 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1449 gboolean ret = FALSE;
1453 case PROP_AUTOPAINT_COLORKEY:
1454 case PROP_DOUBLE_BUFFER:
1456 if (xvimagesink->context != NULL) {
1463 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1470 static GValueArray *
1471 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1472 guint prop_id, const GParamSpec * pspec)
1474 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1475 GValueArray *array = NULL;
1477 if (G_UNLIKELY (!xvimagesink->context)) {
1478 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1487 GValue value = { 0 };
1489 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1490 g_value_init (&value, G_TYPE_STRING);
1492 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1493 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1495 g_value_set_string (&value, adaptor_id_s);
1496 g_value_array_append (array, &value);
1497 g_free (adaptor_id_s);
1499 g_value_unset (&value);
1502 case PROP_AUTOPAINT_COLORKEY:
1503 if (xvimagesink->have_autopaint_colorkey) {
1504 GValue value = { 0 };
1506 array = g_value_array_new (2);
1507 g_value_init (&value, G_TYPE_BOOLEAN);
1508 g_value_set_boolean (&value, FALSE);
1509 g_value_array_append (array, &value);
1510 g_value_set_boolean (&value, TRUE);
1511 g_value_array_append (array, &value);
1512 g_value_unset (&value);
1515 case PROP_DOUBLE_BUFFER:
1516 if (xvimagesink->have_double_buffer) {
1517 GValue value = { 0 };
1519 array = g_value_array_new (2);
1520 g_value_init (&value, G_TYPE_BOOLEAN);
1521 g_value_set_boolean (&value, FALSE);
1522 g_value_array_append (array, &value);
1523 g_value_set_boolean (&value, TRUE);
1524 g_value_array_append (array, &value);
1525 g_value_unset (&value);
1529 if (xvimagesink->have_colorkey) {
1530 GValue value = { 0 };
1532 array = g_value_array_new (1);
1533 g_value_init (&value, GST_TYPE_INT_RANGE);
1534 gst_value_set_int_range (&value, 0, 0xffffff);
1535 g_value_array_append (array, &value);
1536 g_value_unset (&value);
1540 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1549 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1552 iface->get_properties = gst_xvimagesink_probe_get_properties;
1553 iface->probe_property = gst_xvimagesink_probe_probe_property;
1554 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1555 iface->get_values = gst_xvimagesink_probe_get_values;
1559 /* =========================================== */
1561 /* Init & Class init */
1563 /* =========================================== */
1566 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1567 const GValue * value, GParamSpec * pspec)
1569 GstXvImageSink *xvimagesink;
1571 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1573 xvimagesink = GST_XVIMAGESINK (object);
1577 xvimagesink->config.hue = g_value_get_int (value);
1578 xvimagesink->config.cb_changed = TRUE;
1579 gst_xvimagesink_update_colorbalance (xvimagesink);
1582 xvimagesink->config.contrast = g_value_get_int (value);
1583 xvimagesink->config.cb_changed = TRUE;
1584 gst_xvimagesink_update_colorbalance (xvimagesink);
1586 case PROP_BRIGHTNESS:
1587 xvimagesink->config.brightness = g_value_get_int (value);
1588 xvimagesink->config.cb_changed = TRUE;
1589 gst_xvimagesink_update_colorbalance (xvimagesink);
1591 case PROP_SATURATION:
1592 xvimagesink->config.saturation = g_value_get_int (value);
1593 xvimagesink->config.cb_changed = TRUE;
1594 gst_xvimagesink_update_colorbalance (xvimagesink);
1597 g_free (xvimagesink->config.display_name);
1598 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1600 case PROP_SYNCHRONOUS:
1601 xvimagesink->synchronous = g_value_get_boolean (value);
1602 if (xvimagesink->context) {
1603 gst_xvcontext_set_synchronous (xvimagesink->context,
1604 xvimagesink->synchronous);
1607 case PROP_PIXEL_ASPECT_RATIO:
1608 g_free (xvimagesink->par);
1609 xvimagesink->par = g_new0 (GValue, 1);
1610 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1611 if (!g_value_transform (value, xvimagesink->par)) {
1612 g_warning ("Could not transform string to aspect ratio");
1613 gst_value_set_fraction (xvimagesink->par, 1, 1);
1615 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1616 gst_value_get_fraction_numerator (xvimagesink->par),
1617 gst_value_get_fraction_denominator (xvimagesink->par));
1619 case PROP_FORCE_ASPECT_RATIO:
1620 xvimagesink->keep_aspect = g_value_get_boolean (value);
1622 case PROP_HANDLE_EVENTS:
1623 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1624 g_value_get_boolean (value));
1625 gst_xvimagesink_manage_event_thread (xvimagesink);
1628 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1630 case PROP_HANDLE_EXPOSE:
1631 xvimagesink->handle_expose = g_value_get_boolean (value);
1632 gst_xvimagesink_manage_event_thread (xvimagesink);
1634 case PROP_DOUBLE_BUFFER:
1635 xvimagesink->double_buffer = g_value_get_boolean (value);
1637 case PROP_AUTOPAINT_COLORKEY:
1638 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1641 xvimagesink->config.colorkey = g_value_get_int (value);
1643 case PROP_DRAW_BORDERS:
1644 xvimagesink->draw_borders = g_value_get_boolean (value);
1647 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1653 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1654 GValue * value, GParamSpec * pspec)
1656 GstXvImageSink *xvimagesink;
1658 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1660 xvimagesink = GST_XVIMAGESINK (object);
1664 g_value_set_int (value, xvimagesink->config.hue);
1667 g_value_set_int (value, xvimagesink->config.contrast);
1669 case PROP_BRIGHTNESS:
1670 g_value_set_int (value, xvimagesink->config.brightness);
1672 case PROP_SATURATION:
1673 g_value_set_int (value, xvimagesink->config.saturation);
1676 g_value_set_string (value, xvimagesink->config.display_name);
1678 case PROP_SYNCHRONOUS:
1679 g_value_set_boolean (value, xvimagesink->synchronous);
1681 case PROP_PIXEL_ASPECT_RATIO:
1682 if (xvimagesink->par)
1683 g_value_transform (xvimagesink->par, value);
1685 case PROP_FORCE_ASPECT_RATIO:
1686 g_value_set_boolean (value, xvimagesink->keep_aspect);
1688 case PROP_HANDLE_EVENTS:
1689 g_value_set_boolean (value, xvimagesink->handle_events);
1693 char *adaptor_nr_s =
1694 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1696 g_value_set_string (value, adaptor_nr_s);
1697 g_free (adaptor_nr_s);
1700 case PROP_DEVICE_NAME:
1701 if (xvimagesink->context && xvimagesink->context->adaptors) {
1702 g_value_set_string (value,
1703 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1705 g_value_set_string (value, NULL);
1708 case PROP_HANDLE_EXPOSE:
1709 g_value_set_boolean (value, xvimagesink->handle_expose);
1711 case PROP_DOUBLE_BUFFER:
1712 g_value_set_boolean (value, xvimagesink->double_buffer);
1714 case PROP_AUTOPAINT_COLORKEY:
1715 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1718 g_value_set_int (value, xvimagesink->config.colorkey);
1720 case PROP_DRAW_BORDERS:
1721 g_value_set_boolean (value, xvimagesink->draw_borders);
1723 case PROP_WINDOW_WIDTH:
1724 if (xvimagesink->xwindow)
1725 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1727 g_value_set_uint64 (value, 0);
1729 case PROP_WINDOW_HEIGHT:
1730 if (xvimagesink->xwindow)
1731 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1733 g_value_set_uint64 (value, 0);
1736 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1742 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1744 GError *error = NULL;
1746 /* Initializing the XvContext unless already done through GstVideoOverlay */
1747 if (!xvimagesink->context) {
1748 GstXvContext *context;
1749 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1752 GST_OBJECT_LOCK (xvimagesink);
1753 xvimagesink->context = context;
1755 GST_OBJECT_LOCK (xvimagesink);
1756 /* make an allocator for this context */
1757 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1758 GST_OBJECT_UNLOCK (xvimagesink);
1760 /* update object's par with calculated one if not set yet */
1761 if (!xvimagesink->par) {
1762 xvimagesink->par = g_new0 (GValue, 1);
1763 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1764 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1766 /* call XSynchronize with the current value of synchronous */
1767 gst_xvcontext_set_synchronous (xvimagesink->context,
1768 xvimagesink->synchronous);
1769 gst_xvimagesink_update_colorbalance (xvimagesink);
1770 gst_xvimagesink_manage_event_thread (xvimagesink);
1776 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1777 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1778 error->message, __FILE__, GST_FUNCTION, __LINE__);
1784 gst_xvimagesink_close (GstXvImageSink * xvimagesink)
1787 GstXvContext *context;
1789 GST_OBJECT_LOCK (xvimagesink);
1790 xvimagesink->running = FALSE;
1791 /* grab thread and mark it as NULL */
1792 thread = xvimagesink->event_thread;
1793 xvimagesink->event_thread = NULL;
1794 GST_OBJECT_UNLOCK (xvimagesink);
1796 /* Wait for our event thread to finish before we clean up our stuff. */
1798 g_thread_join (thread);
1800 if (xvimagesink->cur_image) {
1801 gst_buffer_unref (xvimagesink->cur_image);
1802 xvimagesink->cur_image = NULL;
1805 g_mutex_lock (&xvimagesink->flow_lock);
1807 if (xvimagesink->pool) {
1808 gst_object_unref (xvimagesink->pool);
1809 xvimagesink->pool = NULL;
1812 if (xvimagesink->xwindow) {
1813 gst_xwindow_clear (xvimagesink->xwindow);
1814 gst_xwindow_destroy (xvimagesink->xwindow);
1815 xvimagesink->xwindow = NULL;
1817 g_mutex_unlock (&xvimagesink->flow_lock);
1819 if (xvimagesink->allocator) {
1820 gst_object_unref (xvimagesink->allocator);
1821 xvimagesink->allocator = NULL;
1824 GST_OBJECT_LOCK (xvimagesink);
1825 /* grab context and mark it as NULL */
1826 context = xvimagesink->context;
1827 xvimagesink->context = NULL;
1828 GST_OBJECT_UNLOCK (xvimagesink);
1831 gst_xvcontext_unref (context);
1834 /* Finalize is called only once, dispose can be called multiple times.
1835 * We use mutexes and don't reset stuff to NULL here so let's register
1838 gst_xvimagesink_finalize (GObject * object)
1840 GstXvImageSink *xvimagesink;
1842 xvimagesink = GST_XVIMAGESINK (object);
1844 gst_xvimagesink_close (xvimagesink);
1846 gst_xvcontext_config_clear (&xvimagesink->config);
1848 if (xvimagesink->par) {
1849 g_free (xvimagesink->par);
1850 xvimagesink->par = NULL;
1852 g_mutex_clear (&xvimagesink->flow_lock);
1853 g_free (xvimagesink->media_title);
1855 G_OBJECT_CLASS (parent_class)->finalize (object);
1859 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1861 xvimagesink->config.display_name = NULL;
1862 xvimagesink->config.adaptor_nr = 0;
1863 xvimagesink->config.autopaint_colorkey = TRUE;
1864 xvimagesink->config.double_buffer = TRUE;
1865 /* on 16bit displays this becomes r,g,b = 1,2,3
1866 * on 24bit displays this becomes r,g,b = 8,8,16
1867 * as a port atom value */
1868 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1869 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1870 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1871 xvimagesink->config.cb_changed = FALSE;
1873 xvimagesink->context = NULL;
1874 xvimagesink->xwindow = NULL;
1875 xvimagesink->cur_image = NULL;
1877 xvimagesink->fps_n = 0;
1878 xvimagesink->fps_d = 0;
1879 xvimagesink->video_width = 0;
1880 xvimagesink->video_height = 0;
1882 g_mutex_init (&xvimagesink->flow_lock);
1884 xvimagesink->pool = NULL;
1886 xvimagesink->synchronous = FALSE;
1887 xvimagesink->running = FALSE;
1888 xvimagesink->keep_aspect = TRUE;
1889 xvimagesink->handle_events = TRUE;
1890 xvimagesink->par = NULL;
1891 xvimagesink->handle_expose = TRUE;
1893 xvimagesink->draw_borders = TRUE;
1897 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1899 GObjectClass *gobject_class;
1900 GstElementClass *gstelement_class;
1901 GstBaseSinkClass *gstbasesink_class;
1902 GstVideoSinkClass *videosink_class;
1904 gobject_class = (GObjectClass *) klass;
1905 gstelement_class = (GstElementClass *) klass;
1906 gstbasesink_class = (GstBaseSinkClass *) klass;
1907 videosink_class = (GstVideoSinkClass *) klass;
1909 parent_class = g_type_class_peek_parent (klass);
1911 gobject_class->set_property = gst_xvimagesink_set_property;
1912 gobject_class->get_property = gst_xvimagesink_get_property;
1914 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1915 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1916 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1917 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1918 g_param_spec_int ("brightness", "Brightness",
1919 "The brightness of the video", -1000, 1000, 0,
1920 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1921 g_object_class_install_property (gobject_class, PROP_HUE,
1922 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1923 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1924 g_object_class_install_property (gobject_class, PROP_SATURATION,
1925 g_param_spec_int ("saturation", "Saturation",
1926 "The saturation of the video", -1000, 1000, 0,
1927 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1928 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1929 g_param_spec_string ("display", "Display", "X Display name", NULL,
1930 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1931 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1932 g_param_spec_boolean ("synchronous", "Synchronous",
1933 "When enabled, runs the X display in synchronous mode. "
1934 "(unrelated to A/V sync, used only for debugging)", FALSE,
1935 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1936 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1937 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1938 "The pixel aspect ratio of the device", "1/1",
1939 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1940 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1941 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1942 "When enabled, scaling will respect original aspect ratio", TRUE,
1943 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1944 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1945 g_param_spec_boolean ("handle-events", "Handle XEvents",
1946 "When enabled, XEvents will be selected and handled", TRUE,
1947 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1948 g_object_class_install_property (gobject_class, PROP_DEVICE,
1949 g_param_spec_string ("device", "Adaptor number",
1950 "The number of the video adaptor", "0",
1951 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1952 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1953 g_param_spec_string ("device-name", "Adaptor name",
1954 "The name of the video adaptor", NULL,
1955 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1957 * GstXvImageSink:handle-expose
1959 * When enabled, the current frame will always be drawn in response to X
1962 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1963 g_param_spec_boolean ("handle-expose", "Handle expose",
1965 "the current frame will always be drawn in response to X Expose "
1966 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1969 * GstXvImageSink:double-buffer
1971 * Whether to double-buffer the output.
1973 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1974 g_param_spec_boolean ("double-buffer", "Double-buffer",
1975 "Whether to double-buffer the output", TRUE,
1976 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1978 * GstXvImageSink:autopaint-colorkey
1980 * Whether to autofill overlay with colorkey
1982 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1983 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1984 "Whether to autofill overlay with colorkey", TRUE,
1985 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1987 * GstXvImageSink:colorkey
1989 * Color to use for the overlay mask.
1991 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1992 g_param_spec_int ("colorkey", "Colorkey",
1993 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1994 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1997 * GstXvImageSink:draw-borders
1999 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2000 * unused parts of the video area.
2002 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2003 g_param_spec_boolean ("draw-borders", "Draw Borders",
2004 "Draw black borders to fill unused area in force-aspect-ratio mode",
2005 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2008 * GstXvImageSink:window-width
2010 * Actual width of the video window.
2012 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2013 g_param_spec_uint64 ("window-width", "window-width",
2014 "Width of the window", 0, G_MAXUINT64, 0,
2015 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2018 * GstXvImageSink:window-height
2020 * Actual height of the video window.
2022 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2023 g_param_spec_uint64 ("window-height", "window-height",
2024 "Height of the window", 0, G_MAXUINT64, 0,
2025 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2027 gobject_class->finalize = gst_xvimagesink_finalize;
2029 gst_element_class_set_static_metadata (gstelement_class,
2030 "Video sink", "Sink/Video",
2031 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2033 gst_element_class_add_pad_template (gstelement_class,
2034 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2036 gstelement_class->change_state =
2037 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2039 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2040 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2041 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2042 gstbasesink_class->propose_allocation =
2043 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2044 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2046 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);