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, dst;
233 GstVideoRectangle mem_crop;
236 /* We take the flow_lock. If expose is in there we don't want to run
237 concurrently from the data flow thread */
238 g_mutex_lock (&xvimagesink->flow_lock);
240 if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
241 g_mutex_unlock (&xvimagesink->flow_lock);
245 /* Draw borders when displaying the first frame. After this
246 draw borders only on expose event or after a size change. */
247 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
248 draw_border = xvimagesink->draw_borders;
249 xvimagesink->redraw_border = FALSE;
252 /* Store a reference to the last image we put, lose the previous one */
253 if (xvimage && xvimagesink->cur_image != xvimage) {
254 if (xvimagesink->cur_image) {
255 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
256 gst_buffer_unref (xvimagesink->cur_image);
258 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
259 xvimagesink->cur_image = gst_buffer_ref (xvimage);
262 /* Expose sends a NULL image, we take the latest frame */
264 if (xvimagesink->cur_image) {
266 xvimage = xvimagesink->cur_image;
268 g_mutex_unlock (&xvimagesink->flow_lock);
273 mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
274 gst_xvimage_memory_get_crop (mem, &mem_crop);
276 crop = gst_buffer_get_video_crop_meta (xvimage);
279 src.x = crop->x + mem_crop.x;
280 src.y = crop->y + mem_crop.y;
282 src.h = crop->height;
283 GST_LOG_OBJECT (xvimagesink,
284 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
289 if (xvimagesink->keep_aspect) {
292 /* We take the size of the source material as it was negotiated and
293 * corrected for DAR. This size can be different from the cropped size in
294 * which case the image will be scaled to fit the negotiated size. */
295 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
296 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
297 dst.w = xwindow->render_rect.w;
298 dst.h = xwindow->render_rect.h;
300 gst_video_sink_center_rect (s, dst, &result, TRUE);
301 result.x += xwindow->render_rect.x;
302 result.y += xwindow->render_rect.y;
304 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
307 gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
309 g_mutex_unlock (&xvimagesink->flow_lock);
315 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
316 GstXWindow * xwindow, const gchar * media_title)
319 g_free (xvimagesink->media_title);
320 xvimagesink->media_title = g_strdup (media_title);
323 /* we have a window */
324 const gchar *app_name;
325 const gchar *title = NULL;
326 gchar *title_mem = NULL;
328 /* set application name as a title */
329 app_name = g_get_application_name ();
331 if (app_name && xvimagesink->media_title) {
332 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
334 } else if (app_name) {
336 } else if (xvimagesink->media_title) {
337 title = xvimagesink->media_title;
340 gst_xwindow_set_title (xwindow, title);
345 /* This function handles a GstXWindow creation
346 * The width and height are the actual pixel size on the display */
348 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
349 gint width, gint height)
351 GstXWindow *xwindow = NULL;
352 GstXvContext *context;
354 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
356 context = xvimagesink->context;
358 xwindow = gst_xvcontext_create_xwindow (context, width, height);
360 /* set application name as a title */
361 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
363 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
365 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
372 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
374 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
376 /* Update the window geometry */
377 g_mutex_lock (&xvimagesink->flow_lock);
378 if (G_LIKELY (xvimagesink->xwindow))
379 gst_xwindow_update_geometry (xvimagesink->xwindow);
380 g_mutex_unlock (&xvimagesink->flow_lock);
383 /* This function commits our internal colorbalance settings to our grabbed Xv
384 port. If the context is not initialized yet it simply returns */
386 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
388 GstXvContext *context;
390 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
392 /* If we haven't initialized the X context we can't update anything */
393 if ((context = xvimagesink->context) == NULL)
396 gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
399 /* This function handles XEvents that might be in the queue. It generates
400 GstEvent that will be sent upstream in the pipeline to handle interactivity
401 and navigation. It will also listen for configure events on the window to
402 trigger caps renegotiation so on the fly software scaling can work. */
404 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
407 guint pointer_x = 0, pointer_y = 0;
408 gboolean pointer_moved = FALSE;
409 gboolean exposed = FALSE, configured = FALSE;
411 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
413 /* Handle Interaction, produces navigation events */
415 /* We get all pointer motion events, only the last position is
417 g_mutex_lock (&xvimagesink->flow_lock);
418 g_mutex_lock (&xvimagesink->context->lock);
419 while (XCheckWindowEvent (xvimagesink->context->disp,
420 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
421 g_mutex_unlock (&xvimagesink->context->lock);
422 g_mutex_unlock (&xvimagesink->flow_lock);
426 pointer_x = e.xmotion.x;
427 pointer_y = e.xmotion.y;
428 pointer_moved = TRUE;
433 g_mutex_lock (&xvimagesink->flow_lock);
434 g_mutex_lock (&xvimagesink->context->lock);
438 g_mutex_unlock (&xvimagesink->context->lock);
439 g_mutex_unlock (&xvimagesink->flow_lock);
441 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
442 pointer_x, pointer_y);
443 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
444 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
446 g_mutex_lock (&xvimagesink->flow_lock);
447 g_mutex_lock (&xvimagesink->context->lock);
450 /* We get all events on our window to throw them upstream */
451 while (XCheckWindowEvent (xvimagesink->context->disp,
452 xvimagesink->xwindow->win,
453 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
456 const char *key_str = NULL;
458 /* We lock only for the X function call */
459 g_mutex_unlock (&xvimagesink->context->lock);
460 g_mutex_unlock (&xvimagesink->flow_lock);
464 /* Mouse button pressed over our window. We send upstream
465 events for interactivity/navigation */
466 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
467 e.xbutton.button, e.xbutton.x, e.xbutton.y);
468 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
469 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
472 /* Mouse button released over our window. We send upstream
473 events for interactivity/navigation */
474 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
475 e.xbutton.button, e.xbutton.x, e.xbutton.y);
476 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
477 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
481 /* Key pressed/released over our window. We send upstream
482 events for interactivity/navigation */
483 g_mutex_lock (&xvimagesink->context->lock);
484 keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
485 e.xkey.keycode, 0, 0);
486 if (keysym != NoSymbol) {
487 key_str = XKeysymToString (keysym);
491 g_mutex_unlock (&xvimagesink->context->lock);
492 GST_DEBUG_OBJECT (xvimagesink,
493 "key %d pressed over window at %d,%d (%s)",
494 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
495 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
496 e.type == KeyPress ? "key-press" : "key-release", key_str);
499 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
502 g_mutex_lock (&xvimagesink->flow_lock);
503 g_mutex_lock (&xvimagesink->context->lock);
507 while (XCheckWindowEvent (xvimagesink->context->disp,
508 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
513 case ConfigureNotify:
514 g_mutex_unlock (&xvimagesink->context->lock);
515 g_mutex_unlock (&xvimagesink->flow_lock);
517 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
519 g_mutex_lock (&xvimagesink->flow_lock);
520 g_mutex_lock (&xvimagesink->context->lock);
528 if (xvimagesink->handle_expose && (exposed || configured)) {
529 g_mutex_unlock (&xvimagesink->context->lock);
530 g_mutex_unlock (&xvimagesink->flow_lock);
532 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
534 g_mutex_lock (&xvimagesink->flow_lock);
535 g_mutex_lock (&xvimagesink->context->lock);
538 /* Handle Display events */
539 while (XPending (xvimagesink->context->disp)) {
540 XNextEvent (xvimagesink->context->disp, &e);
546 wm_delete = XInternAtom (xvimagesink->context->disp,
547 "WM_DELETE_WINDOW", True);
548 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
549 /* Handle window deletion by posting an error on the bus */
550 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
551 ("Output window was closed"), (NULL));
553 g_mutex_unlock (&xvimagesink->context->lock);
554 gst_xwindow_destroy (xvimagesink->xwindow);
555 xvimagesink->xwindow = NULL;
556 g_mutex_lock (&xvimagesink->context->lock);
565 g_mutex_unlock (&xvimagesink->context->lock);
566 g_mutex_unlock (&xvimagesink->flow_lock);
570 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
572 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
574 GST_OBJECT_LOCK (xvimagesink);
575 while (xvimagesink->running) {
576 GST_OBJECT_UNLOCK (xvimagesink);
578 if (xvimagesink->xwindow) {
579 gst_xvimagesink_handle_xevents (xvimagesink);
581 /* FIXME: do we want to align this with the framerate or anything else? */
582 g_usleep (G_USEC_PER_SEC / 20);
584 GST_OBJECT_LOCK (xvimagesink);
586 GST_OBJECT_UNLOCK (xvimagesink);
592 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
594 GThread *thread = NULL;
596 /* don't start the thread too early */
597 if (xvimagesink->context == NULL) {
601 GST_OBJECT_LOCK (xvimagesink);
602 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
603 if (!xvimagesink->event_thread) {
604 /* Setup our event listening thread */
605 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
606 xvimagesink->handle_expose, xvimagesink->handle_events);
607 xvimagesink->running = TRUE;
608 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
609 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
612 if (xvimagesink->event_thread) {
613 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
614 xvimagesink->handle_expose, xvimagesink->handle_events);
615 xvimagesink->running = FALSE;
616 /* grab thread and mark it as NULL */
617 thread = xvimagesink->event_thread;
618 xvimagesink->event_thread = NULL;
621 GST_OBJECT_UNLOCK (xvimagesink);
623 /* Wait for our event thread to finish */
625 g_thread_join (thread);
632 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
634 GstXvImageSink *xvimagesink;
637 xvimagesink = GST_XVIMAGESINK (bsink);
639 if (xvimagesink->context) {
641 return gst_caps_intersect_full (filter, xvimagesink->context->caps,
642 GST_CAPS_INTERSECT_FIRST);
644 return gst_caps_ref (xvimagesink->context->caps);
647 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
649 GstCaps *intersection;
652 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
653 gst_caps_unref (caps);
660 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
662 GstXvImageSink *xvimagesink;
663 GstXvContext *context;
664 GstStructure *structure;
665 GstBufferPool *newpool, *oldpool;
667 guint32 im_format = 0;
668 gint video_par_n, video_par_d; /* video's PAR */
669 gint display_par_n, display_par_d; /* display's PAR */
673 xvimagesink = GST_XVIMAGESINK (bsink);
674 context = xvimagesink->context;
676 GST_DEBUG_OBJECT (xvimagesink,
677 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
678 GST_PTR_FORMAT, context->caps, caps);
680 if (!gst_caps_can_intersect (context->caps, caps))
681 goto incompatible_caps;
683 if (!gst_video_info_from_caps (&info, caps))
686 xvimagesink->fps_n = info.fps_n;
687 xvimagesink->fps_d = info.fps_d;
689 xvimagesink->video_width = info.width;
690 xvimagesink->video_height = info.height;
692 im_format = gst_xvcontext_get_format_from_info (context, &info);
698 /* get aspect ratio from caps if it's present, and
699 * convert video width and height to a display width and height
700 * using wd / hd = wv / hv * PARv / PARd */
702 /* get video's PAR */
703 video_par_n = info.par_n;
704 video_par_d = info.par_d;
706 /* get display's PAR */
707 if (xvimagesink->par) {
708 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
709 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
715 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
716 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
719 GST_DEBUG_OBJECT (xvimagesink,
720 "video width/height: %dx%d, calculated display ratio: %d/%d",
721 info.width, info.height, num, den);
723 /* now find a width x height that respects this display ratio.
724 * prefer those that have one of w/h the same as the incoming video
725 * using wd / hd = num / den */
727 /* start with same height, because of interlaced video */
728 /* check hd / den is an integer scale factor, and scale wd with the PAR */
729 if (info.height % den == 0) {
730 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
731 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
732 gst_util_uint64_scale_int (info.height, num, den);
733 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
734 } else if (info.width % num == 0) {
735 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
736 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
737 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
738 gst_util_uint64_scale_int (info.width, den, num);
740 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
741 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
742 gst_util_uint64_scale_int (info.height, num, den);
743 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
745 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
746 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
748 /* Notify application to set xwindow id now */
749 g_mutex_lock (&xvimagesink->flow_lock);
750 if (!xvimagesink->xwindow) {
751 g_mutex_unlock (&xvimagesink->flow_lock);
752 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
754 g_mutex_unlock (&xvimagesink->flow_lock);
757 /* Creating our window and our image with the display size in pixels */
758 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
759 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
760 goto no_display_size;
762 g_mutex_lock (&xvimagesink->flow_lock);
763 if (!xvimagesink->xwindow) {
764 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
765 GST_VIDEO_SINK_WIDTH (xvimagesink),
766 GST_VIDEO_SINK_HEIGHT (xvimagesink));
769 xvimagesink->info = info;
771 /* After a resize, we want to redraw the borders in case the new frame size
772 * doesn't cover the same area */
773 xvimagesink->redraw_border = TRUE;
775 /* create a new pool for the new configuration */
776 newpool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
778 structure = gst_buffer_pool_get_config (newpool);
779 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
780 if (!gst_buffer_pool_set_config (newpool, structure))
783 oldpool = xvimagesink->pool;
784 /* we don't activate the pool yet, this will be done by downstream after it
785 * has configured the pool. If downstream does not want our pool we will
786 * activate it when we render into it */
787 xvimagesink->pool = newpool;
788 g_mutex_unlock (&xvimagesink->flow_lock);
790 /* unref the old sink */
792 /* we don't deactivate, some elements might still be using it, it will
793 * be deactivated when the last ref is gone */
794 gst_object_unref (oldpool);
802 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
807 GST_DEBUG_OBJECT (xvimagesink,
808 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
813 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
814 ("Error calculating the output display ratio of the video."));
819 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
820 ("Error calculating the output display ratio of the video."));
825 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
826 g_mutex_unlock (&xvimagesink->flow_lock);
831 static GstStateChangeReturn
832 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
834 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
835 GstXvImageSink *xvimagesink;
837 xvimagesink = GST_XVIMAGESINK (element);
839 switch (transition) {
840 case GST_STATE_CHANGE_NULL_TO_READY:
841 if (!gst_xvimagesink_open (xvimagesink))
844 case GST_STATE_CHANGE_READY_TO_PAUSED:
846 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
848 case GST_STATE_CHANGE_PAUSED_TO_READY:
854 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
856 switch (transition) {
857 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
859 case GST_STATE_CHANGE_PAUSED_TO_READY:
860 xvimagesink->fps_n = 0;
861 xvimagesink->fps_d = 1;
862 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
863 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
864 g_mutex_lock (&xvimagesink->flow_lock);
865 if (xvimagesink->pool)
866 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
867 g_mutex_unlock (&xvimagesink->flow_lock);
869 case GST_STATE_CHANGE_READY_TO_NULL:
870 gst_xvimagesink_close (xvimagesink);
879 return GST_STATE_CHANGE_FAILURE;
884 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
885 GstClockTime * start, GstClockTime * end)
887 GstXvImageSink *xvimagesink;
889 xvimagesink = GST_XVIMAGESINK (bsink);
891 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
892 *start = GST_BUFFER_TIMESTAMP (buf);
893 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
894 *end = *start + GST_BUFFER_DURATION (buf);
896 if (xvimagesink->fps_n > 0) {
898 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
906 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
909 GstXvImageSink *xvimagesink;
913 xvimagesink = GST_XVIMAGESINK (vsink);
915 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
916 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
917 /* If this buffer has been allocated using our buffer management we simply
918 put the ximage which is in the PRIVATE pointer */
919 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
924 GstVideoFrame src, dest;
925 GstBufferPoolAcquireParams params = { 0, };
927 /* Else we have to copy the data into our private image, */
928 /* if we have one... */
929 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
931 /* we should have a pool, configured in setcaps */
932 if (xvimagesink->pool == NULL)
935 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
936 goto activate_failed;
938 /* take a buffer from our pool, if there is no buffer in the pool something
939 * is seriously wrong, waiting for the pool here might deadlock when we try
940 * to go to PAUSED because we never flush the pool then. */
941 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
942 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
943 if (res != GST_FLOW_OK)
946 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
947 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
949 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
952 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
953 gst_video_frame_unmap (&src);
957 gst_video_frame_copy (&dest, &src);
959 gst_video_frame_unmap (&dest);
960 gst_video_frame_unmap (&src);
963 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
968 gst_buffer_unref (to_put);
975 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
976 ("Internal error: can't allocate images"),
977 ("We don't have a bufferpool negotiated"));
978 return GST_FLOW_ERROR;
982 /* No image available. That's very bad ! */
983 GST_WARNING_OBJECT (xvimagesink, "could not create image");
988 /* No Window available to put our image into */
989 GST_WARNING_OBJECT (xvimagesink, "could not map image");
995 /* No Window available to put our image into */
996 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
997 res = GST_FLOW_ERROR;
1002 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1003 res = GST_FLOW_ERROR;
1009 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1011 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1013 switch (GST_EVENT_TYPE (event)) {
1014 case GST_EVENT_TAG:{
1016 gchar *title = NULL;
1018 gst_event_parse_tag (event, &l);
1019 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1022 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1023 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1033 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1037 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1039 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1040 GstBufferPool *pool;
1041 GstStructure *config;
1046 gst_query_parse_allocation (query, &caps, &need_pool);
1051 g_mutex_lock (&xvimagesink->flow_lock);
1052 if ((pool = xvimagesink->pool))
1053 gst_object_ref (pool);
1054 g_mutex_unlock (&xvimagesink->flow_lock);
1059 /* we had a pool, check caps */
1060 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1061 config = gst_buffer_pool_get_config (pool);
1062 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1064 if (!gst_caps_is_equal (caps, pcaps)) {
1065 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1066 /* different caps, we can't use this pool */
1067 gst_object_unref (pool);
1070 gst_structure_free (config);
1072 if (pool == NULL && need_pool) {
1075 if (!gst_video_info_from_caps (&info, caps))
1078 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1079 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
1081 /* the normal size of a frame */
1084 config = gst_buffer_pool_get_config (pool);
1085 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1086 if (!gst_buffer_pool_set_config (pool, config))
1090 /* we need at least 2 buffer because we hold on to the last one */
1091 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1092 gst_object_unref (pool);
1095 /* we also support various metadata */
1096 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1097 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1104 GST_DEBUG_OBJECT (bsink, "no caps specified");
1109 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1114 GST_DEBUG_OBJECT (bsink, "failed setting config");
1115 gst_object_unref (pool);
1120 /* Interfaces stuff */
1122 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1123 GstStructure * structure)
1125 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1128 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1130 GstVideoRectangle src, dst, result;
1131 gdouble x, y, xscale = 1.0, yscale = 1.0;
1132 GstXWindow *xwindow;
1134 event = gst_event_new_navigation (structure);
1136 /* We take the flow_lock while we look at the window */
1137 g_mutex_lock (&xvimagesink->flow_lock);
1139 if (!(xwindow = xvimagesink->xwindow)) {
1140 g_mutex_unlock (&xvimagesink->flow_lock);
1144 if (xvimagesink->keep_aspect) {
1145 /* We get the frame position using the calculated geometry from _setcaps
1146 that respect pixel aspect ratios */
1147 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1148 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1149 dst.w = xwindow->render_rect.w;
1150 dst.h = xwindow->render_rect.h;
1152 gst_video_sink_center_rect (src, dst, &result, TRUE);
1153 result.x += xwindow->render_rect.x;
1154 result.y += xwindow->render_rect.y;
1156 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1159 g_mutex_unlock (&xvimagesink->flow_lock);
1161 /* We calculate scaling using the original video frames geometry to include
1162 pixel aspect ratio scaling. */
1163 xscale = (gdouble) xvimagesink->video_width / result.w;
1164 yscale = (gdouble) xvimagesink->video_height / result.h;
1166 /* Converting pointer coordinates to the non scaled geometry */
1167 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1168 x = MIN (x, result.x + result.w);
1169 x = MAX (x - result.x, 0);
1170 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1171 (gdouble) x * xscale, NULL);
1173 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1174 y = MIN (y, result.y + result.h);
1175 y = MAX (y - result.y, 0);
1176 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1177 (gdouble) y * yscale, NULL);
1180 gst_pad_send_event (peer, event);
1181 gst_object_unref (peer);
1186 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1188 iface->send_event = gst_xvimagesink_navigation_send_event;
1192 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1194 XID xwindow_id = id;
1195 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1196 GstXWindow *xwindow = NULL;
1197 GstXvContext *context;
1199 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1201 g_mutex_lock (&xvimagesink->flow_lock);
1203 /* If we already use that window return */
1204 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1205 g_mutex_unlock (&xvimagesink->flow_lock);
1209 /* If the element has not initialized the X11 context try to do so */
1210 if (!xvimagesink->context &&
1211 !(xvimagesink->context =
1212 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1213 g_mutex_unlock (&xvimagesink->flow_lock);
1214 /* we have thrown a GST_ELEMENT_ERROR now */
1218 context = xvimagesink->context;
1220 gst_xvimagesink_update_colorbalance (xvimagesink);
1222 /* If a window is there already we destroy it */
1223 if (xvimagesink->xwindow) {
1224 gst_xwindow_destroy (xvimagesink->xwindow);
1225 xvimagesink->xwindow = NULL;
1228 /* If the xid is 0 we go back to an internal window */
1229 if (xwindow_id == 0) {
1230 /* If no width/height caps nego did not happen window will be created
1231 during caps nego then */
1232 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1233 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1235 gst_xvimagesink_xwindow_new (xvimagesink,
1236 GST_VIDEO_SINK_WIDTH (xvimagesink),
1237 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1240 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1241 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1245 xvimagesink->xwindow = xwindow;
1247 g_mutex_unlock (&xvimagesink->flow_lock);
1251 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1253 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1255 GST_DEBUG ("doing expose");
1256 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1257 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1261 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1262 gboolean handle_events)
1264 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1266 g_mutex_lock (&xvimagesink->flow_lock);
1267 xvimagesink->handle_events = handle_events;
1268 if (G_LIKELY (xvimagesink->xwindow))
1269 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1270 g_mutex_unlock (&xvimagesink->flow_lock);
1274 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1275 gint width, gint height)
1277 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1279 g_mutex_lock (&xvimagesink->flow_lock);
1280 if (G_LIKELY (xvimagesink->xwindow))
1281 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1283 g_mutex_unlock (&xvimagesink->flow_lock);
1287 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1289 iface->set_window_handle = gst_xvimagesink_set_window_handle;
1290 iface->expose = gst_xvimagesink_expose;
1291 iface->handle_events = gst_xvimagesink_set_event_handling;
1292 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1295 static const GList *
1296 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1298 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1300 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1302 if (xvimagesink->context)
1303 return xvimagesink->context->channels_list;
1309 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1310 GstColorBalanceChannel * channel, gint value)
1312 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1314 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1315 g_return_if_fail (channel->label != NULL);
1317 xvimagesink->config.cb_changed = TRUE;
1319 /* Normalize val to [-1000, 1000] */
1320 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1321 (double) (channel->max_value - channel->min_value));
1323 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1324 xvimagesink->config.hue = value;
1325 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1326 xvimagesink->config.saturation = value;
1327 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1328 xvimagesink->config.contrast = value;
1329 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1330 xvimagesink->config.brightness = value;
1332 g_warning ("got an unknown channel %s", channel->label);
1336 gst_xvimagesink_update_colorbalance (xvimagesink);
1340 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1341 GstColorBalanceChannel * channel)
1343 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1346 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1347 g_return_val_if_fail (channel->label != NULL, 0);
1349 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1350 value = xvimagesink->config.hue;
1351 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1352 value = xvimagesink->config.saturation;
1353 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1354 value = xvimagesink->config.contrast;
1355 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1356 value = xvimagesink->config.brightness;
1358 g_warning ("got an unknown channel %s", channel->label);
1361 /* Normalize val to [channel->min_value, channel->max_value] */
1362 value = channel->min_value + (channel->max_value - channel->min_value) *
1363 (value + 1000) / 2000;
1368 static GstColorBalanceType
1369 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1371 return GST_COLOR_BALANCE_HARDWARE;
1375 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1377 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1378 iface->set_value = gst_xvimagesink_colorbalance_set_value;
1379 iface->get_value = gst_xvimagesink_colorbalance_get_value;
1380 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1384 static const GList *
1385 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1387 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1388 static GList *list = NULL;
1391 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1393 g_list_append (list, g_object_class_find_property (klass,
1394 "autopaint-colorkey"));
1396 g_list_append (list, g_object_class_find_property (klass,
1399 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1406 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1407 guint prop_id, const GParamSpec * pspec)
1409 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1413 case PROP_AUTOPAINT_COLORKEY:
1414 case PROP_DOUBLE_BUFFER:
1416 GST_DEBUG_OBJECT (xvimagesink,
1417 "probing device list and get capabilities");
1418 if (!xvimagesink->context) {
1419 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1420 xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1424 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1430 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1431 guint prop_id, const GParamSpec * pspec)
1433 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1434 gboolean ret = FALSE;
1438 case PROP_AUTOPAINT_COLORKEY:
1439 case PROP_DOUBLE_BUFFER:
1441 if (xvimagesink->context != NULL) {
1448 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1455 static GValueArray *
1456 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1457 guint prop_id, const GParamSpec * pspec)
1459 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1460 GValueArray *array = NULL;
1462 if (G_UNLIKELY (!xvimagesink->context)) {
1463 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1472 GValue value = { 0 };
1474 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1475 g_value_init (&value, G_TYPE_STRING);
1477 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1478 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1480 g_value_set_string (&value, adaptor_id_s);
1481 g_value_array_append (array, &value);
1482 g_free (adaptor_id_s);
1484 g_value_unset (&value);
1487 case PROP_AUTOPAINT_COLORKEY:
1488 if (xvimagesink->have_autopaint_colorkey) {
1489 GValue value = { 0 };
1491 array = g_value_array_new (2);
1492 g_value_init (&value, G_TYPE_BOOLEAN);
1493 g_value_set_boolean (&value, FALSE);
1494 g_value_array_append (array, &value);
1495 g_value_set_boolean (&value, TRUE);
1496 g_value_array_append (array, &value);
1497 g_value_unset (&value);
1500 case PROP_DOUBLE_BUFFER:
1501 if (xvimagesink->have_double_buffer) {
1502 GValue value = { 0 };
1504 array = g_value_array_new (2);
1505 g_value_init (&value, G_TYPE_BOOLEAN);
1506 g_value_set_boolean (&value, FALSE);
1507 g_value_array_append (array, &value);
1508 g_value_set_boolean (&value, TRUE);
1509 g_value_array_append (array, &value);
1510 g_value_unset (&value);
1514 if (xvimagesink->have_colorkey) {
1515 GValue value = { 0 };
1517 array = g_value_array_new (1);
1518 g_value_init (&value, GST_TYPE_INT_RANGE);
1519 gst_value_set_int_range (&value, 0, 0xffffff);
1520 g_value_array_append (array, &value);
1521 g_value_unset (&value);
1525 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1534 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1537 iface->get_properties = gst_xvimagesink_probe_get_properties;
1538 iface->probe_property = gst_xvimagesink_probe_probe_property;
1539 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1540 iface->get_values = gst_xvimagesink_probe_get_values;
1544 /* =========================================== */
1546 /* Init & Class init */
1548 /* =========================================== */
1551 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1552 const GValue * value, GParamSpec * pspec)
1554 GstXvImageSink *xvimagesink;
1556 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1558 xvimagesink = GST_XVIMAGESINK (object);
1562 xvimagesink->config.hue = g_value_get_int (value);
1563 xvimagesink->config.cb_changed = TRUE;
1564 gst_xvimagesink_update_colorbalance (xvimagesink);
1567 xvimagesink->config.contrast = g_value_get_int (value);
1568 xvimagesink->config.cb_changed = TRUE;
1569 gst_xvimagesink_update_colorbalance (xvimagesink);
1571 case PROP_BRIGHTNESS:
1572 xvimagesink->config.brightness = g_value_get_int (value);
1573 xvimagesink->config.cb_changed = TRUE;
1574 gst_xvimagesink_update_colorbalance (xvimagesink);
1576 case PROP_SATURATION:
1577 xvimagesink->config.saturation = g_value_get_int (value);
1578 xvimagesink->config.cb_changed = TRUE;
1579 gst_xvimagesink_update_colorbalance (xvimagesink);
1582 g_free (xvimagesink->config.display_name);
1583 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1585 case PROP_SYNCHRONOUS:
1586 xvimagesink->synchronous = g_value_get_boolean (value);
1587 if (xvimagesink->context) {
1588 gst_xvcontext_set_synchronous (xvimagesink->context,
1589 xvimagesink->synchronous);
1592 case PROP_PIXEL_ASPECT_RATIO:
1593 g_free (xvimagesink->par);
1594 xvimagesink->par = g_new0 (GValue, 1);
1595 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1596 if (!g_value_transform (value, xvimagesink->par)) {
1597 g_warning ("Could not transform string to aspect ratio");
1598 gst_value_set_fraction (xvimagesink->par, 1, 1);
1600 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1601 gst_value_get_fraction_numerator (xvimagesink->par),
1602 gst_value_get_fraction_denominator (xvimagesink->par));
1604 case PROP_FORCE_ASPECT_RATIO:
1605 xvimagesink->keep_aspect = g_value_get_boolean (value);
1607 case PROP_HANDLE_EVENTS:
1608 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1609 g_value_get_boolean (value));
1610 gst_xvimagesink_manage_event_thread (xvimagesink);
1613 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1615 case PROP_HANDLE_EXPOSE:
1616 xvimagesink->handle_expose = g_value_get_boolean (value);
1617 gst_xvimagesink_manage_event_thread (xvimagesink);
1619 case PROP_DOUBLE_BUFFER:
1620 xvimagesink->double_buffer = g_value_get_boolean (value);
1622 case PROP_AUTOPAINT_COLORKEY:
1623 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1626 xvimagesink->config.colorkey = g_value_get_int (value);
1628 case PROP_DRAW_BORDERS:
1629 xvimagesink->draw_borders = g_value_get_boolean (value);
1632 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1638 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1639 GValue * value, GParamSpec * pspec)
1641 GstXvImageSink *xvimagesink;
1643 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1645 xvimagesink = GST_XVIMAGESINK (object);
1649 g_value_set_int (value, xvimagesink->config.hue);
1652 g_value_set_int (value, xvimagesink->config.contrast);
1654 case PROP_BRIGHTNESS:
1655 g_value_set_int (value, xvimagesink->config.brightness);
1657 case PROP_SATURATION:
1658 g_value_set_int (value, xvimagesink->config.saturation);
1661 g_value_set_string (value, xvimagesink->config.display_name);
1663 case PROP_SYNCHRONOUS:
1664 g_value_set_boolean (value, xvimagesink->synchronous);
1666 case PROP_PIXEL_ASPECT_RATIO:
1667 if (xvimagesink->par)
1668 g_value_transform (xvimagesink->par, value);
1670 case PROP_FORCE_ASPECT_RATIO:
1671 g_value_set_boolean (value, xvimagesink->keep_aspect);
1673 case PROP_HANDLE_EVENTS:
1674 g_value_set_boolean (value, xvimagesink->handle_events);
1678 char *adaptor_nr_s =
1679 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1681 g_value_set_string (value, adaptor_nr_s);
1682 g_free (adaptor_nr_s);
1685 case PROP_DEVICE_NAME:
1686 if (xvimagesink->context && xvimagesink->context->adaptors) {
1687 g_value_set_string (value,
1688 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1690 g_value_set_string (value, NULL);
1693 case PROP_HANDLE_EXPOSE:
1694 g_value_set_boolean (value, xvimagesink->handle_expose);
1696 case PROP_DOUBLE_BUFFER:
1697 g_value_set_boolean (value, xvimagesink->double_buffer);
1699 case PROP_AUTOPAINT_COLORKEY:
1700 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1703 g_value_set_int (value, xvimagesink->config.colorkey);
1705 case PROP_DRAW_BORDERS:
1706 g_value_set_boolean (value, xvimagesink->draw_borders);
1708 case PROP_WINDOW_WIDTH:
1709 if (xvimagesink->xwindow)
1710 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1712 g_value_set_uint64 (value, 0);
1714 case PROP_WINDOW_HEIGHT:
1715 if (xvimagesink->xwindow)
1716 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1718 g_value_set_uint64 (value, 0);
1721 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1727 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1729 GstXvContext *context;
1730 GError *error = NULL;
1732 /* Initializing the XvContext */
1733 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1736 GST_OBJECT_LOCK (xvimagesink);
1737 xvimagesink->context = context;
1738 /* make an allocator for this context */
1739 xvimagesink->allocator = gst_xvimage_allocator_new (context);
1740 GST_OBJECT_UNLOCK (xvimagesink);
1742 /* update object's par with calculated one if not set yet */
1743 if (!xvimagesink->par) {
1744 xvimagesink->par = g_new0 (GValue, 1);
1745 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1746 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1748 /* call XSynchronize with the current value of synchronous */
1749 gst_xvcontext_set_synchronous (xvimagesink->context,
1750 xvimagesink->synchronous);
1751 gst_xvimagesink_update_colorbalance (xvimagesink);
1752 gst_xvimagesink_manage_event_thread (xvimagesink);
1758 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1759 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1760 error->message, __FILE__, GST_FUNCTION, __LINE__);
1766 gst_xvimagesink_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_xvimagesink_finalize (GObject * object)
1822 GstXvImageSink *xvimagesink;
1824 xvimagesink = GST_XVIMAGESINK (object);
1826 gst_xvimagesink_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_xvimagesink_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_xvimagesink_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_xvimagesink_set_property;
1894 gobject_class->get_property = gst_xvimagesink_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
1946 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1947 g_param_spec_boolean ("handle-expose", "Handle expose",
1949 "the current frame will always be drawn in response to X Expose "
1950 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1953 * GstXvImageSink:double-buffer
1955 * Whether to double-buffer the output.
1959 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1960 g_param_spec_boolean ("double-buffer", "Double-buffer",
1961 "Whether to double-buffer the output", TRUE,
1962 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1964 * GstXvImageSink:autopaint-colorkey
1966 * Whether to autofill overlay with colorkey
1970 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1971 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1972 "Whether to autofill overlay with colorkey", TRUE,
1973 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1975 * GstXvImageSink:colorkey
1977 * Color to use for the overlay mask.
1981 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1982 g_param_spec_int ("colorkey", "Colorkey",
1983 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1984 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1987 * GstXvImageSink:draw-borders
1989 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1990 * unused parts of the video area.
1994 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
1995 g_param_spec_boolean ("draw-borders", "Colorkey",
1996 "Draw black borders to fill unused area in force-aspect-ratio mode",
1997 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2000 * GstXvImageSink:window-width
2002 * Actual width of the video window.
2006 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2007 g_param_spec_uint64 ("window-width", "window-width",
2008 "Width of the window", 0, G_MAXUINT64, 0,
2009 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2012 * GstXvImageSink:window-height
2014 * Actual height of the video window.
2018 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2019 g_param_spec_uint64 ("window-height", "window-height",
2020 "Height of the window", 0, G_MAXUINT64, 0,
2021 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2023 gobject_class->finalize = gst_xvimagesink_finalize;
2025 gst_element_class_set_static_metadata (gstelement_class,
2026 "Video sink", "Sink/Video",
2027 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2029 gst_element_class_add_pad_template (gstelement_class,
2030 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2032 gstelement_class->change_state =
2033 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2035 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2036 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2037 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2038 gstbasesink_class->propose_allocation =
2039 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2040 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2042 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);