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., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, 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 XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/xoverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/video.h>
125 #include "xvimagesink.h"
127 /* Debugging category */
128 #include <gst/gstinfo.h>
130 #include "gst/glib-compat-private.h"
132 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
133 #define GST_CAT_DEFAULT gst_debug_xvimagesink
134 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
139 unsigned long functions;
140 unsigned long decorations;
142 unsigned long status;
144 MotifWmHints, MwmHints;
146 #define MWM_HINTS_DECORATIONS (1L << 1)
148 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
150 static GstBufferClass *xvimage_buffer_parent_class = NULL;
151 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
153 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
155 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
157 static void gst_xvimagesink_expose (GstXOverlay * overlay);
159 /* Default template - initiated with class struct to allow gst-register to work
161 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
162 GST_STATIC_PAD_TEMPLATE ("sink",
165 GST_STATIC_CAPS ("video/x-raw-rgb, "
166 "framerate = (fraction) [ 0, MAX ], "
167 "width = (int) [ 1, MAX ], "
168 "height = (int) [ 1, MAX ]; "
170 "framerate = (fraction) [ 0, MAX ], "
171 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
183 PROP_PIXEL_ASPECT_RATIO,
184 PROP_FORCE_ASPECT_RATIO,
190 PROP_AUTOPAINT_COLORKEY,
197 static void gst_xvimagesink_init_interfaces (GType type);
199 GST_BOILERPLATE_FULL (GstXvImageSink, gst_xvimagesink, GstVideoSink,
200 GST_TYPE_VIDEO_SINK, gst_xvimagesink_init_interfaces);
203 /* ============================================================= */
205 /* Private Methods */
207 /* ============================================================= */
209 /* xvimage buffers */
211 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
213 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
214 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
215 #define GST_XVIMAGE_BUFFER_CAST(obj) ((GstXvImageBuffer *)(obj))
217 /* This function destroys a GstXvImage handling XShm availability */
219 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
221 GstXvImageSink *xvimagesink;
223 GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
225 xvimagesink = xvimage->xvimagesink;
226 if (G_UNLIKELY (xvimagesink == NULL))
229 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
231 GST_OBJECT_LOCK (xvimagesink);
233 /* If the destroyed image is the current one we destroy our reference too */
234 if (xvimagesink->cur_image == xvimage)
235 xvimagesink->cur_image = NULL;
237 /* We might have some buffers destroyed after changing state to NULL */
238 if (xvimagesink->xcontext == NULL) {
239 GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
241 /* Need to free the shared memory segment even if the x context
242 * was already cleaned up */
243 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
244 shmdt (xvimage->SHMInfo.shmaddr);
250 g_mutex_lock (xvimagesink->x_lock);
253 if (xvimagesink->xcontext->use_xshm) {
254 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
255 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
256 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
257 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
258 XSync (xvimagesink->xcontext->disp, FALSE);
260 shmdt (xvimage->SHMInfo.shmaddr);
262 if (xvimage->xvimage)
263 XFree (xvimage->xvimage);
265 #endif /* HAVE_XSHM */
267 if (xvimage->xvimage) {
268 if (xvimage->xvimage->data) {
269 g_free (xvimage->xvimage->data);
271 XFree (xvimage->xvimage);
275 XSync (xvimagesink->xcontext->disp, FALSE);
277 g_mutex_unlock (xvimagesink->x_lock);
280 GST_OBJECT_UNLOCK (xvimagesink);
281 xvimage->xvimagesink = NULL;
282 gst_object_unref (xvimagesink);
284 GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->finalize (GST_MINI_OBJECT
291 GST_WARNING ("no sink found");
297 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
299 GstXvImageSink *xvimagesink;
302 xvimagesink = xvimage->xvimagesink;
303 if (G_UNLIKELY (xvimagesink == NULL))
306 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
308 GST_OBJECT_LOCK (xvimagesink);
309 running = xvimagesink->running;
310 GST_OBJECT_UNLOCK (xvimagesink);
312 /* If our geometry changed we can't reuse that image. */
313 if (running == FALSE) {
314 GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
315 gst_xvimage_buffer_destroy (xvimage);
316 } else if ((xvimage->width != xvimagesink->video_width) ||
317 (xvimage->height != xvimagesink->video_height)) {
318 GST_LOG_OBJECT (xvimage,
319 "destroy image as its size changed %dx%d vs current %dx%d",
320 xvimage->width, xvimage->height,
321 xvimagesink->video_width, xvimagesink->video_height);
322 gst_xvimage_buffer_destroy (xvimage);
324 /* In that case we can reuse the image and add it to our image pool. */
325 GST_LOG_OBJECT (xvimage, "recycling image in pool");
326 /* need to increment the refcount again to recycle */
327 gst_buffer_ref (GST_BUFFER_CAST (xvimage));
328 g_mutex_lock (xvimagesink->pool_lock);
329 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
331 g_mutex_unlock (xvimagesink->pool_lock);
337 GST_WARNING ("no sink found");
343 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
345 /* make sure it is not recycled */
347 xvimage->height = -1;
348 gst_buffer_unref (GST_BUFFER (xvimage));
352 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
355 xvimage->SHMInfo.shmaddr = ((void *) -1);
356 xvimage->SHMInfo.shmid = -1;
361 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
363 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
365 xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
367 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
368 gst_xvimage_buffer_finalize;
372 gst_xvimage_buffer_get_type (void)
374 static GType _gst_xvimage_buffer_type;
376 if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
377 static const GTypeInfo xvimage_buffer_info = {
378 sizeof (GstBufferClass),
381 gst_xvimage_buffer_class_init,
384 sizeof (GstXvImageBuffer),
386 (GInstanceInitFunc) gst_xvimage_buffer_init,
389 _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
390 "GstXvImageBuffer", &xvimage_buffer_info, 0);
392 return _gst_xvimage_buffer_type;
397 static gboolean error_caught = FALSE;
400 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
402 char error_msg[1024];
404 XGetErrorText (display, xevent->error_code, error_msg, 1024);
405 GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
411 /* This function checks that it is actually really possible to create an image
414 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
417 XShmSegmentInfo SHMInfo;
419 int (*handler) (Display *, XErrorEvent *);
420 gboolean result = FALSE;
421 gboolean did_attach = FALSE;
423 g_return_val_if_fail (xcontext != NULL, FALSE);
425 /* Sync to ensure any older errors are already processed */
426 XSync (xcontext->disp, FALSE);
428 /* Set defaults so we don't free these later unnecessarily */
429 SHMInfo.shmaddr = ((void *) -1);
432 /* Setting an error handler to catch failure */
433 error_caught = FALSE;
434 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
436 /* Trying to create a 1x1 picture */
437 GST_DEBUG ("XvShmCreateImage of 1x1");
438 xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
439 xcontext->im_format, NULL, 1, 1, &SHMInfo);
441 /* Might cause an error, sync to ensure it is noticed */
442 XSync (xcontext->disp, FALSE);
443 if (!xvimage || error_caught) {
444 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
447 size = xvimage->data_size;
449 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
450 if (SHMInfo.shmid == -1) {
451 GST_WARNING ("could not get shared memory of %d bytes", size);
455 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
456 if (SHMInfo.shmaddr == ((void *) -1)) {
457 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
458 /* Clean up the shared memory segment */
459 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
463 xvimage->data = SHMInfo.shmaddr;
464 SHMInfo.readOnly = FALSE;
466 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
467 GST_WARNING ("Failed to XShmAttach");
468 /* Clean up the shared memory segment */
469 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
473 /* Sync to ensure we see any errors we caused */
474 XSync (xcontext->disp, FALSE);
476 /* Delete the shared memory segment as soon as everyone is attached.
477 * This way, it will be deleted as soon as we detach later, and not
478 * leaked if we crash. */
479 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
482 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
486 /* store whether we succeeded in result */
489 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
490 "Not using shared memory.");
494 /* Sync to ensure we swallow any errors we caused and reset error_caught */
495 XSync (xcontext->disp, FALSE);
497 error_caught = FALSE;
498 XSetErrorHandler (handler);
501 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
502 SHMInfo.shmid, SHMInfo.shmseg);
503 XShmDetach (xcontext->disp, &SHMInfo);
504 XSync (xcontext->disp, FALSE);
506 if (SHMInfo.shmaddr != ((void *) -1))
507 shmdt (SHMInfo.shmaddr);
512 #endif /* HAVE_XSHM */
514 /* This function handles GstXvImage creation depending on XShm availability */
515 static GstXvImageBuffer *
516 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
518 GstXvImageBuffer *xvimage = NULL;
519 GstStructure *structure = NULL;
520 gboolean succeeded = FALSE;
521 int (*handler) (Display *, XErrorEvent *);
523 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
528 xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
529 GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
531 structure = gst_caps_get_structure (caps, 0);
533 if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
534 !gst_structure_get_int (structure, "height", &xvimage->height)) {
535 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
538 GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
541 xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
542 if (xvimage->im_format == -1) {
543 GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
544 GST_PTR_FORMAT, caps);
545 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
546 ("Failed to create output image buffer of %dx%d pixels",
547 xvimage->width, xvimage->height), ("Invalid input caps"));
550 xvimage->xvimagesink = gst_object_ref (xvimagesink);
552 g_mutex_lock (xvimagesink->x_lock);
554 /* Setting an error handler to catch failure */
555 error_caught = FALSE;
556 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
559 if (xvimagesink->xcontext->use_xshm) {
562 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
563 xvimagesink->xcontext->xv_port_id,
564 xvimage->im_format, NULL,
565 xvimage->width, xvimage->height, &xvimage->SHMInfo);
566 if (!xvimage->xvimage || error_caught) {
567 g_mutex_unlock (xvimagesink->x_lock);
569 /* Reset error flag */
570 error_caught = FALSE;
573 GST_ELEMENT_WARNING (xvimagesink, RESOURCE, WRITE,
574 ("Failed to create output image buffer of %dx%d pixels",
575 xvimage->width, xvimage->height),
576 ("could not XvShmCreateImage a %dx%d image",
577 xvimage->width, xvimage->height));
579 /* Retry without XShm */
580 xvimagesink->xcontext->use_xshm = FALSE;
582 /* Hold X mutex again to try without XShm */
583 g_mutex_lock (xvimagesink->x_lock);
587 /* we have to use the returned data_size for our shm size */
588 xvimage->size = xvimage->xvimage->data_size;
589 GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
592 /* calculate the expected size. This is only for sanity checking the
593 * number we get from X. */
594 switch (xvimage->im_format) {
595 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
596 case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
603 pitches[0] = GST_ROUND_UP_4 (xvimage->width);
604 offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (xvimage->height);
605 pitches[1] = GST_ROUND_UP_8 (xvimage->width) / 2;
607 offsets[1] + pitches[1] * GST_ROUND_UP_2 (xvimage->height) / 2;
608 pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
611 offsets[2] + pitches[2] * GST_ROUND_UP_2 (xvimage->height) / 2;
613 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
614 GST_DEBUG_OBJECT (xvimagesink,
615 "Plane %u has a expected pitch of %d bytes, " "offset of %d",
616 plane, pitches[plane], offsets[plane]);
620 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
621 case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
622 expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
628 if (expected_size != 0 && xvimage->size != expected_size) {
629 GST_WARNING_OBJECT (xvimagesink,
630 "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
631 xvimage->size, expected_size);
634 /* Be verbose about our XvImage stride */
638 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
639 GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
640 "offset of %d", plane, xvimage->xvimage->pitches[plane],
641 xvimage->xvimage->offsets[plane]);
645 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
647 if (xvimage->SHMInfo.shmid == -1) {
648 g_mutex_unlock (xvimagesink->x_lock);
649 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
650 ("Failed to create output image buffer of %dx%d pixels",
651 xvimage->width, xvimage->height),
652 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
657 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
658 if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
659 g_mutex_unlock (xvimagesink->x_lock);
660 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
661 ("Failed to create output image buffer of %dx%d pixels",
662 xvimage->width, xvimage->height),
663 ("Failed to shmat: %s", g_strerror (errno)));
664 /* Clean up the shared memory segment */
665 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
669 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
670 xvimage->SHMInfo.readOnly = FALSE;
672 if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
673 /* Clean up the shared memory segment */
674 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
676 g_mutex_unlock (xvimagesink->x_lock);
677 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
678 ("Failed to create output image buffer of %dx%d pixels",
679 xvimage->width, xvimage->height), ("Failed to XShmAttach"));
683 XSync (xvimagesink->xcontext->disp, FALSE);
685 /* Delete the shared memory segment as soon as we everyone is attached.
686 * This way, it will be deleted as soon as we detach later, and not
687 * leaked if we crash. */
688 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
690 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
691 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
694 #endif /* HAVE_XSHM */
696 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
697 xvimagesink->xcontext->xv_port_id,
698 xvimage->im_format, NULL, xvimage->width, xvimage->height);
699 if (!xvimage->xvimage || error_caught) {
700 g_mutex_unlock (xvimagesink->x_lock);
701 /* Reset error handler */
702 error_caught = FALSE;
703 XSetErrorHandler (handler);
705 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
706 ("Failed to create outputimage buffer of %dx%d pixels",
707 xvimage->width, xvimage->height),
708 ("could not XvCreateImage a %dx%d image",
709 xvimage->width, xvimage->height));
713 /* we have to use the returned data_size for our image size */
714 xvimage->size = xvimage->xvimage->data_size;
715 xvimage->xvimage->data = g_malloc (xvimage->size);
717 XSync (xvimagesink->xcontext->disp, FALSE);
720 /* Reset error handler */
721 error_caught = FALSE;
722 XSetErrorHandler (handler);
726 GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
727 GST_BUFFER_SIZE (xvimage) = xvimage->size;
729 g_mutex_unlock (xvimagesink->x_lock);
733 gst_xvimage_buffer_free (xvimage);
740 /* We are called with the x_lock taken */
742 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
743 GstXWindow * xwindow, GstVideoRectangle rect)
747 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
748 g_return_if_fail (xwindow != NULL);
750 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
751 xvimagesink->xcontext->black);
754 if (rect.x > xvimagesink->render_rect.x) {
755 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
756 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
757 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
761 t1 = rect.x + rect.w;
762 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
764 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
765 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
769 if (rect.y > xvimagesink->render_rect.y) {
770 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
771 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
772 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
776 t1 = rect.y + rect.h;
777 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
779 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
780 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
784 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
785 * if no window was available */
787 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
788 GstXvImageBuffer * xvimage)
790 GstVideoRectangle result;
791 gboolean draw_border = FALSE;
793 /* We take the flow_lock. If expose is in there we don't want to run
794 concurrently from the data flow thread */
795 g_mutex_lock (xvimagesink->flow_lock);
797 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
798 g_mutex_unlock (xvimagesink->flow_lock);
802 /* Draw borders when displaying the first frame. After this
803 draw borders only on expose event or after a size change. */
804 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
808 /* Store a reference to the last image we put, lose the previous one */
809 if (xvimage && xvimagesink->cur_image != xvimage) {
810 if (xvimagesink->cur_image) {
811 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
812 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
814 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
815 xvimagesink->cur_image =
816 GST_XVIMAGE_BUFFER_CAST (gst_buffer_ref (GST_BUFFER_CAST (xvimage)));
819 /* Expose sends a NULL image, we take the latest frame */
821 if (xvimagesink->cur_image) {
823 xvimage = xvimagesink->cur_image;
825 g_mutex_unlock (xvimagesink->flow_lock);
830 if (xvimagesink->keep_aspect) {
831 GstVideoRectangle src, dst;
833 /* We use the calculated geometry from _setcaps as a source to respect
834 source and screen pixel aspect ratios. */
835 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
836 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
837 dst.w = xvimagesink->render_rect.w;
838 dst.h = xvimagesink->render_rect.h;
840 gst_video_sink_center_rect (src, dst, &result, TRUE);
841 result.x += xvimagesink->render_rect.x;
842 result.y += xvimagesink->render_rect.y;
844 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
847 g_mutex_lock (xvimagesink->x_lock);
849 if (draw_border && xvimagesink->draw_borders) {
850 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
852 xvimagesink->redraw_border = FALSE;
855 /* We scale to the window's geometry */
857 if (xvimagesink->xcontext->use_xshm) {
858 GST_LOG_OBJECT (xvimagesink,
859 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
861 xvimage->width, xvimage->height,
862 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
864 XvShmPutImage (xvimagesink->xcontext->disp,
865 xvimagesink->xcontext->xv_port_id,
866 xvimagesink->xwindow->win,
867 xvimagesink->xwindow->gc, xvimage->xvimage,
868 xvimagesink->disp_x, xvimagesink->disp_y,
869 xvimagesink->disp_width, xvimagesink->disp_height,
870 result.x, result.y, result.w, result.h, FALSE);
872 #endif /* HAVE_XSHM */
874 XvPutImage (xvimagesink->xcontext->disp,
875 xvimagesink->xcontext->xv_port_id,
876 xvimagesink->xwindow->win,
877 xvimagesink->xwindow->gc, xvimage->xvimage,
878 xvimagesink->disp_x, xvimagesink->disp_y,
879 xvimagesink->disp_width, xvimagesink->disp_height,
880 result.x, result.y, result.w, result.h);
883 XSync (xvimagesink->xcontext->disp, FALSE);
885 g_mutex_unlock (xvimagesink->x_lock);
887 g_mutex_unlock (xvimagesink->flow_lock);
893 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
896 Atom hints_atom = None;
899 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
900 g_return_val_if_fail (window != NULL, FALSE);
902 g_mutex_lock (xvimagesink->x_lock);
904 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
906 if (hints_atom == None) {
907 g_mutex_unlock (xvimagesink->x_lock);
911 hints = g_malloc0 (sizeof (MotifWmHints));
913 hints->flags |= MWM_HINTS_DECORATIONS;
914 hints->decorations = 1 << 0;
916 XChangeProperty (xvimagesink->xcontext->disp, window->win,
917 hints_atom, hints_atom, 32, PropModeReplace,
918 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
920 XSync (xvimagesink->xcontext->disp, FALSE);
922 g_mutex_unlock (xvimagesink->x_lock);
930 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
931 GstXWindow * xwindow, const gchar * media_title)
934 g_free (xvimagesink->media_title);
935 xvimagesink->media_title = g_strdup (media_title);
938 /* we have a window */
939 if (xwindow->internal) {
940 XTextProperty xproperty;
941 const gchar *app_name;
942 const gchar *title = NULL;
943 gchar *title_mem = NULL;
945 /* set application name as a title */
946 app_name = g_get_application_name ();
948 if (app_name && xvimagesink->media_title) {
949 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
951 } else if (app_name) {
953 } else if (xvimagesink->media_title) {
954 title = xvimagesink->media_title;
958 if ((XStringListToTextProperty (((char **) &title), 1,
960 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
961 XFree (xproperty.value);
970 /* This function handles a GstXWindow creation
971 * The width and height are the actual pixel size on the display */
973 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
974 gint width, gint height)
976 GstXWindow *xwindow = NULL;
979 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
981 xwindow = g_new0 (GstXWindow, 1);
983 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
984 xvimagesink->render_rect.w = width;
985 xvimagesink->render_rect.h = height;
987 xwindow->width = width;
988 xwindow->height = height;
989 xwindow->internal = TRUE;
991 g_mutex_lock (xvimagesink->x_lock);
993 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
994 xvimagesink->xcontext->root,
995 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
997 /* We have to do that to prevent X from redrawing the background on
998 * ConfigureNotify. This takes away flickering of video when resizing. */
999 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
1001 /* set application name as a title */
1002 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
1004 if (xvimagesink->handle_events) {
1007 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
1008 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1009 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1011 /* Tell the window manager we'd like delete client messages instead of
1013 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1014 "WM_DELETE_WINDOW", True);
1015 if (wm_delete != None) {
1016 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
1021 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1022 xwindow->win, 0, &values);
1024 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
1026 XSync (xvimagesink->xcontext->disp, FALSE);
1028 g_mutex_unlock (xvimagesink->x_lock);
1030 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
1032 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
1037 /* This function destroys a GstXWindow */
1039 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
1040 GstXWindow * xwindow)
1042 g_return_if_fail (xwindow != NULL);
1043 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1045 g_mutex_lock (xvimagesink->x_lock);
1047 /* If we did not create that window we just free the GC and let it live */
1048 if (xwindow->internal)
1049 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
1051 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
1053 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
1055 XSync (xvimagesink->xcontext->disp, FALSE);
1057 g_mutex_unlock (xvimagesink->x_lock);
1063 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
1065 XWindowAttributes attr;
1067 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1069 /* Update the window geometry */
1070 g_mutex_lock (xvimagesink->x_lock);
1071 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
1072 g_mutex_unlock (xvimagesink->x_lock);
1076 XGetWindowAttributes (xvimagesink->xcontext->disp,
1077 xvimagesink->xwindow->win, &attr);
1079 xvimagesink->xwindow->width = attr.width;
1080 xvimagesink->xwindow->height = attr.height;
1082 if (!xvimagesink->have_render_rect) {
1083 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1084 xvimagesink->render_rect.w = attr.width;
1085 xvimagesink->render_rect.h = attr.height;
1088 g_mutex_unlock (xvimagesink->x_lock);
1092 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
1093 GstXWindow * xwindow)
1095 g_return_if_fail (xwindow != NULL);
1096 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1098 g_mutex_lock (xvimagesink->x_lock);
1100 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1103 XSync (xvimagesink->xcontext->disp, FALSE);
1105 g_mutex_unlock (xvimagesink->x_lock);
1108 /* This function commits our internal colorbalance settings to our grabbed Xv
1109 port. If the xcontext is not initialized yet it simply returns */
1111 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1113 GList *channels = NULL;
1115 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1117 /* If we haven't initialized the X context we can't update anything */
1118 if (xvimagesink->xcontext == NULL)
1121 /* Don't set the attributes if they haven't been changed, to avoid
1122 * rounding errors changing the values */
1123 if (!xvimagesink->cb_changed)
1126 /* For each channel of the colorbalance we calculate the correct value
1127 doing range conversion and then set the Xv port attribute to match our
1129 channels = xvimagesink->xcontext->channels_list;
1132 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1133 GstColorBalanceChannel *channel = NULL;
1136 gdouble convert_coef;
1138 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1139 g_object_ref (channel);
1141 /* Our range conversion coef */
1142 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1144 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1145 value = xvimagesink->hue;
1146 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1147 value = xvimagesink->saturation;
1148 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1149 value = xvimagesink->contrast;
1150 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1151 value = xvimagesink->brightness;
1153 g_warning ("got an unknown channel %s", channel->label);
1154 g_object_unref (channel);
1158 /* Committing to Xv port */
1159 g_mutex_lock (xvimagesink->x_lock);
1161 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1162 if (prop_atom != None) {
1165 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
1166 XvSetPortAttribute (xvimagesink->xcontext->disp,
1167 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
1169 g_mutex_unlock (xvimagesink->x_lock);
1171 g_object_unref (channel);
1173 channels = g_list_next (channels);
1177 /* This function handles XEvents that might be in the queue. It generates
1178 GstEvent that will be sent upstream in the pipeline to handle interactivity
1179 and navigation. It will also listen for configure events on the window to
1180 trigger caps renegotiation so on the fly software scaling can work. */
1182 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1185 guint pointer_x = 0, pointer_y = 0;
1186 gboolean pointer_moved = FALSE;
1187 gboolean exposed = FALSE, configured = FALSE;
1189 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1191 /* Handle Interaction, produces navigation events */
1193 /* We get all pointer motion events, only the last position is
1195 g_mutex_lock (xvimagesink->flow_lock);
1196 g_mutex_lock (xvimagesink->x_lock);
1197 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1198 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1199 g_mutex_unlock (xvimagesink->x_lock);
1200 g_mutex_unlock (xvimagesink->flow_lock);
1204 pointer_x = e.xmotion.x;
1205 pointer_y = e.xmotion.y;
1206 pointer_moved = TRUE;
1211 g_mutex_lock (xvimagesink->flow_lock);
1212 g_mutex_lock (xvimagesink->x_lock);
1214 if (pointer_moved) {
1215 g_mutex_unlock (xvimagesink->x_lock);
1216 g_mutex_unlock (xvimagesink->flow_lock);
1218 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1219 pointer_x, pointer_y);
1220 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1221 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1223 g_mutex_lock (xvimagesink->flow_lock);
1224 g_mutex_lock (xvimagesink->x_lock);
1227 /* We get all events on our window to throw them upstream */
1228 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1229 xvimagesink->xwindow->win,
1230 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1234 /* We lock only for the X function call */
1235 g_mutex_unlock (xvimagesink->x_lock);
1236 g_mutex_unlock (xvimagesink->flow_lock);
1240 /* Mouse button pressed over our window. We send upstream
1241 events for interactivity/navigation */
1242 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1243 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1244 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1245 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1248 /* Mouse button released over our window. We send upstream
1249 events for interactivity/navigation */
1250 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1251 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1252 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1253 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1257 /* Key pressed/released over our window. We send upstream
1258 events for interactivity/navigation */
1259 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1260 e.xkey.keycode, e.xkey.x, e.xkey.y);
1261 g_mutex_lock (xvimagesink->x_lock);
1262 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1264 g_mutex_unlock (xvimagesink->x_lock);
1265 if (keysym != NoSymbol) {
1266 char *key_str = NULL;
1268 g_mutex_lock (xvimagesink->x_lock);
1269 key_str = XKeysymToString (keysym);
1270 g_mutex_unlock (xvimagesink->x_lock);
1271 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1272 e.type == KeyPress ? "key-press" : "key-release", key_str);
1274 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1275 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1279 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1281 g_mutex_lock (xvimagesink->flow_lock);
1282 g_mutex_lock (xvimagesink->x_lock);
1286 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1287 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1292 case ConfigureNotify:
1293 g_mutex_unlock (xvimagesink->x_lock);
1294 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1295 g_mutex_lock (xvimagesink->x_lock);
1303 if (xvimagesink->handle_expose && (exposed || configured)) {
1304 g_mutex_unlock (xvimagesink->x_lock);
1305 g_mutex_unlock (xvimagesink->flow_lock);
1307 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1309 g_mutex_lock (xvimagesink->flow_lock);
1310 g_mutex_lock (xvimagesink->x_lock);
1313 /* Handle Display events */
1314 while (XPending (xvimagesink->xcontext->disp)) {
1315 XNextEvent (xvimagesink->xcontext->disp, &e);
1318 case ClientMessage:{
1321 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1322 "WM_DELETE_WINDOW", True);
1323 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1324 /* Handle window deletion by posting an error on the bus */
1325 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1326 ("Output window was closed"), (NULL));
1328 g_mutex_unlock (xvimagesink->x_lock);
1329 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1330 xvimagesink->xwindow = NULL;
1331 g_mutex_lock (xvimagesink->x_lock);
1340 g_mutex_unlock (xvimagesink->x_lock);
1341 g_mutex_unlock (xvimagesink->flow_lock);
1345 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1346 XvAdaptorInfo * adaptors, int adaptor_no)
1351 /* Do we support XvImageMask ? */
1352 if (!(adaptors[adaptor_no].type & XvImageMask)) {
1353 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1354 adaptors[adaptor_no].name);
1358 /* We found such an adaptor, looking for an available port */
1359 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1360 /* We try to grab the port */
1361 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1362 if (Success == res) {
1363 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1364 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1365 adaptors[adaptor_no].num_ports);
1367 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1368 adaptors[adaptor_no].name, res);
1373 /* This function generates a caps with all supported format by the first
1374 Xv grabable port we find. We store each one of the supported formats in a
1375 format list and append the format to a newly created caps that we return
1376 If this function does not return NULL because of an error, it also grabs
1377 the port via XvGrabPort */
1379 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1380 GstXContext * xcontext)
1383 XvAdaptorInfo *adaptors;
1385 XvImageFormatValues *formats = NULL;
1387 XvEncodingInfo *encodings = NULL;
1388 gulong max_w = G_MAXINT, max_h = G_MAXINT;
1389 GstCaps *caps = NULL;
1390 GstCaps *rgb_caps = NULL;
1392 g_return_val_if_fail (xcontext != NULL, NULL);
1394 /* First let's check that XVideo extension is available */
1395 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1396 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1397 ("Could not initialise Xv output"),
1398 ("XVideo extension is not available"));
1402 /* Then we get adaptors list */
1403 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1404 &xcontext->nb_adaptors, &adaptors)) {
1405 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1406 ("Could not initialise Xv output"),
1407 ("Failed getting XV adaptors list"));
1411 xcontext->xv_port_id = 0;
1413 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1415 xcontext->adaptors =
1416 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
1418 /* Now fill up our adaptor name array */
1419 for (i = 0; i < xcontext->nb_adaptors; i++) {
1420 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
1423 if (xvimagesink->adaptor_no >= 0 &&
1424 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
1425 /* Find xv port from user defined adaptor */
1426 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
1427 xvimagesink->adaptor_no);
1430 if (!xcontext->xv_port_id) {
1431 /* Now search for an adaptor that supports XvImageMask */
1432 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
1433 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
1434 xvimagesink->adaptor_no = i;
1438 XvFreeAdaptorInfo (adaptors);
1440 if (!xcontext->xv_port_id) {
1441 xvimagesink->adaptor_no = -1;
1442 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
1443 ("Could not initialise Xv output"), ("No port available"));
1447 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1449 int count, todo = 3;
1450 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1451 xcontext->xv_port_id, &count);
1452 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1453 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
1454 static const char colorkey[] = "XV_COLORKEY";
1456 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
1458 xvimagesink->have_autopaint_colorkey = FALSE;
1459 xvimagesink->have_double_buffer = FALSE;
1460 xvimagesink->have_colorkey = FALSE;
1462 for (i = 0; ((i < count) && todo); i++)
1463 if (!strcmp (attr[i].name, autopaint)) {
1464 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
1466 /* turn on autopaint colorkey */
1467 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1468 (xvimagesink->autopaint_colorkey ? 1 : 0));
1470 xvimagesink->have_autopaint_colorkey = TRUE;
1471 } else if (!strcmp (attr[i].name, dbl_buffer)) {
1472 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
1474 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1475 (xvimagesink->double_buffer ? 1 : 0));
1477 xvimagesink->have_double_buffer = TRUE;
1478 } else if (!strcmp (attr[i].name, colorkey)) {
1479 /* Set the colorkey, default is something that is dark but hopefully
1480 * won't randomly appear on the screen elsewhere (ie not black or greys)
1481 * can be overridden by setting "colorkey" property
1483 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
1485 gboolean set_attr = TRUE;
1488 /* set a colorkey in the right format RGB565/RGB888
1489 * We only handle these 2 cases, because they're the only types of
1490 * devices we've encountered. If we don't recognise it, leave it alone
1492 cr = (xvimagesink->colorkey >> 16);
1493 cg = (xvimagesink->colorkey >> 8) & 0xFF;
1494 cb = (xvimagesink->colorkey) & 0xFF;
1495 switch (xcontext->depth) {
1496 case 16: /* RGB 565 */
1500 ckey = (cr << 11) | (cg << 5) | cb;
1503 case 32: /* RGB 888 / ARGB 8888 */
1504 ckey = (cr << 16) | (cg << 8) | cb;
1507 GST_DEBUG_OBJECT (xvimagesink,
1508 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1515 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1516 (guint32) attr[i].max_value);
1517 GST_LOG_OBJECT (xvimagesink,
1518 "Setting color key for display depth %d to 0x%x",
1519 xcontext->depth, ckey);
1521 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1525 xvimagesink->have_colorkey = TRUE;
1531 /* Get the list of encodings supported by the adapter and look for the
1532 * XV_IMAGE encoding so we can determine the maximum width and height
1534 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1537 for (i = 0; i < nb_encodings; i++) {
1538 GST_LOG_OBJECT (xvimagesink,
1539 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1540 i, encodings[i].name, encodings[i].width, encodings[i].height,
1541 encodings[i].rate.numerator, encodings[i].rate.denominator);
1542 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1543 max_w = encodings[i].width;
1544 max_h = encodings[i].height;
1548 XvFreeEncodingInfo (encodings);
1550 /* We get all image formats supported by our port */
1551 formats = XvListImageFormats (xcontext->disp,
1552 xcontext->xv_port_id, &nb_formats);
1553 caps = gst_caps_new_empty ();
1554 for (i = 0; i < nb_formats; i++) {
1555 GstCaps *format_caps = NULL;
1556 gboolean is_rgb_format = FALSE;
1558 /* We set the image format of the xcontext to an existing one. This
1559 is just some valid image format for making our xshm calls check before
1560 caps negotiation really happens. */
1561 xcontext->im_format = formats[i].id;
1563 switch (formats[i].type) {
1566 XvImageFormatValues *fmt = &(formats[i]);
1567 gint endianness = G_BIG_ENDIAN;
1569 if (fmt->byte_order == LSBFirst) {
1570 /* our caps system handles 24/32bpp RGB as big-endian. */
1571 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1572 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1573 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1574 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1576 if (fmt->bits_per_pixel == 24) {
1577 fmt->red_mask >>= 8;
1578 fmt->green_mask >>= 8;
1579 fmt->blue_mask >>= 8;
1582 endianness = G_LITTLE_ENDIAN;
1585 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1586 "endianness", G_TYPE_INT, endianness,
1587 "depth", G_TYPE_INT, fmt->depth,
1588 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1589 "red_mask", G_TYPE_INT, fmt->red_mask,
1590 "green_mask", G_TYPE_INT, fmt->green_mask,
1591 "blue_mask", G_TYPE_INT, fmt->blue_mask,
1592 "width", GST_TYPE_INT_RANGE, 1, max_w,
1593 "height", GST_TYPE_INT_RANGE, 1, max_h,
1594 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1596 is_rgb_format = TRUE;
1600 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1601 "format", GST_TYPE_FOURCC, formats[i].id,
1602 "width", GST_TYPE_INT_RANGE, 1, max_w,
1603 "height", GST_TYPE_INT_RANGE, 1, max_h,
1604 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1607 g_assert_not_reached ();
1612 GstXvImageFormat *format = NULL;
1614 format = g_new0 (GstXvImageFormat, 1);
1616 format->format = formats[i].id;
1617 format->caps = gst_caps_copy (format_caps);
1618 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1621 if (is_rgb_format) {
1622 if (rgb_caps == NULL)
1623 rgb_caps = format_caps;
1625 gst_caps_append (rgb_caps, format_caps);
1627 gst_caps_append (caps, format_caps);
1631 /* Collected all caps into either the caps or rgb_caps structures.
1632 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1634 gst_caps_append (caps, rgb_caps);
1639 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1641 if (gst_caps_is_empty (caps)) {
1642 gst_caps_unref (caps);
1643 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1644 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1645 ("No supported format found"));
1653 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1655 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1657 GST_OBJECT_LOCK (xvimagesink);
1658 while (xvimagesink->running) {
1659 GST_OBJECT_UNLOCK (xvimagesink);
1661 if (xvimagesink->xwindow) {
1662 gst_xvimagesink_handle_xevents (xvimagesink);
1664 /* FIXME: do we want to align this with the framerate or anything else? */
1665 g_usleep (G_USEC_PER_SEC / 20);
1667 GST_OBJECT_LOCK (xvimagesink);
1669 GST_OBJECT_UNLOCK (xvimagesink);
1675 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1677 GThread *thread = NULL;
1679 /* don't start the thread too early */
1680 if (xvimagesink->xcontext == NULL) {
1684 GST_OBJECT_LOCK (xvimagesink);
1685 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1686 if (!xvimagesink->event_thread) {
1687 /* Setup our event listening thread */
1688 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1689 xvimagesink->handle_expose, xvimagesink->handle_events);
1690 xvimagesink->running = TRUE;
1691 #if !GLIB_CHECK_VERSION (2, 31, 0)
1692 xvimagesink->event_thread = g_thread_create (
1693 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1695 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1696 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1700 if (xvimagesink->event_thread) {
1701 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1702 xvimagesink->handle_expose, xvimagesink->handle_events);
1703 xvimagesink->running = FALSE;
1704 /* grab thread and mark it as NULL */
1705 thread = xvimagesink->event_thread;
1706 xvimagesink->event_thread = NULL;
1709 GST_OBJECT_UNLOCK (xvimagesink);
1711 /* Wait for our event thread to finish */
1713 g_thread_join (thread);
1718 /* This function calculates the pixel aspect ratio based on the properties
1719 * in the xcontext structure and stores it there. */
1721 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1723 static const gint par[][2] = {
1724 {1, 1}, /* regular screen */
1725 {16, 15}, /* PAL TV */
1726 {11, 10}, /* 525 line Rec.601 video */
1727 {54, 59}, /* 625 line Rec.601 video */
1728 {64, 45}, /* 1280x1024 on 16:9 display */
1729 {5, 3}, /* 1280x1024 on 4:3 display */
1730 {4, 3} /* 800x600 on 16:9 display */
1737 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1739 /* first calculate the "real" ratio based on the X values;
1740 * which is the "physical" w/h divided by the w/h in pixels of the display */
1741 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1742 / (xcontext->heightmm * xcontext->width);
1744 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1746 if (xcontext->width == 720 && xcontext->height == 576) {
1747 ratio = 4.0 * 576 / (3.0 * 720);
1749 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1750 /* now find the one from par[][2] with the lowest delta to the real one */
1754 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1755 gdouble this_delta = DELTA (i);
1757 if (this_delta < delta) {
1763 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1764 par[index][0], par[index][1]);
1766 g_free (xcontext->par);
1767 xcontext->par = g_new0 (GValue, 1);
1768 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1769 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1770 GST_DEBUG ("set xcontext PAR to %d/%d",
1771 gst_value_get_fraction_numerator (xcontext->par),
1772 gst_value_get_fraction_denominator (xcontext->par));
1775 /* This function gets the X Display and global info about it. Everything is
1776 stored in our object and will be cleaned when the object is disposed. Note
1777 here that caps for supported format are generated without any window or
1779 static GstXContext *
1780 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1782 GstXContext *xcontext = NULL;
1783 XPixmapFormatValues *px_formats = NULL;
1784 gint nb_formats = 0, i, j, N_attr;
1785 XvAttribute *xv_attr;
1787 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1788 "XV_BRIGHTNESS", "XV_CONTRAST"
1791 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1793 xcontext = g_new0 (GstXContext, 1);
1794 xcontext->im_format = 0;
1796 g_mutex_lock (xvimagesink->x_lock);
1798 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1800 if (!xcontext->disp) {
1801 g_mutex_unlock (xvimagesink->x_lock);
1803 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1804 ("Could not initialise Xv output"), ("Could not open display"));
1808 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1809 xcontext->screen_num = DefaultScreen (xcontext->disp);
1810 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1811 xcontext->root = DefaultRootWindow (xcontext->disp);
1812 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1813 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1814 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1816 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1817 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1818 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1819 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1821 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1822 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1824 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1825 /* We get supported pixmap formats at supported depth */
1826 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1829 XCloseDisplay (xcontext->disp);
1830 g_mutex_unlock (xvimagesink->x_lock);
1831 g_free (xcontext->par);
1833 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1834 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1838 /* We get bpp value corresponding to our running depth */
1839 for (i = 0; i < nb_formats; i++) {
1840 if (px_formats[i].depth == xcontext->depth)
1841 xcontext->bpp = px_formats[i].bits_per_pixel;
1846 xcontext->endianness =
1847 (ImageByteOrder (xcontext->disp) ==
1848 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1850 /* our caps system handles 24/32bpp RGB as big-endian. */
1851 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1852 xcontext->endianness == G_LITTLE_ENDIAN) {
1853 xcontext->endianness = G_BIG_ENDIAN;
1854 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1855 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1856 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1857 if (xcontext->bpp == 24) {
1858 xcontext->visual->red_mask >>= 8;
1859 xcontext->visual->green_mask >>= 8;
1860 xcontext->visual->blue_mask >>= 8;
1864 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1866 if (!xcontext->caps) {
1867 XCloseDisplay (xcontext->disp);
1868 g_mutex_unlock (xvimagesink->x_lock);
1869 g_free (xcontext->par);
1871 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1875 /* Search for XShm extension support */
1876 if (XShmQueryExtension (xcontext->disp) &&
1877 gst_xvimagesink_check_xshm_calls (xcontext)) {
1878 xcontext->use_xshm = TRUE;
1879 GST_DEBUG ("xvimagesink is using XShm extension");
1881 #endif /* HAVE_XSHM */
1883 xcontext->use_xshm = FALSE;
1884 GST_DEBUG ("xvimagesink is not using XShm extension");
1887 xv_attr = XvQueryPortAttributes (xcontext->disp,
1888 xcontext->xv_port_id, &N_attr);
1891 /* Generate the channels list */
1892 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1893 XvAttribute *matching_attr = NULL;
1895 /* Retrieve the property atom if it exists. If it doesn't exist,
1896 * the attribute itself must not either, so we can skip */
1897 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1898 if (prop_atom == None)
1901 if (xv_attr != NULL) {
1902 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1903 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1904 matching_attr = xv_attr + j;
1907 if (matching_attr) {
1908 GstColorBalanceChannel *channel;
1910 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1911 channel->label = g_strdup (channels[i]);
1912 channel->min_value = matching_attr->min_value;
1913 channel->max_value = matching_attr->max_value;
1915 xcontext->channels_list = g_list_append (xcontext->channels_list,
1918 /* If the colorbalance settings have not been touched we get Xv values
1919 as defaults and update our internal variables */
1920 if (!xvimagesink->cb_changed) {
1923 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1925 /* Normalize val to [-1000, 1000] */
1926 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1927 (double) (channel->max_value - channel->min_value));
1929 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1930 xvimagesink->hue = val;
1931 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1932 xvimagesink->saturation = val;
1933 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1934 xvimagesink->brightness = val;
1935 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1936 xvimagesink->contrast = val;
1944 g_mutex_unlock (xvimagesink->x_lock);
1949 /* This function cleans the X context. Closing the Display, releasing the XV
1950 port and unrefing the caps for supported formats. */
1952 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1954 GList *formats_list, *channels_list;
1955 GstXContext *xcontext;
1958 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1960 GST_OBJECT_LOCK (xvimagesink);
1961 if (xvimagesink->xcontext == NULL) {
1962 GST_OBJECT_UNLOCK (xvimagesink);
1966 /* Take the XContext from the sink and clean it up */
1967 xcontext = xvimagesink->xcontext;
1968 xvimagesink->xcontext = NULL;
1970 GST_OBJECT_UNLOCK (xvimagesink);
1973 formats_list = xcontext->formats_list;
1975 while (formats_list) {
1976 GstXvImageFormat *format = formats_list->data;
1978 gst_caps_unref (format->caps);
1980 formats_list = g_list_next (formats_list);
1983 if (xcontext->formats_list)
1984 g_list_free (xcontext->formats_list);
1986 channels_list = xcontext->channels_list;
1988 while (channels_list) {
1989 GstColorBalanceChannel *channel = channels_list->data;
1991 g_object_unref (channel);
1992 channels_list = g_list_next (channels_list);
1995 if (xcontext->channels_list)
1996 g_list_free (xcontext->channels_list);
1998 gst_caps_unref (xcontext->caps);
1999 if (xcontext->last_caps)
2000 gst_caps_replace (&xcontext->last_caps, NULL);
2002 for (i = 0; i < xcontext->nb_adaptors; i++) {
2003 g_free (xcontext->adaptors[i]);
2006 g_free (xcontext->adaptors);
2008 g_free (xcontext->par);
2010 g_mutex_lock (xvimagesink->x_lock);
2012 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
2014 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
2016 XCloseDisplay (xcontext->disp);
2018 g_mutex_unlock (xvimagesink->x_lock);
2024 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
2026 g_mutex_lock (xvimagesink->pool_lock);
2028 while (xvimagesink->image_pool) {
2029 GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
2031 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2032 xvimagesink->image_pool);
2033 gst_xvimage_buffer_free (xvimage);
2036 g_mutex_unlock (xvimagesink->pool_lock);
2041 /* This function tries to get a format matching with a given caps in the
2042 supported list of formats we generated in gst_xvimagesink_get_xv_support */
2044 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
2049 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2051 list = xvimagesink->xcontext->formats_list;
2054 GstXvImageFormat *format = list->data;
2057 if (gst_caps_can_intersect (caps, format->caps)) {
2058 return format->format;
2061 list = g_list_next (list);
2068 gst_xvimagesink_getcaps (GstBaseSink * bsink)
2070 GstXvImageSink *xvimagesink;
2072 xvimagesink = GST_XVIMAGESINK (bsink);
2074 if (xvimagesink->xcontext)
2075 return gst_caps_ref (xvimagesink->xcontext->caps);
2078 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
2083 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2085 GstXvImageSink *xvimagesink;
2086 GstStructure *structure;
2087 guint32 im_format = 0;
2089 gint video_width, video_height;
2090 gint disp_x, disp_y;
2091 gint disp_width, disp_height;
2092 gint video_par_n, video_par_d; /* video's PAR */
2093 gint display_par_n, display_par_d; /* display's PAR */
2094 const GValue *caps_par;
2095 const GValue *caps_disp_reg;
2099 xvimagesink = GST_XVIMAGESINK (bsink);
2101 GST_DEBUG_OBJECT (xvimagesink,
2102 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
2103 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
2105 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
2106 goto incompatible_caps;
2108 structure = gst_caps_get_structure (caps, 0);
2109 ret = gst_structure_get_int (structure, "width", &video_width);
2110 ret &= gst_structure_get_int (structure, "height", &video_height);
2111 fps = gst_structure_get_value (structure, "framerate");
2112 ret &= (fps != NULL);
2115 goto incomplete_caps;
2117 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
2118 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
2120 xvimagesink->video_width = video_width;
2121 xvimagesink->video_height = video_height;
2123 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
2124 if (im_format == -1)
2125 goto invalid_format;
2127 /* get aspect ratio from caps if it's present, and
2128 * convert video width and height to a display width and height
2129 * using wd / hd = wv / hv * PARv / PARd */
2131 /* get video's PAR */
2132 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
2134 video_par_n = gst_value_get_fraction_numerator (caps_par);
2135 video_par_d = gst_value_get_fraction_denominator (caps_par);
2140 /* get display's PAR */
2141 if (xvimagesink->par) {
2142 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2143 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2149 /* get the display region */
2150 caps_disp_reg = gst_structure_get_value (structure, "display-region");
2151 if (caps_disp_reg) {
2152 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
2153 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
2154 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
2156 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
2158 disp_x = disp_y = 0;
2159 disp_width = video_width;
2160 disp_height = video_height;
2163 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2164 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
2167 xvimagesink->disp_x = disp_x;
2168 xvimagesink->disp_y = disp_y;
2169 xvimagesink->disp_width = disp_width;
2170 xvimagesink->disp_height = disp_height;
2172 GST_DEBUG_OBJECT (xvimagesink,
2173 "video width/height: %dx%d, calculated display ratio: %d/%d",
2174 video_width, video_height, num, den);
2176 /* now find a width x height that respects this display ratio.
2177 * prefer those that have one of w/h the same as the incoming video
2178 * using wd / hd = num / den */
2180 /* start with same height, because of interlaced video */
2181 /* check hd / den is an integer scale factor, and scale wd with the PAR */
2182 if (video_height % den == 0) {
2183 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2184 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2185 gst_util_uint64_scale_int (video_height, num, den);
2186 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2187 } else if (video_width % num == 0) {
2188 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2189 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2190 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2191 gst_util_uint64_scale_int (video_width, den, num);
2193 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2194 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2195 gst_util_uint64_scale_int (video_height, num, den);
2196 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2198 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2199 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2201 /* Notify application to set xwindow id now */
2202 g_mutex_lock (xvimagesink->flow_lock);
2203 if (!xvimagesink->xwindow) {
2204 g_mutex_unlock (xvimagesink->flow_lock);
2205 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2207 g_mutex_unlock (xvimagesink->flow_lock);
2210 /* Creating our window and our image with the display size in pixels */
2211 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2212 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
2213 goto no_display_size;
2215 g_mutex_lock (xvimagesink->flow_lock);
2216 if (!xvimagesink->xwindow) {
2217 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2218 GST_VIDEO_SINK_WIDTH (xvimagesink),
2219 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2222 /* After a resize, we want to redraw the borders in case the new frame size
2223 * doesn't cover the same area */
2224 xvimagesink->redraw_border = TRUE;
2226 /* We renew our xvimage only if size or format changed;
2227 * the xvimage is the same size as the video pixel size */
2228 if ((xvimagesink->xvimage) &&
2229 ((im_format != xvimagesink->xvimage->im_format) ||
2230 (video_width != xvimagesink->xvimage->width) ||
2231 (video_height != xvimagesink->xvimage->height))) {
2232 GST_DEBUG_OBJECT (xvimagesink,
2233 "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2234 GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2235 GST_FOURCC_ARGS (im_format));
2236 GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2237 gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2238 xvimagesink->xvimage = NULL;
2241 g_mutex_unlock (xvimagesink->flow_lock);
2248 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
2253 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
2254 "height or framerate from intersected caps");
2259 GST_DEBUG_OBJECT (xvimagesink,
2260 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
2265 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2266 ("Error calculating the output display ratio of the video."));
2271 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2272 ("Error calculating the output display ratio of the video."));
2277 static GstStateChangeReturn
2278 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2280 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2281 GstXvImageSink *xvimagesink;
2282 GstXContext *xcontext = NULL;
2284 xvimagesink = GST_XVIMAGESINK (element);
2286 switch (transition) {
2287 case GST_STATE_CHANGE_NULL_TO_READY:
2288 /* Initializing the XContext */
2289 if (xvimagesink->xcontext == NULL) {
2290 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2291 if (xcontext == NULL)
2292 return GST_STATE_CHANGE_FAILURE;
2293 GST_OBJECT_LOCK (xvimagesink);
2295 xvimagesink->xcontext = xcontext;
2296 GST_OBJECT_UNLOCK (xvimagesink);
2299 /* update object's par with calculated one if not set yet */
2300 if (!xvimagesink->par) {
2301 xvimagesink->par = g_new0 (GValue, 1);
2302 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2303 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2305 /* call XSynchronize with the current value of synchronous */
2306 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2307 xvimagesink->synchronous ? "TRUE" : "FALSE");
2308 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2309 gst_xvimagesink_update_colorbalance (xvimagesink);
2310 gst_xvimagesink_manage_event_thread (xvimagesink);
2312 case GST_STATE_CHANGE_READY_TO_PAUSED:
2313 g_mutex_lock (xvimagesink->pool_lock);
2314 xvimagesink->pool_invalid = FALSE;
2315 g_mutex_unlock (xvimagesink->pool_lock);
2317 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2319 case GST_STATE_CHANGE_PAUSED_TO_READY:
2320 g_mutex_lock (xvimagesink->pool_lock);
2321 xvimagesink->pool_invalid = TRUE;
2322 g_mutex_unlock (xvimagesink->pool_lock);
2328 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2330 switch (transition) {
2331 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2333 case GST_STATE_CHANGE_PAUSED_TO_READY:
2334 xvimagesink->fps_n = 0;
2335 xvimagesink->fps_d = 1;
2336 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2337 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2339 case GST_STATE_CHANGE_READY_TO_NULL:
2340 gst_xvimagesink_reset (xvimagesink);
2350 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2351 GstClockTime * start, GstClockTime * end)
2353 GstXvImageSink *xvimagesink;
2355 xvimagesink = GST_XVIMAGESINK (bsink);
2357 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2358 *start = GST_BUFFER_TIMESTAMP (buf);
2359 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2360 *end = *start + GST_BUFFER_DURATION (buf);
2362 if (xvimagesink->fps_n > 0) {
2364 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2365 xvimagesink->fps_n);
2371 static GstFlowReturn
2372 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2374 GstXvImageSink *xvimagesink;
2376 xvimagesink = GST_XVIMAGESINK (vsink);
2378 /* If this buffer has been allocated using our buffer management we simply
2379 put the ximage which is in the PRIVATE pointer */
2380 if (GST_IS_XVIMAGE_BUFFER (buf)) {
2381 GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
2382 if (!gst_xvimagesink_xvimage_put (xvimagesink,
2383 GST_XVIMAGE_BUFFER_CAST (buf)))
2386 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
2387 "slow copy into bufferpool buffer %p", buf);
2388 /* Else we have to copy the data into our private image, */
2389 /* if we have one... */
2390 if (!xvimagesink->xvimage) {
2391 GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
2393 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
2394 GST_BUFFER_CAPS (buf));
2396 if (!xvimagesink->xvimage)
2397 /* The create method should have posted an informative error */
2400 if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
2401 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2402 ("Failed to create output image buffer of %dx%d pixels",
2403 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
2404 ("XServer allocated buffer size did not match input buffer"));
2406 gst_xvimage_buffer_destroy (xvimagesink->xvimage);
2407 xvimagesink->xvimage = NULL;
2412 memcpy (xvimagesink->xvimage->xvimage->data,
2413 GST_BUFFER_DATA (buf),
2414 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
2416 if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
2425 /* No image available. That's very bad ! */
2426 GST_WARNING_OBJECT (xvimagesink, "could not create image");
2427 return GST_FLOW_ERROR;
2431 /* No Window available to put our image into */
2432 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
2433 return GST_FLOW_ERROR;
2438 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
2440 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
2442 switch (GST_EVENT_TYPE (event)) {
2443 case GST_EVENT_TAG:{
2445 gchar *title = NULL;
2447 gst_event_parse_tag (event, &l);
2448 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
2451 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
2452 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
2462 if (GST_BASE_SINK_CLASS (parent_class)->event)
2463 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
2468 /* Buffer management */
2471 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
2474 GstCaps *intersection;
2478 gint par_n = 1, par_d = 1;
2482 new_caps = gst_caps_copy (caps);
2484 s = gst_caps_get_structure (new_caps, 0);
2486 gst_structure_get_int (s, "width", &width);
2487 gst_structure_get_int (s, "height", &height);
2488 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
2490 gst_structure_remove_field (s, "width");
2491 gst_structure_remove_field (s, "height");
2492 gst_structure_remove_field (s, "pixel-aspect-ratio");
2494 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2495 gst_caps_unref (new_caps);
2497 if (gst_caps_is_empty (intersection))
2498 return intersection;
2500 s = gst_caps_get_structure (intersection, 0);
2502 gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
2504 /* xvimagesink supports all PARs */
2506 gst_structure_fixate_field_nearest_int (s, "width", width);
2507 gst_structure_fixate_field_nearest_int (s, "height", height);
2508 gst_structure_get_int (s, "width", &w);
2509 gst_structure_get_int (s, "height", &h);
2511 gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
2512 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
2515 return intersection;
2518 static GstFlowReturn
2519 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
2520 GstCaps * caps, GstBuffer ** buf)
2522 GstFlowReturn ret = GST_FLOW_OK;
2523 GstXvImageSink *xvimagesink;
2524 GstXvImageBuffer *xvimage = NULL;
2525 GstCaps *intersection = NULL;
2526 GstStructure *structure = NULL;
2527 gint width, height, image_format;
2529 xvimagesink = GST_XVIMAGESINK (bsink);
2531 if (G_UNLIKELY (!caps))
2534 g_mutex_lock (xvimagesink->pool_lock);
2535 if (G_UNLIKELY (xvimagesink->pool_invalid))
2538 if (G_LIKELY (xvimagesink->xcontext->last_caps &&
2539 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
2540 GST_LOG_OBJECT (xvimagesink,
2541 "buffer alloc for same last_caps, reusing caps");
2542 intersection = gst_caps_ref (caps);
2543 image_format = xvimagesink->xcontext->last_format;
2544 width = xvimagesink->xcontext->last_width;
2545 height = xvimagesink->xcontext->last_height;
2547 goto reuse_last_caps;
2550 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
2551 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
2552 caps, xvimagesink->xcontext->caps);
2554 /* Check the caps against our xcontext */
2555 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2557 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2558 GST_PTR_FORMAT, intersection);
2560 if (gst_caps_is_empty (intersection)) {
2563 gst_caps_unref (intersection);
2565 /* So we don't support this kind of buffer, let's define one we'd like */
2566 new_caps = gst_caps_copy (caps);
2568 structure = gst_caps_get_structure (new_caps, 0);
2569 if (!gst_structure_has_field (structure, "width") ||
2570 !gst_structure_has_field (structure, "height")) {
2571 gst_caps_unref (new_caps);
2575 /* Try different dimensions */
2577 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2579 if (gst_caps_is_empty (intersection)) {
2580 /* Try with different YUV formats first */
2581 gst_structure_set_name (structure, "video/x-raw-yuv");
2583 /* Remove format specific fields */
2584 gst_structure_remove_field (structure, "format");
2585 gst_structure_remove_field (structure, "endianness");
2586 gst_structure_remove_field (structure, "depth");
2587 gst_structure_remove_field (structure, "bpp");
2588 gst_structure_remove_field (structure, "red_mask");
2589 gst_structure_remove_field (structure, "green_mask");
2590 gst_structure_remove_field (structure, "blue_mask");
2591 gst_structure_remove_field (structure, "alpha_mask");
2593 /* Reuse intersection with Xcontext */
2594 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2597 if (gst_caps_is_empty (intersection)) {
2598 /* Try with different dimensions and YUV formats */
2600 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2603 if (gst_caps_is_empty (intersection)) {
2604 /* Now try with RGB */
2605 gst_structure_set_name (structure, "video/x-raw-rgb");
2606 /* And interset again */
2607 gst_caps_unref (intersection);
2608 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2611 if (gst_caps_is_empty (intersection)) {
2612 /* Try with different dimensions and RGB formats */
2614 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2617 /* Clean this copy */
2618 gst_caps_unref (new_caps);
2620 if (gst_caps_is_empty (intersection))
2624 /* Ensure the returned caps are fixed */
2625 gst_caps_truncate (intersection);
2627 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2628 GST_PTR_FORMAT, intersection);
2629 if (gst_caps_is_equal (intersection, caps)) {
2630 /* Things work better if we return a buffer with the same caps ptr
2631 * as was asked for when we can */
2632 gst_caps_replace (&intersection, caps);
2635 /* Get image format from caps */
2636 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2639 /* Get geometry from caps */
2640 structure = gst_caps_get_structure (intersection, 0);
2641 if (!gst_structure_get_int (structure, "width", &width) ||
2642 !gst_structure_get_int (structure, "height", &height) ||
2646 /* Store our caps and format as the last_caps to avoid expensive
2647 * caps intersection next time */
2648 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2649 xvimagesink->xcontext->last_format = image_format;
2650 xvimagesink->xcontext->last_width = width;
2651 xvimagesink->xcontext->last_height = height;
2655 /* Walking through the pool cleaning unusable images and searching for a
2657 while (xvimagesink->image_pool) {
2658 xvimage = xvimagesink->image_pool->data;
2661 /* Removing from the pool */
2662 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2663 xvimagesink->image_pool);
2665 /* We check for geometry or image format changes */
2666 if ((xvimage->width != width) ||
2667 (xvimage->height != height) || (xvimage->im_format != image_format)) {
2668 /* This image is unusable. Destroying... */
2669 gst_xvimage_buffer_free (xvimage);
2672 /* We found a suitable image */
2673 GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
2680 /* We found no suitable image in the pool. Creating... */
2681 GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
2682 xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
2684 g_mutex_unlock (xvimagesink->pool_lock);
2687 /* Make sure the buffer is cleared of any previously used flags */
2688 GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
2689 gst_buffer_set_caps (GST_BUFFER_CAST (xvimage), intersection);
2692 *buf = GST_BUFFER_CAST (xvimage);
2696 gst_caps_unref (intersection);
2704 GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
2705 ret = GST_FLOW_WRONG_STATE;
2706 g_mutex_unlock (xvimagesink->pool_lock);
2711 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2712 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2713 " are completely incompatible with those caps", caps,
2714 xvimagesink->xcontext->caps);
2715 ret = GST_FLOW_NOT_NEGOTIATED;
2716 g_mutex_unlock (xvimagesink->pool_lock);
2721 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2722 GST_PTR_FORMAT, intersection);
2723 ret = GST_FLOW_NOT_NEGOTIATED;
2724 g_mutex_unlock (xvimagesink->pool_lock);
2729 GST_WARNING_OBJECT (xvimagesink, "have no caps, doing fallback allocation");
2736 /* Interfaces stuff */
2739 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2741 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2742 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE)
2749 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2751 klass->supported = gst_xvimagesink_interface_supported;
2755 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2756 GstStructure * structure)
2758 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2761 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2763 GstVideoRectangle src, dst, result;
2764 gdouble x, y, xscale = 1.0, yscale = 1.0;
2766 event = gst_event_new_navigation (structure);
2768 /* We take the flow_lock while we look at the window */
2769 g_mutex_lock (xvimagesink->flow_lock);
2771 if (!xvimagesink->xwindow) {
2772 g_mutex_unlock (xvimagesink->flow_lock);
2776 if (xvimagesink->keep_aspect) {
2777 /* We get the frame position using the calculated geometry from _setcaps
2778 that respect pixel aspect ratios */
2779 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2780 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2781 dst.w = xvimagesink->render_rect.w;
2782 dst.h = xvimagesink->render_rect.h;
2784 gst_video_sink_center_rect (src, dst, &result, TRUE);
2785 result.x += xvimagesink->render_rect.x;
2786 result.y += xvimagesink->render_rect.y;
2788 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2791 g_mutex_unlock (xvimagesink->flow_lock);
2793 /* We calculate scaling using the original video frames geometry to include
2794 pixel aspect ratio scaling. */
2795 xscale = (gdouble) xvimagesink->video_width / result.w;
2796 yscale = (gdouble) xvimagesink->video_height / result.h;
2798 /* Converting pointer coordinates to the non scaled geometry */
2799 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2800 x = MIN (x, result.x + result.w);
2801 x = MAX (x - result.x, 0);
2802 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2803 (gdouble) x * xscale, NULL);
2805 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2806 y = MIN (y, result.y + result.h);
2807 y = MAX (y - result.y, 0);
2808 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2809 (gdouble) y * yscale, NULL);
2812 gst_pad_send_event (peer, event);
2813 gst_object_unref (peer);
2818 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2820 iface->send_event = gst_xvimagesink_navigation_send_event;
2824 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2826 XID xwindow_id = id;
2827 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2828 GstXWindow *xwindow = NULL;
2830 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2832 g_mutex_lock (xvimagesink->flow_lock);
2834 /* If we already use that window return */
2835 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2836 g_mutex_unlock (xvimagesink->flow_lock);
2840 /* If the element has not initialized the X11 context try to do so */
2841 if (!xvimagesink->xcontext &&
2842 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2843 g_mutex_unlock (xvimagesink->flow_lock);
2844 /* we have thrown a GST_ELEMENT_ERROR now */
2848 gst_xvimagesink_update_colorbalance (xvimagesink);
2850 /* Clear image pool as the images are unusable anyway */
2851 gst_xvimagesink_imagepool_clear (xvimagesink);
2853 /* Clear the xvimage */
2854 if (xvimagesink->xvimage) {
2855 gst_xvimage_buffer_free (xvimagesink->xvimage);
2856 xvimagesink->xvimage = NULL;
2859 /* If a window is there already we destroy it */
2860 if (xvimagesink->xwindow) {
2861 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2862 xvimagesink->xwindow = NULL;
2865 /* If the xid is 0 we go back to an internal window */
2866 if (xwindow_id == 0) {
2867 /* If no width/height caps nego did not happen window will be created
2868 during caps nego then */
2869 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2870 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2872 gst_xvimagesink_xwindow_new (xvimagesink,
2873 GST_VIDEO_SINK_WIDTH (xvimagesink),
2874 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2877 XWindowAttributes attr;
2879 xwindow = g_new0 (GstXWindow, 1);
2880 xwindow->win = xwindow_id;
2882 /* Set the event we want to receive and create a GC */
2883 g_mutex_lock (xvimagesink->x_lock);
2885 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2887 xwindow->width = attr.width;
2888 xwindow->height = attr.height;
2889 xwindow->internal = FALSE;
2890 if (!xvimagesink->have_render_rect) {
2891 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2892 xvimagesink->render_rect.w = attr.width;
2893 xvimagesink->render_rect.h = attr.height;
2895 if (xvimagesink->handle_events) {
2896 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2897 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2901 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2902 xwindow->win, 0, NULL);
2903 g_mutex_unlock (xvimagesink->x_lock);
2907 xvimagesink->xwindow = xwindow;
2909 g_mutex_unlock (xvimagesink->flow_lock);
2913 gst_xvimagesink_expose (GstXOverlay * overlay)
2915 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2917 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2918 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2922 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2923 gboolean handle_events)
2925 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2927 xvimagesink->handle_events = handle_events;
2929 g_mutex_lock (xvimagesink->flow_lock);
2931 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2932 g_mutex_unlock (xvimagesink->flow_lock);
2936 g_mutex_lock (xvimagesink->x_lock);
2938 if (handle_events) {
2939 if (xvimagesink->xwindow->internal) {
2940 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2941 ExposureMask | StructureNotifyMask | PointerMotionMask |
2942 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2944 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2945 ExposureMask | StructureNotifyMask | PointerMotionMask |
2946 KeyPressMask | KeyReleaseMask);
2949 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2952 g_mutex_unlock (xvimagesink->x_lock);
2954 g_mutex_unlock (xvimagesink->flow_lock);
2958 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2959 gint width, gint height)
2961 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2963 /* FIXME: how about some locking? */
2964 if (width >= 0 && height >= 0) {
2965 xvimagesink->render_rect.x = x;
2966 xvimagesink->render_rect.y = y;
2967 xvimagesink->render_rect.w = width;
2968 xvimagesink->render_rect.h = height;
2969 xvimagesink->have_render_rect = TRUE;
2971 xvimagesink->render_rect.x = 0;
2972 xvimagesink->render_rect.y = 0;
2973 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2974 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2975 xvimagesink->have_render_rect = FALSE;
2980 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2982 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2983 iface->expose = gst_xvimagesink_expose;
2984 iface->handle_events = gst_xvimagesink_set_event_handling;
2985 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2988 static const GList *
2989 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2991 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2993 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2995 if (xvimagesink->xcontext)
2996 return xvimagesink->xcontext->channels_list;
3002 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
3003 GstColorBalanceChannel * channel, gint value)
3005 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3007 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
3008 g_return_if_fail (channel->label != NULL);
3010 xvimagesink->cb_changed = TRUE;
3012 /* Normalize val to [-1000, 1000] */
3013 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
3014 (double) (channel->max_value - channel->min_value));
3016 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3017 xvimagesink->hue = value;
3018 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3019 xvimagesink->saturation = value;
3020 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3021 xvimagesink->contrast = value;
3022 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3023 xvimagesink->brightness = value;
3025 g_warning ("got an unknown channel %s", channel->label);
3029 gst_xvimagesink_update_colorbalance (xvimagesink);
3033 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
3034 GstColorBalanceChannel * channel)
3036 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3039 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
3040 g_return_val_if_fail (channel->label != NULL, 0);
3042 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3043 value = xvimagesink->hue;
3044 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3045 value = xvimagesink->saturation;
3046 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3047 value = xvimagesink->contrast;
3048 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3049 value = xvimagesink->brightness;
3051 g_warning ("got an unknown channel %s", channel->label);
3054 /* Normalize val to [channel->min_value, channel->max_value] */
3055 value = channel->min_value + (channel->max_value - channel->min_value) *
3056 (value + 1000) / 2000;
3062 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
3064 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
3065 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
3066 iface->set_value = gst_xvimagesink_colorbalance_set_value;
3067 iface->get_value = gst_xvimagesink_colorbalance_get_value;
3070 static const GList *
3071 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
3073 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
3074 static GList *list = NULL;
3077 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
3079 g_list_append (list, g_object_class_find_property (klass,
3080 "autopaint-colorkey"));
3082 g_list_append (list, g_object_class_find_property (klass,
3085 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
3092 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
3093 guint prop_id, const GParamSpec * pspec)
3095 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3099 case PROP_AUTOPAINT_COLORKEY:
3100 case PROP_DOUBLE_BUFFER:
3102 GST_DEBUG_OBJECT (xvimagesink,
3103 "probing device list and get capabilities");
3104 if (!xvimagesink->xcontext) {
3105 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
3106 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
3110 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3116 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
3117 guint prop_id, const GParamSpec * pspec)
3119 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3120 gboolean ret = FALSE;
3124 case PROP_AUTOPAINT_COLORKEY:
3125 case PROP_DOUBLE_BUFFER:
3127 if (xvimagesink->xcontext != NULL) {
3134 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3141 static GValueArray *
3142 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
3143 guint prop_id, const GParamSpec * pspec)
3145 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3146 GValueArray *array = NULL;
3148 if (G_UNLIKELY (!xvimagesink->xcontext)) {
3149 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
3158 GValue value = { 0 };
3160 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
3161 g_value_init (&value, G_TYPE_STRING);
3163 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
3164 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
3166 g_value_set_string (&value, adaptor_id_s);
3167 g_value_array_append (array, &value);
3168 g_free (adaptor_id_s);
3170 g_value_unset (&value);
3173 case PROP_AUTOPAINT_COLORKEY:
3174 if (xvimagesink->have_autopaint_colorkey) {
3175 GValue value = { 0 };
3177 array = g_value_array_new (2);
3178 g_value_init (&value, G_TYPE_BOOLEAN);
3179 g_value_set_boolean (&value, FALSE);
3180 g_value_array_append (array, &value);
3181 g_value_set_boolean (&value, TRUE);
3182 g_value_array_append (array, &value);
3183 g_value_unset (&value);
3186 case PROP_DOUBLE_BUFFER:
3187 if (xvimagesink->have_double_buffer) {
3188 GValue value = { 0 };
3190 array = g_value_array_new (2);
3191 g_value_init (&value, G_TYPE_BOOLEAN);
3192 g_value_set_boolean (&value, FALSE);
3193 g_value_array_append (array, &value);
3194 g_value_set_boolean (&value, TRUE);
3195 g_value_array_append (array, &value);
3196 g_value_unset (&value);
3200 if (xvimagesink->have_colorkey) {
3201 GValue value = { 0 };
3203 array = g_value_array_new (1);
3204 g_value_init (&value, GST_TYPE_INT_RANGE);
3205 gst_value_set_int_range (&value, 0, 0xffffff);
3206 g_value_array_append (array, &value);
3207 g_value_unset (&value);
3211 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3220 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
3223 iface->get_properties = gst_xvimagesink_probe_get_properties;
3224 iface->probe_property = gst_xvimagesink_probe_probe_property;
3225 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
3226 iface->get_values = gst_xvimagesink_probe_get_values;
3229 /* =========================================== */
3231 /* Init & Class init */
3233 /* =========================================== */
3236 gst_xvimagesink_set_property (GObject * object, guint prop_id,
3237 const GValue * value, GParamSpec * pspec)
3239 GstXvImageSink *xvimagesink;
3241 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3243 xvimagesink = GST_XVIMAGESINK (object);
3247 xvimagesink->hue = g_value_get_int (value);
3248 xvimagesink->cb_changed = TRUE;
3249 gst_xvimagesink_update_colorbalance (xvimagesink);
3252 xvimagesink->contrast = g_value_get_int (value);
3253 xvimagesink->cb_changed = TRUE;
3254 gst_xvimagesink_update_colorbalance (xvimagesink);
3256 case PROP_BRIGHTNESS:
3257 xvimagesink->brightness = g_value_get_int (value);
3258 xvimagesink->cb_changed = TRUE;
3259 gst_xvimagesink_update_colorbalance (xvimagesink);
3261 case PROP_SATURATION:
3262 xvimagesink->saturation = g_value_get_int (value);
3263 xvimagesink->cb_changed = TRUE;
3264 gst_xvimagesink_update_colorbalance (xvimagesink);
3267 xvimagesink->display_name = g_strdup (g_value_get_string (value));
3269 case PROP_SYNCHRONOUS:
3270 xvimagesink->synchronous = g_value_get_boolean (value);
3271 if (xvimagesink->xcontext) {
3272 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
3273 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
3274 xvimagesink->synchronous ? "TRUE" : "FALSE");
3277 case PROP_PIXEL_ASPECT_RATIO:
3278 g_free (xvimagesink->par);
3279 xvimagesink->par = g_new0 (GValue, 1);
3280 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
3281 if (!g_value_transform (value, xvimagesink->par)) {
3282 g_warning ("Could not transform string to aspect ratio");
3283 gst_value_set_fraction (xvimagesink->par, 1, 1);
3285 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
3286 gst_value_get_fraction_numerator (xvimagesink->par),
3287 gst_value_get_fraction_denominator (xvimagesink->par));
3289 case PROP_FORCE_ASPECT_RATIO:
3290 xvimagesink->keep_aspect = g_value_get_boolean (value);
3292 case PROP_HANDLE_EVENTS:
3293 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
3294 g_value_get_boolean (value));
3295 gst_xvimagesink_manage_event_thread (xvimagesink);
3298 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
3300 case PROP_HANDLE_EXPOSE:
3301 xvimagesink->handle_expose = g_value_get_boolean (value);
3302 gst_xvimagesink_manage_event_thread (xvimagesink);
3304 case PROP_DOUBLE_BUFFER:
3305 xvimagesink->double_buffer = g_value_get_boolean (value);
3307 case PROP_AUTOPAINT_COLORKEY:
3308 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
3311 xvimagesink->colorkey = g_value_get_int (value);
3313 case PROP_DRAW_BORDERS:
3314 xvimagesink->draw_borders = g_value_get_boolean (value);
3317 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3323 gst_xvimagesink_get_property (GObject * object, guint prop_id,
3324 GValue * value, GParamSpec * pspec)
3326 GstXvImageSink *xvimagesink;
3328 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3330 xvimagesink = GST_XVIMAGESINK (object);
3334 g_value_set_int (value, xvimagesink->hue);
3337 g_value_set_int (value, xvimagesink->contrast);
3339 case PROP_BRIGHTNESS:
3340 g_value_set_int (value, xvimagesink->brightness);
3342 case PROP_SATURATION:
3343 g_value_set_int (value, xvimagesink->saturation);
3346 g_value_set_string (value, xvimagesink->display_name);
3348 case PROP_SYNCHRONOUS:
3349 g_value_set_boolean (value, xvimagesink->synchronous);
3351 case PROP_PIXEL_ASPECT_RATIO:
3352 if (xvimagesink->par)
3353 g_value_transform (xvimagesink->par, value);
3355 case PROP_FORCE_ASPECT_RATIO:
3356 g_value_set_boolean (value, xvimagesink->keep_aspect);
3358 case PROP_HANDLE_EVENTS:
3359 g_value_set_boolean (value, xvimagesink->handle_events);
3363 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
3365 g_value_set_string (value, adaptor_no_s);
3366 g_free (adaptor_no_s);
3369 case PROP_DEVICE_NAME:
3370 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
3371 g_value_set_string (value,
3372 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
3374 g_value_set_string (value, NULL);
3377 case PROP_HANDLE_EXPOSE:
3378 g_value_set_boolean (value, xvimagesink->handle_expose);
3380 case PROP_DOUBLE_BUFFER:
3381 g_value_set_boolean (value, xvimagesink->double_buffer);
3383 case PROP_AUTOPAINT_COLORKEY:
3384 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
3387 g_value_set_int (value, xvimagesink->colorkey);
3389 case PROP_DRAW_BORDERS:
3390 g_value_set_boolean (value, xvimagesink->draw_borders);
3392 case PROP_WINDOW_WIDTH:
3393 if (xvimagesink->xwindow)
3394 g_value_set_uint64 (value, xvimagesink->xwindow->width);
3396 g_value_set_uint64 (value, 0);
3398 case PROP_WINDOW_HEIGHT:
3399 if (xvimagesink->xwindow)
3400 g_value_set_uint64 (value, xvimagesink->xwindow->height);
3402 g_value_set_uint64 (value, 0);
3405 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3411 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
3415 GST_OBJECT_LOCK (xvimagesink);
3416 xvimagesink->running = FALSE;
3417 /* grab thread and mark it as NULL */
3418 thread = xvimagesink->event_thread;
3419 xvimagesink->event_thread = NULL;
3420 GST_OBJECT_UNLOCK (xvimagesink);
3422 /* invalidate the pool, current allocations continue, new buffer_alloc fails
3423 * with wrong_state */
3424 g_mutex_lock (xvimagesink->pool_lock);
3425 xvimagesink->pool_invalid = TRUE;
3426 g_mutex_unlock (xvimagesink->pool_lock);
3428 /* Wait for our event thread to finish before we clean up our stuff. */
3430 g_thread_join (thread);
3432 if (xvimagesink->cur_image) {
3433 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
3434 xvimagesink->cur_image = NULL;
3436 if (xvimagesink->xvimage) {
3437 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->xvimage));
3438 xvimagesink->xvimage = NULL;
3441 gst_xvimagesink_imagepool_clear (xvimagesink);
3443 if (xvimagesink->xwindow) {
3444 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
3445 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3446 xvimagesink->xwindow = NULL;
3449 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
3450 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
3451 xvimagesink->have_render_rect = FALSE;
3453 gst_xvimagesink_xcontext_clear (xvimagesink);
3456 /* Finalize is called only once, dispose can be called multiple times.
3457 * We use mutexes and don't reset stuff to NULL here so let's register
3460 gst_xvimagesink_finalize (GObject * object)
3462 GstXvImageSink *xvimagesink;
3464 xvimagesink = GST_XVIMAGESINK (object);
3466 gst_xvimagesink_reset (xvimagesink);
3468 if (xvimagesink->display_name) {
3469 g_free (xvimagesink->display_name);
3470 xvimagesink->display_name = NULL;
3473 if (xvimagesink->par) {
3474 g_free (xvimagesink->par);
3475 xvimagesink->par = NULL;
3477 if (xvimagesink->x_lock) {
3478 g_mutex_free (xvimagesink->x_lock);
3479 xvimagesink->x_lock = NULL;
3481 if (xvimagesink->flow_lock) {
3482 g_mutex_free (xvimagesink->flow_lock);
3483 xvimagesink->flow_lock = NULL;
3485 if (xvimagesink->pool_lock) {
3486 g_mutex_free (xvimagesink->pool_lock);
3487 xvimagesink->pool_lock = NULL;
3490 g_free (xvimagesink->media_title);
3492 G_OBJECT_CLASS (parent_class)->finalize (object);
3496 gst_xvimagesink_init (GstXvImageSink * xvimagesink,
3497 GstXvImageSinkClass * xvimagesinkclass)
3499 xvimagesink->display_name = NULL;
3500 xvimagesink->adaptor_no = 0;
3501 xvimagesink->xcontext = NULL;
3502 xvimagesink->xwindow = NULL;
3503 xvimagesink->xvimage = NULL;
3504 xvimagesink->cur_image = NULL;
3506 xvimagesink->hue = xvimagesink->saturation = 0;
3507 xvimagesink->contrast = xvimagesink->brightness = 0;
3508 xvimagesink->cb_changed = FALSE;
3510 xvimagesink->fps_n = 0;
3511 xvimagesink->fps_d = 0;
3512 xvimagesink->video_width = 0;
3513 xvimagesink->video_height = 0;
3515 xvimagesink->x_lock = g_mutex_new ();
3516 xvimagesink->flow_lock = g_mutex_new ();
3518 xvimagesink->image_pool = NULL;
3519 xvimagesink->pool_lock = g_mutex_new ();
3521 xvimagesink->synchronous = FALSE;
3522 xvimagesink->double_buffer = TRUE;
3523 xvimagesink->running = FALSE;
3524 xvimagesink->keep_aspect = FALSE;
3525 xvimagesink->handle_events = TRUE;
3526 xvimagesink->par = NULL;
3527 xvimagesink->handle_expose = TRUE;
3528 xvimagesink->autopaint_colorkey = TRUE;
3530 /* on 16bit displays this becomes r,g,b = 1,2,3
3531 * on 24bit displays this becomes r,g,b = 8,8,16
3532 * as a port atom value
3534 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
3535 xvimagesink->draw_borders = TRUE;
3539 gst_xvimagesink_base_init (gpointer g_class)
3541 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
3543 gst_element_class_set_details_simple (element_class,
3544 "Video sink", "Sink/Video",
3545 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
3547 gst_element_class_add_static_pad_template (element_class,
3548 &gst_xvimagesink_sink_template_factory);
3552 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
3554 GObjectClass *gobject_class;
3555 GstElementClass *gstelement_class;
3556 GstBaseSinkClass *gstbasesink_class;
3557 GstVideoSinkClass *videosink_class;
3559 gobject_class = (GObjectClass *) klass;
3560 gstelement_class = (GstElementClass *) klass;
3561 gstbasesink_class = (GstBaseSinkClass *) klass;
3562 videosink_class = (GstVideoSinkClass *) klass;
3564 gobject_class->set_property = gst_xvimagesink_set_property;
3565 gobject_class->get_property = gst_xvimagesink_get_property;
3567 g_object_class_install_property (gobject_class, PROP_CONTRAST,
3568 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
3569 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3570 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
3571 g_param_spec_int ("brightness", "Brightness",
3572 "The brightness of the video", -1000, 1000, 0,
3573 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3574 g_object_class_install_property (gobject_class, PROP_HUE,
3575 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
3576 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3577 g_object_class_install_property (gobject_class, PROP_SATURATION,
3578 g_param_spec_int ("saturation", "Saturation",
3579 "The saturation of the video", -1000, 1000, 0,
3580 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3581 g_object_class_install_property (gobject_class, PROP_DISPLAY,
3582 g_param_spec_string ("display", "Display", "X Display name", NULL,
3583 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3584 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
3585 g_param_spec_boolean ("synchronous", "Synchronous",
3586 "When enabled, runs the X display in synchronous mode. "
3587 "(unrelated to A/V sync, used only for debugging)", FALSE,
3588 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3589 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
3590 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
3591 "The pixel aspect ratio of the device", "1/1",
3592 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3593 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
3594 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
3595 "When enabled, scaling will respect original aspect ratio", FALSE,
3596 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3597 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
3598 g_param_spec_boolean ("handle-events", "Handle XEvents",
3599 "When enabled, XEvents will be selected and handled", TRUE,
3600 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3601 g_object_class_install_property (gobject_class, PROP_DEVICE,
3602 g_param_spec_string ("device", "Adaptor number",
3603 "The number of the video adaptor", "0",
3604 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3605 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
3606 g_param_spec_string ("device-name", "Adaptor name",
3607 "The name of the video adaptor", NULL,
3608 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3610 * GstXvImageSink:handle-expose
3612 * When enabled, the current frame will always be drawn in response to X
3617 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
3618 g_param_spec_boolean ("handle-expose", "Handle expose",
3620 "the current frame will always be drawn in response to X Expose "
3621 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3624 * GstXvImageSink:double-buffer
3626 * Whether to double-buffer the output.
3630 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
3631 g_param_spec_boolean ("double-buffer", "Double-buffer",
3632 "Whether to double-buffer the output", TRUE,
3633 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3635 * GstXvImageSink:autopaint-colorkey
3637 * Whether to autofill overlay with colorkey
3641 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
3642 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
3643 "Whether to autofill overlay with colorkey", TRUE,
3644 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3646 * GstXvImageSink:colorkey
3648 * Color to use for the overlay mask.
3652 g_object_class_install_property (gobject_class, PROP_COLORKEY,
3653 g_param_spec_int ("colorkey", "Colorkey",
3654 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
3655 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3658 * GstXvImageSink:draw-borders
3660 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
3661 * unused parts of the video area.
3665 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
3666 g_param_spec_boolean ("draw-borders", "Colorkey",
3667 "Draw black borders to fill unused area in force-aspect-ratio mode",
3668 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3671 * GstXvImageSink:window-width
3673 * Actual width of the video window.
3677 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
3678 g_param_spec_uint64 ("window-width", "window-width",
3679 "Width of the window", 0, G_MAXUINT64, 0,
3680 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3683 * GstXvImageSink:window-height
3685 * Actual height of the video window.
3689 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
3690 g_param_spec_uint64 ("window-height", "window-height",
3691 "Height of the window", 0, G_MAXUINT64, 0,
3692 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3694 gobject_class->finalize = gst_xvimagesink_finalize;
3696 gstelement_class->change_state =
3697 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3699 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3700 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3701 gstbasesink_class->buffer_alloc =
3702 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3703 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3704 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3706 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3709 /* ============================================================= */
3711 /* Public Methods */
3713 /* ============================================================= */
3715 /* =========================================== */
3717 /* Object typing & Creation */
3719 /* =========================================== */
3721 gst_xvimagesink_init_interfaces (GType type)
3723 static const GInterfaceInfo iface_info = {
3724 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3728 static const GInterfaceInfo navigation_info = {
3729 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3733 static const GInterfaceInfo overlay_info = {
3734 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3738 static const GInterfaceInfo colorbalance_info = {
3739 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3743 static const GInterfaceInfo propertyprobe_info = {
3744 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3749 g_type_add_interface_static (type,
3750 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3751 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
3752 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &overlay_info);
3753 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE,
3754 &colorbalance_info);
3755 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
3756 &propertyprobe_info);
3758 /* register type and create class in a more safe place instead of at
3759 * runtime since the type registration and class creation is not
3761 g_type_class_ref (gst_xvimage_buffer_get_type ());
3765 plugin_init (GstPlugin * plugin)
3767 if (!gst_element_register (plugin, "xvimagesink",
3768 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
3771 GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
3772 "xvimagesink element");
3773 GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
3778 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3781 "XFree86 video output plugin using Xv extension",
3782 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)