2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-xvimagesink
23 * XvImageSink renders video frames to a drawable (XWindow) on a local display
24 * using the XVideo extension. Rendering to a remote display is theorically
25 * possible but i doubt that the XVideo extension is actually available when
26 * connecting to a remote display. This element can receive a Window ID from the
27 * application through the XOverlay interface and will then render video frames
28 * in this drawable. If no Window ID was provided by the application, the
29 * element will create its own internal window and render into it.
32 * <title>Scaling</title>
34 * The XVideo extension, when it's available, handles hardware accelerated
35 * scaling of video frames. This means that the element will just accept
36 * incoming video frames no matter their geometry and will then put them to the
37 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
38 * property it is possible to enforce scaling with a constant aspect ratio,
39 * which means drawing black borders around the video frame.
43 * <title>Events</title>
45 * XvImageSink creates a thread to handle events coming from the drawable. There
46 * are several kind of events that can be grouped in 2 big categories: input
47 * events and window state related events. Input events will be translated to
48 * navigation events and pushed upstream for other elements to react on them.
49 * This includes events such as pointer moves, key press/release, clicks etc...
50 * Other events are used to handle the drawable appearance even when the data
51 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
52 * paused, it will receive expose events from the drawable and draw the latest
53 * frame with correct borders/aspect-ratio.
57 * <title>Pixel aspect ratio</title>
59 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
60 * the display specified in the #GstXvImageSink:display property or the
61 * default display if nothing specified. Once this connection is open it will
62 * inspect the display configuration including the physical display geometry and
63 * then calculate the pixel aspect ratio. When receiving video frames with a
64 * different pixel aspect ratio, XvImageSink will use hardware scaling to
65 * display the video frames correctly on display's pixel aspect ratio.
66 * Sometimes the calculated pixel aspect ratio can be wrong, it is
67 * then possible to enforce a specific pixel aspect ratio using the
68 * #GstXvImageSink:pixel-aspect-ratio property.
72 * <title>Examples</title>
74 * gst-launch -v videotestsrc ! xvimagesink
75 * ]| A pipeline to test hardware scaling.
76 * When the test video signal appears you can resize the window and see that
77 * video frames are scaled through hardware (no extra CPU cost).
79 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
80 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
81 * You can observe the borders drawn around the scaled image respecting aspect
84 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
85 * ]| A pipeline to test navigation events.
86 * While moving the mouse pointer over the test signal you will see a black box
87 * following the mouse pointer. If you press the mouse button somewhere on the
88 * video and release it somewhere else a green box will appear where you pressed
89 * the button and a red one where you released it. (The navigationtest element
90 * is part of gst-plugins-good.) You can observe here that even if the images
91 * are scaled through hardware the pointer coordinates are converted back to the
92 * original video frame geometry so that the box can be drawn to the correct
93 * position. This also handles borders correctly, limiting coordinates to the
96 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
97 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
98 * videotestsrc, in most cases the pixel aspect ratio of the display will be
99 * 1/1. This means that XvImageSink will have to do the scaling to convert
100 * incoming frames to a size that will match the display pixel aspect ratio
101 * (from 320x240 to 320x180 in this case). Note that you might have to escape
102 * some characters for your shell like '\(fraction\)'.
104 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
105 * ]| Demonstrates how to use the colorbalance interface.
109 /* for developers: there are two useful tools : xvinfo and xvattr */
116 #include <gst/interfaces/navigation.h>
117 #include <gst/interfaces/xoverlay.h>
118 #include <gst/interfaces/colorbalance.h>
119 #include <gst/interfaces/propertyprobe.h>
120 /* Helper functions */
121 #include <gst/video/video.h>
124 #include "xvimagesink.h"
126 /* Debugging category */
127 #include <gst/gstinfo.h>
128 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
129 #define GST_CAT_DEFAULT gst_debug_xvimagesink
134 unsigned long functions;
135 unsigned long decorations;
137 unsigned long status;
139 MotifWmHints, MwmHints;
141 #define MWM_HINTS_DECORATIONS (1L << 1)
143 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
145 static GstBufferClass *xvimage_buffer_parent_class = NULL;
146 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
148 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
149 xvimagesink, GstXWindow * xwindow);
150 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
152 static void gst_xvimagesink_expose (GstXOverlay * overlay);
154 /* ElementFactory information */
155 static const GstElementDetails gst_xvimagesink_details =
156 GST_ELEMENT_DETAILS ("Video sink",
158 "A Xv based videosink",
159 "Julien Moutte <julien@moutte.net>");
161 /* Default template - initiated with class struct to allow gst-register to work
163 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
164 GST_STATIC_PAD_TEMPLATE ("sink",
167 GST_STATIC_CAPS ("video/x-raw-rgb, "
168 "framerate = (fraction) [ 0, MAX ], "
169 "width = (int) [ 1, MAX ], "
170 "height = (int) [ 1, MAX ]; "
172 "framerate = (fraction) [ 0, MAX ], "
173 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
185 ARG_PIXEL_ASPECT_RATIO,
186 ARG_FORCE_ASPECT_RATIO,
194 static GstVideoSinkClass *parent_class = NULL;
196 /* ============================================================= */
198 /* Private Methods */
200 /* ============================================================= */
202 /* xvimage buffers */
204 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
206 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
207 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
209 /* This function destroys a GstXvImage handling XShm availability */
211 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
213 GstXvImageSink *xvimagesink;
215 GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
217 xvimagesink = xvimage->xvimagesink;
218 if (G_UNLIKELY (xvimagesink == NULL))
221 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
223 GST_OBJECT_LOCK (xvimagesink);
225 /* If the destroyed image is the current one we destroy our reference too */
226 if (xvimagesink->cur_image == xvimage)
227 xvimagesink->cur_image = NULL;
229 /* We might have some buffers destroyed after changing state to NULL */
230 if (xvimagesink->xcontext == NULL) {
231 GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
233 /* Need to free the shared memory segment even if the x context
234 * was already cleaned up */
235 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
236 shmdt (xvimage->SHMInfo.shmaddr);
242 g_mutex_lock (xvimagesink->x_lock);
245 if (xvimagesink->xcontext->use_xshm) {
246 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
247 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
248 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
249 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
250 XSync (xvimagesink->xcontext->disp, FALSE);
252 shmdt (xvimage->SHMInfo.shmaddr);
254 if (xvimage->xvimage)
255 XFree (xvimage->xvimage);
257 #endif /* HAVE_XSHM */
259 if (xvimage->xvimage) {
260 if (xvimage->xvimage->data) {
261 g_free (xvimage->xvimage->data);
263 XFree (xvimage->xvimage);
267 XSync (xvimagesink->xcontext->disp, FALSE);
269 g_mutex_unlock (xvimagesink->x_lock);
272 GST_OBJECT_UNLOCK (xvimagesink);
273 xvimage->xvimagesink = NULL;
274 gst_object_unref (xvimagesink);
276 GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->
277 finalize (GST_MINI_OBJECT (xvimage));
283 GST_WARNING ("no sink found");
289 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
291 GstXvImageSink *xvimagesink;
294 xvimagesink = xvimage->xvimagesink;
295 if (G_UNLIKELY (xvimagesink == NULL))
298 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
300 GST_OBJECT_LOCK (xvimagesink);
301 running = xvimagesink->running;
302 GST_OBJECT_UNLOCK (xvimagesink);
304 /* If our geometry changed we can't reuse that image. */
305 if (running == FALSE) {
306 GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
307 gst_xvimage_buffer_destroy (xvimage);
308 } else if ((xvimage->width != xvimagesink->video_width) ||
309 (xvimage->height != xvimagesink->video_height)) {
310 GST_LOG_OBJECT (xvimage,
311 "destroy image as its size changed %dx%d vs current %dx%d",
312 xvimage->width, xvimage->height,
313 xvimagesink->video_width, xvimagesink->video_height);
314 gst_xvimage_buffer_destroy (xvimage);
316 /* In that case we can reuse the image and add it to our image pool. */
317 GST_LOG_OBJECT (xvimage, "recycling image in pool");
318 /* need to increment the refcount again to recycle */
319 gst_buffer_ref (GST_BUFFER (xvimage));
320 g_mutex_lock (xvimagesink->pool_lock);
321 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
323 g_mutex_unlock (xvimagesink->pool_lock);
329 GST_WARNING ("no sink found");
335 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
337 /* make sure it is not recycled */
339 xvimage->height = -1;
340 gst_buffer_unref (GST_BUFFER (xvimage));
344 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
347 xvimage->SHMInfo.shmaddr = ((void *) -1);
348 xvimage->SHMInfo.shmid = -1;
353 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
355 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
357 xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
359 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
360 gst_xvimage_buffer_finalize;
364 gst_xvimage_buffer_get_type (void)
366 static GType _gst_xvimage_buffer_type;
368 if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
369 static const GTypeInfo xvimage_buffer_info = {
370 sizeof (GstBufferClass),
373 gst_xvimage_buffer_class_init,
376 sizeof (GstXvImageBuffer),
378 (GInstanceInitFunc) gst_xvimage_buffer_init,
381 _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
382 "GstXvImageBuffer", &xvimage_buffer_info, 0);
384 return _gst_xvimage_buffer_type;
389 static gboolean error_caught = FALSE;
392 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
394 char error_msg[1024];
396 XGetErrorText (display, xevent->error_code, error_msg, 1024);
397 GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
403 /* This function checks that it is actually really possible to create an image
406 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
409 XShmSegmentInfo SHMInfo;
411 int (*handler) (Display *, XErrorEvent *);
412 gboolean result = FALSE;
413 gboolean did_attach = FALSE;
415 g_return_val_if_fail (xcontext != NULL, FALSE);
417 /* Sync to ensure any older errors are already processed */
418 XSync (xcontext->disp, FALSE);
420 /* Set defaults so we don't free these later unnecessarily */
421 SHMInfo.shmaddr = ((void *) -1);
424 /* Setting an error handler to catch failure */
425 error_caught = FALSE;
426 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
428 /* Trying to create a 1x1 picture */
429 GST_DEBUG ("XvShmCreateImage of 1x1");
430 xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
431 xcontext->im_format, NULL, 1, 1, &SHMInfo);
433 /* Might cause an error, sync to ensure it is noticed */
434 XSync (xcontext->disp, FALSE);
435 if (!xvimage || error_caught) {
436 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
439 size = xvimage->data_size;
441 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
442 if (SHMInfo.shmid == -1) {
443 GST_WARNING ("could not get shared memory of %d bytes", size);
447 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
448 if (SHMInfo.shmaddr == ((void *) -1)) {
449 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
450 /* Clean up the shared memory segment */
451 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
455 xvimage->data = SHMInfo.shmaddr;
456 SHMInfo.readOnly = FALSE;
458 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
459 GST_WARNING ("Failed to XShmAttach");
460 /* Clean up the shared memory segment */
461 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
465 /* Sync to ensure we see any errors we caused */
466 XSync (xcontext->disp, FALSE);
468 /* Delete the shared memory segment as soon as everyone is attached.
469 * This way, it will be deleted as soon as we detach later, and not
470 * leaked if we crash. */
471 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
474 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
478 /* store whether we succeeded in result */
481 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
482 "Not using shared memory.");
486 /* Sync to ensure we swallow any errors we caused and reset error_caught */
487 XSync (xcontext->disp, FALSE);
489 error_caught = FALSE;
490 XSetErrorHandler (handler);
493 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
494 SHMInfo.shmid, SHMInfo.shmseg);
495 XShmDetach (xcontext->disp, &SHMInfo);
496 XSync (xcontext->disp, FALSE);
498 if (SHMInfo.shmaddr != ((void *) -1))
499 shmdt (SHMInfo.shmaddr);
504 #endif /* HAVE_XSHM */
506 /* This function handles GstXvImage creation depending on XShm availability */
507 static GstXvImageBuffer *
508 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
510 GstXvImageBuffer *xvimage = NULL;
511 GstStructure *structure = NULL;
512 gboolean succeeded = FALSE;
513 int (*handler) (Display *, XErrorEvent *);
515 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
517 xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
518 GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
520 structure = gst_caps_get_structure (caps, 0);
522 if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
523 !gst_structure_get_int (structure, "height", &xvimage->height)) {
524 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
527 GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
530 xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
531 if (xvimage->im_format == -1) {
532 GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
533 GST_PTR_FORMAT, caps);
534 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
535 ("Failed to create output image buffer of %dx%d pixels",
536 xvimage->width, xvimage->height), ("Invalid input caps"));
539 xvimage->xvimagesink = gst_object_ref (xvimagesink);
541 g_mutex_lock (xvimagesink->x_lock);
543 /* Setting an error handler to catch failure */
544 error_caught = FALSE;
545 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
548 if (xvimagesink->xcontext->use_xshm) {
551 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
552 xvimagesink->xcontext->xv_port_id,
553 xvimage->im_format, NULL,
554 xvimage->width, xvimage->height, &xvimage->SHMInfo);
555 if (!xvimage->xvimage || error_caught) {
556 g_mutex_unlock (xvimagesink->x_lock);
557 /* Reset error handler */
558 error_caught = FALSE;
559 XSetErrorHandler (handler);
561 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
562 ("Failed to create output image buffer of %dx%d pixels",
563 xvimage->width, xvimage->height),
564 ("could not XvShmCreateImage a %dx%d image",
565 xvimage->width, xvimage->height));
569 /* we have to use the returned data_size for our shm size */
570 xvimage->size = xvimage->xvimage->data_size;
571 GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
574 /* calculate the expected size. This is only for sanity checking the
575 * number we get from X. */
576 switch (xvimage->im_format) {
577 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
578 case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
580 GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_4 (xvimage->width);
582 GST_ROUND_UP_2 (xvimage->height) * GST_ROUND_UP_8 (xvimage->width) /
585 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
586 case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
587 expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
593 if (expected_size != 0 && xvimage->size != expected_size) {
594 GST_WARNING_OBJECT (xvimagesink,
595 "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
596 xvimage->size, expected_size);
599 /* Be verbose about our XvImage stride */
603 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
604 GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
605 "offset of %d", plane, xvimage->xvimage->pitches[plane],
606 xvimage->xvimage->offsets[plane]);
610 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
612 if (xvimage->SHMInfo.shmid == -1) {
613 g_mutex_unlock (xvimagesink->x_lock);
614 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
615 ("Failed to create output image buffer of %dx%d pixels",
616 xvimage->width, xvimage->height),
617 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
622 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
623 if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
624 g_mutex_unlock (xvimagesink->x_lock);
625 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
626 ("Failed to create output image buffer of %dx%d pixels",
627 xvimage->width, xvimage->height),
628 ("Failed to shmat: %s", g_strerror (errno)));
629 /* Clean up the shared memory segment */
630 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
634 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
635 xvimage->SHMInfo.readOnly = FALSE;
637 if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
638 /* Clean up the shared memory segment */
639 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
641 g_mutex_unlock (xvimagesink->x_lock);
642 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
643 ("Failed to create output image buffer of %dx%d pixels",
644 xvimage->width, xvimage->height), ("Failed to XShmAttach"));
648 XSync (xvimagesink->xcontext->disp, FALSE);
650 /* Delete the shared memory segment as soon as we everyone is attached.
651 * This way, it will be deleted as soon as we detach later, and not
652 * leaked if we crash. */
653 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
655 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
656 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
658 #endif /* HAVE_XSHM */
660 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
661 xvimagesink->xcontext->xv_port_id,
662 xvimage->im_format, NULL, xvimage->width, xvimage->height);
663 if (!xvimage->xvimage || error_caught) {
664 g_mutex_unlock (xvimagesink->x_lock);
665 /* Reset error handler */
666 error_caught = FALSE;
667 XSetErrorHandler (handler);
669 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
670 ("Failed to create outputimage buffer of %dx%d pixels",
671 xvimage->width, xvimage->height),
672 ("could not XvCreateImage a %dx%d image",
673 xvimage->width, xvimage->height));
677 /* we have to use the returned data_size for our image size */
678 xvimage->size = xvimage->xvimage->data_size;
679 xvimage->xvimage->data = g_malloc (xvimage->size);
681 XSync (xvimagesink->xcontext->disp, FALSE);
684 /* Reset error handler */
685 error_caught = FALSE;
686 XSetErrorHandler (handler);
690 GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
691 GST_BUFFER_SIZE (xvimage) = xvimage->size;
693 g_mutex_unlock (xvimagesink->x_lock);
697 gst_xvimage_buffer_free (xvimage);
704 /* We are called with the x_lock taken */
706 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
707 GstXWindow * xwindow, GstVideoRectangle rect)
709 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
710 g_return_if_fail (xwindow != NULL);
712 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
713 xvimagesink->xcontext->black);
717 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
718 0, 0, rect.x, xwindow->height);
722 if ((rect.x + rect.w) < xwindow->width) {
723 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
724 rect.x + rect.w, 0, xwindow->width, xwindow->height);
729 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
730 0, 0, xwindow->width, rect.y);
734 if ((rect.y + rect.h) < xwindow->height) {
735 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
736 0, rect.y + rect.h, xwindow->width, xwindow->height);
740 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
741 * if no window was available */
743 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
744 GstXvImageBuffer * xvimage)
746 GstVideoRectangle src, dst, result;
747 gboolean draw_border = FALSE;
749 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
751 /* We take the flow_lock. If expose is in there we don't want to run
752 concurrently from the data flow thread */
753 g_mutex_lock (xvimagesink->flow_lock);
755 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
756 g_mutex_unlock (xvimagesink->flow_lock);
760 /* Draw borders when displaying the first frame. After this
761 draw borders only on expose event or after a size change. */
762 if (!xvimagesink->cur_image || xvimagesink->draw_border) {
766 /* Store a reference to the last image we put, lose the previous one */
767 if (xvimage && xvimagesink->cur_image != xvimage) {
768 if (xvimagesink->cur_image) {
769 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
770 gst_buffer_unref (xvimagesink->cur_image);
772 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
773 xvimagesink->cur_image =
774 GST_XVIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER (xvimage)));
777 /* Expose sends a NULL image, we take the latest frame */
780 if (xvimagesink->cur_image) {
781 xvimage = xvimagesink->cur_image;
783 g_mutex_unlock (xvimagesink->flow_lock);
788 gst_xvimagesink_xwindow_update_geometry (xvimagesink, xvimagesink->xwindow);
790 /* We use the calculated geometry from _setcaps as a source to respect
791 source and screen pixel aspect ratios. */
792 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
793 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
794 dst.w = xvimagesink->xwindow->width;
795 dst.h = xvimagesink->xwindow->height;
797 if (xvimagesink->keep_aspect) {
798 gst_video_sink_center_rect (src, dst, &result, TRUE);
800 result.x = result.y = 0;
805 g_mutex_lock (xvimagesink->x_lock);
808 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
810 xvimagesink->draw_border = FALSE;
813 /* We scale to the window's geometry */
815 if (xvimagesink->xcontext->use_xshm) {
816 GST_LOG_OBJECT (xvimagesink,
817 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
819 xvimage->width, xvimage->height,
820 xvimagesink->xwindow->width, xvimagesink->xwindow->height, xvimage);
822 XvShmPutImage (xvimagesink->xcontext->disp,
823 xvimagesink->xcontext->xv_port_id,
824 xvimagesink->xwindow->win,
825 xvimagesink->xwindow->gc, xvimage->xvimage,
826 0, 0, xvimage->width, xvimage->height,
827 result.x, result.y, result.w, result.h, FALSE);
829 #endif /* HAVE_XSHM */
831 XvPutImage (xvimagesink->xcontext->disp,
832 xvimagesink->xcontext->xv_port_id,
833 xvimagesink->xwindow->win,
834 xvimagesink->xwindow->gc, xvimage->xvimage,
835 0, 0, xvimage->width, xvimage->height,
836 result.x, result.y, result.w, result.h);
839 XSync (xvimagesink->xcontext->disp, FALSE);
841 g_mutex_unlock (xvimagesink->x_lock);
843 g_mutex_unlock (xvimagesink->flow_lock);
849 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
852 Atom hints_atom = None;
855 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
856 g_return_val_if_fail (window != NULL, FALSE);
858 g_mutex_lock (xvimagesink->x_lock);
860 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
862 if (hints_atom == None) {
863 g_mutex_unlock (xvimagesink->x_lock);
867 hints = g_malloc0 (sizeof (MotifWmHints));
869 hints->flags |= MWM_HINTS_DECORATIONS;
870 hints->decorations = 1 << 0;
872 XChangeProperty (xvimagesink->xcontext->disp, window->win,
873 hints_atom, hints_atom, 32, PropModeReplace,
874 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
876 XSync (xvimagesink->xcontext->disp, FALSE);
878 g_mutex_unlock (xvimagesink->x_lock);
885 /* This function handles a GstXWindow creation
886 * The width and height are the actual pixel size on the display */
888 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
889 gint width, gint height)
891 GstXWindow *xwindow = NULL;
894 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
896 xwindow = g_new0 (GstXWindow, 1);
898 xwindow->width = width;
899 xwindow->height = height;
900 xwindow->internal = TRUE;
902 g_mutex_lock (xvimagesink->x_lock);
904 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
905 xvimagesink->xcontext->root,
906 0, 0, xwindow->width, xwindow->height,
907 0, 0, xvimagesink->xcontext->black);
909 /* We have to do that to prevent X from redrawing the background on
910 * ConfigureNotify. This takes away flickering of video when resizing. */
911 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
913 if (xvimagesink->handle_events) {
916 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
917 StructureNotifyMask | PointerMotionMask | KeyPressMask |
918 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
920 /* Tell the window manager we'd like delete client messages instead of
922 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
923 "WM_DELETE_WINDOW", True);
924 if (wm_delete != None) {
925 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
930 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
931 xwindow->win, 0, &values);
933 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
935 XSync (xvimagesink->xcontext->disp, FALSE);
937 g_mutex_unlock (xvimagesink->x_lock);
939 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
941 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (xvimagesink), xwindow->win);
946 /* This function destroys a GstXWindow */
948 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
949 GstXWindow * xwindow)
951 g_return_if_fail (xwindow != NULL);
952 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
954 g_mutex_lock (xvimagesink->x_lock);
956 /* If we did not create that window we just free the GC and let it live */
957 if (xwindow->internal)
958 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
960 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
962 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
964 XSync (xvimagesink->xcontext->disp, FALSE);
966 g_mutex_unlock (xvimagesink->x_lock);
972 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink,
973 GstXWindow * xwindow)
975 XWindowAttributes attr;
977 g_return_if_fail (xwindow != NULL);
978 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
980 /* Update the window geometry */
981 g_mutex_lock (xvimagesink->x_lock);
983 XGetWindowAttributes (xvimagesink->xcontext->disp,
984 xvimagesink->xwindow->win, &attr);
986 xvimagesink->xwindow->width = attr.width;
987 xvimagesink->xwindow->height = attr.height;
989 g_mutex_unlock (xvimagesink->x_lock);
993 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
994 GstXWindow * xwindow)
996 g_return_if_fail (xwindow != NULL);
997 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
999 g_mutex_lock (xvimagesink->x_lock);
1001 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1004 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
1005 xvimagesink->xcontext->black);
1007 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
1008 0, 0, xwindow->width, xwindow->height);
1010 XSync (xvimagesink->xcontext->disp, FALSE);
1012 g_mutex_unlock (xvimagesink->x_lock);
1015 /* This function commits our internal colorbalance settings to our grabbed Xv
1016 port. If the xcontext is not initialized yet it simply returns */
1018 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1020 GList *channels = NULL;
1022 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1024 /* If we haven't initialized the X context we can't update anything */
1025 if (xvimagesink->xcontext == NULL)
1028 /* Don't set the attributes if they haven't been changed, to avoid
1029 * rounding errors changing the values */
1030 if (!xvimagesink->cb_changed)
1033 /* For each channel of the colorbalance we calculate the correct value
1034 doing range conversion and then set the Xv port attribute to match our
1036 channels = xvimagesink->xcontext->channels_list;
1039 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1040 GstColorBalanceChannel *channel = NULL;
1043 gdouble convert_coef;
1045 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1046 g_object_ref (channel);
1048 /* Our range conversion coef */
1049 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1051 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1052 value = (xvimagesink->hue + 1000) * convert_coef + channel->min_value;
1053 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1054 value = (xvimagesink->saturation + 1000) * convert_coef +
1056 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1057 value = (xvimagesink->contrast + 1000) * convert_coef +
1059 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1060 value = (xvimagesink->brightness + 1000) * convert_coef +
1063 g_warning ("got an unknown channel %s", channel->label);
1064 g_object_unref (channel);
1068 /* Committing to Xv port */
1069 g_mutex_lock (xvimagesink->x_lock);
1071 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1072 if (prop_atom != None) {
1073 XvSetPortAttribute (xvimagesink->xcontext->disp,
1074 xvimagesink->xcontext->xv_port_id, prop_atom, value);
1076 g_mutex_unlock (xvimagesink->x_lock);
1078 g_object_unref (channel);
1080 channels = g_list_next (channels);
1084 /* This function handles XEvents that might be in the queue. It generates
1085 GstEvent that will be sent upstream in the pipeline to handle interactivity
1086 and navigation. It will also listen for configure events on the window to
1087 trigger caps renegotiation so on the fly software scaling can work. */
1089 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1092 guint pointer_x = 0, pointer_y = 0;
1093 gboolean pointer_moved = FALSE;
1094 gboolean exposed = FALSE, configured = FALSE;
1096 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1098 /* We get all pointer motion events, only the last position is
1100 g_mutex_lock (xvimagesink->flow_lock);
1101 g_mutex_lock (xvimagesink->x_lock);
1102 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1103 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1104 g_mutex_unlock (xvimagesink->x_lock);
1105 g_mutex_unlock (xvimagesink->flow_lock);
1109 pointer_x = e.xmotion.x;
1110 pointer_y = e.xmotion.y;
1111 pointer_moved = TRUE;
1116 g_mutex_lock (xvimagesink->flow_lock);
1117 g_mutex_lock (xvimagesink->x_lock);
1119 if (pointer_moved) {
1120 g_mutex_unlock (xvimagesink->x_lock);
1121 g_mutex_unlock (xvimagesink->flow_lock);
1123 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1124 pointer_x, pointer_y);
1125 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1126 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1128 g_mutex_lock (xvimagesink->flow_lock);
1129 g_mutex_lock (xvimagesink->x_lock);
1132 /* We get all events on our window to throw them upstream */
1133 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1134 xvimagesink->xwindow->win,
1135 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1139 /* We lock only for the X function call */
1140 g_mutex_unlock (xvimagesink->x_lock);
1141 g_mutex_unlock (xvimagesink->flow_lock);
1145 /* Mouse button pressed over our window. We send upstream
1146 events for interactivity/navigation */
1147 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1148 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1149 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1150 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1153 /* Mouse button released over our window. We send upstream
1154 events for interactivity/navigation */
1155 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1156 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1157 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1158 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1162 /* Key pressed/released over our window. We send upstream
1163 events for interactivity/navigation */
1164 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1165 e.xkey.keycode, e.xkey.x, e.xkey.y);
1166 g_mutex_lock (xvimagesink->x_lock);
1167 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1169 g_mutex_unlock (xvimagesink->x_lock);
1170 if (keysym != NoSymbol) {
1171 char *key_str = NULL;
1173 g_mutex_lock (xvimagesink->x_lock);
1174 key_str = XKeysymToString (keysym);
1175 g_mutex_unlock (xvimagesink->x_lock);
1176 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1177 e.type == KeyPress ? "key-press" : "key-release", key_str);
1179 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1180 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1184 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1186 g_mutex_lock (xvimagesink->flow_lock);
1187 g_mutex_lock (xvimagesink->x_lock);
1191 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1192 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1197 case ConfigureNotify:
1205 if (xvimagesink->handle_expose && (exposed || configured)) {
1206 g_mutex_unlock (xvimagesink->x_lock);
1207 g_mutex_unlock (xvimagesink->flow_lock);
1209 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1211 g_mutex_lock (xvimagesink->flow_lock);
1212 g_mutex_lock (xvimagesink->x_lock);
1215 /* Handle Display events */
1216 while (XPending (xvimagesink->xcontext->disp)) {
1217 XNextEvent (xvimagesink->xcontext->disp, &e);
1220 case ClientMessage:{
1223 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1224 "WM_DELETE_WINDOW", True);
1225 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1226 /* Handle window deletion by posting an error on the bus */
1227 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1228 ("Output window was closed"), (NULL));
1230 g_mutex_unlock (xvimagesink->x_lock);
1231 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1232 xvimagesink->xwindow = NULL;
1233 g_mutex_lock (xvimagesink->x_lock);
1242 g_mutex_unlock (xvimagesink->x_lock);
1243 g_mutex_unlock (xvimagesink->flow_lock);
1247 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1248 XvAdaptorInfo * adaptors, int adaptor_no)
1253 /* Do we support XvImageMask ? */
1254 if (!(adaptors[adaptor_no].type & XvImageMask)) {
1255 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1256 adaptors[adaptor_no].name);
1260 /* We found such an adaptor, looking for an available port */
1261 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1262 /* We try to grab the port */
1263 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1264 if (Success == res) {
1265 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1266 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1267 adaptors[adaptor_no].num_ports);
1269 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1270 adaptors[adaptor_no].name, res);
1275 /* This function generates a caps with all supported format by the first
1276 Xv grabable port we find. We store each one of the supported formats in a
1277 format list and append the format to a newly created caps that we return
1278 If this function does not return NULL because of an error, it also grabs
1279 the port via XvGrabPort */
1281 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1282 GstXContext * xcontext)
1285 XvAdaptorInfo *adaptors;
1287 XvImageFormatValues *formats = NULL;
1289 XvEncodingInfo *encodings = NULL;
1290 gulong max_w = G_MAXINT, max_h = G_MAXINT;
1291 GstCaps *caps = NULL;
1292 GstCaps *rgb_caps = NULL;
1294 g_return_val_if_fail (xcontext != NULL, NULL);
1296 /* First let's check that XVideo extension is available */
1297 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1298 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1299 ("Could not initialise Xv output"),
1300 ("XVideo extension is not available"));
1304 /* Then we get adaptors list */
1305 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1306 &xcontext->nb_adaptors, &adaptors)) {
1307 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1308 ("Could not initialise Xv output"),
1309 ("Failed getting XV adaptors list"));
1313 xcontext->xv_port_id = 0;
1315 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1317 xcontext->adaptors =
1318 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
1320 /* Now fill up our adaptor name array */
1321 for (i = 0; i < xcontext->nb_adaptors; i++) {
1322 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
1325 if (xvimagesink->adaptor_no >= 0 &&
1326 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
1327 /* Find xv port from user defined adaptor */
1328 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
1329 xvimagesink->adaptor_no);
1332 if (!xcontext->xv_port_id) {
1333 /* Now search for an adaptor that supports XvImageMask */
1334 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
1335 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
1336 xvimagesink->adaptor_no = i;
1340 XvFreeAdaptorInfo (adaptors);
1342 if (!xcontext->xv_port_id) {
1343 xvimagesink->adaptor_no = -1;
1344 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
1345 ("Could not initialise Xv output"), ("No port available"));
1349 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1351 int count, todo = 3;
1352 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1353 xcontext->xv_port_id, &count);
1354 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1355 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
1356 static const char colorkey[] = "XV_COLORKEY";
1358 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
1360 for (i = 0; ((i < count) && todo); i++)
1361 if (!strcmp (attr[i].name, autopaint)) {
1362 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
1364 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom, 1);
1366 } else if (!strcmp (attr[i].name, dbl_buffer)) {
1367 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
1369 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1370 (xvimagesink->double_buffer ? 1 : 0));
1372 } else if (!strcmp (attr[i].name, colorkey)) {
1373 /* Set the colorkey to something that is dark but hopefully won't randomly
1374 * appear on the screen elsewhere (ie not black or greys) */
1375 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
1379 gboolean set_attr = TRUE;
1381 /* Count the bits in the colorkey mask 'max' value */
1383 for (keymask = (guint32) (attr[i].max_value);
1384 keymask != 0; keymask >>= 1)
1387 /* set a colorkey in the right format RGB565/RGB888
1388 * note that the colorkey is independent from the display
1389 * depth (xcontext->depth). We only handle these 2 cases, because
1390 * they're the only types of devices we've encountered. If we don't
1391 * recognise it, leave it alone */
1393 ckey = (1 << 11) | (2 << 5) | 3;
1394 else if (bits == 24 || bits == 32)
1395 ckey = (1 << 16) | (2 << 8) | 3;
1400 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1401 (guint32) attr[i].max_value);
1402 GST_LOG_OBJECT (xvimagesink,
1403 "Setting color key for display depth %d to 0x%x",
1404 xcontext->depth, ckey);
1406 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1409 GST_DEBUG_OBJECT (xvimagesink,
1410 "Unknown bit depth %d for Xv Colorkey - not adjusting", bits);
1418 /* Get the list of encodings supported by the adapter and look for the
1419 * XV_IMAGE encoding so we can determine the maximum width and height
1421 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1424 for (i = 0; i < nb_encodings; i++) {
1425 GST_LOG_OBJECT (xvimagesink,
1426 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1427 i, encodings[i].name, encodings[i].width, encodings[i].height,
1428 encodings[i].rate.numerator, encodings[i].rate.denominator);
1429 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1430 max_w = encodings[i].width;
1431 max_h = encodings[i].height;
1435 XvFreeEncodingInfo (encodings);
1437 /* We get all image formats supported by our port */
1438 formats = XvListImageFormats (xcontext->disp,
1439 xcontext->xv_port_id, &nb_formats);
1440 caps = gst_caps_new_empty ();
1441 for (i = 0; i < nb_formats; i++) {
1442 GstCaps *format_caps = NULL;
1443 gboolean is_rgb_format = FALSE;
1445 /* We set the image format of the xcontext to an existing one. This
1446 is just some valid image format for making our xshm calls check before
1447 caps negotiation really happens. */
1448 xcontext->im_format = formats[i].id;
1450 switch (formats[i].type) {
1453 XvImageFormatValues *fmt = &(formats[i]);
1454 gint endianness = G_BIG_ENDIAN;
1456 if (fmt->byte_order == LSBFirst) {
1457 /* our caps system handles 24/32bpp RGB as big-endian. */
1458 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1459 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1460 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1461 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1463 if (fmt->bits_per_pixel == 24) {
1464 fmt->red_mask >>= 8;
1465 fmt->green_mask >>= 8;
1466 fmt->blue_mask >>= 8;
1469 endianness = G_LITTLE_ENDIAN;
1472 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1473 "endianness", G_TYPE_INT, endianness,
1474 "depth", G_TYPE_INT, fmt->depth,
1475 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1476 "red_mask", G_TYPE_INT, fmt->red_mask,
1477 "green_mask", G_TYPE_INT, fmt->green_mask,
1478 "blue_mask", G_TYPE_INT, fmt->blue_mask,
1479 "width", GST_TYPE_INT_RANGE, 1, max_w,
1480 "height", GST_TYPE_INT_RANGE, 1, max_h,
1481 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1483 is_rgb_format = TRUE;
1487 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1488 "format", GST_TYPE_FOURCC, formats[i].id,
1489 "width", GST_TYPE_INT_RANGE, 1, max_w,
1490 "height", GST_TYPE_INT_RANGE, 1, max_h,
1491 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1494 g_assert_not_reached ();
1499 GstXvImageFormat *format = NULL;
1501 format = g_new0 (GstXvImageFormat, 1);
1503 format->format = formats[i].id;
1504 format->caps = gst_caps_copy (format_caps);
1505 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1508 if (is_rgb_format) {
1509 if (rgb_caps == NULL)
1510 rgb_caps = format_caps;
1512 gst_caps_append (rgb_caps, format_caps);
1514 gst_caps_append (caps, format_caps);
1518 /* Collected all caps into either the caps or rgb_caps structures.
1519 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1521 gst_caps_append (caps, rgb_caps);
1526 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1528 if (gst_caps_is_empty (caps)) {
1529 gst_caps_unref (caps);
1530 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1531 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1532 ("No supported format found"));
1540 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1542 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1544 GST_OBJECT_LOCK (xvimagesink);
1545 while (xvimagesink->running) {
1546 GST_OBJECT_UNLOCK (xvimagesink);
1548 if (xvimagesink->xwindow) {
1549 gst_xvimagesink_handle_xevents (xvimagesink);
1553 GST_OBJECT_LOCK (xvimagesink);
1555 GST_OBJECT_UNLOCK (xvimagesink);
1560 /* This function calculates the pixel aspect ratio based on the properties
1561 * in the xcontext structure and stores it there. */
1563 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1565 static const gint par[][2] = {
1566 {1, 1}, /* regular screen */
1567 {16, 15}, /* PAL TV */
1568 {11, 10}, /* 525 line Rec.601 video */
1569 {54, 59}, /* 625 line Rec.601 video */
1570 {64, 45}, /* 1280x1024 on 16:9 display */
1571 {5, 3}, /* 1280x1024 on 4:3 display */
1572 {4, 3} /* 800x600 on 16:9 display */
1579 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1581 /* first calculate the "real" ratio based on the X values;
1582 * which is the "physical" w/h divided by the w/h in pixels of the display */
1583 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1584 / (xcontext->heightmm * xcontext->width);
1586 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1588 if (xcontext->width == 720 && xcontext->height == 576) {
1589 ratio = 4.0 * 576 / (3.0 * 720);
1591 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1592 /* now find the one from par[][2] with the lowest delta to the real one */
1596 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1597 gdouble this_delta = DELTA (i);
1599 if (this_delta < delta) {
1605 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1606 par[index][0], par[index][1]);
1608 g_free (xcontext->par);
1609 xcontext->par = g_new0 (GValue, 1);
1610 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1611 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1612 GST_DEBUG ("set xcontext PAR to %d/%d",
1613 gst_value_get_fraction_numerator (xcontext->par),
1614 gst_value_get_fraction_denominator (xcontext->par));
1617 /* This function gets the X Display and global info about it. Everything is
1618 stored in our object and will be cleaned when the object is disposed. Note
1619 here that caps for supported format are generated without any window or
1621 static GstXContext *
1622 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1624 GstXContext *xcontext = NULL;
1625 XPixmapFormatValues *px_formats = NULL;
1626 gint nb_formats = 0, i, j, N_attr;
1627 XvAttribute *xv_attr;
1629 char *channels[4] = { "XV_HUE", "XV_SATURATION",
1630 "XV_BRIGHTNESS", "XV_CONTRAST"
1633 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1635 xcontext = g_new0 (GstXContext, 1);
1636 xcontext->im_format = 0;
1638 g_mutex_lock (xvimagesink->x_lock);
1640 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1642 if (!xcontext->disp) {
1643 g_mutex_unlock (xvimagesink->x_lock);
1645 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1646 ("Could not initialise Xv output"), ("Could not open display"));
1650 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1651 xcontext->screen_num = DefaultScreen (xcontext->disp);
1652 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1653 xcontext->root = DefaultRootWindow (xcontext->disp);
1654 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1655 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1656 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1658 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1659 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1660 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1661 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1663 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1664 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1666 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1667 /* We get supported pixmap formats at supported depth */
1668 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1671 XCloseDisplay (xcontext->disp);
1672 g_mutex_unlock (xvimagesink->x_lock);
1673 g_free (xcontext->par);
1675 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1676 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1680 /* We get bpp value corresponding to our running depth */
1681 for (i = 0; i < nb_formats; i++) {
1682 if (px_formats[i].depth == xcontext->depth)
1683 xcontext->bpp = px_formats[i].bits_per_pixel;
1688 xcontext->endianness =
1689 (ImageByteOrder (xcontext->disp) ==
1690 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1692 /* our caps system handles 24/32bpp RGB as big-endian. */
1693 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1694 xcontext->endianness == G_LITTLE_ENDIAN) {
1695 xcontext->endianness = G_BIG_ENDIAN;
1696 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1697 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1698 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1699 if (xcontext->bpp == 24) {
1700 xcontext->visual->red_mask >>= 8;
1701 xcontext->visual->green_mask >>= 8;
1702 xcontext->visual->blue_mask >>= 8;
1706 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1708 if (!xcontext->caps) {
1709 XCloseDisplay (xcontext->disp);
1710 g_mutex_unlock (xvimagesink->x_lock);
1711 g_free (xcontext->par);
1713 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1717 /* Search for XShm extension support */
1718 if (XShmQueryExtension (xcontext->disp) &&
1719 gst_xvimagesink_check_xshm_calls (xcontext)) {
1720 xcontext->use_xshm = TRUE;
1721 GST_DEBUG ("xvimagesink is using XShm extension");
1723 #endif /* HAVE_XSHM */
1725 xcontext->use_xshm = FALSE;
1726 GST_DEBUG ("xvimagesink is not using XShm extension");
1729 xv_attr = XvQueryPortAttributes (xcontext->disp,
1730 xcontext->xv_port_id, &N_attr);
1733 /* Generate the channels list */
1734 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1735 XvAttribute *matching_attr = NULL;
1737 /* Retrieve the property atom if it exists. If it doesn't exist,
1738 * the attribute itself must not either, so we can skip */
1739 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1740 if (prop_atom == None)
1743 if (xv_attr != NULL) {
1744 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1745 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1746 matching_attr = xv_attr + j;
1749 if (matching_attr) {
1750 GstColorBalanceChannel *channel;
1752 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1753 channel->label = g_strdup (channels[i]);
1754 channel->min_value = matching_attr ? matching_attr->min_value : -1000;
1755 channel->max_value = matching_attr ? matching_attr->max_value : 1000;
1757 xcontext->channels_list = g_list_append (xcontext->channels_list,
1760 /* If the colorbalance settings have not been touched we get Xv values
1761 as defaults and update our internal variables */
1762 if (!xvimagesink->cb_changed) {
1765 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1767 /* Normalize val to [-1000, 1000] */
1768 val = -1000 + 2000 * (val - channel->min_value) /
1769 (channel->max_value - channel->min_value);
1771 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1772 xvimagesink->hue = val;
1773 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1774 xvimagesink->saturation = val;
1775 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1776 xvimagesink->brightness = val;
1777 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1778 xvimagesink->contrast = val;
1786 g_mutex_unlock (xvimagesink->x_lock);
1788 /* Setup our event listening thread */
1789 GST_OBJECT_LOCK (xvimagesink);
1790 xvimagesink->running = TRUE;
1791 xvimagesink->event_thread = g_thread_create (
1792 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1793 GST_OBJECT_UNLOCK (xvimagesink);
1798 /* This function cleans the X context. Closing the Display, releasing the XV
1799 port and unrefing the caps for supported formats. */
1801 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1803 GList *formats_list, *channels_list;
1804 GstXContext *xcontext;
1807 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1809 GST_OBJECT_LOCK (xvimagesink);
1810 if (xvimagesink->xcontext == NULL) {
1811 GST_OBJECT_UNLOCK (xvimagesink);
1815 /* Take the XContext from the sink and clean it up */
1816 xcontext = xvimagesink->xcontext;
1817 xvimagesink->xcontext = NULL;
1819 GST_OBJECT_UNLOCK (xvimagesink);
1822 formats_list = xcontext->formats_list;
1824 while (formats_list) {
1825 GstXvImageFormat *format = formats_list->data;
1827 gst_caps_unref (format->caps);
1829 formats_list = g_list_next (formats_list);
1832 if (xcontext->formats_list)
1833 g_list_free (xcontext->formats_list);
1835 channels_list = xcontext->channels_list;
1837 while (channels_list) {
1838 GstColorBalanceChannel *channel = channels_list->data;
1840 g_object_unref (channel);
1841 channels_list = g_list_next (channels_list);
1844 if (xcontext->channels_list)
1845 g_list_free (xcontext->channels_list);
1847 gst_caps_unref (xcontext->caps);
1848 if (xcontext->last_caps)
1849 gst_caps_replace (&xcontext->last_caps, NULL);
1851 for (i = 0; i < xcontext->nb_adaptors; i++) {
1852 g_free (xcontext->adaptors[i]);
1855 g_free (xcontext->adaptors);
1857 g_free (xcontext->par);
1859 g_mutex_lock (xvimagesink->x_lock);
1861 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
1863 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1865 XCloseDisplay (xcontext->disp);
1867 g_mutex_unlock (xvimagesink->x_lock);
1873 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
1875 g_mutex_lock (xvimagesink->pool_lock);
1877 while (xvimagesink->image_pool) {
1878 GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
1880 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
1881 xvimagesink->image_pool);
1882 gst_xvimage_buffer_free (xvimage);
1885 g_mutex_unlock (xvimagesink->pool_lock);
1890 /* This function tries to get a format matching with a given caps in the
1891 supported list of formats we generated in gst_xvimagesink_get_xv_support */
1893 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
1898 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
1900 list = xvimagesink->xcontext->formats_list;
1903 GstXvImageFormat *format = list->data;
1906 GstCaps *icaps = NULL;
1908 icaps = gst_caps_intersect (caps, format->caps);
1909 if (!gst_caps_is_empty (icaps)) {
1910 gst_caps_unref (icaps);
1911 return format->format;
1913 gst_caps_unref (icaps);
1915 list = g_list_next (list);
1922 gst_xvimagesink_getcaps (GstBaseSink * bsink)
1924 GstXvImageSink *xvimagesink;
1926 xvimagesink = GST_XVIMAGESINK (bsink);
1928 if (xvimagesink->xcontext)
1929 return gst_caps_ref (xvimagesink->xcontext->caps);
1932 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
1937 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1939 GstXvImageSink *xvimagesink;
1940 GstStructure *structure;
1941 GstCaps *intersection;
1942 guint32 im_format = 0;
1944 gint video_width, video_height;
1945 gint video_par_n, video_par_d; /* video's PAR */
1946 gint display_par_n, display_par_d; /* display's PAR */
1947 const GValue *caps_par;
1951 xvimagesink = GST_XVIMAGESINK (bsink);
1953 GST_DEBUG_OBJECT (xvimagesink,
1954 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
1955 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
1957 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
1958 GST_DEBUG_OBJECT (xvimagesink, "intersection returned %" GST_PTR_FORMAT,
1960 if (gst_caps_is_empty (intersection)) {
1961 gst_caps_unref (intersection);
1965 gst_caps_unref (intersection);
1967 structure = gst_caps_get_structure (caps, 0);
1968 ret = gst_structure_get_int (structure, "width", &video_width);
1969 ret &= gst_structure_get_int (structure, "height", &video_height);
1970 fps = gst_structure_get_value (structure, "framerate");
1971 ret &= (fps != NULL);
1974 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
1975 "height or framerate from intersected caps");
1979 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
1980 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
1982 xvimagesink->video_width = video_width;
1983 xvimagesink->video_height = video_height;
1984 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
1985 if (im_format == -1) {
1986 GST_DEBUG_OBJECT (xvimagesink,
1987 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
1991 /* get aspect ratio from caps if it's present, and
1992 * convert video width and height to a display width and height
1993 * using wd / hd = wv / hv * PARv / PARd */
1995 /* get video's PAR */
1996 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1998 video_par_n = gst_value_get_fraction_numerator (caps_par);
1999 video_par_d = gst_value_get_fraction_denominator (caps_par);
2004 /* get display's PAR */
2005 if (xvimagesink->par) {
2006 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2007 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2013 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2014 video_height, video_par_n, video_par_d, display_par_n,
2016 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2017 ("Error calculating the output display ratio of the video."));
2021 GST_DEBUG_OBJECT (xvimagesink,
2022 "video width/height: %dx%d, calculated display ratio: %d/%d",
2023 video_width, video_height, num, den);
2025 /* now find a width x height that respects this display ratio.
2026 * prefer those that have one of w/h the same as the incoming video
2027 * using wd / hd = num / den */
2029 /* start with same height, because of interlaced video */
2030 /* check hd / den is an integer scale factor, and scale wd with the PAR */
2031 if (video_height % den == 0) {
2032 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2033 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2034 gst_util_uint64_scale_int (video_height, num, den);
2035 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2036 } else if (video_width % num == 0) {
2037 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2038 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2039 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2040 gst_util_uint64_scale_int (video_width, den, num);
2042 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2043 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2044 gst_util_uint64_scale_int (video_height, num, den);
2045 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2047 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2048 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2050 /* Notify application to set xwindow id now */
2051 g_mutex_lock (xvimagesink->flow_lock);
2052 if (!xvimagesink->xwindow) {
2053 g_mutex_unlock (xvimagesink->flow_lock);
2054 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2056 g_mutex_unlock (xvimagesink->flow_lock);
2059 /* Creating our window and our image with the display size in pixels */
2060 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2061 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0) {
2062 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2063 ("Error calculating the output display ratio of the video."));
2067 g_mutex_lock (xvimagesink->flow_lock);
2068 if (!xvimagesink->xwindow) {
2069 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2070 GST_VIDEO_SINK_WIDTH (xvimagesink),
2071 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2074 /* After a resize, we want to redraw the borders in case the new frame size
2075 * doesn't cover the same area */
2076 xvimagesink->draw_border = TRUE;
2078 /* We renew our xvimage only if size or format changed;
2079 * the xvimage is the same size as the video pixel size */
2080 if ((xvimagesink->xvimage) &&
2081 ((im_format != xvimagesink->xvimage->im_format) ||
2082 (video_width != xvimagesink->xvimage->width) ||
2083 (video_height != xvimagesink->xvimage->height))) {
2084 GST_DEBUG_OBJECT (xvimagesink,
2085 "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2086 GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2087 GST_FOURCC_ARGS (im_format));
2088 GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2089 gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2090 xvimagesink->xvimage = NULL;
2093 g_mutex_unlock (xvimagesink->flow_lock);
2098 static GstStateChangeReturn
2099 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2101 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2102 GstXvImageSink *xvimagesink;
2103 GstXContext *xcontext = NULL;
2105 xvimagesink = GST_XVIMAGESINK (element);
2107 switch (transition) {
2108 case GST_STATE_CHANGE_NULL_TO_READY:
2109 /* Initializing the XContext */
2110 if (xvimagesink->xcontext == NULL) {
2111 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2112 if (xcontext == NULL)
2113 return GST_STATE_CHANGE_FAILURE;
2114 GST_OBJECT_LOCK (xvimagesink);
2116 xvimagesink->xcontext = xcontext;
2117 GST_OBJECT_UNLOCK (xvimagesink);
2120 /* update object's par with calculated one if not set yet */
2121 if (!xvimagesink->par) {
2122 xvimagesink->par = g_new0 (GValue, 1);
2123 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2124 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2126 /* call XSynchronize with the current value of synchronous */
2127 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2128 xvimagesink->synchronous ? "TRUE" : "FALSE");
2129 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2130 gst_xvimagesink_update_colorbalance (xvimagesink);
2132 case GST_STATE_CHANGE_READY_TO_PAUSED:
2133 g_mutex_lock (xvimagesink->flow_lock);
2134 if (xvimagesink->xwindow)
2135 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
2136 g_mutex_unlock (xvimagesink->flow_lock);
2138 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2144 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2146 switch (transition) {
2147 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2149 case GST_STATE_CHANGE_PAUSED_TO_READY:
2150 xvimagesink->fps_n = 0;
2151 xvimagesink->fps_d = 1;
2152 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2153 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2155 case GST_STATE_CHANGE_READY_TO_NULL:
2156 gst_xvimagesink_reset (xvimagesink);
2166 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2167 GstClockTime * start, GstClockTime * end)
2169 GstXvImageSink *xvimagesink;
2171 xvimagesink = GST_XVIMAGESINK (bsink);
2173 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2174 *start = GST_BUFFER_TIMESTAMP (buf);
2175 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2176 *end = *start + GST_BUFFER_DURATION (buf);
2178 if (xvimagesink->fps_n > 0) {
2180 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2181 xvimagesink->fps_n);
2187 static GstFlowReturn
2188 gst_xvimagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
2190 GstXvImageSink *xvimagesink;
2192 xvimagesink = GST_XVIMAGESINK (bsink);
2194 /* If this buffer has been allocated using our buffer management we simply
2195 put the ximage which is in the PRIVATE pointer */
2196 if (GST_IS_XVIMAGE_BUFFER (buf)) {
2197 GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
2198 if (!gst_xvimagesink_xvimage_put (xvimagesink, GST_XVIMAGE_BUFFER (buf)))
2201 GST_LOG_OBJECT (xvimagesink, "slow copy into bufferpool buffer %p", buf);
2202 /* Else we have to copy the data into our private image, */
2203 /* if we have one... */
2204 if (!xvimagesink->xvimage) {
2205 GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
2207 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
2208 GST_BUFFER_CAPS (buf));
2210 if (!xvimagesink->xvimage)
2211 /* The create method should have posted an informative error */
2214 if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
2215 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2216 ("Failed to create output image buffer of %dx%d pixels",
2217 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
2218 ("XServer allocated buffer size did not match input buffer"));
2220 gst_xvimage_buffer_destroy (xvimagesink->xvimage);
2221 xvimagesink->xvimage = NULL;
2226 memcpy (xvimagesink->xvimage->xvimage->data,
2227 GST_BUFFER_DATA (buf),
2228 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
2230 if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
2239 /* No image available. That's very bad ! */
2240 GST_WARNING_OBJECT (xvimagesink, "could not create image");
2241 return GST_FLOW_ERROR;
2245 /* No Window available to put our image into */
2246 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
2247 return GST_FLOW_ERROR;
2251 /* Buffer management */
2253 static GstFlowReturn
2254 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
2255 GstCaps * caps, GstBuffer ** buf)
2257 GstFlowReturn ret = GST_FLOW_OK;
2258 GstXvImageSink *xvimagesink;
2259 GstXvImageBuffer *xvimage = NULL;
2260 GstCaps *intersection = NULL;
2261 GstStructure *structure = NULL;
2262 gint width, height, image_format;
2264 xvimagesink = GST_XVIMAGESINK (bsink);
2266 if (G_LIKELY (xvimagesink->xcontext->last_caps &&
2267 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
2268 GST_DEBUG_OBJECT (xvimagesink,
2269 "buffer alloc for same last_caps, reusing caps");
2270 intersection = gst_caps_ref (caps);
2271 image_format = xvimagesink->xcontext->last_format;
2273 goto reuse_last_caps;
2276 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
2277 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
2278 caps, xvimagesink->xcontext->caps);
2280 /* Check the caps against our xcontext */
2281 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2283 /* Ensure the returned caps are fixed */
2284 gst_caps_truncate (intersection);
2286 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2287 GST_PTR_FORMAT, intersection);
2289 if (gst_caps_is_empty (intersection)) {
2290 /* So we don't support this kind of buffer, let's define one we'd like */
2291 GstCaps *new_caps = gst_caps_copy (caps);
2293 structure = gst_caps_get_structure (new_caps, 0);
2295 /* Try with YUV first */
2296 gst_structure_set_name (structure, "video/x-raw-yuv");
2297 gst_structure_remove_field (structure, "format");
2298 gst_structure_remove_field (structure, "endianness");
2299 gst_structure_remove_field (structure, "depth");
2300 gst_structure_remove_field (structure, "bpp");
2301 gst_structure_remove_field (structure, "red_mask");
2302 gst_structure_remove_field (structure, "green_mask");
2303 gst_structure_remove_field (structure, "blue_mask");
2304 gst_structure_remove_field (structure, "alpha_mask");
2306 /* Reuse intersection with Xcontext */
2307 gst_caps_unref (intersection);
2308 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2310 if (gst_caps_is_empty (intersection)) {
2311 /* Now try with RGB */
2312 gst_structure_set_name (structure, "video/x-raw-rgb");
2313 /* And interset again */
2314 gst_caps_unref (intersection);
2315 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2317 if (gst_caps_is_empty (intersection)) {
2318 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2319 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2320 " are completely incompatible with those caps", new_caps,
2321 xvimagesink->xcontext->caps);
2322 gst_caps_unref (new_caps);
2323 ret = GST_FLOW_UNEXPECTED;
2328 /* Clean this copy */
2329 gst_caps_unref (new_caps);
2330 /* We want fixed caps */
2331 gst_caps_truncate (intersection);
2333 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2334 GST_PTR_FORMAT, intersection);
2335 } else if (gst_caps_is_equal (intersection, caps)) {
2336 /* Things work better if we return a buffer with the same caps ptr
2337 * as was asked for when we can */
2338 gst_caps_replace (&intersection, caps);
2341 /* Get image format from caps */
2342 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2345 /* Store our caps and format as the last_caps to avoid expensive
2346 * caps intersection next time */
2347 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2348 xvimagesink->xcontext->last_format = image_format;
2352 /* Get geometry from caps */
2353 structure = gst_caps_get_structure (intersection, 0);
2354 if (!gst_structure_get_int (structure, "width", &width) ||
2355 !gst_structure_get_int (structure, "height", &height) ||
2356 image_format == -1) {
2357 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2358 GST_PTR_FORMAT, intersection);
2359 ret = GST_FLOW_UNEXPECTED;
2363 g_mutex_lock (xvimagesink->pool_lock);
2365 /* Walking through the pool cleaning unusable images and searching for a
2367 while (xvimagesink->image_pool) {
2368 xvimage = xvimagesink->image_pool->data;
2371 /* Removing from the pool */
2372 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2373 xvimagesink->image_pool);
2375 /* We check for geometry or image format changes */
2376 if ((xvimage->width != width) ||
2377 (xvimage->height != height) || (xvimage->im_format != image_format)) {
2378 /* This image is unusable. Destroying... */
2379 gst_xvimage_buffer_free (xvimage);
2382 /* We found a suitable image */
2383 GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
2389 g_mutex_unlock (xvimagesink->pool_lock);
2392 /* We found no suitable image in the pool. Creating... */
2393 GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
2394 xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
2395 if (xvimage && xvimage->size < size) {
2396 /* This image is unusable. Destroying... */
2397 GST_LOG_OBJECT (xvimagesink, "Discarding allocated buffer as unsuitable. "
2398 "Falling back to normal buffer");
2399 gst_xvimage_buffer_free (xvimage);
2405 gst_buffer_set_caps (GST_BUFFER (xvimage), intersection);
2408 *buf = GST_BUFFER (xvimage);
2412 gst_caps_unref (intersection);
2418 /* Interfaces stuff */
2421 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2423 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2424 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE);
2429 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2431 klass->supported = gst_xvimagesink_interface_supported;
2435 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2436 GstStructure * structure)
2438 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2441 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2443 GstVideoRectangle src, dst, result;
2444 gdouble x, y, xscale = 1.0, yscale = 1.0;
2446 event = gst_event_new_navigation (structure);
2448 /* We take the flow_lock while we look at the window */
2449 g_mutex_lock (xvimagesink->flow_lock);
2451 if (!xvimagesink->xwindow) {
2452 g_mutex_unlock (xvimagesink->flow_lock);
2456 /* We get the frame position using the calculated geometry from _setcaps
2457 that respect pixel aspect ratios */
2458 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2459 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2460 dst.w = xvimagesink->xwindow->width;
2461 dst.h = xvimagesink->xwindow->height;
2463 g_mutex_unlock (xvimagesink->flow_lock);
2465 if (xvimagesink->keep_aspect) {
2466 gst_video_sink_center_rect (src, dst, &result, TRUE);
2468 result.x = result.y = 0;
2473 /* We calculate scaling using the original video frames geometry to include
2474 pixel aspect ratio scaling. */
2475 xscale = (gdouble) xvimagesink->video_width / result.w;
2476 yscale = (gdouble) xvimagesink->video_height / result.h;
2478 /* Converting pointer coordinates to the non scaled geometry */
2479 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2480 x = MIN (x, result.x + result.w);
2481 x = MAX (x - result.x, 0);
2482 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2483 (gdouble) x * xscale, NULL);
2485 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2486 y = MIN (y, result.y + result.h);
2487 y = MAX (y - result.y, 0);
2488 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2489 (gdouble) y * yscale, NULL);
2492 gst_pad_send_event (peer, event);
2493 gst_object_unref (peer);
2498 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2500 iface->send_event = gst_xvimagesink_navigation_send_event;
2504 gst_xvimagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
2506 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2507 GstXWindow *xwindow = NULL;
2508 XWindowAttributes attr;
2510 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2512 g_mutex_lock (xvimagesink->flow_lock);
2514 /* If we already use that window return */
2515 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2516 g_mutex_unlock (xvimagesink->flow_lock);
2520 /* If the element has not initialized the X11 context try to do so */
2521 if (!xvimagesink->xcontext &&
2522 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2523 g_mutex_unlock (xvimagesink->flow_lock);
2524 /* we have thrown a GST_ELEMENT_ERROR now */
2528 gst_xvimagesink_update_colorbalance (xvimagesink);
2530 /* Clear image pool as the images are unusable anyway */
2531 gst_xvimagesink_imagepool_clear (xvimagesink);
2533 /* Clear the xvimage */
2534 if (xvimagesink->xvimage) {
2535 gst_xvimage_buffer_free (xvimagesink->xvimage);
2536 xvimagesink->xvimage = NULL;
2539 /* If a window is there already we destroy it */
2540 if (xvimagesink->xwindow) {
2541 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2542 xvimagesink->xwindow = NULL;
2545 /* If the xid is 0 we go back to an internal window */
2546 if (xwindow_id == 0) {
2547 /* If no width/height caps nego did not happen window will be created
2548 during caps nego then */
2549 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2550 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2552 gst_xvimagesink_xwindow_new (xvimagesink,
2553 GST_VIDEO_SINK_WIDTH (xvimagesink),
2554 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2557 xwindow = g_new0 (GstXWindow, 1);
2559 xwindow->win = xwindow_id;
2561 /* We get window geometry, set the event we want to receive,
2563 g_mutex_lock (xvimagesink->x_lock);
2564 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2565 xwindow->width = attr.width;
2566 xwindow->height = attr.height;
2567 xwindow->internal = FALSE;
2568 if (xvimagesink->handle_events) {
2569 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2570 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2574 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2575 xwindow->win, 0, NULL);
2576 g_mutex_unlock (xvimagesink->x_lock);
2580 xvimagesink->xwindow = xwindow;
2582 g_mutex_unlock (xvimagesink->flow_lock);
2586 gst_xvimagesink_expose (GstXOverlay * overlay)
2588 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2590 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2594 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2595 gboolean handle_events)
2597 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2599 xvimagesink->handle_events = handle_events;
2601 g_mutex_lock (xvimagesink->flow_lock);
2603 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2604 g_mutex_unlock (xvimagesink->flow_lock);
2608 g_mutex_lock (xvimagesink->x_lock);
2610 if (handle_events) {
2611 if (xvimagesink->xwindow->internal) {
2612 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2613 ExposureMask | StructureNotifyMask | PointerMotionMask |
2614 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2616 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2617 ExposureMask | StructureNotifyMask | PointerMotionMask |
2618 KeyPressMask | KeyReleaseMask);
2621 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2624 g_mutex_unlock (xvimagesink->x_lock);
2626 g_mutex_unlock (xvimagesink->flow_lock);
2630 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2632 iface->set_xwindow_id = gst_xvimagesink_set_xwindow_id;
2633 iface->expose = gst_xvimagesink_expose;
2634 iface->handle_events = gst_xvimagesink_set_event_handling;
2637 static const GList *
2638 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2640 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2642 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2644 if (xvimagesink->xcontext)
2645 return xvimagesink->xcontext->channels_list;
2651 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
2652 GstColorBalanceChannel * channel, gint value)
2654 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2656 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2657 g_return_if_fail (channel->label != NULL);
2659 xvimagesink->cb_changed = TRUE;
2661 /* Normalize val to [-1000, 1000] */
2662 value = -1000 + 2000 * (value - channel->min_value) /
2663 (channel->max_value - channel->min_value);
2665 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2666 xvimagesink->hue = value;
2667 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2668 xvimagesink->saturation = value;
2669 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2670 xvimagesink->contrast = value;
2671 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2672 xvimagesink->brightness = value;
2674 g_warning ("got an unknown channel %s", channel->label);
2678 gst_xvimagesink_update_colorbalance (xvimagesink);
2682 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
2683 GstColorBalanceChannel * channel)
2685 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2688 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2689 g_return_val_if_fail (channel->label != NULL, 0);
2691 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
2692 value = xvimagesink->hue;
2693 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
2694 value = xvimagesink->saturation;
2695 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
2696 value = xvimagesink->contrast;
2697 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
2698 value = xvimagesink->brightness;
2700 g_warning ("got an unknown channel %s", channel->label);
2703 /* Normalize val to [channel->min_value, channel->max_value] */
2704 value = channel->min_value + (channel->max_value - channel->min_value) *
2705 (value + 1000) / 2000;
2711 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
2713 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
2714 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
2715 iface->set_value = gst_xvimagesink_colorbalance_set_value;
2716 iface->get_value = gst_xvimagesink_colorbalance_get_value;
2719 static const GList *
2720 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
2722 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
2723 static GList *list = NULL;
2726 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
2733 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
2734 guint prop_id, const GParamSpec * pspec)
2736 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2740 GST_DEBUG_OBJECT (xvimagesink, "probing device list");
2741 if (!xvimagesink->xcontext) {
2742 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
2743 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2747 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2753 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
2754 guint prop_id, const GParamSpec * pspec)
2756 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2757 gboolean ret = FALSE;
2761 if (xvimagesink->xcontext != NULL) {
2768 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2775 static GValueArray *
2776 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
2777 guint prop_id, const GParamSpec * pspec)
2779 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
2780 GValueArray *array = NULL;
2782 if (G_UNLIKELY (!xvimagesink->xcontext)) {
2783 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
2792 GValue value = { 0 };
2794 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
2795 g_value_init (&value, G_TYPE_STRING);
2797 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
2798 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
2800 g_value_set_string (&value, adaptor_id_s);
2801 g_value_array_append (array, &value);
2802 g_free (adaptor_id_s);
2804 g_value_unset (&value);
2808 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
2817 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
2820 iface->get_properties = gst_xvimagesink_probe_get_properties;
2821 iface->probe_property = gst_xvimagesink_probe_probe_property;
2822 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
2823 iface->get_values = gst_xvimagesink_probe_get_values;
2826 /* =========================================== */
2828 /* Init & Class init */
2830 /* =========================================== */
2833 gst_xvimagesink_set_property (GObject * object, guint prop_id,
2834 const GValue * value, GParamSpec * pspec)
2836 GstXvImageSink *xvimagesink;
2838 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2840 xvimagesink = GST_XVIMAGESINK (object);
2844 xvimagesink->hue = g_value_get_int (value);
2845 xvimagesink->cb_changed = TRUE;
2846 gst_xvimagesink_update_colorbalance (xvimagesink);
2849 xvimagesink->contrast = g_value_get_int (value);
2850 xvimagesink->cb_changed = TRUE;
2851 gst_xvimagesink_update_colorbalance (xvimagesink);
2853 case ARG_BRIGHTNESS:
2854 xvimagesink->brightness = g_value_get_int (value);
2855 xvimagesink->cb_changed = TRUE;
2856 gst_xvimagesink_update_colorbalance (xvimagesink);
2858 case ARG_SATURATION:
2859 xvimagesink->saturation = g_value_get_int (value);
2860 xvimagesink->cb_changed = TRUE;
2861 gst_xvimagesink_update_colorbalance (xvimagesink);
2864 xvimagesink->display_name = g_strdup (g_value_get_string (value));
2866 case ARG_SYNCHRONOUS:
2867 xvimagesink->synchronous = g_value_get_boolean (value);
2868 if (xvimagesink->xcontext) {
2869 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2870 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2871 xvimagesink->synchronous ? "TRUE" : "FALSE");
2874 case ARG_PIXEL_ASPECT_RATIO:
2875 g_free (xvimagesink->par);
2876 xvimagesink->par = g_new0 (GValue, 1);
2877 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
2878 if (!g_value_transform (value, xvimagesink->par)) {
2879 g_warning ("Could not transform string to aspect ratio");
2880 gst_value_set_fraction (xvimagesink->par, 1, 1);
2882 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
2883 gst_value_get_fraction_numerator (xvimagesink->par),
2884 gst_value_get_fraction_denominator (xvimagesink->par));
2886 case ARG_FORCE_ASPECT_RATIO:
2887 xvimagesink->keep_aspect = g_value_get_boolean (value);
2889 case ARG_HANDLE_EVENTS:
2890 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
2891 g_value_get_boolean (value));
2894 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
2896 case ARG_HANDLE_EXPOSE:
2897 xvimagesink->handle_expose = g_value_get_boolean (value);
2899 case ARG_DOUBLE_BUFFER:
2900 xvimagesink->double_buffer = g_value_get_boolean (value);
2903 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2909 gst_xvimagesink_get_property (GObject * object, guint prop_id,
2910 GValue * value, GParamSpec * pspec)
2912 GstXvImageSink *xvimagesink;
2914 g_return_if_fail (GST_IS_XVIMAGESINK (object));
2916 xvimagesink = GST_XVIMAGESINK (object);
2920 g_value_set_int (value, xvimagesink->hue);
2923 g_value_set_int (value, xvimagesink->contrast);
2925 case ARG_BRIGHTNESS:
2926 g_value_set_int (value, xvimagesink->brightness);
2928 case ARG_SATURATION:
2929 g_value_set_int (value, xvimagesink->saturation);
2932 g_value_set_string (value, xvimagesink->display_name);
2934 case ARG_SYNCHRONOUS:
2935 g_value_set_boolean (value, xvimagesink->synchronous);
2937 case ARG_PIXEL_ASPECT_RATIO:
2938 if (xvimagesink->par)
2939 g_value_transform (xvimagesink->par, value);
2941 case ARG_FORCE_ASPECT_RATIO:
2942 g_value_set_boolean (value, xvimagesink->keep_aspect);
2944 case ARG_HANDLE_EVENTS:
2945 g_value_set_boolean (value, xvimagesink->handle_events);
2949 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
2951 g_value_set_string (value, adaptor_no_s);
2952 g_free (adaptor_no_s);
2955 case ARG_DEVICE_NAME:
2956 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
2957 g_value_set_string (value,
2958 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
2960 g_value_set_string (value, NULL);
2963 case ARG_HANDLE_EXPOSE:
2964 g_value_set_boolean (value, xvimagesink->handle_expose);
2966 case ARG_DOUBLE_BUFFER:
2967 g_value_set_boolean (value, xvimagesink->double_buffer);
2970 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2976 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
2980 GST_OBJECT_LOCK (xvimagesink);
2981 xvimagesink->running = FALSE;
2982 /* grab thread and mark it as NULL */
2983 thread = xvimagesink->event_thread;
2984 xvimagesink->event_thread = NULL;
2985 GST_OBJECT_UNLOCK (xvimagesink);
2987 /* Wait for our event thread to finish before we clean up our stuff. */
2989 g_thread_join (thread);
2991 if (xvimagesink->cur_image) {
2992 gst_buffer_unref (xvimagesink->cur_image);
2993 xvimagesink->cur_image = NULL;
2995 if (xvimagesink->xvimage) {
2996 gst_buffer_unref (xvimagesink->xvimage);
2997 xvimagesink->xvimage = NULL;
3000 gst_xvimagesink_imagepool_clear (xvimagesink);
3002 if (xvimagesink->xwindow) {
3003 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
3004 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3005 xvimagesink->xwindow = NULL;
3008 gst_xvimagesink_xcontext_clear (xvimagesink);
3011 /* Finalize is called only once, dispose can be called multiple times.
3012 * We use mutexes and don't reset stuff to NULL here so let's register
3015 gst_xvimagesink_finalize (GObject * object)
3017 GstXvImageSink *xvimagesink;
3019 xvimagesink = GST_XVIMAGESINK (object);
3021 gst_xvimagesink_reset (xvimagesink);
3023 if (xvimagesink->display_name) {
3024 g_free (xvimagesink->display_name);
3025 xvimagesink->display_name = NULL;
3028 if (xvimagesink->par) {
3029 g_free (xvimagesink->par);
3030 xvimagesink->par = NULL;
3032 if (xvimagesink->x_lock) {
3033 g_mutex_free (xvimagesink->x_lock);
3034 xvimagesink->x_lock = NULL;
3036 if (xvimagesink->flow_lock) {
3037 g_mutex_free (xvimagesink->flow_lock);
3038 xvimagesink->flow_lock = NULL;
3040 if (xvimagesink->pool_lock) {
3041 g_mutex_free (xvimagesink->pool_lock);
3042 xvimagesink->pool_lock = NULL;
3045 G_OBJECT_CLASS (parent_class)->finalize (object);
3049 gst_xvimagesink_init (GstXvImageSink * xvimagesink)
3051 xvimagesink->display_name = NULL;
3052 xvimagesink->adaptor_no = 0;
3053 xvimagesink->xcontext = NULL;
3054 xvimagesink->xwindow = NULL;
3055 xvimagesink->xvimage = NULL;
3056 xvimagesink->cur_image = NULL;
3058 xvimagesink->hue = xvimagesink->saturation = 0;
3059 xvimagesink->contrast = xvimagesink->brightness = 0;
3060 xvimagesink->cb_changed = FALSE;
3062 xvimagesink->fps_n = 0;
3063 xvimagesink->fps_d = 0;
3064 xvimagesink->video_width = 0;
3065 xvimagesink->video_height = 0;
3067 xvimagesink->x_lock = g_mutex_new ();
3068 xvimagesink->flow_lock = g_mutex_new ();
3070 xvimagesink->image_pool = NULL;
3071 xvimagesink->pool_lock = g_mutex_new ();
3073 xvimagesink->synchronous = FALSE;
3074 xvimagesink->double_buffer = TRUE;
3075 xvimagesink->running = FALSE;
3076 xvimagesink->keep_aspect = FALSE;
3077 xvimagesink->handle_events = TRUE;
3078 xvimagesink->par = NULL;
3079 xvimagesink->handle_expose = TRUE;
3083 gst_xvimagesink_base_init (gpointer g_class)
3085 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
3087 gst_element_class_set_details (element_class, &gst_xvimagesink_details);
3089 gst_element_class_add_pad_template (element_class,
3090 gst_static_pad_template_get (&gst_xvimagesink_sink_template_factory));
3094 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
3096 GObjectClass *gobject_class;
3097 GstElementClass *gstelement_class;
3098 GstBaseSinkClass *gstbasesink_class;
3100 gobject_class = (GObjectClass *) klass;
3101 gstelement_class = (GstElementClass *) klass;
3102 gstbasesink_class = (GstBaseSinkClass *) klass;
3104 parent_class = g_type_class_peek_parent (klass);
3106 gobject_class->set_property = gst_xvimagesink_set_property;
3107 gobject_class->get_property = gst_xvimagesink_get_property;
3109 g_object_class_install_property (gobject_class, ARG_CONTRAST,
3110 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
3111 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3112 g_object_class_install_property (gobject_class, ARG_BRIGHTNESS,
3113 g_param_spec_int ("brightness", "Brightness",
3114 "The brightness of the video", -1000, 1000, 0,
3115 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3116 g_object_class_install_property (gobject_class, ARG_HUE,
3117 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
3118 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3119 g_object_class_install_property (gobject_class, ARG_SATURATION,
3120 g_param_spec_int ("saturation", "Saturation",
3121 "The saturation of the video", -1000, 1000, 0,
3122 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3123 g_object_class_install_property (gobject_class, ARG_DISPLAY,
3124 g_param_spec_string ("display", "Display", "X Display name", NULL,
3125 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3126 g_object_class_install_property (gobject_class, ARG_SYNCHRONOUS,
3127 g_param_spec_boolean ("synchronous", "Synchronous",
3128 "When enabled, runs "
3129 "the X display in synchronous mode. (used only for debugging)", FALSE,
3130 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3131 g_object_class_install_property (gobject_class, ARG_PIXEL_ASPECT_RATIO,
3132 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
3133 "The pixel aspect ratio of the device", "1/1",
3134 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3135 g_object_class_install_property (gobject_class, ARG_FORCE_ASPECT_RATIO,
3136 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
3137 "When enabled, scaling will respect original aspect ratio", FALSE,
3138 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3139 g_object_class_install_property (gobject_class, ARG_HANDLE_EVENTS,
3140 g_param_spec_boolean ("handle-events", "Handle XEvents",
3141 "When enabled, XEvents will be selected and handled", TRUE,
3142 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3143 g_object_class_install_property (gobject_class, ARG_DEVICE,
3144 g_param_spec_string ("device", "Adaptor number",
3145 "The number of the video adaptor", "0",
3146 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3147 g_object_class_install_property (gobject_class, ARG_DEVICE_NAME,
3148 g_param_spec_string ("device-name", "Adaptor name",
3149 "The name of the video adaptor", NULL,
3150 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3151 g_object_class_install_property (gobject_class, ARG_HANDLE_EXPOSE,
3152 g_param_spec_boolean ("handle-expose", "Handle expose",
3154 "the current frame will always be drawn in response to X Expose "
3155 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3156 g_object_class_install_property (gobject_class, ARG_DOUBLE_BUFFER,
3157 g_param_spec_boolean ("double-buffer", "Double-buffer",
3158 "Whether to double-buffer the output", TRUE,
3159 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3161 gobject_class->finalize = gst_xvimagesink_finalize;
3163 gstelement_class->change_state =
3164 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3166 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3167 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3168 gstbasesink_class->buffer_alloc =
3169 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3170 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3171 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3172 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3175 /* ============================================================= */
3177 /* Public Methods */
3179 /* ============================================================= */
3181 /* =========================================== */
3183 /* Object typing & Creation */
3185 /* =========================================== */
3188 gst_xvimagesink_get_type (void)
3190 static GType xvimagesink_type = 0;
3192 if (!xvimagesink_type) {
3193 static const GTypeInfo xvimagesink_info = {
3194 sizeof (GstXvImageSinkClass),
3195 gst_xvimagesink_base_init,
3197 (GClassInitFunc) gst_xvimagesink_class_init,
3200 sizeof (GstXvImageSink),
3202 (GInstanceInitFunc) gst_xvimagesink_init,
3204 static const GInterfaceInfo iface_info = {
3205 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3209 static const GInterfaceInfo navigation_info = {
3210 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3214 static const GInterfaceInfo overlay_info = {
3215 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3219 static const GInterfaceInfo colorbalance_info = {
3220 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3224 static const GInterfaceInfo propertyprobe_info = {
3225 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3229 xvimagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
3230 "GstXvImageSink", &xvimagesink_info, 0);
3232 g_type_add_interface_static (xvimagesink_type,
3233 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3234 g_type_add_interface_static (xvimagesink_type, GST_TYPE_NAVIGATION,
3236 g_type_add_interface_static (xvimagesink_type, GST_TYPE_X_OVERLAY,
3238 g_type_add_interface_static (xvimagesink_type, GST_TYPE_COLOR_BALANCE,
3239 &colorbalance_info);
3240 g_type_add_interface_static (xvimagesink_type, GST_TYPE_PROPERTY_PROBE,
3241 &propertyprobe_info);
3244 /* register type and create class in a more safe place instead of at
3245 * runtime since the type registration and class creation is not
3247 g_type_class_ref (gst_xvimage_buffer_get_type ());
3250 return xvimagesink_type;
3254 plugin_init (GstPlugin * plugin)
3256 if (!gst_element_register (plugin, "xvimagesink",
3257 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
3260 GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
3261 "xvimagesink element");
3266 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3269 "XFree86 video output plugin using Xv extension",
3270 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)