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 theorically
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>
129 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
130 #define GST_CAT_DEFAULT gst_debug_xvimagesink
131 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
136 unsigned long functions;
137 unsigned long decorations;
139 unsigned long status;
141 MotifWmHints, MwmHints;
143 #define MWM_HINTS_DECORATIONS (1L << 1)
145 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
147 static GstBufferClass *xvimage_buffer_parent_class = NULL;
148 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
150 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
152 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
154 static void gst_xvimagesink_expose (GstXOverlay * overlay);
156 /* Default template - initiated with class struct to allow gst-register to work
158 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
159 GST_STATIC_PAD_TEMPLATE ("sink",
162 GST_STATIC_CAPS ("video/x-raw-rgb, "
163 "framerate = (fraction) [ 0, MAX ], "
164 "width = (int) [ 1, MAX ], "
165 "height = (int) [ 1, MAX ]; "
167 "framerate = (fraction) [ 0, MAX ], "
168 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
180 PROP_PIXEL_ASPECT_RATIO,
181 PROP_FORCE_ASPECT_RATIO,
187 PROP_AUTOPAINT_COLORKEY,
194 static GstVideoSinkClass *parent_class = NULL;
196 /* ============================================================= */
198 /* Private Methods */
200 /* ============================================================= */
202 /* xvimage buffers */
204 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
206 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
207 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
208 #define GST_XVIMAGE_BUFFER_CAST(obj) ((GstXvImageBuffer *)(obj))
210 /* This function destroys a GstXvImage handling XShm availability */
212 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
214 GstXvImageSink *xvimagesink;
216 GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
218 xvimagesink = xvimage->xvimagesink;
219 if (G_UNLIKELY (xvimagesink == NULL))
222 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
224 GST_OBJECT_LOCK (xvimagesink);
226 /* If the destroyed image is the current one we destroy our reference too */
227 if (xvimagesink->cur_image == xvimage)
228 xvimagesink->cur_image = NULL;
230 /* We might have some buffers destroyed after changing state to NULL */
231 if (xvimagesink->xcontext == NULL) {
232 GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
234 /* Need to free the shared memory segment even if the x context
235 * was already cleaned up */
236 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
237 shmdt (xvimage->SHMInfo.shmaddr);
243 g_mutex_lock (xvimagesink->x_lock);
246 if (xvimagesink->xcontext->use_xshm) {
247 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
248 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
249 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
250 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
251 XSync (xvimagesink->xcontext->disp, FALSE);
253 shmdt (xvimage->SHMInfo.shmaddr);
255 if (xvimage->xvimage)
256 XFree (xvimage->xvimage);
258 #endif /* HAVE_XSHM */
260 if (xvimage->xvimage) {
261 if (xvimage->xvimage->data) {
262 g_free (xvimage->xvimage->data);
264 XFree (xvimage->xvimage);
268 XSync (xvimagesink->xcontext->disp, FALSE);
270 g_mutex_unlock (xvimagesink->x_lock);
273 GST_OBJECT_UNLOCK (xvimagesink);
274 xvimage->xvimagesink = NULL;
275 gst_object_unref (xvimagesink);
277 GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->finalize (GST_MINI_OBJECT
284 GST_WARNING ("no sink found");
290 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
292 GstXvImageSink *xvimagesink;
295 xvimagesink = xvimage->xvimagesink;
296 if (G_UNLIKELY (xvimagesink == NULL))
299 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
301 GST_OBJECT_LOCK (xvimagesink);
302 running = xvimagesink->running;
303 GST_OBJECT_UNLOCK (xvimagesink);
305 /* If our geometry changed we can't reuse that image. */
306 if (running == FALSE) {
307 GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
308 gst_xvimage_buffer_destroy (xvimage);
309 } else if ((xvimage->width != xvimagesink->video_width) ||
310 (xvimage->height != xvimagesink->video_height)) {
311 GST_LOG_OBJECT (xvimage,
312 "destroy image as its size changed %dx%d vs current %dx%d",
313 xvimage->width, xvimage->height,
314 xvimagesink->video_width, xvimagesink->video_height);
315 gst_xvimage_buffer_destroy (xvimage);
317 /* In that case we can reuse the image and add it to our image pool. */
318 GST_LOG_OBJECT (xvimage, "recycling image in pool");
319 /* need to increment the refcount again to recycle */
320 gst_buffer_ref (GST_BUFFER_CAST (xvimage));
321 g_mutex_lock (xvimagesink->pool_lock);
322 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
324 g_mutex_unlock (xvimagesink->pool_lock);
330 GST_WARNING ("no sink found");
336 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
338 /* make sure it is not recycled */
340 xvimage->height = -1;
341 gst_buffer_unref (GST_BUFFER (xvimage));
345 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
348 xvimage->SHMInfo.shmaddr = ((void *) -1);
349 xvimage->SHMInfo.shmid = -1;
354 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
356 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
358 xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
360 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
361 gst_xvimage_buffer_finalize;
365 gst_xvimage_buffer_get_type (void)
367 static GType _gst_xvimage_buffer_type;
369 if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
370 static const GTypeInfo xvimage_buffer_info = {
371 sizeof (GstBufferClass),
374 gst_xvimage_buffer_class_init,
377 sizeof (GstXvImageBuffer),
379 (GInstanceInitFunc) gst_xvimage_buffer_init,
382 _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
383 "GstXvImageBuffer", &xvimage_buffer_info, 0);
385 return _gst_xvimage_buffer_type;
390 static gboolean error_caught = FALSE;
393 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
395 char error_msg[1024];
397 XGetErrorText (display, xevent->error_code, error_msg, 1024);
398 GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
404 /* This function checks that it is actually really possible to create an image
407 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
410 XShmSegmentInfo SHMInfo;
412 int (*handler) (Display *, XErrorEvent *);
413 gboolean result = FALSE;
414 gboolean did_attach = FALSE;
416 g_return_val_if_fail (xcontext != NULL, FALSE);
418 /* Sync to ensure any older errors are already processed */
419 XSync (xcontext->disp, FALSE);
421 /* Set defaults so we don't free these later unnecessarily */
422 SHMInfo.shmaddr = ((void *) -1);
425 /* Setting an error handler to catch failure */
426 error_caught = FALSE;
427 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
429 /* Trying to create a 1x1 picture */
430 GST_DEBUG ("XvShmCreateImage of 1x1");
431 xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
432 xcontext->im_format, NULL, 1, 1, &SHMInfo);
434 /* Might cause an error, sync to ensure it is noticed */
435 XSync (xcontext->disp, FALSE);
436 if (!xvimage || error_caught) {
437 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
440 size = xvimage->data_size;
442 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
443 if (SHMInfo.shmid == -1) {
444 GST_WARNING ("could not get shared memory of %d bytes", size);
448 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
449 if (SHMInfo.shmaddr == ((void *) -1)) {
450 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
451 /* Clean up the shared memory segment */
452 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
456 xvimage->data = SHMInfo.shmaddr;
457 SHMInfo.readOnly = FALSE;
459 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
460 GST_WARNING ("Failed to XShmAttach");
461 /* Clean up the shared memory segment */
462 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
466 /* Sync to ensure we see any errors we caused */
467 XSync (xcontext->disp, FALSE);
469 /* Delete the shared memory segment as soon as everyone is attached.
470 * This way, it will be deleted as soon as we detach later, and not
471 * leaked if we crash. */
472 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
475 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
479 /* store whether we succeeded in result */
482 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
483 "Not using shared memory.");
487 /* Sync to ensure we swallow any errors we caused and reset error_caught */
488 XSync (xcontext->disp, FALSE);
490 error_caught = FALSE;
491 XSetErrorHandler (handler);
494 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
495 SHMInfo.shmid, SHMInfo.shmseg);
496 XShmDetach (xcontext->disp, &SHMInfo);
497 XSync (xcontext->disp, FALSE);
499 if (SHMInfo.shmaddr != ((void *) -1))
500 shmdt (SHMInfo.shmaddr);
505 #endif /* HAVE_XSHM */
507 /* This function handles GstXvImage creation depending on XShm availability */
508 static GstXvImageBuffer *
509 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
511 GstXvImageBuffer *xvimage = NULL;
512 GstStructure *structure = NULL;
513 gboolean succeeded = FALSE;
514 int (*handler) (Display *, XErrorEvent *);
516 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
521 xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
522 GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
524 structure = gst_caps_get_structure (caps, 0);
526 if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
527 !gst_structure_get_int (structure, "height", &xvimage->height)) {
528 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
531 GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
534 xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
535 if (xvimage->im_format == -1) {
536 GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
537 GST_PTR_FORMAT, caps);
538 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
539 ("Failed to create output image buffer of %dx%d pixels",
540 xvimage->width, xvimage->height), ("Invalid input caps"));
543 xvimage->xvimagesink = gst_object_ref (xvimagesink);
545 g_mutex_lock (xvimagesink->x_lock);
547 /* Setting an error handler to catch failure */
548 error_caught = FALSE;
549 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
552 if (xvimagesink->xcontext->use_xshm) {
555 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
556 xvimagesink->xcontext->xv_port_id,
557 xvimage->im_format, NULL,
558 xvimage->width, xvimage->height, &xvimage->SHMInfo);
559 if (!xvimage->xvimage || error_caught) {
560 g_mutex_unlock (xvimagesink->x_lock);
561 /* Reset error handler */
562 error_caught = FALSE;
563 XSetErrorHandler (handler);
565 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
566 ("Failed to create output image buffer of %dx%d pixels",
567 xvimage->width, xvimage->height),
568 ("could not XvShmCreateImage a %dx%d image",
569 xvimage->width, xvimage->height));
573 /* we have to use the returned data_size for our shm size */
574 xvimage->size = xvimage->xvimage->data_size;
575 GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
578 /* calculate the expected size. This is only for sanity checking the
579 * number we get from X. */
580 switch (xvimage->im_format) {
581 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
582 case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
589 pitches[0] = GST_ROUND_UP_4 (xvimage->width);
590 offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (xvimage->height);
591 pitches[1] = GST_ROUND_UP_8 (xvimage->width) / 2;
593 offsets[1] + pitches[1] * GST_ROUND_UP_2 (xvimage->height) / 2;
594 pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
597 offsets[2] + pitches[2] * GST_ROUND_UP_2 (xvimage->height) / 2;
599 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
600 GST_DEBUG_OBJECT (xvimagesink,
601 "Plane %u has a expected pitch of %d bytes, " "offset of %d",
602 plane, pitches[plane], offsets[plane]);
606 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
607 case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
608 expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
614 if (expected_size != 0 && xvimage->size != expected_size) {
615 GST_WARNING_OBJECT (xvimagesink,
616 "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
617 xvimage->size, expected_size);
620 /* Be verbose about our XvImage stride */
624 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
625 GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
626 "offset of %d", plane, xvimage->xvimage->pitches[plane],
627 xvimage->xvimage->offsets[plane]);
631 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
633 if (xvimage->SHMInfo.shmid == -1) {
634 g_mutex_unlock (xvimagesink->x_lock);
635 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
636 ("Failed to create output image buffer of %dx%d pixels",
637 xvimage->width, xvimage->height),
638 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
643 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
644 if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
645 g_mutex_unlock (xvimagesink->x_lock);
646 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
647 ("Failed to create output image buffer of %dx%d pixels",
648 xvimage->width, xvimage->height),
649 ("Failed to shmat: %s", g_strerror (errno)));
650 /* Clean up the shared memory segment */
651 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
655 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
656 xvimage->SHMInfo.readOnly = FALSE;
658 if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
659 /* Clean up the shared memory segment */
660 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
662 g_mutex_unlock (xvimagesink->x_lock);
663 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
664 ("Failed to create output image buffer of %dx%d pixels",
665 xvimage->width, xvimage->height), ("Failed to XShmAttach"));
669 XSync (xvimagesink->xcontext->disp, FALSE);
671 /* Delete the shared memory segment as soon as we everyone is attached.
672 * This way, it will be deleted as soon as we detach later, and not
673 * leaked if we crash. */
674 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
676 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
677 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
679 #endif /* HAVE_XSHM */
681 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
682 xvimagesink->xcontext->xv_port_id,
683 xvimage->im_format, NULL, xvimage->width, xvimage->height);
684 if (!xvimage->xvimage || error_caught) {
685 g_mutex_unlock (xvimagesink->x_lock);
686 /* Reset error handler */
687 error_caught = FALSE;
688 XSetErrorHandler (handler);
690 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
691 ("Failed to create outputimage buffer of %dx%d pixels",
692 xvimage->width, xvimage->height),
693 ("could not XvCreateImage a %dx%d image",
694 xvimage->width, xvimage->height));
698 /* we have to use the returned data_size for our image size */
699 xvimage->size = xvimage->xvimage->data_size;
700 xvimage->xvimage->data = g_malloc (xvimage->size);
702 XSync (xvimagesink->xcontext->disp, FALSE);
705 /* Reset error handler */
706 error_caught = FALSE;
707 XSetErrorHandler (handler);
711 GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
712 GST_BUFFER_SIZE (xvimage) = xvimage->size;
714 g_mutex_unlock (xvimagesink->x_lock);
718 gst_xvimage_buffer_free (xvimage);
725 /* We are called with the x_lock taken */
727 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
728 GstXWindow * xwindow, GstVideoRectangle rect)
732 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
733 g_return_if_fail (xwindow != NULL);
735 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
736 xvimagesink->xcontext->black);
739 if (rect.x > xvimagesink->render_rect.x) {
740 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
741 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
742 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
746 t1 = rect.x + rect.w;
747 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
749 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
750 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
754 if (rect.y > xvimagesink->render_rect.y) {
755 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
756 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
757 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
761 t1 = rect.y + rect.h;
762 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
764 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
765 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
769 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
770 * if no window was available */
772 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
773 GstXvImageBuffer * xvimage)
775 GstVideoRectangle result;
776 gboolean draw_border = FALSE;
778 /* We take the flow_lock. If expose is in there we don't want to run
779 concurrently from the data flow thread */
780 g_mutex_lock (xvimagesink->flow_lock);
782 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
783 g_mutex_unlock (xvimagesink->flow_lock);
787 /* Draw borders when displaying the first frame. After this
788 draw borders only on expose event or after a size change. */
789 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
793 /* Store a reference to the last image we put, lose the previous one */
794 if (xvimage && xvimagesink->cur_image != xvimage) {
795 if (xvimagesink->cur_image) {
796 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
797 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
799 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
800 xvimagesink->cur_image =
801 GST_XVIMAGE_BUFFER_CAST (gst_buffer_ref (GST_BUFFER_CAST (xvimage)));
804 /* Expose sends a NULL image, we take the latest frame */
806 if (xvimagesink->cur_image) {
808 xvimage = xvimagesink->cur_image;
810 g_mutex_unlock (xvimagesink->flow_lock);
815 if (xvimagesink->keep_aspect) {
816 GstVideoRectangle src, dst;
818 /* We use the calculated geometry from _setcaps as a source to respect
819 source and screen pixel aspect ratios. */
820 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
821 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
822 dst.w = xvimagesink->render_rect.w;
823 dst.h = xvimagesink->render_rect.h;
825 gst_video_sink_center_rect (src, dst, &result, TRUE);
826 result.x += xvimagesink->render_rect.x;
827 result.y += xvimagesink->render_rect.y;
829 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
832 g_mutex_lock (xvimagesink->x_lock);
834 if (draw_border && xvimagesink->draw_borders) {
835 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
837 xvimagesink->redraw_border = FALSE;
840 /* We scale to the window's geometry */
842 if (xvimagesink->xcontext->use_xshm) {
843 GST_LOG_OBJECT (xvimagesink,
844 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
846 xvimage->width, xvimage->height,
847 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
849 XvShmPutImage (xvimagesink->xcontext->disp,
850 xvimagesink->xcontext->xv_port_id,
851 xvimagesink->xwindow->win,
852 xvimagesink->xwindow->gc, xvimage->xvimage,
853 xvimagesink->disp_x, xvimagesink->disp_y,
854 xvimagesink->disp_width, xvimagesink->disp_height,
855 result.x, result.y, result.w, result.h, FALSE);
857 #endif /* HAVE_XSHM */
859 XvPutImage (xvimagesink->xcontext->disp,
860 xvimagesink->xcontext->xv_port_id,
861 xvimagesink->xwindow->win,
862 xvimagesink->xwindow->gc, xvimage->xvimage,
863 xvimagesink->disp_x, xvimagesink->disp_y,
864 xvimagesink->disp_width, xvimagesink->disp_height,
865 result.x, result.y, result.w, result.h);
868 XSync (xvimagesink->xcontext->disp, FALSE);
870 g_mutex_unlock (xvimagesink->x_lock);
872 g_mutex_unlock (xvimagesink->flow_lock);
878 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
881 Atom hints_atom = None;
884 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
885 g_return_val_if_fail (window != NULL, FALSE);
887 g_mutex_lock (xvimagesink->x_lock);
889 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
891 if (hints_atom == None) {
892 g_mutex_unlock (xvimagesink->x_lock);
896 hints = g_malloc0 (sizeof (MotifWmHints));
898 hints->flags |= MWM_HINTS_DECORATIONS;
899 hints->decorations = 1 << 0;
901 XChangeProperty (xvimagesink->xcontext->disp, window->win,
902 hints_atom, hints_atom, 32, PropModeReplace,
903 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
905 XSync (xvimagesink->xcontext->disp, FALSE);
907 g_mutex_unlock (xvimagesink->x_lock);
915 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
916 GstXWindow * xwindow, const gchar * media_title)
919 g_free (xvimagesink->media_title);
920 xvimagesink->media_title = g_strdup (media_title);
923 /* we have a window */
924 if (xwindow->internal) {
925 XTextProperty xproperty;
926 const gchar *app_name;
927 const gchar *title = NULL;
928 gchar *title_mem = NULL;
930 /* set application name as a title */
931 app_name = g_get_application_name ();
933 if (app_name && xvimagesink->media_title) {
934 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
936 } else if (app_name) {
938 } else if (xvimagesink->media_title) {
939 title = xvimagesink->media_title;
943 if ((XStringListToTextProperty (((char **) &title), 1,
945 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
946 XFree (xproperty.value);
955 /* This function handles a GstXWindow creation
956 * The width and height are the actual pixel size on the display */
958 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
959 gint width, gint height)
961 GstXWindow *xwindow = NULL;
964 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
966 xwindow = g_new0 (GstXWindow, 1);
968 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
969 xvimagesink->render_rect.w = width;
970 xvimagesink->render_rect.h = height;
972 xwindow->width = width;
973 xwindow->height = height;
974 xwindow->internal = TRUE;
976 g_mutex_lock (xvimagesink->x_lock);
978 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
979 xvimagesink->xcontext->root,
980 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
982 /* We have to do that to prevent X from redrawing the background on
983 * ConfigureNotify. This takes away flickering of video when resizing. */
984 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
986 /* set application name as a title */
987 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
989 if (xvimagesink->handle_events) {
992 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
993 StructureNotifyMask | PointerMotionMask | KeyPressMask |
994 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
996 /* Tell the window manager we'd like delete client messages instead of
998 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
999 "WM_DELETE_WINDOW", True);
1000 if (wm_delete != None) {
1001 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
1006 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1007 xwindow->win, 0, &values);
1009 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
1011 XSync (xvimagesink->xcontext->disp, FALSE);
1013 g_mutex_unlock (xvimagesink->x_lock);
1015 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
1017 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
1022 /* This function destroys a GstXWindow */
1024 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
1025 GstXWindow * xwindow)
1027 g_return_if_fail (xwindow != NULL);
1028 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1030 g_mutex_lock (xvimagesink->x_lock);
1032 /* If we did not create that window we just free the GC and let it live */
1033 if (xwindow->internal)
1034 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
1036 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
1038 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
1040 XSync (xvimagesink->xcontext->disp, FALSE);
1042 g_mutex_unlock (xvimagesink->x_lock);
1048 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
1050 XWindowAttributes attr;
1052 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1054 /* Update the window geometry */
1055 g_mutex_lock (xvimagesink->x_lock);
1056 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
1057 g_mutex_unlock (xvimagesink->x_lock);
1061 XGetWindowAttributes (xvimagesink->xcontext->disp,
1062 xvimagesink->xwindow->win, &attr);
1064 xvimagesink->xwindow->width = attr.width;
1065 xvimagesink->xwindow->height = attr.height;
1067 if (!xvimagesink->have_render_rect) {
1068 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1069 xvimagesink->render_rect.w = attr.width;
1070 xvimagesink->render_rect.h = attr.height;
1073 g_mutex_unlock (xvimagesink->x_lock);
1077 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
1078 GstXWindow * xwindow)
1080 g_return_if_fail (xwindow != NULL);
1081 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1083 g_mutex_lock (xvimagesink->x_lock);
1085 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1088 XSync (xvimagesink->xcontext->disp, FALSE);
1090 g_mutex_unlock (xvimagesink->x_lock);
1093 /* This function commits our internal colorbalance settings to our grabbed Xv
1094 port. If the xcontext is not initialized yet it simply returns */
1096 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1098 GList *channels = NULL;
1100 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1102 /* If we haven't initialized the X context we can't update anything */
1103 if (xvimagesink->xcontext == NULL)
1106 /* Don't set the attributes if they haven't been changed, to avoid
1107 * rounding errors changing the values */
1108 if (!xvimagesink->cb_changed)
1111 /* For each channel of the colorbalance we calculate the correct value
1112 doing range conversion and then set the Xv port attribute to match our
1114 channels = xvimagesink->xcontext->channels_list;
1117 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1118 GstColorBalanceChannel *channel = NULL;
1121 gdouble convert_coef;
1123 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1124 g_object_ref (channel);
1126 /* Our range conversion coef */
1127 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1129 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1130 value = xvimagesink->hue;
1131 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1132 value = xvimagesink->saturation;
1133 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1134 value = xvimagesink->contrast;
1135 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1136 value = xvimagesink->brightness;
1138 g_warning ("got an unknown channel %s", channel->label);
1139 g_object_unref (channel);
1143 /* Committing to Xv port */
1144 g_mutex_lock (xvimagesink->x_lock);
1146 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1147 if (prop_atom != None) {
1150 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
1151 XvSetPortAttribute (xvimagesink->xcontext->disp,
1152 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
1154 g_mutex_unlock (xvimagesink->x_lock);
1156 g_object_unref (channel);
1158 channels = g_list_next (channels);
1162 /* This function handles XEvents that might be in the queue. It generates
1163 GstEvent that will be sent upstream in the pipeline to handle interactivity
1164 and navigation. It will also listen for configure events on the window to
1165 trigger caps renegotiation so on the fly software scaling can work. */
1167 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1170 guint pointer_x = 0, pointer_y = 0;
1171 gboolean pointer_moved = FALSE;
1172 gboolean exposed = FALSE, configured = FALSE;
1174 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1176 /* Handle Interaction, produces navigation events */
1178 /* We get all pointer motion events, only the last position is
1180 g_mutex_lock (xvimagesink->flow_lock);
1181 g_mutex_lock (xvimagesink->x_lock);
1182 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1183 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1184 g_mutex_unlock (xvimagesink->x_lock);
1185 g_mutex_unlock (xvimagesink->flow_lock);
1189 pointer_x = e.xmotion.x;
1190 pointer_y = e.xmotion.y;
1191 pointer_moved = TRUE;
1196 g_mutex_lock (xvimagesink->flow_lock);
1197 g_mutex_lock (xvimagesink->x_lock);
1199 if (pointer_moved) {
1200 g_mutex_unlock (xvimagesink->x_lock);
1201 g_mutex_unlock (xvimagesink->flow_lock);
1203 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1204 pointer_x, pointer_y);
1205 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1206 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1208 g_mutex_lock (xvimagesink->flow_lock);
1209 g_mutex_lock (xvimagesink->x_lock);
1212 /* We get all events on our window to throw them upstream */
1213 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1214 xvimagesink->xwindow->win,
1215 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1219 /* We lock only for the X function call */
1220 g_mutex_unlock (xvimagesink->x_lock);
1221 g_mutex_unlock (xvimagesink->flow_lock);
1225 /* Mouse button pressed over our window. We send upstream
1226 events for interactivity/navigation */
1227 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1228 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1229 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1230 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1233 /* Mouse button released over our window. We send upstream
1234 events for interactivity/navigation */
1235 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1236 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1237 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1238 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1242 /* Key pressed/released over our window. We send upstream
1243 events for interactivity/navigation */
1244 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1245 e.xkey.keycode, e.xkey.x, e.xkey.y);
1246 g_mutex_lock (xvimagesink->x_lock);
1247 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1249 g_mutex_unlock (xvimagesink->x_lock);
1250 if (keysym != NoSymbol) {
1251 char *key_str = NULL;
1253 g_mutex_lock (xvimagesink->x_lock);
1254 key_str = XKeysymToString (keysym);
1255 g_mutex_unlock (xvimagesink->x_lock);
1256 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1257 e.type == KeyPress ? "key-press" : "key-release", key_str);
1259 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1260 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1264 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1266 g_mutex_lock (xvimagesink->flow_lock);
1267 g_mutex_lock (xvimagesink->x_lock);
1271 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1272 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1277 case ConfigureNotify:
1278 g_mutex_unlock (xvimagesink->x_lock);
1279 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1280 g_mutex_lock (xvimagesink->x_lock);
1288 if (xvimagesink->handle_expose && (exposed || configured)) {
1289 g_mutex_unlock (xvimagesink->x_lock);
1290 g_mutex_unlock (xvimagesink->flow_lock);
1292 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1294 g_mutex_lock (xvimagesink->flow_lock);
1295 g_mutex_lock (xvimagesink->x_lock);
1298 /* Handle Display events */
1299 while (XPending (xvimagesink->xcontext->disp)) {
1300 XNextEvent (xvimagesink->xcontext->disp, &e);
1303 case ClientMessage:{
1306 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1307 "WM_DELETE_WINDOW", True);
1308 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1309 /* Handle window deletion by posting an error on the bus */
1310 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1311 ("Output window was closed"), (NULL));
1313 g_mutex_unlock (xvimagesink->x_lock);
1314 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1315 xvimagesink->xwindow = NULL;
1316 g_mutex_lock (xvimagesink->x_lock);
1325 g_mutex_unlock (xvimagesink->x_lock);
1326 g_mutex_unlock (xvimagesink->flow_lock);
1330 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1331 XvAdaptorInfo * adaptors, int adaptor_no)
1336 /* Do we support XvImageMask ? */
1337 if (!(adaptors[adaptor_no].type & XvImageMask)) {
1338 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1339 adaptors[adaptor_no].name);
1343 /* We found such an adaptor, looking for an available port */
1344 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1345 /* We try to grab the port */
1346 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1347 if (Success == res) {
1348 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1349 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1350 adaptors[adaptor_no].num_ports);
1352 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1353 adaptors[adaptor_no].name, res);
1358 /* This function generates a caps with all supported format by the first
1359 Xv grabable port we find. We store each one of the supported formats in a
1360 format list and append the format to a newly created caps that we return
1361 If this function does not return NULL because of an error, it also grabs
1362 the port via XvGrabPort */
1364 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1365 GstXContext * xcontext)
1368 XvAdaptorInfo *adaptors;
1370 XvImageFormatValues *formats = NULL;
1372 XvEncodingInfo *encodings = NULL;
1373 gulong max_w = G_MAXINT, max_h = G_MAXINT;
1374 GstCaps *caps = NULL;
1375 GstCaps *rgb_caps = NULL;
1377 g_return_val_if_fail (xcontext != NULL, NULL);
1379 /* First let's check that XVideo extension is available */
1380 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1381 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1382 ("Could not initialise Xv output"),
1383 ("XVideo extension is not available"));
1387 /* Then we get adaptors list */
1388 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1389 &xcontext->nb_adaptors, &adaptors)) {
1390 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1391 ("Could not initialise Xv output"),
1392 ("Failed getting XV adaptors list"));
1396 xcontext->xv_port_id = 0;
1398 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1400 xcontext->adaptors =
1401 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
1403 /* Now fill up our adaptor name array */
1404 for (i = 0; i < xcontext->nb_adaptors; i++) {
1405 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
1408 if (xvimagesink->adaptor_no >= 0 &&
1409 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
1410 /* Find xv port from user defined adaptor */
1411 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
1412 xvimagesink->adaptor_no);
1415 if (!xcontext->xv_port_id) {
1416 /* Now search for an adaptor that supports XvImageMask */
1417 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
1418 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
1419 xvimagesink->adaptor_no = i;
1423 XvFreeAdaptorInfo (adaptors);
1425 if (!xcontext->xv_port_id) {
1426 xvimagesink->adaptor_no = -1;
1427 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
1428 ("Could not initialise Xv output"), ("No port available"));
1432 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1434 int count, todo = 3;
1435 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1436 xcontext->xv_port_id, &count);
1437 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1438 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
1439 static const char colorkey[] = "XV_COLORKEY";
1441 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
1443 xvimagesink->have_autopaint_colorkey = FALSE;
1444 xvimagesink->have_double_buffer = FALSE;
1445 xvimagesink->have_colorkey = FALSE;
1447 for (i = 0; ((i < count) && todo); i++)
1448 if (!strcmp (attr[i].name, autopaint)) {
1449 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
1451 /* turn on autopaint colorkey */
1452 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1453 (xvimagesink->autopaint_colorkey ? 1 : 0));
1455 xvimagesink->have_autopaint_colorkey = TRUE;
1456 } else if (!strcmp (attr[i].name, dbl_buffer)) {
1457 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
1459 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1460 (xvimagesink->double_buffer ? 1 : 0));
1462 xvimagesink->have_double_buffer = TRUE;
1463 } else if (!strcmp (attr[i].name, colorkey)) {
1464 /* Set the colorkey, default is something that is dark but hopefully
1465 * won't randomly appear on the screen elsewhere (ie not black or greys)
1466 * can be overridden by setting "colorkey" property
1468 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
1470 gboolean set_attr = TRUE;
1473 /* set a colorkey in the right format RGB565/RGB888
1474 * We only handle these 2 cases, because they're the only types of
1475 * devices we've encountered. If we don't recognise it, leave it alone
1477 cr = (xvimagesink->colorkey >> 16);
1478 cg = (xvimagesink->colorkey >> 8) & 0xFF;
1479 cb = (xvimagesink->colorkey) & 0xFF;
1480 switch (xcontext->depth) {
1481 case 16: /* RGB 565 */
1485 ckey = (cr << 11) | (cg << 5) | cb;
1488 case 32: /* RGB 888 / ARGB 8888 */
1489 ckey = (cr << 16) | (cg << 8) | cb;
1492 GST_DEBUG_OBJECT (xvimagesink,
1493 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1500 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1501 (guint32) attr[i].max_value);
1502 GST_LOG_OBJECT (xvimagesink,
1503 "Setting color key for display depth %d to 0x%x",
1504 xcontext->depth, ckey);
1506 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1510 xvimagesink->have_colorkey = TRUE;
1516 /* Get the list of encodings supported by the adapter and look for the
1517 * XV_IMAGE encoding so we can determine the maximum width and height
1519 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1522 for (i = 0; i < nb_encodings; i++) {
1523 GST_LOG_OBJECT (xvimagesink,
1524 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1525 i, encodings[i].name, encodings[i].width, encodings[i].height,
1526 encodings[i].rate.numerator, encodings[i].rate.denominator);
1527 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1528 max_w = encodings[i].width;
1529 max_h = encodings[i].height;
1533 XvFreeEncodingInfo (encodings);
1535 /* We get all image formats supported by our port */
1536 formats = XvListImageFormats (xcontext->disp,
1537 xcontext->xv_port_id, &nb_formats);
1538 caps = gst_caps_new_empty ();
1539 for (i = 0; i < nb_formats; i++) {
1540 GstCaps *format_caps = NULL;
1541 gboolean is_rgb_format = FALSE;
1543 /* We set the image format of the xcontext to an existing one. This
1544 is just some valid image format for making our xshm calls check before
1545 caps negotiation really happens. */
1546 xcontext->im_format = formats[i].id;
1548 switch (formats[i].type) {
1551 XvImageFormatValues *fmt = &(formats[i]);
1552 gint endianness = G_BIG_ENDIAN;
1554 if (fmt->byte_order == LSBFirst) {
1555 /* our caps system handles 24/32bpp RGB as big-endian. */
1556 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1557 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1558 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1559 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1561 if (fmt->bits_per_pixel == 24) {
1562 fmt->red_mask >>= 8;
1563 fmt->green_mask >>= 8;
1564 fmt->blue_mask >>= 8;
1567 endianness = G_LITTLE_ENDIAN;
1570 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1571 "endianness", G_TYPE_INT, endianness,
1572 "depth", G_TYPE_INT, fmt->depth,
1573 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1574 "red_mask", G_TYPE_INT, fmt->red_mask,
1575 "green_mask", G_TYPE_INT, fmt->green_mask,
1576 "blue_mask", G_TYPE_INT, fmt->blue_mask,
1577 "width", GST_TYPE_INT_RANGE, 1, max_w,
1578 "height", GST_TYPE_INT_RANGE, 1, max_h,
1579 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1581 is_rgb_format = TRUE;
1585 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1586 "format", GST_TYPE_FOURCC, formats[i].id,
1587 "width", GST_TYPE_INT_RANGE, 1, max_w,
1588 "height", GST_TYPE_INT_RANGE, 1, max_h,
1589 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1592 g_assert_not_reached ();
1597 GstXvImageFormat *format = NULL;
1599 format = g_new0 (GstXvImageFormat, 1);
1601 format->format = formats[i].id;
1602 format->caps = gst_caps_copy (format_caps);
1603 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1606 if (is_rgb_format) {
1607 if (rgb_caps == NULL)
1608 rgb_caps = format_caps;
1610 gst_caps_append (rgb_caps, format_caps);
1612 gst_caps_append (caps, format_caps);
1616 /* Collected all caps into either the caps or rgb_caps structures.
1617 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1619 gst_caps_append (caps, rgb_caps);
1624 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1626 if (gst_caps_is_empty (caps)) {
1627 gst_caps_unref (caps);
1628 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1629 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1630 ("No supported format found"));
1638 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1640 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1642 GST_OBJECT_LOCK (xvimagesink);
1643 while (xvimagesink->running) {
1644 GST_OBJECT_UNLOCK (xvimagesink);
1646 if (xvimagesink->xwindow) {
1647 gst_xvimagesink_handle_xevents (xvimagesink);
1649 /* FIXME: do we want to align this with the framerate or anything else? */
1650 g_usleep (G_USEC_PER_SEC / 20);
1652 GST_OBJECT_LOCK (xvimagesink);
1654 GST_OBJECT_UNLOCK (xvimagesink);
1660 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1662 GThread *thread = NULL;
1664 /* don't start the thread too early */
1665 if (xvimagesink->xcontext == NULL) {
1669 GST_OBJECT_LOCK (xvimagesink);
1670 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1671 if (!xvimagesink->event_thread) {
1672 /* Setup our event listening thread */
1673 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1674 xvimagesink->handle_expose, xvimagesink->handle_events);
1675 xvimagesink->running = TRUE;
1676 xvimagesink->event_thread = g_thread_create (
1677 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1680 if (xvimagesink->event_thread) {
1681 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1682 xvimagesink->handle_expose, xvimagesink->handle_events);
1683 xvimagesink->running = FALSE;
1684 /* grab thread and mark it as NULL */
1685 thread = xvimagesink->event_thread;
1686 xvimagesink->event_thread = NULL;
1689 GST_OBJECT_UNLOCK (xvimagesink);
1691 /* Wait for our event thread to finish */
1693 g_thread_join (thread);
1698 /* This function calculates the pixel aspect ratio based on the properties
1699 * in the xcontext structure and stores it there. */
1701 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1703 static const gint par[][2] = {
1704 {1, 1}, /* regular screen */
1705 {16, 15}, /* PAL TV */
1706 {11, 10}, /* 525 line Rec.601 video */
1707 {54, 59}, /* 625 line Rec.601 video */
1708 {64, 45}, /* 1280x1024 on 16:9 display */
1709 {5, 3}, /* 1280x1024 on 4:3 display */
1710 {4, 3} /* 800x600 on 16:9 display */
1717 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1719 /* first calculate the "real" ratio based on the X values;
1720 * which is the "physical" w/h divided by the w/h in pixels of the display */
1721 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1722 / (xcontext->heightmm * xcontext->width);
1724 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1726 if (xcontext->width == 720 && xcontext->height == 576) {
1727 ratio = 4.0 * 576 / (3.0 * 720);
1729 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1730 /* now find the one from par[][2] with the lowest delta to the real one */
1734 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1735 gdouble this_delta = DELTA (i);
1737 if (this_delta < delta) {
1743 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1744 par[index][0], par[index][1]);
1746 g_free (xcontext->par);
1747 xcontext->par = g_new0 (GValue, 1);
1748 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1749 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1750 GST_DEBUG ("set xcontext PAR to %d/%d",
1751 gst_value_get_fraction_numerator (xcontext->par),
1752 gst_value_get_fraction_denominator (xcontext->par));
1755 /* This function gets the X Display and global info about it. Everything is
1756 stored in our object and will be cleaned when the object is disposed. Note
1757 here that caps for supported format are generated without any window or
1759 static GstXContext *
1760 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1762 GstXContext *xcontext = NULL;
1763 XPixmapFormatValues *px_formats = NULL;
1764 gint nb_formats = 0, i, j, N_attr;
1765 XvAttribute *xv_attr;
1767 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1768 "XV_BRIGHTNESS", "XV_CONTRAST"
1771 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1773 xcontext = g_new0 (GstXContext, 1);
1774 xcontext->im_format = 0;
1776 g_mutex_lock (xvimagesink->x_lock);
1778 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1780 if (!xcontext->disp) {
1781 g_mutex_unlock (xvimagesink->x_lock);
1783 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1784 ("Could not initialise Xv output"), ("Could not open display"));
1788 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1789 xcontext->screen_num = DefaultScreen (xcontext->disp);
1790 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1791 xcontext->root = DefaultRootWindow (xcontext->disp);
1792 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1793 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1794 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1796 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1797 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1798 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1799 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1801 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1802 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1804 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1805 /* We get supported pixmap formats at supported depth */
1806 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1809 XCloseDisplay (xcontext->disp);
1810 g_mutex_unlock (xvimagesink->x_lock);
1811 g_free (xcontext->par);
1813 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1814 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1818 /* We get bpp value corresponding to our running depth */
1819 for (i = 0; i < nb_formats; i++) {
1820 if (px_formats[i].depth == xcontext->depth)
1821 xcontext->bpp = px_formats[i].bits_per_pixel;
1826 xcontext->endianness =
1827 (ImageByteOrder (xcontext->disp) ==
1828 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1830 /* our caps system handles 24/32bpp RGB as big-endian. */
1831 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1832 xcontext->endianness == G_LITTLE_ENDIAN) {
1833 xcontext->endianness = G_BIG_ENDIAN;
1834 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1835 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1836 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1837 if (xcontext->bpp == 24) {
1838 xcontext->visual->red_mask >>= 8;
1839 xcontext->visual->green_mask >>= 8;
1840 xcontext->visual->blue_mask >>= 8;
1844 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1846 if (!xcontext->caps) {
1847 XCloseDisplay (xcontext->disp);
1848 g_mutex_unlock (xvimagesink->x_lock);
1849 g_free (xcontext->par);
1851 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1855 /* Search for XShm extension support */
1856 if (XShmQueryExtension (xcontext->disp) &&
1857 gst_xvimagesink_check_xshm_calls (xcontext)) {
1858 xcontext->use_xshm = TRUE;
1859 GST_DEBUG ("xvimagesink is using XShm extension");
1861 #endif /* HAVE_XSHM */
1863 xcontext->use_xshm = FALSE;
1864 GST_DEBUG ("xvimagesink is not using XShm extension");
1867 xv_attr = XvQueryPortAttributes (xcontext->disp,
1868 xcontext->xv_port_id, &N_attr);
1871 /* Generate the channels list */
1872 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1873 XvAttribute *matching_attr = NULL;
1875 /* Retrieve the property atom if it exists. If it doesn't exist,
1876 * the attribute itself must not either, so we can skip */
1877 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1878 if (prop_atom == None)
1881 if (xv_attr != NULL) {
1882 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1883 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1884 matching_attr = xv_attr + j;
1887 if (matching_attr) {
1888 GstColorBalanceChannel *channel;
1890 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1891 channel->label = g_strdup (channels[i]);
1892 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1893 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1895 xcontext->channels_list = g_list_append (xcontext->channels_list,
1898 /* If the colorbalance settings have not been touched we get Xv values
1899 as defaults and update our internal variables */
1900 if (!xvimagesink->cb_changed) {
1903 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1905 /* Normalize val to [-1000, 1000] */
1906 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1907 (double) (channel->max_value - channel->min_value));
1909 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1910 xvimagesink->hue = val;
1911 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1912 xvimagesink->saturation = val;
1913 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1914 xvimagesink->brightness = val;
1915 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1916 xvimagesink->contrast = val;
1924 g_mutex_unlock (xvimagesink->x_lock);
1929 /* This function cleans the X context. Closing the Display, releasing the XV
1930 port and unrefing the caps for supported formats. */
1932 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1934 GList *formats_list, *channels_list;
1935 GstXContext *xcontext;
1938 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1940 GST_OBJECT_LOCK (xvimagesink);
1941 if (xvimagesink->xcontext == NULL) {
1942 GST_OBJECT_UNLOCK (xvimagesink);
1946 /* Take the XContext from the sink and clean it up */
1947 xcontext = xvimagesink->xcontext;
1948 xvimagesink->xcontext = NULL;
1950 GST_OBJECT_UNLOCK (xvimagesink);
1953 formats_list = xcontext->formats_list;
1955 while (formats_list) {
1956 GstXvImageFormat *format = formats_list->data;
1958 gst_caps_unref (format->caps);
1960 formats_list = g_list_next (formats_list);
1963 if (xcontext->formats_list)
1964 g_list_free (xcontext->formats_list);
1966 channels_list = xcontext->channels_list;
1968 while (channels_list) {
1969 GstColorBalanceChannel *channel = channels_list->data;
1971 g_object_unref (channel);
1972 channels_list = g_list_next (channels_list);
1975 if (xcontext->channels_list)
1976 g_list_free (xcontext->channels_list);
1978 gst_caps_unref (xcontext->caps);
1979 if (xcontext->last_caps)
1980 gst_caps_replace (&xcontext->last_caps, NULL);
1982 for (i = 0; i < xcontext->nb_adaptors; i++) {
1983 g_free (xcontext->adaptors[i]);
1986 g_free (xcontext->adaptors);
1988 g_free (xcontext->par);
1990 g_mutex_lock (xvimagesink->x_lock);
1992 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1994 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1996 XCloseDisplay (xcontext->disp);
1998 g_mutex_unlock (xvimagesink->x_lock);
2004 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
2006 g_mutex_lock (xvimagesink->pool_lock);
2008 while (xvimagesink->image_pool) {
2009 GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
2011 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2012 xvimagesink->image_pool);
2013 gst_xvimage_buffer_free (xvimage);
2016 g_mutex_unlock (xvimagesink->pool_lock);
2021 /* This function tries to get a format matching with a given caps in the
2022 supported list of formats we generated in gst_xvimagesink_get_xv_support */
2024 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
2029 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2031 list = xvimagesink->xcontext->formats_list;
2034 GstXvImageFormat *format = list->data;
2037 if (gst_caps_can_intersect (caps, format->caps)) {
2038 return format->format;
2041 list = g_list_next (list);
2048 gst_xvimagesink_getcaps (GstBaseSink * bsink)
2050 GstXvImageSink *xvimagesink;
2052 xvimagesink = GST_XVIMAGESINK (bsink);
2054 if (xvimagesink->xcontext)
2055 return gst_caps_ref (xvimagesink->xcontext->caps);
2058 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
2063 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2065 GstXvImageSink *xvimagesink;
2066 GstStructure *structure;
2067 guint32 im_format = 0;
2069 gint video_width, video_height;
2070 gint disp_x, disp_y;
2071 gint disp_width, disp_height;
2072 gint video_par_n, video_par_d; /* video's PAR */
2073 gint display_par_n, display_par_d; /* display's PAR */
2074 const GValue *caps_par;
2075 const GValue *caps_disp_reg;
2079 xvimagesink = GST_XVIMAGESINK (bsink);
2081 GST_DEBUG_OBJECT (xvimagesink,
2082 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
2083 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
2085 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
2086 goto incompatible_caps;
2088 structure = gst_caps_get_structure (caps, 0);
2089 ret = gst_structure_get_int (structure, "width", &video_width);
2090 ret &= gst_structure_get_int (structure, "height", &video_height);
2091 fps = gst_structure_get_value (structure, "framerate");
2092 ret &= (fps != NULL);
2095 goto incomplete_caps;
2097 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
2098 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
2100 xvimagesink->video_width = video_width;
2101 xvimagesink->video_height = video_height;
2103 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
2104 if (im_format == -1)
2105 goto invalid_format;
2107 /* get aspect ratio from caps if it's present, and
2108 * convert video width and height to a display width and height
2109 * using wd / hd = wv / hv * PARv / PARd */
2111 /* get video's PAR */
2112 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
2114 video_par_n = gst_value_get_fraction_numerator (caps_par);
2115 video_par_d = gst_value_get_fraction_denominator (caps_par);
2120 /* get display's PAR */
2121 if (xvimagesink->par) {
2122 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2123 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2129 /* get the display region */
2130 caps_disp_reg = gst_structure_get_value (structure, "display-region");
2131 if (caps_disp_reg) {
2132 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
2133 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
2134 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
2136 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
2138 disp_x = disp_y = 0;
2139 disp_width = video_width;
2140 disp_height = video_height;
2143 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2144 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
2147 xvimagesink->disp_x = disp_x;
2148 xvimagesink->disp_y = disp_y;
2149 xvimagesink->disp_width = disp_width;
2150 xvimagesink->disp_height = disp_height;
2152 GST_DEBUG_OBJECT (xvimagesink,
2153 "video width/height: %dx%d, calculated display ratio: %d/%d",
2154 video_width, video_height, num, den);
2156 /* now find a width x height that respects this display ratio.
2157 * prefer those that have one of w/h the same as the incoming video
2158 * using wd / hd = num / den */
2160 /* start with same height, because of interlaced video */
2161 /* check hd / den is an integer scale factor, and scale wd with the PAR */
2162 if (video_height % den == 0) {
2163 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2164 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2165 gst_util_uint64_scale_int (video_height, num, den);
2166 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2167 } else if (video_width % num == 0) {
2168 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2169 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2170 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2171 gst_util_uint64_scale_int (video_width, den, num);
2173 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2174 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2175 gst_util_uint64_scale_int (video_height, num, den);
2176 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2178 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2179 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2181 /* Notify application to set xwindow id now */
2182 g_mutex_lock (xvimagesink->flow_lock);
2183 if (!xvimagesink->xwindow) {
2184 g_mutex_unlock (xvimagesink->flow_lock);
2185 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2187 g_mutex_unlock (xvimagesink->flow_lock);
2190 /* Creating our window and our image with the display size in pixels */
2191 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2192 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
2193 goto no_display_size;
2195 g_mutex_lock (xvimagesink->flow_lock);
2196 if (!xvimagesink->xwindow) {
2197 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2198 GST_VIDEO_SINK_WIDTH (xvimagesink),
2199 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2202 /* After a resize, we want to redraw the borders in case the new frame size
2203 * doesn't cover the same area */
2204 xvimagesink->redraw_border = TRUE;
2206 /* We renew our xvimage only if size or format changed;
2207 * the xvimage is the same size as the video pixel size */
2208 if ((xvimagesink->xvimage) &&
2209 ((im_format != xvimagesink->xvimage->im_format) ||
2210 (video_width != xvimagesink->xvimage->width) ||
2211 (video_height != xvimagesink->xvimage->height))) {
2212 GST_DEBUG_OBJECT (xvimagesink,
2213 "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2214 GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2215 GST_FOURCC_ARGS (im_format));
2216 GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2217 gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2218 xvimagesink->xvimage = NULL;
2221 g_mutex_unlock (xvimagesink->flow_lock);
2228 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
2233 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
2234 "height or framerate from intersected caps");
2239 GST_DEBUG_OBJECT (xvimagesink,
2240 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
2245 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2246 ("Error calculating the output display ratio of the video."));
2251 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2252 ("Error calculating the output display ratio of the video."));
2257 static GstStateChangeReturn
2258 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2260 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2261 GstXvImageSink *xvimagesink;
2262 GstXContext *xcontext = NULL;
2264 xvimagesink = GST_XVIMAGESINK (element);
2266 switch (transition) {
2267 case GST_STATE_CHANGE_NULL_TO_READY:
2268 /* Initializing the XContext */
2269 if (xvimagesink->xcontext == NULL) {
2270 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2271 if (xcontext == NULL)
2272 return GST_STATE_CHANGE_FAILURE;
2273 GST_OBJECT_LOCK (xvimagesink);
2275 xvimagesink->xcontext = xcontext;
2276 GST_OBJECT_UNLOCK (xvimagesink);
2279 /* update object's par with calculated one if not set yet */
2280 if (!xvimagesink->par) {
2281 xvimagesink->par = g_new0 (GValue, 1);
2282 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2283 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2285 /* call XSynchronize with the current value of synchronous */
2286 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2287 xvimagesink->synchronous ? "TRUE" : "FALSE");
2288 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2289 gst_xvimagesink_update_colorbalance (xvimagesink);
2290 gst_xvimagesink_manage_event_thread (xvimagesink);
2292 case GST_STATE_CHANGE_READY_TO_PAUSED:
2293 g_mutex_lock (xvimagesink->pool_lock);
2294 xvimagesink->pool_invalid = FALSE;
2295 g_mutex_unlock (xvimagesink->pool_lock);
2297 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2299 case GST_STATE_CHANGE_PAUSED_TO_READY:
2300 g_mutex_lock (xvimagesink->pool_lock);
2301 xvimagesink->pool_invalid = TRUE;
2302 g_mutex_unlock (xvimagesink->pool_lock);
2308 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2310 switch (transition) {
2311 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2313 case GST_STATE_CHANGE_PAUSED_TO_READY:
2314 xvimagesink->fps_n = 0;
2315 xvimagesink->fps_d = 1;
2316 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2317 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2319 case GST_STATE_CHANGE_READY_TO_NULL:
2320 gst_xvimagesink_reset (xvimagesink);
2330 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2331 GstClockTime * start, GstClockTime * end)
2333 GstXvImageSink *xvimagesink;
2335 xvimagesink = GST_XVIMAGESINK (bsink);
2337 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2338 *start = GST_BUFFER_TIMESTAMP (buf);
2339 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2340 *end = *start + GST_BUFFER_DURATION (buf);
2342 if (xvimagesink->fps_n > 0) {
2344 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2345 xvimagesink->fps_n);
2351 static GstFlowReturn
2352 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2354 GstXvImageSink *xvimagesink;
2356 xvimagesink = GST_XVIMAGESINK (vsink);
2358 /* If this buffer has been allocated using our buffer management we simply
2359 put the ximage which is in the PRIVATE pointer */
2360 if (GST_IS_XVIMAGE_BUFFER (buf)) {
2361 GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
2362 if (!gst_xvimagesink_xvimage_put (xvimagesink,
2363 GST_XVIMAGE_BUFFER_CAST (buf)))
2366 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
2367 "slow copy into bufferpool buffer %p", buf);
2368 /* Else we have to copy the data into our private image, */
2369 /* if we have one... */
2370 if (!xvimagesink->xvimage) {
2371 GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
2373 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
2374 GST_BUFFER_CAPS (buf));
2376 if (!xvimagesink->xvimage)
2377 /* The create method should have posted an informative error */
2380 if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
2381 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2382 ("Failed to create output image buffer of %dx%d pixels",
2383 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
2384 ("XServer allocated buffer size did not match input buffer"));
2386 gst_xvimage_buffer_destroy (xvimagesink->xvimage);
2387 xvimagesink->xvimage = NULL;
2392 memcpy (xvimagesink->xvimage->xvimage->data,
2393 GST_BUFFER_DATA (buf),
2394 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
2396 if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
2405 /* No image available. That's very bad ! */
2406 GST_WARNING_OBJECT (xvimagesink, "could not create image");
2407 return GST_FLOW_ERROR;
2411 /* No Window available to put our image into */
2412 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
2413 return GST_FLOW_ERROR;
2418 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
2420 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
2422 switch (GST_EVENT_TYPE (event)) {
2423 case GST_EVENT_TAG:{
2425 gchar *title = NULL;
2427 gst_event_parse_tag (event, &l);
2428 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
2431 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
2432 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
2442 if (GST_BASE_SINK_CLASS (parent_class)->event)
2443 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
2448 /* Buffer management */
2451 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
2454 GstCaps *intersection;
2458 gint par_n = 1, par_d = 1;
2462 new_caps = gst_caps_copy (caps);
2464 s = gst_caps_get_structure (new_caps, 0);
2466 gst_structure_get_int (s, "width", &width);
2467 gst_structure_get_int (s, "height", &height);
2468 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
2470 gst_structure_remove_field (s, "width");
2471 gst_structure_remove_field (s, "height");
2472 gst_structure_remove_field (s, "pixel-aspect-ratio");
2474 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2475 gst_caps_unref (new_caps);
2477 if (gst_caps_is_empty (intersection))
2478 return intersection;
2480 s = gst_caps_get_structure (intersection, 0);
2482 gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
2484 /* xvimagesink supports all PARs */
2486 gst_structure_fixate_field_nearest_int (s, "width", width);
2487 gst_structure_fixate_field_nearest_int (s, "height", height);
2488 gst_structure_get_int (s, "width", &w);
2489 gst_structure_get_int (s, "height", &h);
2491 gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
2492 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
2495 return intersection;
2498 static GstFlowReturn
2499 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
2500 GstCaps * caps, GstBuffer ** buf)
2502 GstFlowReturn ret = GST_FLOW_OK;
2503 GstXvImageSink *xvimagesink;
2504 GstXvImageBuffer *xvimage = NULL;
2505 GstCaps *intersection = NULL;
2506 GstStructure *structure = NULL;
2507 gint width, height, image_format;
2509 xvimagesink = GST_XVIMAGESINK (bsink);
2511 if (G_UNLIKELY (!caps))
2514 g_mutex_lock (xvimagesink->pool_lock);
2515 if (G_UNLIKELY (xvimagesink->pool_invalid))
2518 if (G_LIKELY (xvimagesink->xcontext->last_caps &&
2519 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
2520 GST_LOG_OBJECT (xvimagesink,
2521 "buffer alloc for same last_caps, reusing caps");
2522 intersection = gst_caps_ref (caps);
2523 image_format = xvimagesink->xcontext->last_format;
2524 width = xvimagesink->xcontext->last_width;
2525 height = xvimagesink->xcontext->last_height;
2527 goto reuse_last_caps;
2530 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
2531 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
2532 caps, xvimagesink->xcontext->caps);
2534 /* Check the caps against our xcontext */
2535 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2537 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2538 GST_PTR_FORMAT, intersection);
2540 if (gst_caps_is_empty (intersection)) {
2543 gst_caps_unref (intersection);
2545 /* So we don't support this kind of buffer, let's define one we'd like */
2546 new_caps = gst_caps_copy (caps);
2548 structure = gst_caps_get_structure (new_caps, 0);
2549 if (!gst_structure_has_field (structure, "width") ||
2550 !gst_structure_has_field (structure, "height")) {
2551 gst_caps_unref (new_caps);
2555 /* Try different dimensions */
2557 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2559 if (gst_caps_is_empty (intersection)) {
2560 /* Try with different YUV formats first */
2561 gst_structure_set_name (structure, "video/x-raw-yuv");
2563 /* Remove format specific fields */
2564 gst_structure_remove_field (structure, "format");
2565 gst_structure_remove_field (structure, "endianness");
2566 gst_structure_remove_field (structure, "depth");
2567 gst_structure_remove_field (structure, "bpp");
2568 gst_structure_remove_field (structure, "red_mask");
2569 gst_structure_remove_field (structure, "green_mask");
2570 gst_structure_remove_field (structure, "blue_mask");
2571 gst_structure_remove_field (structure, "alpha_mask");
2573 /* Reuse intersection with Xcontext */
2574 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2577 if (gst_caps_is_empty (intersection)) {
2578 /* Try with different dimensions and YUV formats */
2580 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2583 if (gst_caps_is_empty (intersection)) {
2584 /* Now try with RGB */
2585 gst_structure_set_name (structure, "video/x-raw-rgb");
2586 /* And interset again */
2587 gst_caps_unref (intersection);
2588 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2591 if (gst_caps_is_empty (intersection)) {
2592 /* Try with different dimensions and RGB formats */
2594 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2597 /* Clean this copy */
2598 gst_caps_unref (new_caps);
2600 if (gst_caps_is_empty (intersection))
2604 /* Ensure the returned caps are fixed */
2605 gst_caps_truncate (intersection);
2607 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2608 GST_PTR_FORMAT, intersection);
2609 if (gst_caps_is_equal (intersection, caps)) {
2610 /* Things work better if we return a buffer with the same caps ptr
2611 * as was asked for when we can */
2612 gst_caps_replace (&intersection, caps);
2615 /* Get image format from caps */
2616 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2619 /* Get geometry from caps */
2620 structure = gst_caps_get_structure (intersection, 0);
2621 if (!gst_structure_get_int (structure, "width", &width) ||
2622 !gst_structure_get_int (structure, "height", &height) ||
2626 /* Store our caps and format as the last_caps to avoid expensive
2627 * caps intersection next time */
2628 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2629 xvimagesink->xcontext->last_format = image_format;
2630 xvimagesink->xcontext->last_width = width;
2631 xvimagesink->xcontext->last_height = height;
2635 /* Walking through the pool cleaning unusable images and searching for a
2637 while (xvimagesink->image_pool) {
2638 xvimage = xvimagesink->image_pool->data;
2641 /* Removing from the pool */
2642 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2643 xvimagesink->image_pool);
2645 /* We check for geometry or image format changes */
2646 if ((xvimage->width != width) ||
2647 (xvimage->height != height) || (xvimage->im_format != image_format)) {
2648 /* This image is unusable. Destroying... */
2649 gst_xvimage_buffer_free (xvimage);
2652 /* We found a suitable image */
2653 GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
2660 /* We found no suitable image in the pool. Creating... */
2661 GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
2662 xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
2664 g_mutex_unlock (xvimagesink->pool_lock);
2667 /* Make sure the buffer is cleared of any previously used flags */
2668 GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
2669 gst_buffer_set_caps (GST_BUFFER_CAST (xvimage), intersection);
2672 *buf = GST_BUFFER_CAST (xvimage);
2676 gst_caps_unref (intersection);
2684 GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
2685 ret = GST_FLOW_WRONG_STATE;
2686 g_mutex_unlock (xvimagesink->pool_lock);
2691 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2692 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2693 " are completely incompatible with those caps", caps,
2694 xvimagesink->xcontext->caps);
2695 ret = GST_FLOW_NOT_NEGOTIATED;
2696 g_mutex_unlock (xvimagesink->pool_lock);
2701 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2702 GST_PTR_FORMAT, intersection);
2703 ret = GST_FLOW_NOT_NEGOTIATED;
2704 g_mutex_unlock (xvimagesink->pool_lock);
2709 GST_WARNING_OBJECT (xvimagesink, "have no caps, doing fallback allocation");
2716 /* Interfaces stuff */
2719 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2721 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2722 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE);
2727 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2729 klass->supported = gst_xvimagesink_interface_supported;
2733 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2734 GstStructure * structure)
2736 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2739 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2741 GstVideoRectangle src, dst, result;
2742 gdouble x, y, xscale = 1.0, yscale = 1.0;
2744 event = gst_event_new_navigation (structure);
2746 /* We take the flow_lock while we look at the window */
2747 g_mutex_lock (xvimagesink->flow_lock);
2749 if (!xvimagesink->xwindow) {
2750 g_mutex_unlock (xvimagesink->flow_lock);
2754 if (xvimagesink->keep_aspect) {
2755 /* We get the frame position using the calculated geometry from _setcaps
2756 that respect pixel aspect ratios */
2757 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2758 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2759 dst.w = xvimagesink->render_rect.w;
2760 dst.h = xvimagesink->render_rect.h;
2762 gst_video_sink_center_rect (src, dst, &result, TRUE);
2763 result.x += xvimagesink->render_rect.x;
2764 result.y += xvimagesink->render_rect.y;
2766 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2769 g_mutex_unlock (xvimagesink->flow_lock);
2771 /* We calculate scaling using the original video frames geometry to include
2772 pixel aspect ratio scaling. */
2773 xscale = (gdouble) xvimagesink->video_width / result.w;
2774 yscale = (gdouble) xvimagesink->video_height / result.h;
2776 /* Converting pointer coordinates to the non scaled geometry */
2777 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2778 x = MIN (x, result.x + result.w);
2779 x = MAX (x - result.x, 0);
2780 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2781 (gdouble) x * xscale, NULL);
2783 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2784 y = MIN (y, result.y + result.h);
2785 y = MAX (y - result.y, 0);
2786 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2787 (gdouble) y * yscale, NULL);
2790 gst_pad_send_event (peer, event);
2791 gst_object_unref (peer);
2796 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2798 iface->send_event = gst_xvimagesink_navigation_send_event;
2802 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2804 XID xwindow_id = id;
2805 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2806 GstXWindow *xwindow = NULL;
2808 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2810 g_mutex_lock (xvimagesink->flow_lock);
2812 /* If we already use that window return */
2813 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2814 g_mutex_unlock (xvimagesink->flow_lock);
2818 /* If the element has not initialized the X11 context try to do so */
2819 if (!xvimagesink->xcontext &&
2820 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2821 g_mutex_unlock (xvimagesink->flow_lock);
2822 /* we have thrown a GST_ELEMENT_ERROR now */
2826 gst_xvimagesink_update_colorbalance (xvimagesink);
2828 /* Clear image pool as the images are unusable anyway */
2829 gst_xvimagesink_imagepool_clear (xvimagesink);
2831 /* Clear the xvimage */
2832 if (xvimagesink->xvimage) {
2833 gst_xvimage_buffer_free (xvimagesink->xvimage);
2834 xvimagesink->xvimage = NULL;
2837 /* If a window is there already we destroy it */
2838 if (xvimagesink->xwindow) {
2839 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2840 xvimagesink->xwindow = NULL;
2843 /* If the xid is 0 we go back to an internal window */
2844 if (xwindow_id == 0) {
2845 /* If no width/height caps nego did not happen window will be created
2846 during caps nego then */
2847 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2848 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2850 gst_xvimagesink_xwindow_new (xvimagesink,
2851 GST_VIDEO_SINK_WIDTH (xvimagesink),
2852 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2855 XWindowAttributes attr;
2857 xwindow = g_new0 (GstXWindow, 1);
2858 xwindow->win = xwindow_id;
2860 /* Set the event we want to receive and create a GC */
2861 g_mutex_lock (xvimagesink->x_lock);
2863 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2865 xwindow->width = attr.width;
2866 xwindow->height = attr.height;
2867 xwindow->internal = FALSE;
2868 if (!xvimagesink->have_render_rect) {
2869 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2870 xvimagesink->render_rect.w = attr.width;
2871 xvimagesink->render_rect.h = attr.height;
2873 if (xvimagesink->handle_events) {
2874 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2875 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2879 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2880 xwindow->win, 0, NULL);
2881 g_mutex_unlock (xvimagesink->x_lock);
2885 xvimagesink->xwindow = xwindow;
2887 g_mutex_unlock (xvimagesink->flow_lock);
2891 gst_xvimagesink_expose (GstXOverlay * overlay)
2893 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2895 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2896 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2900 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2901 gboolean handle_events)
2903 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2905 xvimagesink->handle_events = handle_events;
2907 g_mutex_lock (xvimagesink->flow_lock);
2909 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2910 g_mutex_unlock (xvimagesink->flow_lock);
2914 g_mutex_lock (xvimagesink->x_lock);
2916 if (handle_events) {
2917 if (xvimagesink->xwindow->internal) {
2918 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2919 ExposureMask | StructureNotifyMask | PointerMotionMask |
2920 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2922 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2923 ExposureMask | StructureNotifyMask | PointerMotionMask |
2924 KeyPressMask | KeyReleaseMask);
2927 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2930 g_mutex_unlock (xvimagesink->x_lock);
2932 g_mutex_unlock (xvimagesink->flow_lock);
2936 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2937 gint width, gint height)
2939 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2941 /* FIXME: how about some locking? */
2942 if (width >= 0 && height >= 0) {
2943 xvimagesink->render_rect.x = x;
2944 xvimagesink->render_rect.y = y;
2945 xvimagesink->render_rect.w = width;
2946 xvimagesink->render_rect.h = height;
2947 xvimagesink->have_render_rect = TRUE;
2949 xvimagesink->render_rect.x = 0;
2950 xvimagesink->render_rect.y = 0;
2951 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2952 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2953 xvimagesink->have_render_rect = FALSE;
2958 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2960 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2961 iface->expose = gst_xvimagesink_expose;
2962 iface->handle_events = gst_xvimagesink_set_event_handling;
2963 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2966 static const GList *
2967 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2969 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2971 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2973 if (xvimagesink->xcontext)
2974 return xvimagesink->xcontext->channels_list;
2980 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2981 GstColorBalanceChannel * channel, gint value)
2983 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2985 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2986 g_return_if_fail (channel->label != NULL);
2988 xvimagesink->cb_changed = TRUE;
2990 /* Normalize val to [-1000, 1000] */
2991 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
2992 (double) (channel->max_value - channel->min_value));
2994 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2995 xvimagesink->hue = value;
2996 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2997 xvimagesink->saturation = value;
2998 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2999 xvimagesink->contrast = value;
3000 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3001 xvimagesink->brightness = value;
3003 g_warning ("got an unknown channel %s", channel->label);
3007 gst_xvimagesink_update_colorbalance (xvimagesink);
3011 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
3012 GstColorBalanceChannel * channel)
3014 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3017 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
3018 g_return_val_if_fail (channel->label != NULL, 0);
3020 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3021 value = xvimagesink->hue;
3022 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3023 value = xvimagesink->saturation;
3024 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3025 value = xvimagesink->contrast;
3026 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3027 value = xvimagesink->brightness;
3029 g_warning ("got an unknown channel %s", channel->label);
3032 /* Normalize val to [channel->min_value, channel->max_value] */
3033 value = channel->min_value + (channel->max_value - channel->min_value) *
3034 (value + 1000) / 2000;
3040 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
3042 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
3043 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
3044 iface->set_value = gst_xvimagesink_colorbalance_set_value;
3045 iface->get_value = gst_xvimagesink_colorbalance_get_value;
3048 static const GList *
3049 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
3051 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
3052 static GList *list = NULL;
3055 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
3057 g_list_append (list, g_object_class_find_property (klass,
3058 "autopaint-colorkey"));
3060 g_list_append (list, g_object_class_find_property (klass,
3063 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
3070 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
3071 guint prop_id, const GParamSpec * pspec)
3073 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3077 case PROP_AUTOPAINT_COLORKEY:
3078 case PROP_DOUBLE_BUFFER:
3080 GST_DEBUG_OBJECT (xvimagesink,
3081 "probing device list and get capabilities");
3082 if (!xvimagesink->xcontext) {
3083 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
3084 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
3088 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3094 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
3095 guint prop_id, const GParamSpec * pspec)
3097 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3098 gboolean ret = FALSE;
3102 case PROP_AUTOPAINT_COLORKEY:
3103 case PROP_DOUBLE_BUFFER:
3105 if (xvimagesink->xcontext != NULL) {
3112 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3119 static GValueArray *
3120 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
3121 guint prop_id, const GParamSpec * pspec)
3123 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3124 GValueArray *array = NULL;
3126 if (G_UNLIKELY (!xvimagesink->xcontext)) {
3127 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
3136 GValue value = { 0 };
3138 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
3139 g_value_init (&value, G_TYPE_STRING);
3141 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
3142 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
3144 g_value_set_string (&value, adaptor_id_s);
3145 g_value_array_append (array, &value);
3146 g_free (adaptor_id_s);
3148 g_value_unset (&value);
3151 case PROP_AUTOPAINT_COLORKEY:
3152 if (xvimagesink->have_autopaint_colorkey) {
3153 GValue value = { 0 };
3155 array = g_value_array_new (2);
3156 g_value_init (&value, G_TYPE_BOOLEAN);
3157 g_value_set_boolean (&value, FALSE);
3158 g_value_array_append (array, &value);
3159 g_value_set_boolean (&value, TRUE);
3160 g_value_array_append (array, &value);
3161 g_value_unset (&value);
3164 case PROP_DOUBLE_BUFFER:
3165 if (xvimagesink->have_double_buffer) {
3166 GValue value = { 0 };
3168 array = g_value_array_new (2);
3169 g_value_init (&value, G_TYPE_BOOLEAN);
3170 g_value_set_boolean (&value, FALSE);
3171 g_value_array_append (array, &value);
3172 g_value_set_boolean (&value, TRUE);
3173 g_value_array_append (array, &value);
3174 g_value_unset (&value);
3178 if (xvimagesink->have_colorkey) {
3179 GValue value = { 0 };
3181 array = g_value_array_new (1);
3182 g_value_init (&value, GST_TYPE_INT_RANGE);
3183 gst_value_set_int_range (&value, 0, 0xffffff);
3184 g_value_array_append (array, &value);
3185 g_value_unset (&value);
3189 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3198 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
3201 iface->get_properties = gst_xvimagesink_probe_get_properties;
3202 iface->probe_property = gst_xvimagesink_probe_probe_property;
3203 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
3204 iface->get_values = gst_xvimagesink_probe_get_values;
3207 /* =========================================== */
3209 /* Init & Class init */
3211 /* =========================================== */
3214 gst_xvimagesink_set_property (GObject * object, guint prop_id,
3215 const GValue * value, GParamSpec * pspec)
3217 GstXvImageSink *xvimagesink;
3219 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3221 xvimagesink = GST_XVIMAGESINK (object);
3225 xvimagesink->hue = g_value_get_int (value);
3226 xvimagesink->cb_changed = TRUE;
3227 gst_xvimagesink_update_colorbalance (xvimagesink);
3230 xvimagesink->contrast = g_value_get_int (value);
3231 xvimagesink->cb_changed = TRUE;
3232 gst_xvimagesink_update_colorbalance (xvimagesink);
3234 case PROP_BRIGHTNESS:
3235 xvimagesink->brightness = g_value_get_int (value);
3236 xvimagesink->cb_changed = TRUE;
3237 gst_xvimagesink_update_colorbalance (xvimagesink);
3239 case PROP_SATURATION:
3240 xvimagesink->saturation = g_value_get_int (value);
3241 xvimagesink->cb_changed = TRUE;
3242 gst_xvimagesink_update_colorbalance (xvimagesink);
3245 xvimagesink->display_name = g_strdup (g_value_get_string (value));
3247 case PROP_SYNCHRONOUS:
3248 xvimagesink->synchronous = g_value_get_boolean (value);
3249 if (xvimagesink->xcontext) {
3250 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
3251 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
3252 xvimagesink->synchronous ? "TRUE" : "FALSE");
3255 case PROP_PIXEL_ASPECT_RATIO:
3256 g_free (xvimagesink->par);
3257 xvimagesink->par = g_new0 (GValue, 1);
3258 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
3259 if (!g_value_transform (value, xvimagesink->par)) {
3260 g_warning ("Could not transform string to aspect ratio");
3261 gst_value_set_fraction (xvimagesink->par, 1, 1);
3263 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
3264 gst_value_get_fraction_numerator (xvimagesink->par),
3265 gst_value_get_fraction_denominator (xvimagesink->par));
3267 case PROP_FORCE_ASPECT_RATIO:
3268 xvimagesink->keep_aspect = g_value_get_boolean (value);
3270 case PROP_HANDLE_EVENTS:
3271 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
3272 g_value_get_boolean (value));
3273 gst_xvimagesink_manage_event_thread (xvimagesink);
3276 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
3278 case PROP_HANDLE_EXPOSE:
3279 xvimagesink->handle_expose = g_value_get_boolean (value);
3280 gst_xvimagesink_manage_event_thread (xvimagesink);
3282 case PROP_DOUBLE_BUFFER:
3283 xvimagesink->double_buffer = g_value_get_boolean (value);
3285 case PROP_AUTOPAINT_COLORKEY:
3286 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
3289 xvimagesink->colorkey = g_value_get_int (value);
3291 case PROP_DRAW_BORDERS:
3292 xvimagesink->draw_borders = g_value_get_boolean (value);
3295 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3301 gst_xvimagesink_get_property (GObject * object, guint prop_id,
3302 GValue * value, GParamSpec * pspec)
3304 GstXvImageSink *xvimagesink;
3306 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3308 xvimagesink = GST_XVIMAGESINK (object);
3312 g_value_set_int (value, xvimagesink->hue);
3315 g_value_set_int (value, xvimagesink->contrast);
3317 case PROP_BRIGHTNESS:
3318 g_value_set_int (value, xvimagesink->brightness);
3320 case PROP_SATURATION:
3321 g_value_set_int (value, xvimagesink->saturation);
3324 g_value_set_string (value, xvimagesink->display_name);
3326 case PROP_SYNCHRONOUS:
3327 g_value_set_boolean (value, xvimagesink->synchronous);
3329 case PROP_PIXEL_ASPECT_RATIO:
3330 if (xvimagesink->par)
3331 g_value_transform (xvimagesink->par, value);
3333 case PROP_FORCE_ASPECT_RATIO:
3334 g_value_set_boolean (value, xvimagesink->keep_aspect);
3336 case PROP_HANDLE_EVENTS:
3337 g_value_set_boolean (value, xvimagesink->handle_events);
3341 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
3343 g_value_set_string (value, adaptor_no_s);
3344 g_free (adaptor_no_s);
3347 case PROP_DEVICE_NAME:
3348 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
3349 g_value_set_string (value,
3350 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
3352 g_value_set_string (value, NULL);
3355 case PROP_HANDLE_EXPOSE:
3356 g_value_set_boolean (value, xvimagesink->handle_expose);
3358 case PROP_DOUBLE_BUFFER:
3359 g_value_set_boolean (value, xvimagesink->double_buffer);
3361 case PROP_AUTOPAINT_COLORKEY:
3362 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
3365 g_value_set_int (value, xvimagesink->colorkey);
3367 case PROP_DRAW_BORDERS:
3368 g_value_set_boolean (value, xvimagesink->draw_borders);
3370 case PROP_WINDOW_WIDTH:
3371 if (xvimagesink->xwindow)
3372 g_value_set_uint64 (value, xvimagesink->xwindow->width);
3374 g_value_set_uint64 (value, 0);
3376 case PROP_WINDOW_HEIGHT:
3377 if (xvimagesink->xwindow)
3378 g_value_set_uint64 (value, xvimagesink->xwindow->height);
3380 g_value_set_uint64 (value, 0);
3383 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3389 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
3393 GST_OBJECT_LOCK (xvimagesink);
3394 xvimagesink->running = FALSE;
3395 /* grab thread and mark it as NULL */
3396 thread = xvimagesink->event_thread;
3397 xvimagesink->event_thread = NULL;
3398 GST_OBJECT_UNLOCK (xvimagesink);
3400 /* invalidate the pool, current allocations continue, new buffer_alloc fails
3401 * with wrong_state */
3402 g_mutex_lock (xvimagesink->pool_lock);
3403 xvimagesink->pool_invalid = TRUE;
3404 g_mutex_unlock (xvimagesink->pool_lock);
3406 /* Wait for our event thread to finish before we clean up our stuff. */
3408 g_thread_join (thread);
3410 if (xvimagesink->cur_image) {
3411 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
3412 xvimagesink->cur_image = NULL;
3414 if (xvimagesink->xvimage) {
3415 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->xvimage));
3416 xvimagesink->xvimage = NULL;
3419 gst_xvimagesink_imagepool_clear (xvimagesink);
3421 if (xvimagesink->xwindow) {
3422 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
3423 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3424 xvimagesink->xwindow = NULL;
3427 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
3428 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
3429 xvimagesink->have_render_rect = FALSE;
3431 gst_xvimagesink_xcontext_clear (xvimagesink);
3434 /* Finalize is called only once, dispose can be called multiple times.
3435 * We use mutexes and don't reset stuff to NULL here so let's register
3438 gst_xvimagesink_finalize (GObject * object)
3440 GstXvImageSink *xvimagesink;
3442 xvimagesink = GST_XVIMAGESINK (object);
3444 gst_xvimagesink_reset (xvimagesink);
3446 if (xvimagesink->display_name) {
3447 g_free (xvimagesink->display_name);
3448 xvimagesink->display_name = NULL;
3451 if (xvimagesink->par) {
3452 g_free (xvimagesink->par);
3453 xvimagesink->par = NULL;
3455 if (xvimagesink->x_lock) {
3456 g_mutex_free (xvimagesink->x_lock);
3457 xvimagesink->x_lock = NULL;
3459 if (xvimagesink->flow_lock) {
3460 g_mutex_free (xvimagesink->flow_lock);
3461 xvimagesink->flow_lock = NULL;
3463 if (xvimagesink->pool_lock) {
3464 g_mutex_free (xvimagesink->pool_lock);
3465 xvimagesink->pool_lock = NULL;
3468 g_free (xvimagesink->media_title);
3470 G_OBJECT_CLASS (parent_class)->finalize (object);
3474 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
3476 xvimagesink->display_name = NULL;
3477 xvimagesink->adaptor_no = 0;
3478 xvimagesink->xcontext = NULL;
3479 xvimagesink->xwindow = NULL;
3480 xvimagesink->xvimage = NULL;
3481 xvimagesink->cur_image = NULL;
3483 xvimagesink->hue = xvimagesink->saturation = 0;
3484 xvimagesink->contrast = xvimagesink->brightness = 0;
3485 xvimagesink->cb_changed = FALSE;
3487 xvimagesink->fps_n = 0;
3488 xvimagesink->fps_d = 0;
3489 xvimagesink->video_width = 0;
3490 xvimagesink->video_height = 0;
3492 xvimagesink->x_lock = g_mutex_new ();
3493 xvimagesink->flow_lock = g_mutex_new ();
3495 xvimagesink->image_pool = NULL;
3496 xvimagesink->pool_lock = g_mutex_new ();
3498 xvimagesink->synchronous = FALSE;
3499 xvimagesink->double_buffer = TRUE;
3500 xvimagesink->running = FALSE;
3501 xvimagesink->keep_aspect = FALSE;
3502 xvimagesink->handle_events = TRUE;
3503 xvimagesink->par = NULL;
3504 xvimagesink->handle_expose = TRUE;
3505 xvimagesink->autopaint_colorkey = TRUE;
3507 /* on 16bit displays this becomes r,g,b = 1,2,3
3508 * on 24bit displays this becomes r,g,b = 8,8,16
3509 * as a port atom value
3511 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
3512 xvimagesink->draw_borders = TRUE;
3516 gst_xvimagesink_base_init (gpointer g_class)
3518 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
3520 gst_element_class_set_details_simple (element_class,
3521 "Video sink", "Sink/Video",
3522 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
3524 gst_element_class_add_pad_template (element_class,
3525 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
3529 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
3531 GObjectClass *gobject_class;
3532 GstElementClass *gstelement_class;
3533 GstBaseSinkClass *gstbasesink_class;
3534 GstVideoSinkClass *videosink_class;
3536 gobject_class = (GObjectClass *) klass;
3537 gstelement_class = (GstElementClass *) klass;
3538 gstbasesink_class = (GstBaseSinkClass *) klass;
3539 videosink_class = (GstVideoSinkClass *) klass;
3541 parent_class = g_type_class_peek_parent (klass);
3543 gobject_class->set_property = gst_xvimagesink_set_property;
3544 gobject_class->get_property = gst_xvimagesink_get_property;
3546 g_object_class_install_property (gobject_class, PROP_CONTRAST,
3547 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
3548 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3549 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
3550 g_param_spec_int ("brightness", "Brightness",
3551 "The brightness of the video", -1000, 1000, 0,
3552 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3553 g_object_class_install_property (gobject_class, PROP_HUE,
3554 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
3555 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3556 g_object_class_install_property (gobject_class, PROP_SATURATION,
3557 g_param_spec_int ("saturation", "Saturation",
3558 "The saturation of the video", -1000, 1000, 0,
3559 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3560 g_object_class_install_property (gobject_class, PROP_DISPLAY,
3561 g_param_spec_string ("display", "Display", "X Display name", NULL,
3562 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3563 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
3564 g_param_spec_boolean ("synchronous", "Synchronous",
3565 "When enabled, runs "
3566 "the X display in synchronous mode. (used only for debugging)", FALSE,
3567 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3568 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
3569 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
3570 "The pixel aspect ratio of the device", "1/1",
3571 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3572 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
3573 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
3574 "When enabled, scaling will respect original aspect ratio", FALSE,
3575 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3576 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
3577 g_param_spec_boolean ("handle-events", "Handle XEvents",
3578 "When enabled, XEvents will be selected and handled", TRUE,
3579 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3580 g_object_class_install_property (gobject_class, PROP_DEVICE,
3581 g_param_spec_string ("device", "Adaptor number",
3582 "The number of the video adaptor", "0",
3583 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3584 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
3585 g_param_spec_string ("device-name", "Adaptor name",
3586 "The name of the video adaptor", NULL,
3587 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3589 * GstXvImageSink:handle-expose
3591 * When enabled, the current frame will always be drawn in response to X
3596 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
3597 g_param_spec_boolean ("handle-expose", "Handle expose",
3599 "the current frame will always be drawn in response to X Expose "
3600 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3603 * GstXvImageSink:double-buffer
3605 * Whether to double-buffer the output.
3609 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
3610 g_param_spec_boolean ("double-buffer", "Double-buffer",
3611 "Whether to double-buffer the output", TRUE,
3612 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3614 * GstXvImageSink:autopaint-colorkey
3616 * Whether to autofill overlay with colorkey
3620 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
3621 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
3622 "Whether to autofill overlay with colorkey", TRUE,
3623 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3625 * GstXvImageSink:colorkey
3627 * Color to use for the overlay mask.
3631 g_object_class_install_property (gobject_class, PROP_COLORKEY,
3632 g_param_spec_int ("colorkey", "Colorkey",
3633 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
3634 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3637 * GstXvImageSink:draw-borders
3639 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
3640 * unused parts of the video area.
3644 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
3645 g_param_spec_boolean ("draw-borders", "Colorkey",
3646 "Draw black borders to fill unused area in force-aspect-ratio mode",
3647 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3650 * GstXvImageSink:window-width
3652 * Actual width of the video window.
3656 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
3657 g_param_spec_uint64 ("window-width", "window-width",
3658 "Width of the window", 0, G_MAXUINT64, 0,
3659 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3662 * GstXvImageSink:window-height
3664 * Actual height of the video window.
3668 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
3669 g_param_spec_uint64 ("window-height", "window-height",
3670 "Height of the window", 0, G_MAXUINT64, 0,
3671 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3673 gobject_class->finalize = gst_xvimagesink_finalize;
3675 gstelement_class->change_state =
3676 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3678 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3679 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3680 gstbasesink_class->buffer_alloc =
3681 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3682 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3683 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3685 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3688 /* ============================================================= */
3690 /* Public Methods */
3692 /* ============================================================= */
3694 /* =========================================== */
3696 /* Object typing & Creation */
3698 /* =========================================== */
3701 gst_xvimagesink_get_type (void)
3703 static GType xvimagesink_type = 0;
3705 if (!xvimagesink_type) {
3706 static const GTypeInfo xvimagesink_info = {
3707 sizeof (GstXvImageSinkClass),
3708 gst_xvimagesink_base_init,
3710 (GClassInitFunc) gst_xvimagesink_class_init,
3713 sizeof (GstXvImageSink),
3715 (GInstanceInitFunc) gst_xvimagesink_init,
3717 static const GInterfaceInfo iface_info = {
3718 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3722 static const GInterfaceInfo navigation_info = {
3723 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3727 static const GInterfaceInfo overlay_info = {
3728 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3732 static const GInterfaceInfo colorbalance_info = {
3733 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3737 static const GInterfaceInfo propertyprobe_info = {
3738 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3742 xvimagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
3743 "GstXvImageSink", &xvimagesink_info, 0);
3745 g_type_add_interface_static (xvimagesink_type,
3746 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3747 g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
3749 g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY,
3751 g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE,
3752 &colorbalance_info);
3753 g_type_add_interface_static (xvimagesink_type, GST_TYPE_PROPERTY_PROBE,
3754 &propertyprobe_info);
3757 /* register type and create class in a more safe place instead of at
3758 * runtime since the type registration and class creation is not
3760 g_type_class_ref (gst_xvimage_buffer_get_type ());
3763 return xvimagesink_type;
3767 plugin_init (GstPlugin * plugin)
3769 if (!gst_element_register (plugin, "xvimagesink",
3770 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
3773 GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
3774 "xvimagesink element");
3775 GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
3780 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3783 "XFree86 video output plugin using Xv extension",
3784 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)