2 * Copyright (C) <2005> Julien Moutte <julien@moutte.net>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-ximagesink
25 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
26 * display. This element can receive a Window ID from the application through
27 * the XOverlay interface and will then render video frames in this drawable.
28 * If no Window ID was provided by the application, the element will create its
29 * own internal window and render into it.
31 * <title>Scaling</title>
33 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
34 * reverse caps negotiation to try to get scaled video frames for the drawable.
35 * This is accomplished by asking the peer pad if it accepts some different caps
36 * which in most cases implies that there is a scaling element in the pipeline,
37 * or that an element generating the video frames can generate them with a
38 * different geometry. This mechanism is handled during buffer allocations, for
39 * each allocation request the video sink will check the drawable geometry, look
41 * <link linkend="GstXImageSink--force-aspect-ratio">force-aspect-ratio</link>
42 * property, calculate the geometry of desired video frames and then check that
43 * the peer pad accept those new caps. If it does it will then allocate a buffer
44 * in video memory with this new geometry and return it with the new caps.
46 * <title>Events</title>
48 * XImageSink creates a thread to handle events coming from the drawable. There
49 * are several kind of events that can be grouped in 2 big categories: input
50 * events and window state related events. Input events will be translated to
51 * navigation events and pushed upstream for other elements to react on them.
52 * This includes events such as pointer moves, key press/release, clicks etc...
53 * Other events are used to handle the drawable appearance even when the data
54 * is not flowing (GST_STATE_PAUSED). That means that even when the element is
55 * paused, it will receive expose events from the drawable and draw the latest
56 * frame with correct borders/aspect-ratio.
58 * <title>Pixel aspect ratio</title>
60 * When changing state to GST_STATE_READY, XImageSink will open a connection to
61 * the display specified in the
62 * <link linkend="GstXImageSink--display">display</link> property or the default
63 * display if nothing specified. Once this connection is open it will inspect
64 * the display configuration including the physical display geometry and
65 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
66 * video sink will set the calculated pixel aspect ratio on the caps to make
67 * sure that incoming video frames will have the correct pixel aspect ratio for
68 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
69 * then possible to enforce a specific pixel aspect ratio using the
70 * <link linkend="GstXImageSink--pixel-aspect-ratio">pixel-aspect-ratio</link>
73 * <title>Examples</title>
75 * Here is a simple pipeline to test reverse negotiation :
77 * gst-launch -v videotestsrc ! queue ! ximagesink
79 * When the test video signal appears you can resize the window and see that
80 * scaled buffers of the desired size are going to arrive with a short delay.
81 * This illustrates how buffers of desired size are allocated along the way.
82 * If you take away the queue, scaling will happen almost immediately.
85 * Here is a simple pipeline to test navigation events :
87 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
89 * While moving the mouse pointer over the test signal you will see a black box
90 * following the mouse pointer. If you press the mouse button somewhere on the
91 * video and release it somewhere else a green box will appear where you pressed
92 * the button and a red one where you released it. (The navigationtest element
93 * is part of gst-plugins-good.)
96 * Here is a simple pipeline to test pixel aspect ratio :
98 * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
100 * This is faking a 4/3 pixel aspect ratio caps on video frames produced by
101 * videotestsrc, in most cases the pixel aspect ratio of the display will be
102 * 1/1. This means that videoscale will have to do the scaling to convert
103 * incoming frames to a size that will match the display pixel aspect ratio
104 * (from 320x240 to 320x180 in this case). Note that you might have to escape
105 * some characters for your shell like '\(fraction\)'.
115 #include <gst/interfaces/navigation.h>
116 #include <gst/interfaces/xoverlay.h>
119 #include "ximagesink.h"
121 /* Debugging category */
122 #include <gst/gstinfo.h>
124 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
125 #define GST_CAT_DEFAULT gst_debug_ximagesink
130 unsigned long functions;
131 unsigned long decorations;
133 unsigned long status;
135 MotifWmHints, MwmHints;
137 #define MWM_HINTS_DECORATIONS (1L << 1)
139 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
140 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
141 GstXImageBuffer * ximage);
142 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
143 GstXWindow * xwindow);
144 static void gst_ximagesink_expose (GstXOverlay * overlay);
146 /* ElementFactory information */
147 static const GstElementDetails gst_ximagesink_details =
148 GST_ELEMENT_DETAILS ("Video sink",
150 "A standard X based videosink",
151 "Julien Moutte <julien@moutte.net>");
153 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
154 GST_STATIC_PAD_TEMPLATE ("sink",
157 GST_STATIC_CAPS ("video/x-raw-rgb, "
158 "framerate = (fraction) [ 0, MAX ], "
159 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
167 PROP_PIXEL_ASPECT_RATIO,
168 PROP_FORCE_ASPECT_RATIO,
173 static GstVideoSinkClass *parent_class = NULL;
175 /* ============================================================= */
177 /* Private Methods */
179 /* ============================================================= */
183 static GstBufferClass *ximage_buffer_parent_class = NULL;
185 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
187 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
188 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
189 #define GST_XIMAGE_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
191 /* So some words about GstMiniObject, this is pretty messy...
192 GstMiniObject does not use the standard finalizing of GObjects, you are
193 supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
194 which will handle its own refcount system and call gst_mini_object_free.
195 gst_mini_object_free will call the class finalize method which is not the
196 one from GObject, after calling this finalize method it will free the object
197 instance for you if the refcount is still 0 so you should not chain up */
199 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
201 GstXImageSink *ximagesink = NULL;
202 gboolean recycled = FALSE;
205 g_return_if_fail (ximage != NULL);
207 ximagesink = ximage->ximagesink;
208 if (G_UNLIKELY (ximagesink == NULL)) {
209 GST_WARNING_OBJECT (ximagesink, "no sink found");
213 GST_OBJECT_LOCK (ximagesink);
214 running = ximagesink->running;
215 GST_OBJECT_UNLOCK (ximagesink);
217 if (running == FALSE) {
218 /* If the sink is shutting down, need to clear the image */
219 GST_DEBUG_OBJECT (ximagesink,
220 "destroy image %p because the sink is shutting down", ximage);
221 gst_ximagesink_ximage_destroy (ximagesink, ximage);
222 } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
223 (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
224 /* If our geometry changed we can't reuse that image. */
225 GST_DEBUG_OBJECT (ximagesink,
226 "destroy image %p as its size changed %dx%d vs current %dx%d",
227 ximage, ximage->width, ximage->height,
228 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
229 gst_ximagesink_ximage_destroy (ximagesink, ximage);
231 /* In that case we can reuse the image and add it to our image pool. */
232 GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
233 /* need to increment the refcount again to recycle */
234 gst_buffer_ref (GST_BUFFER_CAST (ximage));
235 g_mutex_lock (ximagesink->pool_lock);
236 ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
237 g_mutex_unlock (ximagesink->pool_lock);
242 GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->
243 finalize (GST_MINI_OBJECT (ximage));
250 gst_ximage_buffer_free (GstXImageBuffer * ximage)
252 /* make sure it is not recycled */
255 gst_buffer_unref (GST_BUFFER_CAST (ximage));
259 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
262 ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
263 ximage_buffer->SHMInfo.shmid = -1;
268 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
270 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
272 ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
274 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
275 gst_ximage_buffer_finalize;
279 gst_ximage_buffer_get_type (void)
281 static GType _gst_ximage_buffer_type;
283 if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
284 static const GTypeInfo ximage_buffer_info = {
285 sizeof (GstBufferClass),
288 gst_ximage_buffer_class_init,
291 sizeof (GstXImageBuffer),
293 (GInstanceInitFunc) gst_ximage_buffer_init,
296 _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
297 "GstXImageBuffer", &ximage_buffer_info, 0);
299 return _gst_ximage_buffer_type;
304 static gboolean error_caught = FALSE;
307 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
309 char error_msg[1024];
311 XGetErrorText (display, xevent->error_code, error_msg, 1024);
312 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
317 #ifdef HAVE_XSHM /* Check that XShm calls actually work */
320 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
321 GstXContext * xcontext)
324 XShmSegmentInfo SHMInfo;
326 int (*handler) (Display *, XErrorEvent *);
327 gboolean result = FALSE;
328 gboolean did_attach = FALSE;
330 g_return_val_if_fail (xcontext != NULL, FALSE);
332 /* Sync to ensure any older errors are already processed */
333 XSync (xcontext->disp, FALSE);
335 /* Set defaults so we don't free these later unnecessarily */
336 SHMInfo.shmaddr = ((void *) -1);
339 /* Setting an error handler to catch failure */
340 error_caught = FALSE;
341 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
343 /* Trying to create a 1x1 ximage */
344 GST_DEBUG ("XShmCreateImage of 1x1");
346 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
347 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
349 /* Might cause an error, sync to ensure it is noticed */
350 XSync (xcontext->disp, FALSE);
351 if (!ximage || error_caught) {
352 GST_WARNING ("could not XShmCreateImage a 1x1 image");
355 size = ximage->height * ximage->bytes_per_line;
357 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
358 if (SHMInfo.shmid == -1) {
359 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
364 SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
365 if (SHMInfo.shmaddr == ((void *) -1)) {
366 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
367 /* Clean up shm seg */
368 shmctl (SHMInfo.shmid, IPC_RMID, 0);
372 /* Delete the shared memory segment as soon as we manage to attach.
373 * This way, it will be deleted as soon as we detach later, and not
374 * leaked if we crash. */
375 shmctl (SHMInfo.shmid, IPC_RMID, 0);
377 ximage->data = SHMInfo.shmaddr;
378 SHMInfo.readOnly = FALSE;
380 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
381 GST_WARNING ("Failed to XShmAttach");
385 /* Sync to ensure we see any errors we caused */
386 XSync (xcontext->disp, FALSE);
390 /* store whether we succeeded in result */
395 /* Sync to ensure we swallow any errors we caused and reset error_caught */
396 XSync (xcontext->disp, FALSE);
397 error_caught = FALSE;
398 XSetErrorHandler (handler);
401 XShmDetach (xcontext->disp, &SHMInfo);
402 XSync (xcontext->disp, FALSE);
404 if (SHMInfo.shmaddr != ((void *) -1))
405 shmdt (SHMInfo.shmaddr);
407 XDestroyImage (ximage);
410 #endif /* HAVE_XSHM */
412 /* This function handles GstXImageBuffer creation depending on XShm availability */
413 static GstXImageBuffer *
414 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
416 GstXImageBuffer *ximage = NULL;
417 GstStructure *structure = NULL;
418 gboolean succeeded = FALSE;
419 int (*handler) (Display *, XErrorEvent *);
421 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
423 ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
425 structure = gst_caps_get_structure (caps, 0);
427 if (!gst_structure_get_int (structure, "width", &ximage->width) ||
428 !gst_structure_get_int (structure, "height", &ximage->height)) {
429 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
432 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
433 ximage->width, ximage->height);
435 g_mutex_lock (ximagesink->x_lock);
437 /* Setting an error handler to catch failure */
438 error_caught = FALSE;
439 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
442 if (ximagesink->xcontext->use_xshm) {
443 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
444 ximagesink->xcontext->visual,
445 ximagesink->xcontext->depth,
446 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
447 if (!ximage->ximage || error_caught) {
448 g_mutex_unlock (ximagesink->x_lock);
449 /* Reset error handler */
450 error_caught = FALSE;
451 XSetErrorHandler (handler);
453 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
454 ("Failed to create output image buffer of %dx%d pixels",
455 ximage->width, ximage->height),
456 ("could not XShmCreateImage a %dx%d image",
457 ximage->width, ximage->height));
461 /* we have to use the returned bytes_per_line for our shm size */
462 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
463 GST_LOG_OBJECT (ximagesink,
464 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
465 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
467 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
469 if (ximage->SHMInfo.shmid == -1) {
470 g_mutex_unlock (ximagesink->x_lock);
471 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
472 ("Failed to create output image buffer of %dx%d pixels",
473 ximage->width, ximage->height),
474 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
479 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
480 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
481 g_mutex_unlock (ximagesink->x_lock);
482 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
483 ("Failed to create output image buffer of %dx%d pixels",
484 ximage->width, ximage->height),
485 ("Failed to shmat: %s", g_strerror (errno)));
486 /* Clean up the shared memory segment */
487 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
491 /* Now that we've attached, we can delete the shared memory segment.
492 * This way, it will be deleted as soon as we detach later, and not
493 * leaked if we crash. */
494 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
496 ximage->ximage->data = ximage->SHMInfo.shmaddr;
497 ximage->SHMInfo.readOnly = FALSE;
499 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
500 g_mutex_unlock (ximagesink->x_lock);
501 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
502 ("Failed to create output image buffer of %dx%d pixels",
503 ximage->width, ximage->height), ("Failed to XShmAttach"));
507 XSync (ximagesink->xcontext->disp, FALSE);
509 #endif /* HAVE_XSHM */
513 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
514 ximagesink->xcontext->visual,
515 ximagesink->xcontext->depth,
517 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
518 if (!ximage->ximage || error_caught) {
519 g_mutex_unlock (ximagesink->x_lock);
520 /* Reset error handler */
521 error_caught = FALSE;
522 XSetErrorHandler (handler);
524 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
525 ("Failed to create output image buffer of %dx%d pixels",
526 ximage->width, ximage->height),
527 ("could not XCreateImage a %dx%d image",
528 ximage->width, ximage->height));
532 /* upstream will assume that rowstrides are multiples of 4, but this
533 * doesn't always seem to be the case with XCreateImage() */
534 if ((ximage->ximage->bytes_per_line % 4) != 0) {
535 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
539 /* we have to use the returned bytes_per_line for our image size */
540 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
542 /* alloc a bit more for unexpected strides to avoid crashes upstream.
543 * FIXME: if we get an unrounded stride, the image will be displayed
544 * distorted, since all upstream elements assume a rounded stride */
546 GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
547 ximage->ximage->height;
548 ximage->ximage->data = g_malloc (allocsize);
549 GST_LOG_OBJECT (ximagesink,
550 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
551 "stride %d", ximage->size, allocsize, ximage->width,
552 ximage->ximage->bytes_per_line);
554 XSync (ximagesink->xcontext->disp, FALSE);
557 /* Reset error handler */
558 error_caught = FALSE;
559 XSetErrorHandler (handler);
563 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
564 GST_BUFFER_SIZE (ximage) = ximage->size;
566 /* Keep a ref to our sink */
567 ximage->ximagesink = gst_object_ref (ximagesink);
569 g_mutex_unlock (ximagesink->x_lock);
572 gst_ximage_buffer_free (ximage);
579 /* This function destroys a GstXImageBuffer handling XShm availability */
581 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
582 GstXImageBuffer * ximage)
584 g_return_if_fail (ximage != NULL);
585 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
587 /* Hold the object lock to ensure the XContext doesn't disappear */
588 GST_OBJECT_LOCK (ximagesink);
590 /* If the destroyed image is the current one we destroy our reference too */
591 if (ximagesink->cur_image == ximage) {
592 ximagesink->cur_image = NULL;
595 /* We might have some buffers destroyed after changing state to NULL */
596 if (!ximagesink->xcontext) {
597 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
599 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
600 shmdt (ximage->SHMInfo.shmaddr);
606 g_mutex_lock (ximagesink->x_lock);
609 if (ximagesink->xcontext->use_xshm) {
610 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
611 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
612 XSync (ximagesink->xcontext->disp, 0);
613 shmdt (ximage->SHMInfo.shmaddr);
616 XDestroyImage (ximage->ximage);
619 #endif /* HAVE_XSHM */
621 if (ximage->ximage) {
622 XDestroyImage (ximage->ximage);
626 XSync (ximagesink->xcontext->disp, FALSE);
628 g_mutex_unlock (ximagesink->x_lock);
631 GST_OBJECT_UNLOCK (ximagesink);
633 if (ximage->ximagesink) {
634 /* Release the ref to our sink */
635 ximage->ximagesink = NULL;
636 gst_object_unref (ximagesink);
642 /* We are called with the x_lock taken */
644 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
645 GstXWindow * xwindow, GstVideoRectangle rect)
647 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
648 g_return_if_fail (xwindow != NULL);
650 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
651 ximagesink->xcontext->black);
655 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
656 0, 0, rect.x, xwindow->height);
660 if ((rect.x + rect.w) < xwindow->width) {
661 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
662 rect.x + rect.w, 0, xwindow->width, xwindow->height);
667 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
668 0, 0, xwindow->width, rect.y);
672 if ((rect.y + rect.h) < xwindow->height) {
673 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
674 0, rect.y + rect.h, xwindow->width, xwindow->height);
678 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
680 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
682 GstVideoRectangle src, dst, result;
683 gboolean draw_border = FALSE;
685 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
687 /* We take the flow_lock. If expose is in there we don't want to run
688 concurrently from the data flow thread */
689 g_mutex_lock (ximagesink->flow_lock);
691 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
692 g_mutex_unlock (ximagesink->flow_lock);
696 /* Draw borders when displaying the first frame. After this
697 draw borders only on expose event. */
698 if (!ximagesink->cur_image) {
702 /* Store a reference to the last image we put, lose the previous one */
703 if (ximage && ximagesink->cur_image != ximage) {
704 if (ximagesink->cur_image) {
705 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
706 gst_buffer_unref (ximagesink->cur_image);
708 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
709 ximagesink->cur_image =
710 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
713 /* Expose sends a NULL image, we take the latest frame */
716 if (ximagesink->cur_image) {
717 ximage = ximagesink->cur_image;
719 g_mutex_unlock (ximagesink->flow_lock);
724 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
726 src.w = ximage->width;
727 src.h = ximage->height;
728 dst.w = ximagesink->xwindow->width;
729 dst.h = ximagesink->xwindow->height;
731 gst_video_sink_center_rect (src, dst, &result, FALSE);
733 g_mutex_lock (ximagesink->x_lock);
736 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
740 if (ximagesink->xcontext->use_xshm) {
741 GST_LOG_OBJECT (ximagesink,
742 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
743 ximage, 0, 0, result.x, result.y, result.w, result.h,
744 ximagesink->xwindow->width, ximagesink->xwindow->height);
745 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
746 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
747 result.w, result.h, FALSE);
749 #endif /* HAVE_XSHM */
751 GST_LOG_OBJECT (ximagesink,
752 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
753 ximage, 0, 0, result.x, result.y, result.w, result.h,
754 ximagesink->xwindow->width, ximagesink->xwindow->height);
755 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
756 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
760 XSync (ximagesink->xcontext->disp, FALSE);
762 g_mutex_unlock (ximagesink->x_lock);
764 g_mutex_unlock (ximagesink->flow_lock);
770 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
773 Atom hints_atom = None;
776 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
777 g_return_val_if_fail (window != NULL, FALSE);
779 g_mutex_lock (ximagesink->x_lock);
781 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
782 if (hints_atom == None) {
783 g_mutex_unlock (ximagesink->x_lock);
787 hints = g_malloc0 (sizeof (MotifWmHints));
789 hints->flags |= MWM_HINTS_DECORATIONS;
790 hints->decorations = 1 << 0;
792 XChangeProperty (ximagesink->xcontext->disp, window->win,
793 hints_atom, hints_atom, 32, PropModeReplace,
794 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
796 XSync (ximagesink->xcontext->disp, FALSE);
798 g_mutex_unlock (ximagesink->x_lock);
805 /* This function handles a GstXWindow creation */
807 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
809 GstXWindow *xwindow = NULL;
812 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
814 xwindow = g_new0 (GstXWindow, 1);
816 xwindow->width = width;
817 xwindow->height = height;
818 xwindow->internal = TRUE;
820 g_mutex_lock (ximagesink->x_lock);
822 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
823 ximagesink->xcontext->root,
824 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
826 /* We have to do that to prevent X from redrawing the background on
827 ConfigureNotify. This takes away flickering of video when resizing. */
828 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
830 if (ximagesink->handle_events) {
833 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
834 StructureNotifyMask | PointerMotionMask | KeyPressMask |
835 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
837 /* Tell the window manager we'd like delete client messages instead of
839 wm_delete = XInternAtom (ximagesink->xcontext->disp,
840 "WM_DELETE_WINDOW", False);
841 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
845 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
848 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
850 XSync (ximagesink->xcontext->disp, FALSE);
852 g_mutex_unlock (ximagesink->x_lock);
854 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
856 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
861 /* This function destroys a GstXWindow */
863 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
864 GstXWindow * xwindow)
866 g_return_if_fail (xwindow != NULL);
867 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
869 g_mutex_lock (ximagesink->x_lock);
871 /* If we did not create that window we just free the GC and let it live */
872 if (xwindow->internal)
873 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
875 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
877 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
879 XSync (ximagesink->xcontext->disp, FALSE);
881 g_mutex_unlock (ximagesink->x_lock);
887 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
888 GstXWindow * xwindow)
890 XWindowAttributes attr;
892 g_return_if_fail (xwindow != NULL);
893 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
895 /* Update the window geometry */
896 g_mutex_lock (ximagesink->x_lock);
898 XGetWindowAttributes (ximagesink->xcontext->disp,
899 ximagesink->xwindow->win, &attr);
901 ximagesink->xwindow->width = attr.width;
902 ximagesink->xwindow->height = attr.height;
904 g_mutex_unlock (ximagesink->x_lock);
908 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
910 g_return_if_fail (xwindow != NULL);
911 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
913 g_mutex_lock (ximagesink->x_lock);
915 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
916 ximagesink->xcontext->black);
918 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
919 0, 0, xwindow->width, xwindow->height);
921 XSync (ximagesink->xcontext->disp, FALSE);
923 g_mutex_unlock (ximagesink->x_lock);
926 /* This function handles XEvents that might be in the queue. It generates
927 GstEvent that will be sent upstream in the pipeline to handle interactivity
930 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
933 guint pointer_x = 0, pointer_y = 0;
934 gboolean pointer_moved = FALSE;
935 gboolean exposed = FALSE, configured = FALSE;
937 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
939 /* Then we get all pointer motion events, only the last position is
941 g_mutex_lock (ximagesink->flow_lock);
942 g_mutex_lock (ximagesink->x_lock);
943 while (XCheckWindowEvent (ximagesink->xcontext->disp,
944 ximagesink->xwindow->win, PointerMotionMask, &e)) {
945 g_mutex_unlock (ximagesink->x_lock);
946 g_mutex_unlock (ximagesink->flow_lock);
950 pointer_x = e.xmotion.x;
951 pointer_y = e.xmotion.y;
952 pointer_moved = TRUE;
957 g_mutex_lock (ximagesink->flow_lock);
958 g_mutex_lock (ximagesink->x_lock);
962 g_mutex_unlock (ximagesink->x_lock);
963 g_mutex_unlock (ximagesink->flow_lock);
965 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
966 pointer_x, pointer_y);
967 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
968 "mouse-move", 0, pointer_x, pointer_y);
970 g_mutex_lock (ximagesink->flow_lock);
971 g_mutex_lock (ximagesink->x_lock);
974 /* We get all remaining events on our window to throw them upstream */
975 while (XCheckWindowEvent (ximagesink->xcontext->disp,
976 ximagesink->xwindow->win,
977 KeyPressMask | KeyReleaseMask |
978 ButtonPressMask | ButtonReleaseMask, &e)) {
981 /* We lock only for the X function call */
982 g_mutex_unlock (ximagesink->x_lock);
983 g_mutex_unlock (ximagesink->flow_lock);
987 /* Mouse button pressed/released over our window. We send upstream
988 events for interactivity/navigation */
989 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
990 e.xbutton.button, e.xbutton.x, e.xbutton.x);
991 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
992 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
995 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
996 e.xbutton.button, e.xbutton.x, e.xbutton.x);
997 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
998 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1002 /* Key pressed/released over our window. We send upstream
1003 events for interactivity/navigation */
1004 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1005 e.xkey.keycode, e.xkey.x, e.xkey.x);
1006 g_mutex_lock (ximagesink->x_lock);
1007 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1009 g_mutex_unlock (ximagesink->x_lock);
1010 if (keysym != NoSymbol) {
1011 char *key_str = NULL;
1013 g_mutex_lock (ximagesink->x_lock);
1014 key_str = XKeysymToString (keysym);
1015 g_mutex_unlock (ximagesink->x_lock);
1016 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1017 e.type == KeyPress ? "key-press" : "key-release", key_str);
1020 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1021 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1025 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1028 g_mutex_lock (ximagesink->flow_lock);
1029 g_mutex_lock (ximagesink->x_lock);
1032 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1033 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1038 case ConfigureNotify:
1046 if (ximagesink->handle_expose && (exposed || configured)) {
1047 g_mutex_unlock (ximagesink->x_lock);
1048 g_mutex_unlock (ximagesink->flow_lock);
1050 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1052 g_mutex_lock (ximagesink->flow_lock);
1053 g_mutex_lock (ximagesink->x_lock);
1056 /* Handle Display events */
1057 while (XPending (ximagesink->xcontext->disp)) {
1058 XNextEvent (ximagesink->xcontext->disp, &e);
1061 case ClientMessage:{
1064 wm_delete = XInternAtom (ximagesink->xcontext->disp,
1065 "WM_DELETE_WINDOW", False);
1066 if (wm_delete == (Atom) e.xclient.data.l[0]) {
1067 /* Handle window deletion by posting an error on the bus */
1068 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1069 ("Output window was closed"), (NULL));
1071 g_mutex_unlock (ximagesink->x_lock);
1072 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1073 ximagesink->xwindow = NULL;
1074 g_mutex_lock (ximagesink->x_lock);
1083 g_mutex_unlock (ximagesink->x_lock);
1084 g_mutex_unlock (ximagesink->flow_lock);
1088 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1090 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1092 GST_OBJECT_LOCK (ximagesink);
1093 while (ximagesink->running) {
1094 GST_OBJECT_UNLOCK (ximagesink);
1096 if (ximagesink->xwindow) {
1097 gst_ximagesink_handle_xevents (ximagesink);
1101 GST_OBJECT_LOCK (ximagesink);
1103 GST_OBJECT_UNLOCK (ximagesink);
1108 /* This function calculates the pixel aspect ratio based on the properties
1109 * in the xcontext structure and stores it there. */
1111 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1113 static const gint par[][2] = {
1114 {1, 1}, /* regular screen */
1115 {16, 15}, /* PAL TV */
1116 {11, 10}, /* 525 line Rec.601 video */
1117 {54, 59}, /* 625 line Rec.601 video */
1118 {64, 45}, /* 1280x1024 on 16:9 display */
1119 {5, 3}, /* 1280x1024 on 4:3 display */
1120 {4, 3} /* 800x600 on 16:9 display */
1127 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1129 /* first calculate the "real" ratio based on the X values;
1130 * which is the "physical" w/h divided by the w/h in pixels of the display */
1131 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1132 / (xcontext->heightmm * xcontext->width);
1134 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1136 if (xcontext->width == 720 && xcontext->height == 576) {
1137 ratio = 4.0 * 576 / (3.0 * 720);
1139 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1141 /* now find the one from par[][2] with the lowest delta to the real one */
1145 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1146 gdouble this_delta = DELTA (i);
1148 if (this_delta < delta) {
1154 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1155 par[index][0], par[index][1]);
1157 g_free (xcontext->par);
1158 xcontext->par = g_new0 (GValue, 1);
1159 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1160 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1161 GST_DEBUG ("set xcontext PAR to %d/%d",
1162 gst_value_get_fraction_numerator (xcontext->par),
1163 gst_value_get_fraction_denominator (xcontext->par));
1166 /* This function gets the X Display and global info about it. Everything is
1167 stored in our object and will be cleaned when the object is disposed. Note
1168 here that caps for supported format are generated without any window or
1170 static GstXContext *
1171 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1173 GstXContext *xcontext = NULL;
1174 XPixmapFormatValues *px_formats = NULL;
1175 gint nb_formats = 0, i;
1177 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1179 xcontext = g_new0 (GstXContext, 1);
1181 g_mutex_lock (ximagesink->x_lock);
1183 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1185 if (!xcontext->disp) {
1186 g_mutex_unlock (ximagesink->x_lock);
1188 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1189 ("Could not initialise X output"), ("Could not open display"));
1193 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1194 xcontext->screen_num = DefaultScreen (xcontext->disp);
1195 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1196 xcontext->root = DefaultRootWindow (xcontext->disp);
1197 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1198 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1199 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1201 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1202 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1203 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1204 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1206 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1207 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1209 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1211 /* We get supported pixmap formats at supported depth */
1212 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1215 XCloseDisplay (xcontext->disp);
1216 g_mutex_unlock (ximagesink->x_lock);
1217 g_free (xcontext->par);
1222 /* We get bpp value corresponding to our running depth */
1223 for (i = 0; i < nb_formats; i++) {
1224 if (px_formats[i].depth == xcontext->depth)
1225 xcontext->bpp = px_formats[i].bits_per_pixel;
1230 xcontext->endianness =
1231 (ImageByteOrder (xcontext->disp) ==
1232 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1234 /* Search for XShm extension support */
1236 if (XShmQueryExtension (xcontext->disp) &&
1237 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1238 xcontext->use_xshm = TRUE;
1239 GST_DEBUG ("ximagesink is using XShm extension");
1243 xcontext->use_xshm = FALSE;
1244 GST_DEBUG ("ximagesink is not using XShm extension");
1247 /* our caps system handles 24/32bpp RGB as big-endian. */
1248 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1249 xcontext->endianness == G_LITTLE_ENDIAN) {
1250 xcontext->endianness = G_BIG_ENDIAN;
1251 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1252 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1253 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1254 if (xcontext->bpp == 24) {
1255 xcontext->visual->red_mask >>= 8;
1256 xcontext->visual->green_mask >>= 8;
1257 xcontext->visual->blue_mask >>= 8;
1261 /* update object's par with calculated one if not set yet */
1262 if (!ximagesink->par) {
1263 ximagesink->par = g_new0 (GValue, 1);
1264 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1265 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1267 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1268 "bpp", G_TYPE_INT, xcontext->bpp,
1269 "depth", G_TYPE_INT, xcontext->depth,
1270 "endianness", G_TYPE_INT, xcontext->endianness,
1271 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1272 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1273 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1274 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1275 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1276 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1277 if (ximagesink->par) {
1280 nom = gst_value_get_fraction_numerator (ximagesink->par);
1281 den = gst_value_get_fraction_denominator (ximagesink->par);
1282 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1283 GST_TYPE_FRACTION, nom, den, NULL);
1286 g_mutex_unlock (ximagesink->x_lock);
1288 /* Setup our event listening thread */
1289 GST_OBJECT_LOCK (ximagesink);
1290 ximagesink->running = TRUE;
1291 ximagesink->event_thread = g_thread_create (
1292 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1293 GST_OBJECT_UNLOCK (ximagesink);
1298 /* This function cleans the X context. Closing the Display and unrefing the
1299 caps for supported formats. */
1301 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1303 GstXContext *xcontext;
1305 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1307 GST_OBJECT_LOCK (ximagesink);
1308 if (ximagesink->xcontext == NULL) {
1309 GST_OBJECT_UNLOCK (ximagesink);
1313 /* Take the xcontext reference and NULL it while we
1314 * clean it up, so that any buffer-alloced buffers
1315 * arriving after this will be freed correctly */
1316 xcontext = ximagesink->xcontext;
1317 ximagesink->xcontext = NULL;
1319 GST_OBJECT_UNLOCK (ximagesink);
1321 gst_caps_unref (xcontext->caps);
1322 g_free (xcontext->par);
1323 g_free (ximagesink->par);
1324 ximagesink->par = NULL;
1326 g_mutex_lock (ximagesink->x_lock);
1328 XCloseDisplay (xcontext->disp);
1330 g_mutex_unlock (ximagesink->x_lock);
1336 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1339 g_mutex_lock (ximagesink->pool_lock);
1341 while (ximagesink->buffer_pool) {
1342 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1344 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1345 ximagesink->buffer_pool);
1346 gst_ximage_buffer_free (ximage);
1349 g_mutex_unlock (ximagesink->pool_lock);
1355 gst_ximagesink_getcaps (GstBaseSink * bsink)
1357 GstXImageSink *ximagesink;
1361 ximagesink = GST_XIMAGESINK (bsink);
1363 if (ximagesink->xcontext)
1364 return gst_caps_ref (ximagesink->xcontext->caps);
1366 /* get a template copy and add the pixel aspect ratio */
1368 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->
1370 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1371 GstStructure *structure = gst_caps_get_structure (caps, i);
1373 if (ximagesink->par) {
1376 nom = gst_value_get_fraction_numerator (ximagesink->par);
1377 den = gst_value_get_fraction_denominator (ximagesink->par);
1378 gst_structure_set (structure, "pixel-aspect-ratio",
1379 GST_TYPE_FRACTION, nom, den, NULL);
1387 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1389 GstXImageSink *ximagesink;
1390 gboolean ret = TRUE;
1391 GstStructure *structure;
1392 GstCaps *intersection;
1394 gint new_width, new_height;
1397 ximagesink = GST_XIMAGESINK (bsink);
1399 if (!ximagesink->xcontext)
1402 GST_DEBUG_OBJECT (ximagesink,
1403 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1404 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1406 /* We intersect those caps with our template to make sure they are correct */
1407 intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1408 GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1410 if (gst_caps_is_empty (intersection)) {
1411 gst_caps_unref (intersection);
1415 gst_caps_unref (intersection);
1417 structure = gst_caps_get_structure (caps, 0);
1419 ret &= gst_structure_get_int (structure, "width", &new_width);
1420 ret &= gst_structure_get_int (structure, "height", &new_height);
1421 fps = gst_structure_get_value (structure, "framerate");
1422 ret &= (fps != NULL);
1426 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1427 * otherwise linking should fail */
1428 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1430 if (ximagesink->par) {
1431 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1434 } else if (ximagesink->xcontext->par) {
1435 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1441 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1442 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1443 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1444 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1446 /* Notify application to set xwindow id now */
1447 g_mutex_lock (ximagesink->flow_lock);
1448 if (!ximagesink->xwindow) {
1449 g_mutex_unlock (ximagesink->flow_lock);
1450 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1452 g_mutex_unlock (ximagesink->flow_lock);
1455 /* Creating our window and our image */
1456 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1457 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1458 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1459 ("Invalid image size."));
1463 g_mutex_lock (ximagesink->flow_lock);
1464 if (!ximagesink->xwindow) {
1465 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1466 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1468 g_mutex_unlock (ximagesink->flow_lock);
1470 /* If our ximage has changed we destroy it, next chain iteration will create
1472 if ((ximagesink->ximage) &&
1473 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1474 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1475 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1476 ximagesink->ximage);
1477 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1478 ximagesink->ximage = NULL;
1486 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1491 static GstStateChangeReturn
1492 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1494 GstXImageSink *ximagesink;
1495 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1496 GstXContext *xcontext = NULL;
1498 ximagesink = GST_XIMAGESINK (element);
1500 switch (transition) {
1501 case GST_STATE_CHANGE_NULL_TO_READY:
1503 /* Initializing the XContext */
1504 if (ximagesink->xcontext == NULL) {
1505 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1506 if (xcontext == NULL) {
1507 ret = GST_STATE_CHANGE_FAILURE;
1510 GST_OBJECT_LOCK (ximagesink);
1512 ximagesink->xcontext = xcontext;
1513 GST_OBJECT_UNLOCK (ximagesink);
1516 /* call XSynchronize with the current value of synchronous */
1517 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1518 ximagesink->synchronous ? "TRUE" : "FALSE");
1519 g_mutex_lock (ximagesink->x_lock);
1520 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1521 g_mutex_unlock (ximagesink->x_lock);
1523 case GST_STATE_CHANGE_READY_TO_PAUSED:
1524 g_mutex_lock (ximagesink->flow_lock);
1525 if (ximagesink->xwindow)
1526 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1527 g_mutex_unlock (ximagesink->flow_lock);
1529 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1535 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1537 switch (transition) {
1538 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1540 case GST_STATE_CHANGE_PAUSED_TO_READY:
1541 ximagesink->fps_n = 0;
1542 ximagesink->fps_d = 1;
1543 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1544 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1546 case GST_STATE_CHANGE_READY_TO_NULL:
1547 gst_ximagesink_reset (ximagesink);
1558 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1559 GstClockTime * start, GstClockTime * end)
1561 GstXImageSink *ximagesink;
1563 ximagesink = GST_XIMAGESINK (bsink);
1565 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1566 *start = GST_BUFFER_TIMESTAMP (buf);
1567 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1568 *end = *start + GST_BUFFER_DURATION (buf);
1570 if (ximagesink->fps_n > 0) {
1572 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1579 static GstFlowReturn
1580 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1582 GstXImageSink *ximagesink;
1584 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1586 ximagesink = GST_XIMAGESINK (bsink);
1588 /* If this buffer has been allocated using our buffer management we simply
1589 put the ximage which is in the PRIVATE pointer */
1590 if (GST_IS_XIMAGE_BUFFER (buf)) {
1591 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1592 if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1595 /* Else we have to copy the data into our private image, */
1596 /* if we have one... */
1597 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1598 if (!ximagesink->ximage) {
1599 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1600 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1601 GST_BUFFER_CAPS (buf));
1602 if (!ximagesink->ximage)
1603 /* The create method should have posted an informative error */
1606 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1607 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1608 ("Failed to create output image buffer of %dx%d pixels",
1609 ximagesink->ximage->width, ximagesink->ximage->height),
1610 ("XServer allocated buffer size did not match input buffer"));
1612 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1613 ximagesink->ximage = NULL;
1617 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1618 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1619 if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1628 /* No image available. That's very bad ! */
1629 GST_DEBUG ("could not create image");
1630 return GST_FLOW_ERROR;
1634 /* No Window available to put our image into */
1635 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1636 return GST_FLOW_ERROR;
1640 /* Buffer management
1642 * The buffer_alloc function must either return a buffer with given size and
1643 * caps or create a buffer with different caps attached to the buffer. This
1644 * last option is called reverse negotiation, ie, where the sink suggests a
1645 * different format from the upstream peer.
1647 * We try to do reverse negotiation when our geometry changes and we like a
1650 static GstFlowReturn
1651 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1652 GstCaps * caps, GstBuffer ** buf)
1654 GstXImageSink *ximagesink;
1655 GstXImageBuffer *ximage = NULL;
1656 GstStructure *structure = NULL;
1657 GstFlowReturn ret = GST_FLOW_OK;
1658 GstCaps *alloc_caps;
1659 gboolean alloc_unref = FALSE;
1662 ximagesink = GST_XIMAGESINK (bsink);
1664 GST_LOG_OBJECT (ximagesink,
1665 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1666 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1668 /* assume we're going to alloc what was requested, keep track of
1669 * wheter we need to unref or not. When we suggest a new format
1670 * upstream we will create a new caps that we need to unref. */
1672 alloc_unref = FALSE;
1674 /* get struct to see what is requested */
1675 structure = gst_caps_get_structure (caps, 0);
1677 if (gst_structure_get_int (structure, "width", &width) &&
1678 gst_structure_get_int (structure, "height", &height)) {
1679 GstVideoRectangle dst, src, result;
1684 /* We take the flow_lock because the window might go away */
1685 g_mutex_lock (ximagesink->flow_lock);
1686 if (!ximagesink->xwindow) {
1687 g_mutex_unlock (ximagesink->flow_lock);
1691 /* What is our geometry */
1692 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1693 dst.w = ximagesink->xwindow->width;
1694 dst.h = ximagesink->xwindow->height;
1696 g_mutex_unlock (ximagesink->flow_lock);
1698 if (ximagesink->keep_aspect) {
1699 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1701 gst_video_sink_center_rect (src, dst, &result, TRUE);
1703 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1704 "ignoring aspect ratio");
1705 result.x = result.y = 0;
1710 /* We would like another geometry */
1711 if (width != result.w || height != result.h) {
1713 GstCaps *desired_caps;
1714 GstStructure *desired_struct;
1716 /* make a copy of the incomming caps to create the new
1717 * suggestion. We can't use make_writable because we might
1718 * then destroy the original caps which we still need when the
1719 * peer does not accept the suggestion. */
1720 desired_caps = gst_caps_copy (caps);
1721 desired_struct = gst_caps_get_structure (desired_caps, 0);
1723 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1724 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1725 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1727 /* PAR property overrides the X calculated one */
1728 if (ximagesink->par) {
1729 nom = gst_value_get_fraction_numerator (ximagesink->par);
1730 den = gst_value_get_fraction_denominator (ximagesink->par);
1731 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1732 GST_TYPE_FRACTION, nom, den, NULL);
1733 } else if (ximagesink->xcontext->par) {
1734 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1735 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1736 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1737 GST_TYPE_FRACTION, nom, den, NULL);
1740 /* see if peer accepts our new suggestion, if there is no peer, this
1741 * function returns true. */
1742 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1746 bpp = size / height / width;
1747 /* we will not alloc a buffer of the new suggested caps. Make sure
1748 * we also unref this new caps after we set it on the buffer. */
1749 alloc_caps = desired_caps;
1753 size = bpp * width * height;
1754 GST_DEBUG ("peed pad accepts our desired caps %" GST_PTR_FORMAT
1755 " buffer size is now %d bytes", desired_caps, size);
1757 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1759 /* we alloc a buffer with the original incomming caps */
1760 width = GST_VIDEO_SINK_WIDTH (ximagesink);
1761 height = GST_VIDEO_SINK_HEIGHT (ximagesink);
1767 /* Inspect our buffer pool */
1768 g_mutex_lock (ximagesink->pool_lock);
1769 while (ximagesink->buffer_pool) {
1770 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1773 /* Removing from the pool */
1774 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1775 ximagesink->buffer_pool);
1777 /* If the ximage is invalid for our need, destroy */
1778 if ((ximage->width != width) || (ximage->height != height)) {
1779 gst_ximage_buffer_free (ximage);
1782 /* We found a suitable ximage */
1787 g_mutex_unlock (ximagesink->pool_lock);
1789 /* We haven't found anything, creating a new one */
1791 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1793 /* Now we should have a ximage, set appropriate caps on it */
1795 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1798 /* could be our new reffed suggestion or the original unreffed caps */
1800 gst_caps_unref (alloc_caps);
1802 *buf = GST_BUFFER_CAST (ximage);
1807 /* Interfaces stuff */
1810 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1812 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1817 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1819 klass->supported = gst_ximagesink_interface_supported;
1823 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1824 GstStructure * structure)
1826 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1828 gint x_offset, y_offset;
1832 event = gst_event_new_navigation (structure);
1834 /* We are not converting the pointer coordinates as there's no hardware
1835 scaling done here. The only possible scaling is done by videoscale and
1836 videoscale will have to catch those events and tranform the coordinates
1837 to match the applied scaling. So here we just add the offset if the image
1838 is centered in the window. */
1840 /* We take the flow_lock while we look at the window */
1841 g_mutex_lock (ximagesink->flow_lock);
1843 if (!ximagesink->xwindow) {
1844 g_mutex_unlock (ximagesink->flow_lock);
1848 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1849 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1851 g_mutex_unlock (ximagesink->flow_lock);
1853 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1855 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1857 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1859 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1862 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1864 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1865 gst_pad_send_event (pad, event);
1867 gst_object_unref (pad);
1872 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1874 iface->send_event = gst_ximagesink_navigation_send_event;
1878 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1880 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1881 GstXWindow *xwindow = NULL;
1882 XWindowAttributes attr;
1884 /* We acquire the stream lock while setting this window in the element.
1885 We are basically cleaning tons of stuff replacing the old window, putting
1886 images while we do that would surely crash */
1887 g_mutex_lock (ximagesink->flow_lock);
1889 /* If we already use that window return */
1890 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1891 g_mutex_unlock (ximagesink->flow_lock);
1895 /* If the element has not initialized the X11 context try to do so */
1896 if (!ximagesink->xcontext &&
1897 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1898 g_mutex_unlock (ximagesink->flow_lock);
1899 /* we have thrown a GST_ELEMENT_ERROR now */
1903 /* If a window is there already we destroy it */
1904 if (ximagesink->xwindow) {
1905 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1906 ximagesink->xwindow = NULL;
1909 /* If the xid is 0 we go back to an internal window */
1910 if (xwindow_id == 0) {
1911 /* If no width/height caps nego did not happen window will be created
1912 during caps nego then */
1913 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1914 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1915 GST_VIDEO_SINK_WIDTH (ximagesink),
1916 GST_VIDEO_SINK_HEIGHT (ximagesink));
1919 xwindow = g_new0 (GstXWindow, 1);
1921 xwindow->win = xwindow_id;
1923 /* We get window geometry, set the event we want to receive,
1925 g_mutex_lock (ximagesink->x_lock);
1926 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1927 xwindow->width = attr.width;
1928 xwindow->height = attr.height;
1929 xwindow->internal = FALSE;
1930 if (ximagesink->handle_events) {
1931 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1932 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1936 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1937 g_mutex_unlock (ximagesink->x_lock);
1941 ximagesink->xwindow = xwindow;
1943 g_mutex_unlock (ximagesink->flow_lock);
1947 gst_ximagesink_expose (GstXOverlay * overlay)
1949 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1951 gst_ximagesink_ximage_put (ximagesink, NULL);
1955 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1956 gboolean handle_events)
1958 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1960 ximagesink->handle_events = handle_events;
1962 g_mutex_lock (ximagesink->flow_lock);
1964 if (G_UNLIKELY (!ximagesink->xwindow)) {
1965 g_mutex_unlock (ximagesink->flow_lock);
1969 g_mutex_lock (ximagesink->x_lock);
1971 if (handle_events) {
1972 if (ximagesink->xwindow->internal) {
1973 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1974 ExposureMask | StructureNotifyMask | PointerMotionMask |
1975 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1977 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1978 ExposureMask | StructureNotifyMask | PointerMotionMask |
1979 KeyPressMask | KeyReleaseMask);
1982 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1985 g_mutex_unlock (ximagesink->x_lock);
1987 g_mutex_unlock (ximagesink->flow_lock);
1991 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1993 iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1994 iface->expose = gst_ximagesink_expose;
1995 iface->handle_events = gst_ximagesink_set_event_handling;
1998 /* =========================================== */
2000 /* Init & Class init */
2002 /* =========================================== */
2005 gst_ximagesink_set_property (GObject * object, guint prop_id,
2006 const GValue * value, GParamSpec * pspec)
2008 GstXImageSink *ximagesink;
2010 g_return_if_fail (GST_IS_XIMAGESINK (object));
2012 ximagesink = GST_XIMAGESINK (object);
2016 ximagesink->display_name = g_strdup (g_value_get_string (value));
2018 case PROP_SYNCHRONOUS:
2019 ximagesink->synchronous = g_value_get_boolean (value);
2020 if (ximagesink->xcontext) {
2021 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2022 ximagesink->synchronous ? "TRUE" : "FALSE");
2023 g_mutex_lock (ximagesink->x_lock);
2024 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2025 g_mutex_unlock (ximagesink->x_lock);
2028 case PROP_FORCE_ASPECT_RATIO:
2029 ximagesink->keep_aspect = g_value_get_boolean (value);
2031 case PROP_PIXEL_ASPECT_RATIO:
2035 tmp = g_new0 (GValue, 1);
2036 g_value_init (tmp, GST_TYPE_FRACTION);
2038 if (!g_value_transform (value, tmp)) {
2039 GST_WARNING_OBJECT (ximagesink,
2040 "Could not transform string to aspect ratio");
2043 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2044 gst_value_get_fraction_numerator (tmp),
2045 gst_value_get_fraction_denominator (tmp));
2046 g_free (ximagesink->par);
2047 ximagesink->par = tmp;
2051 case PROP_HANDLE_EVENTS:
2052 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2053 g_value_get_boolean (value));
2055 case PROP_HANDLE_EXPOSE:
2056 ximagesink->handle_expose = g_value_get_boolean (value);
2059 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2065 gst_ximagesink_get_property (GObject * object, guint prop_id,
2066 GValue * value, GParamSpec * pspec)
2068 GstXImageSink *ximagesink;
2070 g_return_if_fail (GST_IS_XIMAGESINK (object));
2072 ximagesink = GST_XIMAGESINK (object);
2076 g_value_set_string (value, ximagesink->display_name);
2078 case PROP_SYNCHRONOUS:
2079 g_value_set_boolean (value, ximagesink->synchronous);
2081 case PROP_FORCE_ASPECT_RATIO:
2082 g_value_set_boolean (value, ximagesink->keep_aspect);
2084 case PROP_PIXEL_ASPECT_RATIO:
2085 if (ximagesink->par)
2086 g_value_transform (ximagesink->par, value);
2088 case PROP_HANDLE_EVENTS:
2089 g_value_set_boolean (value, ximagesink->handle_events);
2091 case PROP_HANDLE_EXPOSE:
2092 g_value_set_boolean (value, ximagesink->handle_expose);
2095 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2101 gst_ximagesink_reset (GstXImageSink * ximagesink)
2105 GST_OBJECT_LOCK (ximagesink);
2106 ximagesink->running = FALSE;
2107 /* grab thread and mark it as NULL */
2108 thread = ximagesink->event_thread;
2109 ximagesink->event_thread = NULL;
2110 GST_OBJECT_UNLOCK (ximagesink);
2112 /* Wait for our event thread to finish before we clean up our stuff. */
2114 g_thread_join (thread);
2116 if (ximagesink->ximage) {
2117 gst_buffer_unref (ximagesink->ximage);
2118 ximagesink->ximage = NULL;
2120 if (ximagesink->cur_image) {
2121 gst_buffer_unref (ximagesink->cur_image);
2122 ximagesink->cur_image = NULL;
2125 gst_ximagesink_bufferpool_clear (ximagesink);
2127 g_mutex_lock (ximagesink->flow_lock);
2128 if (ximagesink->xwindow) {
2129 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2130 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2131 ximagesink->xwindow = NULL;
2133 g_mutex_unlock (ximagesink->flow_lock);
2135 gst_ximagesink_xcontext_clear (ximagesink);
2139 gst_ximagesink_finalize (GObject * object)
2141 GstXImageSink *ximagesink;
2143 ximagesink = GST_XIMAGESINK (object);
2145 gst_ximagesink_reset (ximagesink);
2147 if (ximagesink->display_name) {
2148 g_free (ximagesink->display_name);
2149 ximagesink->display_name = NULL;
2151 if (ximagesink->par) {
2152 g_free (ximagesink->par);
2153 ximagesink->par = NULL;
2155 if (ximagesink->x_lock) {
2156 g_mutex_free (ximagesink->x_lock);
2157 ximagesink->x_lock = NULL;
2159 if (ximagesink->flow_lock) {
2160 g_mutex_free (ximagesink->flow_lock);
2161 ximagesink->flow_lock = NULL;
2163 if (ximagesink->pool_lock) {
2164 g_mutex_free (ximagesink->pool_lock);
2165 ximagesink->pool_lock = NULL;
2168 G_OBJECT_CLASS (parent_class)->finalize (object);
2172 gst_ximagesink_init (GstXImageSink * ximagesink)
2174 ximagesink->display_name = NULL;
2175 ximagesink->xcontext = NULL;
2176 ximagesink->xwindow = NULL;
2177 ximagesink->ximage = NULL;
2178 ximagesink->cur_image = NULL;
2180 ximagesink->event_thread = NULL;
2181 ximagesink->running = FALSE;
2183 ximagesink->fps_n = 0;
2184 ximagesink->fps_d = 1;
2186 ximagesink->x_lock = g_mutex_new ();
2187 ximagesink->flow_lock = g_mutex_new ();
2189 ximagesink->par = NULL;
2191 ximagesink->pool_lock = g_mutex_new ();
2192 ximagesink->buffer_pool = NULL;
2194 ximagesink->synchronous = FALSE;
2195 ximagesink->keep_aspect = FALSE;
2196 ximagesink->handle_events = TRUE;
2197 ximagesink->handle_expose = TRUE;
2201 gst_ximagesink_base_init (gpointer g_class)
2203 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2205 gst_element_class_set_details (element_class, &gst_ximagesink_details);
2207 gst_element_class_add_pad_template (element_class,
2208 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2212 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2214 GObjectClass *gobject_class;
2215 GstElementClass *gstelement_class;
2216 GstBaseSinkClass *gstbasesink_class;
2218 gobject_class = (GObjectClass *) klass;
2219 gstelement_class = (GstElementClass *) klass;
2220 gstbasesink_class = (GstBaseSinkClass *) klass;
2222 parent_class = g_type_class_peek_parent (klass);
2224 gobject_class->finalize = gst_ximagesink_finalize;
2225 gobject_class->set_property = gst_ximagesink_set_property;
2226 gobject_class->get_property = gst_ximagesink_get_property;
2228 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2229 g_param_spec_string ("display", "Display", "X Display name",
2230 NULL, G_PARAM_READWRITE));
2231 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2232 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2233 "the X display in synchronous mode. (used only for debugging)", FALSE,
2234 G_PARAM_READWRITE));
2235 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2236 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2237 "When enabled, reverse caps negotiation (scaling) will respect "
2238 "original aspect ratio", FALSE, G_PARAM_READWRITE));
2239 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2240 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2241 "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2242 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2243 g_param_spec_boolean ("handle-events", "Handle XEvents",
2244 "When enabled, XEvents will be selected and handled", TRUE,
2245 G_PARAM_READWRITE));
2246 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2247 g_param_spec_boolean ("handle-expose", "Handle expose", "When enabled, "
2248 "the current frame will always be drawn in response to X Expose "
2249 "events", TRUE, G_PARAM_READWRITE));
2251 gstelement_class->change_state = gst_ximagesink_change_state;
2253 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2254 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2255 gstbasesink_class->buffer_alloc =
2256 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2257 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2258 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2259 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2262 /* ============================================================= */
2264 /* Public Methods */
2266 /* ============================================================= */
2268 /* =========================================== */
2270 /* Object typing & Creation */
2272 /* =========================================== */
2275 gst_ximagesink_get_type (void)
2277 static GType ximagesink_type = 0;
2279 if (!ximagesink_type) {
2280 static const GTypeInfo ximagesink_info = {
2281 sizeof (GstXImageSinkClass),
2282 gst_ximagesink_base_init,
2284 (GClassInitFunc) gst_ximagesink_class_init,
2287 sizeof (GstXImageSink),
2289 (GInstanceInitFunc) gst_ximagesink_init,
2291 static const GInterfaceInfo iface_info = {
2292 (GInterfaceInitFunc) gst_ximagesink_interface_init,
2296 static const GInterfaceInfo navigation_info = {
2297 (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2301 static const GInterfaceInfo overlay_info = {
2302 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2307 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2308 "GstXImageSink", &ximagesink_info, 0);
2310 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2312 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2314 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2317 /* register type and create class in a more safe place instead of at
2318 * runtime since the type registration and class creation is not
2320 g_type_class_ref (gst_ximage_buffer_get_type ());
2323 return ximagesink_type;