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 void gst_xvimagesink_init_interfaces (GType type);
196 GST_BOILERPLATE_FULL (GstXvImageSink, gst_xvimagesink, GstVideoSink,
197 GST_TYPE_VIDEO_SINK, gst_xvimagesink_init_interfaces);
200 /* ============================================================= */
202 /* Private Methods */
204 /* ============================================================= */
206 /* xvimage buffers */
208 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
210 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
211 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
212 #define GST_XVIMAGE_BUFFER_CAST(obj) ((GstXvImageBuffer *)(obj))
214 /* This function destroys a GstXvImage handling XShm availability */
216 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
218 GstXvImageSink *xvimagesink;
220 GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
222 xvimagesink = xvimage->xvimagesink;
223 if (G_UNLIKELY (xvimagesink == NULL))
226 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
228 GST_OBJECT_LOCK (xvimagesink);
230 /* If the destroyed image is the current one we destroy our reference too */
231 if (xvimagesink->cur_image == xvimage)
232 xvimagesink->cur_image = NULL;
234 /* We might have some buffers destroyed after changing state to NULL */
235 if (xvimagesink->xcontext == NULL) {
236 GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
238 /* Need to free the shared memory segment even if the x context
239 * was already cleaned up */
240 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
241 shmdt (xvimage->SHMInfo.shmaddr);
247 g_mutex_lock (xvimagesink->x_lock);
250 if (xvimagesink->xcontext->use_xshm) {
251 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
252 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
253 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
254 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
255 XSync (xvimagesink->xcontext->disp, FALSE);
257 shmdt (xvimage->SHMInfo.shmaddr);
259 if (xvimage->xvimage)
260 XFree (xvimage->xvimage);
262 #endif /* HAVE_XSHM */
264 if (xvimage->xvimage) {
265 if (xvimage->xvimage->data) {
266 g_free (xvimage->xvimage->data);
268 XFree (xvimage->xvimage);
272 XSync (xvimagesink->xcontext->disp, FALSE);
274 g_mutex_unlock (xvimagesink->x_lock);
277 GST_OBJECT_UNLOCK (xvimagesink);
278 xvimage->xvimagesink = NULL;
279 gst_object_unref (xvimagesink);
281 GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->finalize (GST_MINI_OBJECT
288 GST_WARNING ("no sink found");
294 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
296 GstXvImageSink *xvimagesink;
299 xvimagesink = xvimage->xvimagesink;
300 if (G_UNLIKELY (xvimagesink == NULL))
303 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
305 GST_OBJECT_LOCK (xvimagesink);
306 running = xvimagesink->running;
307 GST_OBJECT_UNLOCK (xvimagesink);
309 /* If our geometry changed we can't reuse that image. */
310 if (running == FALSE) {
311 GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
312 gst_xvimage_buffer_destroy (xvimage);
313 } else if ((xvimage->width != xvimagesink->video_width) ||
314 (xvimage->height != xvimagesink->video_height)) {
315 GST_LOG_OBJECT (xvimage,
316 "destroy image as its size changed %dx%d vs current %dx%d",
317 xvimage->width, xvimage->height,
318 xvimagesink->video_width, xvimagesink->video_height);
319 gst_xvimage_buffer_destroy (xvimage);
321 /* In that case we can reuse the image and add it to our image pool. */
322 GST_LOG_OBJECT (xvimage, "recycling image in pool");
323 /* need to increment the refcount again to recycle */
324 gst_buffer_ref (GST_BUFFER_CAST (xvimage));
325 g_mutex_lock (xvimagesink->pool_lock);
326 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
328 g_mutex_unlock (xvimagesink->pool_lock);
334 GST_WARNING ("no sink found");
340 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
342 /* make sure it is not recycled */
344 xvimage->height = -1;
345 gst_buffer_unref (GST_BUFFER (xvimage));
349 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
352 xvimage->SHMInfo.shmaddr = ((void *) -1);
353 xvimage->SHMInfo.shmid = -1;
358 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
360 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
362 xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
364 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
365 gst_xvimage_buffer_finalize;
369 gst_xvimage_buffer_get_type (void)
371 static GType _gst_xvimage_buffer_type;
373 if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
374 static const GTypeInfo xvimage_buffer_info = {
375 sizeof (GstBufferClass),
378 gst_xvimage_buffer_class_init,
381 sizeof (GstXvImageBuffer),
383 (GInstanceInitFunc) gst_xvimage_buffer_init,
386 _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
387 "GstXvImageBuffer", &xvimage_buffer_info, 0);
389 return _gst_xvimage_buffer_type;
394 static gboolean error_caught = FALSE;
397 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
399 char error_msg[1024];
401 XGetErrorText (display, xevent->error_code, error_msg, 1024);
402 GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
408 /* This function checks that it is actually really possible to create an image
411 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
414 XShmSegmentInfo SHMInfo;
416 int (*handler) (Display *, XErrorEvent *);
417 gboolean result = FALSE;
418 gboolean did_attach = FALSE;
420 g_return_val_if_fail (xcontext != NULL, FALSE);
422 /* Sync to ensure any older errors are already processed */
423 XSync (xcontext->disp, FALSE);
425 /* Set defaults so we don't free these later unnecessarily */
426 SHMInfo.shmaddr = ((void *) -1);
429 /* Setting an error handler to catch failure */
430 error_caught = FALSE;
431 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
433 /* Trying to create a 1x1 picture */
434 GST_DEBUG ("XvShmCreateImage of 1x1");
435 xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
436 xcontext->im_format, NULL, 1, 1, &SHMInfo);
438 /* Might cause an error, sync to ensure it is noticed */
439 XSync (xcontext->disp, FALSE);
440 if (!xvimage || error_caught) {
441 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
444 size = xvimage->data_size;
446 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
447 if (SHMInfo.shmid == -1) {
448 GST_WARNING ("could not get shared memory of %d bytes", size);
452 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
453 if (SHMInfo.shmaddr == ((void *) -1)) {
454 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
455 /* Clean up the shared memory segment */
456 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
460 xvimage->data = SHMInfo.shmaddr;
461 SHMInfo.readOnly = FALSE;
463 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
464 GST_WARNING ("Failed to XShmAttach");
465 /* Clean up the shared memory segment */
466 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
470 /* Sync to ensure we see any errors we caused */
471 XSync (xcontext->disp, FALSE);
473 /* Delete the shared memory segment as soon as everyone is attached.
474 * This way, it will be deleted as soon as we detach later, and not
475 * leaked if we crash. */
476 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
479 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
483 /* store whether we succeeded in result */
486 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
487 "Not using shared memory.");
491 /* Sync to ensure we swallow any errors we caused and reset error_caught */
492 XSync (xcontext->disp, FALSE);
494 error_caught = FALSE;
495 XSetErrorHandler (handler);
498 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
499 SHMInfo.shmid, SHMInfo.shmseg);
500 XShmDetach (xcontext->disp, &SHMInfo);
501 XSync (xcontext->disp, FALSE);
503 if (SHMInfo.shmaddr != ((void *) -1))
504 shmdt (SHMInfo.shmaddr);
509 #endif /* HAVE_XSHM */
511 /* This function handles GstXvImage creation depending on XShm availability */
512 static GstXvImageBuffer *
513 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
515 GstXvImageBuffer *xvimage = NULL;
516 GstStructure *structure = NULL;
517 gboolean succeeded = FALSE;
518 int (*handler) (Display *, XErrorEvent *);
520 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
525 xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
526 GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
528 structure = gst_caps_get_structure (caps, 0);
530 if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
531 !gst_structure_get_int (structure, "height", &xvimage->height)) {
532 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
535 GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
538 xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
539 if (xvimage->im_format == -1) {
540 GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
541 GST_PTR_FORMAT, caps);
542 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
543 ("Failed to create output image buffer of %dx%d pixels",
544 xvimage->width, xvimage->height), ("Invalid input caps"));
547 xvimage->xvimagesink = gst_object_ref (xvimagesink);
549 g_mutex_lock (xvimagesink->x_lock);
551 /* Setting an error handler to catch failure */
552 error_caught = FALSE;
553 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
556 if (xvimagesink->xcontext->use_xshm) {
559 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
560 xvimagesink->xcontext->xv_port_id,
561 xvimage->im_format, NULL,
562 xvimage->width, xvimage->height, &xvimage->SHMInfo);
563 if (!xvimage->xvimage || error_caught) {
564 g_mutex_unlock (xvimagesink->x_lock);
566 /* Reset error flag */
567 error_caught = FALSE;
570 GST_ELEMENT_WARNING (xvimagesink, RESOURCE, WRITE,
571 ("Failed to create output image buffer of %dx%d pixels",
572 xvimage->width, xvimage->height),
573 ("could not XvShmCreateImage a %dx%d image",
574 xvimage->width, xvimage->height));
576 /* Retry without XShm */
577 xvimagesink->xcontext->use_xshm = FALSE;
579 /* Hold X mutex again to try without XShm */
580 g_mutex_lock (xvimagesink->x_lock);
584 /* we have to use the returned data_size for our shm size */
585 xvimage->size = xvimage->xvimage->data_size;
586 GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
589 /* calculate the expected size. This is only for sanity checking the
590 * number we get from X. */
591 switch (xvimage->im_format) {
592 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
593 case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
600 pitches[0] = GST_ROUND_UP_4 (xvimage->width);
601 offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (xvimage->height);
602 pitches[1] = GST_ROUND_UP_8 (xvimage->width) / 2;
604 offsets[1] + pitches[1] * GST_ROUND_UP_2 (xvimage->height) / 2;
605 pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
608 offsets[2] + pitches[2] * GST_ROUND_UP_2 (xvimage->height) / 2;
610 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
611 GST_DEBUG_OBJECT (xvimagesink,
612 "Plane %u has a expected pitch of %d bytes, " "offset of %d",
613 plane, pitches[plane], offsets[plane]);
617 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
618 case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
619 expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
625 if (expected_size != 0 && xvimage->size != expected_size) {
626 GST_WARNING_OBJECT (xvimagesink,
627 "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
628 xvimage->size, expected_size);
631 /* Be verbose about our XvImage stride */
635 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
636 GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
637 "offset of %d", plane, xvimage->xvimage->pitches[plane],
638 xvimage->xvimage->offsets[plane]);
642 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
644 if (xvimage->SHMInfo.shmid == -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 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
654 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
655 if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
656 g_mutex_unlock (xvimagesink->x_lock);
657 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
658 ("Failed to create output image buffer of %dx%d pixels",
659 xvimage->width, xvimage->height),
660 ("Failed to shmat: %s", g_strerror (errno)));
661 /* Clean up the shared memory segment */
662 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
666 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
667 xvimage->SHMInfo.readOnly = FALSE;
669 if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
670 /* Clean up the shared memory segment */
671 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
673 g_mutex_unlock (xvimagesink->x_lock);
674 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
675 ("Failed to create output image buffer of %dx%d pixels",
676 xvimage->width, xvimage->height), ("Failed to XShmAttach"));
680 XSync (xvimagesink->xcontext->disp, FALSE);
682 /* Delete the shared memory segment as soon as we everyone is attached.
683 * This way, it will be deleted as soon as we detach later, and not
684 * leaked if we crash. */
685 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
687 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
688 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
691 #endif /* HAVE_XSHM */
693 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
694 xvimagesink->xcontext->xv_port_id,
695 xvimage->im_format, NULL, xvimage->width, xvimage->height);
696 if (!xvimage->xvimage || error_caught) {
697 g_mutex_unlock (xvimagesink->x_lock);
698 /* Reset error handler */
699 error_caught = FALSE;
700 XSetErrorHandler (handler);
702 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
703 ("Failed to create outputimage buffer of %dx%d pixels",
704 xvimage->width, xvimage->height),
705 ("could not XvCreateImage a %dx%d image",
706 xvimage->width, xvimage->height));
710 /* we have to use the returned data_size for our image size */
711 xvimage->size = xvimage->xvimage->data_size;
712 xvimage->xvimage->data = g_malloc (xvimage->size);
714 XSync (xvimagesink->xcontext->disp, FALSE);
717 /* Reset error handler */
718 error_caught = FALSE;
719 XSetErrorHandler (handler);
723 GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
724 GST_BUFFER_SIZE (xvimage) = xvimage->size;
726 g_mutex_unlock (xvimagesink->x_lock);
730 gst_xvimage_buffer_free (xvimage);
737 /* We are called with the x_lock taken */
739 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
740 GstXWindow * xwindow, GstVideoRectangle rect)
744 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
745 g_return_if_fail (xwindow != NULL);
747 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
748 xvimagesink->xcontext->black);
751 if (rect.x > xvimagesink->render_rect.x) {
752 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
753 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
754 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
758 t1 = rect.x + rect.w;
759 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
761 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
762 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
766 if (rect.y > xvimagesink->render_rect.y) {
767 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
768 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
769 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
773 t1 = rect.y + rect.h;
774 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
776 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
777 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
781 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
782 * if no window was available */
784 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
785 GstXvImageBuffer * xvimage)
787 GstVideoRectangle result;
788 gboolean draw_border = FALSE;
790 /* We take the flow_lock. If expose is in there we don't want to run
791 concurrently from the data flow thread */
792 g_mutex_lock (xvimagesink->flow_lock);
794 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
795 g_mutex_unlock (xvimagesink->flow_lock);
799 /* Draw borders when displaying the first frame. After this
800 draw borders only on expose event or after a size change. */
801 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
805 /* Store a reference to the last image we put, lose the previous one */
806 if (xvimage && xvimagesink->cur_image != xvimage) {
807 if (xvimagesink->cur_image) {
808 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
809 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
811 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
812 xvimagesink->cur_image =
813 GST_XVIMAGE_BUFFER_CAST (gst_buffer_ref (GST_BUFFER_CAST (xvimage)));
816 /* Expose sends a NULL image, we take the latest frame */
818 if (xvimagesink->cur_image) {
820 xvimage = xvimagesink->cur_image;
822 g_mutex_unlock (xvimagesink->flow_lock);
827 if (xvimagesink->keep_aspect) {
828 GstVideoRectangle src, dst;
830 /* We use the calculated geometry from _setcaps as a source to respect
831 source and screen pixel aspect ratios. */
832 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
833 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
834 dst.w = xvimagesink->render_rect.w;
835 dst.h = xvimagesink->render_rect.h;
837 gst_video_sink_center_rect (src, dst, &result, TRUE);
838 result.x += xvimagesink->render_rect.x;
839 result.y += xvimagesink->render_rect.y;
841 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
844 g_mutex_lock (xvimagesink->x_lock);
846 if (draw_border && xvimagesink->draw_borders) {
847 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
849 xvimagesink->redraw_border = FALSE;
852 /* We scale to the window's geometry */
854 if (xvimagesink->xcontext->use_xshm) {
855 GST_LOG_OBJECT (xvimagesink,
856 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
858 xvimage->width, xvimage->height,
859 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
861 XvShmPutImage (xvimagesink->xcontext->disp,
862 xvimagesink->xcontext->xv_port_id,
863 xvimagesink->xwindow->win,
864 xvimagesink->xwindow->gc, xvimage->xvimage,
865 xvimagesink->disp_x, xvimagesink->disp_y,
866 xvimagesink->disp_width, xvimagesink->disp_height,
867 result.x, result.y, result.w, result.h, FALSE);
869 #endif /* HAVE_XSHM */
871 XvPutImage (xvimagesink->xcontext->disp,
872 xvimagesink->xcontext->xv_port_id,
873 xvimagesink->xwindow->win,
874 xvimagesink->xwindow->gc, xvimage->xvimage,
875 xvimagesink->disp_x, xvimagesink->disp_y,
876 xvimagesink->disp_width, xvimagesink->disp_height,
877 result.x, result.y, result.w, result.h);
880 XSync (xvimagesink->xcontext->disp, FALSE);
882 g_mutex_unlock (xvimagesink->x_lock);
884 g_mutex_unlock (xvimagesink->flow_lock);
890 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
893 Atom hints_atom = None;
896 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
897 g_return_val_if_fail (window != NULL, FALSE);
899 g_mutex_lock (xvimagesink->x_lock);
901 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
903 if (hints_atom == None) {
904 g_mutex_unlock (xvimagesink->x_lock);
908 hints = g_malloc0 (sizeof (MotifWmHints));
910 hints->flags |= MWM_HINTS_DECORATIONS;
911 hints->decorations = 1 << 0;
913 XChangeProperty (xvimagesink->xcontext->disp, window->win,
914 hints_atom, hints_atom, 32, PropModeReplace,
915 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
917 XSync (xvimagesink->xcontext->disp, FALSE);
919 g_mutex_unlock (xvimagesink->x_lock);
927 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
928 GstXWindow * xwindow, const gchar * media_title)
931 g_free (xvimagesink->media_title);
932 xvimagesink->media_title = g_strdup (media_title);
935 /* we have a window */
936 if (xwindow->internal) {
937 XTextProperty xproperty;
938 const gchar *app_name;
939 const gchar *title = NULL;
940 gchar *title_mem = NULL;
942 /* set application name as a title */
943 app_name = g_get_application_name ();
945 if (app_name && xvimagesink->media_title) {
946 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
948 } else if (app_name) {
950 } else if (xvimagesink->media_title) {
951 title = xvimagesink->media_title;
955 if ((XStringListToTextProperty (((char **) &title), 1,
957 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
958 XFree (xproperty.value);
967 /* This function handles a GstXWindow creation
968 * The width and height are the actual pixel size on the display */
970 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
971 gint width, gint height)
973 GstXWindow *xwindow = NULL;
976 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
978 xwindow = g_new0 (GstXWindow, 1);
980 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
981 xvimagesink->render_rect.w = width;
982 xvimagesink->render_rect.h = height;
984 xwindow->width = width;
985 xwindow->height = height;
986 xwindow->internal = TRUE;
988 g_mutex_lock (xvimagesink->x_lock);
990 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
991 xvimagesink->xcontext->root,
992 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
994 /* We have to do that to prevent X from redrawing the background on
995 * ConfigureNotify. This takes away flickering of video when resizing. */
996 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
998 /* set application name as a title */
999 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
1001 if (xvimagesink->handle_events) {
1004 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
1005 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1006 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1008 /* Tell the window manager we'd like delete client messages instead of
1010 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1011 "WM_DELETE_WINDOW", True);
1012 if (wm_delete != None) {
1013 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
1018 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1019 xwindow->win, 0, &values);
1021 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
1023 XSync (xvimagesink->xcontext->disp, FALSE);
1025 g_mutex_unlock (xvimagesink->x_lock);
1027 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
1029 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
1034 /* This function destroys a GstXWindow */
1036 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
1037 GstXWindow * xwindow)
1039 g_return_if_fail (xwindow != NULL);
1040 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1042 g_mutex_lock (xvimagesink->x_lock);
1044 /* If we did not create that window we just free the GC and let it live */
1045 if (xwindow->internal)
1046 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
1048 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
1050 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
1052 XSync (xvimagesink->xcontext->disp, FALSE);
1054 g_mutex_unlock (xvimagesink->x_lock);
1060 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
1062 XWindowAttributes attr;
1064 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1066 /* Update the window geometry */
1067 g_mutex_lock (xvimagesink->x_lock);
1068 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
1069 g_mutex_unlock (xvimagesink->x_lock);
1073 XGetWindowAttributes (xvimagesink->xcontext->disp,
1074 xvimagesink->xwindow->win, &attr);
1076 xvimagesink->xwindow->width = attr.width;
1077 xvimagesink->xwindow->height = attr.height;
1079 if (!xvimagesink->have_render_rect) {
1080 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1081 xvimagesink->render_rect.w = attr.width;
1082 xvimagesink->render_rect.h = attr.height;
1085 g_mutex_unlock (xvimagesink->x_lock);
1089 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
1090 GstXWindow * xwindow)
1092 g_return_if_fail (xwindow != NULL);
1093 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1095 g_mutex_lock (xvimagesink->x_lock);
1097 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1100 XSync (xvimagesink->xcontext->disp, FALSE);
1102 g_mutex_unlock (xvimagesink->x_lock);
1105 /* This function commits our internal colorbalance settings to our grabbed Xv
1106 port. If the xcontext is not initialized yet it simply returns */
1108 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1110 GList *channels = NULL;
1112 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1114 /* If we haven't initialized the X context we can't update anything */
1115 if (xvimagesink->xcontext == NULL)
1118 /* Don't set the attributes if they haven't been changed, to avoid
1119 * rounding errors changing the values */
1120 if (!xvimagesink->cb_changed)
1123 /* For each channel of the colorbalance we calculate the correct value
1124 doing range conversion and then set the Xv port attribute to match our
1126 channels = xvimagesink->xcontext->channels_list;
1129 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1130 GstColorBalanceChannel *channel = NULL;
1133 gdouble convert_coef;
1135 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1136 g_object_ref (channel);
1138 /* Our range conversion coef */
1139 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1141 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1142 value = xvimagesink->hue;
1143 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1144 value = xvimagesink->saturation;
1145 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1146 value = xvimagesink->contrast;
1147 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1148 value = xvimagesink->brightness;
1150 g_warning ("got an unknown channel %s", channel->label);
1151 g_object_unref (channel);
1155 /* Committing to Xv port */
1156 g_mutex_lock (xvimagesink->x_lock);
1158 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1159 if (prop_atom != None) {
1162 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
1163 XvSetPortAttribute (xvimagesink->xcontext->disp,
1164 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
1166 g_mutex_unlock (xvimagesink->x_lock);
1168 g_object_unref (channel);
1170 channels = g_list_next (channels);
1174 /* This function handles XEvents that might be in the queue. It generates
1175 GstEvent that will be sent upstream in the pipeline to handle interactivity
1176 and navigation. It will also listen for configure events on the window to
1177 trigger caps renegotiation so on the fly software scaling can work. */
1179 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1182 guint pointer_x = 0, pointer_y = 0;
1183 gboolean pointer_moved = FALSE;
1184 gboolean exposed = FALSE, configured = FALSE;
1186 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1188 /* Handle Interaction, produces navigation events */
1190 /* We get all pointer motion events, only the last position is
1192 g_mutex_lock (xvimagesink->flow_lock);
1193 g_mutex_lock (xvimagesink->x_lock);
1194 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1195 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1196 g_mutex_unlock (xvimagesink->x_lock);
1197 g_mutex_unlock (xvimagesink->flow_lock);
1201 pointer_x = e.xmotion.x;
1202 pointer_y = e.xmotion.y;
1203 pointer_moved = TRUE;
1208 g_mutex_lock (xvimagesink->flow_lock);
1209 g_mutex_lock (xvimagesink->x_lock);
1211 if (pointer_moved) {
1212 g_mutex_unlock (xvimagesink->x_lock);
1213 g_mutex_unlock (xvimagesink->flow_lock);
1215 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1216 pointer_x, pointer_y);
1217 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1218 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1220 g_mutex_lock (xvimagesink->flow_lock);
1221 g_mutex_lock (xvimagesink->x_lock);
1224 /* We get all events on our window to throw them upstream */
1225 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1226 xvimagesink->xwindow->win,
1227 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1231 /* We lock only for the X function call */
1232 g_mutex_unlock (xvimagesink->x_lock);
1233 g_mutex_unlock (xvimagesink->flow_lock);
1237 /* Mouse button pressed over our window. We send upstream
1238 events for interactivity/navigation */
1239 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1240 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1241 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1242 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1245 /* Mouse button released over our window. We send upstream
1246 events for interactivity/navigation */
1247 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1248 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1249 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1250 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1254 /* Key pressed/released over our window. We send upstream
1255 events for interactivity/navigation */
1256 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1257 e.xkey.keycode, e.xkey.x, e.xkey.y);
1258 g_mutex_lock (xvimagesink->x_lock);
1259 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1261 g_mutex_unlock (xvimagesink->x_lock);
1262 if (keysym != NoSymbol) {
1263 char *key_str = NULL;
1265 g_mutex_lock (xvimagesink->x_lock);
1266 key_str = XKeysymToString (keysym);
1267 g_mutex_unlock (xvimagesink->x_lock);
1268 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1269 e.type == KeyPress ? "key-press" : "key-release", key_str);
1271 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1272 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1276 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1278 g_mutex_lock (xvimagesink->flow_lock);
1279 g_mutex_lock (xvimagesink->x_lock);
1283 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1284 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1289 case ConfigureNotify:
1290 g_mutex_unlock (xvimagesink->x_lock);
1291 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1292 g_mutex_lock (xvimagesink->x_lock);
1300 if (xvimagesink->handle_expose && (exposed || configured)) {
1301 g_mutex_unlock (xvimagesink->x_lock);
1302 g_mutex_unlock (xvimagesink->flow_lock);
1304 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1306 g_mutex_lock (xvimagesink->flow_lock);
1307 g_mutex_lock (xvimagesink->x_lock);
1310 /* Handle Display events */
1311 while (XPending (xvimagesink->xcontext->disp)) {
1312 XNextEvent (xvimagesink->xcontext->disp, &e);
1315 case ClientMessage:{
1318 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1319 "WM_DELETE_WINDOW", True);
1320 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1321 /* Handle window deletion by posting an error on the bus */
1322 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1323 ("Output window was closed"), (NULL));
1325 g_mutex_unlock (xvimagesink->x_lock);
1326 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1327 xvimagesink->xwindow = NULL;
1328 g_mutex_lock (xvimagesink->x_lock);
1337 g_mutex_unlock (xvimagesink->x_lock);
1338 g_mutex_unlock (xvimagesink->flow_lock);
1342 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1343 XvAdaptorInfo * adaptors, int adaptor_no)
1348 /* Do we support XvImageMask ? */
1349 if (!(adaptors[adaptor_no].type & XvImageMask)) {
1350 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1351 adaptors[adaptor_no].name);
1355 /* We found such an adaptor, looking for an available port */
1356 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1357 /* We try to grab the port */
1358 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1359 if (Success == res) {
1360 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1361 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1362 adaptors[adaptor_no].num_ports);
1364 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1365 adaptors[adaptor_no].name, res);
1370 /* This function generates a caps with all supported format by the first
1371 Xv grabable port we find. We store each one of the supported formats in a
1372 format list and append the format to a newly created caps that we return
1373 If this function does not return NULL because of an error, it also grabs
1374 the port via XvGrabPort */
1376 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1377 GstXContext * xcontext)
1380 XvAdaptorInfo *adaptors;
1382 XvImageFormatValues *formats = NULL;
1384 XvEncodingInfo *encodings = NULL;
1385 gulong max_w = G_MAXINT, max_h = G_MAXINT;
1386 GstCaps *caps = NULL;
1387 GstCaps *rgb_caps = NULL;
1389 g_return_val_if_fail (xcontext != NULL, NULL);
1391 /* First let's check that XVideo extension is available */
1392 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1393 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1394 ("Could not initialise Xv output"),
1395 ("XVideo extension is not available"));
1399 /* Then we get adaptors list */
1400 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1401 &xcontext->nb_adaptors, &adaptors)) {
1402 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1403 ("Could not initialise Xv output"),
1404 ("Failed getting XV adaptors list"));
1408 xcontext->xv_port_id = 0;
1410 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1412 xcontext->adaptors =
1413 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
1415 /* Now fill up our adaptor name array */
1416 for (i = 0; i < xcontext->nb_adaptors; i++) {
1417 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
1420 if (xvimagesink->adaptor_no >= 0 &&
1421 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
1422 /* Find xv port from user defined adaptor */
1423 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
1424 xvimagesink->adaptor_no);
1427 if (!xcontext->xv_port_id) {
1428 /* Now search for an adaptor that supports XvImageMask */
1429 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
1430 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
1431 xvimagesink->adaptor_no = i;
1435 XvFreeAdaptorInfo (adaptors);
1437 if (!xcontext->xv_port_id) {
1438 xvimagesink->adaptor_no = -1;
1439 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
1440 ("Could not initialise Xv output"), ("No port available"));
1444 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1446 int count, todo = 3;
1447 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1448 xcontext->xv_port_id, &count);
1449 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1450 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
1451 static const char colorkey[] = "XV_COLORKEY";
1453 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
1455 xvimagesink->have_autopaint_colorkey = FALSE;
1456 xvimagesink->have_double_buffer = FALSE;
1457 xvimagesink->have_colorkey = FALSE;
1459 for (i = 0; ((i < count) && todo); i++)
1460 if (!strcmp (attr[i].name, autopaint)) {
1461 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
1463 /* turn on autopaint colorkey */
1464 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1465 (xvimagesink->autopaint_colorkey ? 1 : 0));
1467 xvimagesink->have_autopaint_colorkey = TRUE;
1468 } else if (!strcmp (attr[i].name, dbl_buffer)) {
1469 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
1471 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1472 (xvimagesink->double_buffer ? 1 : 0));
1474 xvimagesink->have_double_buffer = TRUE;
1475 } else if (!strcmp (attr[i].name, colorkey)) {
1476 /* Set the colorkey, default is something that is dark but hopefully
1477 * won't randomly appear on the screen elsewhere (ie not black or greys)
1478 * can be overridden by setting "colorkey" property
1480 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
1482 gboolean set_attr = TRUE;
1485 /* set a colorkey in the right format RGB565/RGB888
1486 * We only handle these 2 cases, because they're the only types of
1487 * devices we've encountered. If we don't recognise it, leave it alone
1489 cr = (xvimagesink->colorkey >> 16);
1490 cg = (xvimagesink->colorkey >> 8) & 0xFF;
1491 cb = (xvimagesink->colorkey) & 0xFF;
1492 switch (xcontext->depth) {
1493 case 16: /* RGB 565 */
1497 ckey = (cr << 11) | (cg << 5) | cb;
1500 case 32: /* RGB 888 / ARGB 8888 */
1501 ckey = (cr << 16) | (cg << 8) | cb;
1504 GST_DEBUG_OBJECT (xvimagesink,
1505 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1512 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1513 (guint32) attr[i].max_value);
1514 GST_LOG_OBJECT (xvimagesink,
1515 "Setting color key for display depth %d to 0x%x",
1516 xcontext->depth, ckey);
1518 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1522 xvimagesink->have_colorkey = TRUE;
1528 /* Get the list of encodings supported by the adapter and look for the
1529 * XV_IMAGE encoding so we can determine the maximum width and height
1531 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1534 for (i = 0; i < nb_encodings; i++) {
1535 GST_LOG_OBJECT (xvimagesink,
1536 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1537 i, encodings[i].name, encodings[i].width, encodings[i].height,
1538 encodings[i].rate.numerator, encodings[i].rate.denominator);
1539 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1540 max_w = encodings[i].width;
1541 max_h = encodings[i].height;
1545 XvFreeEncodingInfo (encodings);
1547 /* We get all image formats supported by our port */
1548 formats = XvListImageFormats (xcontext->disp,
1549 xcontext->xv_port_id, &nb_formats);
1550 caps = gst_caps_new_empty ();
1551 for (i = 0; i < nb_formats; i++) {
1552 GstCaps *format_caps = NULL;
1553 gboolean is_rgb_format = FALSE;
1555 /* We set the image format of the xcontext to an existing one. This
1556 is just some valid image format for making our xshm calls check before
1557 caps negotiation really happens. */
1558 xcontext->im_format = formats[i].id;
1560 switch (formats[i].type) {
1563 XvImageFormatValues *fmt = &(formats[i]);
1564 gint endianness = G_BIG_ENDIAN;
1566 if (fmt->byte_order == LSBFirst) {
1567 /* our caps system handles 24/32bpp RGB as big-endian. */
1568 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1569 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1570 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1571 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1573 if (fmt->bits_per_pixel == 24) {
1574 fmt->red_mask >>= 8;
1575 fmt->green_mask >>= 8;
1576 fmt->blue_mask >>= 8;
1579 endianness = G_LITTLE_ENDIAN;
1582 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1583 "endianness", G_TYPE_INT, endianness,
1584 "depth", G_TYPE_INT, fmt->depth,
1585 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1586 "red_mask", G_TYPE_INT, fmt->red_mask,
1587 "green_mask", G_TYPE_INT, fmt->green_mask,
1588 "blue_mask", G_TYPE_INT, fmt->blue_mask,
1589 "width", GST_TYPE_INT_RANGE, 1, max_w,
1590 "height", GST_TYPE_INT_RANGE, 1, max_h,
1591 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1593 is_rgb_format = TRUE;
1597 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1598 "format", GST_TYPE_FOURCC, formats[i].id,
1599 "width", GST_TYPE_INT_RANGE, 1, max_w,
1600 "height", GST_TYPE_INT_RANGE, 1, max_h,
1601 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1604 g_assert_not_reached ();
1609 GstXvImageFormat *format = NULL;
1611 format = g_new0 (GstXvImageFormat, 1);
1613 format->format = formats[i].id;
1614 format->caps = gst_caps_copy (format_caps);
1615 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1618 if (is_rgb_format) {
1619 if (rgb_caps == NULL)
1620 rgb_caps = format_caps;
1622 gst_caps_append (rgb_caps, format_caps);
1624 gst_caps_append (caps, format_caps);
1628 /* Collected all caps into either the caps or rgb_caps structures.
1629 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1631 gst_caps_append (caps, rgb_caps);
1636 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1638 if (gst_caps_is_empty (caps)) {
1639 gst_caps_unref (caps);
1640 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1641 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1642 ("No supported format found"));
1650 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1652 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1654 GST_OBJECT_LOCK (xvimagesink);
1655 while (xvimagesink->running) {
1656 GST_OBJECT_UNLOCK (xvimagesink);
1658 if (xvimagesink->xwindow) {
1659 gst_xvimagesink_handle_xevents (xvimagesink);
1661 /* FIXME: do we want to align this with the framerate or anything else? */
1662 g_usleep (G_USEC_PER_SEC / 20);
1664 GST_OBJECT_LOCK (xvimagesink);
1666 GST_OBJECT_UNLOCK (xvimagesink);
1672 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1674 GThread *thread = NULL;
1676 /* don't start the thread too early */
1677 if (xvimagesink->xcontext == NULL) {
1681 GST_OBJECT_LOCK (xvimagesink);
1682 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1683 if (!xvimagesink->event_thread) {
1684 /* Setup our event listening thread */
1685 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1686 xvimagesink->handle_expose, xvimagesink->handle_events);
1687 xvimagesink->running = TRUE;
1688 xvimagesink->event_thread = g_thread_create (
1689 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1692 if (xvimagesink->event_thread) {
1693 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1694 xvimagesink->handle_expose, xvimagesink->handle_events);
1695 xvimagesink->running = FALSE;
1696 /* grab thread and mark it as NULL */
1697 thread = xvimagesink->event_thread;
1698 xvimagesink->event_thread = NULL;
1701 GST_OBJECT_UNLOCK (xvimagesink);
1703 /* Wait for our event thread to finish */
1705 g_thread_join (thread);
1710 /* This function calculates the pixel aspect ratio based on the properties
1711 * in the xcontext structure and stores it there. */
1713 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1715 static const gint par[][2] = {
1716 {1, 1}, /* regular screen */
1717 {16, 15}, /* PAL TV */
1718 {11, 10}, /* 525 line Rec.601 video */
1719 {54, 59}, /* 625 line Rec.601 video */
1720 {64, 45}, /* 1280x1024 on 16:9 display */
1721 {5, 3}, /* 1280x1024 on 4:3 display */
1722 {4, 3} /* 800x600 on 16:9 display */
1729 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1731 /* first calculate the "real" ratio based on the X values;
1732 * which is the "physical" w/h divided by the w/h in pixels of the display */
1733 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1734 / (xcontext->heightmm * xcontext->width);
1736 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1738 if (xcontext->width == 720 && xcontext->height == 576) {
1739 ratio = 4.0 * 576 / (3.0 * 720);
1741 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1742 /* now find the one from par[][2] with the lowest delta to the real one */
1746 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1747 gdouble this_delta = DELTA (i);
1749 if (this_delta < delta) {
1755 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1756 par[index][0], par[index][1]);
1758 g_free (xcontext->par);
1759 xcontext->par = g_new0 (GValue, 1);
1760 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1761 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1762 GST_DEBUG ("set xcontext PAR to %d/%d",
1763 gst_value_get_fraction_numerator (xcontext->par),
1764 gst_value_get_fraction_denominator (xcontext->par));
1767 /* This function gets the X Display and global info about it. Everything is
1768 stored in our object and will be cleaned when the object is disposed. Note
1769 here that caps for supported format are generated without any window or
1771 static GstXContext *
1772 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1774 GstXContext *xcontext = NULL;
1775 XPixmapFormatValues *px_formats = NULL;
1776 gint nb_formats = 0, i, j, N_attr;
1777 XvAttribute *xv_attr;
1779 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1780 "XV_BRIGHTNESS", "XV_CONTRAST"
1783 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1785 xcontext = g_new0 (GstXContext, 1);
1786 xcontext->im_format = 0;
1788 g_mutex_lock (xvimagesink->x_lock);
1790 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1792 if (!xcontext->disp) {
1793 g_mutex_unlock (xvimagesink->x_lock);
1795 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1796 ("Could not initialise Xv output"), ("Could not open display"));
1800 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1801 xcontext->screen_num = DefaultScreen (xcontext->disp);
1802 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1803 xcontext->root = DefaultRootWindow (xcontext->disp);
1804 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1805 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1806 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1808 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1809 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1810 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1811 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1813 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1814 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1816 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1817 /* We get supported pixmap formats at supported depth */
1818 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1821 XCloseDisplay (xcontext->disp);
1822 g_mutex_unlock (xvimagesink->x_lock);
1823 g_free (xcontext->par);
1825 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1826 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1830 /* We get bpp value corresponding to our running depth */
1831 for (i = 0; i < nb_formats; i++) {
1832 if (px_formats[i].depth == xcontext->depth)
1833 xcontext->bpp = px_formats[i].bits_per_pixel;
1838 xcontext->endianness =
1839 (ImageByteOrder (xcontext->disp) ==
1840 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1842 /* our caps system handles 24/32bpp RGB as big-endian. */
1843 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1844 xcontext->endianness == G_LITTLE_ENDIAN) {
1845 xcontext->endianness = G_BIG_ENDIAN;
1846 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1847 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1848 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1849 if (xcontext->bpp == 24) {
1850 xcontext->visual->red_mask >>= 8;
1851 xcontext->visual->green_mask >>= 8;
1852 xcontext->visual->blue_mask >>= 8;
1856 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1858 if (!xcontext->caps) {
1859 XCloseDisplay (xcontext->disp);
1860 g_mutex_unlock (xvimagesink->x_lock);
1861 g_free (xcontext->par);
1863 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1867 /* Search for XShm extension support */
1868 if (XShmQueryExtension (xcontext->disp) &&
1869 gst_xvimagesink_check_xshm_calls (xcontext)) {
1870 xcontext->use_xshm = TRUE;
1871 GST_DEBUG ("xvimagesink is using XShm extension");
1873 #endif /* HAVE_XSHM */
1875 xcontext->use_xshm = FALSE;
1876 GST_DEBUG ("xvimagesink is not using XShm extension");
1879 xv_attr = XvQueryPortAttributes (xcontext->disp,
1880 xcontext->xv_port_id, &N_attr);
1883 /* Generate the channels list */
1884 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1885 XvAttribute *matching_attr = NULL;
1887 /* Retrieve the property atom if it exists. If it doesn't exist,
1888 * the attribute itself must not either, so we can skip */
1889 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1890 if (prop_atom == None)
1893 if (xv_attr != NULL) {
1894 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1895 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1896 matching_attr = xv_attr + j;
1899 if (matching_attr) {
1900 GstColorBalanceChannel *channel;
1902 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1903 channel->label = g_strdup (channels[i]);
1904 channel->min_value = matching_attr->min_value;
1905 channel->max_value = matching_attr->max_value;
1907 xcontext->channels_list = g_list_append (xcontext->channels_list,
1910 /* If the colorbalance settings have not been touched we get Xv values
1911 as defaults and update our internal variables */
1912 if (!xvimagesink->cb_changed) {
1915 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1917 /* Normalize val to [-1000, 1000] */
1918 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1919 (double) (channel->max_value - channel->min_value));
1921 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1922 xvimagesink->hue = val;
1923 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1924 xvimagesink->saturation = val;
1925 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1926 xvimagesink->brightness = val;
1927 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1928 xvimagesink->contrast = val;
1936 g_mutex_unlock (xvimagesink->x_lock);
1941 /* This function cleans the X context. Closing the Display, releasing the XV
1942 port and unrefing the caps for supported formats. */
1944 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1946 GList *formats_list, *channels_list;
1947 GstXContext *xcontext;
1950 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1952 GST_OBJECT_LOCK (xvimagesink);
1953 if (xvimagesink->xcontext == NULL) {
1954 GST_OBJECT_UNLOCK (xvimagesink);
1958 /* Take the XContext from the sink and clean it up */
1959 xcontext = xvimagesink->xcontext;
1960 xvimagesink->xcontext = NULL;
1962 GST_OBJECT_UNLOCK (xvimagesink);
1965 formats_list = xcontext->formats_list;
1967 while (formats_list) {
1968 GstXvImageFormat *format = formats_list->data;
1970 gst_caps_unref (format->caps);
1972 formats_list = g_list_next (formats_list);
1975 if (xcontext->formats_list)
1976 g_list_free (xcontext->formats_list);
1978 channels_list = xcontext->channels_list;
1980 while (channels_list) {
1981 GstColorBalanceChannel *channel = channels_list->data;
1983 g_object_unref (channel);
1984 channels_list = g_list_next (channels_list);
1987 if (xcontext->channels_list)
1988 g_list_free (xcontext->channels_list);
1990 gst_caps_unref (xcontext->caps);
1991 if (xcontext->last_caps)
1992 gst_caps_replace (&xcontext->last_caps, NULL);
1994 for (i = 0; i < xcontext->nb_adaptors; i++) {
1995 g_free (xcontext->adaptors[i]);
1998 g_free (xcontext->adaptors);
2000 g_free (xcontext->par);
2002 g_mutex_lock (xvimagesink->x_lock);
2004 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
2006 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
2008 XCloseDisplay (xcontext->disp);
2010 g_mutex_unlock (xvimagesink->x_lock);
2016 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
2018 g_mutex_lock (xvimagesink->pool_lock);
2020 while (xvimagesink->image_pool) {
2021 GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
2023 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2024 xvimagesink->image_pool);
2025 gst_xvimage_buffer_free (xvimage);
2028 g_mutex_unlock (xvimagesink->pool_lock);
2033 /* This function tries to get a format matching with a given caps in the
2034 supported list of formats we generated in gst_xvimagesink_get_xv_support */
2036 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
2041 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2043 list = xvimagesink->xcontext->formats_list;
2046 GstXvImageFormat *format = list->data;
2049 if (gst_caps_can_intersect (caps, format->caps)) {
2050 return format->format;
2053 list = g_list_next (list);
2060 gst_xvimagesink_getcaps (GstBaseSink * bsink)
2062 GstXvImageSink *xvimagesink;
2064 xvimagesink = GST_XVIMAGESINK (bsink);
2066 if (xvimagesink->xcontext)
2067 return gst_caps_ref (xvimagesink->xcontext->caps);
2070 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
2075 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2077 GstXvImageSink *xvimagesink;
2078 GstStructure *structure;
2079 guint32 im_format = 0;
2081 gint video_width, video_height;
2082 gint disp_x, disp_y;
2083 gint disp_width, disp_height;
2084 gint video_par_n, video_par_d; /* video's PAR */
2085 gint display_par_n, display_par_d; /* display's PAR */
2086 const GValue *caps_par;
2087 const GValue *caps_disp_reg;
2091 xvimagesink = GST_XVIMAGESINK (bsink);
2093 GST_DEBUG_OBJECT (xvimagesink,
2094 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
2095 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
2097 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
2098 goto incompatible_caps;
2100 structure = gst_caps_get_structure (caps, 0);
2101 ret = gst_structure_get_int (structure, "width", &video_width);
2102 ret &= gst_structure_get_int (structure, "height", &video_height);
2103 fps = gst_structure_get_value (structure, "framerate");
2104 ret &= (fps != NULL);
2107 goto incomplete_caps;
2109 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
2110 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
2112 xvimagesink->video_width = video_width;
2113 xvimagesink->video_height = video_height;
2115 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
2116 if (im_format == -1)
2117 goto invalid_format;
2119 /* get aspect ratio from caps if it's present, and
2120 * convert video width and height to a display width and height
2121 * using wd / hd = wv / hv * PARv / PARd */
2123 /* get video's PAR */
2124 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
2126 video_par_n = gst_value_get_fraction_numerator (caps_par);
2127 video_par_d = gst_value_get_fraction_denominator (caps_par);
2132 /* get display's PAR */
2133 if (xvimagesink->par) {
2134 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2135 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2141 /* get the display region */
2142 caps_disp_reg = gst_structure_get_value (structure, "display-region");
2143 if (caps_disp_reg) {
2144 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
2145 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
2146 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
2148 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
2150 disp_x = disp_y = 0;
2151 disp_width = video_width;
2152 disp_height = video_height;
2155 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2156 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
2159 xvimagesink->disp_x = disp_x;
2160 xvimagesink->disp_y = disp_y;
2161 xvimagesink->disp_width = disp_width;
2162 xvimagesink->disp_height = disp_height;
2164 GST_DEBUG_OBJECT (xvimagesink,
2165 "video width/height: %dx%d, calculated display ratio: %d/%d",
2166 video_width, video_height, num, den);
2168 /* now find a width x height that respects this display ratio.
2169 * prefer those that have one of w/h the same as the incoming video
2170 * using wd / hd = num / den */
2172 /* start with same height, because of interlaced video */
2173 /* check hd / den is an integer scale factor, and scale wd with the PAR */
2174 if (video_height % den == 0) {
2175 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2176 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2177 gst_util_uint64_scale_int (video_height, num, den);
2178 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2179 } else if (video_width % num == 0) {
2180 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2181 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2182 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2183 gst_util_uint64_scale_int (video_width, den, num);
2185 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2186 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2187 gst_util_uint64_scale_int (video_height, num, den);
2188 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2190 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2191 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2193 /* Notify application to set xwindow id now */
2194 g_mutex_lock (xvimagesink->flow_lock);
2195 if (!xvimagesink->xwindow) {
2196 g_mutex_unlock (xvimagesink->flow_lock);
2197 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2199 g_mutex_unlock (xvimagesink->flow_lock);
2202 /* Creating our window and our image with the display size in pixels */
2203 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2204 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
2205 goto no_display_size;
2207 g_mutex_lock (xvimagesink->flow_lock);
2208 if (!xvimagesink->xwindow) {
2209 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2210 GST_VIDEO_SINK_WIDTH (xvimagesink),
2211 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2214 /* After a resize, we want to redraw the borders in case the new frame size
2215 * doesn't cover the same area */
2216 xvimagesink->redraw_border = TRUE;
2218 /* We renew our xvimage only if size or format changed;
2219 * the xvimage is the same size as the video pixel size */
2220 if ((xvimagesink->xvimage) &&
2221 ((im_format != xvimagesink->xvimage->im_format) ||
2222 (video_width != xvimagesink->xvimage->width) ||
2223 (video_height != xvimagesink->xvimage->height))) {
2224 GST_DEBUG_OBJECT (xvimagesink,
2225 "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2226 GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2227 GST_FOURCC_ARGS (im_format));
2228 GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2229 gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2230 xvimagesink->xvimage = NULL;
2233 g_mutex_unlock (xvimagesink->flow_lock);
2240 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
2245 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
2246 "height or framerate from intersected caps");
2251 GST_DEBUG_OBJECT (xvimagesink,
2252 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
2257 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2258 ("Error calculating the output display ratio of the video."));
2263 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2264 ("Error calculating the output display ratio of the video."));
2269 static GstStateChangeReturn
2270 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2272 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2273 GstXvImageSink *xvimagesink;
2274 GstXContext *xcontext = NULL;
2276 xvimagesink = GST_XVIMAGESINK (element);
2278 switch (transition) {
2279 case GST_STATE_CHANGE_NULL_TO_READY:
2280 /* Initializing the XContext */
2281 if (xvimagesink->xcontext == NULL) {
2282 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2283 if (xcontext == NULL)
2284 return GST_STATE_CHANGE_FAILURE;
2285 GST_OBJECT_LOCK (xvimagesink);
2287 xvimagesink->xcontext = xcontext;
2288 GST_OBJECT_UNLOCK (xvimagesink);
2291 /* update object's par with calculated one if not set yet */
2292 if (!xvimagesink->par) {
2293 xvimagesink->par = g_new0 (GValue, 1);
2294 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2295 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2297 /* call XSynchronize with the current value of synchronous */
2298 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2299 xvimagesink->synchronous ? "TRUE" : "FALSE");
2300 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2301 gst_xvimagesink_update_colorbalance (xvimagesink);
2302 gst_xvimagesink_manage_event_thread (xvimagesink);
2304 case GST_STATE_CHANGE_READY_TO_PAUSED:
2305 g_mutex_lock (xvimagesink->pool_lock);
2306 xvimagesink->pool_invalid = FALSE;
2307 g_mutex_unlock (xvimagesink->pool_lock);
2309 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2311 case GST_STATE_CHANGE_PAUSED_TO_READY:
2312 g_mutex_lock (xvimagesink->pool_lock);
2313 xvimagesink->pool_invalid = TRUE;
2314 g_mutex_unlock (xvimagesink->pool_lock);
2320 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2322 switch (transition) {
2323 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2325 case GST_STATE_CHANGE_PAUSED_TO_READY:
2326 xvimagesink->fps_n = 0;
2327 xvimagesink->fps_d = 1;
2328 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2329 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2331 case GST_STATE_CHANGE_READY_TO_NULL:
2332 gst_xvimagesink_reset (xvimagesink);
2342 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2343 GstClockTime * start, GstClockTime * end)
2345 GstXvImageSink *xvimagesink;
2347 xvimagesink = GST_XVIMAGESINK (bsink);
2349 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2350 *start = GST_BUFFER_TIMESTAMP (buf);
2351 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2352 *end = *start + GST_BUFFER_DURATION (buf);
2354 if (xvimagesink->fps_n > 0) {
2356 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2357 xvimagesink->fps_n);
2363 static GstFlowReturn
2364 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2366 GstXvImageSink *xvimagesink;
2368 xvimagesink = GST_XVIMAGESINK (vsink);
2370 /* If this buffer has been allocated using our buffer management we simply
2371 put the ximage which is in the PRIVATE pointer */
2372 if (GST_IS_XVIMAGE_BUFFER (buf)) {
2373 GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
2374 if (!gst_xvimagesink_xvimage_put (xvimagesink,
2375 GST_XVIMAGE_BUFFER_CAST (buf)))
2378 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
2379 "slow copy into bufferpool buffer %p", buf);
2380 /* Else we have to copy the data into our private image, */
2381 /* if we have one... */
2382 if (!xvimagesink->xvimage) {
2383 GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
2385 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
2386 GST_BUFFER_CAPS (buf));
2388 if (!xvimagesink->xvimage)
2389 /* The create method should have posted an informative error */
2392 if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
2393 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2394 ("Failed to create output image buffer of %dx%d pixels",
2395 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
2396 ("XServer allocated buffer size did not match input buffer"));
2398 gst_xvimage_buffer_destroy (xvimagesink->xvimage);
2399 xvimagesink->xvimage = NULL;
2404 memcpy (xvimagesink->xvimage->xvimage->data,
2405 GST_BUFFER_DATA (buf),
2406 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
2408 if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
2417 /* No image available. That's very bad ! */
2418 GST_WARNING_OBJECT (xvimagesink, "could not create image");
2419 return GST_FLOW_ERROR;
2423 /* No Window available to put our image into */
2424 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
2425 return GST_FLOW_ERROR;
2430 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
2432 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
2434 switch (GST_EVENT_TYPE (event)) {
2435 case GST_EVENT_TAG:{
2437 gchar *title = NULL;
2439 gst_event_parse_tag (event, &l);
2440 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
2443 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
2444 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
2454 if (GST_BASE_SINK_CLASS (parent_class)->event)
2455 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
2460 /* Buffer management */
2463 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
2466 GstCaps *intersection;
2470 gint par_n = 1, par_d = 1;
2474 new_caps = gst_caps_copy (caps);
2476 s = gst_caps_get_structure (new_caps, 0);
2478 gst_structure_get_int (s, "width", &width);
2479 gst_structure_get_int (s, "height", &height);
2480 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
2482 gst_structure_remove_field (s, "width");
2483 gst_structure_remove_field (s, "height");
2484 gst_structure_remove_field (s, "pixel-aspect-ratio");
2486 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2487 gst_caps_unref (new_caps);
2489 if (gst_caps_is_empty (intersection))
2490 return intersection;
2492 s = gst_caps_get_structure (intersection, 0);
2494 gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
2496 /* xvimagesink supports all PARs */
2498 gst_structure_fixate_field_nearest_int (s, "width", width);
2499 gst_structure_fixate_field_nearest_int (s, "height", height);
2500 gst_structure_get_int (s, "width", &w);
2501 gst_structure_get_int (s, "height", &h);
2503 gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
2504 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
2507 return intersection;
2510 static GstFlowReturn
2511 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
2512 GstCaps * caps, GstBuffer ** buf)
2514 GstFlowReturn ret = GST_FLOW_OK;
2515 GstXvImageSink *xvimagesink;
2516 GstXvImageBuffer *xvimage = NULL;
2517 GstCaps *intersection = NULL;
2518 GstStructure *structure = NULL;
2519 gint width, height, image_format;
2521 xvimagesink = GST_XVIMAGESINK (bsink);
2523 if (G_UNLIKELY (!caps))
2526 g_mutex_lock (xvimagesink->pool_lock);
2527 if (G_UNLIKELY (xvimagesink->pool_invalid))
2530 if (G_LIKELY (xvimagesink->xcontext->last_caps &&
2531 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
2532 GST_LOG_OBJECT (xvimagesink,
2533 "buffer alloc for same last_caps, reusing caps");
2534 intersection = gst_caps_ref (caps);
2535 image_format = xvimagesink->xcontext->last_format;
2536 width = xvimagesink->xcontext->last_width;
2537 height = xvimagesink->xcontext->last_height;
2539 goto reuse_last_caps;
2542 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
2543 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
2544 caps, xvimagesink->xcontext->caps);
2546 /* Check the caps against our xcontext */
2547 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2549 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2550 GST_PTR_FORMAT, intersection);
2552 if (gst_caps_is_empty (intersection)) {
2555 gst_caps_unref (intersection);
2557 /* So we don't support this kind of buffer, let's define one we'd like */
2558 new_caps = gst_caps_copy (caps);
2560 structure = gst_caps_get_structure (new_caps, 0);
2561 if (!gst_structure_has_field (structure, "width") ||
2562 !gst_structure_has_field (structure, "height")) {
2563 gst_caps_unref (new_caps);
2567 /* Try different dimensions */
2569 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2571 if (gst_caps_is_empty (intersection)) {
2572 /* Try with different YUV formats first */
2573 gst_structure_set_name (structure, "video/x-raw-yuv");
2575 /* Remove format specific fields */
2576 gst_structure_remove_field (structure, "format");
2577 gst_structure_remove_field (structure, "endianness");
2578 gst_structure_remove_field (structure, "depth");
2579 gst_structure_remove_field (structure, "bpp");
2580 gst_structure_remove_field (structure, "red_mask");
2581 gst_structure_remove_field (structure, "green_mask");
2582 gst_structure_remove_field (structure, "blue_mask");
2583 gst_structure_remove_field (structure, "alpha_mask");
2585 /* Reuse intersection with Xcontext */
2586 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2589 if (gst_caps_is_empty (intersection)) {
2590 /* Try with different dimensions and YUV formats */
2592 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2595 if (gst_caps_is_empty (intersection)) {
2596 /* Now try with RGB */
2597 gst_structure_set_name (structure, "video/x-raw-rgb");
2598 /* And interset again */
2599 gst_caps_unref (intersection);
2600 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2603 if (gst_caps_is_empty (intersection)) {
2604 /* Try with different dimensions and RGB formats */
2606 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2609 /* Clean this copy */
2610 gst_caps_unref (new_caps);
2612 if (gst_caps_is_empty (intersection))
2616 /* Ensure the returned caps are fixed */
2617 gst_caps_truncate (intersection);
2619 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2620 GST_PTR_FORMAT, intersection);
2621 if (gst_caps_is_equal (intersection, caps)) {
2622 /* Things work better if we return a buffer with the same caps ptr
2623 * as was asked for when we can */
2624 gst_caps_replace (&intersection, caps);
2627 /* Get image format from caps */
2628 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2631 /* Get geometry from caps */
2632 structure = gst_caps_get_structure (intersection, 0);
2633 if (!gst_structure_get_int (structure, "width", &width) ||
2634 !gst_structure_get_int (structure, "height", &height) ||
2638 /* Store our caps and format as the last_caps to avoid expensive
2639 * caps intersection next time */
2640 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2641 xvimagesink->xcontext->last_format = image_format;
2642 xvimagesink->xcontext->last_width = width;
2643 xvimagesink->xcontext->last_height = height;
2647 /* Walking through the pool cleaning unusable images and searching for a
2649 while (xvimagesink->image_pool) {
2650 xvimage = xvimagesink->image_pool->data;
2653 /* Removing from the pool */
2654 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2655 xvimagesink->image_pool);
2657 /* We check for geometry or image format changes */
2658 if ((xvimage->width != width) ||
2659 (xvimage->height != height) || (xvimage->im_format != image_format)) {
2660 /* This image is unusable. Destroying... */
2661 gst_xvimage_buffer_free (xvimage);
2664 /* We found a suitable image */
2665 GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
2672 /* We found no suitable image in the pool. Creating... */
2673 GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
2674 xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
2676 g_mutex_unlock (xvimagesink->pool_lock);
2679 /* Make sure the buffer is cleared of any previously used flags */
2680 GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
2681 gst_buffer_set_caps (GST_BUFFER_CAST (xvimage), intersection);
2684 *buf = GST_BUFFER_CAST (xvimage);
2688 gst_caps_unref (intersection);
2696 GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
2697 ret = GST_FLOW_WRONG_STATE;
2698 g_mutex_unlock (xvimagesink->pool_lock);
2703 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2704 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2705 " are completely incompatible with those caps", caps,
2706 xvimagesink->xcontext->caps);
2707 ret = GST_FLOW_NOT_NEGOTIATED;
2708 g_mutex_unlock (xvimagesink->pool_lock);
2713 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2714 GST_PTR_FORMAT, intersection);
2715 ret = GST_FLOW_NOT_NEGOTIATED;
2716 g_mutex_unlock (xvimagesink->pool_lock);
2721 GST_WARNING_OBJECT (xvimagesink, "have no caps, doing fallback allocation");
2728 /* Interfaces stuff */
2731 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2733 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2734 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE)
2741 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2743 klass->supported = gst_xvimagesink_interface_supported;
2747 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2748 GstStructure * structure)
2750 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2753 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2755 GstVideoRectangle src, dst, result;
2756 gdouble x, y, xscale = 1.0, yscale = 1.0;
2758 event = gst_event_new_navigation (structure);
2760 /* We take the flow_lock while we look at the window */
2761 g_mutex_lock (xvimagesink->flow_lock);
2763 if (!xvimagesink->xwindow) {
2764 g_mutex_unlock (xvimagesink->flow_lock);
2768 if (xvimagesink->keep_aspect) {
2769 /* We get the frame position using the calculated geometry from _setcaps
2770 that respect pixel aspect ratios */
2771 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2772 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2773 dst.w = xvimagesink->render_rect.w;
2774 dst.h = xvimagesink->render_rect.h;
2776 gst_video_sink_center_rect (src, dst, &result, TRUE);
2777 result.x += xvimagesink->render_rect.x;
2778 result.y += xvimagesink->render_rect.y;
2780 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2783 g_mutex_unlock (xvimagesink->flow_lock);
2785 /* We calculate scaling using the original video frames geometry to include
2786 pixel aspect ratio scaling. */
2787 xscale = (gdouble) xvimagesink->video_width / result.w;
2788 yscale = (gdouble) xvimagesink->video_height / result.h;
2790 /* Converting pointer coordinates to the non scaled geometry */
2791 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2792 x = MIN (x, result.x + result.w);
2793 x = MAX (x - result.x, 0);
2794 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2795 (gdouble) x * xscale, NULL);
2797 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2798 y = MIN (y, result.y + result.h);
2799 y = MAX (y - result.y, 0);
2800 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2801 (gdouble) y * yscale, NULL);
2804 gst_pad_send_event (peer, event);
2805 gst_object_unref (peer);
2810 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2812 iface->send_event = gst_xvimagesink_navigation_send_event;
2816 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2818 XID xwindow_id = id;
2819 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2820 GstXWindow *xwindow = NULL;
2822 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2824 g_mutex_lock (xvimagesink->flow_lock);
2826 /* If we already use that window return */
2827 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2828 g_mutex_unlock (xvimagesink->flow_lock);
2832 /* If the element has not initialized the X11 context try to do so */
2833 if (!xvimagesink->xcontext &&
2834 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2835 g_mutex_unlock (xvimagesink->flow_lock);
2836 /* we have thrown a GST_ELEMENT_ERROR now */
2840 gst_xvimagesink_update_colorbalance (xvimagesink);
2842 /* Clear image pool as the images are unusable anyway */
2843 gst_xvimagesink_imagepool_clear (xvimagesink);
2845 /* Clear the xvimage */
2846 if (xvimagesink->xvimage) {
2847 gst_xvimage_buffer_free (xvimagesink->xvimage);
2848 xvimagesink->xvimage = NULL;
2851 /* If a window is there already we destroy it */
2852 if (xvimagesink->xwindow) {
2853 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2854 xvimagesink->xwindow = NULL;
2857 /* If the xid is 0 we go back to an internal window */
2858 if (xwindow_id == 0) {
2859 /* If no width/height caps nego did not happen window will be created
2860 during caps nego then */
2861 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2862 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2864 gst_xvimagesink_xwindow_new (xvimagesink,
2865 GST_VIDEO_SINK_WIDTH (xvimagesink),
2866 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2869 XWindowAttributes attr;
2871 xwindow = g_new0 (GstXWindow, 1);
2872 xwindow->win = xwindow_id;
2874 /* Set the event we want to receive and create a GC */
2875 g_mutex_lock (xvimagesink->x_lock);
2877 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2879 xwindow->width = attr.width;
2880 xwindow->height = attr.height;
2881 xwindow->internal = FALSE;
2882 if (!xvimagesink->have_render_rect) {
2883 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2884 xvimagesink->render_rect.w = attr.width;
2885 xvimagesink->render_rect.h = attr.height;
2887 if (xvimagesink->handle_events) {
2888 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2889 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2893 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2894 xwindow->win, 0, NULL);
2895 g_mutex_unlock (xvimagesink->x_lock);
2899 xvimagesink->xwindow = xwindow;
2901 g_mutex_unlock (xvimagesink->flow_lock);
2905 gst_xvimagesink_expose (GstXOverlay * overlay)
2907 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2909 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2910 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2914 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2915 gboolean handle_events)
2917 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2919 xvimagesink->handle_events = handle_events;
2921 g_mutex_lock (xvimagesink->flow_lock);
2923 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2924 g_mutex_unlock (xvimagesink->flow_lock);
2928 g_mutex_lock (xvimagesink->x_lock);
2930 if (handle_events) {
2931 if (xvimagesink->xwindow->internal) {
2932 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2933 ExposureMask | StructureNotifyMask | PointerMotionMask |
2934 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2936 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2937 ExposureMask | StructureNotifyMask | PointerMotionMask |
2938 KeyPressMask | KeyReleaseMask);
2941 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2944 g_mutex_unlock (xvimagesink->x_lock);
2946 g_mutex_unlock (xvimagesink->flow_lock);
2950 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2951 gint width, gint height)
2953 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2955 /* FIXME: how about some locking? */
2956 if (width >= 0 && height >= 0) {
2957 xvimagesink->render_rect.x = x;
2958 xvimagesink->render_rect.y = y;
2959 xvimagesink->render_rect.w = width;
2960 xvimagesink->render_rect.h = height;
2961 xvimagesink->have_render_rect = TRUE;
2963 xvimagesink->render_rect.x = 0;
2964 xvimagesink->render_rect.y = 0;
2965 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2966 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2967 xvimagesink->have_render_rect = FALSE;
2972 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2974 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2975 iface->expose = gst_xvimagesink_expose;
2976 iface->handle_events = gst_xvimagesink_set_event_handling;
2977 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2980 static const GList *
2981 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2983 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2985 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2987 if (xvimagesink->xcontext)
2988 return xvimagesink->xcontext->channels_list;
2994 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2995 GstColorBalanceChannel * channel, gint value)
2997 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2999 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
3000 g_return_if_fail (channel->label != NULL);
3002 xvimagesink->cb_changed = TRUE;
3004 /* Normalize val to [-1000, 1000] */
3005 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
3006 (double) (channel->max_value - channel->min_value));
3008 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3009 xvimagesink->hue = value;
3010 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3011 xvimagesink->saturation = value;
3012 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3013 xvimagesink->contrast = value;
3014 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3015 xvimagesink->brightness = value;
3017 g_warning ("got an unknown channel %s", channel->label);
3021 gst_xvimagesink_update_colorbalance (xvimagesink);
3025 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
3026 GstColorBalanceChannel * channel)
3028 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3031 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
3032 g_return_val_if_fail (channel->label != NULL, 0);
3034 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3035 value = xvimagesink->hue;
3036 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3037 value = xvimagesink->saturation;
3038 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3039 value = xvimagesink->contrast;
3040 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3041 value = xvimagesink->brightness;
3043 g_warning ("got an unknown channel %s", channel->label);
3046 /* Normalize val to [channel->min_value, channel->max_value] */
3047 value = channel->min_value + (channel->max_value - channel->min_value) *
3048 (value + 1000) / 2000;
3054 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
3056 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
3057 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
3058 iface->set_value = gst_xvimagesink_colorbalance_set_value;
3059 iface->get_value = gst_xvimagesink_colorbalance_get_value;
3062 static const GList *
3063 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
3065 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
3066 static GList *list = NULL;
3069 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
3071 g_list_append (list, g_object_class_find_property (klass,
3072 "autopaint-colorkey"));
3074 g_list_append (list, g_object_class_find_property (klass,
3077 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
3084 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
3085 guint prop_id, const GParamSpec * pspec)
3087 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3091 case PROP_AUTOPAINT_COLORKEY:
3092 case PROP_DOUBLE_BUFFER:
3094 GST_DEBUG_OBJECT (xvimagesink,
3095 "probing device list and get capabilities");
3096 if (!xvimagesink->xcontext) {
3097 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
3098 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
3102 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3108 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
3109 guint prop_id, const GParamSpec * pspec)
3111 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3112 gboolean ret = FALSE;
3116 case PROP_AUTOPAINT_COLORKEY:
3117 case PROP_DOUBLE_BUFFER:
3119 if (xvimagesink->xcontext != NULL) {
3126 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3133 static GValueArray *
3134 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
3135 guint prop_id, const GParamSpec * pspec)
3137 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3138 GValueArray *array = NULL;
3140 if (G_UNLIKELY (!xvimagesink->xcontext)) {
3141 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
3150 GValue value = { 0 };
3152 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
3153 g_value_init (&value, G_TYPE_STRING);
3155 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
3156 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
3158 g_value_set_string (&value, adaptor_id_s);
3159 g_value_array_append (array, &value);
3160 g_free (adaptor_id_s);
3162 g_value_unset (&value);
3165 case PROP_AUTOPAINT_COLORKEY:
3166 if (xvimagesink->have_autopaint_colorkey) {
3167 GValue value = { 0 };
3169 array = g_value_array_new (2);
3170 g_value_init (&value, G_TYPE_BOOLEAN);
3171 g_value_set_boolean (&value, FALSE);
3172 g_value_array_append (array, &value);
3173 g_value_set_boolean (&value, TRUE);
3174 g_value_array_append (array, &value);
3175 g_value_unset (&value);
3178 case PROP_DOUBLE_BUFFER:
3179 if (xvimagesink->have_double_buffer) {
3180 GValue value = { 0 };
3182 array = g_value_array_new (2);
3183 g_value_init (&value, G_TYPE_BOOLEAN);
3184 g_value_set_boolean (&value, FALSE);
3185 g_value_array_append (array, &value);
3186 g_value_set_boolean (&value, TRUE);
3187 g_value_array_append (array, &value);
3188 g_value_unset (&value);
3192 if (xvimagesink->have_colorkey) {
3193 GValue value = { 0 };
3195 array = g_value_array_new (1);
3196 g_value_init (&value, GST_TYPE_INT_RANGE);
3197 gst_value_set_int_range (&value, 0, 0xffffff);
3198 g_value_array_append (array, &value);
3199 g_value_unset (&value);
3203 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3212 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
3215 iface->get_properties = gst_xvimagesink_probe_get_properties;
3216 iface->probe_property = gst_xvimagesink_probe_probe_property;
3217 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
3218 iface->get_values = gst_xvimagesink_probe_get_values;
3221 /* =========================================== */
3223 /* Init & Class init */
3225 /* =========================================== */
3228 gst_xvimagesink_set_property (GObject * object, guint prop_id,
3229 const GValue * value, GParamSpec * pspec)
3231 GstXvImageSink *xvimagesink;
3233 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3235 xvimagesink = GST_XVIMAGESINK (object);
3239 xvimagesink->hue = g_value_get_int (value);
3240 xvimagesink->cb_changed = TRUE;
3241 gst_xvimagesink_update_colorbalance (xvimagesink);
3244 xvimagesink->contrast = g_value_get_int (value);
3245 xvimagesink->cb_changed = TRUE;
3246 gst_xvimagesink_update_colorbalance (xvimagesink);
3248 case PROP_BRIGHTNESS:
3249 xvimagesink->brightness = g_value_get_int (value);
3250 xvimagesink->cb_changed = TRUE;
3251 gst_xvimagesink_update_colorbalance (xvimagesink);
3253 case PROP_SATURATION:
3254 xvimagesink->saturation = g_value_get_int (value);
3255 xvimagesink->cb_changed = TRUE;
3256 gst_xvimagesink_update_colorbalance (xvimagesink);
3259 xvimagesink->display_name = g_strdup (g_value_get_string (value));
3261 case PROP_SYNCHRONOUS:
3262 xvimagesink->synchronous = g_value_get_boolean (value);
3263 if (xvimagesink->xcontext) {
3264 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
3265 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
3266 xvimagesink->synchronous ? "TRUE" : "FALSE");
3269 case PROP_PIXEL_ASPECT_RATIO:
3270 g_free (xvimagesink->par);
3271 xvimagesink->par = g_new0 (GValue, 1);
3272 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
3273 if (!g_value_transform (value, xvimagesink->par)) {
3274 g_warning ("Could not transform string to aspect ratio");
3275 gst_value_set_fraction (xvimagesink->par, 1, 1);
3277 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
3278 gst_value_get_fraction_numerator (xvimagesink->par),
3279 gst_value_get_fraction_denominator (xvimagesink->par));
3281 case PROP_FORCE_ASPECT_RATIO:
3282 xvimagesink->keep_aspect = g_value_get_boolean (value);
3284 case PROP_HANDLE_EVENTS:
3285 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
3286 g_value_get_boolean (value));
3287 gst_xvimagesink_manage_event_thread (xvimagesink);
3290 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
3292 case PROP_HANDLE_EXPOSE:
3293 xvimagesink->handle_expose = g_value_get_boolean (value);
3294 gst_xvimagesink_manage_event_thread (xvimagesink);
3296 case PROP_DOUBLE_BUFFER:
3297 xvimagesink->double_buffer = g_value_get_boolean (value);
3299 case PROP_AUTOPAINT_COLORKEY:
3300 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
3303 xvimagesink->colorkey = g_value_get_int (value);
3305 case PROP_DRAW_BORDERS:
3306 xvimagesink->draw_borders = g_value_get_boolean (value);
3309 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3315 gst_xvimagesink_get_property (GObject * object, guint prop_id,
3316 GValue * value, GParamSpec * pspec)
3318 GstXvImageSink *xvimagesink;
3320 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3322 xvimagesink = GST_XVIMAGESINK (object);
3326 g_value_set_int (value, xvimagesink->hue);
3329 g_value_set_int (value, xvimagesink->contrast);
3331 case PROP_BRIGHTNESS:
3332 g_value_set_int (value, xvimagesink->brightness);
3334 case PROP_SATURATION:
3335 g_value_set_int (value, xvimagesink->saturation);
3338 g_value_set_string (value, xvimagesink->display_name);
3340 case PROP_SYNCHRONOUS:
3341 g_value_set_boolean (value, xvimagesink->synchronous);
3343 case PROP_PIXEL_ASPECT_RATIO:
3344 if (xvimagesink->par)
3345 g_value_transform (xvimagesink->par, value);
3347 case PROP_FORCE_ASPECT_RATIO:
3348 g_value_set_boolean (value, xvimagesink->keep_aspect);
3350 case PROP_HANDLE_EVENTS:
3351 g_value_set_boolean (value, xvimagesink->handle_events);
3355 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
3357 g_value_set_string (value, adaptor_no_s);
3358 g_free (adaptor_no_s);
3361 case PROP_DEVICE_NAME:
3362 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
3363 g_value_set_string (value,
3364 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
3366 g_value_set_string (value, NULL);
3369 case PROP_HANDLE_EXPOSE:
3370 g_value_set_boolean (value, xvimagesink->handle_expose);
3372 case PROP_DOUBLE_BUFFER:
3373 g_value_set_boolean (value, xvimagesink->double_buffer);
3375 case PROP_AUTOPAINT_COLORKEY:
3376 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
3379 g_value_set_int (value, xvimagesink->colorkey);
3381 case PROP_DRAW_BORDERS:
3382 g_value_set_boolean (value, xvimagesink->draw_borders);
3384 case PROP_WINDOW_WIDTH:
3385 if (xvimagesink->xwindow)
3386 g_value_set_uint64 (value, xvimagesink->xwindow->width);
3388 g_value_set_uint64 (value, 0);
3390 case PROP_WINDOW_HEIGHT:
3391 if (xvimagesink->xwindow)
3392 g_value_set_uint64 (value, xvimagesink->xwindow->height);
3394 g_value_set_uint64 (value, 0);
3397 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3403 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
3407 GST_OBJECT_LOCK (xvimagesink);
3408 xvimagesink->running = FALSE;
3409 /* grab thread and mark it as NULL */
3410 thread = xvimagesink->event_thread;
3411 xvimagesink->event_thread = NULL;
3412 GST_OBJECT_UNLOCK (xvimagesink);
3414 /* invalidate the pool, current allocations continue, new buffer_alloc fails
3415 * with wrong_state */
3416 g_mutex_lock (xvimagesink->pool_lock);
3417 xvimagesink->pool_invalid = TRUE;
3418 g_mutex_unlock (xvimagesink->pool_lock);
3420 /* Wait for our event thread to finish before we clean up our stuff. */
3422 g_thread_join (thread);
3424 if (xvimagesink->cur_image) {
3425 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
3426 xvimagesink->cur_image = NULL;
3428 if (xvimagesink->xvimage) {
3429 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->xvimage));
3430 xvimagesink->xvimage = NULL;
3433 gst_xvimagesink_imagepool_clear (xvimagesink);
3435 if (xvimagesink->xwindow) {
3436 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
3437 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3438 xvimagesink->xwindow = NULL;
3441 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
3442 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
3443 xvimagesink->have_render_rect = FALSE;
3445 gst_xvimagesink_xcontext_clear (xvimagesink);
3448 /* Finalize is called only once, dispose can be called multiple times.
3449 * We use mutexes and don't reset stuff to NULL here so let's register
3452 gst_xvimagesink_finalize (GObject * object)
3454 GstXvImageSink *xvimagesink;
3456 xvimagesink = GST_XVIMAGESINK (object);
3458 gst_xvimagesink_reset (xvimagesink);
3460 if (xvimagesink->display_name) {
3461 g_free (xvimagesink->display_name);
3462 xvimagesink->display_name = NULL;
3465 if (xvimagesink->par) {
3466 g_free (xvimagesink->par);
3467 xvimagesink->par = NULL;
3469 if (xvimagesink->x_lock) {
3470 g_mutex_free (xvimagesink->x_lock);
3471 xvimagesink->x_lock = NULL;
3473 if (xvimagesink->flow_lock) {
3474 g_mutex_free (xvimagesink->flow_lock);
3475 xvimagesink->flow_lock = NULL;
3477 if (xvimagesink->pool_lock) {
3478 g_mutex_free (xvimagesink->pool_lock);
3479 xvimagesink->pool_lock = NULL;
3482 g_free (xvimagesink->media_title);
3484 G_OBJECT_CLASS (parent_class)->finalize (object);
3488 gst_xvimagesink_init (GstXvImageSink * xvimagesink,
3489 GstXvImageSinkClass * xvimagesinkclass)
3491 xvimagesink->display_name = NULL;
3492 xvimagesink->adaptor_no = 0;
3493 xvimagesink->xcontext = NULL;
3494 xvimagesink->xwindow = NULL;
3495 xvimagesink->xvimage = NULL;
3496 xvimagesink->cur_image = NULL;
3498 xvimagesink->hue = xvimagesink->saturation = 0;
3499 xvimagesink->contrast = xvimagesink->brightness = 0;
3500 xvimagesink->cb_changed = FALSE;
3502 xvimagesink->fps_n = 0;
3503 xvimagesink->fps_d = 0;
3504 xvimagesink->video_width = 0;
3505 xvimagesink->video_height = 0;
3507 xvimagesink->x_lock = g_mutex_new ();
3508 xvimagesink->flow_lock = g_mutex_new ();
3510 xvimagesink->image_pool = NULL;
3511 xvimagesink->pool_lock = g_mutex_new ();
3513 xvimagesink->synchronous = FALSE;
3514 xvimagesink->double_buffer = TRUE;
3515 xvimagesink->running = FALSE;
3516 xvimagesink->keep_aspect = FALSE;
3517 xvimagesink->handle_events = TRUE;
3518 xvimagesink->par = NULL;
3519 xvimagesink->handle_expose = TRUE;
3520 xvimagesink->autopaint_colorkey = TRUE;
3522 /* on 16bit displays this becomes r,g,b = 1,2,3
3523 * on 24bit displays this becomes r,g,b = 8,8,16
3524 * as a port atom value
3526 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
3527 xvimagesink->draw_borders = TRUE;
3531 gst_xvimagesink_base_init (gpointer g_class)
3533 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
3535 gst_element_class_set_details_simple (element_class,
3536 "Video sink", "Sink/Video",
3537 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
3539 gst_element_class_add_static_pad_template (element_class,
3540 &gst_xvimagesink_sink_template_factory);
3544 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
3546 GObjectClass *gobject_class;
3547 GstElementClass *gstelement_class;
3548 GstBaseSinkClass *gstbasesink_class;
3549 GstVideoSinkClass *videosink_class;
3551 gobject_class = (GObjectClass *) klass;
3552 gstelement_class = (GstElementClass *) klass;
3553 gstbasesink_class = (GstBaseSinkClass *) klass;
3554 videosink_class = (GstVideoSinkClass *) klass;
3556 gobject_class->set_property = gst_xvimagesink_set_property;
3557 gobject_class->get_property = gst_xvimagesink_get_property;
3559 g_object_class_install_property (gobject_class, PROP_CONTRAST,
3560 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
3561 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3562 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
3563 g_param_spec_int ("brightness", "Brightness",
3564 "The brightness of the video", -1000, 1000, 0,
3565 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3566 g_object_class_install_property (gobject_class, PROP_HUE,
3567 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
3568 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3569 g_object_class_install_property (gobject_class, PROP_SATURATION,
3570 g_param_spec_int ("saturation", "Saturation",
3571 "The saturation of the video", -1000, 1000, 0,
3572 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3573 g_object_class_install_property (gobject_class, PROP_DISPLAY,
3574 g_param_spec_string ("display", "Display", "X Display name", NULL,
3575 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3576 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
3577 g_param_spec_boolean ("synchronous", "Synchronous",
3578 "When enabled, runs the X display in synchronous mode. "
3579 "(unrelated to A/V sync, used only for debugging)", FALSE,
3580 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3581 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
3582 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
3583 "The pixel aspect ratio of the device", "1/1",
3584 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3585 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
3586 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
3587 "When enabled, scaling will respect original aspect ratio", FALSE,
3588 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3589 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
3590 g_param_spec_boolean ("handle-events", "Handle XEvents",
3591 "When enabled, XEvents will be selected and handled", TRUE,
3592 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3593 g_object_class_install_property (gobject_class, PROP_DEVICE,
3594 g_param_spec_string ("device", "Adaptor number",
3595 "The number of the video adaptor", "0",
3596 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3597 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
3598 g_param_spec_string ("device-name", "Adaptor name",
3599 "The name of the video adaptor", NULL,
3600 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3602 * GstXvImageSink:handle-expose
3604 * When enabled, the current frame will always be drawn in response to X
3609 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
3610 g_param_spec_boolean ("handle-expose", "Handle expose",
3612 "the current frame will always be drawn in response to X Expose "
3613 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3616 * GstXvImageSink:double-buffer
3618 * Whether to double-buffer the output.
3622 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
3623 g_param_spec_boolean ("double-buffer", "Double-buffer",
3624 "Whether to double-buffer the output", TRUE,
3625 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3627 * GstXvImageSink:autopaint-colorkey
3629 * Whether to autofill overlay with colorkey
3633 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
3634 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
3635 "Whether to autofill overlay with colorkey", TRUE,
3636 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3638 * GstXvImageSink:colorkey
3640 * Color to use for the overlay mask.
3644 g_object_class_install_property (gobject_class, PROP_COLORKEY,
3645 g_param_spec_int ("colorkey", "Colorkey",
3646 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
3647 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3650 * GstXvImageSink:draw-borders
3652 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
3653 * unused parts of the video area.
3657 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
3658 g_param_spec_boolean ("draw-borders", "Colorkey",
3659 "Draw black borders to fill unused area in force-aspect-ratio mode",
3660 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3663 * GstXvImageSink:window-width
3665 * Actual width of the video window.
3669 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
3670 g_param_spec_uint64 ("window-width", "window-width",
3671 "Width of the window", 0, G_MAXUINT64, 0,
3672 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3675 * GstXvImageSink:window-height
3677 * Actual height of the video window.
3681 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
3682 g_param_spec_uint64 ("window-height", "window-height",
3683 "Height of the window", 0, G_MAXUINT64, 0,
3684 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3686 gobject_class->finalize = gst_xvimagesink_finalize;
3688 gstelement_class->change_state =
3689 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3691 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3692 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3693 gstbasesink_class->buffer_alloc =
3694 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3695 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3696 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3698 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3701 /* ============================================================= */
3703 /* Public Methods */
3705 /* ============================================================= */
3707 /* =========================================== */
3709 /* Object typing & Creation */
3711 /* =========================================== */
3713 gst_xvimagesink_init_interfaces (GType type)
3715 static const GInterfaceInfo iface_info = {
3716 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3720 static const GInterfaceInfo navigation_info = {
3721 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3725 static const GInterfaceInfo overlay_info = {
3726 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3730 static const GInterfaceInfo colorbalance_info = {
3731 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3735 static const GInterfaceInfo propertyprobe_info = {
3736 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3741 g_type_add_interface_static (type,
3742 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3743 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
3744 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &overlay_info);
3745 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE,
3746 &colorbalance_info);
3747 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
3748 &propertyprobe_info);
3750 /* register type and create class in a more safe place instead of at
3751 * runtime since the type registration and class creation is not
3753 g_type_class_ref (gst_xvimage_buffer_get_type ());
3757 plugin_init (GstPlugin * plugin)
3759 if (!gst_element_register (plugin, "xvimagesink",
3760 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
3763 GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
3764 "xvimagesink element");
3765 GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
3770 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3773 "XFree86 video output plugin using Xv extension",
3774 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)