2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
3 * <2009>,<2010> Stefan Kost <stefan.kost@nokia.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-xvimagesink
24 * XvImageSink renders video frames to a drawable (XWindow) on a local display
25 * using the XVideo extension. Rendering to a remote display is theoretically
26 * possible but i doubt that the XVideo extension is actually available when
27 * connecting to a remote display. This element can receive a Window ID from the
28 * application through the XOverlay interface and will then render video frames
29 * in this drawable. If no Window ID was provided by the application, the
30 * element will create its own internal window and render into it.
33 * <title>Scaling</title>
35 * The XVideo extension, when it's available, handles hardware accelerated
36 * scaling of video frames. This means that the element will just accept
37 * incoming video frames no matter their geometry and will then put them to the
38 * drawable scaling them on the fly. Using the #GstXvImageSink:force-aspect-ratio
39 * property it is possible to enforce scaling with a constant aspect ratio,
40 * which means drawing black borders around the video frame.
44 * <title>Events</title>
46 * XvImageSink creates a thread to handle events coming from the drawable. There
47 * are several kind of events that can be grouped in 2 big categories: input
48 * events and window state related events. Input events will be translated to
49 * navigation events and pushed upstream for other elements to react on them.
50 * This includes events such as pointer moves, key press/release, clicks etc...
51 * Other events are used to handle the drawable appearance even when the data
52 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
53 * paused, it will receive expose events from the drawable and draw the latest
54 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XvImageSink will open a connection to
61 * the display specified in the #GstXvImageSink:display property or the
62 * default display if nothing specified. Once this connection is open it will
63 * inspect the display configuration including the physical display geometry and
64 * then calculate the pixel aspect ratio. When receiving video frames with a
65 * different pixel aspect ratio, XvImageSink will use hardware scaling to
66 * display the video frames correctly on display's pixel aspect ratio.
67 * Sometimes the calculated pixel aspect ratio can be wrong, it is
68 * then possible to enforce a specific pixel aspect ratio using the
69 * #GstXvImageSink:pixel-aspect-ratio property.
73 * <title>Examples</title>
75 * gst-launch -v videotestsrc ! xvimagesink
76 * ]| A pipeline to test hardware scaling.
77 * When the test video signal appears you can resize the window and see that
78 * video frames are scaled through hardware (no extra CPU cost).
80 * gst-launch -v videotestsrc ! xvimagesink force-aspect-ratio=true
81 * ]| Same pipeline with #GstXvImageSink:force-aspect-ratio property set to true
82 * You can observe the borders drawn around the scaled image respecting aspect
85 * gst-launch -v videotestsrc ! navigationtest ! xvimagesink
86 * ]| A pipeline to test navigation events.
87 * While moving the mouse pointer over the test signal you will see a black box
88 * following the mouse pointer. If you press the mouse button somewhere on the
89 * video and release it somewhere else a green box will appear where you pressed
90 * the button and a red one where you released it. (The navigationtest element
91 * is part of gst-plugins-good.) You can observe here that even if the images
92 * are scaled through hardware the pointer coordinates are converted back to the
93 * original video frame geometry so that the box can be drawn to the correct
94 * position. This also handles borders correctly, limiting coordinates to the
97 * gst-launch -v videotestsrc ! video/x-raw-yuv, pixel-aspect-ratio=(fraction)4/3 ! xvimagesink
98 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
99 * videotestsrc, in most cases the pixel aspect ratio of the display will be
100 * 1/1. This means that XvImageSink will have to do the scaling to convert
101 * incoming frames to a size that will match the display pixel aspect ratio
102 * (from 320x240 to 320x180 in this case). Note that you might have to escape
103 * some characters for your shell like '\(fraction\)'.
105 * gst-launch -v videotestsrc ! xvimagesink hue=100 saturation=-100 brightness=100
106 * ]| Demonstrates how to use the colorbalance interface.
110 /* for developers: there are two useful tools : xvinfo and xvattr */
117 #include <gst/interfaces/navigation.h>
118 #include <gst/interfaces/xoverlay.h>
119 #include <gst/interfaces/colorbalance.h>
120 #include <gst/interfaces/propertyprobe.h>
121 /* Helper functions */
122 #include <gst/video/video.h>
125 #include "xvimagesink.h"
127 /* Debugging category */
128 #include <gst/gstinfo.h>
130 #include "gst/glib-compat-private.h"
132 GST_DEBUG_CATEGORY_STATIC (gst_debug_xvimagesink);
133 #define GST_CAT_DEFAULT gst_debug_xvimagesink
134 GST_DEBUG_CATEGORY_STATIC (GST_CAT_PERFORMANCE);
139 unsigned long functions;
140 unsigned long decorations;
142 unsigned long status;
144 MotifWmHints, MwmHints;
146 #define MWM_HINTS_DECORATIONS (1L << 1)
148 static void gst_xvimagesink_reset (GstXvImageSink * xvimagesink);
150 static GstBufferClass *xvimage_buffer_parent_class = NULL;
151 static void gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage);
153 static void gst_xvimagesink_xwindow_update_geometry (GstXvImageSink *
155 static gint gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
157 static void gst_xvimagesink_expose (GstXOverlay * overlay);
159 /* Default template - initiated with class struct to allow gst-register to work
161 static GstStaticPadTemplate gst_xvimagesink_sink_template_factory =
162 GST_STATIC_PAD_TEMPLATE ("sink",
165 GST_STATIC_CAPS ("video/x-raw-rgb, "
166 "framerate = (fraction) [ 0, MAX ], "
167 "width = (int) [ 1, MAX ], "
168 "height = (int) [ 1, MAX ]; "
170 "framerate = (fraction) [ 0, MAX ], "
171 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
183 PROP_PIXEL_ASPECT_RATIO,
184 PROP_FORCE_ASPECT_RATIO,
190 PROP_AUTOPAINT_COLORKEY,
197 static void gst_xvimagesink_init_interfaces (GType type);
199 GST_BOILERPLATE_FULL (GstXvImageSink, gst_xvimagesink, GstVideoSink,
200 GST_TYPE_VIDEO_SINK, gst_xvimagesink_init_interfaces);
203 /* ============================================================= */
205 /* Private Methods */
207 /* ============================================================= */
209 /* xvimage buffers */
211 #define GST_TYPE_XVIMAGE_BUFFER (gst_xvimage_buffer_get_type())
213 #define GST_IS_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XVIMAGE_BUFFER))
214 #define GST_XVIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XVIMAGE_BUFFER, GstXvImageBuffer))
215 #define GST_XVIMAGE_BUFFER_CAST(obj) ((GstXvImageBuffer *)(obj))
217 /* This function destroys a GstXvImage handling XShm availability */
219 gst_xvimage_buffer_destroy (GstXvImageBuffer * xvimage)
221 GstXvImageSink *xvimagesink;
223 GST_DEBUG_OBJECT (xvimage, "Destroying buffer");
225 xvimagesink = xvimage->xvimagesink;
226 if (G_UNLIKELY (xvimagesink == NULL))
229 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
231 GST_OBJECT_LOCK (xvimagesink);
233 /* If the destroyed image is the current one we destroy our reference too */
234 if (xvimagesink->cur_image == xvimage)
235 xvimagesink->cur_image = NULL;
237 /* We might have some buffers destroyed after changing state to NULL */
238 if (xvimagesink->xcontext == NULL) {
239 GST_DEBUG_OBJECT (xvimagesink, "Destroying XvImage after Xcontext");
241 /* Need to free the shared memory segment even if the x context
242 * was already cleaned up */
243 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
244 shmdt (xvimage->SHMInfo.shmaddr);
247 if (xvimage->xvimage)
248 XFree (xvimage->xvimage);
252 g_mutex_lock (xvimagesink->x_lock);
255 if (xvimagesink->xcontext->use_xshm) {
256 if (xvimage->SHMInfo.shmaddr != ((void *) -1)) {
257 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmDetaching from 0x%x id 0x%lx",
258 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
259 XShmDetach (xvimagesink->xcontext->disp, &xvimage->SHMInfo);
260 XSync (xvimagesink->xcontext->disp, FALSE);
262 shmdt (xvimage->SHMInfo.shmaddr);
264 if (xvimage->xvimage)
265 XFree (xvimage->xvimage);
267 #endif /* HAVE_XSHM */
269 if (xvimage->xvimage) {
270 if (xvimage->xvimage->data) {
271 g_free (xvimage->xvimage->data);
273 XFree (xvimage->xvimage);
277 XSync (xvimagesink->xcontext->disp, FALSE);
279 g_mutex_unlock (xvimagesink->x_lock);
282 GST_OBJECT_UNLOCK (xvimagesink);
283 xvimage->xvimagesink = NULL;
284 gst_object_unref (xvimagesink);
286 GST_MINI_OBJECT_CLASS (xvimage_buffer_parent_class)->finalize (GST_MINI_OBJECT
293 GST_WARNING ("no sink found");
299 gst_xvimage_buffer_finalize (GstXvImageBuffer * xvimage)
301 GstXvImageSink *xvimagesink;
304 xvimagesink = xvimage->xvimagesink;
305 if (G_UNLIKELY (xvimagesink == NULL))
308 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
310 GST_OBJECT_LOCK (xvimagesink);
311 running = xvimagesink->running;
312 GST_OBJECT_UNLOCK (xvimagesink);
314 /* If our geometry changed we can't reuse that image. */
315 if (running == FALSE) {
316 GST_LOG_OBJECT (xvimage, "destroy image as sink is shutting down");
317 gst_xvimage_buffer_destroy (xvimage);
318 } else if ((xvimage->width != xvimagesink->video_width) ||
319 (xvimage->height != xvimagesink->video_height)) {
320 GST_LOG_OBJECT (xvimage,
321 "destroy image as its size changed %dx%d vs current %dx%d",
322 xvimage->width, xvimage->height,
323 xvimagesink->video_width, xvimagesink->video_height);
324 gst_xvimage_buffer_destroy (xvimage);
326 /* In that case we can reuse the image and add it to our image pool. */
327 GST_LOG_OBJECT (xvimage, "recycling image in pool");
328 /* need to increment the refcount again to recycle */
329 gst_buffer_ref (GST_BUFFER_CAST (xvimage));
330 g_mutex_lock (xvimagesink->pool_lock);
331 xvimagesink->image_pool = g_slist_prepend (xvimagesink->image_pool,
333 g_mutex_unlock (xvimagesink->pool_lock);
339 GST_WARNING ("no sink found");
345 gst_xvimage_buffer_free (GstXvImageBuffer * xvimage)
347 /* make sure it is not recycled */
349 xvimage->height = -1;
350 gst_buffer_unref (GST_BUFFER (xvimage));
354 gst_xvimage_buffer_init (GstXvImageBuffer * xvimage, gpointer g_class)
357 xvimage->SHMInfo.shmaddr = ((void *) -1);
358 xvimage->SHMInfo.shmid = -1;
363 gst_xvimage_buffer_class_init (gpointer g_class, gpointer class_data)
365 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
367 xvimage_buffer_parent_class = g_type_class_peek_parent (g_class);
369 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
370 gst_xvimage_buffer_finalize;
374 gst_xvimage_buffer_get_type (void)
376 static GType _gst_xvimage_buffer_type;
378 if (G_UNLIKELY (_gst_xvimage_buffer_type == 0)) {
379 static const GTypeInfo xvimage_buffer_info = {
380 sizeof (GstBufferClass),
383 gst_xvimage_buffer_class_init,
386 sizeof (GstXvImageBuffer),
388 (GInstanceInitFunc) gst_xvimage_buffer_init,
391 _gst_xvimage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
392 "GstXvImageBuffer", &xvimage_buffer_info, 0);
394 return _gst_xvimage_buffer_type;
399 static gboolean error_caught = FALSE;
402 gst_xvimagesink_handle_xerror (Display * display, XErrorEvent * xevent)
404 char error_msg[1024];
406 XGetErrorText (display, xevent->error_code, error_msg, 1024);
407 GST_DEBUG ("xvimagesink triggered an XError. error: %s", error_msg);
413 /* This function checks that it is actually really possible to create an image
416 gst_xvimagesink_check_xshm_calls (GstXContext * xcontext)
419 XShmSegmentInfo SHMInfo;
421 int (*handler) (Display *, XErrorEvent *);
422 gboolean result = FALSE;
423 gboolean did_attach = FALSE;
425 g_return_val_if_fail (xcontext != NULL, FALSE);
427 /* Sync to ensure any older errors are already processed */
428 XSync (xcontext->disp, FALSE);
430 /* Set defaults so we don't free these later unnecessarily */
431 SHMInfo.shmaddr = ((void *) -1);
434 /* Setting an error handler to catch failure */
435 error_caught = FALSE;
436 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
438 /* Trying to create a 1x1 picture */
439 GST_DEBUG ("XvShmCreateImage of 1x1");
440 xvimage = XvShmCreateImage (xcontext->disp, xcontext->xv_port_id,
441 xcontext->im_format, NULL, 1, 1, &SHMInfo);
443 /* Might cause an error, sync to ensure it is noticed */
444 XSync (xcontext->disp, FALSE);
445 if (!xvimage || error_caught) {
446 GST_WARNING ("could not XvShmCreateImage a 1x1 image");
449 size = xvimage->data_size;
451 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
452 if (SHMInfo.shmid == -1) {
453 GST_WARNING ("could not get shared memory of %d bytes", size);
457 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
458 if (SHMInfo.shmaddr == ((void *) -1)) {
459 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
460 /* Clean up the shared memory segment */
461 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
465 xvimage->data = SHMInfo.shmaddr;
466 SHMInfo.readOnly = FALSE;
468 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
469 GST_WARNING ("Failed to XShmAttach");
470 /* Clean up the shared memory segment */
471 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
475 /* Sync to ensure we see any errors we caused */
476 XSync (xcontext->disp, FALSE);
478 /* Delete the shared memory segment as soon as everyone is attached.
479 * This way, it will be deleted as soon as we detach later, and not
480 * leaked if we crash. */
481 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
484 GST_DEBUG ("XServer ShmAttached to 0x%x, id 0x%lx", SHMInfo.shmid,
488 /* store whether we succeeded in result */
491 GST_WARNING ("MIT-SHM extension check failed at XShmAttach. "
492 "Not using shared memory.");
496 /* Sync to ensure we swallow any errors we caused and reset error_caught */
497 XSync (xcontext->disp, FALSE);
499 error_caught = FALSE;
500 XSetErrorHandler (handler);
503 GST_DEBUG ("XServer ShmDetaching from 0x%x id 0x%lx",
504 SHMInfo.shmid, SHMInfo.shmseg);
505 XShmDetach (xcontext->disp, &SHMInfo);
506 XSync (xcontext->disp, FALSE);
508 if (SHMInfo.shmaddr != ((void *) -1))
509 shmdt (SHMInfo.shmaddr);
514 #endif /* HAVE_XSHM */
516 /* This function handles GstXvImage creation depending on XShm availability */
517 static GstXvImageBuffer *
518 gst_xvimagesink_xvimage_new (GstXvImageSink * xvimagesink, GstCaps * caps)
520 GstXvImageBuffer *xvimage = NULL;
521 GstStructure *structure = NULL;
522 gboolean succeeded = FALSE;
523 int (*handler) (Display *, XErrorEvent *);
525 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
530 xvimage = (GstXvImageBuffer *) gst_mini_object_new (GST_TYPE_XVIMAGE_BUFFER);
531 GST_DEBUG_OBJECT (xvimage, "Creating new XvImageBuffer");
533 structure = gst_caps_get_structure (caps, 0);
535 if (!gst_structure_get_int (structure, "width", &xvimage->width) ||
536 !gst_structure_get_int (structure, "height", &xvimage->height)) {
537 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
540 GST_LOG_OBJECT (xvimagesink, "creating %dx%d", xvimage->width,
543 xvimage->im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
544 if (xvimage->im_format == -1) {
545 GST_WARNING_OBJECT (xvimagesink, "failed to get format from caps %"
546 GST_PTR_FORMAT, caps);
547 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
548 ("Failed to create output image buffer of %dx%d pixels",
549 xvimage->width, xvimage->height), ("Invalid input caps"));
552 xvimage->xvimagesink = gst_object_ref (xvimagesink);
554 g_mutex_lock (xvimagesink->x_lock);
556 /* Setting an error handler to catch failure */
557 error_caught = FALSE;
558 handler = XSetErrorHandler (gst_xvimagesink_handle_xerror);
561 if (xvimagesink->xcontext->use_xshm) {
564 xvimage->xvimage = XvShmCreateImage (xvimagesink->xcontext->disp,
565 xvimagesink->xcontext->xv_port_id,
566 xvimage->im_format, NULL,
567 xvimage->width, xvimage->height, &xvimage->SHMInfo);
568 if (!xvimage->xvimage || error_caught) {
569 g_mutex_unlock (xvimagesink->x_lock);
571 /* Reset error flag */
572 error_caught = FALSE;
575 GST_ELEMENT_WARNING (xvimagesink, RESOURCE, WRITE,
576 ("Failed to create output image buffer of %dx%d pixels",
577 xvimage->width, xvimage->height),
578 ("could not XvShmCreateImage a %dx%d image",
579 xvimage->width, xvimage->height));
581 /* Retry without XShm */
582 xvimagesink->xcontext->use_xshm = FALSE;
584 /* Hold X mutex again to try without XShm */
585 g_mutex_lock (xvimagesink->x_lock);
589 /* we have to use the returned data_size for our shm size */
590 xvimage->size = xvimage->xvimage->data_size;
591 GST_LOG_OBJECT (xvimagesink, "XShm image size is %" G_GSIZE_FORMAT,
594 /* calculate the expected size. This is only for sanity checking the
595 * number we get from X. */
596 switch (xvimage->im_format) {
597 case GST_MAKE_FOURCC ('I', '4', '2', '0'):
598 case GST_MAKE_FOURCC ('Y', 'V', '1', '2'):
605 pitches[0] = GST_ROUND_UP_4 (xvimage->width);
606 offsets[1] = offsets[0] + pitches[0] * GST_ROUND_UP_2 (xvimage->height);
607 pitches[1] = GST_ROUND_UP_8 (xvimage->width) / 2;
609 offsets[1] + pitches[1] * GST_ROUND_UP_2 (xvimage->height) / 2;
610 pitches[2] = GST_ROUND_UP_8 (pitches[0]) / 2;
613 offsets[2] + pitches[2] * GST_ROUND_UP_2 (xvimage->height) / 2;
615 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
616 GST_DEBUG_OBJECT (xvimagesink,
617 "Plane %u has a expected pitch of %d bytes, " "offset of %d",
618 plane, pitches[plane], offsets[plane]);
622 case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
623 case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
624 expected_size = xvimage->height * GST_ROUND_UP_4 (xvimage->width * 2);
630 if (expected_size != 0 && xvimage->size != expected_size) {
631 GST_WARNING_OBJECT (xvimagesink,
632 "unexpected XShm image size (got %" G_GSIZE_FORMAT ", expected %d)",
633 xvimage->size, expected_size);
636 /* Be verbose about our XvImage stride */
640 for (plane = 0; plane < xvimage->xvimage->num_planes; plane++) {
641 GST_DEBUG_OBJECT (xvimagesink, "Plane %u has a pitch of %d bytes, "
642 "offset of %d", plane, xvimage->xvimage->pitches[plane],
643 xvimage->xvimage->offsets[plane]);
647 xvimage->SHMInfo.shmid = shmget (IPC_PRIVATE, xvimage->size,
649 if (xvimage->SHMInfo.shmid == -1) {
650 g_mutex_unlock (xvimagesink->x_lock);
651 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
652 ("Failed to create output image buffer of %dx%d pixels",
653 xvimage->width, xvimage->height),
654 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
659 xvimage->SHMInfo.shmaddr = shmat (xvimage->SHMInfo.shmid, NULL, 0);
660 if (xvimage->SHMInfo.shmaddr == ((void *) -1)) {
661 g_mutex_unlock (xvimagesink->x_lock);
662 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
663 ("Failed to create output image buffer of %dx%d pixels",
664 xvimage->width, xvimage->height),
665 ("Failed to shmat: %s", g_strerror (errno)));
666 /* Clean up the shared memory segment */
667 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
671 xvimage->xvimage->data = xvimage->SHMInfo.shmaddr;
672 xvimage->SHMInfo.readOnly = FALSE;
674 if (XShmAttach (xvimagesink->xcontext->disp, &xvimage->SHMInfo) == 0) {
675 /* Clean up the shared memory segment */
676 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
678 g_mutex_unlock (xvimagesink->x_lock);
679 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
680 ("Failed to create output image buffer of %dx%d pixels",
681 xvimage->width, xvimage->height), ("Failed to XShmAttach"));
685 XSync (xvimagesink->xcontext->disp, FALSE);
687 /* Delete the shared memory segment as soon as we everyone is attached.
688 * This way, it will be deleted as soon as we detach later, and not
689 * leaked if we crash. */
690 shmctl (xvimage->SHMInfo.shmid, IPC_RMID, NULL);
692 GST_DEBUG_OBJECT (xvimagesink, "XServer ShmAttached to 0x%x, id 0x%lx",
693 xvimage->SHMInfo.shmid, xvimage->SHMInfo.shmseg);
696 #endif /* HAVE_XSHM */
698 xvimage->xvimage = XvCreateImage (xvimagesink->xcontext->disp,
699 xvimagesink->xcontext->xv_port_id,
700 xvimage->im_format, NULL, xvimage->width, xvimage->height);
701 if (!xvimage->xvimage || error_caught) {
702 g_mutex_unlock (xvimagesink->x_lock);
703 /* Reset error handler */
704 error_caught = FALSE;
705 XSetErrorHandler (handler);
707 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
708 ("Failed to create outputimage buffer of %dx%d pixels",
709 xvimage->width, xvimage->height),
710 ("could not XvCreateImage a %dx%d image",
711 xvimage->width, xvimage->height));
715 /* we have to use the returned data_size for our image size */
716 xvimage->size = xvimage->xvimage->data_size;
717 xvimage->xvimage->data = g_malloc (xvimage->size);
719 XSync (xvimagesink->xcontext->disp, FALSE);
722 /* Reset error handler */
723 error_caught = FALSE;
724 XSetErrorHandler (handler);
728 GST_BUFFER_DATA (xvimage) = (guchar *) xvimage->xvimage->data;
729 GST_BUFFER_SIZE (xvimage) = xvimage->size;
731 g_mutex_unlock (xvimagesink->x_lock);
735 gst_xvimage_buffer_free (xvimage);
742 /* We are called with the x_lock taken */
744 gst_xvimagesink_xwindow_draw_borders (GstXvImageSink * xvimagesink,
745 GstXWindow * xwindow, GstVideoRectangle rect)
749 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
750 g_return_if_fail (xwindow != NULL);
752 XSetForeground (xvimagesink->xcontext->disp, xwindow->gc,
753 xvimagesink->xcontext->black);
756 if (rect.x > xvimagesink->render_rect.x) {
757 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
758 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
759 rect.x - xvimagesink->render_rect.x, xvimagesink->render_rect.h);
763 t1 = rect.x + rect.w;
764 t2 = xvimagesink->render_rect.x + xvimagesink->render_rect.w;
766 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
767 t1, xvimagesink->render_rect.y, t2 - t1, xvimagesink->render_rect.h);
771 if (rect.y > xvimagesink->render_rect.y) {
772 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
773 xvimagesink->render_rect.x, xvimagesink->render_rect.y,
774 xvimagesink->render_rect.w, rect.y - xvimagesink->render_rect.y);
778 t1 = rect.y + rect.h;
779 t2 = xvimagesink->render_rect.y + xvimagesink->render_rect.h;
781 XFillRectangle (xvimagesink->xcontext->disp, xwindow->win, xwindow->gc,
782 xvimagesink->render_rect.x, t1, xvimagesink->render_rect.w, t2 - t1);
786 /* This function puts a GstXvImage on a GstXvImageSink's window. Returns FALSE
787 * if no window was available */
789 gst_xvimagesink_xvimage_put (GstXvImageSink * xvimagesink,
790 GstXvImageBuffer * xvimage)
792 GstVideoRectangle result;
793 gboolean draw_border = FALSE;
795 /* We take the flow_lock. If expose is in there we don't want to run
796 concurrently from the data flow thread */
797 g_mutex_lock (xvimagesink->flow_lock);
799 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
800 g_mutex_unlock (xvimagesink->flow_lock);
804 /* Draw borders when displaying the first frame. After this
805 draw borders only on expose event or after a size change. */
806 if (!xvimagesink->cur_image || xvimagesink->redraw_border) {
810 /* Store a reference to the last image we put, lose the previous one */
811 if (xvimage && xvimagesink->cur_image != xvimage) {
812 if (xvimagesink->cur_image) {
813 GST_LOG_OBJECT (xvimagesink, "unreffing %p", xvimagesink->cur_image);
814 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
816 GST_LOG_OBJECT (xvimagesink, "reffing %p as our current image", xvimage);
817 xvimagesink->cur_image =
818 GST_XVIMAGE_BUFFER_CAST (gst_buffer_ref (GST_BUFFER_CAST (xvimage)));
821 /* Expose sends a NULL image, we take the latest frame */
823 if (xvimagesink->cur_image) {
825 xvimage = xvimagesink->cur_image;
827 g_mutex_unlock (xvimagesink->flow_lock);
832 if (xvimagesink->keep_aspect) {
833 GstVideoRectangle src, dst;
835 /* We use the calculated geometry from _setcaps as a source to respect
836 source and screen pixel aspect ratios. */
837 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
838 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
839 dst.w = xvimagesink->render_rect.w;
840 dst.h = xvimagesink->render_rect.h;
842 gst_video_sink_center_rect (src, dst, &result, TRUE);
843 result.x += xvimagesink->render_rect.x;
844 result.y += xvimagesink->render_rect.y;
846 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
849 g_mutex_lock (xvimagesink->x_lock);
851 if (draw_border && xvimagesink->draw_borders) {
852 gst_xvimagesink_xwindow_draw_borders (xvimagesink, xvimagesink->xwindow,
854 xvimagesink->redraw_border = FALSE;
857 /* We scale to the window's geometry */
859 if (xvimagesink->xcontext->use_xshm) {
860 GST_LOG_OBJECT (xvimagesink,
861 "XvShmPutImage with image %dx%d and window %dx%d, from xvimage %"
863 xvimage->width, xvimage->height,
864 xvimagesink->render_rect.w, xvimagesink->render_rect.h, xvimage);
866 XvShmPutImage (xvimagesink->xcontext->disp,
867 xvimagesink->xcontext->xv_port_id,
868 xvimagesink->xwindow->win,
869 xvimagesink->xwindow->gc, xvimage->xvimage,
870 xvimagesink->disp_x, xvimagesink->disp_y,
871 xvimagesink->disp_width, xvimagesink->disp_height,
872 result.x, result.y, result.w, result.h, FALSE);
874 #endif /* HAVE_XSHM */
876 XvPutImage (xvimagesink->xcontext->disp,
877 xvimagesink->xcontext->xv_port_id,
878 xvimagesink->xwindow->win,
879 xvimagesink->xwindow->gc, xvimage->xvimage,
880 xvimagesink->disp_x, xvimagesink->disp_y,
881 xvimagesink->disp_width, xvimagesink->disp_height,
882 result.x, result.y, result.w, result.h);
885 XSync (xvimagesink->xcontext->disp, FALSE);
887 g_mutex_unlock (xvimagesink->x_lock);
889 g_mutex_unlock (xvimagesink->flow_lock);
895 gst_xvimagesink_xwindow_decorate (GstXvImageSink * xvimagesink,
898 Atom hints_atom = None;
901 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), FALSE);
902 g_return_val_if_fail (window != NULL, FALSE);
904 g_mutex_lock (xvimagesink->x_lock);
906 hints_atom = XInternAtom (xvimagesink->xcontext->disp, "_MOTIF_WM_HINTS",
908 if (hints_atom == None) {
909 g_mutex_unlock (xvimagesink->x_lock);
913 hints = g_malloc0 (sizeof (MotifWmHints));
915 hints->flags |= MWM_HINTS_DECORATIONS;
916 hints->decorations = 1 << 0;
918 XChangeProperty (xvimagesink->xcontext->disp, window->win,
919 hints_atom, hints_atom, 32, PropModeReplace,
920 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
922 XSync (xvimagesink->xcontext->disp, FALSE);
924 g_mutex_unlock (xvimagesink->x_lock);
932 gst_xvimagesink_xwindow_set_title (GstXvImageSink * xvimagesink,
933 GstXWindow * xwindow, const gchar * media_title)
936 g_free (xvimagesink->media_title);
937 xvimagesink->media_title = g_strdup (media_title);
940 /* we have a window */
941 if (xwindow->internal) {
942 XTextProperty xproperty;
943 const gchar *app_name;
944 const gchar *title = NULL;
945 gchar *title_mem = NULL;
947 /* set application name as a title */
948 app_name = g_get_application_name ();
950 if (app_name && xvimagesink->media_title) {
951 title = title_mem = g_strconcat (xvimagesink->media_title, " : ",
953 } else if (app_name) {
955 } else if (xvimagesink->media_title) {
956 title = xvimagesink->media_title;
960 if ((XStringListToTextProperty (((char **) &title), 1,
962 XSetWMName (xvimagesink->xcontext->disp, xwindow->win, &xproperty);
963 XFree (xproperty.value);
972 /* This function handles a GstXWindow creation
973 * The width and height are the actual pixel size on the display */
975 gst_xvimagesink_xwindow_new (GstXvImageSink * xvimagesink,
976 gint width, gint height)
978 GstXWindow *xwindow = NULL;
981 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
983 xwindow = g_new0 (GstXWindow, 1);
985 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
986 xvimagesink->render_rect.w = width;
987 xvimagesink->render_rect.h = height;
989 xwindow->width = width;
990 xwindow->height = height;
991 xwindow->internal = TRUE;
993 g_mutex_lock (xvimagesink->x_lock);
995 xwindow->win = XCreateSimpleWindow (xvimagesink->xcontext->disp,
996 xvimagesink->xcontext->root,
997 0, 0, width, height, 0, 0, xvimagesink->xcontext->black);
999 /* We have to do that to prevent X from redrawing the background on
1000 * ConfigureNotify. This takes away flickering of video when resizing. */
1001 XSetWindowBackgroundPixmap (xvimagesink->xcontext->disp, xwindow->win, None);
1003 /* set application name as a title */
1004 gst_xvimagesink_xwindow_set_title (xvimagesink, xwindow, NULL);
1006 if (xvimagesink->handle_events) {
1009 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
1010 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1011 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1013 /* Tell the window manager we'd like delete client messages instead of
1015 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1016 "WM_DELETE_WINDOW", True);
1017 if (wm_delete != None) {
1018 (void) XSetWMProtocols (xvimagesink->xcontext->disp, xwindow->win,
1023 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
1024 xwindow->win, 0, &values);
1026 XMapRaised (xvimagesink->xcontext->disp, xwindow->win);
1028 XSync (xvimagesink->xcontext->disp, FALSE);
1030 g_mutex_unlock (xvimagesink->x_lock);
1032 gst_xvimagesink_xwindow_decorate (xvimagesink, xwindow);
1034 gst_x_overlay_got_window_handle (GST_X_OVERLAY (xvimagesink), xwindow->win);
1039 /* This function destroys a GstXWindow */
1041 gst_xvimagesink_xwindow_destroy (GstXvImageSink * xvimagesink,
1042 GstXWindow * xwindow)
1044 g_return_if_fail (xwindow != NULL);
1045 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1047 g_mutex_lock (xvimagesink->x_lock);
1049 /* If we did not create that window we just free the GC and let it live */
1050 if (xwindow->internal)
1051 XDestroyWindow (xvimagesink->xcontext->disp, xwindow->win);
1053 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, 0);
1055 XFreeGC (xvimagesink->xcontext->disp, xwindow->gc);
1057 XSync (xvimagesink->xcontext->disp, FALSE);
1059 g_mutex_unlock (xvimagesink->x_lock);
1065 gst_xvimagesink_xwindow_update_geometry (GstXvImageSink * xvimagesink)
1067 XWindowAttributes attr;
1069 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1071 /* Update the window geometry */
1072 g_mutex_lock (xvimagesink->x_lock);
1073 if (G_UNLIKELY (xvimagesink->xwindow == NULL)) {
1074 g_mutex_unlock (xvimagesink->x_lock);
1078 XGetWindowAttributes (xvimagesink->xcontext->disp,
1079 xvimagesink->xwindow->win, &attr);
1081 xvimagesink->xwindow->width = attr.width;
1082 xvimagesink->xwindow->height = attr.height;
1084 if (!xvimagesink->have_render_rect) {
1085 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
1086 xvimagesink->render_rect.w = attr.width;
1087 xvimagesink->render_rect.h = attr.height;
1090 g_mutex_unlock (xvimagesink->x_lock);
1094 gst_xvimagesink_xwindow_clear (GstXvImageSink * xvimagesink,
1095 GstXWindow * xwindow)
1097 g_return_if_fail (xwindow != NULL);
1098 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1100 g_mutex_lock (xvimagesink->x_lock);
1102 XvStopVideo (xvimagesink->xcontext->disp, xvimagesink->xcontext->xv_port_id,
1105 XSync (xvimagesink->xcontext->disp, FALSE);
1107 g_mutex_unlock (xvimagesink->x_lock);
1110 /* This function commits our internal colorbalance settings to our grabbed Xv
1111 port. If the xcontext is not initialized yet it simply returns */
1113 gst_xvimagesink_update_colorbalance (GstXvImageSink * xvimagesink)
1115 GList *channels = NULL;
1117 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1119 /* If we haven't initialized the X context we can't update anything */
1120 if (xvimagesink->xcontext == NULL)
1123 /* Don't set the attributes if they haven't been changed, to avoid
1124 * rounding errors changing the values */
1125 if (!xvimagesink->cb_changed)
1128 /* For each channel of the colorbalance we calculate the correct value
1129 doing range conversion and then set the Xv port attribute to match our
1131 channels = xvimagesink->xcontext->channels_list;
1134 if (channels->data && GST_IS_COLOR_BALANCE_CHANNEL (channels->data)) {
1135 GstColorBalanceChannel *channel = NULL;
1138 gdouble convert_coef;
1140 channel = GST_COLOR_BALANCE_CHANNEL (channels->data);
1141 g_object_ref (channel);
1143 /* Our range conversion coef */
1144 convert_coef = (channel->max_value - channel->min_value) / 2000.0;
1146 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
1147 value = xvimagesink->hue;
1148 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
1149 value = xvimagesink->saturation;
1150 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
1151 value = xvimagesink->contrast;
1152 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
1153 value = xvimagesink->brightness;
1155 g_warning ("got an unknown channel %s", channel->label);
1156 g_object_unref (channel);
1160 /* Committing to Xv port */
1161 g_mutex_lock (xvimagesink->x_lock);
1163 XInternAtom (xvimagesink->xcontext->disp, channel->label, True);
1164 if (prop_atom != None) {
1167 floor (0.5 + (value + 1000) * convert_coef + channel->min_value);
1168 XvSetPortAttribute (xvimagesink->xcontext->disp,
1169 xvimagesink->xcontext->xv_port_id, prop_atom, xv_value);
1171 g_mutex_unlock (xvimagesink->x_lock);
1173 g_object_unref (channel);
1175 channels = g_list_next (channels);
1179 /* This function handles XEvents that might be in the queue. It generates
1180 GstEvent that will be sent upstream in the pipeline to handle interactivity
1181 and navigation. It will also listen for configure events on the window to
1182 trigger caps renegotiation so on the fly software scaling can work. */
1184 gst_xvimagesink_handle_xevents (GstXvImageSink * xvimagesink)
1187 guint pointer_x = 0, pointer_y = 0;
1188 gboolean pointer_moved = FALSE;
1189 gboolean exposed = FALSE, configured = FALSE;
1191 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1193 /* Handle Interaction, produces navigation events */
1195 /* We get all pointer motion events, only the last position is
1197 g_mutex_lock (xvimagesink->flow_lock);
1198 g_mutex_lock (xvimagesink->x_lock);
1199 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1200 xvimagesink->xwindow->win, PointerMotionMask, &e)) {
1201 g_mutex_unlock (xvimagesink->x_lock);
1202 g_mutex_unlock (xvimagesink->flow_lock);
1206 pointer_x = e.xmotion.x;
1207 pointer_y = e.xmotion.y;
1208 pointer_moved = TRUE;
1213 g_mutex_lock (xvimagesink->flow_lock);
1214 g_mutex_lock (xvimagesink->x_lock);
1216 if (pointer_moved) {
1217 g_mutex_unlock (xvimagesink->x_lock);
1218 g_mutex_unlock (xvimagesink->flow_lock);
1220 GST_DEBUG ("xvimagesink pointer moved over window at %d,%d",
1221 pointer_x, pointer_y);
1222 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1223 "mouse-move", 0, e.xbutton.x, e.xbutton.y);
1225 g_mutex_lock (xvimagesink->flow_lock);
1226 g_mutex_lock (xvimagesink->x_lock);
1229 /* We get all events on our window to throw them upstream */
1230 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1231 xvimagesink->xwindow->win,
1232 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask,
1236 /* We lock only for the X function call */
1237 g_mutex_unlock (xvimagesink->x_lock);
1238 g_mutex_unlock (xvimagesink->flow_lock);
1242 /* Mouse button pressed over our window. We send upstream
1243 events for interactivity/navigation */
1244 GST_DEBUG ("xvimagesink button %d pressed over window at %d,%d",
1245 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1246 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1247 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1250 /* Mouse button released over our window. We send upstream
1251 events for interactivity/navigation */
1252 GST_DEBUG ("xvimagesink button %d released over window at %d,%d",
1253 e.xbutton.button, e.xbutton.x, e.xbutton.y);
1254 gst_navigation_send_mouse_event (GST_NAVIGATION (xvimagesink),
1255 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1259 /* Key pressed/released over our window. We send upstream
1260 events for interactivity/navigation */
1261 GST_DEBUG ("xvimagesink key %d pressed over window at %d,%d",
1262 e.xkey.keycode, e.xkey.x, e.xkey.y);
1263 g_mutex_lock (xvimagesink->x_lock);
1264 keysym = XKeycodeToKeysym (xvimagesink->xcontext->disp,
1266 g_mutex_unlock (xvimagesink->x_lock);
1267 if (keysym != NoSymbol) {
1268 char *key_str = NULL;
1270 g_mutex_lock (xvimagesink->x_lock);
1271 key_str = XKeysymToString (keysym);
1272 g_mutex_unlock (xvimagesink->x_lock);
1273 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1274 e.type == KeyPress ? "key-press" : "key-release", key_str);
1276 gst_navigation_send_key_event (GST_NAVIGATION (xvimagesink),
1277 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1281 GST_DEBUG ("xvimagesink unhandled X event (%d)", e.type);
1283 g_mutex_lock (xvimagesink->flow_lock);
1284 g_mutex_lock (xvimagesink->x_lock);
1288 while (XCheckWindowEvent (xvimagesink->xcontext->disp,
1289 xvimagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1294 case ConfigureNotify:
1295 g_mutex_unlock (xvimagesink->x_lock);
1296 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
1297 g_mutex_lock (xvimagesink->x_lock);
1305 if (xvimagesink->handle_expose && (exposed || configured)) {
1306 g_mutex_unlock (xvimagesink->x_lock);
1307 g_mutex_unlock (xvimagesink->flow_lock);
1309 gst_xvimagesink_expose (GST_X_OVERLAY (xvimagesink));
1311 g_mutex_lock (xvimagesink->flow_lock);
1312 g_mutex_lock (xvimagesink->x_lock);
1315 /* Handle Display events */
1316 while (XPending (xvimagesink->xcontext->disp)) {
1317 XNextEvent (xvimagesink->xcontext->disp, &e);
1320 case ClientMessage:{
1323 wm_delete = XInternAtom (xvimagesink->xcontext->disp,
1324 "WM_DELETE_WINDOW", True);
1325 if (wm_delete != None && wm_delete == (Atom) e.xclient.data.l[0]) {
1326 /* Handle window deletion by posting an error on the bus */
1327 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, NOT_FOUND,
1328 ("Output window was closed"), (NULL));
1330 g_mutex_unlock (xvimagesink->x_lock);
1331 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
1332 xvimagesink->xwindow = NULL;
1333 g_mutex_lock (xvimagesink->x_lock);
1342 g_mutex_unlock (xvimagesink->x_lock);
1343 g_mutex_unlock (xvimagesink->flow_lock);
1347 gst_lookup_xv_port_from_adaptor (GstXContext * xcontext,
1348 XvAdaptorInfo * adaptors, int adaptor_no)
1353 /* Do we support XvImageMask ? */
1354 if (!(adaptors[adaptor_no].type & XvImageMask)) {
1355 GST_DEBUG ("XV Adaptor %s has no support for XvImageMask",
1356 adaptors[adaptor_no].name);
1360 /* We found such an adaptor, looking for an available port */
1361 for (j = 0; j < adaptors[adaptor_no].num_ports && !xcontext->xv_port_id; j++) {
1362 /* We try to grab the port */
1363 res = XvGrabPort (xcontext->disp, adaptors[adaptor_no].base_id + j, 0);
1364 if (Success == res) {
1365 xcontext->xv_port_id = adaptors[adaptor_no].base_id + j;
1366 GST_DEBUG ("XV Adaptor %s with %ld ports", adaptors[adaptor_no].name,
1367 adaptors[adaptor_no].num_ports);
1369 GST_DEBUG ("GrabPort %d for XV Adaptor %s failed: %d", j,
1370 adaptors[adaptor_no].name, res);
1375 /* This function generates a caps with all supported format by the first
1376 Xv grabable port we find. We store each one of the supported formats in a
1377 format list and append the format to a newly created caps that we return
1378 If this function does not return NULL because of an error, it also grabs
1379 the port via XvGrabPort */
1381 gst_xvimagesink_get_xv_support (GstXvImageSink * xvimagesink,
1382 GstXContext * xcontext)
1385 XvAdaptorInfo *adaptors;
1387 XvImageFormatValues *formats = NULL;
1389 XvEncodingInfo *encodings = NULL;
1390 gulong max_w = G_MAXINT, max_h = G_MAXINT;
1391 GstCaps *caps = NULL;
1392 GstCaps *rgb_caps = NULL;
1394 g_return_val_if_fail (xcontext != NULL, NULL);
1396 /* First let's check that XVideo extension is available */
1397 if (!XQueryExtension (xcontext->disp, "XVideo", &i, &i, &i)) {
1398 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1399 ("Could not initialise Xv output"),
1400 ("XVideo extension is not available"));
1404 /* Then we get adaptors list */
1405 if (Success != XvQueryAdaptors (xcontext->disp, xcontext->root,
1406 &xcontext->nb_adaptors, &adaptors)) {
1407 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1408 ("Could not initialise Xv output"),
1409 ("Failed getting XV adaptors list"));
1413 xcontext->xv_port_id = 0;
1415 GST_DEBUG ("Found %u XV adaptor(s)", xcontext->nb_adaptors);
1417 xcontext->adaptors =
1418 (gchar **) g_malloc0 (xcontext->nb_adaptors * sizeof (gchar *));
1420 /* Now fill up our adaptor name array */
1421 for (i = 0; i < xcontext->nb_adaptors; i++) {
1422 xcontext->adaptors[i] = g_strdup (adaptors[i].name);
1425 if (xvimagesink->adaptor_no >= 0 &&
1426 xvimagesink->adaptor_no < xcontext->nb_adaptors) {
1427 /* Find xv port from user defined adaptor */
1428 gst_lookup_xv_port_from_adaptor (xcontext, adaptors,
1429 xvimagesink->adaptor_no);
1432 if (!xcontext->xv_port_id) {
1433 /* Now search for an adaptor that supports XvImageMask */
1434 for (i = 0; i < xcontext->nb_adaptors && !xcontext->xv_port_id; i++) {
1435 gst_lookup_xv_port_from_adaptor (xcontext, adaptors, i);
1436 xvimagesink->adaptor_no = i;
1440 XvFreeAdaptorInfo (adaptors);
1442 if (!xcontext->xv_port_id) {
1443 xvimagesink->adaptor_no = -1;
1444 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, BUSY,
1445 ("Could not initialise Xv output"), ("No port available"));
1449 /* Set XV_AUTOPAINT_COLORKEY and XV_DOUBLE_BUFFER and XV_COLORKEY */
1451 int count, todo = 3;
1452 XvAttribute *const attr = XvQueryPortAttributes (xcontext->disp,
1453 xcontext->xv_port_id, &count);
1454 static const char autopaint[] = "XV_AUTOPAINT_COLORKEY";
1455 static const char dbl_buffer[] = "XV_DOUBLE_BUFFER";
1456 static const char colorkey[] = "XV_COLORKEY";
1458 GST_DEBUG_OBJECT (xvimagesink, "Checking %d Xv port attributes", count);
1460 xvimagesink->have_autopaint_colorkey = FALSE;
1461 xvimagesink->have_double_buffer = FALSE;
1462 xvimagesink->have_colorkey = FALSE;
1464 for (i = 0; ((i < count) && todo); i++)
1465 if (!strcmp (attr[i].name, autopaint)) {
1466 const Atom atom = XInternAtom (xcontext->disp, autopaint, False);
1468 /* turn on autopaint colorkey */
1469 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1470 (xvimagesink->autopaint_colorkey ? 1 : 0));
1472 xvimagesink->have_autopaint_colorkey = TRUE;
1473 } else if (!strcmp (attr[i].name, dbl_buffer)) {
1474 const Atom atom = XInternAtom (xcontext->disp, dbl_buffer, False);
1476 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1477 (xvimagesink->double_buffer ? 1 : 0));
1479 xvimagesink->have_double_buffer = TRUE;
1480 } else if (!strcmp (attr[i].name, colorkey)) {
1481 /* Set the colorkey, default is something that is dark but hopefully
1482 * won't randomly appear on the screen elsewhere (ie not black or greys)
1483 * can be overridden by setting "colorkey" property
1485 const Atom atom = XInternAtom (xcontext->disp, colorkey, False);
1487 gboolean set_attr = TRUE;
1490 /* set a colorkey in the right format RGB565/RGB888
1491 * We only handle these 2 cases, because they're the only types of
1492 * devices we've encountered. If we don't recognise it, leave it alone
1494 cr = (xvimagesink->colorkey >> 16);
1495 cg = (xvimagesink->colorkey >> 8) & 0xFF;
1496 cb = (xvimagesink->colorkey) & 0xFF;
1497 switch (xcontext->depth) {
1498 case 16: /* RGB 565 */
1502 ckey = (cr << 11) | (cg << 5) | cb;
1505 case 32: /* RGB 888 / ARGB 8888 */
1506 ckey = (cr << 16) | (cg << 8) | cb;
1509 GST_DEBUG_OBJECT (xvimagesink,
1510 "Unknown bit depth %d for Xv Colorkey - not adjusting",
1517 ckey = CLAMP (ckey, (guint32) attr[i].min_value,
1518 (guint32) attr[i].max_value);
1519 GST_LOG_OBJECT (xvimagesink,
1520 "Setting color key for display depth %d to 0x%x",
1521 xcontext->depth, ckey);
1523 XvSetPortAttribute (xcontext->disp, xcontext->xv_port_id, atom,
1527 xvimagesink->have_colorkey = TRUE;
1533 /* Get the list of encodings supported by the adapter and look for the
1534 * XV_IMAGE encoding so we can determine the maximum width and height
1536 XvQueryEncodings (xcontext->disp, xcontext->xv_port_id, &nb_encodings,
1539 for (i = 0; i < nb_encodings; i++) {
1540 GST_LOG_OBJECT (xvimagesink,
1541 "Encoding %d, name %s, max wxh %lux%lu rate %d/%d",
1542 i, encodings[i].name, encodings[i].width, encodings[i].height,
1543 encodings[i].rate.numerator, encodings[i].rate.denominator);
1544 if (strcmp (encodings[i].name, "XV_IMAGE") == 0) {
1545 max_w = encodings[i].width;
1546 max_h = encodings[i].height;
1550 XvFreeEncodingInfo (encodings);
1552 /* We get all image formats supported by our port */
1553 formats = XvListImageFormats (xcontext->disp,
1554 xcontext->xv_port_id, &nb_formats);
1555 caps = gst_caps_new_empty ();
1556 for (i = 0; i < nb_formats; i++) {
1557 GstCaps *format_caps = NULL;
1558 gboolean is_rgb_format = FALSE;
1560 /* We set the image format of the xcontext to an existing one. This
1561 is just some valid image format for making our xshm calls check before
1562 caps negotiation really happens. */
1563 xcontext->im_format = formats[i].id;
1565 switch (formats[i].type) {
1568 XvImageFormatValues *fmt = &(formats[i]);
1569 gint endianness = G_BIG_ENDIAN;
1571 if (fmt->byte_order == LSBFirst) {
1572 /* our caps system handles 24/32bpp RGB as big-endian. */
1573 if (fmt->bits_per_pixel == 24 || fmt->bits_per_pixel == 32) {
1574 fmt->red_mask = GUINT32_TO_BE (fmt->red_mask);
1575 fmt->green_mask = GUINT32_TO_BE (fmt->green_mask);
1576 fmt->blue_mask = GUINT32_TO_BE (fmt->blue_mask);
1578 if (fmt->bits_per_pixel == 24) {
1579 fmt->red_mask >>= 8;
1580 fmt->green_mask >>= 8;
1581 fmt->blue_mask >>= 8;
1584 endianness = G_LITTLE_ENDIAN;
1587 format_caps = gst_caps_new_simple ("video/x-raw-rgb",
1588 "endianness", G_TYPE_INT, endianness,
1589 "depth", G_TYPE_INT, fmt->depth,
1590 "bpp", G_TYPE_INT, fmt->bits_per_pixel,
1591 "red_mask", G_TYPE_INT, fmt->red_mask,
1592 "green_mask", G_TYPE_INT, fmt->green_mask,
1593 "blue_mask", G_TYPE_INT, fmt->blue_mask,
1594 "width", GST_TYPE_INT_RANGE, 1, max_w,
1595 "height", GST_TYPE_INT_RANGE, 1, max_h,
1596 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1598 is_rgb_format = TRUE;
1602 format_caps = gst_caps_new_simple ("video/x-raw-yuv",
1603 "format", GST_TYPE_FOURCC, formats[i].id,
1604 "width", GST_TYPE_INT_RANGE, 1, max_w,
1605 "height", GST_TYPE_INT_RANGE, 1, max_h,
1606 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1609 g_assert_not_reached ();
1614 GstXvImageFormat *format = NULL;
1616 format = g_new0 (GstXvImageFormat, 1);
1618 format->format = formats[i].id;
1619 format->caps = gst_caps_copy (format_caps);
1620 xcontext->formats_list = g_list_append (xcontext->formats_list, format);
1623 if (is_rgb_format) {
1624 if (rgb_caps == NULL)
1625 rgb_caps = format_caps;
1627 gst_caps_append (rgb_caps, format_caps);
1629 gst_caps_append (caps, format_caps);
1633 /* Collected all caps into either the caps or rgb_caps structures.
1634 * Append rgb_caps on the end of YUV, so that YUV is always preferred */
1636 gst_caps_append (caps, rgb_caps);
1641 GST_DEBUG ("Generated the following caps: %" GST_PTR_FORMAT, caps);
1643 if (gst_caps_is_empty (caps)) {
1644 gst_caps_unref (caps);
1645 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
1646 GST_ELEMENT_ERROR (xvimagesink, STREAM, WRONG_TYPE, (NULL),
1647 ("No supported format found"));
1655 gst_xvimagesink_event_thread (GstXvImageSink * xvimagesink)
1657 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1659 GST_OBJECT_LOCK (xvimagesink);
1660 while (xvimagesink->running) {
1661 GST_OBJECT_UNLOCK (xvimagesink);
1663 if (xvimagesink->xwindow) {
1664 gst_xvimagesink_handle_xevents (xvimagesink);
1666 /* FIXME: do we want to align this with the framerate or anything else? */
1667 g_usleep (G_USEC_PER_SEC / 20);
1669 GST_OBJECT_LOCK (xvimagesink);
1671 GST_OBJECT_UNLOCK (xvimagesink);
1677 gst_xvimagesink_manage_event_thread (GstXvImageSink * xvimagesink)
1679 GThread *thread = NULL;
1681 /* don't start the thread too early */
1682 if (xvimagesink->xcontext == NULL) {
1686 GST_OBJECT_LOCK (xvimagesink);
1687 if (xvimagesink->handle_expose || xvimagesink->handle_events) {
1688 if (!xvimagesink->event_thread) {
1689 /* Setup our event listening thread */
1690 GST_DEBUG_OBJECT (xvimagesink, "run xevent thread, expose %d, events %d",
1691 xvimagesink->handle_expose, xvimagesink->handle_events);
1692 xvimagesink->running = TRUE;
1693 #if !GLIB_CHECK_VERSION (2, 31, 0)
1694 xvimagesink->event_thread = g_thread_create (
1695 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, TRUE, NULL);
1697 xvimagesink->event_thread = g_thread_try_new ("xvimagesink-events",
1698 (GThreadFunc) gst_xvimagesink_event_thread, xvimagesink, NULL);
1702 if (xvimagesink->event_thread) {
1703 GST_DEBUG_OBJECT (xvimagesink, "stop xevent thread, expose %d, events %d",
1704 xvimagesink->handle_expose, xvimagesink->handle_events);
1705 xvimagesink->running = FALSE;
1706 /* grab thread and mark it as NULL */
1707 thread = xvimagesink->event_thread;
1708 xvimagesink->event_thread = NULL;
1711 GST_OBJECT_UNLOCK (xvimagesink);
1713 /* Wait for our event thread to finish */
1715 g_thread_join (thread);
1720 /* This function calculates the pixel aspect ratio based on the properties
1721 * in the xcontext structure and stores it there. */
1723 gst_xvimagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1725 static const gint par[][2] = {
1726 {1, 1}, /* regular screen */
1727 {16, 15}, /* PAL TV */
1728 {11, 10}, /* 525 line Rec.601 video */
1729 {54, 59}, /* 625 line Rec.601 video */
1730 {64, 45}, /* 1280x1024 on 16:9 display */
1731 {5, 3}, /* 1280x1024 on 4:3 display */
1732 {4, 3} /* 800x600 on 16:9 display */
1739 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1741 /* first calculate the "real" ratio based on the X values;
1742 * which is the "physical" w/h divided by the w/h in pixels of the display */
1743 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1744 / (xcontext->heightmm * xcontext->width);
1746 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1748 if (xcontext->width == 720 && xcontext->height == 576) {
1749 ratio = 4.0 * 576 / (3.0 * 720);
1751 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1752 /* now find the one from par[][2] with the lowest delta to the real one */
1756 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1757 gdouble this_delta = DELTA (i);
1759 if (this_delta < delta) {
1765 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1766 par[index][0], par[index][1]);
1768 g_free (xcontext->par);
1769 xcontext->par = g_new0 (GValue, 1);
1770 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1771 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1772 GST_DEBUG ("set xcontext PAR to %d/%d",
1773 gst_value_get_fraction_numerator (xcontext->par),
1774 gst_value_get_fraction_denominator (xcontext->par));
1777 /* This function gets the X Display and global info about it. Everything is
1778 stored in our object and will be cleaned when the object is disposed. Note
1779 here that caps for supported format are generated without any window or
1781 static GstXContext *
1782 gst_xvimagesink_xcontext_get (GstXvImageSink * xvimagesink)
1784 GstXContext *xcontext = NULL;
1785 XPixmapFormatValues *px_formats = NULL;
1786 gint nb_formats = 0, i, j, N_attr;
1787 XvAttribute *xv_attr;
1789 const char *channels[4] = { "XV_HUE", "XV_SATURATION",
1790 "XV_BRIGHTNESS", "XV_CONTRAST"
1793 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
1795 xcontext = g_new0 (GstXContext, 1);
1796 xcontext->im_format = 0;
1798 g_mutex_lock (xvimagesink->x_lock);
1800 xcontext->disp = XOpenDisplay (xvimagesink->display_name);
1802 if (!xcontext->disp) {
1803 g_mutex_unlock (xvimagesink->x_lock);
1805 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
1806 ("Could not initialise Xv output"), ("Could not open display"));
1810 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1811 xcontext->screen_num = DefaultScreen (xcontext->disp);
1812 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1813 xcontext->root = DefaultRootWindow (xcontext->disp);
1814 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1815 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1816 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1818 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1819 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1820 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1821 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1823 GST_DEBUG_OBJECT (xvimagesink, "X reports %dx%d pixels and %d mm x %d mm",
1824 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1826 gst_xvimagesink_calculate_pixel_aspect_ratio (xcontext);
1827 /* We get supported pixmap formats at supported depth */
1828 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1831 XCloseDisplay (xcontext->disp);
1832 g_mutex_unlock (xvimagesink->x_lock);
1833 g_free (xcontext->par);
1835 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, SETTINGS,
1836 ("Could not initialise Xv output"), ("Could not get pixel formats"));
1840 /* We get bpp value corresponding to our running depth */
1841 for (i = 0; i < nb_formats; i++) {
1842 if (px_formats[i].depth == xcontext->depth)
1843 xcontext->bpp = px_formats[i].bits_per_pixel;
1848 xcontext->endianness =
1849 (ImageByteOrder (xcontext->disp) ==
1850 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1852 /* our caps system handles 24/32bpp RGB as big-endian. */
1853 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1854 xcontext->endianness == G_LITTLE_ENDIAN) {
1855 xcontext->endianness = G_BIG_ENDIAN;
1856 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1857 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1858 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1859 if (xcontext->bpp == 24) {
1860 xcontext->visual->red_mask >>= 8;
1861 xcontext->visual->green_mask >>= 8;
1862 xcontext->visual->blue_mask >>= 8;
1866 xcontext->caps = gst_xvimagesink_get_xv_support (xvimagesink, xcontext);
1868 if (!xcontext->caps) {
1869 XCloseDisplay (xcontext->disp);
1870 g_mutex_unlock (xvimagesink->x_lock);
1871 g_free (xcontext->par);
1873 /* GST_ELEMENT_ERROR is thrown by gst_xvimagesink_get_xv_support */
1877 /* Search for XShm extension support */
1878 if (XShmQueryExtension (xcontext->disp) &&
1879 gst_xvimagesink_check_xshm_calls (xcontext)) {
1880 xcontext->use_xshm = TRUE;
1881 GST_DEBUG ("xvimagesink is using XShm extension");
1883 #endif /* HAVE_XSHM */
1885 xcontext->use_xshm = FALSE;
1886 GST_DEBUG ("xvimagesink is not using XShm extension");
1889 xv_attr = XvQueryPortAttributes (xcontext->disp,
1890 xcontext->xv_port_id, &N_attr);
1893 /* Generate the channels list */
1894 for (i = 0; i < (sizeof (channels) / sizeof (char *)); i++) {
1895 XvAttribute *matching_attr = NULL;
1897 /* Retrieve the property atom if it exists. If it doesn't exist,
1898 * the attribute itself must not either, so we can skip */
1899 prop_atom = XInternAtom (xcontext->disp, channels[i], True);
1900 if (prop_atom == None)
1903 if (xv_attr != NULL) {
1904 for (j = 0; j < N_attr && matching_attr == NULL; ++j)
1905 if (!g_ascii_strcasecmp (channels[i], xv_attr[j].name))
1906 matching_attr = xv_attr + j;
1909 if (matching_attr) {
1910 GstColorBalanceChannel *channel;
1912 channel = g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL, NULL);
1913 channel->label = g_strdup (channels[i]);
1914 channel->min_value = matching_attr->min_value;
1915 channel->max_value = matching_attr->max_value;
1917 xcontext->channels_list = g_list_append (xcontext->channels_list,
1920 /* If the colorbalance settings have not been touched we get Xv values
1921 as defaults and update our internal variables */
1922 if (!xvimagesink->cb_changed) {
1925 XvGetPortAttribute (xcontext->disp, xcontext->xv_port_id,
1927 /* Normalize val to [-1000, 1000] */
1928 val = floor (0.5 + -1000 + 2000 * (val - channel->min_value) /
1929 (double) (channel->max_value - channel->min_value));
1931 if (!g_ascii_strcasecmp (channels[i], "XV_HUE"))
1932 xvimagesink->hue = val;
1933 else if (!g_ascii_strcasecmp (channels[i], "XV_SATURATION"))
1934 xvimagesink->saturation = val;
1935 else if (!g_ascii_strcasecmp (channels[i], "XV_BRIGHTNESS"))
1936 xvimagesink->brightness = val;
1937 else if (!g_ascii_strcasecmp (channels[i], "XV_CONTRAST"))
1938 xvimagesink->contrast = val;
1946 g_mutex_unlock (xvimagesink->x_lock);
1951 /* This function cleans the X context. Closing the Display, releasing the XV
1952 port and unrefing the caps for supported formats. */
1954 gst_xvimagesink_xcontext_clear (GstXvImageSink * xvimagesink)
1956 GList *formats_list, *channels_list;
1957 GstXContext *xcontext;
1960 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
1962 GST_OBJECT_LOCK (xvimagesink);
1963 if (xvimagesink->xcontext == NULL) {
1964 GST_OBJECT_UNLOCK (xvimagesink);
1968 /* Take the XContext from the sink and clean it up */
1969 xcontext = xvimagesink->xcontext;
1970 xvimagesink->xcontext = NULL;
1972 GST_OBJECT_UNLOCK (xvimagesink);
1975 formats_list = xcontext->formats_list;
1977 while (formats_list) {
1978 GstXvImageFormat *format = formats_list->data;
1980 gst_caps_unref (format->caps);
1982 formats_list = g_list_next (formats_list);
1985 if (xcontext->formats_list)
1986 g_list_free (xcontext->formats_list);
1988 channels_list = xcontext->channels_list;
1990 while (channels_list) {
1991 GstColorBalanceChannel *channel = channels_list->data;
1993 g_object_unref (channel);
1994 channels_list = g_list_next (channels_list);
1997 if (xcontext->channels_list)
1998 g_list_free (xcontext->channels_list);
2000 gst_caps_unref (xcontext->caps);
2001 if (xcontext->last_caps)
2002 gst_caps_replace (&xcontext->last_caps, NULL);
2004 for (i = 0; i < xcontext->nb_adaptors; i++) {
2005 g_free (xcontext->adaptors[i]);
2008 g_free (xcontext->adaptors);
2010 g_free (xcontext->par);
2012 g_mutex_lock (xvimagesink->x_lock);
2014 GST_DEBUG_OBJECT (xvimagesink, "Closing display and freeing X Context");
2016 XvUngrabPort (xcontext->disp, xcontext->xv_port_id, 0);
2018 XCloseDisplay (xcontext->disp);
2020 g_mutex_unlock (xvimagesink->x_lock);
2026 gst_xvimagesink_imagepool_clear (GstXvImageSink * xvimagesink)
2028 g_mutex_lock (xvimagesink->pool_lock);
2030 while (xvimagesink->image_pool) {
2031 GstXvImageBuffer *xvimage = xvimagesink->image_pool->data;
2033 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2034 xvimagesink->image_pool);
2035 gst_xvimage_buffer_free (xvimage);
2038 g_mutex_unlock (xvimagesink->pool_lock);
2043 /* This function tries to get a format matching with a given caps in the
2044 supported list of formats we generated in gst_xvimagesink_get_xv_support */
2046 gst_xvimagesink_get_format_from_caps (GstXvImageSink * xvimagesink,
2051 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
2053 list = xvimagesink->xcontext->formats_list;
2056 GstXvImageFormat *format = list->data;
2059 if (gst_caps_can_intersect (caps, format->caps)) {
2060 return format->format;
2063 list = g_list_next (list);
2070 gst_xvimagesink_getcaps (GstBaseSink * bsink)
2072 GstXvImageSink *xvimagesink;
2074 xvimagesink = GST_XVIMAGESINK (bsink);
2076 if (xvimagesink->xcontext)
2077 return gst_caps_ref (xvimagesink->xcontext->caps);
2080 gst_caps_copy (gst_pad_get_pad_template_caps (GST_VIDEO_SINK_PAD
2085 gst_xvimagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
2087 GstXvImageSink *xvimagesink;
2088 GstStructure *structure;
2089 guint32 im_format = 0;
2091 gint video_width, video_height;
2092 gint disp_x, disp_y;
2093 gint disp_width, disp_height;
2094 gint video_par_n, video_par_d; /* video's PAR */
2095 gint display_par_n, display_par_d; /* display's PAR */
2096 const GValue *caps_par;
2097 const GValue *caps_disp_reg;
2101 xvimagesink = GST_XVIMAGESINK (bsink);
2103 GST_DEBUG_OBJECT (xvimagesink,
2104 "In setcaps. Possible caps %" GST_PTR_FORMAT ", setting caps %"
2105 GST_PTR_FORMAT, xvimagesink->xcontext->caps, caps);
2107 if (!gst_caps_can_intersect (xvimagesink->xcontext->caps, caps))
2108 goto incompatible_caps;
2110 structure = gst_caps_get_structure (caps, 0);
2111 ret = gst_structure_get_int (structure, "width", &video_width);
2112 ret &= gst_structure_get_int (structure, "height", &video_height);
2113 fps = gst_structure_get_value (structure, "framerate");
2114 ret &= (fps != NULL);
2117 goto incomplete_caps;
2119 xvimagesink->fps_n = gst_value_get_fraction_numerator (fps);
2120 xvimagesink->fps_d = gst_value_get_fraction_denominator (fps);
2122 xvimagesink->video_width = video_width;
2123 xvimagesink->video_height = video_height;
2125 im_format = gst_xvimagesink_get_format_from_caps (xvimagesink, caps);
2126 if (im_format == -1)
2127 goto invalid_format;
2129 /* get aspect ratio from caps if it's present, and
2130 * convert video width and height to a display width and height
2131 * using wd / hd = wv / hv * PARv / PARd */
2133 /* get video's PAR */
2134 caps_par = gst_structure_get_value (structure, "pixel-aspect-ratio");
2136 video_par_n = gst_value_get_fraction_numerator (caps_par);
2137 video_par_d = gst_value_get_fraction_denominator (caps_par);
2142 /* get display's PAR */
2143 if (xvimagesink->par) {
2144 display_par_n = gst_value_get_fraction_numerator (xvimagesink->par);
2145 display_par_d = gst_value_get_fraction_denominator (xvimagesink->par);
2151 /* get the display region */
2152 caps_disp_reg = gst_structure_get_value (structure, "display-region");
2153 if (caps_disp_reg) {
2154 disp_x = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 0));
2155 disp_y = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 1));
2156 disp_width = g_value_get_int (gst_value_array_get_value (caps_disp_reg, 2));
2158 g_value_get_int (gst_value_array_get_value (caps_disp_reg, 3));
2160 disp_x = disp_y = 0;
2161 disp_width = video_width;
2162 disp_height = video_height;
2165 if (!gst_video_calculate_display_ratio (&num, &den, video_width,
2166 video_height, video_par_n, video_par_d, display_par_n, display_par_d))
2169 xvimagesink->disp_x = disp_x;
2170 xvimagesink->disp_y = disp_y;
2171 xvimagesink->disp_width = disp_width;
2172 xvimagesink->disp_height = disp_height;
2174 GST_DEBUG_OBJECT (xvimagesink,
2175 "video width/height: %dx%d, calculated display ratio: %d/%d",
2176 video_width, video_height, num, den);
2178 /* now find a width x height that respects this display ratio.
2179 * prefer those that have one of w/h the same as the incoming video
2180 * using wd / hd = num / den */
2182 /* start with same height, because of interlaced video */
2183 /* check hd / den is an integer scale factor, and scale wd with the PAR */
2184 if (video_height % den == 0) {
2185 GST_DEBUG_OBJECT (xvimagesink, "keeping video height");
2186 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2187 gst_util_uint64_scale_int (video_height, num, den);
2188 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2189 } else if (video_width % num == 0) {
2190 GST_DEBUG_OBJECT (xvimagesink, "keeping video width");
2191 GST_VIDEO_SINK_WIDTH (xvimagesink) = video_width;
2192 GST_VIDEO_SINK_HEIGHT (xvimagesink) = (guint)
2193 gst_util_uint64_scale_int (video_width, den, num);
2195 GST_DEBUG_OBJECT (xvimagesink, "approximating while keeping video height");
2196 GST_VIDEO_SINK_WIDTH (xvimagesink) = (guint)
2197 gst_util_uint64_scale_int (video_height, num, den);
2198 GST_VIDEO_SINK_HEIGHT (xvimagesink) = video_height;
2200 GST_DEBUG_OBJECT (xvimagesink, "scaling to %dx%d",
2201 GST_VIDEO_SINK_WIDTH (xvimagesink), GST_VIDEO_SINK_HEIGHT (xvimagesink));
2203 /* Notify application to set xwindow id now */
2204 g_mutex_lock (xvimagesink->flow_lock);
2205 if (!xvimagesink->xwindow) {
2206 g_mutex_unlock (xvimagesink->flow_lock);
2207 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (xvimagesink));
2209 g_mutex_unlock (xvimagesink->flow_lock);
2212 /* Creating our window and our image with the display size in pixels */
2213 if (GST_VIDEO_SINK_WIDTH (xvimagesink) <= 0 ||
2214 GST_VIDEO_SINK_HEIGHT (xvimagesink) <= 0)
2215 goto no_display_size;
2217 g_mutex_lock (xvimagesink->flow_lock);
2218 if (!xvimagesink->xwindow) {
2219 xvimagesink->xwindow = gst_xvimagesink_xwindow_new (xvimagesink,
2220 GST_VIDEO_SINK_WIDTH (xvimagesink),
2221 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2224 /* After a resize, we want to redraw the borders in case the new frame size
2225 * doesn't cover the same area */
2226 xvimagesink->redraw_border = TRUE;
2228 /* We renew our xvimage only if size or format changed;
2229 * the xvimage is the same size as the video pixel size */
2230 if ((xvimagesink->xvimage) &&
2231 ((im_format != xvimagesink->xvimage->im_format) ||
2232 (video_width != xvimagesink->xvimage->width) ||
2233 (video_height != xvimagesink->xvimage->height))) {
2234 GST_DEBUG_OBJECT (xvimagesink,
2235 "old format %" GST_FOURCC_FORMAT ", new format %" GST_FOURCC_FORMAT,
2236 GST_FOURCC_ARGS (xvimagesink->xvimage->im_format),
2237 GST_FOURCC_ARGS (im_format));
2238 GST_DEBUG_OBJECT (xvimagesink, "renewing xvimage");
2239 gst_buffer_unref (GST_BUFFER (xvimagesink->xvimage));
2240 xvimagesink->xvimage = NULL;
2243 g_mutex_unlock (xvimagesink->flow_lock);
2250 GST_ERROR_OBJECT (xvimagesink, "caps incompatible");
2255 GST_DEBUG_OBJECT (xvimagesink, "Failed to retrieve either width, "
2256 "height or framerate from intersected caps");
2261 GST_DEBUG_OBJECT (xvimagesink,
2262 "Could not locate image format from caps %" GST_PTR_FORMAT, caps);
2267 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2268 ("Error calculating the output display ratio of the video."));
2273 GST_ELEMENT_ERROR (xvimagesink, CORE, NEGOTIATION, (NULL),
2274 ("Error calculating the output display ratio of the video."));
2279 static GstStateChangeReturn
2280 gst_xvimagesink_change_state (GstElement * element, GstStateChange transition)
2282 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
2283 GstXvImageSink *xvimagesink;
2284 GstXContext *xcontext = NULL;
2286 xvimagesink = GST_XVIMAGESINK (element);
2288 switch (transition) {
2289 case GST_STATE_CHANGE_NULL_TO_READY:
2290 /* Initializing the XContext */
2291 if (xvimagesink->xcontext == NULL) {
2292 xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
2293 if (xcontext == NULL)
2294 return GST_STATE_CHANGE_FAILURE;
2295 GST_OBJECT_LOCK (xvimagesink);
2297 xvimagesink->xcontext = xcontext;
2298 GST_OBJECT_UNLOCK (xvimagesink);
2301 /* update object's par with calculated one if not set yet */
2302 if (!xvimagesink->par) {
2303 xvimagesink->par = g_new0 (GValue, 1);
2304 gst_value_init_and_copy (xvimagesink->par, xvimagesink->xcontext->par);
2305 GST_DEBUG_OBJECT (xvimagesink, "set calculated PAR on object's PAR");
2307 /* call XSynchronize with the current value of synchronous */
2308 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
2309 xvimagesink->synchronous ? "TRUE" : "FALSE");
2310 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
2311 gst_xvimagesink_update_colorbalance (xvimagesink);
2312 gst_xvimagesink_manage_event_thread (xvimagesink);
2314 case GST_STATE_CHANGE_READY_TO_PAUSED:
2315 g_mutex_lock (xvimagesink->pool_lock);
2316 xvimagesink->pool_invalid = FALSE;
2317 g_mutex_unlock (xvimagesink->pool_lock);
2319 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
2321 case GST_STATE_CHANGE_PAUSED_TO_READY:
2322 g_mutex_lock (xvimagesink->pool_lock);
2323 xvimagesink->pool_invalid = TRUE;
2324 g_mutex_unlock (xvimagesink->pool_lock);
2330 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
2332 switch (transition) {
2333 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
2335 case GST_STATE_CHANGE_PAUSED_TO_READY:
2336 xvimagesink->fps_n = 0;
2337 xvimagesink->fps_d = 1;
2338 GST_VIDEO_SINK_WIDTH (xvimagesink) = 0;
2339 GST_VIDEO_SINK_HEIGHT (xvimagesink) = 0;
2341 case GST_STATE_CHANGE_READY_TO_NULL:
2342 gst_xvimagesink_reset (xvimagesink);
2352 gst_xvimagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
2353 GstClockTime * start, GstClockTime * end)
2355 GstXvImageSink *xvimagesink;
2357 xvimagesink = GST_XVIMAGESINK (bsink);
2359 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
2360 *start = GST_BUFFER_TIMESTAMP (buf);
2361 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
2362 *end = *start + GST_BUFFER_DURATION (buf);
2364 if (xvimagesink->fps_n > 0) {
2366 gst_util_uint64_scale_int (GST_SECOND, xvimagesink->fps_d,
2367 xvimagesink->fps_n);
2373 static GstFlowReturn
2374 gst_xvimagesink_show_frame (GstVideoSink * vsink, GstBuffer * buf)
2376 GstXvImageSink *xvimagesink;
2378 xvimagesink = GST_XVIMAGESINK (vsink);
2380 /* If this buffer has been allocated using our buffer management we simply
2381 put the ximage which is in the PRIVATE pointer */
2382 if (GST_IS_XVIMAGE_BUFFER (buf)) {
2383 GST_LOG_OBJECT (xvimagesink, "fast put of bufferpool buffer %p", buf);
2384 if (!gst_xvimagesink_xvimage_put (xvimagesink,
2385 GST_XVIMAGE_BUFFER_CAST (buf)))
2388 GST_CAT_LOG_OBJECT (GST_CAT_PERFORMANCE, xvimagesink,
2389 "slow copy into bufferpool buffer %p", buf);
2390 /* Else we have to copy the data into our private image, */
2391 /* if we have one... */
2392 if (!xvimagesink->xvimage) {
2393 GST_DEBUG_OBJECT (xvimagesink, "creating our xvimage");
2395 xvimagesink->xvimage = gst_xvimagesink_xvimage_new (xvimagesink,
2396 GST_BUFFER_CAPS (buf));
2398 if (!xvimagesink->xvimage)
2399 /* The create method should have posted an informative error */
2402 if (xvimagesink->xvimage->size < GST_BUFFER_SIZE (buf)) {
2403 GST_ELEMENT_ERROR (xvimagesink, RESOURCE, WRITE,
2404 ("Failed to create output image buffer of %dx%d pixels",
2405 xvimagesink->xvimage->width, xvimagesink->xvimage->height),
2406 ("XServer allocated buffer size did not match input buffer"));
2408 gst_xvimage_buffer_destroy (xvimagesink->xvimage);
2409 xvimagesink->xvimage = NULL;
2414 memcpy (xvimagesink->xvimage->xvimage->data,
2415 GST_BUFFER_DATA (buf),
2416 MIN (GST_BUFFER_SIZE (buf), xvimagesink->xvimage->size));
2418 if (!gst_xvimagesink_xvimage_put (xvimagesink, xvimagesink->xvimage))
2427 /* No image available. That's very bad ! */
2428 GST_WARNING_OBJECT (xvimagesink, "could not create image");
2429 return GST_FLOW_ERROR;
2433 /* No Window available to put our image into */
2434 GST_WARNING_OBJECT (xvimagesink, "could not output image - no window");
2435 return GST_FLOW_ERROR;
2440 gst_xvimagesink_event (GstBaseSink * sink, GstEvent * event)
2442 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (sink);
2444 switch (GST_EVENT_TYPE (event)) {
2445 case GST_EVENT_TAG:{
2447 gchar *title = NULL;
2449 gst_event_parse_tag (event, &l);
2450 gst_tag_list_get_string (l, GST_TAG_TITLE, &title);
2453 GST_DEBUG_OBJECT (xvimagesink, "got tags, title='%s'", title);
2454 gst_xvimagesink_xwindow_set_title (xvimagesink, xvimagesink->xwindow,
2464 if (GST_BASE_SINK_CLASS (parent_class)->event)
2465 return GST_BASE_SINK_CLASS (parent_class)->event (sink, event);
2470 /* Buffer management */
2473 gst_xvimage_sink_different_size_suggestion (GstXvImageSink * xvimagesink,
2476 GstCaps *intersection;
2480 gint par_n = 1, par_d = 1;
2484 new_caps = gst_caps_copy (caps);
2486 s = gst_caps_get_structure (new_caps, 0);
2488 gst_structure_get_int (s, "width", &width);
2489 gst_structure_get_int (s, "height", &height);
2490 gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d);
2492 gst_structure_remove_field (s, "width");
2493 gst_structure_remove_field (s, "height");
2494 gst_structure_remove_field (s, "pixel-aspect-ratio");
2496 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2497 gst_caps_unref (new_caps);
2499 if (gst_caps_is_empty (intersection))
2500 return intersection;
2502 s = gst_caps_get_structure (intersection, 0);
2504 gst_util_fraction_multiply (width, height, par_n, par_d, &dar_n, &dar_d);
2506 /* xvimagesink supports all PARs */
2508 gst_structure_fixate_field_nearest_int (s, "width", width);
2509 gst_structure_fixate_field_nearest_int (s, "height", height);
2510 gst_structure_get_int (s, "width", &w);
2511 gst_structure_get_int (s, "height", &h);
2513 gst_util_fraction_multiply (h, w, dar_n, dar_d, &par_n, &par_d);
2514 gst_structure_set (s, "pixel-aspect-ratio", GST_TYPE_FRACTION, par_n, par_d,
2517 return intersection;
2520 static GstFlowReturn
2521 gst_xvimagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
2522 GstCaps * caps, GstBuffer ** buf)
2524 GstFlowReturn ret = GST_FLOW_OK;
2525 GstXvImageSink *xvimagesink;
2526 GstXvImageBuffer *xvimage = NULL;
2527 GstCaps *intersection = NULL;
2528 GstStructure *structure = NULL;
2529 gint width, height, image_format;
2531 xvimagesink = GST_XVIMAGESINK (bsink);
2533 if (G_UNLIKELY (!caps))
2536 g_mutex_lock (xvimagesink->pool_lock);
2537 if (G_UNLIKELY (xvimagesink->pool_invalid))
2540 if (G_LIKELY (xvimagesink->xcontext->last_caps &&
2541 gst_caps_is_equal (caps, xvimagesink->xcontext->last_caps))) {
2542 GST_LOG_OBJECT (xvimagesink,
2543 "buffer alloc for same last_caps, reusing caps");
2544 intersection = gst_caps_ref (caps);
2545 image_format = xvimagesink->xcontext->last_format;
2546 width = xvimagesink->xcontext->last_width;
2547 height = xvimagesink->xcontext->last_height;
2549 goto reuse_last_caps;
2552 GST_DEBUG_OBJECT (xvimagesink, "buffer alloc requested size %d with caps %"
2553 GST_PTR_FORMAT ", intersecting with our caps %" GST_PTR_FORMAT, size,
2554 caps, xvimagesink->xcontext->caps);
2556 /* Check the caps against our xcontext */
2557 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, caps);
2559 GST_DEBUG_OBJECT (xvimagesink, "intersection in buffer alloc returned %"
2560 GST_PTR_FORMAT, intersection);
2562 if (gst_caps_is_empty (intersection)) {
2565 gst_caps_unref (intersection);
2567 /* So we don't support this kind of buffer, let's define one we'd like */
2568 new_caps = gst_caps_copy (caps);
2570 structure = gst_caps_get_structure (new_caps, 0);
2571 if (!gst_structure_has_field (structure, "width") ||
2572 !gst_structure_has_field (structure, "height")) {
2573 gst_caps_unref (new_caps);
2577 /* Try different dimensions */
2579 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2581 if (gst_caps_is_empty (intersection)) {
2582 /* Try with different YUV formats first */
2583 gst_structure_set_name (structure, "video/x-raw-yuv");
2585 /* Remove format specific fields */
2586 gst_structure_remove_field (structure, "format");
2587 gst_structure_remove_field (structure, "endianness");
2588 gst_structure_remove_field (structure, "depth");
2589 gst_structure_remove_field (structure, "bpp");
2590 gst_structure_remove_field (structure, "red_mask");
2591 gst_structure_remove_field (structure, "green_mask");
2592 gst_structure_remove_field (structure, "blue_mask");
2593 gst_structure_remove_field (structure, "alpha_mask");
2595 /* Reuse intersection with Xcontext */
2596 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2599 if (gst_caps_is_empty (intersection)) {
2600 /* Try with different dimensions and YUV formats */
2602 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2605 if (gst_caps_is_empty (intersection)) {
2606 /* Now try with RGB */
2607 gst_structure_set_name (structure, "video/x-raw-rgb");
2608 /* And interset again */
2609 gst_caps_unref (intersection);
2610 intersection = gst_caps_intersect (xvimagesink->xcontext->caps, new_caps);
2613 if (gst_caps_is_empty (intersection)) {
2614 /* Try with different dimensions and RGB formats */
2616 gst_xvimage_sink_different_size_suggestion (xvimagesink, new_caps);
2619 /* Clean this copy */
2620 gst_caps_unref (new_caps);
2622 if (gst_caps_is_empty (intersection))
2626 /* Ensure the returned caps are fixed */
2627 gst_caps_truncate (intersection);
2629 GST_DEBUG_OBJECT (xvimagesink, "allocating a buffer with caps %"
2630 GST_PTR_FORMAT, intersection);
2631 if (gst_caps_is_equal (intersection, caps)) {
2632 /* Things work better if we return a buffer with the same caps ptr
2633 * as was asked for when we can */
2634 gst_caps_replace (&intersection, caps);
2637 /* Get image format from caps */
2638 image_format = gst_xvimagesink_get_format_from_caps (xvimagesink,
2641 /* Get geometry from caps */
2642 structure = gst_caps_get_structure (intersection, 0);
2643 if (!gst_structure_get_int (structure, "width", &width) ||
2644 !gst_structure_get_int (structure, "height", &height) ||
2648 /* Store our caps and format as the last_caps to avoid expensive
2649 * caps intersection next time */
2650 gst_caps_replace (&xvimagesink->xcontext->last_caps, intersection);
2651 xvimagesink->xcontext->last_format = image_format;
2652 xvimagesink->xcontext->last_width = width;
2653 xvimagesink->xcontext->last_height = height;
2657 /* Walking through the pool cleaning unusable images and searching for a
2659 while (xvimagesink->image_pool) {
2660 xvimage = xvimagesink->image_pool->data;
2663 /* Removing from the pool */
2664 xvimagesink->image_pool = g_slist_delete_link (xvimagesink->image_pool,
2665 xvimagesink->image_pool);
2667 /* We check for geometry or image format changes */
2668 if ((xvimage->width != width) ||
2669 (xvimage->height != height) || (xvimage->im_format != image_format)) {
2670 /* This image is unusable. Destroying... */
2671 gst_xvimage_buffer_free (xvimage);
2674 /* We found a suitable image */
2675 GST_LOG_OBJECT (xvimagesink, "found usable image in pool");
2682 /* We found no suitable image in the pool. Creating... */
2683 GST_DEBUG_OBJECT (xvimagesink, "no usable image in pool, creating xvimage");
2684 xvimage = gst_xvimagesink_xvimage_new (xvimagesink, intersection);
2686 g_mutex_unlock (xvimagesink->pool_lock);
2689 /* Make sure the buffer is cleared of any previously used flags */
2690 GST_MINI_OBJECT_CAST (xvimage)->flags = 0;
2691 gst_buffer_set_caps (GST_BUFFER_CAST (xvimage), intersection);
2694 *buf = GST_BUFFER_CAST (xvimage);
2698 gst_caps_unref (intersection);
2706 GST_DEBUG_OBJECT (xvimagesink, "the pool is flushing");
2707 ret = GST_FLOW_WRONG_STATE;
2708 g_mutex_unlock (xvimagesink->pool_lock);
2713 GST_WARNING_OBJECT (xvimagesink, "we were requested a buffer with "
2714 "caps %" GST_PTR_FORMAT ", but our xcontext caps %" GST_PTR_FORMAT
2715 " are completely incompatible with those caps", caps,
2716 xvimagesink->xcontext->caps);
2717 ret = GST_FLOW_NOT_NEGOTIATED;
2718 g_mutex_unlock (xvimagesink->pool_lock);
2723 GST_WARNING_OBJECT (xvimagesink, "invalid caps for buffer allocation %"
2724 GST_PTR_FORMAT, intersection);
2725 ret = GST_FLOW_NOT_NEGOTIATED;
2726 g_mutex_unlock (xvimagesink->pool_lock);
2731 GST_WARNING_OBJECT (xvimagesink, "have no caps, doing fallback allocation");
2738 /* Interfaces stuff */
2741 gst_xvimagesink_interface_supported (GstImplementsInterface * iface, GType type)
2743 if (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY ||
2744 type == GST_TYPE_COLOR_BALANCE || type == GST_TYPE_PROPERTY_PROBE)
2751 gst_xvimagesink_interface_init (GstImplementsInterfaceClass * klass)
2753 klass->supported = gst_xvimagesink_interface_supported;
2757 gst_xvimagesink_navigation_send_event (GstNavigation * navigation,
2758 GstStructure * structure)
2760 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (navigation);
2763 if ((peer = gst_pad_get_peer (GST_VIDEO_SINK_PAD (xvimagesink)))) {
2765 GstVideoRectangle src, dst, result;
2766 gdouble x, y, xscale = 1.0, yscale = 1.0;
2768 event = gst_event_new_navigation (structure);
2770 /* We take the flow_lock while we look at the window */
2771 g_mutex_lock (xvimagesink->flow_lock);
2773 if (!xvimagesink->xwindow) {
2774 g_mutex_unlock (xvimagesink->flow_lock);
2778 if (xvimagesink->keep_aspect) {
2779 /* We get the frame position using the calculated geometry from _setcaps
2780 that respect pixel aspect ratios */
2781 src.w = GST_VIDEO_SINK_WIDTH (xvimagesink);
2782 src.h = GST_VIDEO_SINK_HEIGHT (xvimagesink);
2783 dst.w = xvimagesink->render_rect.w;
2784 dst.h = xvimagesink->render_rect.h;
2786 gst_video_sink_center_rect (src, dst, &result, TRUE);
2787 result.x += xvimagesink->render_rect.x;
2788 result.y += xvimagesink->render_rect.y;
2790 memcpy (&result, &xvimagesink->render_rect, sizeof (GstVideoRectangle));
2793 g_mutex_unlock (xvimagesink->flow_lock);
2795 /* We calculate scaling using the original video frames geometry to include
2796 pixel aspect ratio scaling. */
2797 xscale = (gdouble) xvimagesink->video_width / result.w;
2798 yscale = (gdouble) xvimagesink->video_height / result.h;
2800 /* Converting pointer coordinates to the non scaled geometry */
2801 if (gst_structure_get_double (structure, "pointer_x", &x)) {
2802 x = MIN (x, result.x + result.w);
2803 x = MAX (x - result.x, 0);
2804 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE,
2805 (gdouble) x * xscale, NULL);
2807 if (gst_structure_get_double (structure, "pointer_y", &y)) {
2808 y = MIN (y, result.y + result.h);
2809 y = MAX (y - result.y, 0);
2810 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE,
2811 (gdouble) y * yscale, NULL);
2814 gst_pad_send_event (peer, event);
2815 gst_object_unref (peer);
2820 gst_xvimagesink_navigation_init (GstNavigationInterface * iface)
2822 iface->send_event = gst_xvimagesink_navigation_send_event;
2826 gst_xvimagesink_set_window_handle (GstXOverlay * overlay, guintptr id)
2828 XID xwindow_id = id;
2829 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2830 GstXWindow *xwindow = NULL;
2832 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
2834 g_mutex_lock (xvimagesink->flow_lock);
2836 /* If we already use that window return */
2837 if (xvimagesink->xwindow && (xwindow_id == xvimagesink->xwindow->win)) {
2838 g_mutex_unlock (xvimagesink->flow_lock);
2842 /* If the element has not initialized the X11 context try to do so */
2843 if (!xvimagesink->xcontext &&
2844 !(xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink))) {
2845 g_mutex_unlock (xvimagesink->flow_lock);
2846 /* we have thrown a GST_ELEMENT_ERROR now */
2850 gst_xvimagesink_update_colorbalance (xvimagesink);
2852 /* Clear image pool as the images are unusable anyway */
2853 gst_xvimagesink_imagepool_clear (xvimagesink);
2855 /* Clear the xvimage */
2856 if (xvimagesink->xvimage) {
2857 gst_xvimage_buffer_free (xvimagesink->xvimage);
2858 xvimagesink->xvimage = NULL;
2861 /* If a window is there already we destroy it */
2862 if (xvimagesink->xwindow) {
2863 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
2864 xvimagesink->xwindow = NULL;
2867 /* If the xid is 0 we go back to an internal window */
2868 if (xwindow_id == 0) {
2869 /* If no width/height caps nego did not happen window will be created
2870 during caps nego then */
2871 if (GST_VIDEO_SINK_WIDTH (xvimagesink)
2872 && GST_VIDEO_SINK_HEIGHT (xvimagesink)) {
2874 gst_xvimagesink_xwindow_new (xvimagesink,
2875 GST_VIDEO_SINK_WIDTH (xvimagesink),
2876 GST_VIDEO_SINK_HEIGHT (xvimagesink));
2879 XWindowAttributes attr;
2881 xwindow = g_new0 (GstXWindow, 1);
2882 xwindow->win = xwindow_id;
2884 /* Set the event we want to receive and create a GC */
2885 g_mutex_lock (xvimagesink->x_lock);
2887 XGetWindowAttributes (xvimagesink->xcontext->disp, xwindow->win, &attr);
2889 xwindow->width = attr.width;
2890 xwindow->height = attr.height;
2891 xwindow->internal = FALSE;
2892 if (!xvimagesink->have_render_rect) {
2893 xvimagesink->render_rect.x = xvimagesink->render_rect.y = 0;
2894 xvimagesink->render_rect.w = attr.width;
2895 xvimagesink->render_rect.h = attr.height;
2897 if (xvimagesink->handle_events) {
2898 XSelectInput (xvimagesink->xcontext->disp, xwindow->win, ExposureMask |
2899 StructureNotifyMask | PointerMotionMask | KeyPressMask |
2903 xwindow->gc = XCreateGC (xvimagesink->xcontext->disp,
2904 xwindow->win, 0, NULL);
2905 g_mutex_unlock (xvimagesink->x_lock);
2909 xvimagesink->xwindow = xwindow;
2911 g_mutex_unlock (xvimagesink->flow_lock);
2915 gst_xvimagesink_expose (GstXOverlay * overlay)
2917 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2919 gst_xvimagesink_xwindow_update_geometry (xvimagesink);
2920 gst_xvimagesink_xvimage_put (xvimagesink, NULL);
2924 gst_xvimagesink_set_event_handling (GstXOverlay * overlay,
2925 gboolean handle_events)
2927 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2929 xvimagesink->handle_events = handle_events;
2931 g_mutex_lock (xvimagesink->flow_lock);
2933 if (G_UNLIKELY (!xvimagesink->xwindow)) {
2934 g_mutex_unlock (xvimagesink->flow_lock);
2938 g_mutex_lock (xvimagesink->x_lock);
2940 if (handle_events) {
2941 if (xvimagesink->xwindow->internal) {
2942 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2943 ExposureMask | StructureNotifyMask | PointerMotionMask |
2944 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
2946 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win,
2947 ExposureMask | StructureNotifyMask | PointerMotionMask |
2948 KeyPressMask | KeyReleaseMask);
2951 XSelectInput (xvimagesink->xcontext->disp, xvimagesink->xwindow->win, 0);
2954 g_mutex_unlock (xvimagesink->x_lock);
2956 g_mutex_unlock (xvimagesink->flow_lock);
2960 gst_xvimagesink_set_render_rectangle (GstXOverlay * overlay, gint x, gint y,
2961 gint width, gint height)
2963 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (overlay);
2965 /* FIXME: how about some locking? */
2966 if (width >= 0 && height >= 0) {
2967 xvimagesink->render_rect.x = x;
2968 xvimagesink->render_rect.y = y;
2969 xvimagesink->render_rect.w = width;
2970 xvimagesink->render_rect.h = height;
2971 xvimagesink->have_render_rect = TRUE;
2973 xvimagesink->render_rect.x = 0;
2974 xvimagesink->render_rect.y = 0;
2975 xvimagesink->render_rect.w = xvimagesink->xwindow->width;
2976 xvimagesink->render_rect.h = xvimagesink->xwindow->height;
2977 xvimagesink->have_render_rect = FALSE;
2982 gst_xvimagesink_xoverlay_init (GstXOverlayClass * iface)
2984 iface->set_window_handle = gst_xvimagesink_set_window_handle;
2985 iface->expose = gst_xvimagesink_expose;
2986 iface->handle_events = gst_xvimagesink_set_event_handling;
2987 iface->set_render_rectangle = gst_xvimagesink_set_render_rectangle;
2990 static const GList *
2991 gst_xvimagesink_colorbalance_list_channels (GstColorBalance * balance)
2993 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
2995 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), NULL);
2997 if (xvimagesink->xcontext)
2998 return xvimagesink->xcontext->channels_list;
3004 gst_xvimagesink_colorbalance_set_value (GstColorBalance * balance,
3005 GstColorBalanceChannel * channel, gint value)
3007 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3009 g_return_if_fail (GST_IS_XVIMAGESINK (xvimagesink));
3010 g_return_if_fail (channel->label != NULL);
3012 xvimagesink->cb_changed = TRUE;
3014 /* Normalize val to [-1000, 1000] */
3015 value = floor (0.5 + -1000 + 2000 * (value - channel->min_value) /
3016 (double) (channel->max_value - channel->min_value));
3018 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3019 xvimagesink->hue = value;
3020 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3021 xvimagesink->saturation = value;
3022 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3023 xvimagesink->contrast = value;
3024 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3025 xvimagesink->brightness = value;
3027 g_warning ("got an unknown channel %s", channel->label);
3031 gst_xvimagesink_update_colorbalance (xvimagesink);
3035 gst_xvimagesink_colorbalance_get_value (GstColorBalance * balance,
3036 GstColorBalanceChannel * channel)
3038 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (balance);
3041 g_return_val_if_fail (GST_IS_XVIMAGESINK (xvimagesink), 0);
3042 g_return_val_if_fail (channel->label != NULL, 0);
3044 if (g_ascii_strcasecmp (channel->label, "XV_HUE") == 0) {
3045 value = xvimagesink->hue;
3046 } else if (g_ascii_strcasecmp (channel->label, "XV_SATURATION") == 0) {
3047 value = xvimagesink->saturation;
3048 } else if (g_ascii_strcasecmp (channel->label, "XV_CONTRAST") == 0) {
3049 value = xvimagesink->contrast;
3050 } else if (g_ascii_strcasecmp (channel->label, "XV_BRIGHTNESS") == 0) {
3051 value = xvimagesink->brightness;
3053 g_warning ("got an unknown channel %s", channel->label);
3056 /* Normalize val to [channel->min_value, channel->max_value] */
3057 value = channel->min_value + (channel->max_value - channel->min_value) *
3058 (value + 1000) / 2000;
3064 gst_xvimagesink_colorbalance_init (GstColorBalanceClass * iface)
3066 GST_COLOR_BALANCE_TYPE (iface) = GST_COLOR_BALANCE_HARDWARE;
3067 iface->list_channels = gst_xvimagesink_colorbalance_list_channels;
3068 iface->set_value = gst_xvimagesink_colorbalance_set_value;
3069 iface->get_value = gst_xvimagesink_colorbalance_get_value;
3072 static const GList *
3073 gst_xvimagesink_probe_get_properties (GstPropertyProbe * probe)
3075 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
3076 static GList *list = NULL;
3079 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
3081 g_list_append (list, g_object_class_find_property (klass,
3082 "autopaint-colorkey"));
3084 g_list_append (list, g_object_class_find_property (klass,
3087 g_list_append (list, g_object_class_find_property (klass, "colorkey"));
3094 gst_xvimagesink_probe_probe_property (GstPropertyProbe * probe,
3095 guint prop_id, const GParamSpec * pspec)
3097 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3101 case PROP_AUTOPAINT_COLORKEY:
3102 case PROP_DOUBLE_BUFFER:
3104 GST_DEBUG_OBJECT (xvimagesink,
3105 "probing device list and get capabilities");
3106 if (!xvimagesink->xcontext) {
3107 GST_DEBUG_OBJECT (xvimagesink, "generating xcontext");
3108 xvimagesink->xcontext = gst_xvimagesink_xcontext_get (xvimagesink);
3112 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3118 gst_xvimagesink_probe_needs_probe (GstPropertyProbe * probe,
3119 guint prop_id, const GParamSpec * pspec)
3121 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3122 gboolean ret = FALSE;
3126 case PROP_AUTOPAINT_COLORKEY:
3127 case PROP_DOUBLE_BUFFER:
3129 if (xvimagesink->xcontext != NULL) {
3136 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3143 static GValueArray *
3144 gst_xvimagesink_probe_get_values (GstPropertyProbe * probe,
3145 guint prop_id, const GParamSpec * pspec)
3147 GstXvImageSink *xvimagesink = GST_XVIMAGESINK (probe);
3148 GValueArray *array = NULL;
3150 if (G_UNLIKELY (!xvimagesink->xcontext)) {
3151 GST_WARNING_OBJECT (xvimagesink, "we don't have any xcontext, can't "
3160 GValue value = { 0 };
3162 array = g_value_array_new (xvimagesink->xcontext->nb_adaptors);
3163 g_value_init (&value, G_TYPE_STRING);
3165 for (i = 0; i < xvimagesink->xcontext->nb_adaptors; i++) {
3166 gchar *adaptor_id_s = g_strdup_printf ("%u", i);
3168 g_value_set_string (&value, adaptor_id_s);
3169 g_value_array_append (array, &value);
3170 g_free (adaptor_id_s);
3172 g_value_unset (&value);
3175 case PROP_AUTOPAINT_COLORKEY:
3176 if (xvimagesink->have_autopaint_colorkey) {
3177 GValue value = { 0 };
3179 array = g_value_array_new (2);
3180 g_value_init (&value, G_TYPE_BOOLEAN);
3181 g_value_set_boolean (&value, FALSE);
3182 g_value_array_append (array, &value);
3183 g_value_set_boolean (&value, TRUE);
3184 g_value_array_append (array, &value);
3185 g_value_unset (&value);
3188 case PROP_DOUBLE_BUFFER:
3189 if (xvimagesink->have_double_buffer) {
3190 GValue value = { 0 };
3192 array = g_value_array_new (2);
3193 g_value_init (&value, G_TYPE_BOOLEAN);
3194 g_value_set_boolean (&value, FALSE);
3195 g_value_array_append (array, &value);
3196 g_value_set_boolean (&value, TRUE);
3197 g_value_array_append (array, &value);
3198 g_value_unset (&value);
3202 if (xvimagesink->have_colorkey) {
3203 GValue value = { 0 };
3205 array = g_value_array_new (1);
3206 g_value_init (&value, GST_TYPE_INT_RANGE);
3207 gst_value_set_int_range (&value, 0, 0xffffff);
3208 g_value_array_append (array, &value);
3209 g_value_unset (&value);
3213 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
3222 gst_xvimagesink_property_probe_interface_init (GstPropertyProbeInterface *
3225 iface->get_properties = gst_xvimagesink_probe_get_properties;
3226 iface->probe_property = gst_xvimagesink_probe_probe_property;
3227 iface->needs_probe = gst_xvimagesink_probe_needs_probe;
3228 iface->get_values = gst_xvimagesink_probe_get_values;
3231 /* =========================================== */
3233 /* Init & Class init */
3235 /* =========================================== */
3238 gst_xvimagesink_set_property (GObject * object, guint prop_id,
3239 const GValue * value, GParamSpec * pspec)
3241 GstXvImageSink *xvimagesink;
3243 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3245 xvimagesink = GST_XVIMAGESINK (object);
3249 xvimagesink->hue = g_value_get_int (value);
3250 xvimagesink->cb_changed = TRUE;
3251 gst_xvimagesink_update_colorbalance (xvimagesink);
3254 xvimagesink->contrast = g_value_get_int (value);
3255 xvimagesink->cb_changed = TRUE;
3256 gst_xvimagesink_update_colorbalance (xvimagesink);
3258 case PROP_BRIGHTNESS:
3259 xvimagesink->brightness = g_value_get_int (value);
3260 xvimagesink->cb_changed = TRUE;
3261 gst_xvimagesink_update_colorbalance (xvimagesink);
3263 case PROP_SATURATION:
3264 xvimagesink->saturation = g_value_get_int (value);
3265 xvimagesink->cb_changed = TRUE;
3266 gst_xvimagesink_update_colorbalance (xvimagesink);
3269 xvimagesink->display_name = g_strdup (g_value_get_string (value));
3271 case PROP_SYNCHRONOUS:
3272 xvimagesink->synchronous = g_value_get_boolean (value);
3273 if (xvimagesink->xcontext) {
3274 XSynchronize (xvimagesink->xcontext->disp, xvimagesink->synchronous);
3275 GST_DEBUG_OBJECT (xvimagesink, "XSynchronize called with %s",
3276 xvimagesink->synchronous ? "TRUE" : "FALSE");
3279 case PROP_PIXEL_ASPECT_RATIO:
3280 g_free (xvimagesink->par);
3281 xvimagesink->par = g_new0 (GValue, 1);
3282 g_value_init (xvimagesink->par, GST_TYPE_FRACTION);
3283 if (!g_value_transform (value, xvimagesink->par)) {
3284 g_warning ("Could not transform string to aspect ratio");
3285 gst_value_set_fraction (xvimagesink->par, 1, 1);
3287 GST_DEBUG_OBJECT (xvimagesink, "set PAR to %d/%d",
3288 gst_value_get_fraction_numerator (xvimagesink->par),
3289 gst_value_get_fraction_denominator (xvimagesink->par));
3291 case PROP_FORCE_ASPECT_RATIO:
3292 xvimagesink->keep_aspect = g_value_get_boolean (value);
3294 case PROP_HANDLE_EVENTS:
3295 gst_xvimagesink_set_event_handling (GST_X_OVERLAY (xvimagesink),
3296 g_value_get_boolean (value));
3297 gst_xvimagesink_manage_event_thread (xvimagesink);
3300 xvimagesink->adaptor_no = atoi (g_value_get_string (value));
3302 case PROP_HANDLE_EXPOSE:
3303 xvimagesink->handle_expose = g_value_get_boolean (value);
3304 gst_xvimagesink_manage_event_thread (xvimagesink);
3306 case PROP_DOUBLE_BUFFER:
3307 xvimagesink->double_buffer = g_value_get_boolean (value);
3309 case PROP_AUTOPAINT_COLORKEY:
3310 xvimagesink->autopaint_colorkey = g_value_get_boolean (value);
3313 xvimagesink->colorkey = g_value_get_int (value);
3315 case PROP_DRAW_BORDERS:
3316 xvimagesink->draw_borders = g_value_get_boolean (value);
3319 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3325 gst_xvimagesink_get_property (GObject * object, guint prop_id,
3326 GValue * value, GParamSpec * pspec)
3328 GstXvImageSink *xvimagesink;
3330 g_return_if_fail (GST_IS_XVIMAGESINK (object));
3332 xvimagesink = GST_XVIMAGESINK (object);
3336 g_value_set_int (value, xvimagesink->hue);
3339 g_value_set_int (value, xvimagesink->contrast);
3341 case PROP_BRIGHTNESS:
3342 g_value_set_int (value, xvimagesink->brightness);
3344 case PROP_SATURATION:
3345 g_value_set_int (value, xvimagesink->saturation);
3348 g_value_set_string (value, xvimagesink->display_name);
3350 case PROP_SYNCHRONOUS:
3351 g_value_set_boolean (value, xvimagesink->synchronous);
3353 case PROP_PIXEL_ASPECT_RATIO:
3354 if (xvimagesink->par)
3355 g_value_transform (xvimagesink->par, value);
3357 case PROP_FORCE_ASPECT_RATIO:
3358 g_value_set_boolean (value, xvimagesink->keep_aspect);
3360 case PROP_HANDLE_EVENTS:
3361 g_value_set_boolean (value, xvimagesink->handle_events);
3365 char *adaptor_no_s = g_strdup_printf ("%u", xvimagesink->adaptor_no);
3367 g_value_set_string (value, adaptor_no_s);
3368 g_free (adaptor_no_s);
3371 case PROP_DEVICE_NAME:
3372 if (xvimagesink->xcontext && xvimagesink->xcontext->adaptors) {
3373 g_value_set_string (value,
3374 xvimagesink->xcontext->adaptors[xvimagesink->adaptor_no]);
3376 g_value_set_string (value, NULL);
3379 case PROP_HANDLE_EXPOSE:
3380 g_value_set_boolean (value, xvimagesink->handle_expose);
3382 case PROP_DOUBLE_BUFFER:
3383 g_value_set_boolean (value, xvimagesink->double_buffer);
3385 case PROP_AUTOPAINT_COLORKEY:
3386 g_value_set_boolean (value, xvimagesink->autopaint_colorkey);
3389 g_value_set_int (value, xvimagesink->colorkey);
3391 case PROP_DRAW_BORDERS:
3392 g_value_set_boolean (value, xvimagesink->draw_borders);
3394 case PROP_WINDOW_WIDTH:
3395 if (xvimagesink->xwindow)
3396 g_value_set_uint64 (value, xvimagesink->xwindow->width);
3398 g_value_set_uint64 (value, 0);
3400 case PROP_WINDOW_HEIGHT:
3401 if (xvimagesink->xwindow)
3402 g_value_set_uint64 (value, xvimagesink->xwindow->height);
3404 g_value_set_uint64 (value, 0);
3407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3413 gst_xvimagesink_reset (GstXvImageSink * xvimagesink)
3417 GST_OBJECT_LOCK (xvimagesink);
3418 xvimagesink->running = FALSE;
3419 /* grab thread and mark it as NULL */
3420 thread = xvimagesink->event_thread;
3421 xvimagesink->event_thread = NULL;
3422 GST_OBJECT_UNLOCK (xvimagesink);
3424 /* invalidate the pool, current allocations continue, new buffer_alloc fails
3425 * with wrong_state */
3426 g_mutex_lock (xvimagesink->pool_lock);
3427 xvimagesink->pool_invalid = TRUE;
3428 g_mutex_unlock (xvimagesink->pool_lock);
3430 /* Wait for our event thread to finish before we clean up our stuff. */
3432 g_thread_join (thread);
3434 if (xvimagesink->cur_image) {
3435 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->cur_image));
3436 xvimagesink->cur_image = NULL;
3438 if (xvimagesink->xvimage) {
3439 gst_buffer_unref (GST_BUFFER_CAST (xvimagesink->xvimage));
3440 xvimagesink->xvimage = NULL;
3443 gst_xvimagesink_imagepool_clear (xvimagesink);
3445 if (xvimagesink->xwindow) {
3446 gst_xvimagesink_xwindow_clear (xvimagesink, xvimagesink->xwindow);
3447 gst_xvimagesink_xwindow_destroy (xvimagesink, xvimagesink->xwindow);
3448 xvimagesink->xwindow = NULL;
3451 xvimagesink->render_rect.x = xvimagesink->render_rect.y =
3452 xvimagesink->render_rect.w = xvimagesink->render_rect.h = 0;
3453 xvimagesink->have_render_rect = FALSE;
3455 gst_xvimagesink_xcontext_clear (xvimagesink);
3458 /* Finalize is called only once, dispose can be called multiple times.
3459 * We use mutexes and don't reset stuff to NULL here so let's register
3462 gst_xvimagesink_finalize (GObject * object)
3464 GstXvImageSink *xvimagesink;
3466 xvimagesink = GST_XVIMAGESINK (object);
3468 gst_xvimagesink_reset (xvimagesink);
3470 if (xvimagesink->display_name) {
3471 g_free (xvimagesink->display_name);
3472 xvimagesink->display_name = NULL;
3475 if (xvimagesink->par) {
3476 g_free (xvimagesink->par);
3477 xvimagesink->par = NULL;
3479 if (xvimagesink->x_lock) {
3480 g_mutex_free (xvimagesink->x_lock);
3481 xvimagesink->x_lock = NULL;
3483 if (xvimagesink->flow_lock) {
3484 g_mutex_free (xvimagesink->flow_lock);
3485 xvimagesink->flow_lock = NULL;
3487 if (xvimagesink->pool_lock) {
3488 g_mutex_free (xvimagesink->pool_lock);
3489 xvimagesink->pool_lock = NULL;
3492 g_free (xvimagesink->media_title);
3494 G_OBJECT_CLASS (parent_class)->finalize (object);
3498 gst_xvimagesink_init (GstXvImageSink * xvimagesink,
3499 GstXvImageSinkClass * xvimagesinkclass)
3501 xvimagesink->display_name = NULL;
3502 xvimagesink->adaptor_no = 0;
3503 xvimagesink->xcontext = NULL;
3504 xvimagesink->xwindow = NULL;
3505 xvimagesink->xvimage = NULL;
3506 xvimagesink->cur_image = NULL;
3508 xvimagesink->hue = xvimagesink->saturation = 0;
3509 xvimagesink->contrast = xvimagesink->brightness = 0;
3510 xvimagesink->cb_changed = FALSE;
3512 xvimagesink->fps_n = 0;
3513 xvimagesink->fps_d = 0;
3514 xvimagesink->video_width = 0;
3515 xvimagesink->video_height = 0;
3517 xvimagesink->x_lock = g_mutex_new ();
3518 xvimagesink->flow_lock = g_mutex_new ();
3520 xvimagesink->image_pool = NULL;
3521 xvimagesink->pool_lock = g_mutex_new ();
3523 xvimagesink->synchronous = FALSE;
3524 xvimagesink->double_buffer = TRUE;
3525 xvimagesink->running = FALSE;
3526 xvimagesink->keep_aspect = FALSE;
3527 xvimagesink->handle_events = TRUE;
3528 xvimagesink->par = NULL;
3529 xvimagesink->handle_expose = TRUE;
3530 xvimagesink->autopaint_colorkey = TRUE;
3532 /* on 16bit displays this becomes r,g,b = 1,2,3
3533 * on 24bit displays this becomes r,g,b = 8,8,16
3534 * as a port atom value
3536 xvimagesink->colorkey = (8 << 16) | (8 << 8) | 16;
3537 xvimagesink->draw_borders = TRUE;
3541 gst_xvimagesink_base_init (gpointer g_class)
3543 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
3545 gst_element_class_set_details_simple (element_class,
3546 "Video sink", "Sink/Video",
3547 "A Xv based videosink", "Julien Moutte <julien@moutte.net>");
3549 gst_element_class_add_static_pad_template (element_class,
3550 &gst_xvimagesink_sink_template_factory);
3554 gst_xvimagesink_class_init (GstXvImageSinkClass * klass)
3556 GObjectClass *gobject_class;
3557 GstElementClass *gstelement_class;
3558 GstBaseSinkClass *gstbasesink_class;
3559 GstVideoSinkClass *videosink_class;
3561 gobject_class = (GObjectClass *) klass;
3562 gstelement_class = (GstElementClass *) klass;
3563 gstbasesink_class = (GstBaseSinkClass *) klass;
3564 videosink_class = (GstVideoSinkClass *) klass;
3566 gobject_class->set_property = gst_xvimagesink_set_property;
3567 gobject_class->get_property = gst_xvimagesink_get_property;
3569 g_object_class_install_property (gobject_class, PROP_CONTRAST,
3570 g_param_spec_int ("contrast", "Contrast", "The contrast of the video",
3571 -1000, 1000, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3572 g_object_class_install_property (gobject_class, PROP_BRIGHTNESS,
3573 g_param_spec_int ("brightness", "Brightness",
3574 "The brightness of the video", -1000, 1000, 0,
3575 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3576 g_object_class_install_property (gobject_class, PROP_HUE,
3577 g_param_spec_int ("hue", "Hue", "The hue of the video", -1000, 1000, 0,
3578 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3579 g_object_class_install_property (gobject_class, PROP_SATURATION,
3580 g_param_spec_int ("saturation", "Saturation",
3581 "The saturation of the video", -1000, 1000, 0,
3582 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3583 g_object_class_install_property (gobject_class, PROP_DISPLAY,
3584 g_param_spec_string ("display", "Display", "X Display name", NULL,
3585 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3586 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
3587 g_param_spec_boolean ("synchronous", "Synchronous",
3588 "When enabled, runs the X display in synchronous mode. "
3589 "(unrelated to A/V sync, used only for debugging)", FALSE,
3590 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3591 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
3592 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
3593 "The pixel aspect ratio of the device", "1/1",
3594 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3595 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
3596 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
3597 "When enabled, scaling will respect original aspect ratio", FALSE,
3598 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3599 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
3600 g_param_spec_boolean ("handle-events", "Handle XEvents",
3601 "When enabled, XEvents will be selected and handled", TRUE,
3602 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3603 g_object_class_install_property (gobject_class, PROP_DEVICE,
3604 g_param_spec_string ("device", "Adaptor number",
3605 "The number of the video adaptor", "0",
3606 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3607 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
3608 g_param_spec_string ("device-name", "Adaptor name",
3609 "The name of the video adaptor", NULL,
3610 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3612 * GstXvImageSink:handle-expose
3614 * When enabled, the current frame will always be drawn in response to X
3619 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
3620 g_param_spec_boolean ("handle-expose", "Handle expose",
3622 "the current frame will always be drawn in response to X Expose "
3623 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3626 * GstXvImageSink:double-buffer
3628 * Whether to double-buffer the output.
3632 g_object_class_install_property (gobject_class, PROP_DOUBLE_BUFFER,
3633 g_param_spec_boolean ("double-buffer", "Double-buffer",
3634 "Whether to double-buffer the output", TRUE,
3635 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3637 * GstXvImageSink:autopaint-colorkey
3639 * Whether to autofill overlay with colorkey
3643 g_object_class_install_property (gobject_class, PROP_AUTOPAINT_COLORKEY,
3644 g_param_spec_boolean ("autopaint-colorkey", "Autofill with colorkey",
3645 "Whether to autofill overlay with colorkey", TRUE,
3646 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3648 * GstXvImageSink:colorkey
3650 * Color to use for the overlay mask.
3654 g_object_class_install_property (gobject_class, PROP_COLORKEY,
3655 g_param_spec_int ("colorkey", "Colorkey",
3656 "Color to use for the overlay mask", G_MININT, G_MAXINT, 0,
3657 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3660 * GstXvImageSink:draw-borders
3662 * Draw black borders when using GstXvImageSink:force-aspect-ratio to fill
3663 * unused parts of the video area.
3667 g_object_class_install_property (gobject_class, PROP_DRAW_BORDERS,
3668 g_param_spec_boolean ("draw-borders", "Colorkey",
3669 "Draw black borders to fill unused area in force-aspect-ratio mode",
3670 TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
3673 * GstXvImageSink:window-width
3675 * Actual width of the video window.
3679 g_object_class_install_property (gobject_class, PROP_WINDOW_WIDTH,
3680 g_param_spec_uint64 ("window-width", "window-width",
3681 "Width of the window", 0, G_MAXUINT64, 0,
3682 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3685 * GstXvImageSink:window-height
3687 * Actual height of the video window.
3691 g_object_class_install_property (gobject_class, PROP_WINDOW_HEIGHT,
3692 g_param_spec_uint64 ("window-height", "window-height",
3693 "Height of the window", 0, G_MAXUINT64, 0,
3694 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
3696 gobject_class->finalize = gst_xvimagesink_finalize;
3698 gstelement_class->change_state =
3699 GST_DEBUG_FUNCPTR (gst_xvimagesink_change_state);
3701 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_getcaps);
3702 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_xvimagesink_setcaps);
3703 gstbasesink_class->buffer_alloc =
3704 GST_DEBUG_FUNCPTR (gst_xvimagesink_buffer_alloc);
3705 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_xvimagesink_get_times);
3706 gstbasesink_class->event = GST_DEBUG_FUNCPTR (gst_xvimagesink_event);
3708 videosink_class->show_frame = GST_DEBUG_FUNCPTR (gst_xvimagesink_show_frame);
3711 /* ============================================================= */
3713 /* Public Methods */
3715 /* ============================================================= */
3717 /* =========================================== */
3719 /* Object typing & Creation */
3721 /* =========================================== */
3723 gst_xvimagesink_init_interfaces (GType type)
3725 static const GInterfaceInfo iface_info = {
3726 (GInterfaceInitFunc) gst_xvimagesink_interface_init,
3730 static const GInterfaceInfo navigation_info = {
3731 (GInterfaceInitFunc) gst_xvimagesink_navigation_init,
3735 static const GInterfaceInfo overlay_info = {
3736 (GInterfaceInitFunc) gst_xvimagesink_xoverlay_init,
3740 static const GInterfaceInfo colorbalance_info = {
3741 (GInterfaceInitFunc) gst_xvimagesink_colorbalance_init,
3745 static const GInterfaceInfo propertyprobe_info = {
3746 (GInterfaceInitFunc) gst_xvimagesink_property_probe_interface_init,
3751 g_type_add_interface_static (type,
3752 GST_TYPE_IMPLEMENTS_INTERFACE, &iface_info);
3753 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &navigation_info);
3754 g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &overlay_info);
3755 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE,
3756 &colorbalance_info);
3757 g_type_add_interface_static (type, GST_TYPE_PROPERTY_PROBE,
3758 &propertyprobe_info);
3760 /* register type and create class in a more safe place instead of at
3761 * runtime since the type registration and class creation is not
3763 g_type_class_ref (gst_xvimage_buffer_get_type ());
3767 plugin_init (GstPlugin * plugin)
3769 if (!gst_element_register (plugin, "xvimagesink",
3770 GST_RANK_PRIMARY, GST_TYPE_XVIMAGESINK))
3773 GST_DEBUG_CATEGORY_INIT (gst_debug_xvimagesink, "xvimagesink", 0,
3774 "xvimagesink element");
3775 GST_DEBUG_CATEGORY_GET (GST_CAT_PERFORMANCE, "GST_PERFORMANCE");
3780 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
3783 "XFree86 video output plugin using Xv extension",
3784 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)