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
25 * XvImageSink renders video frames to a drawable (XWindow) on a local display
26 * using the XVideo extension. Rendering to a remote display is theoretically
27 * possible but i doubt that the XVideo extension is actually available when
28 * connecting to a remote display. This element can receive a Window ID from the
29 * application through the #GstVideoOverlay interface and will then render
30 * video frames in this drawable. If no Window ID was provided by the
31 * application, the element will create its own internal window and render
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 * XvImageSink creates a thread to handle events coming from the drawable. There
46 * are several kind of events that can be grouped in 2 big categories: input
47 * events and window state related events. Input events will be translated to
48 * navigation events and pushed upstream for other elements to react on them.
49 * This includes events such as pointer moves, key press/release, clicks etc...
50 * Other events are used to handle the drawable appearance even when the data
51 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
52 * paused, it will receive expose events from the drawable and draw the latest
53 * frame with correct borders/aspect-ratio.
55 * ## Pixel aspect ratio
57 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
58 * the display specified in the #GstXvImageSink:display property or the
59 * default display if nothing specified. Once this connection is open it will
60 * inspect the display configuration including the physical display geometry and
61 * then calculate the pixel aspect ratio. When receiving video frames with a
62 * different pixel aspect ratio, XvImageSink will use hardware scaling to
63 * display the video frames correctly on display's pixel aspect ratio.
64 * Sometimes the calculated pixel aspect ratio can be wrong, it is
65 * then possible to enforce a specific pixel aspect ratio using the
66 * #GstXvImageSink:pixel-aspect-ratio property.
70 * gst-launch-1.0 -v videotestsrc ! xvimagesink
72 * A pipeline to test hardware scaling.
73 * When the test video signal appears you can resize the window and see that
74 * video frames are scaled through hardware (no extra CPU cost). By default
75 * the image will never be distorted when scaled, instead black borders will
78 * gst-launch-1.0 -v videotestsrc ! xvimagesink force-aspect-ratio=false
80 * Same pipeline with #GstXvImageSink:force-aspect-ratio property set to
81 * false. You can observe that no borders are drawn around the scaled image
82 * now and it will be distorted to fill the entire frame instead of respecting
85 * gst-launch-1.0 -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-1.0 -v videotestsrc ! video/x-raw, pixel-aspect-ratio=4/3 ! xvimagesink
100 * This is faking a 4/3 pixel aspect ratio caps on video frames produced by
101 * videotestsrc, in most cases the pixel aspect ratio of the display will be
102 * 1/1. This means that XvImageSink will have to do the scaling to convert
103 * incoming frames to a size that will match the display pixel aspect ratio
104 * (from 320x240 to 320x180 in this case).
106 * gst-launch-1.0 -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
108 * Demonstrates how to use the colorbalance interface.
112 /* for developers: there are two useful tools : xvinfo and xvattr */
119 #include <gst/video/navigation.h>
120 #include <gst/video/videooverlay.h>
121 #include <gst/video/colorbalance.h>
122 /* Helper functions */
123 #include <gst/video/gstvideometa.h>
126 #include "xvimagesink.h"
127 #include "xvimageallocator.h"
129 /* Debugging category */
130 #include <gst/gstinfo.h>
132 /* for XkbKeycodeToKeysym */
133 #include <X11/XKBlib.h>
135 GST_DEBUG_CATEGORY_EXTERN (gst_debug_xv_image_sink);
136 GST_DEBUG_CATEGORY_EXTERN (CAT_PERFORMANCE);
137 #define GST_CAT_DEFAULT gst_debug_xv_image_sink
142 unsigned long functions;
143 unsigned long decorations;
145 unsigned long status;
147 MotifWmHints, MwmHints;
149 #define MWM_HINTS_DECORATIONS (1L << 1)
151 static gboolean gst_xv_image_sink_open (GstXvImageSink * xvimagesink);
152 static void gst_xv_image_sink_close (GstXvImageSink * xvimagesink);
153 static void gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink *
155 static void gst_xv_image_sink_expose (GstVideoOverlay * overlay);
157 /* Default template - initiated with class struct to allow gst-register to work
159 static GstStaticPadTemplate gst_xv_image_sink_sink_template_factory =
160 GST_STATIC_PAD_TEMPLATE ("sink",
163 GST_STATIC_CAPS ("video/x-raw, "
164 "framerate = (fraction) [ 0, MAX ], "
165 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
177 PROP_PIXEL_ASPECT_RATIO,
178 PROP_FORCE_ASPECT_RATIO,
184 PROP_AUTOPAINT_COLORKEY,
192 /* ============================================================= */
196 /* ============================================================= */
198 /* =========================================== */
200 /* Object typing & Creation */
202 /* =========================================== */
203 static void gst_xv_image_sink_navigation_init (GstNavigationInterface * iface);
204 static void gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface *
206 static void gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface *
208 #define gst_xv_image_sink_parent_class parent_class
209 G_DEFINE_TYPE_WITH_CODE (GstXvImageSink, gst_xv_image_sink, GST_TYPE_VIDEO_SINK,
210 G_IMPLEMENT_INTERFACE (GST_TYPE_NAVIGATION,
211 gst_xv_image_sink_navigation_init);
212 G_IMPLEMENT_INTERFACE (GST_TYPE_VIDEO_OVERLAY,
213 gst_xv_image_sink_video_overlay_init);
214 G_IMPLEMENT_INTERFACE (GST_TYPE_COLOR_BALANCE,
215 gst_xv_image_sink_colorbalance_init));
218 /* ============================================================= */
220 /* Private Methods */
222 /* ============================================================= */
225 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
226 * if no window was available */
228 gst_xv_image_sink_xvimage_put (GstXvImageSink * xvimagesink,
231 GstXvImageMemory *mem;
232 GstVideoCropMeta *crop;
233 GstVideoRectangle result;
234 gboolean draw_border = FALSE;
235 GstVideoRectangle src = { 0, };
236 GstVideoRectangle dst = { 0, };
237 GstVideoRectangle mem_crop;
240 /* We take the flow_lock. If expose is in there we don't want to run
241 concurrently from the data flow thread */
242 g_mutex_lock (&xvimagesink->flow_lock);
244 if (G_UNLIKELY ((xwindow = xvimagesink->xwindow) == NULL)) {
245 g_mutex_unlock (&xvimagesink->flow_lock);
249 /* Draw borders when displaying the first frame. After this
250 draw borders only on expose event or after a size change. */
251 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
252 draw_border = xvimagesink->draw_borders;
253 xvimagesink->redraw_border = FALSE;
256 /* Store a reference to the last image we put, lose the previous one */
257 if (xvimage && xvimagesink->cur_image != xvimage) {
258 if (xvimagesink->cur_image) {
259 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
260 gst_buffer_unref (xvimagesink->cur_image);
262 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
263 xvimagesink->cur_image = gst_buffer_ref (xvimage);
266 /* Expose sends a NULL image, we take the latest frame */
268 if (xvimagesink->cur_image) {
270 xvimage = xvimagesink->cur_image;
272 g_mutex_unlock (&xvimagesink->flow_lock);
277 mem = (GstXvImageMemory *) gst_buffer_peek_memory (xvimage, 0);
278 gst_xvimage_memory_get_crop (mem, &mem_crop);
280 crop = gst_buffer_get_video_crop_meta (xvimage);
283 src.x = crop->x + mem_crop.x;
284 src.y = crop->y + mem_crop.y;
286 src.h = crop->height;
287 GST_LOG_OBJECT (xvimagesink,
288 "crop %dx%d-%dx%d", crop->x, crop->y, crop->width, crop->height);
293 if (xvimagesink->keep_aspect) {
296 /* We take the size of the source material as it was negotiated and
297 * corrected for DAR. This size can be different from the cropped size in
298 * which case the image will be scaled to fit the negotiated size. */
299 s.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
300 s.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
301 dst.w = xwindow->render_rect.w;
302 dst.h = xwindow->render_rect.h;
304 gst_video_sink_center_rect (s, dst, &result, TRUE);
305 result.x += xwindow->render_rect.x;
306 result.y += xwindow->render_rect.y;
308 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
311 gst_xvimage_memory_render (mem, &src, xwindow, &result, draw_border);
313 g_mutex_unlock (&xvimagesink->flow_lock);
319 gst_xv_image_sink_xwindow_set_title (GstXvImageSink * xvimagesink,
320 GstXWindow * xwindow, const gchar * media_title)
323 g_free (xvimagesink->media_title);
324 xvimagesink->media_title = g_strdup (media_title);
327 /* we have a window */
328 const gchar *app_name;
329 const gchar *title = NULL;
330 gchar *title_mem = NULL;
332 /* set application name as a title */
333 app_name = g_get_application_name ();
335 if (app_name && xvimagesink->media_title) {
336 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
338 } else if (app_name) {
340 } else if (xvimagesink->media_title) {
341 title = xvimagesink->media_title;
344 gst_xwindow_set_title (xwindow, title);
349 /* This function handles a GstXWindow creation
350 * The width and height are the actual pixel size on the display */
352 gst_xv_image_sink_xwindow_new (GstXvImageSink * xvimagesink,
353 gint width, gint height)
355 GstXWindow *xwindow = NULL;
356 GstXvContext *context;
358 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
360 context = xvimagesink->context;
362 xwindow = gst_xvcontext_create_xwindow (context, width, height);
364 /* set application name as a title */
365 gst_xv_image_sink_xwindow_set_title (xvimagesink, xwindow, NULL);
367 gst_xwindow_set_event_handling (xwindow, xvimagesink->handle_events);
369 gst_video_overlay_got_window_handle (GST_VIDEO_OVERLAY (xvimagesink),
376 gst_xv_image_sink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
378 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
380 /* Update the window geometry */
381 g_mutex_lock (&xvimagesink->flow_lock);
382 if (G_LIKELY (xvimagesink->xwindow))
383 gst_xwindow_update_geometry (xvimagesink->xwindow);
384 g_mutex_unlock (&xvimagesink->flow_lock);
387 /* This function commits our internal colorbalance settings to our grabbed Xv
388 port. If the context is not initialized yet it simply returns */
390 gst_xv_image_sink_update_colorbalance (GstXvImageSink * xvimagesink)
392 GstXvContext *context;
394 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
396 /* If we haven't initialized the X context we can't update anything */
397 if ((context = xvimagesink->context) == NULL)
400 gst_xvcontext_update_colorbalance (context, &xvimagesink->config);
403 /* This function handles XEvents that might be in the queue. It generates
404 GstEvent that will be sent upstream in the pipeline to handle interactivity
405 and navigation. It will also listen for configure events on the window to
406 trigger caps renegotiation so on the fly software scaling can work. */
408 gst_xv_image_sink_handle_xevents (GstXvImageSink * xvimagesink)
411 gint pointer_x = 0, pointer_y = 0;
412 gboolean pointer_moved = FALSE;
413 gboolean exposed = FALSE, configured = FALSE;
415 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
417 /* Handle Interaction, produces navigation events */
419 /* We get all pointer motion events, only the last position is
421 g_mutex_lock (&xvimagesink->flow_lock);
422 g_mutex_lock (&xvimagesink->context->lock);
423 while (XCheckWindowEvent (xvimagesink->context->disp,
424 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
425 g_mutex_unlock (&xvimagesink->context->lock);
426 g_mutex_unlock (&xvimagesink->flow_lock);
430 pointer_x = e.xmotion.x;
431 pointer_y = e.xmotion.y;
432 pointer_moved = TRUE;
437 g_mutex_lock (&xvimagesink->flow_lock);
438 g_mutex_lock (&xvimagesink->context->lock);
442 g_mutex_unlock (&xvimagesink->context->lock);
443 g_mutex_unlock (&xvimagesink->flow_lock);
445 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
446 pointer_x, pointer_y);
447 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
448 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
450 g_mutex_lock (&xvimagesink->flow_lock);
451 g_mutex_lock (&xvimagesink->context->lock);
454 /* We get all events on our window to throw them upstream */
455 while (XCheckWindowEvent (xvimagesink->context->disp,
456 xvimagesink->xwindow->win,
457 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
460 const char *key_str = NULL;
462 /* We lock only for the X function call */
463 g_mutex_unlock (&xvimagesink->context->lock);
464 g_mutex_unlock (&xvimagesink->flow_lock);
468 /* Mouse button pressed over our window. We send upstream
469 events for interactivity/navigation */
470 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
471 e.xbutton.button, e.xbutton.x, e.xbutton.y);
472 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
473 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
476 /* Mouse button released over our window. We send upstream
477 events for interactivity/navigation */
478 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
479 e.xbutton.button, e.xbutton.x, e.xbutton.y);
480 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
481 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
485 /* Key pressed/released over our window. We send upstream
486 events for interactivity/navigation */
487 g_mutex_lock (&xvimagesink->context->lock);
488 keysym = XkbKeycodeToKeysym (xvimagesink->context->disp,
489 e.xkey.keycode, 0, 0);
490 if (keysym != NoSymbol) {
491 key_str = XKeysymToString (keysym);
495 g_mutex_unlock (&xvimagesink->context->lock);
496 GST_DEBUG_OBJECT (xvimagesink,
497 "key %d pressed over window at %d,%d (%s)",
498 e.xkey.keycode, e.xkey.x, e.xkey.y, key_str);
499 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
500 e.type == KeyPress ? "key-press" : "key-release", key_str);
503 GST_DEBUG_OBJECT (xvimagesink, "xvimagesink unhandled X event (%d)",
506 g_mutex_lock (&xvimagesink->flow_lock);
507 g_mutex_lock (&xvimagesink->context->lock);
511 while (XCheckWindowEvent (xvimagesink->context->disp,
512 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
517 case ConfigureNotify:
518 g_mutex_unlock (&xvimagesink->context->lock);
519 g_mutex_unlock (&xvimagesink->flow_lock);
521 gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
523 g_mutex_lock (&xvimagesink->flow_lock);
524 g_mutex_lock (&xvimagesink->context->lock);
532 if (xvimagesink->handle_expose && (exposed || configured)) {
533 g_mutex_unlock (&xvimagesink->context->lock);
534 g_mutex_unlock (&xvimagesink->flow_lock);
536 gst_xv_image_sink_expose (GST_VIDEO_OVERLAY (xvimagesink));
538 g_mutex_lock (&xvimagesink->flow_lock);
539 g_mutex_lock (&xvimagesink->context->lock);
542 /* Handle Display events */
543 while (XPending (xvimagesink->context->disp)) {
544 XNextEvent (xvimagesink->context->disp, &e);
550 wm_delete = XInternAtom (xvimagesink->context->disp,
551 "WM_DELETE_WINDOW", True);
552 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
553 /* Handle window deletion by posting an error on the bus */
554 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
555 ("Output window was closed"), (NULL));
557 g_mutex_unlock (&xvimagesink->context->lock);
558 gst_xwindow_destroy (xvimagesink->xwindow);
559 xvimagesink->xwindow = NULL;
560 g_mutex_lock (&xvimagesink->context->lock);
569 g_mutex_unlock (&xvimagesink->context->lock);
570 g_mutex_unlock (&xvimagesink->flow_lock);
574 gst_xv_image_sink_event_thread (GstXvImageSink * xvimagesink)
576 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
578 GST_OBJECT_LOCK (xvimagesink);
579 while (xvimagesink->running) {
580 GST_OBJECT_UNLOCK (xvimagesink);
582 if (xvimagesink->xwindow) {
583 gst_xv_image_sink_handle_xevents (xvimagesink);
585 /* FIXME: do we want to align this with the framerate or anything else? */
586 g_usleep (G_USEC_PER_SEC / 20);
588 GST_OBJECT_LOCK (xvimagesink);
590 GST_OBJECT_UNLOCK (xvimagesink);
596 gst_xv_image_sink_manage_event_thread (GstXvImageSink * xvimagesink)
598 GThread *thread = NULL;
600 /* don't start the thread too early */
601 if (xvimagesink->context == NULL) {
605 GST_OBJECT_LOCK (xvimagesink);
606 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
607 if (!xvimagesink->event_thread) {
608 /* Setup our event listening thread */
609 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
610 xvimagesink->handle_expose, xvimagesink->handle_events);
611 xvimagesink->running = TRUE;
612 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
613 (GThreadFunc) gst_xv_image_sink_event_thread, xvimagesink, NULL);
616 if (xvimagesink->event_thread) {
617 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
618 xvimagesink->handle_expose, xvimagesink->handle_events);
619 xvimagesink->running = FALSE;
620 /* grab thread and mark it as NULL */
621 thread = xvimagesink->event_thread;
622 xvimagesink->event_thread = NULL;
625 GST_OBJECT_UNLOCK (xvimagesink);
627 /* Wait for our event thread to finish */
629 g_thread_join (thread);
636 gst_xv_image_sink_getcaps (GstBaseSink * bsink, GstCaps * filter)
638 GstXvImageSink *xvimagesink;
641 xvimagesink = GST_XV_IMAGE_SINK (bsink);
643 if (xvimagesink->context) {
645 return gst_caps_intersect_full (filter, xvimagesink->context->caps,
646 GST_CAPS_INTERSECT_FIRST);
648 return gst_caps_ref (xvimagesink->context->caps);
651 caps = gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD (xvimagesink));
653 GstCaps *intersection;
656 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
657 gst_caps_unref (caps);
663 static GstBufferPool *
664 gst_xv_image_sink_create_pool (GstXvImageSink * xvimagesink, GstCaps * caps,
665 gsize size, gint min)
668 GstStructure *config;
670 pool = gst_xvimage_buffer_pool_new (xvimagesink->allocator);
672 config = gst_buffer_pool_get_config (pool);
673 gst_buffer_pool_config_set_params (config, caps, size, min, 0);
675 if (!gst_buffer_pool_set_config (pool, config))
682 GST_ERROR_OBJECT (xvimagesink, "failed to set config.");
683 gst_object_unref (pool);
689 gst_xv_image_sink_setcaps (GstBaseSink * bsink, GstCaps * caps)
691 GstXvImageSink *xvimagesink;
692 GstXvContext *context;
693 GstBufferPool *newpool, *oldpool;
695 guint32 im_format = 0;
696 gint video_par_n, video_par_d; /* video's PAR */
697 gint display_par_n, display_par_d; /* display's PAR */
700 xvimagesink = GST_XV_IMAGE_SINK (bsink);
701 context = xvimagesink->context;
703 GST_DEBUG_OBJECT (xvimagesink,
704 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
705 GST_PTR_FORMAT, context->caps, caps);
707 if (!gst_caps_can_intersect (context->caps, caps))
708 goto incompatible_caps;
710 if (!gst_video_info_from_caps (&info, caps))
713 xvimagesink->fps_n = info.fps_n;
714 xvimagesink->fps_d = info.fps_d;
716 xvimagesink->video_width = info.width;
717 xvimagesink->video_height = info.height;
719 im_format = gst_xvcontext_get_format_from_info (context, &info);
723 gst_xvcontext_set_colorimetry (context, &info.colorimetry);
725 /* get aspect ratio from caps if it's present, and
726 * convert video width and height to a display width and height
727 * using wd / hd = wv / hv * PARv / PARd */
729 /* get video's PAR */
730 video_par_n = info.par_n;
731 video_par_d = info.par_d;
733 /* get display's PAR */
734 if (xvimagesink->par) {
735 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
736 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
742 if (!gst_video_calculate_display_ratio (&num, &den, info.width,
743 info.height, video_par_n, video_par_d, display_par_n, display_par_d))
746 GST_DEBUG_OBJECT (xvimagesink,
747 "video width/height: %dx%d, calculated display ratio: %d/%d",
748 info.width, info.height, num, den);
750 /* now find a width x height that respects this display ratio.
751 * prefer those that have one of w/h the same as the incoming video
752 * using wd / hd = num / den */
754 /* start with same height, because of interlaced video */
755 /* check hd / den is an integer scale factor, and scale wd with the PAR */
756 if (info.height % den == 0) {
757 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
758 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
759 gst_util_uint64_scale_int (info.height, num, den);
760 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
761 } else if (info.width % num == 0) {
762 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
763 GST_VIDEO_SINK_WIDTH (xvimagesink) = info.width;
764 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
765 gst_util_uint64_scale_int (info.width, den, num);
767 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
768 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
769 gst_util_uint64_scale_int (info.height, num, den);
770 GST_VIDEO_SINK_HEIGHT (xvimagesink) = info.height;
772 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
773 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
775 /* Notify application to set xwindow id now */
776 g_mutex_lock (&xvimagesink->flow_lock);
777 if (!xvimagesink->xwindow) {
778 g_mutex_unlock (&xvimagesink->flow_lock);
779 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (xvimagesink));
781 g_mutex_unlock (&xvimagesink->flow_lock);
784 /* Creating our window and our image with the display size in pixels */
785 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
786 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
787 goto no_display_size;
789 g_mutex_lock (&xvimagesink->flow_lock);
790 if (!xvimagesink->xwindow) {
791 xvimagesink->xwindow = gst_xv_image_sink_xwindow_new (xvimagesink,
792 GST_VIDEO_SINK_WIDTH (xvimagesink),
793 GST_VIDEO_SINK_HEIGHT (xvimagesink));
796 if (xvimagesink->pending_render_rect) {
797 xvimagesink->pending_render_rect = FALSE;
798 gst_xwindow_set_render_rectangle (xvimagesink->xwindow,
799 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
800 xvimagesink->render_rect.w, xvimagesink->render_rect.h);
803 xvimagesink->info = info;
805 /* After a resize, we want to redraw the borders in case the new frame size
806 * doesn't cover the same area */
807 xvimagesink->redraw_border = TRUE;
809 /* create a new pool for the new configuration */
810 newpool = gst_xv_image_sink_create_pool (xvimagesink, caps, info.size, 2);
812 /* we don't activate the internal pool yet as it may not be needed */
813 oldpool = xvimagesink->pool;
814 xvimagesink->pool = newpool;
815 g_mutex_unlock (&xvimagesink->flow_lock);
817 /* deactivate and unref the old internal pool */
819 gst_buffer_pool_set_active (oldpool, FALSE);
820 gst_object_unref (oldpool);
828 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
833 GST_DEBUG_OBJECT (xvimagesink,
834 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
839 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
840 ("Error calculating the output display ratio of the video."));
845 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
846 ("Error calculating the output display size of the video."));
851 static GstStateChangeReturn
852 gst_xv_image_sink_change_state (GstElement * element, GstStateChange transition)
854 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
855 GstXvImageSink *xvimagesink;
857 xvimagesink = GST_XV_IMAGE_SINK (element);
859 switch (transition) {
860 case GST_STATE_CHANGE_NULL_TO_READY:
861 if (!gst_xv_image_sink_open (xvimagesink))
864 case GST_STATE_CHANGE_READY_TO_PAUSED:
866 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
868 case GST_STATE_CHANGE_PAUSED_TO_READY:
874 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
876 switch (transition) {
877 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
879 case GST_STATE_CHANGE_PAUSED_TO_READY:
880 xvimagesink->fps_n = 0;
881 xvimagesink->fps_d = 1;
882 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
883 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
884 g_mutex_lock (&xvimagesink->flow_lock);
885 if (xvimagesink->pool)
886 gst_buffer_pool_set_active (xvimagesink->pool, FALSE);
887 g_mutex_unlock (&xvimagesink->flow_lock);
889 case GST_STATE_CHANGE_READY_TO_NULL:
890 gst_xv_image_sink_close (xvimagesink);
899 return GST_STATE_CHANGE_FAILURE;
904 gst_xv_image_sink_get_times (GstBaseSink * bsink, GstBuffer * buf,
905 GstClockTime * start, GstClockTime * end)
907 GstXvImageSink *xvimagesink;
909 xvimagesink = GST_XV_IMAGE_SINK (bsink);
911 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
912 *start = GST_BUFFER_TIMESTAMP (buf);
913 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
914 *end = *start + GST_BUFFER_DURATION (buf);
916 if (xvimagesink->fps_n > 0) {
918 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
926 gst_xv_image_sink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
929 GstXvImageSink *xvimagesink;
930 GstBuffer *to_put = NULL;
933 xvimagesink = GST_XV_IMAGE_SINK (vsink);
935 if (gst_buffer_n_memory (buf) == 1 && (mem = gst_buffer_peek_memory (buf, 0))
936 && gst_xvimage_memory_is_from_context (mem, xvimagesink->context)) {
937 /* If this buffer has been allocated using our buffer management we simply
938 put the ximage which is in the PRIVATE pointer */
939 GST_LOG_OBJECT (xvimagesink, "buffer %p from our pool, writing directly",
944 GstVideoFrame src, dest;
945 GstBufferPoolAcquireParams params = { 0, };
947 /* Else we have to copy the data into our private image, */
948 /* if we have one... */
949 GST_LOG_OBJECT (xvimagesink, "buffer %p not from our pool, copying", buf);
951 /* we should have a pool, configured in setcaps */
952 if (xvimagesink->pool == NULL)
955 if (!gst_buffer_pool_set_active (xvimagesink->pool, TRUE))
956 goto activate_failed;
958 /* take a buffer from our pool, if there is no buffer in the pool something
959 * is seriously wrong, waiting for the pool here might deadlock when we try
960 * to go to PAUSED because we never flush the pool then. */
961 params.flags = GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT;
962 res = gst_buffer_pool_acquire_buffer (xvimagesink->pool, &to_put, ¶ms);
963 if (res != GST_FLOW_OK)
966 GST_CAT_LOG_OBJECT (CAT_PERFORMANCE, xvimagesink,
967 "slow copy buffer %p into bufferpool buffer %p", buf, to_put);
969 if (!gst_video_frame_map (&src, &xvimagesink->info, buf, GST_MAP_READ))
972 if (!gst_video_frame_map (&dest, &xvimagesink->info, to_put, GST_MAP_WRITE)) {
973 gst_video_frame_unmap (&src);
977 gst_video_frame_copy (&dest, &src);
979 gst_video_frame_unmap (&dest);
980 gst_video_frame_unmap (&src);
983 if (!gst_xv_image_sink_xvimage_put (xvimagesink, to_put))
988 gst_buffer_unref (to_put);
995 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
996 ("Internal error: can't allocate images"),
997 ("We don't have a bufferpool negotiated"));
998 return GST_FLOW_ERROR;
1002 /* No image available. That's very bad ! */
1003 GST_WARNING_OBJECT (xvimagesink, "could not create image");
1008 /* No Window available to put our image into */
1009 GST_WARNING_OBJECT (xvimagesink, "could not map image");
1015 /* No Window available to put our image into */
1016 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
1017 res = GST_FLOW_ERROR;
1022 GST_ERROR_OBJECT (xvimagesink, "failed to activate bufferpool.");
1023 res = GST_FLOW_ERROR;
1029 gst_xv_image_sink_event (GstBaseSink * sink, GstEvent * event)
1031 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (sink);
1033 switch (GST_EVENT_TYPE (event)) {
1034 case GST_EVENT_TAG:{
1036 gchar *title = NULL;
1038 gst_event_parse_tag (event, &l);
1039 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
1042 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
1043 gst_xv_image_sink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
1053 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
1057 gst_xv_image_sink_propose_allocation (GstBaseSink * bsink, GstQuery * query)
1059 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (bsink);
1060 GstBufferPool *pool = NULL;
1066 gst_query_parse_allocation (query, &caps, &need_pool);
1071 if (!gst_video_info_from_caps (&info, caps))
1074 /* the normal size of a frame */
1078 GST_DEBUG_OBJECT (xvimagesink, "create new pool");
1079 pool = gst_xv_image_sink_create_pool (xvimagesink, caps, info.size, 0);
1085 /* we need at least 2 buffer because we hold on to the last one */
1086 gst_query_add_allocation_pool (query, pool, size, 2, 0);
1088 gst_object_unref (pool);
1090 /* we also support various metadata */
1091 gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
1092 gst_query_add_allocation_meta (query, GST_VIDEO_CROP_META_API_TYPE, NULL);
1099 GST_DEBUG_OBJECT (bsink, "no caps specified");
1104 GST_DEBUG_OBJECT (bsink, "invalid caps specified");
1109 /* Already warned in create_pool */
1114 /* Interfaces stuff */
1116 gst_xv_image_sink_navigation_send_event (GstNavigation * navigation,
1117 GstStructure * structure)
1119 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (navigation);
1120 gboolean handled = FALSE;
1121 GstEvent *event = NULL;
1123 GstVideoRectangle src = { 0, };
1124 GstVideoRectangle dst = { 0, };
1125 GstVideoRectangle result;
1126 gdouble x, y, xscale = 1.0, yscale = 1.0;
1127 GstXWindow *xwindow;
1129 /* We take the flow_lock while we look at the window */
1130 g_mutex_lock (&xvimagesink->flow_lock);
1132 if (!(xwindow = xvimagesink->xwindow)) {
1133 g_mutex_unlock (&xvimagesink->flow_lock);
1134 gst_structure_free (structure);
1138 if (xvimagesink->keep_aspect) {
1139 /* We get the frame position using the calculated geometry from _setcaps
1140 that respect pixel aspect ratios */
1141 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
1142 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
1143 dst.w = xwindow->render_rect.w;
1144 dst.h = xwindow->render_rect.h;
1146 gst_video_sink_center_rect (src, dst, &result, TRUE);
1147 result.x += xwindow->render_rect.x;
1148 result.y += xwindow->render_rect.y;
1150 memcpy (&result, &xwindow->render_rect, sizeof (GstVideoRectangle));
1153 g_mutex_unlock (&xvimagesink->flow_lock);
1155 /* We calculate scaling using the original video frames geometry to include
1156 pixel aspect ratio scaling. */
1157 xscale = (gdouble) xvimagesink->video_width / result.w;
1158 yscale = (gdouble) xvimagesink->video_height / result.h;
1160 /* Converting pointer coordinates to the non scaled geometry */
1161 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1162 x = MIN (x, result.x + result.w);
1163 x = MAX (x - result.x, 0);
1164 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
1165 (gdouble) x * xscale, NULL);
1167 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1168 y = MIN (y, result.y + result.h);
1169 y = MAX (y - result.y, 0);
1170 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
1171 (gdouble) y * yscale, NULL);
1174 event = gst_event_new_navigation (structure);
1176 gst_event_ref (event);
1177 handled = gst_pad_push_event (GST_VIDEO_SINK_PAD (xvimagesink), event);
1180 gst_element_post_message ((GstElement *) xvimagesink,
1181 gst_navigation_message_new_event ((GstObject *) xvimagesink, event));
1183 gst_event_unref (event);
1188 gst_xv_image_sink_navigation_init (GstNavigationInterface * iface)
1190 iface->send_event = gst_xv_image_sink_navigation_send_event;
1194 gst_xv_image_sink_set_window_handle (GstVideoOverlay * overlay, guintptr id)
1196 XID xwindow_id = id;
1197 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1198 GstXWindow *xwindow = NULL;
1199 GstXvContext *context;
1201 g_return_if_fail (GST_IS_XV_IMAGE_SINK (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_xv_image_sink_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_xv_image_sink_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_xv_image_sink_expose (GstVideoOverlay * overlay)
1255 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (overlay);
1257 GST_DEBUG ("doing expose");
1258 gst_xv_image_sink_xwindow_update_geometry (xvimagesink);
1259 gst_xv_image_sink_xvimage_put (xvimagesink, NULL);
1263 gst_xv_image_sink_set_event_handling (GstVideoOverlay * overlay,
1264 gboolean handle_events)
1266 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (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_xv_image_sink_set_render_rectangle (GstVideoOverlay * overlay, gint x,
1277 gint y, gint width, gint height)
1279 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (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,
1286 xvimagesink->render_rect.x = x;
1287 xvimagesink->render_rect.y = y;
1288 xvimagesink->render_rect.w = width;
1289 xvimagesink->render_rect.h = height;
1290 xvimagesink->pending_render_rect = TRUE;
1292 g_mutex_unlock (&xvimagesink->flow_lock);
1296 gst_xv_image_sink_video_overlay_init (GstVideoOverlayInterface * iface)
1298 iface->set_window_handle = gst_xv_image_sink_set_window_handle;
1299 iface->expose = gst_xv_image_sink_expose;
1300 iface->handle_events = gst_xv_image_sink_set_event_handling;
1301 iface->set_render_rectangle = gst_xv_image_sink_set_render_rectangle;
1304 static const GList *
1305 gst_xv_image_sink_colorbalance_list_channels (GstColorBalance * balance)
1307 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1309 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), NULL);
1311 if (xvimagesink->context)
1312 return xvimagesink->context->channels_list;
1318 gst_xv_image_sink_colorbalance_set_value (GstColorBalance * balance,
1319 GstColorBalanceChannel * channel, gint value)
1321 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1323 g_return_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink));
1324 g_return_if_fail (channel->label != NULL);
1326 xvimagesink->config.cb_changed = TRUE;
1328 /* Normalize val to [-1000, 1000] */
1329 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
1330 (double) (channel->max_value - channel->min_value));
1332 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1333 xvimagesink->config.hue = value;
1334 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1335 xvimagesink->config.saturation = value;
1336 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1337 xvimagesink->config.contrast = value;
1338 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1339 xvimagesink->config.brightness = value;
1341 g_warning ("got an unknown channel %s", channel->label);
1345 gst_xv_image_sink_update_colorbalance (xvimagesink);
1349 gst_xv_image_sink_colorbalance_get_value (GstColorBalance * balance,
1350 GstColorBalanceChannel * channel)
1352 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (balance);
1355 g_return_val_if_fail (GST_IS_XV_IMAGE_SINK (xvimagesink), 0);
1356 g_return_val_if_fail (channel->label != NULL, 0);
1358 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1359 value = xvimagesink->config.hue;
1360 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1361 value = xvimagesink->config.saturation;
1362 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1363 value = xvimagesink->config.contrast;
1364 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1365 value = xvimagesink->config.brightness;
1367 g_warning ("got an unknown channel %s", channel->label);
1370 /* Normalize val to [channel->min_value, channel->max_value] */
1371 value = channel->min_value + (channel->max_value - channel->min_value) *
1372 (value + 1000) / 2000;
1377 static GstColorBalanceType
1378 gst_xv_image_sink_colorbalance_get_balance_type (GstColorBalance * balance)
1380 return GST_COLOR_BALANCE_HARDWARE;
1384 gst_xv_image_sink_colorbalance_init (GstColorBalanceInterface * iface)
1386 iface->list_channels = gst_xv_image_sink_colorbalance_list_channels;
1387 iface->set_value = gst_xv_image_sink_colorbalance_set_value;
1388 iface->get_value = gst_xv_image_sink_colorbalance_get_value;
1389 iface->get_balance_type = gst_xv_image_sink_colorbalance_get_balance_type;
1393 static const GList *
1394 gst_xv_image_sink_probe_get_properties (GstPropertyProbe * probe)
1396 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
1397 static GList *list = NULL;
1400 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
1402 g_list_append (list, g_object_class_find_property (klass,
1403 "autopaint-colorkey"));
1405 g_list_append (list, g_object_class_find_property (klass,
1408 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
1415 gst_xv_image_sink_probe_probe_property (GstPropertyProbe * probe,
1416 guint prop_id, const GParamSpec * pspec)
1418 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1422 case PROP_AUTOPAINT_COLORKEY:
1423 case PROP_DOUBLE_BUFFER:
1425 GST_DEBUG_OBJECT (xvimagesink,
1426 "probing device list and get capabilities");
1427 if (!xvimagesink->context) {
1428 GST_DEBUG_OBJECT (xvimagesink, "generating context");
1429 xvimagesink->context = gst_xv_image_sink_context_get (xvimagesink);
1433 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1439 gst_xv_image_sink_probe_needs_probe (GstPropertyProbe * probe,
1440 guint prop_id, const GParamSpec * pspec)
1442 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1443 gboolean ret = FALSE;
1447 case PROP_AUTOPAINT_COLORKEY:
1448 case PROP_DOUBLE_BUFFER:
1450 if (xvimagesink->context != NULL) {
1457 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1464 static GValueArray *
1465 gst_xv_image_sink_probe_get_values (GstPropertyProbe * probe,
1466 guint prop_id, const GParamSpec * pspec)
1468 GstXvImageSink *xvimagesink = GST_XV_IMAGE_SINK (probe);
1469 GValueArray *array = NULL;
1471 if (G_UNLIKELY (!xvimagesink->context)) {
1472 GST_WARNING_OBJECT (xvimagesink, "we don't have any context, can't "
1481 GValue value = { 0 };
1483 array = g_value_array_new (xvimagesink->context->nb_adaptors);
1484 g_value_init (&value, G_TYPE_STRING);
1486 for (i = 0; i < xvimagesink->context->nb_adaptors; i++) {
1487 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
1489 g_value_set_string (&value, adaptor_id_s);
1490 g_value_array_append (array, &value);
1491 g_free (adaptor_id_s);
1493 g_value_unset (&value);
1496 case PROP_AUTOPAINT_COLORKEY:
1497 if (xvimagesink->have_autopaint_colorkey) {
1498 GValue value = { 0 };
1500 array = g_value_array_new (2);
1501 g_value_init (&value, G_TYPE_BOOLEAN);
1502 g_value_set_boolean (&value, FALSE);
1503 g_value_array_append (array, &value);
1504 g_value_set_boolean (&value, TRUE);
1505 g_value_array_append (array, &value);
1506 g_value_unset (&value);
1509 case PROP_DOUBLE_BUFFER:
1510 if (xvimagesink->have_double_buffer) {
1511 GValue value = { 0 };
1513 array = g_value_array_new (2);
1514 g_value_init (&value, G_TYPE_BOOLEAN);
1515 g_value_set_boolean (&value, FALSE);
1516 g_value_array_append (array, &value);
1517 g_value_set_boolean (&value, TRUE);
1518 g_value_array_append (array, &value);
1519 g_value_unset (&value);
1523 if (xvimagesink->have_colorkey) {
1524 GValue value = { 0 };
1526 array = g_value_array_new (1);
1527 g_value_init (&value, GST_TYPE_INT_RANGE);
1528 gst_value_set_int_range (&value, 0, 0xffffff);
1529 g_value_array_append (array, &value);
1530 g_value_unset (&value);
1534 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
1543 gst_xv_image_sink_property_probe_interface_init (GstPropertyProbeInterface *
1546 iface->get_properties = gst_xv_image_sink_probe_get_properties;
1547 iface->probe_property = gst_xv_image_sink_probe_probe_property;
1548 iface->needs_probe = gst_xv_image_sink_probe_needs_probe;
1549 iface->get_values = gst_xv_image_sink_probe_get_values;
1553 /* =========================================== */
1555 /* Init & Class init */
1557 /* =========================================== */
1560 gst_xv_image_sink_set_property (GObject * object, guint prop_id,
1561 const GValue * value, GParamSpec * pspec)
1563 GstXvImageSink *xvimagesink;
1565 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1567 xvimagesink = GST_XV_IMAGE_SINK (object);
1571 xvimagesink->config.hue = g_value_get_int (value);
1572 xvimagesink->config.cb_changed = TRUE;
1573 gst_xv_image_sink_update_colorbalance (xvimagesink);
1576 xvimagesink->config.contrast = g_value_get_int (value);
1577 xvimagesink->config.cb_changed = TRUE;
1578 gst_xv_image_sink_update_colorbalance (xvimagesink);
1580 case PROP_BRIGHTNESS:
1581 xvimagesink->config.brightness = g_value_get_int (value);
1582 xvimagesink->config.cb_changed = TRUE;
1583 gst_xv_image_sink_update_colorbalance (xvimagesink);
1585 case PROP_SATURATION:
1586 xvimagesink->config.saturation = g_value_get_int (value);
1587 xvimagesink->config.cb_changed = TRUE;
1588 gst_xv_image_sink_update_colorbalance (xvimagesink);
1591 g_free (xvimagesink->config.display_name);
1592 xvimagesink->config.display_name = g_strdup (g_value_get_string (value));
1594 case PROP_SYNCHRONOUS:
1595 xvimagesink->synchronous = g_value_get_boolean (value);
1596 if (xvimagesink->context) {
1597 gst_xvcontext_set_synchronous (xvimagesink->context,
1598 xvimagesink->synchronous);
1601 case PROP_PIXEL_ASPECT_RATIO:
1602 g_free (xvimagesink->par);
1603 xvimagesink->par = g_new0 (GValue, 1);
1604 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
1605 if (!g_value_transform (value, xvimagesink->par)) {
1606 g_warning ("Could not transform string to aspect ratio");
1607 gst_value_set_fraction (xvimagesink->par, 1, 1);
1609 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
1610 gst_value_get_fraction_numerator (xvimagesink->par),
1611 gst_value_get_fraction_denominator (xvimagesink->par));
1613 case PROP_FORCE_ASPECT_RATIO:
1614 xvimagesink->keep_aspect = g_value_get_boolean (value);
1616 case PROP_HANDLE_EVENTS:
1617 gst_xv_image_sink_set_event_handling (GST_VIDEO_OVERLAY (xvimagesink),
1618 g_value_get_boolean (value));
1619 gst_xv_image_sink_manage_event_thread (xvimagesink);
1622 xvimagesink->config.adaptor_nr = atoi (g_value_get_string (value));
1624 case PROP_HANDLE_EXPOSE:
1625 xvimagesink->handle_expose = g_value_get_boolean (value);
1626 gst_xv_image_sink_manage_event_thread (xvimagesink);
1628 case PROP_DOUBLE_BUFFER:
1629 xvimagesink->double_buffer = g_value_get_boolean (value);
1631 case PROP_AUTOPAINT_COLORKEY:
1632 xvimagesink->config.autopaint_colorkey = g_value_get_boolean (value);
1635 xvimagesink->config.colorkey = g_value_get_int (value);
1637 case PROP_DRAW_BORDERS:
1638 xvimagesink->draw_borders = g_value_get_boolean (value);
1641 if (!gst_video_overlay_set_property (object, PROP_LAST, prop_id, value))
1642 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1648 gst_xv_image_sink_get_property (GObject * object, guint prop_id,
1649 GValue * value, GParamSpec * pspec)
1651 GstXvImageSink *xvimagesink;
1653 g_return_if_fail (GST_IS_XV_IMAGE_SINK (object));
1655 xvimagesink = GST_XV_IMAGE_SINK (object);
1659 g_value_set_int (value, xvimagesink->config.hue);
1662 g_value_set_int (value, xvimagesink->config.contrast);
1664 case PROP_BRIGHTNESS:
1665 g_value_set_int (value, xvimagesink->config.brightness);
1667 case PROP_SATURATION:
1668 g_value_set_int (value, xvimagesink->config.saturation);
1671 g_value_set_string (value, xvimagesink->config.display_name);
1673 case PROP_SYNCHRONOUS:
1674 g_value_set_boolean (value, xvimagesink->synchronous);
1676 case PROP_PIXEL_ASPECT_RATIO:
1677 if (xvimagesink->par)
1678 g_value_transform (xvimagesink->par, value);
1680 case PROP_FORCE_ASPECT_RATIO:
1681 g_value_set_boolean (value, xvimagesink->keep_aspect);
1683 case PROP_HANDLE_EVENTS:
1684 g_value_set_boolean (value, xvimagesink->handle_events);
1688 char *adaptor_nr_s =
1689 g_strdup_printf ("%u", xvimagesink->config.adaptor_nr);
1691 g_value_set_string (value, adaptor_nr_s);
1692 g_free (adaptor_nr_s);
1695 case PROP_DEVICE_NAME:
1696 if (xvimagesink->context && xvimagesink->context->adaptors) {
1697 g_value_set_string (value,
1698 xvimagesink->context->adaptors[xvimagesink->config.adaptor_nr]);
1700 g_value_set_string (value, NULL);
1703 case PROP_HANDLE_EXPOSE:
1704 g_value_set_boolean (value, xvimagesink->handle_expose);
1706 case PROP_DOUBLE_BUFFER:
1707 g_value_set_boolean (value, xvimagesink->double_buffer);
1709 case PROP_AUTOPAINT_COLORKEY:
1710 g_value_set_boolean (value, xvimagesink->config.autopaint_colorkey);
1713 g_value_set_int (value, xvimagesink->config.colorkey);
1715 case PROP_DRAW_BORDERS:
1716 g_value_set_boolean (value, xvimagesink->draw_borders);
1718 case PROP_WINDOW_WIDTH:
1719 if (xvimagesink->xwindow)
1720 g_value_set_uint64 (value, xvimagesink->xwindow->width);
1722 g_value_set_uint64 (value, 0);
1724 case PROP_WINDOW_HEIGHT:
1725 if (xvimagesink->xwindow)
1726 g_value_set_uint64 (value, xvimagesink->xwindow->height);
1728 g_value_set_uint64 (value, 0);
1731 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1737 gst_xv_image_sink_open (GstXvImageSink * xvimagesink)
1739 GError *error = NULL;
1741 /* Initializing the XvContext unless already done through GstVideoOverlay */
1742 if (!xvimagesink->context) {
1743 GstXvContext *context;
1744 if (!(context = gst_xvcontext_new (&xvimagesink->config, &error)))
1747 GST_OBJECT_LOCK (xvimagesink);
1748 xvimagesink->context = context;
1750 GST_OBJECT_LOCK (xvimagesink);
1751 /* make an allocator for this context */
1752 xvimagesink->allocator = gst_xvimage_allocator_new (xvimagesink->context);
1753 GST_OBJECT_UNLOCK (xvimagesink);
1755 /* update object's par with calculated one if not set yet */
1756 if (!xvimagesink->par) {
1757 xvimagesink->par = g_new0 (GValue, 1);
1758 gst_value_init_and_copy (xvimagesink->par, xvimagesink->context->par);
1759 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
1761 /* call XSynchronize with the current value of synchronous */
1762 gst_xvcontext_set_synchronous (xvimagesink->context,
1763 xvimagesink->synchronous);
1764 gst_xv_image_sink_update_colorbalance (xvimagesink);
1765 gst_xv_image_sink_manage_event_thread (xvimagesink);
1771 gst_element_message_full (GST_ELEMENT (xvimagesink), GST_MESSAGE_ERROR,
1772 error->domain, error->code, g_strdup ("Could not initialise Xv output"),
1773 g_strdup (error->message), __FILE__, GST_FUNCTION, __LINE__);
1774 g_clear_error (&error);
1780 gst_xv_image_sink_close (GstXvImageSink * xvimagesink)
1783 GstXvContext *context;
1785 GST_OBJECT_LOCK (xvimagesink);
1786 xvimagesink->running = FALSE;
1787 /* grab thread and mark it as NULL */
1788 thread = xvimagesink->event_thread;
1789 xvimagesink->event_thread = NULL;
1790 GST_OBJECT_UNLOCK (xvimagesink);
1792 /* Wait for our event thread to finish before we clean up our stuff. */
1794 g_thread_join (thread);
1796 if (xvimagesink->cur_image) {
1797 gst_buffer_unref (xvimagesink->cur_image);
1798 xvimagesink->cur_image = NULL;
1801 g_mutex_lock (&xvimagesink->flow_lock);
1803 if (xvimagesink->pool) {
1804 gst_object_unref (xvimagesink->pool);
1805 xvimagesink->pool = NULL;
1808 if (xvimagesink->xwindow) {
1809 gst_xwindow_clear (xvimagesink->xwindow);
1810 gst_xwindow_destroy (xvimagesink->xwindow);
1811 xvimagesink->xwindow = NULL;
1813 g_mutex_unlock (&xvimagesink->flow_lock);
1815 if (xvimagesink->allocator) {
1816 gst_object_unref (xvimagesink->allocator);
1817 xvimagesink->allocator = NULL;
1820 GST_OBJECT_LOCK (xvimagesink);
1821 /* grab context and mark it as NULL */
1822 context = xvimagesink->context;
1823 xvimagesink->context = NULL;
1824 GST_OBJECT_UNLOCK (xvimagesink);
1827 gst_xvcontext_unref (context);
1830 /* Finalize is called only once, dispose can be called multiple times.
1831 * We use mutexes and don't reset stuff to NULL here so let's register
1834 gst_xv_image_sink_finalize (GObject * object)
1836 GstXvImageSink *xvimagesink;
1838 xvimagesink = GST_XV_IMAGE_SINK (object);
1840 gst_xv_image_sink_close (xvimagesink);
1842 gst_xvcontext_config_clear (&xvimagesink->config);
1844 if (xvimagesink->par) {
1845 g_free (xvimagesink->par);
1846 xvimagesink->par = NULL;
1848 g_mutex_clear (&xvimagesink->flow_lock);
1849 g_free (xvimagesink->media_title);
1851 G_OBJECT_CLASS (parent_class)->finalize (object);
1855 gst_xv_image_sink_init (GstXvImageSink * xvimagesink)
1857 xvimagesink->config.display_name = NULL;
1858 xvimagesink->config.adaptor_nr = 0;
1859 xvimagesink->config.autopaint_colorkey = TRUE;
1860 xvimagesink->config.double_buffer = TRUE;
1861 /* on 16bit displays this becomes r,g,b = 1,2,3
1862 * on 24bit displays this becomes r,g,b = 8,8,16
1863 * as a port atom value */
1864 xvimagesink->config.colorkey = (8 << 16) | (8 << 8) | 16;
1865 xvimagesink->config.hue = xvimagesink->config.saturation = 0;
1866 xvimagesink->config.contrast = xvimagesink->config.brightness = 0;
1867 xvimagesink->config.cb_changed = FALSE;
1869 xvimagesink->context = NULL;
1870 xvimagesink->xwindow = NULL;
1871 xvimagesink->cur_image = NULL;
1873 xvimagesink->fps_n = 0;
1874 xvimagesink->fps_d = 0;
1875 xvimagesink->video_width = 0;
1876 xvimagesink->video_height = 0;
1878 g_mutex_init (&xvimagesink->flow_lock);
1880 xvimagesink->pool = NULL;
1882 xvimagesink->synchronous = FALSE;
1883 xvimagesink->running = FALSE;
1884 xvimagesink->keep_aspect = TRUE;
1885 xvimagesink->handle_events = TRUE;
1886 xvimagesink->par = NULL;
1887 xvimagesink->handle_expose = TRUE;
1889 xvimagesink->draw_borders = TRUE;
1893 gst_xv_image_sink_class_init (GstXvImageSinkClass * klass)
1895 GObjectClass *gobject_class;
1896 GstElementClass *gstelement_class;
1897 GstBaseSinkClass *gstbasesink_class;
1898 GstVideoSinkClass *videosink_class;
1900 gobject_class = (GObjectClass *) klass;
1901 gstelement_class = (GstElementClass *) klass;
1902 gstbasesink_class = (GstBaseSinkClass *) klass;
1903 videosink_class = (GstVideoSinkClass *) klass;
1905 parent_class = g_type_class_peek_parent (klass);
1907 gobject_class->set_property = gst_xv_image_sink_set_property;
1908 gobject_class->get_property = gst_xv_image_sink_get_property;
1910 g_object_class_install_property (gobject_class, PROP_CONTRAST,
1911 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
1912 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1913 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
1914 g_param_spec_int ("brightness", "Brightness",
1915 "The brightness of the video", -1000, 1000, 0,
1916 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1917 g_object_class_install_property (gobject_class, PROP_HUE,
1918 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
1919 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1920 g_object_class_install_property (gobject_class, PROP_SATURATION,
1921 g_param_spec_int ("saturation", "Saturation",
1922 "The saturation of the video", -1000, 1000, 0,
1923 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1924 g_object_class_install_property (gobject_class, PROP_DISPLAY,
1925 g_param_spec_string ("display", "Display", "X Display name", NULL,
1926 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1927 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
1928 g_param_spec_boolean ("synchronous", "Synchronous",
1929 "When enabled, runs the X display in synchronous mode. "
1930 "(unrelated to A/V sync, used only for debugging)", FALSE,
1931 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1932 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
1933 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
1934 "The pixel aspect ratio of the device", "1/1",
1935 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1936 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
1937 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
1938 "When enabled, scaling will respect original aspect ratio", TRUE,
1939 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1940 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
1941 g_param_spec_boolean ("handle-events", "Handle XEvents",
1942 "When enabled, XEvents will be selected and handled", TRUE,
1943 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1944 g_object_class_install_property (gobject_class, PROP_DEVICE,
1945 g_param_spec_string ("device", "Adaptor number",
1946 "The number of the video adaptor", "0",
1947 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1948 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
1949 g_param_spec_string ("device-name", "Adaptor name",
1950 "The name of the video adaptor", NULL,
1951 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
1953 gst_video_overlay_install_properties (gobject_class, PROP_LAST);
1956 * GstXvImageSink:handle-expose
1958 * When enabled, the current frame will always be drawn in response to X
1961 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
1962 g_param_spec_boolean ("handle-expose", "Handle expose",
1964 "the current frame will always be drawn in response to X Expose "
1965 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1968 * GstXvImageSink:double-buffer
1970 * Whether to double-buffer the output.
1972 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
1973 g_param_spec_boolean ("double-buffer", "Double-buffer",
1974 "Whether to double-buffer the output", TRUE,
1975 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1977 * GstXvImageSink:autopaint-colorkey
1979 * Whether to autofill overlay with colorkey
1981 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
1982 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
1983 "Whether to autofill overlay with colorkey", TRUE,
1984 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1986 * GstXvImageSink:colorkey
1988 * Color to use for the overlay mask.
1990 g_object_class_install_property (gobject_class, PROP_COLORKEY,
1991 g_param_spec_int ("colorkey", "Colorkey",
1992 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
1993 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1996 * GstXvImageSink:draw-borders
1998 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
1999 * unused parts of the video area.
2001 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
2002 g_param_spec_boolean ("draw-borders", "Draw Borders",
2003 "Draw black borders to fill unused area in force-aspect-ratio mode",
2004 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2007 * GstXvImageSink:window-width
2009 * Actual width of the video window.
2011 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
2012 g_param_spec_uint64 ("window-width", "window-width",
2013 "Width of the window", 0, G_MAXUINT64, 0,
2014 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2017 * GstXvImageSink:window-height
2019 * Actual height of the video window.
2021 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
2022 g_param_spec_uint64 ("window-height", "window-height",
2023 "Height of the window", 0, G_MAXUINT64, 0,
2024 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
2026 gobject_class->finalize = gst_xv_image_sink_finalize;
2028 gst_element_class_set_static_metadata (gstelement_class,
2029 "Video sink", "Sink/Video",
2030 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
2032 gst_element_class_add_static_pad_template (gstelement_class,
2033 &gst_xv_image_sink_sink_template_factory);
2035 gstelement_class->change_state =
2036 GST_DEBUG_FUNCPTR (gst_xv_image_sink_change_state);
2038 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_getcaps);
2039 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xv_image_sink_setcaps);
2040 gstbasesink_class->get_times =
2041 GST_DEBUG_FUNCPTR (gst_xv_image_sink_get_times);
2042 gstbasesink_class->propose_allocation =
2043 GST_DEBUG_FUNCPTR (gst_xv_image_sink_propose_allocation);
2044 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xv_image_sink_event);
2046 videosink_class->show_frame =
2047 GST_DEBUG_FUNCPTR (gst_xv_image_sink_show_frame);