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_ximage_destroy (GstXImageSink * ximagesink,
140 GstXImageBuffer * ximage);
141 static void gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
142 GstXWindow * xwindow);
143 static void gst_ximagesink_expose (GstXOverlay * overlay);
145 /* ElementFactory information */
146 static const GstElementDetails gst_ximagesink_details =
147 GST_ELEMENT_DETAILS ("Video sink",
149 "A standard X based videosink",
150 "Julien Moutte <julien@moutte.net>");
152 static GstStaticPadTemplate gst_ximagesink_sink_template_factory =
153 GST_STATIC_PAD_TEMPLATE ("sink",
156 GST_STATIC_CAPS ("video/x-raw-rgb, "
157 "framerate = (fraction) [ 0, MAX ], "
158 "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ]")
166 PROP_PIXEL_ASPECT_RATIO,
167 PROP_FORCE_ASPECT_RATIO,
172 static GstVideoSinkClass *parent_class = NULL;
174 /* ============================================================= */
176 /* Private Methods */
178 /* ============================================================= */
182 #define GST_TYPE_XIMAGE_BUFFER (gst_ximage_buffer_get_type())
184 #define GST_IS_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_XIMAGE_BUFFER))
185 #define GST_XIMAGE_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBuffer))
186 #define GST_XIMAGE_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_XIMAGE_BUFFER, GstXImageBufferClass))
188 /* So some words about GstMiniObject, this is pretty messy...
189 GstMiniObject does not use the standard finalizing of GObjects, you are
190 supposed to call gst_buffer_unref that's going to call gst_mini_objec_unref
191 which will handle its own refcount system and call gst_mini_object_free.
192 gst_mini_object_free will call the class finalize method which is not the
193 one from GObject, after calling this finalize method it will free the object
194 instance for you if the refcount is still 0 so you should not chain up */
196 gst_ximage_buffer_finalize (GstXImageBuffer * ximage)
198 GstXImageSink *ximagesink = NULL;
199 gboolean recycled = FALSE;
202 g_return_if_fail (ximage != NULL);
204 ximagesink = ximage->ximagesink;
205 if (G_UNLIKELY (ximagesink == NULL)) {
206 GST_WARNING_OBJECT (ximagesink, "no sink found");
210 GST_OBJECT_LOCK (ximagesink);
211 running = ximagesink->running;
212 GST_OBJECT_UNLOCK (ximagesink);
214 if (running == FALSE) {
215 /* If the sink is shutting down, need to clear the image */
216 GST_DEBUG_OBJECT (ximagesink,
217 "destroy image %p because the sink is shutting down", ximage);
218 gst_ximagesink_ximage_destroy (ximagesink, ximage);
219 } else if ((ximage->width != GST_VIDEO_SINK_WIDTH (ximagesink)) ||
220 (ximage->height != GST_VIDEO_SINK_HEIGHT (ximagesink))) {
221 /* If our geometry changed we can't reuse that image. */
222 GST_DEBUG_OBJECT (ximagesink,
223 "destroy image %p as its size changed %dx%d vs current %dx%d",
224 ximage, ximage->width, ximage->height,
225 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
226 gst_ximagesink_ximage_destroy (ximagesink, ximage);
228 /* In that case we can reuse the image and add it to our image pool. */
229 GST_LOG_OBJECT (ximagesink, "recycling image %p in pool", ximage);
230 /* need to increment the refcount again to recycle */
231 gst_buffer_ref (GST_BUFFER_CAST (ximage));
232 g_mutex_lock (ximagesink->pool_lock);
233 ximagesink->buffer_pool = g_slist_prepend (ximagesink->buffer_pool, ximage);
234 g_mutex_unlock (ximagesink->pool_lock);
243 gst_ximage_buffer_free (GstXImageBuffer * ximage)
245 /* make sure it is not recycled */
248 gst_buffer_unref (GST_BUFFER_CAST (ximage));
252 gst_ximage_buffer_init (GstXImageBuffer * ximage_buffer, gpointer g_class)
255 ximage_buffer->SHMInfo.shmaddr = ((void *) -1);
256 ximage_buffer->SHMInfo.shmid = -1;
261 gst_ximage_buffer_class_init (gpointer g_class, gpointer class_data)
263 GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class);
265 mini_object_class->finalize = (GstMiniObjectFinalizeFunction)
266 gst_ximage_buffer_finalize;
270 gst_ximage_buffer_get_type (void)
272 static GType _gst_ximage_buffer_type;
274 if (G_UNLIKELY (_gst_ximage_buffer_type == 0)) {
275 static const GTypeInfo ximage_buffer_info = {
276 sizeof (GstBufferClass),
279 gst_ximage_buffer_class_init,
282 sizeof (GstXImageBuffer),
284 (GInstanceInitFunc) gst_ximage_buffer_init,
287 _gst_ximage_buffer_type = g_type_register_static (GST_TYPE_BUFFER,
288 "GstXImageBuffer", &ximage_buffer_info, 0);
290 return _gst_ximage_buffer_type;
295 #ifdef HAVE_XSHM /* Check that XShm calls actually work */
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);
310 gst_ximagesink_check_xshm_calls (GstXImageSink * ximagesink,
311 GstXContext * xcontext)
314 XShmSegmentInfo SHMInfo;
316 int (*handler) (Display *, XErrorEvent *);
317 gboolean result = FALSE;
318 gboolean did_attach = FALSE;
320 g_return_val_if_fail (xcontext != NULL, FALSE);
322 /* Sync to ensure any older errors are already processed */
323 XSync (xcontext->disp, FALSE);
325 /* Set defaults so we don't free these later unnecessarily */
326 SHMInfo.shmaddr = ((void *) -1);
329 /* Setting an error handler to catch failure */
330 error_caught = FALSE;
331 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
333 /* Trying to create a 1x1 ximage */
334 GST_DEBUG ("XShmCreateImage of 1x1");
336 ximage = XShmCreateImage (xcontext->disp, xcontext->visual,
337 xcontext->depth, ZPixmap, NULL, &SHMInfo, 1, 1);
339 /* Might cause an error, sync to ensure it is noticed */
340 XSync (xcontext->disp, FALSE);
341 if (!ximage || error_caught) {
342 GST_WARNING ("could not XShmCreateImage a 1x1 image");
345 size = ximage->height * ximage->bytes_per_line;
347 SHMInfo.shmid = shmget (IPC_PRIVATE, size, IPC_CREAT | 0777);
348 if (SHMInfo.shmid == -1) {
349 GST_WARNING ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
354 SHMInfo.shmaddr = shmat (SHMInfo.shmid, 0, 0);
355 if (SHMInfo.shmaddr == ((void *) -1)) {
356 GST_WARNING ("Failed to shmat: %s", g_strerror (errno));
357 /* Clean up shm seg */
358 shmctl (SHMInfo.shmid, IPC_RMID, 0);
362 /* Delete the shared memory segment as soon as we manage to attach.
363 * This way, it will be deleted as soon as we detach later, and not
364 * leaked if we crash. */
365 shmctl (SHMInfo.shmid, IPC_RMID, 0);
367 ximage->data = SHMInfo.shmaddr;
368 SHMInfo.readOnly = FALSE;
370 if (XShmAttach (xcontext->disp, &SHMInfo) == 0) {
371 GST_WARNING ("Failed to XShmAttach");
375 /* Sync to ensure we see any errors we caused */
376 XSync (xcontext->disp, FALSE);
380 /* store whether we succeeded in result */
385 /* Sync to ensure we swallow any errors we caused and reset error_caught */
386 XSync (xcontext->disp, FALSE);
387 error_caught = FALSE;
388 XSetErrorHandler (handler);
391 XShmDetach (xcontext->disp, &SHMInfo);
392 XSync (xcontext->disp, FALSE);
394 if (SHMInfo.shmaddr != ((void *) -1))
395 shmdt (SHMInfo.shmaddr);
397 XDestroyImage (ximage);
400 #endif /* HAVE_XSHM */
402 /* This function handles GstXImageBuffer creation depending on XShm availability */
403 static GstXImageBuffer *
404 gst_ximagesink_ximage_new (GstXImageSink * ximagesink, GstCaps * caps)
406 GstXImageBuffer *ximage = NULL;
407 GstStructure *structure = NULL;
408 gboolean succeeded = FALSE;
409 int (*handler) (Display *, XErrorEvent *);
411 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
413 ximage = (GstXImageBuffer *) gst_mini_object_new (GST_TYPE_XIMAGE_BUFFER);
415 structure = gst_caps_get_structure (caps, 0);
417 if (!gst_structure_get_int (structure, "width", &ximage->width) ||
418 !gst_structure_get_int (structure, "height", &ximage->height)) {
419 GST_WARNING ("failed getting geometry from caps %" GST_PTR_FORMAT, caps);
422 GST_DEBUG_OBJECT (ximagesink, "creating image %p (%dx%d)", ximage,
423 ximage->width, ximage->height);
425 g_mutex_lock (ximagesink->x_lock);
427 /* Setting an error handler to catch failure */
428 error_caught = FALSE;
429 handler = XSetErrorHandler (gst_ximagesink_handle_xerror);
432 if (ximagesink->xcontext->use_xshm) {
433 ximage->ximage = XShmCreateImage (ximagesink->xcontext->disp,
434 ximagesink->xcontext->visual,
435 ximagesink->xcontext->depth,
436 ZPixmap, NULL, &ximage->SHMInfo, ximage->width, ximage->height);
437 if (!ximage->ximage || error_caught) {
438 g_mutex_unlock (ximagesink->x_lock);
439 /* Reset error handler */
440 error_caught = FALSE;
441 XSetErrorHandler (handler);
443 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
444 ("Failed to create output image buffer of %dx%d pixels",
445 ximage->width, ximage->height),
446 ("could not XShmCreateImage a %dx%d image",
447 ximage->width, ximage->height));
451 /* we have to use the returned bytes_per_line for our shm size */
452 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
453 GST_LOG_OBJECT (ximagesink,
454 "XShm image size is %" G_GSIZE_FORMAT ", width %d, stride %d",
455 ximage->size, ximage->width, ximage->ximage->bytes_per_line);
457 ximage->SHMInfo.shmid = shmget (IPC_PRIVATE, ximage->size,
459 if (ximage->SHMInfo.shmid == -1) {
460 g_mutex_unlock (ximagesink->x_lock);
461 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
462 ("Failed to create output image buffer of %dx%d pixels",
463 ximage->width, ximage->height),
464 ("could not get shared memory of %" G_GSIZE_FORMAT " bytes",
469 ximage->SHMInfo.shmaddr = shmat (ximage->SHMInfo.shmid, 0, 0);
470 if (ximage->SHMInfo.shmaddr == ((void *) -1)) {
471 g_mutex_unlock (ximagesink->x_lock);
472 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
473 ("Failed to create output image buffer of %dx%d pixels",
474 ximage->width, ximage->height),
475 ("Failed to shmat: %s", g_strerror (errno)));
476 /* Clean up the shared memory segment */
477 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
481 /* Now that we've attached, we can delete the shared memory segment.
482 * This way, it will be deleted as soon as we detach later, and not
483 * leaked if we crash. */
484 shmctl (ximage->SHMInfo.shmid, IPC_RMID, 0);
486 ximage->ximage->data = ximage->SHMInfo.shmaddr;
487 ximage->SHMInfo.readOnly = FALSE;
489 if (XShmAttach (ximagesink->xcontext->disp, &ximage->SHMInfo) == 0) {
490 g_mutex_unlock (ximagesink->x_lock);
491 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
492 ("Failed to create output image buffer of %dx%d pixels",
493 ximage->width, ximage->height), ("Failed to XShmAttach"));
497 XSync (ximagesink->xcontext->disp, FALSE);
499 #endif /* HAVE_XSHM */
501 ximage->ximage = XCreateImage (ximagesink->xcontext->disp,
502 ximagesink->xcontext->visual,
503 ximagesink->xcontext->depth,
505 ximage->width, ximage->height, ximagesink->xcontext->bpp, 0);
506 if (!ximage->ximage || error_caught) {
507 g_mutex_unlock (ximagesink->x_lock);
508 /* Reset error handler */
509 error_caught = FALSE;
510 XSetErrorHandler (handler);
512 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
513 ("Failed to create output image buffer of %dx%d pixels",
514 ximage->width, ximage->height),
515 ("could not XCreateImage a %dx%d image",
516 ximage->width, ximage->height));
520 /* we have to use the returned bytes_per_line for our image size */
521 ximage->size = ximage->ximage->bytes_per_line * ximage->ximage->height;
522 ximage->ximage->data = g_malloc (ximage->size);
524 XSync (ximagesink->xcontext->disp, FALSE);
527 /* Reset error handler */
528 error_caught = FALSE;
529 XSetErrorHandler (handler);
533 GST_BUFFER_DATA (ximage) = (guchar *) ximage->ximage->data;
534 GST_BUFFER_SIZE (ximage) = ximage->size;
536 /* Keep a ref to our sink */
537 ximage->ximagesink = gst_object_ref (ximagesink);
539 g_mutex_unlock (ximagesink->x_lock);
542 gst_ximage_buffer_free (ximage);
549 /* This function destroys a GstXImageBuffer handling XShm availability */
551 gst_ximagesink_ximage_destroy (GstXImageSink * ximagesink,
552 GstXImageBuffer * ximage)
554 g_return_if_fail (ximage != NULL);
555 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
557 /* Hold the object lock to ensure the XContext doesn't disappear */
558 GST_OBJECT_LOCK (ximagesink);
560 /* If the destroyed image is the current one we destroy our reference too */
561 if (ximagesink->cur_image == ximage) {
562 ximagesink->cur_image = NULL;
565 /* We might have some buffers destroyed after changing state to NULL */
566 if (!ximagesink->xcontext) {
567 GST_DEBUG_OBJECT (ximagesink, "Destroying XImage after XContext");
569 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
570 shmdt (ximage->SHMInfo.shmaddr);
576 g_mutex_lock (ximagesink->x_lock);
579 if (ximagesink->xcontext->use_xshm) {
580 if (ximage->SHMInfo.shmaddr != ((void *) -1)) {
581 XShmDetach (ximagesink->xcontext->disp, &ximage->SHMInfo);
582 XSync (ximagesink->xcontext->disp, 0);
583 shmdt (ximage->SHMInfo.shmaddr);
586 XDestroyImage (ximage->ximage);
589 #endif /* HAVE_XSHM */
591 if (ximage->ximage) {
592 XDestroyImage (ximage->ximage);
596 XSync (ximagesink->xcontext->disp, FALSE);
598 g_mutex_unlock (ximagesink->x_lock);
601 GST_OBJECT_UNLOCK (ximagesink);
603 if (ximage->ximagesink) {
604 /* Release the ref to our sink */
605 ximage->ximagesink = NULL;
606 gst_object_unref (ximagesink);
612 /* We are called with the x_lock taken */
614 gst_ximagesink_xwindow_draw_borders (GstXImageSink * ximagesink,
615 GstXWindow * xwindow, GstVideoRectangle rect)
617 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
618 g_return_if_fail (xwindow != NULL);
620 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
621 ximagesink->xcontext->black);
625 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
626 0, 0, rect.x, xwindow->height);
630 if ((rect.x + rect.w) < xwindow->width) {
631 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
632 rect.x + rect.w, 0, xwindow->width, xwindow->height);
637 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
638 0, 0, xwindow->width, rect.y);
642 if ((rect.y + rect.h) < xwindow->height) {
643 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
644 0, rect.y + rect.h, xwindow->width, xwindow->height);
648 /* This function puts a GstXImageBuffer on a GstXImageSink's window */
650 gst_ximagesink_ximage_put (GstXImageSink * ximagesink, GstXImageBuffer * ximage)
652 GstVideoRectangle src, dst, result;
653 gboolean draw_border = FALSE;
655 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
657 /* We take the flow_lock. If expose is in there we don't want to run
658 concurrently from the data flow thread */
659 g_mutex_lock (ximagesink->flow_lock);
661 if (G_UNLIKELY (ximagesink->xwindow == NULL)) {
662 g_mutex_unlock (ximagesink->flow_lock);
666 /* Draw borders when displaying the first frame. After this
667 draw borders only on expose event. */
668 if (!ximagesink->cur_image) {
672 /* Store a reference to the last image we put, lose the previous one */
673 if (ximage && ximagesink->cur_image != ximage) {
674 if (ximagesink->cur_image) {
675 GST_LOG_OBJECT (ximagesink, "unreffing %p", ximagesink->cur_image);
676 gst_buffer_unref (ximagesink->cur_image);
678 GST_LOG_OBJECT (ximagesink, "reffing %p as our current image", ximage);
679 ximagesink->cur_image =
680 GST_XIMAGE_BUFFER (gst_buffer_ref (GST_BUFFER_CAST (ximage)));
683 /* Expose sends a NULL image, we take the latest frame */
686 if (ximagesink->cur_image) {
687 ximage = ximagesink->cur_image;
689 g_mutex_unlock (ximagesink->flow_lock);
694 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
696 src.w = ximage->width;
697 src.h = ximage->height;
698 dst.w = ximagesink->xwindow->width;
699 dst.h = ximagesink->xwindow->height;
701 gst_video_sink_center_rect (src, dst, &result, FALSE);
703 g_mutex_lock (ximagesink->x_lock);
706 gst_ximagesink_xwindow_draw_borders (ximagesink, ximagesink->xwindow,
710 if (ximagesink->xcontext->use_xshm) {
711 GST_LOG_OBJECT (ximagesink,
712 "XShmPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
713 ximage, 0, 0, result.x, result.y, result.w, result.h,
714 ximagesink->xwindow->width, ximagesink->xwindow->height);
715 XShmPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
716 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
717 result.w, result.h, FALSE);
719 #endif /* HAVE_XSHM */
721 GST_LOG_OBJECT (ximagesink,
722 "XPutImage on %p, src: %d, %d - dest: %d, %d, dim: %dx%d, win %dx%d",
723 ximage, 0, 0, result.x, result.y, result.w, result.h,
724 ximagesink->xwindow->width, ximagesink->xwindow->height);
725 XPutImage (ximagesink->xcontext->disp, ximagesink->xwindow->win,
726 ximagesink->xwindow->gc, ximage->ximage, 0, 0, result.x, result.y,
730 XSync (ximagesink->xcontext->disp, FALSE);
732 g_mutex_unlock (ximagesink->x_lock);
734 g_mutex_unlock (ximagesink->flow_lock);
738 gst_ximagesink_xwindow_decorate (GstXImageSink * ximagesink,
741 Atom hints_atom = None;
744 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), FALSE);
745 g_return_val_if_fail (window != NULL, FALSE);
747 g_mutex_lock (ximagesink->x_lock);
749 hints_atom = XInternAtom (ximagesink->xcontext->disp, "_MOTIF_WM_HINTS", 1);
750 if (hints_atom == None) {
751 g_mutex_unlock (ximagesink->x_lock);
755 hints = g_malloc0 (sizeof (MotifWmHints));
757 hints->flags |= MWM_HINTS_DECORATIONS;
758 hints->decorations = 1 << 0;
760 XChangeProperty (ximagesink->xcontext->disp, window->win,
761 hints_atom, hints_atom, 32, PropModeReplace,
762 (guchar *) hints, sizeof (MotifWmHints) / sizeof (long));
764 XSync (ximagesink->xcontext->disp, FALSE);
766 g_mutex_unlock (ximagesink->x_lock);
773 /* This function handles a GstXWindow creation */
775 gst_ximagesink_xwindow_new (GstXImageSink * ximagesink, gint width, gint height)
777 GstXWindow *xwindow = NULL;
780 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
782 xwindow = g_new0 (GstXWindow, 1);
784 xwindow->width = width;
785 xwindow->height = height;
786 xwindow->internal = TRUE;
788 g_mutex_lock (ximagesink->x_lock);
790 xwindow->win = XCreateSimpleWindow (ximagesink->xcontext->disp,
791 ximagesink->xcontext->root,
792 0, 0, xwindow->width, xwindow->height, 0, 0, ximagesink->xcontext->black);
794 /* We have to do that to prevent X from redrawing the background on
795 ConfigureNotify. This takes away flickering of video when resizing. */
796 XSetWindowBackgroundPixmap (ximagesink->xcontext->disp, xwindow->win, None);
798 if (ximagesink->handle_events) {
799 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
800 StructureNotifyMask | PointerMotionMask | KeyPressMask |
801 KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
804 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win,
807 XMapRaised (ximagesink->xcontext->disp, xwindow->win);
809 XSync (ximagesink->xcontext->disp, FALSE);
811 g_mutex_unlock (ximagesink->x_lock);
813 gst_ximagesink_xwindow_decorate (ximagesink, xwindow);
815 gst_x_overlay_got_xwindow_id (GST_X_OVERLAY (ximagesink), xwindow->win);
820 /* This function destroys a GstXWindow */
822 gst_ximagesink_xwindow_destroy (GstXImageSink * ximagesink,
823 GstXWindow * xwindow)
825 g_return_if_fail (xwindow != NULL);
826 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
828 g_mutex_lock (ximagesink->x_lock);
830 /* If we did not create that window we just free the GC and let it live */
831 if (xwindow->internal)
832 XDestroyWindow (ximagesink->xcontext->disp, xwindow->win);
834 XSelectInput (ximagesink->xcontext->disp, xwindow->win, 0);
836 XFreeGC (ximagesink->xcontext->disp, xwindow->gc);
838 XSync (ximagesink->xcontext->disp, FALSE);
840 g_mutex_unlock (ximagesink->x_lock);
846 gst_ximagesink_xwindow_update_geometry (GstXImageSink * ximagesink,
847 GstXWindow * xwindow)
849 XWindowAttributes attr;
851 g_return_if_fail (xwindow != NULL);
852 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
854 /* Update the window geometry */
855 g_mutex_lock (ximagesink->x_lock);
857 XGetWindowAttributes (ximagesink->xcontext->disp,
858 ximagesink->xwindow->win, &attr);
860 ximagesink->xwindow->width = attr.width;
861 ximagesink->xwindow->height = attr.height;
863 g_mutex_unlock (ximagesink->x_lock);
867 gst_ximagesink_xwindow_clear (GstXImageSink * ximagesink, GstXWindow * xwindow)
869 g_return_if_fail (xwindow != NULL);
870 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
872 g_mutex_lock (ximagesink->x_lock);
874 XSetForeground (ximagesink->xcontext->disp, xwindow->gc,
875 ximagesink->xcontext->black);
877 XFillRectangle (ximagesink->xcontext->disp, xwindow->win, xwindow->gc,
878 0, 0, xwindow->width, xwindow->height);
880 XSync (ximagesink->xcontext->disp, FALSE);
882 g_mutex_unlock (ximagesink->x_lock);
885 /* This function handles XEvents that might be in the queue. It generates
886 GstEvent that will be sent upstream in the pipeline to handle interactivity
889 gst_ximagesink_handle_xevents (GstXImageSink * ximagesink)
893 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
896 guint pointer_x = 0, pointer_y = 0;
897 gboolean pointer_moved = FALSE;
899 /* Then we get all pointer motion events, only the last position is
901 g_mutex_lock (ximagesink->flow_lock);
902 g_mutex_lock (ximagesink->x_lock);
903 while (XCheckWindowEvent (ximagesink->xcontext->disp,
904 ximagesink->xwindow->win, PointerMotionMask, &e)) {
905 g_mutex_unlock (ximagesink->x_lock);
906 g_mutex_unlock (ximagesink->flow_lock);
910 pointer_x = e.xmotion.x;
911 pointer_y = e.xmotion.y;
912 pointer_moved = TRUE;
917 g_mutex_lock (ximagesink->flow_lock);
918 g_mutex_lock (ximagesink->x_lock);
920 g_mutex_unlock (ximagesink->x_lock);
921 g_mutex_unlock (ximagesink->flow_lock);
924 GST_DEBUG ("ximagesink pointer moved over window at %d,%d",
925 pointer_x, pointer_y);
926 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
927 "mouse-move", 0, pointer_x, pointer_y);
931 /* We get all remaining events on our window to throw them upstream */
932 g_mutex_lock (ximagesink->flow_lock);
933 g_mutex_lock (ximagesink->x_lock);
934 while (XCheckWindowEvent (ximagesink->xcontext->disp,
935 ximagesink->xwindow->win,
936 KeyPressMask | KeyReleaseMask |
937 ButtonPressMask | ButtonReleaseMask, &e)) {
940 /* We lock only for the X function call */
941 g_mutex_unlock (ximagesink->x_lock);
942 g_mutex_unlock (ximagesink->flow_lock);
946 /* Mouse button pressed/released over our window. We send upstream
947 events for interactivity/navigation */
948 GST_DEBUG ("ximagesink button %d pressed over window at %d,%d",
949 e.xbutton.button, e.xbutton.x, e.xbutton.x);
950 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
951 "mouse-button-press", e.xbutton.button, e.xbutton.x, e.xbutton.y);
954 GST_DEBUG ("ximagesink button %d release over window at %d,%d",
955 e.xbutton.button, e.xbutton.x, e.xbutton.x);
956 gst_navigation_send_mouse_event (GST_NAVIGATION (ximagesink),
957 "mouse-button-release", e.xbutton.button, e.xbutton.x, e.xbutton.y);
961 /* Key pressed/released over our window. We send upstream
962 events for interactivity/navigation */
963 GST_DEBUG ("ximagesink key %d pressed over window at %d,%d",
964 e.xkey.keycode, e.xkey.x, e.xkey.x);
965 g_mutex_lock (ximagesink->x_lock);
966 keysym = XKeycodeToKeysym (ximagesink->xcontext->disp,
968 g_mutex_unlock (ximagesink->x_lock);
969 if (keysym != NoSymbol) {
970 char *key_str = NULL;
972 g_mutex_lock (ximagesink->x_lock);
973 key_str = XKeysymToString (keysym);
974 g_mutex_unlock (ximagesink->x_lock);
975 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
976 e.type == KeyPress ? "key-press" : "key-release", key_str);
979 gst_navigation_send_key_event (GST_NAVIGATION (ximagesink),
980 e.type == KeyPress ? "key-press" : "key-release", "unknown");
984 GST_DEBUG_OBJECT (ximagesink, "ximagesink unhandled X event (%d)",
987 g_mutex_lock (ximagesink->flow_lock);
988 g_mutex_lock (ximagesink->x_lock);
990 g_mutex_unlock (ximagesink->x_lock);
991 g_mutex_unlock (ximagesink->flow_lock);
994 gboolean exposed = FALSE;
996 g_mutex_lock (ximagesink->flow_lock);
997 g_mutex_lock (ximagesink->x_lock);
998 while (XCheckWindowEvent (ximagesink->xcontext->disp,
999 ximagesink->xwindow->win, ExposureMask, &e)) {
1000 g_mutex_unlock (ximagesink->x_lock);
1001 g_mutex_unlock (ximagesink->flow_lock);
1010 g_mutex_lock (ximagesink->flow_lock);
1011 g_mutex_lock (ximagesink->x_lock);
1013 g_mutex_unlock (ximagesink->x_lock);
1014 g_mutex_unlock (ximagesink->flow_lock);
1017 gst_ximagesink_expose (GST_X_OVERLAY (ximagesink));
1023 gst_ximagesink_event_thread (GstXImageSink * ximagesink)
1025 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1027 while (ximagesink->running) {
1028 if (ximagesink->xwindow) {
1029 gst_ximagesink_handle_xevents (ximagesink);
1037 /* This function calculates the pixel aspect ratio based on the properties
1038 * in the xcontext structure and stores it there. */
1040 gst_ximagesink_calculate_pixel_aspect_ratio (GstXContext * xcontext)
1042 static const gint par[][2] = {
1043 {1, 1}, /* regular screen */
1044 {16, 15}, /* PAL TV */
1045 {11, 10}, /* 525 line Rec.601 video */
1046 {54, 59}, /* 625 line Rec.601 video */
1047 {64, 45}, /* 1280x1024 on 16:9 display */
1048 {5, 3}, /* 1280x1024 on 4:3 display */
1049 {4, 3} /* 800x600 on 16:9 display */
1056 #define DELTA(idx) (ABS (ratio - ((gdouble) par[idx][0] / par[idx][1])))
1058 /* first calculate the "real" ratio based on the X values;
1059 * which is the "physical" w/h divided by the w/h in pixels of the display */
1060 ratio = (gdouble) (xcontext->widthmm * xcontext->height)
1061 / (xcontext->heightmm * xcontext->width);
1063 /* DirectFB's X in 720x576 reports the physical dimensions wrong, so
1065 if (xcontext->width == 720 && xcontext->height == 576) {
1066 ratio = 4.0 * 576 / (3.0 * 720);
1068 GST_DEBUG ("calculated pixel aspect ratio: %f", ratio);
1070 /* now find the one from par[][2] with the lowest delta to the real one */
1074 for (i = 1; i < sizeof (par) / (sizeof (gint) * 2); ++i) {
1075 gdouble this_delta = DELTA (i);
1077 if (this_delta < delta) {
1083 GST_DEBUG ("Decided on index %d (%d/%d)", index,
1084 par[index][0], par[index][1]);
1086 g_free (xcontext->par);
1087 xcontext->par = g_new0 (GValue, 1);
1088 g_value_init (xcontext->par, GST_TYPE_FRACTION);
1089 gst_value_set_fraction (xcontext->par, par[index][0], par[index][1]);
1090 GST_DEBUG ("set xcontext PAR to %d/%d",
1091 gst_value_get_fraction_numerator (xcontext->par),
1092 gst_value_get_fraction_denominator (xcontext->par));
1095 /* This function gets the X Display and global info about it. Everything is
1096 stored in our object and will be cleaned when the object is disposed. Note
1097 here that caps for supported format are generated without any window or
1099 static GstXContext *
1100 gst_ximagesink_xcontext_get (GstXImageSink * ximagesink)
1102 GstXContext *xcontext = NULL;
1103 XPixmapFormatValues *px_formats = NULL;
1104 gint nb_formats = 0, i;
1106 g_return_val_if_fail (GST_IS_XIMAGESINK (ximagesink), NULL);
1108 xcontext = g_new0 (GstXContext, 1);
1110 g_mutex_lock (ximagesink->x_lock);
1112 xcontext->disp = XOpenDisplay (ximagesink->display_name);
1114 if (!xcontext->disp) {
1115 g_mutex_unlock (ximagesink->x_lock);
1117 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1118 ("Could not initialise X output"), ("Could not open display"));
1122 xcontext->screen = DefaultScreenOfDisplay (xcontext->disp);
1123 xcontext->screen_num = DefaultScreen (xcontext->disp);
1124 xcontext->visual = DefaultVisual (xcontext->disp, xcontext->screen_num);
1125 xcontext->root = DefaultRootWindow (xcontext->disp);
1126 xcontext->white = XWhitePixel (xcontext->disp, xcontext->screen_num);
1127 xcontext->black = XBlackPixel (xcontext->disp, xcontext->screen_num);
1128 xcontext->depth = DefaultDepthOfScreen (xcontext->screen);
1130 xcontext->width = DisplayWidth (xcontext->disp, xcontext->screen_num);
1131 xcontext->height = DisplayHeight (xcontext->disp, xcontext->screen_num);
1132 xcontext->widthmm = DisplayWidthMM (xcontext->disp, xcontext->screen_num);
1133 xcontext->heightmm = DisplayHeightMM (xcontext->disp, xcontext->screen_num);
1135 GST_DEBUG_OBJECT (ximagesink, "X reports %dx%d pixels and %d mm x %d mm",
1136 xcontext->width, xcontext->height, xcontext->widthmm, xcontext->heightmm);
1138 gst_ximagesink_calculate_pixel_aspect_ratio (xcontext);
1140 /* We get supported pixmap formats at supported depth */
1141 px_formats = XListPixmapFormats (xcontext->disp, &nb_formats);
1144 XCloseDisplay (xcontext->disp);
1145 g_mutex_unlock (ximagesink->x_lock);
1150 /* We get bpp value corresponding to our running depth */
1151 for (i = 0; i < nb_formats; i++) {
1152 if (px_formats[i].depth == xcontext->depth)
1153 xcontext->bpp = px_formats[i].bits_per_pixel;
1158 xcontext->endianness =
1159 (ImageByteOrder (xcontext->disp) ==
1160 LSBFirst) ? G_LITTLE_ENDIAN : G_BIG_ENDIAN;
1162 /* Search for XShm extension support */
1164 if (XShmQueryExtension (xcontext->disp) &&
1165 gst_ximagesink_check_xshm_calls (ximagesink, xcontext)) {
1166 xcontext->use_xshm = TRUE;
1167 GST_DEBUG ("ximagesink is using XShm extension");
1171 xcontext->use_xshm = FALSE;
1172 GST_DEBUG ("ximagesink is not using XShm extension");
1175 /* our caps system handles 24/32bpp RGB as big-endian. */
1176 if ((xcontext->bpp == 24 || xcontext->bpp == 32) &&
1177 xcontext->endianness == G_LITTLE_ENDIAN) {
1178 xcontext->endianness = G_BIG_ENDIAN;
1179 xcontext->visual->red_mask = GUINT32_TO_BE (xcontext->visual->red_mask);
1180 xcontext->visual->green_mask = GUINT32_TO_BE (xcontext->visual->green_mask);
1181 xcontext->visual->blue_mask = GUINT32_TO_BE (xcontext->visual->blue_mask);
1182 if (xcontext->bpp == 24) {
1183 xcontext->visual->red_mask >>= 8;
1184 xcontext->visual->green_mask >>= 8;
1185 xcontext->visual->blue_mask >>= 8;
1189 /* update object's par with calculated one if not set yet */
1190 if (!ximagesink->par) {
1191 ximagesink->par = g_new0 (GValue, 1);
1192 gst_value_init_and_copy (ximagesink->par, xcontext->par);
1193 GST_DEBUG_OBJECT (ximagesink, "set calculated PAR on object's PAR");
1195 xcontext->caps = gst_caps_new_simple ("video/x-raw-rgb",
1196 "bpp", G_TYPE_INT, xcontext->bpp,
1197 "depth", G_TYPE_INT, xcontext->depth,
1198 "endianness", G_TYPE_INT, xcontext->endianness,
1199 "red_mask", G_TYPE_INT, xcontext->visual->red_mask,
1200 "green_mask", G_TYPE_INT, xcontext->visual->green_mask,
1201 "blue_mask", G_TYPE_INT, xcontext->visual->blue_mask,
1202 "width", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1203 "height", GST_TYPE_INT_RANGE, 1, G_MAXINT,
1204 "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1, NULL);
1205 if (ximagesink->par) {
1208 nom = gst_value_get_fraction_numerator (ximagesink->par);
1209 den = gst_value_get_fraction_denominator (ximagesink->par);
1210 gst_caps_set_simple (xcontext->caps, "pixel-aspect-ratio",
1211 GST_TYPE_FRACTION, nom, den, NULL);
1214 g_mutex_unlock (ximagesink->x_lock);
1216 /* Setup our event listening thread */
1217 ximagesink->event_thread = g_thread_create (
1218 (GThreadFunc) gst_ximagesink_event_thread, ximagesink, TRUE, NULL);
1223 /* This function cleans the X context. Closing the Display and unrefing the
1224 caps for supported formats. */
1226 gst_ximagesink_xcontext_clear (GstXImageSink * ximagesink)
1228 GstXContext *xcontext;
1230 g_return_if_fail (GST_IS_XIMAGESINK (ximagesink));
1232 GST_OBJECT_LOCK (ximagesink);
1233 if (ximagesink->xcontext == NULL) {
1234 GST_OBJECT_UNLOCK (ximagesink);
1238 /* Take the xcontext reference and NULL it while we
1239 * clean it up, so that any buffer-alloced buffers
1240 * arriving after this will be freed correctly */
1241 xcontext = ximagesink->xcontext;
1242 ximagesink->xcontext = NULL;
1244 GST_OBJECT_UNLOCK (ximagesink);
1246 /* Wait for our event thread */
1247 if (ximagesink->event_thread) {
1248 g_thread_join (ximagesink->event_thread);
1249 ximagesink->event_thread = NULL;
1252 gst_caps_unref (xcontext->caps);
1253 g_free (xcontext->par);
1254 g_free (ximagesink->par);
1255 ximagesink->par = NULL;
1257 g_mutex_lock (ximagesink->x_lock);
1259 XCloseDisplay (xcontext->disp);
1261 g_mutex_unlock (ximagesink->x_lock);
1263 g_free (ximagesink->xcontext);
1267 gst_ximagesink_bufferpool_clear (GstXImageSink * ximagesink)
1270 g_mutex_lock (ximagesink->pool_lock);
1272 while (ximagesink->buffer_pool) {
1273 GstXImageBuffer *ximage = ximagesink->buffer_pool->data;
1275 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1276 ximagesink->buffer_pool);
1277 gst_ximage_buffer_free (ximage);
1280 g_mutex_unlock (ximagesink->pool_lock);
1286 gst_ximagesink_getcaps (GstBaseSink * bsink)
1288 GstXImageSink *ximagesink;
1292 ximagesink = GST_XIMAGESINK (bsink);
1294 if (ximagesink->xcontext)
1295 return gst_caps_ref (ximagesink->xcontext->caps);
1297 /* get a template copy and add the pixel aspect ratio */
1299 gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SINK (ximagesink)->
1301 for (i = 0; i < gst_caps_get_size (caps); ++i) {
1302 GstStructure *structure = gst_caps_get_structure (caps, i);
1304 if (ximagesink->par) {
1307 nom = gst_value_get_fraction_numerator (ximagesink->par);
1308 den = gst_value_get_fraction_denominator (ximagesink->par);
1309 gst_structure_set (structure, "pixel-aspect-ratio",
1310 GST_TYPE_FRACTION, nom, den, NULL);
1318 gst_ximagesink_setcaps (GstBaseSink * bsink, GstCaps * caps)
1320 GstXImageSink *ximagesink;
1321 gboolean ret = TRUE;
1322 GstStructure *structure;
1323 GstCaps *intersection;
1325 gint new_width, new_height;
1328 ximagesink = GST_XIMAGESINK (bsink);
1330 if (!ximagesink->xcontext)
1333 GST_DEBUG_OBJECT (ximagesink,
1334 "sinkconnect possible caps %" GST_PTR_FORMAT " with given caps %"
1335 GST_PTR_FORMAT, ximagesink->xcontext->caps, caps);
1337 /* We intersect those caps with our template to make sure they are correct */
1338 intersection = gst_caps_intersect (ximagesink->xcontext->caps, caps);
1339 GST_DEBUG_OBJECT (ximagesink, "intersection returned %" GST_PTR_FORMAT,
1341 if (gst_caps_is_empty (intersection)) {
1342 gst_caps_unref (intersection);
1346 gst_caps_unref (intersection);
1348 structure = gst_caps_get_structure (caps, 0);
1350 ret &= gst_structure_get_int (structure, "width", &new_width);
1351 ret &= gst_structure_get_int (structure, "height", &new_height);
1352 fps = gst_structure_get_value (structure, "framerate");
1353 ret &= (fps != NULL);
1357 /* if the caps contain pixel-aspect-ratio, they have to match ours,
1358 * otherwise linking should fail */
1359 par = gst_structure_get_value (structure, "pixel-aspect-ratio");
1361 if (ximagesink->par) {
1362 if (gst_value_compare (par, ximagesink->par) != GST_VALUE_EQUAL) {
1365 } else if (ximagesink->xcontext->par) {
1366 if (gst_value_compare (par, ximagesink->xcontext->par) != GST_VALUE_EQUAL) {
1372 GST_VIDEO_SINK_WIDTH (ximagesink) = new_width;
1373 GST_VIDEO_SINK_HEIGHT (ximagesink) = new_height;
1374 ximagesink->fps_n = gst_value_get_fraction_numerator (fps);
1375 ximagesink->fps_d = gst_value_get_fraction_denominator (fps);
1377 /* Notify application to set xwindow id now */
1378 g_mutex_lock (ximagesink->flow_lock);
1379 if (!ximagesink->xwindow) {
1380 g_mutex_unlock (ximagesink->flow_lock);
1381 gst_x_overlay_prepare_xwindow_id (GST_X_OVERLAY (ximagesink));
1383 g_mutex_unlock (ximagesink->flow_lock);
1386 /* Creating our window and our image */
1387 if (GST_VIDEO_SINK_WIDTH (ximagesink) <= 0 ||
1388 GST_VIDEO_SINK_HEIGHT (ximagesink) <= 0) {
1389 GST_ELEMENT_ERROR (ximagesink, CORE, NEGOTIATION, (NULL),
1390 ("Invalid image size."));
1394 g_mutex_lock (ximagesink->flow_lock);
1395 if (!ximagesink->xwindow) {
1396 ximagesink->xwindow = gst_ximagesink_xwindow_new (ximagesink,
1397 GST_VIDEO_SINK_WIDTH (ximagesink), GST_VIDEO_SINK_HEIGHT (ximagesink));
1399 g_mutex_unlock (ximagesink->flow_lock);
1401 /* If our ximage has changed we destroy it, next chain iteration will create
1403 if ((ximagesink->ximage) &&
1404 ((GST_VIDEO_SINK_WIDTH (ximagesink) != ximagesink->ximage->width) ||
1405 (GST_VIDEO_SINK_HEIGHT (ximagesink) != ximagesink->ximage->height))) {
1406 GST_DEBUG_OBJECT (ximagesink, "our image is not usable anymore, unref %p",
1407 ximagesink->ximage);
1408 gst_buffer_unref (GST_BUFFER_CAST (ximagesink->ximage));
1409 ximagesink->ximage = NULL;
1417 GST_INFO_OBJECT (ximagesink, "pixel aspect ratio does not match");
1422 static GstStateChangeReturn
1423 gst_ximagesink_change_state (GstElement * element, GstStateChange transition)
1425 GstXImageSink *ximagesink;
1426 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1427 GstXContext *xcontext = NULL;
1429 ximagesink = GST_XIMAGESINK (element);
1431 switch (transition) {
1432 case GST_STATE_CHANGE_NULL_TO_READY:
1434 /* Initializing the XContext */
1435 if (!ximagesink->xcontext)
1436 xcontext = gst_ximagesink_xcontext_get (ximagesink);
1438 GST_OBJECT_LOCK (ximagesink);
1439 ximagesink->running = TRUE;
1441 ximagesink->xcontext = xcontext;
1442 GST_OBJECT_UNLOCK (ximagesink);
1444 if (!ximagesink->xcontext) {
1445 ret = GST_STATE_CHANGE_FAILURE;
1448 /* call XSynchronize with the current value of synchronous */
1449 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1450 ximagesink->synchronous ? "TRUE" : "FALSE");
1451 g_mutex_lock (ximagesink->x_lock);
1452 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1453 g_mutex_unlock (ximagesink->x_lock);
1455 case GST_STATE_CHANGE_READY_TO_PAUSED:
1456 g_mutex_lock (ximagesink->flow_lock);
1457 if (ximagesink->xwindow)
1458 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1459 g_mutex_unlock (ximagesink->flow_lock);
1461 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1467 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1468 if (ret == GST_STATE_CHANGE_FAILURE)
1471 switch (transition) {
1472 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1474 case GST_STATE_CHANGE_PAUSED_TO_READY:
1475 ximagesink->fps_n = 0;
1476 ximagesink->fps_d = 1;
1477 GST_VIDEO_SINK_WIDTH (ximagesink) = 0;
1478 GST_VIDEO_SINK_HEIGHT (ximagesink) = 0;
1480 case GST_STATE_CHANGE_READY_TO_NULL:
1481 GST_OBJECT_LOCK (ximagesink);
1482 ximagesink->running = FALSE;
1483 GST_OBJECT_UNLOCK (ximagesink);
1485 if (ximagesink->ximage) {
1486 gst_buffer_unref (ximagesink->ximage);
1487 ximagesink->ximage = NULL;
1489 if (ximagesink->cur_image) {
1490 gst_buffer_unref (ximagesink->cur_image);
1491 ximagesink->cur_image = NULL;
1494 gst_ximagesink_bufferpool_clear (ximagesink);
1496 g_mutex_lock (ximagesink->flow_lock);
1497 if (ximagesink->xwindow) {
1498 gst_ximagesink_xwindow_clear (ximagesink, ximagesink->xwindow);
1499 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1500 ximagesink->xwindow = NULL;
1502 g_mutex_unlock (ximagesink->flow_lock);
1504 gst_ximagesink_xcontext_clear (ximagesink);
1515 gst_ximagesink_get_times (GstBaseSink * bsink, GstBuffer * buf,
1516 GstClockTime * start, GstClockTime * end)
1518 GstXImageSink *ximagesink;
1520 ximagesink = GST_XIMAGESINK (bsink);
1522 if (GST_BUFFER_TIMESTAMP_IS_VALID (buf)) {
1523 *start = GST_BUFFER_TIMESTAMP (buf);
1524 if (GST_BUFFER_DURATION_IS_VALID (buf)) {
1525 *end = *start + GST_BUFFER_DURATION (buf);
1527 if (ximagesink->fps_n > 0) {
1529 gst_util_uint64_scale_int (GST_SECOND, ximagesink->fps_d,
1536 static GstFlowReturn
1537 gst_ximagesink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
1539 GstXImageSink *ximagesink;
1541 g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
1543 ximagesink = GST_XIMAGESINK (bsink);
1545 /* If this buffer has been allocated using our buffer management we simply
1546 put the ximage which is in the PRIVATE pointer */
1547 if (GST_IS_XIMAGE_BUFFER (buf)) {
1548 GST_LOG_OBJECT (ximagesink, "buffer from our pool, writing directly");
1549 gst_ximagesink_ximage_put (ximagesink, GST_XIMAGE_BUFFER (buf));
1551 /* Else we have to copy the data into our private image, */
1552 /* if we have one... */
1553 GST_LOG_OBJECT (ximagesink, "normal buffer, copying from it");
1554 if (!ximagesink->ximage) {
1555 GST_DEBUG_OBJECT (ximagesink, "creating our ximage");
1556 ximagesink->ximage = gst_ximagesink_ximage_new (ximagesink,
1557 GST_BUFFER_CAPS (buf));
1558 if (!ximagesink->ximage)
1559 /* The create method should have posted an informative error */
1562 if (ximagesink->ximage->size < GST_BUFFER_SIZE (buf)) {
1563 GST_ELEMENT_ERROR (ximagesink, RESOURCE, WRITE,
1564 ("Failed to create output image buffer of %dx%d pixels",
1565 ximagesink->ximage->width, ximagesink->ximage->height),
1566 ("XServer allocated buffer size did not match input buffer"));
1568 gst_ximagesink_ximage_destroy (ximagesink, ximagesink->ximage);
1569 ximagesink->ximage = NULL;
1573 memcpy (GST_BUFFER_DATA (ximagesink->ximage), GST_BUFFER_DATA (buf),
1574 MIN (GST_BUFFER_SIZE (buf), ximagesink->ximage->size));
1575 gst_ximagesink_ximage_put (ximagesink, ximagesink->ximage);
1583 /* No image available. That's very bad ! */
1584 GST_DEBUG ("could not create image");
1585 return GST_FLOW_ERROR;
1589 /* Buffer management
1591 * The buffer_alloc function must either return a buffer with given size and
1592 * caps or create a buffer with different caps attached to the buffer. This
1593 * last option is called reverse negotiation, ie, where the sink suggests a
1594 * different format from the upstream peer.
1596 * We try to do reverse negotiation when our geometry changes and we like a
1599 static GstFlowReturn
1600 gst_ximagesink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size,
1601 GstCaps * caps, GstBuffer ** buf)
1603 GstXImageSink *ximagesink;
1604 GstXImageBuffer *ximage = NULL;
1605 GstStructure *structure = NULL;
1606 GstFlowReturn ret = GST_FLOW_OK;
1607 GstCaps *alloc_caps;
1608 gboolean alloc_unref = FALSE;
1611 ximagesink = GST_XIMAGESINK (bsink);
1613 GST_LOG_OBJECT (ximagesink,
1614 "a buffer of %d bytes was requested with caps %" GST_PTR_FORMAT
1615 " and offset %" G_GUINT64_FORMAT, size, caps, offset);
1617 /* assume we're going to alloc what was requested, keep track of
1618 * wheter we need to unref or not. When we suggest a new format
1619 * upstream we will create a new caps that we need to unref. */
1621 alloc_unref = FALSE;
1623 /* get struct to see what is requested */
1624 structure = gst_caps_get_structure (caps, 0);
1626 if (gst_structure_get_int (structure, "width", &width) &&
1627 gst_structure_get_int (structure, "height", &height)) {
1628 GstVideoRectangle dst, src, result;
1633 /* We take the flow_lock because the window might go away */
1634 g_mutex_lock (ximagesink->flow_lock);
1635 if (!ximagesink->xwindow) {
1636 g_mutex_unlock (ximagesink->flow_lock);
1640 /* What is our geometry */
1641 gst_ximagesink_xwindow_update_geometry (ximagesink, ximagesink->xwindow);
1642 dst.w = ximagesink->xwindow->width;
1643 dst.h = ximagesink->xwindow->height;
1645 g_mutex_unlock (ximagesink->flow_lock);
1647 if (ximagesink->keep_aspect) {
1648 GST_LOG_OBJECT (ximagesink, "enforcing aspect ratio in reverse caps "
1650 gst_video_sink_center_rect (src, dst, &result, TRUE);
1652 GST_LOG_OBJECT (ximagesink, "trying to resize to window geometry "
1653 "ignoring aspect ratio");
1654 result.x = result.y = 0;
1659 /* We would like another geometry */
1660 if (width != result.w || height != result.h) {
1662 GstCaps *desired_caps;
1663 GstStructure *desired_struct;
1665 /* make a copy of the incomming caps to create the new
1666 * suggestion. We can't use make_writable because we might
1667 * then destroy the original caps which we still need when the
1668 * peer does not accept the suggestion. */
1669 desired_caps = gst_caps_copy (caps);
1670 desired_struct = gst_caps_get_structure (desired_caps, 0);
1672 GST_DEBUG ("we would love to receive a %dx%d video", result.w, result.h);
1673 gst_structure_set (desired_struct, "width", G_TYPE_INT, result.w, NULL);
1674 gst_structure_set (desired_struct, "height", G_TYPE_INT, result.h, NULL);
1676 /* PAR property overrides the X calculated one */
1677 if (ximagesink->par) {
1678 nom = gst_value_get_fraction_numerator (ximagesink->par);
1679 den = gst_value_get_fraction_denominator (ximagesink->par);
1680 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1681 GST_TYPE_FRACTION, nom, den, NULL);
1682 } else if (ximagesink->xcontext->par) {
1683 nom = gst_value_get_fraction_numerator (ximagesink->xcontext->par);
1684 den = gst_value_get_fraction_denominator (ximagesink->xcontext->par);
1685 gst_structure_set (desired_struct, "pixel-aspect-ratio",
1686 GST_TYPE_FRACTION, nom, den, NULL);
1689 /* see if peer accepts our new suggestion, if there is no peer, this
1690 * function returns true. */
1691 if (gst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (ximagesink),
1695 bpp = size / height / width;
1696 /* we will not alloc a buffer of the new suggested caps. Make sure
1697 * we also unref this new caps after we set it on the buffer. */
1698 alloc_caps = desired_caps;
1702 size = bpp * width * height;
1703 GST_DEBUG ("peed pad accepts our desired caps %" GST_PTR_FORMAT
1704 " buffer size is now %d bytes", desired_caps, size);
1706 GST_DEBUG ("peer pad does not accept our desired caps %" GST_PTR_FORMAT,
1708 /* we alloc a buffer with the original incomming caps */
1709 width = GST_VIDEO_SINK_WIDTH (ximagesink);
1710 height = GST_VIDEO_SINK_HEIGHT (ximagesink);
1716 /* Inspect our buffer pool */
1717 g_mutex_lock (ximagesink->pool_lock);
1718 while (ximagesink->buffer_pool) {
1719 ximage = (GstXImageBuffer *) ximagesink->buffer_pool->data;
1722 /* Removing from the pool */
1723 ximagesink->buffer_pool = g_slist_delete_link (ximagesink->buffer_pool,
1724 ximagesink->buffer_pool);
1726 /* If the ximage is invalid for our need, destroy */
1727 if ((ximage->width != width) || (ximage->height != height)) {
1728 gst_ximage_buffer_free (ximage);
1731 /* We found a suitable ximage */
1736 g_mutex_unlock (ximagesink->pool_lock);
1738 /* We haven't found anything, creating a new one */
1740 ximage = gst_ximagesink_ximage_new (ximagesink, alloc_caps);
1742 /* Now we should have a ximage, set appropriate caps on it */
1744 gst_buffer_set_caps (GST_BUFFER_CAST (ximage), alloc_caps);
1747 /* could be our new reffed suggestion or the original unreffed caps */
1749 gst_caps_unref (alloc_caps);
1751 *buf = GST_BUFFER_CAST (ximage);
1756 /* Interfaces stuff */
1759 gst_ximagesink_interface_supported (GstImplementsInterface * iface, GType type)
1761 g_assert (type == GST_TYPE_NAVIGATION || type == GST_TYPE_X_OVERLAY);
1766 gst_ximagesink_interface_init (GstImplementsInterfaceClass * klass)
1768 klass->supported = gst_ximagesink_interface_supported;
1772 gst_ximagesink_navigation_send_event (GstNavigation * navigation,
1773 GstStructure * structure)
1775 GstXImageSink *ximagesink = GST_XIMAGESINK (navigation);
1777 gint x_offset, y_offset;
1781 event = gst_event_new_navigation (structure);
1783 /* We are not converting the pointer coordinates as there's no hardware
1784 scaling done here. The only possible scaling is done by videoscale and
1785 videoscale will have to catch those events and tranform the coordinates
1786 to match the applied scaling. So here we just add the offset if the image
1787 is centered in the window. */
1789 /* We take the flow_lock while we look at the window */
1790 g_mutex_lock (ximagesink->flow_lock);
1792 if (!ximagesink->xwindow) {
1793 g_mutex_unlock (ximagesink->flow_lock);
1797 x_offset = ximagesink->xwindow->width - GST_VIDEO_SINK_WIDTH (ximagesink);
1798 y_offset = ximagesink->xwindow->height - GST_VIDEO_SINK_HEIGHT (ximagesink);
1800 g_mutex_unlock (ximagesink->flow_lock);
1802 if (gst_structure_get_double (structure, "pointer_x", &x)) {
1804 gst_structure_set (structure, "pointer_x", G_TYPE_DOUBLE, x, NULL);
1806 if (gst_structure_get_double (structure, "pointer_y", &y)) {
1808 gst_structure_set (structure, "pointer_y", G_TYPE_DOUBLE, y, NULL);
1811 pad = gst_pad_get_peer (GST_VIDEO_SINK_PAD (ximagesink));
1813 if (GST_IS_PAD (pad) && GST_IS_EVENT (event)) {
1814 gst_pad_send_event (pad, event);
1816 gst_object_unref (pad);
1821 gst_ximagesink_navigation_init (GstNavigationInterface * iface)
1823 iface->send_event = gst_ximagesink_navigation_send_event;
1827 gst_ximagesink_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id)
1829 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1830 GstXWindow *xwindow = NULL;
1831 XWindowAttributes attr;
1833 /* We acquire the stream lock while setting this window in the element.
1834 We are basically cleaning tons of stuff replacing the old window, putting
1835 images while we do that would surely crash */
1836 g_mutex_lock (ximagesink->flow_lock);
1838 /* If we already use that window return */
1839 if (ximagesink->xwindow && (xwindow_id == ximagesink->xwindow->win)) {
1840 g_mutex_unlock (ximagesink->flow_lock);
1844 /* If the element has not initialized the X11 context try to do so */
1845 if (!ximagesink->xcontext &&
1846 !(ximagesink->xcontext = gst_ximagesink_xcontext_get (ximagesink))) {
1847 g_mutex_unlock (ximagesink->flow_lock);
1848 /* we have thrown a GST_ELEMENT_ERROR now */
1852 /* If a window is there already we destroy it */
1853 if (ximagesink->xwindow) {
1854 gst_ximagesink_xwindow_destroy (ximagesink, ximagesink->xwindow);
1855 ximagesink->xwindow = NULL;
1858 /* If the xid is 0 we go back to an internal window */
1859 if (xwindow_id == 0) {
1860 /* If no width/height caps nego did not happen window will be created
1861 during caps nego then */
1862 if (GST_VIDEO_SINK_WIDTH (ximagesink) && GST_VIDEO_SINK_HEIGHT (ximagesink)) {
1863 xwindow = gst_ximagesink_xwindow_new (ximagesink,
1864 GST_VIDEO_SINK_WIDTH (ximagesink),
1865 GST_VIDEO_SINK_HEIGHT (ximagesink));
1868 xwindow = g_new0 (GstXWindow, 1);
1870 xwindow->win = xwindow_id;
1872 /* We get window geometry, set the event we want to receive,
1874 g_mutex_lock (ximagesink->x_lock);
1875 XGetWindowAttributes (ximagesink->xcontext->disp, xwindow->win, &attr);
1876 xwindow->width = attr.width;
1877 xwindow->height = attr.height;
1878 xwindow->internal = FALSE;
1879 if (ximagesink->handle_events) {
1880 XSelectInput (ximagesink->xcontext->disp, xwindow->win, ExposureMask |
1881 StructureNotifyMask | PointerMotionMask | KeyPressMask |
1885 xwindow->gc = XCreateGC (ximagesink->xcontext->disp, xwindow->win, 0, NULL);
1886 g_mutex_unlock (ximagesink->x_lock);
1890 ximagesink->xwindow = xwindow;
1892 g_mutex_unlock (ximagesink->flow_lock);
1896 gst_ximagesink_expose (GstXOverlay * overlay)
1898 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1900 gst_ximagesink_ximage_put (ximagesink, NULL);
1904 gst_ximagesink_set_event_handling (GstXOverlay * overlay,
1905 gboolean handle_events)
1907 GstXImageSink *ximagesink = GST_XIMAGESINK (overlay);
1909 ximagesink->handle_events = handle_events;
1911 g_mutex_lock (ximagesink->flow_lock);
1913 if (G_UNLIKELY (!ximagesink->xwindow)) {
1914 g_mutex_unlock (ximagesink->flow_lock);
1918 g_mutex_lock (ximagesink->x_lock);
1920 if (handle_events) {
1921 if (ximagesink->xwindow->internal) {
1922 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1923 ExposureMask | StructureNotifyMask | PointerMotionMask |
1924 KeyPressMask | KeyReleaseMask | ButtonPressMask | ButtonReleaseMask);
1926 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win,
1927 ExposureMask | StructureNotifyMask | PointerMotionMask |
1928 KeyPressMask | KeyReleaseMask);
1931 XSelectInput (ximagesink->xcontext->disp, ximagesink->xwindow->win, 0);
1934 g_mutex_unlock (ximagesink->x_lock);
1936 g_mutex_unlock (ximagesink->flow_lock);
1940 gst_ximagesink_xoverlay_init (GstXOverlayClass * iface)
1942 iface->set_xwindow_id = gst_ximagesink_set_xwindow_id;
1943 iface->expose = gst_ximagesink_expose;
1944 iface->handle_events = gst_ximagesink_set_event_handling;
1947 /* =========================================== */
1949 /* Init & Class init */
1951 /* =========================================== */
1954 gst_ximagesink_set_property (GObject * object, guint prop_id,
1955 const GValue * value, GParamSpec * pspec)
1957 GstXImageSink *ximagesink;
1959 g_return_if_fail (GST_IS_XIMAGESINK (object));
1961 ximagesink = GST_XIMAGESINK (object);
1965 ximagesink->display_name = g_strdup (g_value_get_string (value));
1967 case PROP_SYNCHRONOUS:
1968 ximagesink->synchronous = g_value_get_boolean (value);
1969 if (ximagesink->xcontext) {
1970 GST_DEBUG_OBJECT (ximagesink, "XSynchronize called with %s",
1971 ximagesink->synchronous ? "TRUE" : "FALSE");
1972 g_mutex_lock (ximagesink->x_lock);
1973 XSynchronize (ximagesink->xcontext->disp, ximagesink->synchronous);
1974 g_mutex_unlock (ximagesink->x_lock);
1977 case PROP_FORCE_ASPECT_RATIO:
1978 ximagesink->keep_aspect = g_value_get_boolean (value);
1980 case PROP_PIXEL_ASPECT_RATIO:
1984 tmp = g_new0 (GValue, 1);
1985 g_value_init (tmp, GST_TYPE_FRACTION);
1987 if (!g_value_transform (value, tmp)) {
1988 GST_WARNING_OBJECT (ximagesink,
1989 "Could not transform string to aspect ratio");
1992 GST_DEBUG_OBJECT (ximagesink, "set PAR to %d/%d",
1993 gst_value_get_fraction_numerator (tmp),
1994 gst_value_get_fraction_denominator (tmp));
1995 g_free (ximagesink->par);
1996 ximagesink->par = tmp;
2000 case PROP_HANDLE_EVENTS:
2001 gst_ximagesink_set_event_handling (GST_X_OVERLAY (ximagesink),
2002 g_value_get_boolean (value));
2005 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2011 gst_ximagesink_get_property (GObject * object, guint prop_id,
2012 GValue * value, GParamSpec * pspec)
2014 GstXImageSink *ximagesink;
2016 g_return_if_fail (GST_IS_XIMAGESINK (object));
2018 ximagesink = GST_XIMAGESINK (object);
2022 g_value_set_string (value, ximagesink->display_name);
2024 case PROP_SYNCHRONOUS:
2025 g_value_set_boolean (value, ximagesink->synchronous);
2027 case PROP_FORCE_ASPECT_RATIO:
2028 g_value_set_boolean (value, ximagesink->keep_aspect);
2030 case PROP_PIXEL_ASPECT_RATIO:
2031 if (ximagesink->par)
2032 g_value_transform (ximagesink->par, value);
2034 case PROP_HANDLE_EVENTS:
2035 g_value_set_boolean (value, ximagesink->handle_events);
2038 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2044 gst_ximagesink_finalize (GObject * object)
2046 GstXImageSink *ximagesink;
2048 ximagesink = GST_XIMAGESINK (object);
2050 if (ximagesink->display_name) {
2051 g_free (ximagesink->display_name);
2052 ximagesink->display_name = NULL;
2054 if (ximagesink->par) {
2055 g_free (ximagesink->par);
2056 ximagesink->par = NULL;
2058 if (ximagesink->x_lock) {
2059 g_mutex_free (ximagesink->x_lock);
2060 ximagesink->x_lock = NULL;
2062 if (ximagesink->flow_lock) {
2063 g_mutex_free (ximagesink->flow_lock);
2064 ximagesink->flow_lock = NULL;
2066 if (ximagesink->pool_lock) {
2067 g_mutex_free (ximagesink->pool_lock);
2068 ximagesink->pool_lock = NULL;
2071 G_OBJECT_CLASS (parent_class)->finalize (object);
2075 gst_ximagesink_init (GstXImageSink * ximagesink)
2077 ximagesink->display_name = NULL;
2078 ximagesink->xcontext = NULL;
2079 ximagesink->xwindow = NULL;
2080 ximagesink->ximage = NULL;
2081 ximagesink->cur_image = NULL;
2083 ximagesink->event_thread = NULL;
2084 ximagesink->running = FALSE;
2086 ximagesink->fps_n = 0;
2087 ximagesink->fps_d = 1;
2089 ximagesink->x_lock = g_mutex_new ();
2090 ximagesink->flow_lock = g_mutex_new ();
2092 ximagesink->par = NULL;
2094 ximagesink->pool_lock = g_mutex_new ();
2095 ximagesink->buffer_pool = NULL;
2097 ximagesink->synchronous = FALSE;
2098 ximagesink->keep_aspect = FALSE;
2099 ximagesink->handle_events = TRUE;
2103 gst_ximagesink_base_init (gpointer g_class)
2105 GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
2107 gst_element_class_set_details (element_class, &gst_ximagesink_details);
2109 gst_element_class_add_pad_template (element_class,
2110 gst_static_pad_template_get (&gst_ximagesink_sink_template_factory));
2114 gst_ximagesink_class_init (GstXImageSinkClass * klass)
2116 GObjectClass *gobject_class;
2117 GstElementClass *gstelement_class;
2118 GstBaseSinkClass *gstbasesink_class;
2120 gobject_class = (GObjectClass *) klass;
2121 gstelement_class = (GstElementClass *) klass;
2122 gstbasesink_class = (GstBaseSinkClass *) klass;
2124 parent_class = g_type_class_peek_parent (klass);
2126 gobject_class->finalize = gst_ximagesink_finalize;
2127 gobject_class->set_property = gst_ximagesink_set_property;
2128 gobject_class->get_property = gst_ximagesink_get_property;
2130 g_object_class_install_property (gobject_class, PROP_DISPLAY,
2131 g_param_spec_string ("display", "Display", "X Display name",
2132 NULL, G_PARAM_READWRITE));
2133 g_object_class_install_property (gobject_class, PROP_SYNCHRONOUS,
2134 g_param_spec_boolean ("synchronous", "Synchronous", "When enabled, runs "
2135 "the X display in synchronous mode. (used only for debugging)", FALSE,
2136 G_PARAM_READWRITE));
2137 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
2138 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
2139 "When enabled, reverse caps negotiation (scaling) will respect "
2140 "original aspect ratio", FALSE, G_PARAM_READWRITE));
2141 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
2142 g_param_spec_string ("pixel-aspect-ratio", "Pixel Aspect Ratio",
2143 "The pixel aspect ratio of the device", "1/1", G_PARAM_READWRITE));
2144 g_object_class_install_property (gobject_class, PROP_HANDLE_EVENTS,
2145 g_param_spec_boolean ("handle-events", "Handle XEvents",
2146 "When enabled, XEvents will be selected and handled", TRUE,
2147 G_PARAM_READWRITE));
2149 gstelement_class->change_state = gst_ximagesink_change_state;
2151 gstbasesink_class->get_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_getcaps);
2152 gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_ximagesink_setcaps);
2153 gstbasesink_class->buffer_alloc =
2154 GST_DEBUG_FUNCPTR (gst_ximagesink_buffer_alloc);
2155 gstbasesink_class->get_times = GST_DEBUG_FUNCPTR (gst_ximagesink_get_times);
2156 gstbasesink_class->preroll = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2157 gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_ximagesink_show_frame);
2160 /* ============================================================= */
2162 /* Public Methods */
2164 /* ============================================================= */
2166 /* =========================================== */
2168 /* Object typing & Creation */
2170 /* =========================================== */
2173 gst_ximagesink_get_type (void)
2175 static GType ximagesink_type = 0;
2177 if (!ximagesink_type) {
2178 static const GTypeInfo ximagesink_info = {
2179 sizeof (GstXImageSinkClass),
2180 gst_ximagesink_base_init,
2182 (GClassInitFunc) gst_ximagesink_class_init,
2185 sizeof (GstXImageSink),
2187 (GInstanceInitFunc) gst_ximagesink_init,
2189 static const GInterfaceInfo iface_info = {
2190 (GInterfaceInitFunc) gst_ximagesink_interface_init,
2194 static const GInterfaceInfo navigation_info = {
2195 (GInterfaceInitFunc) gst_ximagesink_navigation_init,
2199 static const GInterfaceInfo overlay_info = {
2200 (GInterfaceInitFunc) gst_ximagesink_xoverlay_init,
2205 ximagesink_type = g_type_register_static (GST_TYPE_VIDEO_SINK,
2206 "GstXImageSink", &ximagesink_info, 0);
2208 g_type_add_interface_static (ximagesink_type, GST_TYPE_IMPLEMENTS_INTERFACE,
2210 g_type_add_interface_static (ximagesink_type, GST_TYPE_NAVIGATION,
2212 g_type_add_interface_static (ximagesink_type, GST_TYPE_X_OVERLAY,
2215 /* register type and create class in a more safe place instead of at
2216 * runtime since the type registration and class creation is not
2218 g_type_class_ref (gst_ximage_buffer_get_type ());
2221 return ximagesink_type;