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
23 * XImageSink renders video frames to a drawable (XWindow) on a local or remote
24 * display. This element can receive a Window ID from the application through
25 * the XOverlay interface and will then render video frames in this drawable.
26 * If no Window ID was provided by the application, the element will create its
27 * own internal window and render into it.
30 * <title>Scaling</title>
32 * As standard XImage rendering to a drawable is not scaled, XImageSink will use
33 * reverse caps negotiation to try to get scaled video frames for the drawable.
34 * This is accomplished by asking the peer pad if it accepts some different caps
35 * which in most cases implies that there is a scaling element in the pipeline,
36 * or that an element generating the video frames can generate them with a
37 * different geometry. This mechanism is handled during buffer allocations, for
38 * each allocation request the video sink will check the drawable geometry, look
39 * at the #GstXImageSink:force-aspect-ratio property, calculate the geometry of
40 * desired video frames and then check that the peer pad accept those new caps.
41 * If it does it will then allocate a buffer in video memory with this new
42 * 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.
60 * <title>Pixel aspect ratio</title>
62 * When changing state to GST_STATE_READY, XImageSink will open a connection to
63 * the display specified in the #GstXImageSink:display property or the default
64 * display if nothing specified. Once this connection is open it will inspect
65 * the display configuration including the physical display geometry and
66 * then calculate the pixel aspect ratio. When caps negotiation will occur, the
67 * video sink will set the calculated pixel aspect ratio on the caps to make
68 * sure that incoming video frames will have the correct pixel aspect ratio for
69 * this display. Sometimes the calculated pixel aspect ratio can be wrong, it is
70 * then possible to enforce a specific pixel aspect ratio using the
71 * #GstXImageSink:pixel-aspect-ratio property.
75 * <title>Examples</title>
77 * gst-launch -v videotestsrc ! queue ! ximagesink
78 * ]| A pipeline to test reverse negotiation. When the test video signal appears
79 * you can resize the window and see that scaled buffers of the desired size are
80 * going to arrive with a short delay. This illustrates how buffers of desired
81 * size are allocated along the way. If you take away the queue, scaling will
82 * happen almost immediately.
84 * gst-launch -v videotestsrc ! navigationtest ! ffmpegcolorspace ! ximagesink
85 * ]| A pipeline to test navigation events.
86 * While moving the mouse pointer over the test signal you will see a black box
87 * following the mouse pointer. If you press the mouse button somewhere on the
88 * video and release it somewhere else a green box will appear where you pressed
89 * the button and a red one where you released it. (The navigationtest element
90 * is part of gst-plugins-good.)
92 * gst-launch -v videotestsrc ! video/x-raw-rgb, pixel-aspect-ratio=(fraction)4/3 ! videoscale ! ximagesink
93 * ]| This is faking a 4/3 pixel aspect ratio caps on video frames produced by
94 * videotestsrc, in most cases the pixel aspect ratio of the display will be
95 * 1/1. This means that videoscale will have to do the scaling to convert
96 * incoming frames to a size that will match the display pixel aspect ratio
97 * (from 320x240 to 320x180 in this case). Note that you might have to escape
98 * some characters for your shell like '\(fraction\)'.
107 #include <gst/interfaces/navigation.h>
108 #include <gst/interfaces/xoverlay.h>
111 #include "ximagesink.h"
113 /* Debugging category */
114 #include <gst/gstinfo.h>
116 GST_DEBUG_CATEGORY_EXTERN (gst_debug_ximagesink);
117 #define GST_CAT_DEFAULT gst_debug_ximagesink
122 unsigned long functions;
123 unsigned long decorations;
125 unsigned long status;
127 MotifWmHints, MwmHints;
129 #define MWM_HINTS_DECORATIONS (1L << 1)
131 static void gst_ximagesink_reset (GstXImageSink * ximagesink);
132 static void gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
133 GstXImageBuffer * ximage);
134 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
135 GstXWindow * xwindow);
136 static void gst_ximagesink_expose (GstXOverlay * overlay);
138 /* ElementFactory information */
139 static const GstElementDetails gst_ximagesink_details =
140 GST_ELEMENT_DETAILS ("Video sink",
142 "A standard X based videosink",
143 "Julien Moutte <julien@moutte.net>");
145 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
146 GST_STATIC_PAD_TEMPLATE ("sink",
149 GST_STATIC_CAPS ("video/x-raw-rgb, "
150 "framerate = (fraction) [ 0, MAX ], "
151 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
159 PROP_PIXEL_ASPECT_RATIO,
160 PROP_FORCE_ASPECT_RATIO,
165 static GstVideoSinkClass *parent_class = NULL;
167 /* ============================================================= */
169 /* Private Methods */
171 /* ============================================================= */
175 static GstBufferClass *ximage_buffer_parent_class = NULL;
177 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
179 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
180 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
181 #define GST_XIMAGE_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
183 /* So some words about GstMiniObject, this is pretty messy...
184 GstMiniObject does not use the standard finalizing of GObjects, you are
185 supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
186 which will handle its own refcount system and call gst_mini_object_free.
187 gst_mini_object_free will call the class finalize method which is not the
188 one from GObject, after calling this finalize method it will free the object
189 instance for you if the refcount is still 0 so you should not chain up */
191 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
193 GstXImageSink *ximagesink = NULL;
194 gboolean recycled = FALSE;
197 g_return_if_fail (ximage != NULL);
199 ximagesink = ximage->ximagesink;
200 if (G_UNLIKELY (ximagesink == NULL)) {
201 GST_WARNING_OBJECT (ximagesink, "no sink found");
205 GST_OBJECT_LOCK (ximagesink);
206 running = ximagesink->running;
207 GST_OBJECT_UNLOCK (ximagesink);
209 if (running == FALSE) {
210 /* If the sink is shutting down, need to clear the image */
211 GST_DEBUG_OBJECT (ximagesink,
212 "destroy image %p because the sink is shutting down", ximage);
213 gst_ximagesink_ximage_destroy (ximagesink, ximage);
214 } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
215 (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
216 /* If our geometry changed we can't reuse that image. */
217 GST_DEBUG_OBJECT (ximagesink,
218 "destroy image %p as its size changed %dx%d vs current %dx%d",
219 ximage, ximage->width, ximage->height,
220 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
221 gst_ximagesink_ximage_destroy (ximagesink, ximage);
223 /* In that case we can reuse the image and add it to our image pool. */
224 GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
225 /* need to increment the refcount again to recycle */
226 gst_buffer_ref (GST_BUFFER_CAST (ximage));
227 g_mutex_lock (ximagesink->pool_lock);
228 ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
229 g_mutex_unlock (ximagesink->pool_lock);
234 GST_MINI_OBJECT_CLASS (ximage_buffer_parent_class)->
235 finalize (GST_MINI_OBJECT (ximage));
242 gst_ximage_buffer_free (GstXImageBuffer * ximage)
244 /* make sure it is not recycled */
247 gst_buffer_unref (GST_BUFFER_CAST (ximage));
251 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
254 ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
255 ximage_buffer->SHMInfo.shmid = -1;
260 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
262 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
264 ximage_buffer_parent_class = g_type_class_peek_parent (g_class);
266 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
267 gst_ximage_buffer_finalize;
271 gst_ximage_buffer_get_type (void)
273 static GType _gst_ximage_buffer_type;
275 if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
276 static const GTypeInfo ximage_buffer_info = {
277 sizeof (GstBufferClass),
280 gst_ximage_buffer_class_init,
283 sizeof (GstXImageBuffer),
285 (GInstanceInitFunc) gst_ximage_buffer_init,
288 _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
289 "GstXImageBuffer", &ximage_buffer_info, 0);
291 return _gst_ximage_buffer_type;
296 static gboolean error_caught = FALSE;
299 gst_ximagesink_handle_xerror (Display * display, XErrorEvent * xevent)
301 char error_msg[1024];
303 XGetErrorText (display, xevent->error_code, error_msg, 1024);
304 GST_DEBUG ("ximagesink triggered an XError. error: %s", error_msg);
309 #ifdef HAVE_XSHM /* Check that XShm calls actually work */
312 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
313 GstXContext * xcontext)
316 XShmSegmentInfo SHMInfo;
318 int (*handler) (Display *, XErrorEvent *);
319 gboolean result = FALSE;
320 gboolean did_attach = FALSE;
322 g_return_val_if_fail (xcontext != NULL, FALSE);
324 /* Sync to ensure any older errors are already processed */
325 XSync (xcontext->disp, FALSE);
327 /* Set defaults so we don't free these later unnecessarily */
328 SHMInfo.shmaddr = ((void *) -1);
331 /* Setting an error handler to catch failure */
332 error_caught = FALSE;
333 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
335 /* Trying to create a 1x1 ximage */
336 GST_DEBUG ("XShmCreateImage of 1x1");
338 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
339 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
341 /* Might cause an error, sync to ensure it is noticed */
342 XSync (xcontext->disp, FALSE);
343 if (!ximage || error_caught) {
344 GST_WARNING ("could not XShmCreateImage a 1x1 image");
347 size = ximage->height * ximage->bytes_per_line;
349 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
350 if (SHMInfo.shmid == -1) {
351 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
356 SHMInfo.shmaddr = shmat (SHMInfo.shmid, NULL, 0);
357 if (SHMInfo.shmaddr == ((void *) -1)) {
358 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
359 /* Clean up shm seg */
360 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
364 ximage->data = SHMInfo.shmaddr;
365 SHMInfo.readOnly = FALSE;
367 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
368 GST_WARNING ("Failed to XShmAttach");
369 /* Clean up shm seg */
370 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
374 /* Sync to ensure we see any errors we caused */
375 XSync (xcontext->disp, FALSE);
377 /* Delete the shared memory segment as soon as everyone is attached.
378 * This way, it will be deleted as soon as we detach later, and not
379 * leaked if we crash. */
380 shmctl (SHMInfo.shmid, IPC_RMID, NULL);
384 /* store whether we succeeded in result */
389 /* Sync to ensure we swallow any errors we caused and reset error_caught */
390 XSync (xcontext->disp, FALSE);
391 error_caught = FALSE;
392 XSetErrorHandler (handler);
395 XShmDetach (xcontext->disp, &SHMInfo);
396 XSync (xcontext->disp, FALSE);
398 if (SHMInfo.shmaddr != ((void *) -1))
399 shmdt (SHMInfo.shmaddr);
401 XDestroyImage (ximage);
404 #endif /* HAVE_XSHM */
406 /* This function handles GstXImageBuffer creation depending on XShm availability */
407 static GstXImageBuffer *
408 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
410 GstXImageBuffer *ximage = NULL;
411 GstStructure *structure = NULL;
412 gboolean succeeded = FALSE;
413 int (*handler) (Display *, XErrorEvent *);
415 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
417 ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
419 structure = gst_caps_get_structure (caps, 0);
421 if (!gst_structure_get_int (structure, "width", &ximage->width) ||
422 !gst_structure_get_int (structure, "height", &ximage->height)) {
423 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
426 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
427 ximage->width, ximage->height);
429 g_mutex_lock (ximagesink->x_lock);
431 /* Setting an error handler to catch failure */
432 error_caught = FALSE;
433 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
436 if (ximagesink->xcontext->use_xshm) {
437 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
438 ximagesink->xcontext->visual,
439 ximagesink->xcontext->depth,
440 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
441 if (!ximage->ximage || error_caught) {
442 g_mutex_unlock (ximagesink->x_lock);
443 /* Reset error handler */
444 error_caught = FALSE;
445 XSetErrorHandler (handler);
447 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
448 ("Failed to create output image buffer of %dx%d pixels",
449 ximage->width, ximage->height),
450 ("could not XShmCreateImage a %dx%d image",
451 ximage->width, ximage->height));
455 /* we have to use the returned bytes_per_line for our shm size */
456 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
457 GST_LOG_OBJECT (ximagesink,
458 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
459 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
461 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
463 if (ximage->SHMInfo.shmid == -1) {
464 g_mutex_unlock (ximagesink->x_lock);
465 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
466 ("Failed to create output image buffer of %dx%d pixels",
467 ximage->width, ximage->height),
468 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
473 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, NULL, 0);
474 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
475 g_mutex_unlock (ximagesink->x_lock);
476 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
477 ("Failed to create output image buffer of %dx%d pixels",
478 ximage->width, ximage->height),
479 ("Failed to shmat: %s", g_strerror (errno)));
480 /* Clean up the shared memory segment */
481 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
485 ximage->ximage->data = ximage->SHMInfo.shmaddr;
486 ximage->SHMInfo.readOnly = FALSE;
488 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
489 /* Clean up shm seg */
490 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
492 g_mutex_unlock (ximagesink->x_lock);
493 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
494 ("Failed to create output image buffer of %dx%d pixels",
495 ximage->width, ximage->height), ("Failed to XShmAttach"));
499 XSync (ximagesink->xcontext->disp, FALSE);
501 /* Now that everyone has attached, we can delete the shared memory segment.
502 * This way, it will be deleted as soon as we detach later, and not
503 * leaked if we crash. */
504 shmctl (ximage->SHMInfo.shmid, IPC_RMID, NULL);
507 #endif /* HAVE_XSHM */
511 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
512 ximagesink->xcontext->visual,
513 ximagesink->xcontext->depth,
515 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
516 if (!ximage->ximage || error_caught) {
517 g_mutex_unlock (ximagesink->x_lock);
518 /* Reset error handler */
519 error_caught = FALSE;
520 XSetErrorHandler (handler);
522 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
523 ("Failed to create output image buffer of %dx%d pixels",
524 ximage->width, ximage->height),
525 ("could not XCreateImage a %dx%d image",
526 ximage->width, ximage->height));
530 /* upstream will assume that rowstrides are multiples of 4, but this
531 * doesn't always seem to be the case with XCreateImage() */
532 if ((ximage->ximage->bytes_per_line % 4) != 0) {
533 GST_WARNING_OBJECT (ximagesink, "returned stride not a multiple of 4 as "
537 /* we have to use the returned bytes_per_line for our image size */
538 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
540 /* alloc a bit more for unexpected strides to avoid crashes upstream.
541 * FIXME: if we get an unrounded stride, the image will be displayed
542 * distorted, since all upstream elements assume a rounded stride */
544 GST_ROUND_UP_4 (ximage->ximage->bytes_per_line) *
545 ximage->ximage->height;
546 ximage->ximage->data = g_malloc (allocsize);
547 GST_LOG_OBJECT (ximagesink,
548 "non-XShm image size is %" G_GSIZE_FORMAT " (alloced: %u), width %d, "
549 "stride %d", ximage->size, allocsize, ximage->width,
550 ximage->ximage->bytes_per_line);
552 XSync (ximagesink->xcontext->disp, FALSE);
555 /* Reset error handler */
556 error_caught = FALSE;
557 XSetErrorHandler (handler);
561 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
562 GST_BUFFER_SIZE (ximage) = ximage->size;
564 /* Keep a ref to our sink */
565 ximage->ximagesink = gst_object_ref (ximagesink);
567 g_mutex_unlock (ximagesink->x_lock);
570 gst_ximage_buffer_free (ximage);
577 /* This function destroys a GstXImageBuffer handling XShm availability */
579 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
580 GstXImageBuffer * ximage)
582 g_return_if_fail (ximage != NULL);
583 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
585 /* Hold the object lock to ensure the XContext doesn't disappear */
586 GST_OBJECT_LOCK (ximagesink);
588 /* If the destroyed image is the current one we destroy our reference too */
589 if (ximagesink->cur_image == ximage) {
590 ximagesink->cur_image = NULL;
593 /* We might have some buffers destroyed after changing state to NULL */
594 if (!ximagesink->xcontext) {
595 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
597 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
598 shmdt (ximage->SHMInfo.shmaddr);
604 g_mutex_lock (ximagesink->x_lock);
607 if (ximagesink->xcontext->use_xshm) {
608 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
609 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
610 XSync (ximagesink->xcontext->disp, 0);
611 shmdt (ximage->SHMInfo.shmaddr);
614 XDestroyImage (ximage->ximage);
617 #endif /* HAVE_XSHM */
619 if (ximage->ximage) {
620 XDestroyImage (ximage->ximage);
624 XSync (ximagesink->xcontext->disp, FALSE);
626 g_mutex_unlock (ximagesink->x_lock);
629 GST_OBJECT_UNLOCK (ximagesink);
631 if (ximage->ximagesink) {
632 /* Release the ref to our sink */
633 ximage->ximagesink = NULL;
634 gst_object_unref (ximagesink);
640 /* We are called with the x_lock taken */
642 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
643 GstXWindow * xwindow, GstVideoRectangle rect)
645 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
646 g_return_if_fail (xwindow != NULL);
648 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
649 ximagesink->xcontext->black);
653 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
654 0, 0, rect.x, xwindow->height);
658 if ((rect.x + rect.w) < xwindow->width) {
659 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
660 rect.x + rect.w, 0, xwindow->width, xwindow->height);
665 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
666 0, 0, xwindow->width, rect.y);
670 if ((rect.y + rect.h) < xwindow->height) {
671 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
672 0, rect.y + rect.h, xwindow->width, xwindow->height);
676 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
678 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
680 GstVideoRectangle src, dst, result;
681 gboolean draw_border = FALSE;
683 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
685 /* We take the flow_lock. If expose is in there we don't want to run
686 concurrently from the data flow thread */
687 g_mutex_lock (ximagesink->flow_lock);
689 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
690 g_mutex_unlock (ximagesink->flow_lock);
694 /* Draw borders when displaying the first frame. After this
695 draw borders only on expose event or caps change (ximagesink->draw_border = TRUE). */
696 if (!ximagesink->cur_image || ximagesink->draw_border) {
700 /* Store a reference to the last image we put, lose the previous one */
701 if (ximage && ximagesink->cur_image != ximage) {
702 if (ximagesink->cur_image) {
703 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
704 gst_buffer_unref (ximagesink->cur_image);
706 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
707 ximagesink->cur_image =
708 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
711 /* Expose sends a NULL image, we take the latest frame */
714 if (ximagesink->cur_image) {
715 ximage = ximagesink->cur_image;
717 g_mutex_unlock (ximagesink->flow_lock);
722 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
724 src.w = ximage->width;
725 src.h = ximage->height;
726 dst.w = ximagesink->xwindow->width;
727 dst.h = ximagesink->xwindow->height;
729 gst_video_sink_center_rect (src, dst, &result, FALSE);
731 g_mutex_lock (ximagesink->x_lock);
734 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
736 ximagesink->draw_border = FALSE;
739 if (ximagesink->xcontext->use_xshm) {
740 GST_LOG_OBJECT (ximagesink,
741 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
742 ximage, 0, 0, result.x, result.y, result.w, result.h,
743 ximagesink->xwindow->width, ximagesink->xwindow->height);
744 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
745 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
746 result.w, result.h, FALSE);
748 #endif /* HAVE_XSHM */
750 GST_LOG_OBJECT (ximagesink,
751 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
752 ximage, 0, 0, result.x, result.y, result.w, result.h,
753 ximagesink->xwindow->width, ximagesink->xwindow->height);
754 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
755 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
759 XSync (ximagesink->xcontext->disp, FALSE);
761 g_mutex_unlock (ximagesink->x_lock);
763 g_mutex_unlock (ximagesink->flow_lock);
769 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
772 Atom hints_atom = None;
775 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
776 g_return_val_if_fail (window != NULL, FALSE);
778 g_mutex_lock (ximagesink->x_lock);
780 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
781 if (hints_atom == None) {
782 g_mutex_unlock (ximagesink->x_lock);
786 hints = g_malloc0 (sizeof (MotifWmHints));
788 hints->flags |= MWM_HINTS_DECORATIONS;
789 hints->decorations = 1 << 0;
791 XChangeProperty (ximagesink->xcontext->disp, window->win,
792 hints_atom, hints_atom, 32, PropModeReplace,
793 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
795 XSync (ximagesink->xcontext->disp, FALSE);
797 g_mutex_unlock (ximagesink->x_lock);
804 /* This function handles a GstXWindow creation */
806 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
808 GstXWindow *xwindow = NULL;
811 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
813 xwindow = g_new0 (GstXWindow, 1);
815 xwindow->width = width;
816 xwindow->height = height;
817 xwindow->internal = TRUE;
819 g_mutex_lock (ximagesink->x_lock);
821 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
822 ximagesink->xcontext->root,
823 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
825 /* We have to do that to prevent X from redrawing the background on
826 ConfigureNotify. This takes away flickering of video when resizing. */
827 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
829 if (ximagesink->handle_events) {
832 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
833 StructureNotifyMask | PointerMotionMask | KeyPressMask |
834 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
836 /* Tell the window manager we'd like delete client messages instead of
838 wm_delete = XInternAtom (ximagesink->xcontext->disp,
839 "WM_DELETE_WINDOW", False);
840 (void) XSetWMProtocols (ximagesink->xcontext->disp, xwindow->win,
844 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
847 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
849 XSync (ximagesink->xcontext->disp, FALSE);
851 g_mutex_unlock (ximagesink->x_lock);
853 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
855 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
860 /* This function destroys a GstXWindow */
862 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
863 GstXWindow * xwindow)
865 g_return_if_fail (xwindow != NULL);
866 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
868 g_mutex_lock (ximagesink->x_lock);
870 /* If we did not create that window we just free the GC and let it live */
871 if (xwindow->internal)
872 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
874 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
876 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
878 XSync (ximagesink->xcontext->disp, FALSE);
880 g_mutex_unlock (ximagesink->x_lock);
886 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
887 GstXWindow * xwindow)
889 XWindowAttributes attr;
891 g_return_if_fail (xwindow != NULL);
892 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
894 /* Update the window geometry */
895 g_mutex_lock (ximagesink->x_lock);
897 XGetWindowAttributes (ximagesink->xcontext->disp,
898 ximagesink->xwindow->win, &attr);
900 ximagesink->xwindow->width = attr.width;
901 ximagesink->xwindow->height = attr.height;
903 g_mutex_unlock (ximagesink->x_lock);
907 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
909 g_return_if_fail (xwindow != NULL);
910 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
912 g_mutex_lock (ximagesink->x_lock);
914 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
915 ximagesink->xcontext->black);
917 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
918 0, 0, xwindow->width, xwindow->height);
920 XSync (ximagesink->xcontext->disp, FALSE);
922 g_mutex_unlock (ximagesink->x_lock);
925 /* This function handles XEvents that might be in the queue. It generates
926 GstEvent that will be sent upstream in the pipeline to handle interactivity
929 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
932 guint pointer_x = 0, pointer_y = 0;
933 gboolean pointer_moved = FALSE;
934 gboolean exposed = FALSE, configured = FALSE;
936 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
938 /* Then we get all pointer motion events, only the last position is
940 g_mutex_lock (ximagesink->flow_lock);
941 g_mutex_lock (ximagesink->x_lock);
942 while (XCheckWindowEvent (ximagesink->xcontext->disp,
943 ximagesink->xwindow->win, PointerMotionMask, &e)) {
944 g_mutex_unlock (ximagesink->x_lock);
945 g_mutex_unlock (ximagesink->flow_lock);
949 pointer_x = e.xmotion.x;
950 pointer_y = e.xmotion.y;
951 pointer_moved = TRUE;
956 g_mutex_lock (ximagesink->flow_lock);
957 g_mutex_lock (ximagesink->x_lock);
961 g_mutex_unlock (ximagesink->x_lock);
962 g_mutex_unlock (ximagesink->flow_lock);
964 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
965 pointer_x, pointer_y);
966 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
967 "mouse-move", 0, pointer_x, pointer_y);
969 g_mutex_lock (ximagesink->flow_lock);
970 g_mutex_lock (ximagesink->x_lock);
973 /* We get all remaining events on our window to throw them upstream */
974 while (XCheckWindowEvent (ximagesink->xcontext->disp,
975 ximagesink->xwindow->win,
976 KeyPressMask | KeyReleaseMask |
977 ButtonPressMask | ButtonReleaseMask, &e)) {
980 /* We lock only for the X function call */
981 g_mutex_unlock (ximagesink->x_lock);
982 g_mutex_unlock (ximagesink->flow_lock);
986 /* Mouse button pressed/released over our window. We send upstream
987 events for interactivity/navigation */
988 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
989 e.xbutton.button, e.xbutton.x, e.xbutton.x);
990 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
991 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
994 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
995 e.xbutton.button, e.xbutton.x, e.xbutton.x);
996 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
997 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
1001 /* Key pressed/released over our window. We send upstream
1002 events for interactivity/navigation */
1003 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
1004 e.xkey.keycode, e.xkey.x, e.xkey.x);
1005 g_mutex_lock (ximagesink->x_lock);
1006 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
1008 g_mutex_unlock (ximagesink->x_lock);
1009 if (keysym != NoSymbol) {
1010 char *key_str = NULL;
1012 g_mutex_lock (ximagesink->x_lock);
1013 key_str = XKeysymToString (keysym);
1014 g_mutex_unlock (ximagesink->x_lock);
1015 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1016 e.type == KeyPress ? "key-press" : "key-release", key_str);
1019 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
1020 e.type == KeyPress ? "key-press" : "key-release", "unknown");
1024 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
1027 g_mutex_lock (ximagesink->flow_lock);
1028 g_mutex_lock (ximagesink->x_lock);
1031 while (XCheckWindowEvent (ximagesink->xcontext->disp,
1032 ximagesink->xwindow->win, ExposureMask | StructureNotifyMask, &e)) {
1037 case ConfigureNotify:
1045 if (ximagesink->handle_expose && (exposed || configured)) {
1046 g_mutex_unlock (ximagesink->x_lock);
1047 g_mutex_unlock (ximagesink->flow_lock);
1049 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1051 g_mutex_lock (ximagesink->flow_lock);
1052 g_mutex_lock (ximagesink->x_lock);
1055 /* Handle Display events */
1056 while (XPending (ximagesink->xcontext->disp)) {
1057 XNextEvent (ximagesink->xcontext->disp, &e);
1060 case ClientMessage:{
1063 wm_delete = XInternAtom (ximagesink->xcontext->disp,
1064 "WM_DELETE_WINDOW", False);
1065 if (wm_delete == (Atom) e.xclient.data.l[0]) {
1066 /* Handle window deletion by posting an error on the bus */
1067 GST_ELEMENT_ERROR (ximagesink, RESOURCE, NOT_FOUND,
1068 ("Output window was closed"), (NULL));
1070 g_mutex_unlock (ximagesink->x_lock);
1071 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1072 ximagesink->xwindow = NULL;
1073 g_mutex_lock (ximagesink->x_lock);
1082 g_mutex_unlock (ximagesink->x_lock);
1083 g_mutex_unlock (ximagesink->flow_lock);
1087 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1089 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1091 GST_OBJECT_LOCK (ximagesink);
1092 while (ximagesink->running) {
1093 GST_OBJECT_UNLOCK (ximagesink);
1095 if (ximagesink->xwindow) {
1096 gst_ximagesink_handle_xevents (ximagesink);
1100 GST_OBJECT_LOCK (ximagesink);
1102 GST_OBJECT_UNLOCK (ximagesink);
1107 /* This function calculates the pixel aspect ratio based on the properties
1108 * in the xcontext structure and stores it there. */
1110 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1112 static const gint par[][2] = {
1113 {1, 1}, /* regular screen */
1114 {16, 15}, /* PAL TV */
1115 {11, 10}, /* 525 line Rec.601 video */
1116 {54, 59}, /* 625 line Rec.601 video */
1117 {64, 45}, /* 1280x1024 on 16:9 display */
1118 {5, 3}, /* 1280x1024 on 4:3 display */
1119 {4, 3} /* 800x600 on 16:9 display */
1126 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1128 /* first calculate the "real" ratio based on the X values;
1129 * which is the "physical" w/h divided by the w/h in pixels of the display */
1130 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1131 / (xcontext->heightmm * xcontext->width);
1133 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1135 if (xcontext->width == 720 && xcontext->height == 576) {
1136 ratio = 4.0 * 576 / (3.0 * 720);
1138 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1140 /* now find the one from par[][2] with the lowest delta to the real one */
1144 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1145 gdouble this_delta = DELTA (i);
1147 if (this_delta < delta) {
1153 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1154 par[index][0], par[index][1]);
1156 g_free (xcontext->par);
1157 xcontext->par = g_new0 (GValue, 1);
1158 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1159 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1160 GST_DEBUG ("set xcontext PAR to %d/%d",
1161 gst_value_get_fraction_numerator (xcontext->par),
1162 gst_value_get_fraction_denominator (xcontext->par));
1165 /* This function gets the X Display and global info about it. Everything is
1166 stored in our object and will be cleaned when the object is disposed. Note
1167 here that caps for supported format are generated without any window or
1169 static GstXContext *
1170 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1172 GstXContext *xcontext = NULL;
1173 XPixmapFormatValues *px_formats = NULL;
1174 gint nb_formats = 0, i;
1176 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1178 xcontext = g_new0 (GstXContext, 1);
1180 g_mutex_lock (ximagesink->x_lock);
1182 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1184 if (!xcontext->disp) {
1185 g_mutex_unlock (ximagesink->x_lock);
1187 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1188 ("Could not initialise X output"), ("Could not open display"));
1192 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1193 xcontext->screen_num = DefaultScreen (xcontext->disp);
1194 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1195 xcontext->root = DefaultRootWindow (xcontext->disp);
1196 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1197 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1198 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1200 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1201 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1202 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1203 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1205 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1206 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1208 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1210 /* We get supported pixmap formats at supported depth */
1211 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1214 XCloseDisplay (xcontext->disp);
1215 g_mutex_unlock (ximagesink->x_lock);
1216 g_free (xcontext->par);
1221 /* We get bpp value corresponding to our running depth */
1222 for (i = 0; i < nb_formats; i++) {
1223 if (px_formats[i].depth == xcontext->depth)
1224 xcontext->bpp = px_formats[i].bits_per_pixel;
1229 xcontext->endianness =
1230 (ImageByteOrder (xcontext->disp) ==
1231 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1233 /* Search for XShm extension support */
1235 if (XShmQueryExtension (xcontext->disp) &&
1236 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1237 xcontext->use_xshm = TRUE;
1238 GST_DEBUG ("ximagesink is using XShm extension");
1242 xcontext->use_xshm = FALSE;
1243 GST_DEBUG ("ximagesink is not using XShm extension");
1246 /* our caps system handles 24/32bpp RGB as big-endian. */
1247 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1248 xcontext->endianness == G_LITTLE_ENDIAN) {
1249 xcontext->endianness = G_BIG_ENDIAN;
1250 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1251 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1252 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1253 if (xcontext->bpp == 24) {
1254 xcontext->visual->red_mask >>= 8;
1255 xcontext->visual->green_mask >>= 8;
1256 xcontext->visual->blue_mask >>= 8;
1260 /* update object's par with calculated one if not set yet */
1261 if (!ximagesink->par) {
1262 ximagesink->par = g_new0 (GValue, 1);
1263 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1264 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1266 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1267 "bpp", G_TYPE_INT, xcontext->bpp,
1268 "depth", G_TYPE_INT, xcontext->depth,
1269 "endianness", G_TYPE_INT, xcontext->endianness,
1270 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1271 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1272 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1273 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1274 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1275 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1276 if (ximagesink->par) {
1279 nom = gst_value_get_fraction_numerator (ximagesink->par);
1280 den = gst_value_get_fraction_denominator (ximagesink->par);
1281 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1282 GST_TYPE_FRACTION, nom, den, NULL);
1285 g_mutex_unlock (ximagesink->x_lock);
1287 /* Setup our event listening thread */
1288 GST_OBJECT_LOCK (ximagesink);
1289 ximagesink->running = TRUE;
1290 ximagesink->event_thread = g_thread_create (
1291 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1292 GST_OBJECT_UNLOCK (ximagesink);
1297 /* This function cleans the X context. Closing the Display and unrefing the
1298 caps for supported formats. */
1300 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1302 GstXContext *xcontext;
1304 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1306 GST_OBJECT_LOCK (ximagesink);
1307 if (ximagesink->xcontext == NULL) {
1308 GST_OBJECT_UNLOCK (ximagesink);
1312 /* Take the xcontext reference and NULL it while we
1313 * clean it up, so that any buffer-alloced buffers
1314 * arriving after this will be freed correctly */
1315 xcontext = ximagesink->xcontext;
1316 ximagesink->xcontext = NULL;
1318 GST_OBJECT_UNLOCK (ximagesink);
1320 gst_caps_unref (xcontext->caps);
1321 g_free (xcontext->par);
1322 g_free (ximagesink->par);
1323 ximagesink->par = NULL;
1325 g_mutex_lock (ximagesink->x_lock);
1327 XCloseDisplay (xcontext->disp);
1329 g_mutex_unlock (ximagesink->x_lock);
1335 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1338 g_mutex_lock (ximagesink->pool_lock);
1340 while (ximagesink->buffer_pool) {
1341 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1343 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1344 ximagesink->buffer_pool);
1345 gst_ximage_buffer_free (ximage);
1348 g_mutex_unlock (ximagesink->pool_lock);
1354 gst_ximagesink_getcaps (GstBaseSink * bsink)
1356 GstXImageSink *ximagesink;
1360 ximagesink = GST_XIMAGESINK (bsink);
1362 if (ximagesink->xcontext)
1363 return gst_caps_ref (ximagesink->xcontext->caps);
1365 /* get a template copy and add the pixel aspect ratio */
1367 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->
1369 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1370 GstStructure *structure = gst_caps_get_structure (caps, i);
1372 if (ximagesink->par) {
1375 nom = gst_value_get_fraction_numerator (ximagesink->par);
1376 den = gst_value_get_fraction_denominator (ximagesink->par);
1377 gst_structure_set (structure, "pixel-aspect-ratio",
1378 GST_TYPE_FRACTION, nom, den, NULL);
1386 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1388 GstXImageSink *ximagesink;
1389 gboolean ret = TRUE;
1390 GstStructure *structure;
1391 GstCaps *intersection;
1393 gint new_width, new_height;
1396 ximagesink = GST_XIMAGESINK (bsink);
1398 if (!ximagesink->xcontext)
1401 GST_DEBUG_OBJECT (ximagesink,
1402 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1403 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1405 /* We intersect those caps with our template to make sure they are correct */
1406 intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1407 GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1409 if (gst_caps_is_empty (intersection)) {
1410 gst_caps_unref (intersection);
1414 gst_caps_unref (intersection);
1416 structure = gst_caps_get_structure (caps, 0);
1418 ret &= gst_structure_get_int (structure, "width", &new_width);
1419 ret &= gst_structure_get_int (structure, "height", &new_height);
1420 fps = gst_structure_get_value (structure, "framerate");
1421 ret &= (fps != NULL);
1425 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1426 * otherwise linking should fail */
1427 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1429 if (ximagesink->par) {
1430 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1433 } else if (ximagesink->xcontext->par) {
1434 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1440 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1441 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1442 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1443 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1445 /* Notify application to set xwindow id now */
1446 g_mutex_lock (ximagesink->flow_lock);
1447 if (!ximagesink->xwindow) {
1448 g_mutex_unlock (ximagesink->flow_lock);
1449 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1451 g_mutex_unlock (ximagesink->flow_lock);
1454 /* Creating our window and our image */
1455 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1456 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1457 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1458 ("Invalid image size."));
1462 g_mutex_lock (ximagesink->flow_lock);
1463 if (!ximagesink->xwindow) {
1464 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1465 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1467 /* Remember to draw borders for next frame */
1468 ximagesink->draw_border = TRUE;
1469 g_mutex_unlock (ximagesink->flow_lock);
1471 /* If our ximage has changed we destroy it, next chain iteration will create
1473 if ((ximagesink->ximage) &&
1474 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1475 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1476 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1477 ximagesink->ximage);
1478 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1479 ximagesink->ximage = NULL;
1487 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1492 static GstStateChangeReturn
1493 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1495 GstXImageSink *ximagesink;
1496 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1497 GstXContext *xcontext = NULL;
1499 ximagesink = GST_XIMAGESINK (element);
1501 switch (transition) {
1502 case GST_STATE_CHANGE_NULL_TO_READY:
1504 /* Initializing the XContext */
1505 if (ximagesink->xcontext == NULL) {
1506 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1507 if (xcontext == NULL) {
1508 ret = GST_STATE_CHANGE_FAILURE;
1511 GST_OBJECT_LOCK (ximagesink);
1513 ximagesink->xcontext = xcontext;
1514 GST_OBJECT_UNLOCK (ximagesink);
1517 /* call XSynchronize with the current value of synchronous */
1518 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1519 ximagesink->synchronous ? "TRUE" : "FALSE");
1520 g_mutex_lock (ximagesink->x_lock);
1521 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1522 g_mutex_unlock (ximagesink->x_lock);
1524 case GST_STATE_CHANGE_READY_TO_PAUSED:
1525 g_mutex_lock (ximagesink->flow_lock);
1526 if (ximagesink->xwindow)
1527 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1528 g_mutex_unlock (ximagesink->flow_lock);
1530 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1536 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1538 switch (transition) {
1539 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1541 case GST_STATE_CHANGE_PAUSED_TO_READY:
1542 ximagesink->fps_n = 0;
1543 ximagesink->fps_d = 1;
1544 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1545 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1547 case GST_STATE_CHANGE_READY_TO_NULL:
1548 gst_ximagesink_reset (ximagesink);
1559 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1560 GstClockTime * start, GstClockTime * end)
1562 GstXImageSink *ximagesink;
1564 ximagesink = GST_XIMAGESINK (bsink);
1566 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1567 *start = GST_BUFFER_TIMESTAMP (buf);
1568 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1569 *end = *start + GST_BUFFER_DURATION (buf);
1571 if (ximagesink->fps_n > 0) {
1573 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1580 static GstFlowReturn
1581 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1583 GstXImageSink *ximagesink;
1585 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1587 ximagesink = GST_XIMAGESINK (bsink);
1589 /* If this buffer has been allocated using our buffer management we simply
1590 put the ximage which is in the PRIVATE pointer */
1591 if (GST_IS_XIMAGE_BUFFER (buf)) {
1592 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1593 if (!gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf)))
1596 /* Else we have to copy the data into our private image, */
1597 /* if we have one... */
1598 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1599 if (!ximagesink->ximage) {
1600 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1601 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1602 GST_BUFFER_CAPS (buf));
1603 if (!ximagesink->ximage)
1604 /* The create method should have posted an informative error */
1607 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1608 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1609 ("Failed to create output image buffer of %dx%d pixels",
1610 ximagesink->ximage->width, ximagesink->ximage->height),
1611 ("XServer allocated buffer size did not match input buffer"));
1613 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1614 ximagesink->ximage = NULL;
1618 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1619 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1620 if (!gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage))
1629 /* No image available. That's very bad ! */
1630 GST_DEBUG ("could not create image");
1631 return GST_FLOW_ERROR;
1635 /* No Window available to put our image into */
1636 GST_WARNING_OBJECT (ximagesink, "could not output image - no window");
1637 return GST_FLOW_ERROR;
1641 /* Buffer management
1643 * The buffer_alloc function must either return a buffer with given size and
1644 * caps or create a buffer with different caps attached to the buffer. This
1645 * last option is called reverse negotiation, ie, where the sink suggests a
1646 * different format from the upstream peer.
1648 * We try to do reverse negotiation when our geometry changes and we like a
1651 static GstFlowReturn
1652 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1653 GstCaps * caps, GstBuffer ** buf)
1655 GstXImageSink *ximagesink;
1656 GstXImageBuffer *ximage = NULL;
1657 GstStructure *structure = NULL;
1658 GstFlowReturn ret = GST_FLOW_OK;
1659 GstCaps *alloc_caps;
1660 gboolean alloc_unref = FALSE;
1663 ximagesink = GST_XIMAGESINK (bsink);
1665 GST_LOG_OBJECT (ximagesink,
1666 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1667 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1669 /* assume we're going to alloc what was requested, keep track of
1670 * wheter we need to unref or not. When we suggest a new format
1671 * upstream we will create a new caps that we need to unref. */
1673 alloc_unref = FALSE;
1675 /* get struct to see what is requested */
1676 structure = gst_caps_get_structure (caps, 0);
1678 if (gst_structure_get_int (structure, "width", &width) &&
1679 gst_structure_get_int (structure, "height", &height)) {
1680 GstVideoRectangle dst, src, result;
1685 /* We take the flow_lock because the window might go away */
1686 g_mutex_lock (ximagesink->flow_lock);
1687 if (!ximagesink->xwindow) {
1688 g_mutex_unlock (ximagesink->flow_lock);
1692 /* What is our geometry */
1693 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1694 dst.w = ximagesink->xwindow->width;
1695 dst.h = ximagesink->xwindow->height;
1697 g_mutex_unlock (ximagesink->flow_lock);
1699 if (ximagesink->keep_aspect) {
1700 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1702 gst_video_sink_center_rect (src, dst, &result, TRUE);
1704 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1705 "ignoring aspect ratio");
1706 result.x = result.y = 0;
1711 /* We would like another geometry */
1712 if (width != result.w || height != result.h) {
1714 GstCaps *desired_caps;
1715 GstStructure *desired_struct;
1717 /* make a copy of the incomming caps to create the new
1718 * suggestion. We can't use make_writable because we might
1719 * then destroy the original caps which we still need when the
1720 * peer does not accept the suggestion. */
1721 desired_caps = gst_caps_copy (caps);
1722 desired_struct = gst_caps_get_structure (desired_caps, 0);
1724 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1725 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1726 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1728 /* PAR property overrides the X calculated one */
1729 if (ximagesink->par) {
1730 nom = gst_value_get_fraction_numerator (ximagesink->par);
1731 den = gst_value_get_fraction_denominator (ximagesink->par);
1732 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1733 GST_TYPE_FRACTION, nom, den, NULL);
1734 } else if (ximagesink->xcontext->par) {
1735 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1736 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1737 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1738 GST_TYPE_FRACTION, nom, den, NULL);
1741 /* see if peer accepts our new suggestion, if there is no peer, this
1742 * function returns true. */
1743 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1747 bpp = size / height / width;
1748 /* we will not alloc a buffer of the new suggested caps. Make sure
1749 * we also unref this new caps after we set it on the buffer. */
1750 alloc_caps = desired_caps;
1754 size = bpp * width * height;
1755 GST_DEBUG ("peer pad accepts our desired caps %" GST_PTR_FORMAT
1756 " buffer size is now %d bytes", desired_caps, size);
1758 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1760 /* we alloc a buffer with the original incomming caps */
1761 width = GST_VIDEO_SINK_WIDTH (ximagesink);
1762 height = GST_VIDEO_SINK_HEIGHT (ximagesink);
1768 /* Inspect our buffer pool */
1769 g_mutex_lock (ximagesink->pool_lock);
1770 while (ximagesink->buffer_pool) {
1771 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1774 /* Removing from the pool */
1775 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1776 ximagesink->buffer_pool);
1778 /* If the ximage is invalid for our need, destroy */
1779 if ((ximage->width != width) || (ximage->height != height)) {
1780 gst_ximage_buffer_free (ximage);
1783 /* We found a suitable ximage */
1788 g_mutex_unlock (ximagesink->pool_lock);
1790 /* We haven't found anything, creating a new one */
1792 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1794 /* Now we should have a ximage, set appropriate caps on it */
1796 /* Make sure the buffer is cleared of any previously used flags */
1797 GST_MINI_OBJECT_CAST (ximage)->flags = 0;
1798 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1801 /* could be our new reffed suggestion or the original unreffed caps */
1803 gst_caps_unref (alloc_caps);
1805 *buf = GST_BUFFER_CAST (ximage);
1810 /* Interfaces stuff */
1813 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1815 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1820 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1822 klass->supported = gst_ximagesink_interface_supported;
1826 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1827 GstStructure * structure)
1829 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1831 gint x_offset, y_offset;
1835 event = gst_event_new_navigation (structure);
1837 /* We are not converting the pointer coordinates as there's no hardware
1838 scaling done here. The only possible scaling is done by videoscale and
1839 videoscale will have to catch those events and tranform the coordinates
1840 to match the applied scaling. So here we just add the offset if the image
1841 is centered in the window. */
1843 /* We take the flow_lock while we look at the window */
1844 g_mutex_lock (ximagesink->flow_lock);
1846 if (!ximagesink->xwindow) {
1847 g_mutex_unlock (ximagesink->flow_lock);
1851 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1852 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1854 g_mutex_unlock (ximagesink->flow_lock);
1856 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1858 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1860 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1862 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1865 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1867 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1868 gst_pad_send_event (pad, event);
1870 gst_object_unref (pad);
1875 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1877 iface->send_event = gst_ximagesink_navigation_send_event;
1881 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1883 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1884 GstXWindow *xwindow = NULL;
1885 XWindowAttributes attr;
1887 /* We acquire the stream lock while setting this window in the element.
1888 We are basically cleaning tons of stuff replacing the old window, putting
1889 images while we do that would surely crash */
1890 g_mutex_lock (ximagesink->flow_lock);
1892 /* If we already use that window return */
1893 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1894 g_mutex_unlock (ximagesink->flow_lock);
1898 /* If the element has not initialized the X11 context try to do so */
1899 if (!ximagesink->xcontext &&
1900 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1901 g_mutex_unlock (ximagesink->flow_lock);
1902 /* we have thrown a GST_ELEMENT_ERROR now */
1906 /* If a window is there already we destroy it */
1907 if (ximagesink->xwindow) {
1908 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1909 ximagesink->xwindow = NULL;
1912 /* If the xid is 0 we go back to an internal window */
1913 if (xwindow_id == 0) {
1914 /* If no width/height caps nego did not happen window will be created
1915 during caps nego then */
1916 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1917 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1918 GST_VIDEO_SINK_WIDTH (ximagesink),
1919 GST_VIDEO_SINK_HEIGHT (ximagesink));
1922 xwindow = g_new0 (GstXWindow, 1);
1924 xwindow->win = xwindow_id;
1926 /* We get window geometry, set the event we want to receive,
1928 g_mutex_lock (ximagesink->x_lock);
1929 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1930 xwindow->width = attr.width;
1931 xwindow->height = attr.height;
1932 xwindow->internal = FALSE;
1933 if (ximagesink->handle_events) {
1934 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1935 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1939 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1940 g_mutex_unlock (ximagesink->x_lock);
1944 ximagesink->xwindow = xwindow;
1946 g_mutex_unlock (ximagesink->flow_lock);
1950 gst_ximagesink_expose (GstXOverlay * overlay)
1952 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1954 gst_ximagesink_ximage_put (ximagesink, NULL);
1958 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1959 gboolean handle_events)
1961 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1963 ximagesink->handle_events = handle_events;
1965 g_mutex_lock (ximagesink->flow_lock);
1967 if (G_UNLIKELY (!ximagesink->xwindow)) {
1968 g_mutex_unlock (ximagesink->flow_lock);
1972 g_mutex_lock (ximagesink->x_lock);
1974 if (handle_events) {
1975 if (ximagesink->xwindow->internal) {
1976 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1977 ExposureMask | StructureNotifyMask | PointerMotionMask |
1978 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1980 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1981 ExposureMask | StructureNotifyMask | PointerMotionMask |
1982 KeyPressMask | KeyReleaseMask);
1985 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1988 g_mutex_unlock (ximagesink->x_lock);
1990 g_mutex_unlock (ximagesink->flow_lock);
1994 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1996 iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1997 iface->expose = gst_ximagesink_expose;
1998 iface->handle_events = gst_ximagesink_set_event_handling;
2001 /* =========================================== */
2003 /* Init & Class init */
2005 /* =========================================== */
2008 gst_ximagesink_set_property (GObject * object, guint prop_id,
2009 const GValue * value, GParamSpec * pspec)
2011 GstXImageSink *ximagesink;
2013 g_return_if_fail (GST_IS_XIMAGESINK (object));
2015 ximagesink = GST_XIMAGESINK (object);
2019 ximagesink->display_name = g_strdup (g_value_get_string (value));
2021 case PROP_SYNCHRONOUS:
2022 ximagesink->synchronous = g_value_get_boolean (value);
2023 if (ximagesink->xcontext) {
2024 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
2025 ximagesink->synchronous ? "TRUE" : "FALSE");
2026 g_mutex_lock (ximagesink->x_lock);
2027 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
2028 g_mutex_unlock (ximagesink->x_lock);
2031 case PROP_FORCE_ASPECT_RATIO:
2032 ximagesink->keep_aspect = g_value_get_boolean (value);
2034 case PROP_PIXEL_ASPECT_RATIO:
2038 tmp = g_new0 (GValue, 1);
2039 g_value_init (tmp, GST_TYPE_FRACTION);
2041 if (!g_value_transform (value, tmp)) {
2042 GST_WARNING_OBJECT (ximagesink,
2043 "Could not transform string to aspect ratio");
2046 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
2047 gst_value_get_fraction_numerator (tmp),
2048 gst_value_get_fraction_denominator (tmp));
2049 g_free (ximagesink->par);
2050 ximagesink->par = tmp;
2054 case PROP_HANDLE_EVENTS:
2055 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2056 g_value_get_boolean (value));
2058 case PROP_HANDLE_EXPOSE:
2059 ximagesink->handle_expose = g_value_get_boolean (value);
2062 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2068 gst_ximagesink_get_property (GObject * object, guint prop_id,
2069 GValue * value, GParamSpec * pspec)
2071 GstXImageSink *ximagesink;
2073 g_return_if_fail (GST_IS_XIMAGESINK (object));
2075 ximagesink = GST_XIMAGESINK (object);
2079 g_value_set_string (value, ximagesink->display_name);
2081 case PROP_SYNCHRONOUS:
2082 g_value_set_boolean (value, ximagesink->synchronous);
2084 case PROP_FORCE_ASPECT_RATIO:
2085 g_value_set_boolean (value, ximagesink->keep_aspect);
2087 case PROP_PIXEL_ASPECT_RATIO:
2088 if (ximagesink->par)
2089 g_value_transform (ximagesink->par, value);
2091 case PROP_HANDLE_EVENTS:
2092 g_value_set_boolean (value, ximagesink->handle_events);
2094 case PROP_HANDLE_EXPOSE:
2095 g_value_set_boolean (value, ximagesink->handle_expose);
2098 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2104 gst_ximagesink_reset (GstXImageSink * ximagesink)
2108 GST_OBJECT_LOCK (ximagesink);
2109 ximagesink->running = FALSE;
2110 /* grab thread and mark it as NULL */
2111 thread = ximagesink->event_thread;
2112 ximagesink->event_thread = NULL;
2113 GST_OBJECT_UNLOCK (ximagesink);
2115 /* Wait for our event thread to finish before we clean up our stuff. */
2117 g_thread_join (thread);
2119 if (ximagesink->ximage) {
2120 gst_buffer_unref (ximagesink->ximage);
2121 ximagesink->ximage = NULL;
2123 if (ximagesink->cur_image) {
2124 gst_buffer_unref (ximagesink->cur_image);
2125 ximagesink->cur_image = NULL;
2128 gst_ximagesink_bufferpool_clear (ximagesink);
2130 g_mutex_lock (ximagesink->flow_lock);
2131 if (ximagesink->xwindow) {
2132 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
2133 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
2134 ximagesink->xwindow = NULL;
2136 g_mutex_unlock (ximagesink->flow_lock);
2138 gst_ximagesink_xcontext_clear (ximagesink);
2142 gst_ximagesink_finalize (GObject * object)
2144 GstXImageSink *ximagesink;
2146 ximagesink = GST_XIMAGESINK (object);
2148 gst_ximagesink_reset (ximagesink);
2150 if (ximagesink->display_name) {
2151 g_free (ximagesink->display_name);
2152 ximagesink->display_name = NULL;
2154 if (ximagesink->par) {
2155 g_free (ximagesink->par);
2156 ximagesink->par = NULL;
2158 if (ximagesink->x_lock) {
2159 g_mutex_free (ximagesink->x_lock);
2160 ximagesink->x_lock = NULL;
2162 if (ximagesink->flow_lock) {
2163 g_mutex_free (ximagesink->flow_lock);
2164 ximagesink->flow_lock = NULL;
2166 if (ximagesink->pool_lock) {
2167 g_mutex_free (ximagesink->pool_lock);
2168 ximagesink->pool_lock = NULL;
2171 G_OBJECT_CLASS (parent_class)->finalize (object);
2175 gst_ximagesink_init (GstXImageSink * ximagesink)
2177 ximagesink->display_name = NULL;
2178 ximagesink->xcontext = NULL;
2179 ximagesink->xwindow = NULL;
2180 ximagesink->ximage = NULL;
2181 ximagesink->cur_image = NULL;
2183 ximagesink->event_thread = NULL;
2184 ximagesink->running = FALSE;
2186 ximagesink->fps_n = 0;
2187 ximagesink->fps_d = 1;
2189 ximagesink->x_lock = g_mutex_new ();
2190 ximagesink->flow_lock = g_mutex_new ();
2192 ximagesink->par = NULL;
2194 ximagesink->pool_lock = g_mutex_new ();
2195 ximagesink->buffer_pool = NULL;
2197 ximagesink->synchronous = FALSE;
2198 ximagesink->keep_aspect = FALSE;
2199 ximagesink->handle_events = TRUE;
2200 ximagesink->handle_expose = TRUE;
2204 gst_ximagesink_base_init (gpointer g_class)
2206 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2208 gst_element_class_set_details (element_class, &gst_ximagesink_details);
2210 gst_element_class_add_pad_template (element_class,
2211 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2215 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2217 GObjectClass *gobject_class;
2218 GstElementClass *gstelement_class;
2219 GstBaseSinkClass *gstbasesink_class;
2221 gobject_class = (GObjectClass *) klass;
2222 gstelement_class = (GstElementClass *) klass;
2223 gstbasesink_class = (GstBaseSinkClass *) klass;
2225 parent_class = g_type_class_peek_parent (klass);
2227 gobject_class->finalize = gst_ximagesink_finalize;
2228 gobject_class->set_property = gst_ximagesink_set_property;
2229 gobject_class->get_property = gst_ximagesink_get_property;
2231 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2232 g_param_spec_string ("display", "Display", "X Display name",
2233 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2234 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2235 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2236 "the X display in synchronous mode. (used only for debugging)", FALSE,
2237 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2238 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2239 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2240 "When enabled, reverse caps negotiation (scaling) will respect "
2241 "original aspect ratio", FALSE,
2242 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2243 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2244 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2245 "The pixel aspect ratio of the device", "1/1",
2246 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2247 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2248 g_param_spec_boolean ("handle-events", "Handle XEvents",
2249 "When enabled, XEvents will be selected and handled", TRUE,
2250 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2251 g_object_class_install_property (gobject_class, PROP_HANDLE_EXPOSE,
2252 g_param_spec_boolean ("handle-expose", "Handle expose",
2254 "the current frame will always be drawn in response to X Expose "
2255 "events", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
2257 gstelement_class->change_state = gst_ximagesink_change_state;
2259 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2260 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2261 gstbasesink_class->buffer_alloc =
2262 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2263 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2264 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2265 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2268 /* ============================================================= */
2270 /* Public Methods */
2272 /* ============================================================= */
2274 /* =========================================== */
2276 /* Object typing & Creation */
2278 /* =========================================== */
2281 gst_ximagesink_get_type (void)
2283 static GType ximagesink_type = 0;
2285 if (!ximagesink_type) {
2286 static const GTypeInfo ximagesink_info = {
2287 sizeof (GstXImageSinkClass),
2288 gst_ximagesink_base_init,
2290 (GClassInitFunc) gst_ximagesink_class_init,
2293 sizeof (GstXImageSink),
2295 (GInstanceInitFunc) gst_ximagesink_init,
2297 static const GInterfaceInfo iface_info = {
2298 (GInterfaceInitFunc) gst_ximagesink_interface_init,
2302 static const GInterfaceInfo navigation_info = {
2303 (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2307 static const GInterfaceInfo overlay_info = {
2308 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2313 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2314 "GstXImageSink", &ximagesink_info, 0);
2316 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2318 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2320 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2323 /* register type and create class in a more safe place instead of at
2324 * runtime since the type registration and class creation is not
2326 g_type_class_ref (gst_ximage_buffer_get_type ());
2329 return ximagesink_type;