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);
696 gst_xvcontext_set_colorimetry (context, &info.colorimetry);
700 /* get aspect ratio from caps if it's present, and
701 * convert video width and height to a display width and height
702 * using wd / hd = wv / hv * PARv / PARd */
704 /* get video's PAR */
705 video_par_n = info.par_n;
706 video_par_d = info.par_d;
708 /* get display's PAR */
709 if (xvimagesink->par) {
710 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
711 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
717 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
718 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
721 GST_DEBUG_OBJECT (xvimagesink,
722 "video width/height: %dx%d, calculated display ratio: %d/%d",
723 info.width, info.height, num, den);
725 /* now find a width x height that respects this display ratio.
726 * prefer those that have one of w/h the same as the incoming video
727 * using wd / hd = num / den */
729 /* start with same height, because of interlaced video */
730 /* check hd / den is an integer scale factor, and scale wd with the PAR */
731 if (info.height % den == 0) {
732 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
733 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
734 gst_util_uint64_scale_int (info.height, num, den);
735 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
736 } else if (info.width % num == 0) {
737 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
738 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
739 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
740 gst_util_uint64_scale_int (info.width, den, num);
742 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
743 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
744 gst_util_uint64_scale_int (info.height, num, den);
745 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
747 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
748 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
750 /* Notify application to set xwindow id now */
751 g_mutex_lock (&xvimagesink->flow_lock);
752 if (!xvimagesink->xwindow) {
753 g_mutex_unlock (&xvimagesink->flow_lock);
754 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
756 g_mutex_unlock (&xvimagesink->flow_lock);
759 /* Creating our window and our image with the display size in pixels */
760 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
761 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
762 goto no_display_size;
764 g_mutex_lock (&xvimagesink->flow_lock);
765 if (!xvimagesink->xwindow) {
766 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
767 GST_VIDEO_SINK_WIDTH (xvimagesink),
768 GST_VIDEO_SINK_HEIGHT (xvimagesink));
771 xvimagesink->info = info;
773 /* After a resize, we want to redraw the borders in case the new frame size
774 * doesn't cover the same area */
775 xvimagesink->redraw_border = TRUE;
777 /* create a new pool for the new configuration */
778 newpool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
780 structure = gst_buffer_pool_get_config (newpool);
781 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
782 if (!gst_buffer_pool_set_config (newpool, structure))
785 oldpool = xvimagesink->pool;
786 /* we don't activate the pool yet, this will be done by downstream after it
787 * has configured the pool. If downstream does not want our pool we will
788 * activate it when we render into it */
789 xvimagesink->pool = newpool;
790 g_mutex_unlock (&xvimagesink->flow_lock);
792 /* unref the old sink */
794 /* we don't deactivate, some elements might still be using it, it will
795 * be deactivated when the last ref is gone */
796 gst_object_unref (oldpool);
804 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
809 GST_DEBUG_OBJECT (xvimagesink,
810 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
815 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
816 ("Error calculating the output display ratio of the video."));
821 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
822 ("Error calculating the output display ratio of the video."));
827 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
828 g_mutex_unlock (&xvimagesink->flow_lock);
833 static GstStateChangeReturn
834 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
836 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
837 GstXvImageSink *xvimagesink;
839 xvimagesink = GST_XVIMAGESINK (element);
841 switch (transition) {
842 case GST_STATE_CHANGE_NULL_TO_READY:
843 if (!gst_xvimagesink_open (xvimagesink))
846 case GST_STATE_CHANGE_READY_TO_PAUSED:
848 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
850 case GST_STATE_CHANGE_PAUSED_TO_READY:
856 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
858 switch (transition) {
859 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
861 case GST_STATE_CHANGE_PAUSED_TO_READY:
862 xvimagesink->fps_n = 0;
863 xvimagesink->fps_d = 1;
864 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
865 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
866 g_mutex_lock (&xvimagesink->flow_lock);
867 if (xvimagesink->pool)
868 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
869 g_mutex_unlock (&xvimagesink->flow_lock);
871 case GST_STATE_CHANGE_READY_TO_NULL:
872 gst_xvimagesink_close (xvimagesink);
881 return GST_STATE_CHANGE_FAILURE;
886 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
887 GstClockTime * start, GstClockTime * end)
889 GstXvImageSink *xvimagesink;
891 xvimagesink = GST_XVIMAGESINK (bsink);
893 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
894 *start = GST_BUFFER_TIMESTAMP (buf);
895 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
896 *end = *start + GST_BUFFER_DURATION (buf);
898 if (xvimagesink->fps_n > 0) {
900 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
908 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
911 GstXvImageSink *xvimagesink;
912 GstBuffer *to_put = NULL;
915 xvimagesink = GST_XVIMAGESINK (vsink);
917 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
918 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
919 /* If this buffer has been allocated using our buffer management we simply
920 put the ximage which is in the PRIVATE pointer */
921 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
926 GstVideoFrame src, dest;
927 GstBufferPoolAcquireParams params = { 0, };
929 /* Else we have to copy the data into our private image, */
930 /* if we have one... */
931 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
933 /* we should have a pool, configured in setcaps */
934 if (xvimagesink->pool == NULL)
937 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
938 goto activate_failed;
940 /* take a buffer from our pool, if there is no buffer in the pool something
941 * is seriously wrong, waiting for the pool here might deadlock when we try
942 * to go to PAUSED because we never flush the pool then. */
943 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
944 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
945 if (res != GST_FLOW_OK)
948 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
949 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
951 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
954 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
955 gst_video_frame_unmap (&src);
959 gst_video_frame_copy (&dest, &src);
961 gst_video_frame_unmap (&dest);
962 gst_video_frame_unmap (&src);
965 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
970 gst_buffer_unref (to_put);
977 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
978 ("Internal error: can't allocate images"),
979 ("We don't have a bufferpool negotiated"));
980 return GST_FLOW_ERROR;
984 /* No image available. That's very bad ! */
985 GST_WARNING_OBJECT (xvimagesink, "could not create image");
990 /* No Window available to put our image into */
991 GST_WARNING_OBJECT (xvimagesink, "could not map image");
997 /* No Window available to put our image into */
998 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
999 res = GST_FLOW_ERROR;
1004 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1005 res = GST_FLOW_ERROR;
1011 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1013 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1015 switch (GST_EVENT_TYPE (event)) {
1016 case GST_EVENT_TAG:{
1018 gchar *title = NULL;
1020 gst_event_parse_tag (event, &l);
1021 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1024 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1025 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1035 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1039 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1041 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1042 GstBufferPool *pool;
1043 GstStructure *config;
1048 gst_query_parse_allocation (query, &caps, &need_pool);
1053 g_mutex_lock (&xvimagesink->flow_lock);
1054 if ((pool = xvimagesink->pool))
1055 gst_object_ref (pool);
1056 g_mutex_unlock (&xvimagesink->flow_lock);
1061 /* we had a pool, check caps */
1062 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1063 config = gst_buffer_pool_get_config (pool);
1064 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1066 if (!gst_caps_is_equal (caps, pcaps)) {
1067 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1068 /* different caps, we can't use this pool */
1069 gst_object_unref (pool);
1072 gst_structure_free (config);
1074 if (pool == NULL && need_pool) {
1077 if (!gst_video_info_from_caps (&info, caps))
1080 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1081 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
1083 /* the normal size of a frame */
1086 config = gst_buffer_pool_get_config (pool);
1087 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1088 if (!gst_buffer_pool_set_config (pool, config))
1092 /* we need at least 2 buffer because we hold on to the last one */
1093 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1094 gst_object_unref (pool);
1097 /* we also support various metadata */
1098 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1099 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1106 GST_DEBUG_OBJECT (bsink, "no caps specified");
1111 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1116 GST_DEBUG_OBJECT (bsink, "failed setting config");
1117 gst_object_unref (pool);
1122 /* Interfaces stuff */
1124 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1125 GstStructure * structure)
1127 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1130 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1132 GstVideoRectangle src, dst, result;
1133 gdouble x, y, xscale = 1.0, yscale = 1.0;
1134 GstXWindow *xwindow;
1136 event = gst_event_new_navigation (structure);
1138 /* We take the flow_lock while we look at the window */
1139 g_mutex_lock (&xvimagesink->flow_lock);
1141 if (!(xwindow = xvimagesink->xwindow)) {
1142 g_mutex_unlock (&xvimagesink->flow_lock);
1146 if (xvimagesink->keep_aspect) {
1147 /* We get the frame position using the calculated geometry from _setcaps
1148 that respect pixel aspect ratios */
1149 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1150 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1151 dst.w = xwindow->render_rect.w;
1152 dst.h = xwindow->render_rect.h;
1154 gst_video_sink_center_rect (src, dst, &result, TRUE);
1155 result.x += xwindow->render_rect.x;
1156 result.y += xwindow->render_rect.y;
1158 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1161 g_mutex_unlock (&xvimagesink->flow_lock);
1163 /* We calculate scaling using the original video frames geometry to include
1164 pixel aspect ratio scaling. */
1165 xscale = (gdouble) xvimagesink->video_width / result.w;
1166 yscale = (gdouble) xvimagesink->video_height / result.h;
1168 /* Converting pointer coordinates to the non scaled geometry */
1169 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1170 x = MIN (x, result.x + result.w);
1171 x = MAX (x - result.x, 0);
1172 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1173 (gdouble) x * xscale, NULL);
1175 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1176 y = MIN (y, result.y + result.h);
1177 y = MAX (y - result.y, 0);
1178 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1179 (gdouble) y * yscale, NULL);
1182 gst_pad_send_event (peer, event);
1183 gst_object_unref (peer);
1188 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1190 iface->send_event = gst_xvimagesink_navigation_send_event;
1194 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1196 XID xwindow_id = id;
1197 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1198 GstXWindow *xwindow = NULL;
1199 GstXvContext *context;
1201 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1203 g_mutex_lock (&xvimagesink->flow_lock);
1205 /* If we already use that window return */
1206 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1207 g_mutex_unlock (&xvimagesink->flow_lock);
1211 /* If the element has not initialized the X11 context try to do so */
1212 if (!xvimagesink->context &&
1213 !(xvimagesink->context =
1214 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1215 g_mutex_unlock (&xvimagesink->flow_lock);
1216 /* we have thrown a GST_ELEMENT_ERROR now */
1220 context = xvimagesink->context;
1222 gst_xvimagesink_update_colorbalance (xvimagesink);
1224 /* If a window is there already we destroy it */
1225 if (xvimagesink->xwindow) {
1226 gst_xwindow_destroy (xvimagesink->xwindow);
1227 xvimagesink->xwindow = NULL;
1230 /* If the xid is 0 we go back to an internal window */
1231 if (xwindow_id == 0) {
1232 /* If no width/height caps nego did not happen window will be created
1233 during caps nego then */
1234 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1235 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1237 gst_xvimagesink_xwindow_new (xvimagesink,
1238 GST_VIDEO_SINK_WIDTH (xvimagesink),
1239 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1242 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1243 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1247 xvimagesink->xwindow = xwindow;
1249 g_mutex_unlock (&xvimagesink->flow_lock);
1253 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1255 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1257 GST_DEBUG ("doing expose");
1258 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1259 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1263 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1264 gboolean handle_events)
1266 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1268 g_mutex_lock (&xvimagesink->flow_lock);
1269 xvimagesink->handle_events = handle_events;
1270 if (G_LIKELY (xvimagesink->xwindow))
1271 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1272 g_mutex_unlock (&xvimagesink->flow_lock);
1276 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1277 gint width, gint height)
1279 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1281 g_mutex_lock (&xvimagesink->flow_lock);
1282 if (G_LIKELY (xvimagesink->xwindow))
1283 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1285 g_mutex_unlock (&xvimagesink->flow_lock);
1289 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1291 iface->set_window_handle = gst_xvimagesink_set_window_handle;
1292 iface->expose = gst_xvimagesink_expose;
1293 iface->handle_events = gst_xvimagesink_set_event_handling;
1294 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1297 static const GList *
1298 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1300 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1302 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1304 if (xvimagesink->context)
1305 return xvimagesink->context->channels_list;
1311 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1312 GstColorBalanceChannel * channel, gint value)
1314 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1316 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1317 g_return_if_fail (channel->label != NULL);
1319 xvimagesink->config.cb_changed = TRUE;
1321 /* Normalize val to [-1000, 1000] */
1322 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1323 (double) (channel->max_value - channel->min_value));
1325 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1326 xvimagesink->config.hue = value;
1327 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1328 xvimagesink->config.saturation = value;
1329 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1330 xvimagesink->config.contrast = value;
1331 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1332 xvimagesink->config.brightness = value;
1334 g_warning ("got an unknown channel %s", channel->label);
1338 gst_xvimagesink_update_colorbalance (xvimagesink);
1342 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1343 GstColorBalanceChannel * channel)
1345 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1348 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1349 g_return_val_if_fail (channel->label != NULL, 0);
1351 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1352 value = xvimagesink->config.hue;
1353 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1354 value = xvimagesink->config.saturation;
1355 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1356 value = xvimagesink->config.contrast;
1357 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1358 value = xvimagesink->config.brightness;
1360 g_warning ("got an unknown channel %s", channel->label);
1363 /* Normalize val to [channel->min_value, channel->max_value] */
1364 value = channel->min_value + (channel->max_value - channel->min_value) *
1365 (value + 1000) / 2000;
1370 static GstColorBalanceType
1371 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1373 return GST_COLOR_BALANCE_HARDWARE;
1377 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1379 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1380 iface->set_value = gst_xvimagesink_colorbalance_set_value;
1381 iface->get_value = gst_xvimagesink_colorbalance_get_value;
1382 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1386 static const GList *
1387 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1389 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1390 static GList *list = NULL;
1393 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1395 g_list_append (list, g_object_class_find_property (klass,
1396 "autopaint-colorkey"));
1398 g_list_append (list, g_object_class_find_property (klass,
1401 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1408 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1409 guint prop_id, const GParamSpec * pspec)
1411 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1415 case PROP_AUTOPAINT_COLORKEY:
1416 case PROP_DOUBLE_BUFFER:
1418 GST_DEBUG_OBJECT (xvimagesink,
1419 "probing device list and get capabilities");
1420 if (!xvimagesink->context) {
1421 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1422 xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1426 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1432 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1433 guint prop_id, const GParamSpec * pspec)
1435 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1436 gboolean ret = FALSE;
1440 case PROP_AUTOPAINT_COLORKEY:
1441 case PROP_DOUBLE_BUFFER:
1443 if (xvimagesink->context != NULL) {
1450 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1457 static GValueArray *
1458 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1459 guint prop_id, const GParamSpec * pspec)
1461 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1462 GValueArray *array = NULL;
1464 if (G_UNLIKELY (!xvimagesink->context)) {
1465 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1474 GValue value = { 0 };
1476 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1477 g_value_init (&value, G_TYPE_STRING);
1479 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1480 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1482 g_value_set_string (&value, adaptor_id_s);
1483 g_value_array_append (array, &value);
1484 g_free (adaptor_id_s);
1486 g_value_unset (&value);
1489 case PROP_AUTOPAINT_COLORKEY:
1490 if (xvimagesink->have_autopaint_colorkey) {
1491 GValue value = { 0 };
1493 array = g_value_array_new (2);
1494 g_value_init (&value, G_TYPE_BOOLEAN);
1495 g_value_set_boolean (&value, FALSE);
1496 g_value_array_append (array, &value);
1497 g_value_set_boolean (&value, TRUE);
1498 g_value_array_append (array, &value);
1499 g_value_unset (&value);
1502 case PROP_DOUBLE_BUFFER:
1503 if (xvimagesink->have_double_buffer) {
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);
1516 if (xvimagesink->have_colorkey) {
1517 GValue value = { 0 };
1519 array = g_value_array_new (1);
1520 g_value_init (&value, GST_TYPE_INT_RANGE);
1521 gst_value_set_int_range (&value, 0, 0xffffff);
1522 g_value_array_append (array, &value);
1523 g_value_unset (&value);
1527 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1536 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1539 iface->get_properties = gst_xvimagesink_probe_get_properties;
1540 iface->probe_property = gst_xvimagesink_probe_probe_property;
1541 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1542 iface->get_values = gst_xvimagesink_probe_get_values;
1546 /* =========================================== */
1548 /* Init & Class init */
1550 /* =========================================== */
1553 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1554 const GValue * value, GParamSpec * pspec)
1556 GstXvImageSink *xvimagesink;
1558 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1560 xvimagesink = GST_XVIMAGESINK (object);
1564 xvimagesink->config.hue = g_value_get_int (value);
1565 xvimagesink->config.cb_changed = TRUE;
1566 gst_xvimagesink_update_colorbalance (xvimagesink);
1569 xvimagesink->config.contrast = g_value_get_int (value);
1570 xvimagesink->config.cb_changed = TRUE;
1571 gst_xvimagesink_update_colorbalance (xvimagesink);
1573 case PROP_BRIGHTNESS:
1574 xvimagesink->config.brightness = g_value_get_int (value);
1575 xvimagesink->config.cb_changed = TRUE;
1576 gst_xvimagesink_update_colorbalance (xvimagesink);
1578 case PROP_SATURATION:
1579 xvimagesink->config.saturation = g_value_get_int (value);
1580 xvimagesink->config.cb_changed = TRUE;
1581 gst_xvimagesink_update_colorbalance (xvimagesink);
1584 g_free (xvimagesink->config.display_name);
1585 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1587 case PROP_SYNCHRONOUS:
1588 xvimagesink->synchronous = g_value_get_boolean (value);
1589 if (xvimagesink->context) {
1590 gst_xvcontext_set_synchronous (xvimagesink->context,
1591 xvimagesink->synchronous);
1594 case PROP_PIXEL_ASPECT_RATIO:
1595 g_free (xvimagesink->par);
1596 xvimagesink->par = g_new0 (GValue, 1);
1597 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1598 if (!g_value_transform (value, xvimagesink->par)) {
1599 g_warning ("Could not transform string to aspect ratio");
1600 gst_value_set_fraction (xvimagesink->par, 1, 1);
1602 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1603 gst_value_get_fraction_numerator (xvimagesink->par),
1604 gst_value_get_fraction_denominator (xvimagesink->par));
1606 case PROP_FORCE_ASPECT_RATIO:
1607 xvimagesink->keep_aspect = g_value_get_boolean (value);
1609 case PROP_HANDLE_EVENTS:
1610 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1611 g_value_get_boolean (value));
1612 gst_xvimagesink_manage_event_thread (xvimagesink);
1615 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1617 case PROP_HANDLE_EXPOSE:
1618 xvimagesink->handle_expose = g_value_get_boolean (value);
1619 gst_xvimagesink_manage_event_thread (xvimagesink);
1621 case PROP_DOUBLE_BUFFER:
1622 xvimagesink->double_buffer = g_value_get_boolean (value);
1624 case PROP_AUTOPAINT_COLORKEY:
1625 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1628 xvimagesink->config.colorkey = g_value_get_int (value);
1630 case PROP_DRAW_BORDERS:
1631 xvimagesink->draw_borders = g_value_get_boolean (value);
1634 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1640 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1641 GValue * value, GParamSpec * pspec)
1643 GstXvImageSink *xvimagesink;
1645 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1647 xvimagesink = GST_XVIMAGESINK (object);
1651 g_value_set_int (value, xvimagesink->config.hue);
1654 g_value_set_int (value, xvimagesink->config.contrast);
1656 case PROP_BRIGHTNESS:
1657 g_value_set_int (value, xvimagesink->config.brightness);
1659 case PROP_SATURATION:
1660 g_value_set_int (value, xvimagesink->config.saturation);
1663 g_value_set_string (value, xvimagesink->config.display_name);
1665 case PROP_SYNCHRONOUS:
1666 g_value_set_boolean (value, xvimagesink->synchronous);
1668 case PROP_PIXEL_ASPECT_RATIO:
1669 if (xvimagesink->par)
1670 g_value_transform (xvimagesink->par, value);
1672 case PROP_FORCE_ASPECT_RATIO:
1673 g_value_set_boolean (value, xvimagesink->keep_aspect);
1675 case PROP_HANDLE_EVENTS:
1676 g_value_set_boolean (value, xvimagesink->handle_events);
1680 char *adaptor_nr_s =
1681 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1683 g_value_set_string (value, adaptor_nr_s);
1684 g_free (adaptor_nr_s);
1687 case PROP_DEVICE_NAME:
1688 if (xvimagesink->context && xvimagesink->context->adaptors) {
1689 g_value_set_string (value,
1690 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1692 g_value_set_string (value, NULL);
1695 case PROP_HANDLE_EXPOSE:
1696 g_value_set_boolean (value, xvimagesink->handle_expose);
1698 case PROP_DOUBLE_BUFFER:
1699 g_value_set_boolean (value, xvimagesink->double_buffer);
1701 case PROP_AUTOPAINT_COLORKEY:
1702 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1705 g_value_set_int (value, xvimagesink->config.colorkey);
1707 case PROP_DRAW_BORDERS:
1708 g_value_set_boolean (value, xvimagesink->draw_borders);
1710 case PROP_WINDOW_WIDTH:
1711 if (xvimagesink->xwindow)
1712 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1714 g_value_set_uint64 (value, 0);
1716 case PROP_WINDOW_HEIGHT:
1717 if (xvimagesink->xwindow)
1718 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1720 g_value_set_uint64 (value, 0);
1723 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1729 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1731 GError *error = NULL;
1733 /* Initializing the XvContext unless already done through GstVideoOverlay */
1734 if (!xvimagesink->context) {
1735 GstXvContext *context;
1736 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1739 GST_OBJECT_LOCK (xvimagesink);
1740 xvimagesink->context = context;
1742 GST_OBJECT_LOCK (xvimagesink);
1743 /* make an allocator for this context */
1744 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1745 GST_OBJECT_UNLOCK (xvimagesink);
1747 /* update object's par with calculated one if not set yet */
1748 if (!xvimagesink->par) {
1749 xvimagesink->par = g_new0 (GValue, 1);
1750 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1751 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1753 /* call XSynchronize with the current value of synchronous */
1754 gst_xvcontext_set_synchronous (xvimagesink->context,
1755 xvimagesink->synchronous);
1756 gst_xvimagesink_update_colorbalance (xvimagesink);
1757 gst_xvimagesink_manage_event_thread (xvimagesink);
1763 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1764 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1765 error->message, __FILE__, GST_FUNCTION, __LINE__);
1771 gst_xvimagesink_close (GstXvImageSink * xvimagesink)
1774 GstXvContext *context;
1776 GST_OBJECT_LOCK (xvimagesink);
1777 xvimagesink->running = FALSE;
1778 /* grab thread and mark it as NULL */
1779 thread = xvimagesink->event_thread;
1780 xvimagesink->event_thread = NULL;
1781 GST_OBJECT_UNLOCK (xvimagesink);
1783 /* Wait for our event thread to finish before we clean up our stuff. */
1785 g_thread_join (thread);
1787 if (xvimagesink->cur_image) {
1788 gst_buffer_unref (xvimagesink->cur_image);
1789 xvimagesink->cur_image = NULL;
1792 g_mutex_lock (&xvimagesink->flow_lock);
1794 if (xvimagesink->pool) {
1795 gst_object_unref (xvimagesink->pool);
1796 xvimagesink->pool = NULL;
1799 if (xvimagesink->xwindow) {
1800 gst_xwindow_clear (xvimagesink->xwindow);
1801 gst_xwindow_destroy (xvimagesink->xwindow);
1802 xvimagesink->xwindow = NULL;
1804 g_mutex_unlock (&xvimagesink->flow_lock);
1806 if (xvimagesink->allocator) {
1807 gst_object_unref (xvimagesink->allocator);
1808 xvimagesink->allocator = NULL;
1811 GST_OBJECT_LOCK (xvimagesink);
1812 /* grab context and mark it as NULL */
1813 context = xvimagesink->context;
1814 xvimagesink->context = NULL;
1815 GST_OBJECT_UNLOCK (xvimagesink);
1818 gst_xvcontext_unref (context);
1821 /* Finalize is called only once, dispose can be called multiple times.
1822 * We use mutexes and don't reset stuff to NULL here so let's register
1825 gst_xvimagesink_finalize (GObject * object)
1827 GstXvImageSink *xvimagesink;
1829 xvimagesink = GST_XVIMAGESINK (object);
1831 gst_xvimagesink_close (xvimagesink);
1833 gst_xvcontext_config_clear (&xvimagesink->config);
1835 if (xvimagesink->par) {
1836 g_free (xvimagesink->par);
1837 xvimagesink->par = NULL;
1839 g_mutex_clear (&xvimagesink->flow_lock);
1840 g_free (xvimagesink->media_title);
1842 G_OBJECT_CLASS (parent_class)->finalize (object);
1846 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1848 xvimagesink->config.display_name = NULL;
1849 xvimagesink->config.adaptor_nr = 0;
1850 xvimagesink->config.autopaint_colorkey = TRUE;
1851 xvimagesink->config.double_buffer = TRUE;
1852 /* on 16bit displays this becomes r,g,b = 1,2,3
1853 * on 24bit displays this becomes r,g,b = 8,8,16
1854 * as a port atom value */
1855 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1856 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1857 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1858 xvimagesink->config.cb_changed = FALSE;
1860 xvimagesink->context = NULL;
1861 xvimagesink->xwindow = NULL;
1862 xvimagesink->cur_image = NULL;
1864 xvimagesink->fps_n = 0;
1865 xvimagesink->fps_d = 0;
1866 xvimagesink->video_width = 0;
1867 xvimagesink->video_height = 0;
1869 g_mutex_init (&xvimagesink->flow_lock);
1871 xvimagesink->pool = NULL;
1873 xvimagesink->synchronous = FALSE;
1874 xvimagesink->running = FALSE;
1875 xvimagesink->keep_aspect = TRUE;
1876 xvimagesink->handle_events = TRUE;
1877 xvimagesink->par = NULL;
1878 xvimagesink->handle_expose = TRUE;
1880 xvimagesink->draw_borders = TRUE;
1884 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1886 GObjectClass *gobject_class;
1887 GstElementClass *gstelement_class;
1888 GstBaseSinkClass *gstbasesink_class;
1889 GstVideoSinkClass *videosink_class;
1891 gobject_class = (GObjectClass *) klass;
1892 gstelement_class = (GstElementClass *) klass;
1893 gstbasesink_class = (GstBaseSinkClass *) klass;
1894 videosink_class = (GstVideoSinkClass *) klass;
1896 parent_class = g_type_class_peek_parent (klass);
1898 gobject_class->set_property = gst_xvimagesink_set_property;
1899 gobject_class->get_property = gst_xvimagesink_get_property;
1901 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1902 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1903 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1904 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1905 g_param_spec_int ("brightness", "Brightness",
1906 "The brightness of the video", -1000, 1000, 0,
1907 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1908 g_object_class_install_property (gobject_class, PROP_HUE,
1909 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1910 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1911 g_object_class_install_property (gobject_class, PROP_SATURATION,
1912 g_param_spec_int ("saturation", "Saturation",
1913 "The saturation of the video", -1000, 1000, 0,
1914 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1915 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1916 g_param_spec_string ("display", "Display", "X Display name", NULL,
1917 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1918 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1919 g_param_spec_boolean ("synchronous", "Synchronous",
1920 "When enabled, runs the X display in synchronous mode. "
1921 "(unrelated to A/V sync, used only for debugging)", FALSE,
1922 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1923 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1924 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1925 "The pixel aspect ratio of the device", "1/1",
1926 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1927 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1928 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1929 "When enabled, scaling will respect original aspect ratio", TRUE,
1930 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1931 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1932 g_param_spec_boolean ("handle-events", "Handle XEvents",
1933 "When enabled, XEvents will be selected and handled", TRUE,
1934 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1935 g_object_class_install_property (gobject_class, PROP_DEVICE,
1936 g_param_spec_string ("device", "Adaptor number",
1937 "The number of the video adaptor", "0",
1938 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1939 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1940 g_param_spec_string ("device-name", "Adaptor name",
1941 "The name of the video adaptor", NULL,
1942 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1944 * GstXvImageSink:handle-expose
1946 * When enabled, the current frame will always be drawn in response to X
1949 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1950 g_param_spec_boolean ("handle-expose", "Handle expose",
1952 "the current frame will always be drawn in response to X Expose "
1953 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1956 * GstXvImageSink:double-buffer
1958 * Whether to double-buffer the output.
1960 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1961 g_param_spec_boolean ("double-buffer", "Double-buffer",
1962 "Whether to double-buffer the output", TRUE,
1963 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1965 * GstXvImageSink:autopaint-colorkey
1967 * Whether to autofill overlay with colorkey
1969 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1970 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1971 "Whether to autofill overlay with colorkey", TRUE,
1972 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1974 * GstXvImageSink:colorkey
1976 * Color to use for the overlay mask.
1978 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1979 g_param_spec_int ("colorkey", "Colorkey",
1980 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1981 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1984 * GstXvImageSink:draw-borders
1986 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1987 * unused parts of the video area.
1989 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
1990 g_param_spec_boolean ("draw-borders", "Colorkey",
1991 "Draw black borders to fill unused area in force-aspect-ratio mode",
1992 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1995 * GstXvImageSink:window-width
1997 * Actual width of the video window.
1999 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2000 g_param_spec_uint64 ("window-width", "window-width",
2001 "Width of the window", 0, G_MAXUINT64, 0,
2002 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2005 * GstXvImageSink:window-height
2007 * Actual height of the video window.
2009 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2010 g_param_spec_uint64 ("window-height", "window-height",
2011 "Height of the window", 0, G_MAXUINT64, 0,
2012 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2014 gobject_class->finalize = gst_xvimagesink_finalize;
2016 gst_element_class_set_static_metadata (gstelement_class,
2017 "Video sink", "Sink/Video",
2018 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2020 gst_element_class_add_pad_template (gstelement_class,
2021 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2023 gstelement_class->change_state =
2024 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2026 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2027 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2028 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2029 gstbasesink_class->propose_allocation =
2030 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2031 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2033 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);