2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the #GstVideoOverlay interface and will then render
29 * video frames in this drawable. If no Window ID was provided by the
30 * application, the element will create its own internal window and render
34 * <title>Scaling</title>
36 * The XVideo extension, when it's available, handles hardware accelerated
37 * scaling of video frames. This means that the element will just accept
38 * incoming video frames no matter their geometry and will then put them to the
39 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
40 * property it is possible to enforce scaling with a constant aspect ratio,
41 * which means drawing black borders around the video frame.
45 * <title>Events</title>
47 * XvImageSink creates a thread to handle events coming from the drawable. There
48 * are several kind of events that can be grouped in 2 big categories: input
49 * events and window state related events. Input events will be translated to
50 * navigation events and pushed upstream for other elements to react on them.
51 * This includes events such as pointer moves, key press/release, clicks etc...
52 * Other events are used to handle the drawable appearance even when the data
53 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
54 * paused, it will receive expose events from the drawable and draw the latest
55 * frame with correct borders/aspect-ratio.
59 * <title>Pixel aspect ratio</title>
61 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
62 * the display specified in the #GstXvImageSink:display property or the
63 * default display if nothing specified. Once this connection is open it will
64 * inspect the display configuration including the physical display geometry and
65 * then calculate the pixel aspect ratio. When receiving video frames with a
66 * different pixel aspect ratio, XvImageSink will use hardware scaling to
67 * display the video frames correctly on display's pixel aspect ratio.
68 * Sometimes the calculated pixel aspect ratio can be wrong, it is
69 * then possible to enforce a specific pixel aspect ratio using the
70 * #GstXvImageSink:pixel-aspect-ratio property.
74 * <title>Examples</title>
76 * gst-launch-1.0 -v videotestsrc ! xvimagesink
77 * ]| A pipeline to test hardware scaling.
78 * When the test video signal appears you can resize the window and see that
79 * video frames are scaled through hardware (no extra CPU cost). By default
80 * the image will never be distorted when scaled, instead black borders will
83 * gst-launch-1.0 -v videotestsrc ! xvimagesink force-aspect-ratio=false
84 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to
85 * false. You can observe that no borders are drawn around the scaled image
86 * now and it will be distorted to fill the entire frame instead of respecting
89 * gst-launch-1.0 -v videotestsrc ! navigationtest ! xvimagesink
90 * ]| A pipeline to test navigation events.
91 * While moving the mouse pointer over the test signal you will see a black box
92 * following the mouse pointer. If you press the mouse button somewhere on the
93 * video and release it somewhere else a green box will appear where you pressed
94 * the button and a red one where you released it. (The navigationtest element
95 * is part of gst-plugins-good.) You can observe here that even if the images
96 * are scaled through hardware the pointer coordinates are converted back to the
97 * original video frame geometry so that the box can be drawn to the correct
98 * position. This also handles borders correctly, limiting coordinates to the
101 * gst-launch-1.0 -v videotestsrc ! video/x-raw, pixel-aspect-ratio=4/3 ! xvimagesink
102 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
103 * videotestsrc, in most cases the pixel aspect ratio of the display will be
104 * 1/1. This means that XvImageSink will have to do the scaling to convert
105 * incoming frames to a size that will match the display pixel aspect ratio
106 * (from 320x240 to 320x180 in this case).
108 * gst-launch-1.0 -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
109 * ]| Demonstrates how to use the colorbalance interface.
113 /* for developers: there are two useful tools : xvinfo and xvattr */
120 #include <gst/video/navigation.h>
121 #include <gst/video/videooverlay.h>
122 #include <gst/video/colorbalance.h>
123 /* Helper functions */
124 #include <gst/video/gstvideometa.h>
127 #include "xvimagesink.h"
128 #include "xvimageallocator.h"
130 /* Debugging category */
131 #include <gst/gstinfo.h>
133 /* for XkbKeycodeToKeysym */
134 #include <X11/XKBlib.h>
136 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xvimagesink);
137 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_PERFORMANCE);
138 #define GST_CAT_DEFAULT gst_debug_xvimagesink
143 unsigned long functions;
144 unsigned long decorations;
146 unsigned long status;
148 MotifWmHints, MwmHints;
150 #define MWM_HINTS_DECORATIONS (1L << 1)
152 static gboolean gst_xvimagesink_open (GstXvImageSink * xvimagesink);
153 static void gst_xvimagesink_close (GstXvImageSink * xvimagesink);
154 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
156 static void gst_xvimagesink_expose (GstVideoOverlay * overlay);
158 /* Default template - initiated with class struct to allow gst-register to work
160 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
161 GST_STATIC_PAD_TEMPLATE ("sink",
164 GST_STATIC_CAPS ("video/x-raw, "
165 "framerate = (fraction) [ 0, MAX ], "
166 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
178 PROP_PIXEL_ASPECT_RATIO,
179 PROP_FORCE_ASPECT_RATIO,
185 PROP_AUTOPAINT_COLORKEY,
192 /* ============================================================= */
196 /* ============================================================= */
198 /* =========================================== */
200 /* Object typing & Creation */
202 /* =========================================== */
203 static void gst_xvimagesink_navigation_init (GstNavigationInterface * iface);
204 static void gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface *
206 static void gst_xvimagesink_colorbalance_init (GstColorBalanceInterface *
208 #define gst_xvimagesink_parent_class parent_class
209 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xvimagesink, GST_TYPE_VIDEO_SINK,
210 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
211 gst_xvimagesink_navigation_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
213 gst_xvimagesink_video_overlay_init);
214 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
215 gst_xvimagesink_colorbalance_init));
218 /* ============================================================= */
220 /* Private Methods */
222 /* ============================================================= */
225 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
226 * if no window was available */
228 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink, GstBuffer * xvimage)
230 GstXvImageMemory *mem;
231 GstVideoCropMeta *crop;
232 GstVideoRectangle result;
233 gboolean draw_border = FALSE;
234 GstVideoRectangle src = { 0, };
235 GstVideoRectangle dst = { 0, };
236 GstVideoRectangle mem_crop;
239 /* We take the flow_lock. If expose is in there we don't want to run
240 concurrently from the data flow thread */
241 g_mutex_lock (&xvimagesink->flow_lock);
243 if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
244 g_mutex_unlock (&xvimagesink->flow_lock);
248 /* Draw borders when displaying the first frame. After this
249 draw borders only on expose event or after a size change. */
250 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
251 draw_border = xvimagesink->draw_borders;
252 xvimagesink->redraw_border = FALSE;
255 /* Store a reference to the last image we put, lose the previous one */
256 if (xvimage && xvimagesink->cur_image != xvimage) {
257 if (xvimagesink->cur_image) {
258 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
259 gst_buffer_unref (xvimagesink->cur_image);
261 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
262 xvimagesink->cur_image = gst_buffer_ref (xvimage);
265 /* Expose sends a NULL image, we take the latest frame */
267 if (xvimagesink->cur_image) {
269 xvimage = xvimagesink->cur_image;
271 g_mutex_unlock (&xvimagesink->flow_lock);
276 mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
277 gst_xvimage_memory_get_crop (mem, &mem_crop);
279 crop = gst_buffer_get_video_crop_meta (xvimage);
282 src.x = crop->x + mem_crop.x;
283 src.y = crop->y + mem_crop.y;
285 src.h = crop->height;
286 GST_LOG_OBJECT (xvimagesink,
287 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
292 if (xvimagesink->keep_aspect) {
295 /* We take the size of the source material as it was negotiated and
296 * corrected for DAR. This size can be different from the cropped size in
297 * which case the image will be scaled to fit the negotiated size. */
298 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
299 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
300 dst.w = xwindow->render_rect.w;
301 dst.h = xwindow->render_rect.h;
303 gst_video_sink_center_rect (s, dst, &result, TRUE);
304 result.x += xwindow->render_rect.x;
305 result.y += xwindow->render_rect.y;
307 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
310 gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
312 g_mutex_unlock (&xvimagesink->flow_lock);
318 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
319 GstXWindow * xwindow, const gchar * media_title)
322 g_free (xvimagesink->media_title);
323 xvimagesink->media_title = g_strdup (media_title);
326 /* we have a window */
327 const gchar *app_name;
328 const gchar *title = NULL;
329 gchar *title_mem = NULL;
331 /* set application name as a title */
332 app_name = g_get_application_name ();
334 if (app_name && xvimagesink->media_title) {
335 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
337 } else if (app_name) {
339 } else if (xvimagesink->media_title) {
340 title = xvimagesink->media_title;
343 gst_xwindow_set_title (xwindow, title);
348 /* This function handles a GstXWindow creation
349 * The width and height are the actual pixel size on the display */
351 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
352 gint width, gint height)
354 GstXWindow *xwindow = NULL;
355 GstXvContext *context;
357 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
359 context = xvimagesink->context;
361 xwindow = gst_xvcontext_create_xwindow (context, width, height);
363 /* set application name as a title */
364 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
366 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
368 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
375 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
377 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
379 /* Update the window geometry */
380 g_mutex_lock (&xvimagesink->flow_lock);
381 if (G_LIKELY (xvimagesink->xwindow))
382 gst_xwindow_update_geometry (xvimagesink->xwindow);
383 g_mutex_unlock (&xvimagesink->flow_lock);
386 /* This function commits our internal colorbalance settings to our grabbed Xv
387 port. If the context is not initialized yet it simply returns */
389 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
391 GstXvContext *context;
393 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
395 /* If we haven't initialized the X context we can't update anything */
396 if ((context = xvimagesink->context) == NULL)
399 gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
402 /* This function handles XEvents that might be in the queue. It generates
403 GstEvent that will be sent upstream in the pipeline to handle interactivity
404 and navigation. It will also listen for configure events on the window to
405 trigger caps renegotiation so on the fly software scaling can work. */
407 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
410 guint pointer_x = 0, pointer_y = 0;
411 gboolean pointer_moved = FALSE;
412 gboolean exposed = FALSE, configured = FALSE;
414 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
416 /* Handle Interaction, produces navigation events */
418 /* We get all pointer motion events, only the last position is
420 g_mutex_lock (&xvimagesink->flow_lock);
421 g_mutex_lock (&xvimagesink->context->lock);
422 while (XCheckWindowEvent (xvimagesink->context->disp,
423 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
424 g_mutex_unlock (&xvimagesink->context->lock);
425 g_mutex_unlock (&xvimagesink->flow_lock);
429 pointer_x = e.xmotion.x;
430 pointer_y = e.xmotion.y;
431 pointer_moved = TRUE;
436 g_mutex_lock (&xvimagesink->flow_lock);
437 g_mutex_lock (&xvimagesink->context->lock);
441 g_mutex_unlock (&xvimagesink->context->lock);
442 g_mutex_unlock (&xvimagesink->flow_lock);
444 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
445 pointer_x, pointer_y);
446 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
447 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
449 g_mutex_lock (&xvimagesink->flow_lock);
450 g_mutex_lock (&xvimagesink->context->lock);
453 /* We get all events on our window to throw them upstream */
454 while (XCheckWindowEvent (xvimagesink->context->disp,
455 xvimagesink->xwindow->win,
456 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
459 const char *key_str = NULL;
461 /* We lock only for the X function call */
462 g_mutex_unlock (&xvimagesink->context->lock);
463 g_mutex_unlock (&xvimagesink->flow_lock);
467 /* Mouse button pressed over our window. We send upstream
468 events for interactivity/navigation */
469 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
470 e.xbutton.button, e.xbutton.x, e.xbutton.y);
471 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
472 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
475 /* Mouse button released over our window. We send upstream
476 events for interactivity/navigation */
477 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
478 e.xbutton.button, e.xbutton.x, e.xbutton.y);
479 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
480 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
484 /* Key pressed/released over our window. We send upstream
485 events for interactivity/navigation */
486 g_mutex_lock (&xvimagesink->context->lock);
487 keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
488 e.xkey.keycode, 0, 0);
489 if (keysym != NoSymbol) {
490 key_str = XKeysymToString (keysym);
494 g_mutex_unlock (&xvimagesink->context->lock);
495 GST_DEBUG_OBJECT (xvimagesink,
496 "key %d pressed over window at %d,%d (%s)",
497 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
498 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
499 e.type == KeyPress ? "key-press" : "key-release", key_str);
502 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
505 g_mutex_lock (&xvimagesink->flow_lock);
506 g_mutex_lock (&xvimagesink->context->lock);
510 while (XCheckWindowEvent (xvimagesink->context->disp,
511 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
516 case ConfigureNotify:
517 g_mutex_unlock (&xvimagesink->context->lock);
518 g_mutex_unlock (&xvimagesink->flow_lock);
520 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
522 g_mutex_lock (&xvimagesink->flow_lock);
523 g_mutex_lock (&xvimagesink->context->lock);
531 if (xvimagesink->handle_expose && (exposed || configured)) {
532 g_mutex_unlock (&xvimagesink->context->lock);
533 g_mutex_unlock (&xvimagesink->flow_lock);
535 gst_xvimagesink_expose (GST_VIDEO_OVERLAY (xvimagesink));
537 g_mutex_lock (&xvimagesink->flow_lock);
538 g_mutex_lock (&xvimagesink->context->lock);
541 /* Handle Display events */
542 while (XPending (xvimagesink->context->disp)) {
543 XNextEvent (xvimagesink->context->disp, &e);
549 wm_delete = XInternAtom (xvimagesink->context->disp,
550 "WM_DELETE_WINDOW", True);
551 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
552 /* Handle window deletion by posting an error on the bus */
553 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
554 ("Output window was closed"), (NULL));
556 g_mutex_unlock (&xvimagesink->context->lock);
557 gst_xwindow_destroy (xvimagesink->xwindow);
558 xvimagesink->xwindow = NULL;
559 g_mutex_lock (&xvimagesink->context->lock);
568 g_mutex_unlock (&xvimagesink->context->lock);
569 g_mutex_unlock (&xvimagesink->flow_lock);
573 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
575 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
577 GST_OBJECT_LOCK (xvimagesink);
578 while (xvimagesink->running) {
579 GST_OBJECT_UNLOCK (xvimagesink);
581 if (xvimagesink->xwindow) {
582 gst_xvimagesink_handle_xevents (xvimagesink);
584 /* FIXME: do we want to align this with the framerate or anything else? */
585 g_usleep (G_USEC_PER_SEC / 20);
587 GST_OBJECT_LOCK (xvimagesink);
589 GST_OBJECT_UNLOCK (xvimagesink);
595 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
597 GThread *thread = NULL;
599 /* don't start the thread too early */
600 if (xvimagesink->context == NULL) {
604 GST_OBJECT_LOCK (xvimagesink);
605 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
606 if (!xvimagesink->event_thread) {
607 /* Setup our event listening thread */
608 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
609 xvimagesink->handle_expose, xvimagesink->handle_events);
610 xvimagesink->running = TRUE;
611 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
612 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
615 if (xvimagesink->event_thread) {
616 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
617 xvimagesink->handle_expose, xvimagesink->handle_events);
618 xvimagesink->running = FALSE;
619 /* grab thread and mark it as NULL */
620 thread = xvimagesink->event_thread;
621 xvimagesink->event_thread = NULL;
624 GST_OBJECT_UNLOCK (xvimagesink);
626 /* Wait for our event thread to finish */
628 g_thread_join (thread);
635 gst_xvimagesink_getcaps (GstBaseSink * bsink, GstCaps * filter)
637 GstXvImageSink *xvimagesink;
640 xvimagesink = GST_XVIMAGESINK (bsink);
642 if (xvimagesink->context) {
644 return gst_caps_intersect_full (filter, xvimagesink->context->caps,
645 GST_CAPS_INTERSECT_FIRST);
647 return gst_caps_ref (xvimagesink->context->caps);
650 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
652 GstCaps *intersection;
655 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
656 gst_caps_unref (caps);
663 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
665 GstXvImageSink *xvimagesink;
666 GstXvContext *context;
667 GstStructure *structure;
668 GstBufferPool *newpool, *oldpool;
670 guint32 im_format = 0;
671 gint video_par_n, video_par_d; /* video's PAR */
672 gint display_par_n, display_par_d; /* display's PAR */
676 xvimagesink = GST_XVIMAGESINK (bsink);
677 context = xvimagesink->context;
679 GST_DEBUG_OBJECT (xvimagesink,
680 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
681 GST_PTR_FORMAT, context->caps, caps);
683 if (!gst_caps_can_intersect (context->caps, caps))
684 goto incompatible_caps;
686 if (!gst_video_info_from_caps (&info, caps))
689 xvimagesink->fps_n = info.fps_n;
690 xvimagesink->fps_d = info.fps_d;
692 xvimagesink->video_width = info.width;
693 xvimagesink->video_height = info.height;
695 im_format = gst_xvcontext_get_format_from_info (context, &info);
699 gst_xvcontext_set_colorimetry (context, &info.colorimetry);
703 /* get aspect ratio from caps if it's present, and
704 * convert video width and height to a display width and height
705 * using wd / hd = wv / hv * PARv / PARd */
707 /* get video's PAR */
708 video_par_n = info.par_n;
709 video_par_d = info.par_d;
711 /* get display's PAR */
712 if (xvimagesink->par) {
713 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
714 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
720 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
721 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
724 GST_DEBUG_OBJECT (xvimagesink,
725 "video width/height: %dx%d, calculated display ratio: %d/%d",
726 info.width, info.height, num, den);
728 /* now find a width x height that respects this display ratio.
729 * prefer those that have one of w/h the same as the incoming video
730 * using wd / hd = num / den */
732 /* start with same height, because of interlaced video */
733 /* check hd / den is an integer scale factor, and scale wd with the PAR */
734 if (info.height % den == 0) {
735 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
736 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
737 gst_util_uint64_scale_int (info.height, num, den);
738 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
739 } else if (info.width % num == 0) {
740 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
741 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
742 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
743 gst_util_uint64_scale_int (info.width, den, num);
745 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
746 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
747 gst_util_uint64_scale_int (info.height, num, den);
748 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
750 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
751 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
753 /* Notify application to set xwindow id now */
754 g_mutex_lock (&xvimagesink->flow_lock);
755 if (!xvimagesink->xwindow) {
756 g_mutex_unlock (&xvimagesink->flow_lock);
757 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
759 g_mutex_unlock (&xvimagesink->flow_lock);
762 /* Creating our window and our image with the display size in pixels */
763 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
764 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
765 goto no_display_size;
767 g_mutex_lock (&xvimagesink->flow_lock);
768 if (!xvimagesink->xwindow) {
769 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
770 GST_VIDEO_SINK_WIDTH (xvimagesink),
771 GST_VIDEO_SINK_HEIGHT (xvimagesink));
774 xvimagesink->info = info;
776 /* After a resize, we want to redraw the borders in case the new frame size
777 * doesn't cover the same area */
778 xvimagesink->redraw_border = TRUE;
780 /* create a new pool for the new configuration */
781 newpool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
783 structure = gst_buffer_pool_get_config (newpool);
784 gst_buffer_pool_config_set_params (structure, caps, size, 2, 0);
785 if (!gst_buffer_pool_set_config (newpool, structure))
788 oldpool = xvimagesink->pool;
789 /* we don't activate the pool yet, this will be done by downstream after it
790 * has configured the pool. If downstream does not want our pool we will
791 * activate it when we render into it */
792 xvimagesink->pool = newpool;
793 g_mutex_unlock (&xvimagesink->flow_lock);
795 /* unref the old sink */
797 /* we don't deactivate, some elements might still be using it, it will
798 * be deactivated when the last ref is gone */
799 gst_object_unref (oldpool);
807 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
812 GST_DEBUG_OBJECT (xvimagesink,
813 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
818 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
819 ("Error calculating the output display ratio of the video."));
824 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
825 ("Error calculating the output display ratio of the video."));
830 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
831 gst_object_unref (newpool);
832 g_mutex_unlock (&xvimagesink->flow_lock);
837 static GstStateChangeReturn
838 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
840 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
841 GstXvImageSink *xvimagesink;
843 xvimagesink = GST_XVIMAGESINK (element);
845 switch (transition) {
846 case GST_STATE_CHANGE_NULL_TO_READY:
847 if (!gst_xvimagesink_open (xvimagesink))
850 case GST_STATE_CHANGE_READY_TO_PAUSED:
852 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
854 case GST_STATE_CHANGE_PAUSED_TO_READY:
860 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
862 switch (transition) {
863 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
865 case GST_STATE_CHANGE_PAUSED_TO_READY:
866 xvimagesink->fps_n = 0;
867 xvimagesink->fps_d = 1;
868 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
869 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
870 g_mutex_lock (&xvimagesink->flow_lock);
871 if (xvimagesink->pool)
872 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
873 g_mutex_unlock (&xvimagesink->flow_lock);
875 case GST_STATE_CHANGE_READY_TO_NULL:
876 gst_xvimagesink_close (xvimagesink);
885 return GST_STATE_CHANGE_FAILURE;
890 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
891 GstClockTime * start, GstClockTime * end)
893 GstXvImageSink *xvimagesink;
895 xvimagesink = GST_XVIMAGESINK (bsink);
897 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
898 *start = GST_BUFFER_TIMESTAMP (buf);
899 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
900 *end = *start + GST_BUFFER_DURATION (buf);
902 if (xvimagesink->fps_n > 0) {
904 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
912 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
915 GstXvImageSink *xvimagesink;
916 GstBuffer *to_put = NULL;
919 xvimagesink = GST_XVIMAGESINK (vsink);
921 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
922 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
923 /* If this buffer has been allocated using our buffer management we simply
924 put the ximage which is in the PRIVATE pointer */
925 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
930 GstVideoFrame src, dest;
931 GstBufferPoolAcquireParams params = { 0, };
933 /* Else we have to copy the data into our private image, */
934 /* if we have one... */
935 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
937 /* we should have a pool, configured in setcaps */
938 if (xvimagesink->pool == NULL)
941 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
942 goto activate_failed;
944 /* take a buffer from our pool, if there is no buffer in the pool something
945 * is seriously wrong, waiting for the pool here might deadlock when we try
946 * to go to PAUSED because we never flush the pool then. */
947 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
948 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
949 if (res != GST_FLOW_OK)
952 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
953 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
955 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
958 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
959 gst_video_frame_unmap (&src);
963 gst_video_frame_copy (&dest, &src);
965 gst_video_frame_unmap (&dest);
966 gst_video_frame_unmap (&src);
969 if (!gst_xvimagesink_xvimage_put (xvimagesink, to_put))
974 gst_buffer_unref (to_put);
981 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
982 ("Internal error: can't allocate images"),
983 ("We don't have a bufferpool negotiated"));
984 return GST_FLOW_ERROR;
988 /* No image available. That's very bad ! */
989 GST_WARNING_OBJECT (xvimagesink, "could not create image");
994 /* No Window available to put our image into */
995 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1001 /* No Window available to put our image into */
1002 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1003 res = GST_FLOW_ERROR;
1008 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1009 res = GST_FLOW_ERROR;
1015 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
1017 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
1019 switch (GST_EVENT_TYPE (event)) {
1020 case GST_EVENT_TAG:{
1022 gchar *title = NULL;
1024 gst_event_parse_tag (event, &l);
1025 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1028 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1029 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1039 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1043 gst_xvimagesink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1045 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (bsink);
1046 GstBufferPool *pool;
1047 GstStructure *config;
1052 gst_query_parse_allocation (query, &caps, &need_pool);
1057 g_mutex_lock (&xvimagesink->flow_lock);
1058 if ((pool = xvimagesink->pool))
1059 gst_object_ref (pool);
1060 g_mutex_unlock (&xvimagesink->flow_lock);
1065 /* we had a pool, check caps */
1066 GST_DEBUG_OBJECT (xvimagesink, "check existing pool caps");
1067 config = gst_buffer_pool_get_config (pool);
1068 gst_buffer_pool_config_get_params (config, &pcaps, &size, NULL, NULL);
1070 if (!gst_caps_is_equal (caps, pcaps)) {
1071 GST_DEBUG_OBJECT (xvimagesink, "pool has different caps");
1072 /* different caps, we can't use this pool */
1073 gst_object_unref (pool);
1076 gst_structure_free (config);
1078 if (pool == NULL && need_pool) {
1081 if (!gst_video_info_from_caps (&info, caps))
1084 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1085 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
1087 /* the normal size of a frame */
1090 config = gst_buffer_pool_get_config (pool);
1091 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
1092 if (!gst_buffer_pool_set_config (pool, config))
1096 /* we need at least 2 buffer because we hold on to the last one */
1097 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1098 gst_object_unref (pool);
1101 /* we also support various metadata */
1102 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1103 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1110 GST_DEBUG_OBJECT (bsink, "no caps specified");
1115 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1120 GST_DEBUG_OBJECT (bsink, "failed setting config");
1121 gst_object_unref (pool);
1126 /* Interfaces stuff */
1128 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
1129 GstStructure * structure)
1131 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
1133 gboolean handled = FALSE;
1134 GstEvent *event = NULL;
1136 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
1137 GstVideoRectangle src = { 0, };
1138 GstVideoRectangle dst = { 0, };
1139 GstVideoRectangle result;
1140 gdouble x, y, xscale = 1.0, yscale = 1.0;
1141 GstXWindow *xwindow;
1143 /* We take the flow_lock while we look at the window */
1144 g_mutex_lock (&xvimagesink->flow_lock);
1146 if (!(xwindow = xvimagesink->xwindow)) {
1147 g_mutex_unlock (&xvimagesink->flow_lock);
1151 if (xvimagesink->keep_aspect) {
1152 /* We get the frame position using the calculated geometry from _setcaps
1153 that respect pixel aspect ratios */
1154 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1155 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1156 dst.w = xwindow->render_rect.w;
1157 dst.h = xwindow->render_rect.h;
1159 gst_video_sink_center_rect (src, dst, &result, TRUE);
1160 result.x += xwindow->render_rect.x;
1161 result.y += xwindow->render_rect.y;
1163 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1166 g_mutex_unlock (&xvimagesink->flow_lock);
1168 /* We calculate scaling using the original video frames geometry to include
1169 pixel aspect ratio scaling. */
1170 xscale = (gdouble) xvimagesink->video_width / result.w;
1171 yscale = (gdouble) xvimagesink->video_height / result.h;
1173 /* Converting pointer coordinates to the non scaled geometry */
1174 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1175 x = MIN (x, result.x + result.w);
1176 x = MAX (x - result.x, 0);
1177 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1178 (gdouble) x * xscale, NULL);
1180 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1181 y = MIN (y, result.y + result.h);
1182 y = MAX (y - result.y, 0);
1183 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1184 (gdouble) y * yscale, NULL);
1187 event = gst_event_new_navigation (structure);
1188 gst_event_ref (event);
1189 handled = gst_pad_send_event (peer, event);
1190 gst_object_unref (peer);
1193 if (!handled && event) {
1194 gst_element_post_message ((GstElement *) xvimagesink,
1195 gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1199 gst_event_unref (event);
1203 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
1205 iface->send_event = gst_xvimagesink_navigation_send_event;
1209 gst_xvimagesink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1211 XID xwindow_id = id;
1212 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1213 GstXWindow *xwindow = NULL;
1214 GstXvContext *context;
1216 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1218 g_mutex_lock (&xvimagesink->flow_lock);
1220 /* If we already use that window return */
1221 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
1222 g_mutex_unlock (&xvimagesink->flow_lock);
1226 /* If the element has not initialized the X11 context try to do so */
1227 if (!xvimagesink->context &&
1228 !(xvimagesink->context =
1229 gst_xvcontext_new (&xvimagesink->config, NULL))) {
1230 g_mutex_unlock (&xvimagesink->flow_lock);
1231 /* we have thrown a GST_ELEMENT_ERROR now */
1235 context = xvimagesink->context;
1237 gst_xvimagesink_update_colorbalance (xvimagesink);
1239 /* If a window is there already we destroy it */
1240 if (xvimagesink->xwindow) {
1241 gst_xwindow_destroy (xvimagesink->xwindow);
1242 xvimagesink->xwindow = NULL;
1245 /* If the xid is 0 we go back to an internal window */
1246 if (xwindow_id == 0) {
1247 /* If no width/height caps nego did not happen window will be created
1248 during caps nego then */
1249 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
1250 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
1252 gst_xvimagesink_xwindow_new (xvimagesink,
1253 GST_VIDEO_SINK_WIDTH (xvimagesink),
1254 GST_VIDEO_SINK_HEIGHT (xvimagesink));
1257 xwindow = gst_xvcontext_create_xwindow_from_xid (context, xwindow_id);
1258 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
1262 xvimagesink->xwindow = xwindow;
1264 g_mutex_unlock (&xvimagesink->flow_lock);
1268 gst_xvimagesink_expose (GstVideoOverlay * overlay)
1270 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1272 GST_DEBUG ("doing expose");
1273 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1274 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
1278 gst_xvimagesink_set_event_handling (GstVideoOverlay * overlay,
1279 gboolean handle_events)
1281 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1283 g_mutex_lock (&xvimagesink->flow_lock);
1284 xvimagesink->handle_events = handle_events;
1285 if (G_LIKELY (xvimagesink->xwindow))
1286 gst_xwindow_set_event_handling (xvimagesink->xwindow, handle_events);
1287 g_mutex_unlock (&xvimagesink->flow_lock);
1291 gst_xvimagesink_set_render_rectangle (GstVideoOverlay * overlay, gint x, gint y,
1292 gint width, gint height)
1294 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
1296 g_mutex_lock (&xvimagesink->flow_lock);
1297 if (G_LIKELY (xvimagesink->xwindow))
1298 gst_xwindow_set_render_rectangle (xvimagesink->xwindow, x, y, width,
1300 g_mutex_unlock (&xvimagesink->flow_lock);
1304 gst_xvimagesink_video_overlay_init (GstVideoOverlayInterface * iface)
1306 iface->set_window_handle = gst_xvimagesink_set_window_handle;
1307 iface->expose = gst_xvimagesink_expose;
1308 iface->handle_events = gst_xvimagesink_set_event_handling;
1309 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
1312 static const GList *
1313 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
1315 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1317 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1319 if (xvimagesink->context)
1320 return xvimagesink->context->channels_list;
1326 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
1327 GstColorBalanceChannel * channel, gint value)
1329 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1331 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1332 g_return_if_fail (channel->label != NULL);
1334 xvimagesink->config.cb_changed = TRUE;
1336 /* Normalize val to [-1000, 1000] */
1337 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1338 (double) (channel->max_value - channel->min_value));
1340 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1341 xvimagesink->config.hue = value;
1342 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1343 xvimagesink->config.saturation = value;
1344 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1345 xvimagesink->config.contrast = value;
1346 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1347 xvimagesink->config.brightness = value;
1349 g_warning ("got an unknown channel %s", channel->label);
1353 gst_xvimagesink_update_colorbalance (xvimagesink);
1357 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
1358 GstColorBalanceChannel * channel)
1360 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
1363 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1364 g_return_val_if_fail (channel->label != NULL, 0);
1366 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1367 value = xvimagesink->config.hue;
1368 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1369 value = xvimagesink->config.saturation;
1370 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1371 value = xvimagesink->config.contrast;
1372 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1373 value = xvimagesink->config.brightness;
1375 g_warning ("got an unknown channel %s", channel->label);
1378 /* Normalize val to [channel->min_value, channel->max_value] */
1379 value = channel->min_value + (channel->max_value - channel->min_value) *
1380 (value + 1000) / 2000;
1385 static GstColorBalanceType
1386 gst_xvimagesink_colorbalance_get_balance_type (GstColorBalance * balance)
1388 return GST_COLOR_BALANCE_HARDWARE;
1392 gst_xvimagesink_colorbalance_init (GstColorBalanceInterface * iface)
1394 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
1395 iface->set_value = gst_xvimagesink_colorbalance_set_value;
1396 iface->get_value = gst_xvimagesink_colorbalance_get_value;
1397 iface->get_balance_type = gst_xvimagesink_colorbalance_get_balance_type;
1401 static const GList *
1402 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
1404 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1405 static GList *list = NULL;
1408 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1410 g_list_append (list, g_object_class_find_property (klass,
1411 "autopaint-colorkey"));
1413 g_list_append (list, g_object_class_find_property (klass,
1416 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1423 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
1424 guint prop_id, const GParamSpec * pspec)
1426 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1430 case PROP_AUTOPAINT_COLORKEY:
1431 case PROP_DOUBLE_BUFFER:
1433 GST_DEBUG_OBJECT (xvimagesink,
1434 "probing device list and get capabilities");
1435 if (!xvimagesink->context) {
1436 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1437 xvimagesink->context = gst_xvimagesink_context_get (xvimagesink);
1441 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1447 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
1448 guint prop_id, const GParamSpec * pspec)
1450 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1451 gboolean ret = FALSE;
1455 case PROP_AUTOPAINT_COLORKEY:
1456 case PROP_DOUBLE_BUFFER:
1458 if (xvimagesink->context != NULL) {
1465 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1472 static GValueArray *
1473 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
1474 guint prop_id, const GParamSpec * pspec)
1476 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
1477 GValueArray *array = NULL;
1479 if (G_UNLIKELY (!xvimagesink->context)) {
1480 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1489 GValue value = { 0 };
1491 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1492 g_value_init (&value, G_TYPE_STRING);
1494 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1495 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1497 g_value_set_string (&value, adaptor_id_s);
1498 g_value_array_append (array, &value);
1499 g_free (adaptor_id_s);
1501 g_value_unset (&value);
1504 case PROP_AUTOPAINT_COLORKEY:
1505 if (xvimagesink->have_autopaint_colorkey) {
1506 GValue value = { 0 };
1508 array = g_value_array_new (2);
1509 g_value_init (&value, G_TYPE_BOOLEAN);
1510 g_value_set_boolean (&value, FALSE);
1511 g_value_array_append (array, &value);
1512 g_value_set_boolean (&value, TRUE);
1513 g_value_array_append (array, &value);
1514 g_value_unset (&value);
1517 case PROP_DOUBLE_BUFFER:
1518 if (xvimagesink->have_double_buffer) {
1519 GValue value = { 0 };
1521 array = g_value_array_new (2);
1522 g_value_init (&value, G_TYPE_BOOLEAN);
1523 g_value_set_boolean (&value, FALSE);
1524 g_value_array_append (array, &value);
1525 g_value_set_boolean (&value, TRUE);
1526 g_value_array_append (array, &value);
1527 g_value_unset (&value);
1531 if (xvimagesink->have_colorkey) {
1532 GValue value = { 0 };
1534 array = g_value_array_new (1);
1535 g_value_init (&value, GST_TYPE_INT_RANGE);
1536 gst_value_set_int_range (&value, 0, 0xffffff);
1537 g_value_array_append (array, &value);
1538 g_value_unset (&value);
1542 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1551 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
1554 iface->get_properties = gst_xvimagesink_probe_get_properties;
1555 iface->probe_property = gst_xvimagesink_probe_probe_property;
1556 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
1557 iface->get_values = gst_xvimagesink_probe_get_values;
1561 /* =========================================== */
1563 /* Init & Class init */
1565 /* =========================================== */
1568 gst_xvimagesink_set_property (GObject * object, guint prop_id,
1569 const GValue * value, GParamSpec * pspec)
1571 GstXvImageSink *xvimagesink;
1573 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1575 xvimagesink = GST_XVIMAGESINK (object);
1579 xvimagesink->config.hue = g_value_get_int (value);
1580 xvimagesink->config.cb_changed = TRUE;
1581 gst_xvimagesink_update_colorbalance (xvimagesink);
1584 xvimagesink->config.contrast = g_value_get_int (value);
1585 xvimagesink->config.cb_changed = TRUE;
1586 gst_xvimagesink_update_colorbalance (xvimagesink);
1588 case PROP_BRIGHTNESS:
1589 xvimagesink->config.brightness = g_value_get_int (value);
1590 xvimagesink->config.cb_changed = TRUE;
1591 gst_xvimagesink_update_colorbalance (xvimagesink);
1593 case PROP_SATURATION:
1594 xvimagesink->config.saturation = g_value_get_int (value);
1595 xvimagesink->config.cb_changed = TRUE;
1596 gst_xvimagesink_update_colorbalance (xvimagesink);
1599 g_free (xvimagesink->config.display_name);
1600 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1602 case PROP_SYNCHRONOUS:
1603 xvimagesink->synchronous = g_value_get_boolean (value);
1604 if (xvimagesink->context) {
1605 gst_xvcontext_set_synchronous (xvimagesink->context,
1606 xvimagesink->synchronous);
1609 case PROP_PIXEL_ASPECT_RATIO:
1610 g_free (xvimagesink->par);
1611 xvimagesink->par = g_new0 (GValue, 1);
1612 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1613 if (!g_value_transform (value, xvimagesink->par)) {
1614 g_warning ("Could not transform string to aspect ratio");
1615 gst_value_set_fraction (xvimagesink->par, 1, 1);
1617 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1618 gst_value_get_fraction_numerator (xvimagesink->par),
1619 gst_value_get_fraction_denominator (xvimagesink->par));
1621 case PROP_FORCE_ASPECT_RATIO:
1622 xvimagesink->keep_aspect = g_value_get_boolean (value);
1624 case PROP_HANDLE_EVENTS:
1625 gst_xvimagesink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1626 g_value_get_boolean (value));
1627 gst_xvimagesink_manage_event_thread (xvimagesink);
1630 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1632 case PROP_HANDLE_EXPOSE:
1633 xvimagesink->handle_expose = g_value_get_boolean (value);
1634 gst_xvimagesink_manage_event_thread (xvimagesink);
1636 case PROP_DOUBLE_BUFFER:
1637 xvimagesink->double_buffer = g_value_get_boolean (value);
1639 case PROP_AUTOPAINT_COLORKEY:
1640 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1643 xvimagesink->config.colorkey = g_value_get_int (value);
1645 case PROP_DRAW_BORDERS:
1646 xvimagesink->draw_borders = g_value_get_boolean (value);
1649 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1655 gst_xvimagesink_get_property (GObject * object, guint prop_id,
1656 GValue * value, GParamSpec * pspec)
1658 GstXvImageSink *xvimagesink;
1660 g_return_if_fail (GST_IS_XVIMAGESINK (object));
1662 xvimagesink = GST_XVIMAGESINK (object);
1666 g_value_set_int (value, xvimagesink->config.hue);
1669 g_value_set_int (value, xvimagesink->config.contrast);
1671 case PROP_BRIGHTNESS:
1672 g_value_set_int (value, xvimagesink->config.brightness);
1674 case PROP_SATURATION:
1675 g_value_set_int (value, xvimagesink->config.saturation);
1678 g_value_set_string (value, xvimagesink->config.display_name);
1680 case PROP_SYNCHRONOUS:
1681 g_value_set_boolean (value, xvimagesink->synchronous);
1683 case PROP_PIXEL_ASPECT_RATIO:
1684 if (xvimagesink->par)
1685 g_value_transform (xvimagesink->par, value);
1687 case PROP_FORCE_ASPECT_RATIO:
1688 g_value_set_boolean (value, xvimagesink->keep_aspect);
1690 case PROP_HANDLE_EVENTS:
1691 g_value_set_boolean (value, xvimagesink->handle_events);
1695 char *adaptor_nr_s =
1696 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1698 g_value_set_string (value, adaptor_nr_s);
1699 g_free (adaptor_nr_s);
1702 case PROP_DEVICE_NAME:
1703 if (xvimagesink->context && xvimagesink->context->adaptors) {
1704 g_value_set_string (value,
1705 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1707 g_value_set_string (value, NULL);
1710 case PROP_HANDLE_EXPOSE:
1711 g_value_set_boolean (value, xvimagesink->handle_expose);
1713 case PROP_DOUBLE_BUFFER:
1714 g_value_set_boolean (value, xvimagesink->double_buffer);
1716 case PROP_AUTOPAINT_COLORKEY:
1717 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1720 g_value_set_int (value, xvimagesink->config.colorkey);
1722 case PROP_DRAW_BORDERS:
1723 g_value_set_boolean (value, xvimagesink->draw_borders);
1725 case PROP_WINDOW_WIDTH:
1726 if (xvimagesink->xwindow)
1727 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1729 g_value_set_uint64 (value, 0);
1731 case PROP_WINDOW_HEIGHT:
1732 if (xvimagesink->xwindow)
1733 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1735 g_value_set_uint64 (value, 0);
1738 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1744 gst_xvimagesink_open (GstXvImageSink * xvimagesink)
1746 GError *error = NULL;
1748 /* Initializing the XvContext unless already done through GstVideoOverlay */
1749 if (!xvimagesink->context) {
1750 GstXvContext *context;
1751 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1754 GST_OBJECT_LOCK (xvimagesink);
1755 xvimagesink->context = context;
1757 GST_OBJECT_LOCK (xvimagesink);
1758 /* make an allocator for this context */
1759 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1760 GST_OBJECT_UNLOCK (xvimagesink);
1762 /* update object's par with calculated one if not set yet */
1763 if (!xvimagesink->par) {
1764 xvimagesink->par = g_new0 (GValue, 1);
1765 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1766 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1768 /* call XSynchronize with the current value of synchronous */
1769 gst_xvcontext_set_synchronous (xvimagesink->context,
1770 xvimagesink->synchronous);
1771 gst_xvimagesink_update_colorbalance (xvimagesink);
1772 gst_xvimagesink_manage_event_thread (xvimagesink);
1778 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1779 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1780 error->message, __FILE__, GST_FUNCTION, __LINE__);
1786 gst_xvimagesink_close (GstXvImageSink * xvimagesink)
1789 GstXvContext *context;
1791 GST_OBJECT_LOCK (xvimagesink);
1792 xvimagesink->running = FALSE;
1793 /* grab thread and mark it as NULL */
1794 thread = xvimagesink->event_thread;
1795 xvimagesink->event_thread = NULL;
1796 GST_OBJECT_UNLOCK (xvimagesink);
1798 /* Wait for our event thread to finish before we clean up our stuff. */
1800 g_thread_join (thread);
1802 if (xvimagesink->cur_image) {
1803 gst_buffer_unref (xvimagesink->cur_image);
1804 xvimagesink->cur_image = NULL;
1807 g_mutex_lock (&xvimagesink->flow_lock);
1809 if (xvimagesink->pool) {
1810 gst_object_unref (xvimagesink->pool);
1811 xvimagesink->pool = NULL;
1814 if (xvimagesink->xwindow) {
1815 gst_xwindow_clear (xvimagesink->xwindow);
1816 gst_xwindow_destroy (xvimagesink->xwindow);
1817 xvimagesink->xwindow = NULL;
1819 g_mutex_unlock (&xvimagesink->flow_lock);
1821 if (xvimagesink->allocator) {
1822 gst_object_unref (xvimagesink->allocator);
1823 xvimagesink->allocator = NULL;
1826 GST_OBJECT_LOCK (xvimagesink);
1827 /* grab context and mark it as NULL */
1828 context = xvimagesink->context;
1829 xvimagesink->context = NULL;
1830 GST_OBJECT_UNLOCK (xvimagesink);
1833 gst_xvcontext_unref (context);
1836 /* Finalize is called only once, dispose can be called multiple times.
1837 * We use mutexes and don't reset stuff to NULL here so let's register
1840 gst_xvimagesink_finalize (GObject * object)
1842 GstXvImageSink *xvimagesink;
1844 xvimagesink = GST_XVIMAGESINK (object);
1846 gst_xvimagesink_close (xvimagesink);
1848 gst_xvcontext_config_clear (&xvimagesink->config);
1850 if (xvimagesink->par) {
1851 g_free (xvimagesink->par);
1852 xvimagesink->par = NULL;
1854 g_mutex_clear (&xvimagesink->flow_lock);
1855 g_free (xvimagesink->media_title);
1857 G_OBJECT_CLASS (parent_class)->finalize (object);
1861 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
1863 xvimagesink->config.display_name = NULL;
1864 xvimagesink->config.adaptor_nr = 0;
1865 xvimagesink->config.autopaint_colorkey = TRUE;
1866 xvimagesink->config.double_buffer = TRUE;
1867 /* on 16bit displays this becomes r,g,b = 1,2,3
1868 * on 24bit displays this becomes r,g,b = 8,8,16
1869 * as a port atom value */
1870 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1871 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1872 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1873 xvimagesink->config.cb_changed = FALSE;
1875 xvimagesink->context = NULL;
1876 xvimagesink->xwindow = NULL;
1877 xvimagesink->cur_image = NULL;
1879 xvimagesink->fps_n = 0;
1880 xvimagesink->fps_d = 0;
1881 xvimagesink->video_width = 0;
1882 xvimagesink->video_height = 0;
1884 g_mutex_init (&xvimagesink->flow_lock);
1886 xvimagesink->pool = NULL;
1888 xvimagesink->synchronous = FALSE;
1889 xvimagesink->running = FALSE;
1890 xvimagesink->keep_aspect = TRUE;
1891 xvimagesink->handle_events = TRUE;
1892 xvimagesink->par = NULL;
1893 xvimagesink->handle_expose = TRUE;
1895 xvimagesink->draw_borders = TRUE;
1899 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
1901 GObjectClass *gobject_class;
1902 GstElementClass *gstelement_class;
1903 GstBaseSinkClass *gstbasesink_class;
1904 GstVideoSinkClass *videosink_class;
1906 gobject_class = (GObjectClass *) klass;
1907 gstelement_class = (GstElementClass *) klass;
1908 gstbasesink_class = (GstBaseSinkClass *) klass;
1909 videosink_class = (GstVideoSinkClass *) klass;
1911 parent_class = g_type_class_peek_parent (klass);
1913 gobject_class->set_property = gst_xvimagesink_set_property;
1914 gobject_class->get_property = gst_xvimagesink_get_property;
1916 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1917 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1918 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1919 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1920 g_param_spec_int ("brightness", "Brightness",
1921 "The brightness of the video", -1000, 1000, 0,
1922 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1923 g_object_class_install_property (gobject_class, PROP_HUE,
1924 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1925 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1926 g_object_class_install_property (gobject_class, PROP_SATURATION,
1927 g_param_spec_int ("saturation", "Saturation",
1928 "The saturation of the video", -1000, 1000, 0,
1929 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1930 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1931 g_param_spec_string ("display", "Display", "X Display name", NULL,
1932 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1933 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1934 g_param_spec_boolean ("synchronous", "Synchronous",
1935 "When enabled, runs the X display in synchronous mode. "
1936 "(unrelated to A/V sync, used only for debugging)", FALSE,
1937 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1938 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1939 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1940 "The pixel aspect ratio of the device", "1/1",
1941 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1942 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1943 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1944 "When enabled, scaling will respect original aspect ratio", TRUE,
1945 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1946 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1947 g_param_spec_boolean ("handle-events", "Handle XEvents",
1948 "When enabled, XEvents will be selected and handled", TRUE,
1949 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1950 g_object_class_install_property (gobject_class, PROP_DEVICE,
1951 g_param_spec_string ("device", "Adaptor number",
1952 "The number of the video adaptor", "0",
1953 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1954 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1955 g_param_spec_string ("device-name", "Adaptor name",
1956 "The name of the video adaptor", NULL,
1957 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1959 * GstXvImageSink:handle-expose
1961 * When enabled, the current frame will always be drawn in response to X
1964 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1965 g_param_spec_boolean ("handle-expose", "Handle expose",
1967 "the current frame will always be drawn in response to X Expose "
1968 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1971 * GstXvImageSink:double-buffer
1973 * Whether to double-buffer the output.
1975 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1976 g_param_spec_boolean ("double-buffer", "Double-buffer",
1977 "Whether to double-buffer the output", TRUE,
1978 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1980 * GstXvImageSink:autopaint-colorkey
1982 * Whether to autofill overlay with colorkey
1984 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1985 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1986 "Whether to autofill overlay with colorkey", TRUE,
1987 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1989 * GstXvImageSink:colorkey
1991 * Color to use for the overlay mask.
1993 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1994 g_param_spec_int ("colorkey", "Colorkey",
1995 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1996 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1999 * GstXvImageSink:draw-borders
2001 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
2002 * unused parts of the video area.
2004 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2005 g_param_spec_boolean ("draw-borders", "Draw Borders",
2006 "Draw black borders to fill unused area in force-aspect-ratio mode",
2007 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2010 * GstXvImageSink:window-width
2012 * Actual width of the video window.
2014 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2015 g_param_spec_uint64 ("window-width", "window-width",
2016 "Width of the window", 0, G_MAXUINT64, 0,
2017 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2020 * GstXvImageSink:window-height
2022 * Actual height of the video window.
2024 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2025 g_param_spec_uint64 ("window-height", "window-height",
2026 "Height of the window", 0, G_MAXUINT64, 0,
2027 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2029 gobject_class->finalize = gst_xvimagesink_finalize;
2031 gst_element_class_set_static_metadata (gstelement_class,
2032 "Video sink", "Sink/Video",
2033 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2035 gst_element_class_add_pad_template (gstelement_class,
2036 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
2038 gstelement_class->change_state =
2039 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
2041 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
2042 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
2043 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
2044 gstbasesink_class->propose_allocation =
2045 GST_DEBUG_FUNCPTR (gst_xvimagesink_propose_allocation);
2046 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
2048 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);