ximagesrc: Add missing return value to Buffer dispose function
[platform/upstream/gst-plugins-good.git] / sys / ximage / gstximagesrc.c
1 /* GStreamer
2  *
3  * Copyright (C) 2006 Zaheer Merali <zaheerabbas at merali dot org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 /**
22  * SECTION:element-ximagesrc
23  *
24  * This element captures your X Display and creates raw RGB video.  It uses
25  * the XDamage extension if available to only capture areas of the screen that
26  * have changed since the last frame.  It uses the XFixes extension if
27  * available to also capture your mouse pointer.  By default it will fixate to
28  * 25 frames per second.
29  *
30  * <refsect2>
31  * <title>Example pipelines</title>
32  * |[
33  * gst-launch-1.0 ximagesrc ! video/x-raw,framerate=5/1 ! videoconvert ! theoraenc ! oggmux ! filesink location=desktop.ogg
34  * ]| Encodes your X display to an Ogg theora video at 5 frames per second.
35  * </refsect2>
36  */
37
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41 #include "gstximagesrc.h"
42
43 #include <string.h>
44 #include <stdlib.h>
45
46 #include <X11/Xlib.h>
47 #include <X11/Xutil.h>
48
49 #include <gst/gst.h>
50 #include <gst/gst-i18n-plugin.h>
51 #include <gst/video/video.h>
52
53 #include "gst/glib-compat-private.h"
54
55 GST_DEBUG_CATEGORY_STATIC (gst_debug_ximage_src);
56 #define GST_CAT_DEFAULT gst_debug_ximage_src
57
58 static GstStaticPadTemplate t =
59 GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
60     GST_STATIC_CAPS ("video/x-raw, "
61         "framerate = (fraction) [ 0, MAX ], "
62         "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], "
63         "pixel-aspect-ratio = (fraction) [ 0, MAX ]"));
64
65 enum
66 {
67   PROP_0,
68   PROP_DISPLAY_NAME,
69   PROP_SCREEN_NUM,
70   PROP_SHOW_POINTER,
71   PROP_USE_DAMAGE,
72   PROP_STARTX,
73   PROP_STARTY,
74   PROP_ENDX,
75   PROP_ENDY,
76   PROP_REMOTE,
77   PROP_XID,
78   PROP_XNAME,
79 };
80
81 #define gst_ximage_src_parent_class parent_class
82 G_DEFINE_TYPE (GstXImageSrc, gst_ximage_src, GST_TYPE_PUSH_SRC);
83
84 static GstCaps *gst_ximage_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
85 static void gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc);
86
87 /* Called when a buffer is returned from the pipeline */
88 static gboolean
89 gst_ximage_src_return_buf (GstXImageSrc * ximagesrc, GstBuffer * ximage)
90 {
91   GstMetaXImage *meta = GST_META_XIMAGE_GET (ximage);
92   /* True will make dispose free the buffer, while false will keep it */
93   gboolean ret = TRUE;
94
95   /* If our geometry changed we can't reuse that image. */
96   if ((meta->width != ximagesrc->width) || (meta->height != ximagesrc->height)) {
97     GST_DEBUG_OBJECT (ximagesrc,
98         "destroy image %p as its size changed %dx%d vs current %dx%d",
99         ximage, meta->width, meta->height, ximagesrc->width, ximagesrc->height);
100     g_mutex_lock (&ximagesrc->x_lock);
101     gst_ximageutil_ximage_destroy (ximagesrc->xcontext, ximage);
102     g_mutex_unlock (&ximagesrc->x_lock);
103   } else {
104     /* In that case we can reuse the image and add it to our image pool. */
105     GST_LOG_OBJECT (ximagesrc, "recycling image %p in pool", ximage);
106     /* need to increment the refcount again to recycle */
107     gst_buffer_ref (ximage);
108     g_mutex_lock (&ximagesrc->pool_lock);
109     GST_BUFFER_FLAGS (GST_BUFFER (ximage)) = 0; /* clear out any flags from the previous use */
110     ximagesrc->buffer_pool = g_slist_prepend (ximagesrc->buffer_pool, ximage);
111     g_mutex_unlock (&ximagesrc->pool_lock);
112     ret = FALSE;
113   }
114
115   return ret;
116 }
117
118 static Window
119 gst_ximage_src_find_window (GstXImageSrc * src, Window root, const char *name)
120 {
121   Window *children;
122   Window window = 0, root_return, parent_return;
123   unsigned int nchildren;
124   char *tmpname;
125   int n, status;
126
127   status = XFetchName (src->xcontext->disp, root, &tmpname);
128   if (status && !strcmp (name, tmpname))
129     return root;
130
131   status =
132       XQueryTree (src->xcontext->disp, root, &root_return, &parent_return,
133       &children, &nchildren);
134   if (!status || !children)
135     return (Window) 0;
136
137   for (n = 0; n < nchildren; ++n) {
138     window = gst_ximage_src_find_window (src, children[n], name);
139     if (window != 0)
140       break;
141   }
142
143   XFree (children);
144   return window;
145 }
146
147 static gboolean
148 gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name)
149 {
150   g_return_val_if_fail (GST_IS_XIMAGE_SRC (s), FALSE);
151
152   if (s->xcontext != NULL)
153     return TRUE;
154
155   g_mutex_lock (&s->x_lock);
156   s->xcontext = ximageutil_xcontext_get (GST_ELEMENT (s), name);
157   if (s->xcontext == NULL) {
158     g_mutex_unlock (&s->x_lock);
159     GST_ELEMENT_ERROR (s, RESOURCE, OPEN_READ,
160         ("Could not open X display for reading"),
161         ("NULL returned from getting xcontext"));
162     return FALSE;
163   }
164   s->width = s->xcontext->width;
165   s->height = s->xcontext->height;
166
167   s->xwindow = s->xcontext->root;
168   if (s->xid != 0 || s->xname) {
169     int status;
170     XWindowAttributes attrs;
171     Window window;
172
173     if (s->xid != 0) {
174       status = XGetWindowAttributes (s->xcontext->disp, s->xid, &attrs);
175       if (status) {
176         GST_DEBUG_OBJECT (s, "Found window XID %" G_GUINT64_FORMAT, s->xid);
177         s->xwindow = s->xid;
178         goto window_found;
179       } else {
180         GST_WARNING_OBJECT (s, "Failed to get window %" G_GUINT64_FORMAT
181             " attributes", s->xid);
182       }
183     }
184
185     if (s->xname) {
186       GST_DEBUG_OBJECT (s, "Looking for window %s", s->xname);
187       window = gst_ximage_src_find_window (s, s->xcontext->root, s->xname);
188       if (window != 0) {
189         GST_DEBUG_OBJECT (s, "Found window named %s, ", s->xname);
190         status = XGetWindowAttributes (s->xcontext->disp, window, &attrs);
191         if (status) {
192           s->xwindow = window;
193           goto window_found;
194         } else {
195           GST_WARNING_OBJECT (s, "Failed to get window attributes for "
196               "window named %s", s->xname);
197         }
198       }
199     }
200
201     GST_INFO_OBJECT (s, "Using root window");
202     goto use_root_window;
203
204   window_found:
205     g_assert (s->xwindow != 0);
206     s->width = attrs.width;
207     s->height = attrs.height;
208     GST_INFO_OBJECT (s, "Using default window size of %dx%d",
209         s->width, s->height);
210   }
211 use_root_window:
212
213 #ifdef HAVE_XFIXES
214   /* check if xfixes supported */
215   {
216     int error_base;
217
218     if (XFixesQueryExtension (s->xcontext->disp, &s->fixes_event_base,
219             &error_base)) {
220       s->have_xfixes = TRUE;
221       GST_DEBUG_OBJECT (s, "X Server supports XFixes");
222     } else {
223
224       GST_DEBUG_OBJECT (s, "X Server does not support XFixes");
225     }
226   }
227
228 #ifdef HAVE_XDAMAGE
229   /* check if xdamage is supported */
230   {
231     int error_base;
232     long evmask = NoEventMask;
233
234     s->have_xdamage = FALSE;
235     s->damage = None;
236     s->damage_copy_gc = None;
237     s->damage_region = None;
238
239     if (XDamageQueryExtension (s->xcontext->disp, &s->damage_event_base,
240             &error_base)) {
241       s->damage =
242           XDamageCreate (s->xcontext->disp, s->xwindow, XDamageReportNonEmpty);
243       if (s->damage != None) {
244         s->damage_region = XFixesCreateRegion (s->xcontext->disp, NULL, 0);
245         if (s->damage_region != None) {
246           XGCValues values;
247
248           GST_DEBUG_OBJECT (s, "Using XDamage extension");
249           values.subwindow_mode = IncludeInferiors;
250           s->damage_copy_gc = XCreateGC (s->xcontext->disp,
251               s->xwindow, GCSubwindowMode, &values);
252           XSelectInput (s->xcontext->disp, s->xwindow, evmask);
253
254           s->have_xdamage = TRUE;
255         } else {
256           XDamageDestroy (s->xcontext->disp, s->damage);
257           s->damage = None;
258         }
259       } else
260         GST_DEBUG_OBJECT (s, "Could not attach to XDamage");
261     } else {
262       GST_DEBUG_OBJECT (s, "X Server does not have XDamage extension");
263     }
264   }
265 #endif
266 #endif
267
268   g_mutex_unlock (&s->x_lock);
269
270   if (s->xcontext == NULL)
271     return FALSE;
272
273   return TRUE;
274 }
275
276 static gboolean
277 gst_ximage_src_start (GstBaseSrc * basesrc)
278 {
279   GstXImageSrc *s = GST_XIMAGE_SRC (basesrc);
280
281   s->last_frame_no = -1;
282 #ifdef HAVE_XDAMAGE
283   if (s->last_ximage)
284     gst_buffer_unref (GST_BUFFER_CAST (s->last_ximage));
285   s->last_ximage = NULL;
286 #endif
287   return gst_ximage_src_open_display (s, s->display_name);
288 }
289
290 static gboolean
291 gst_ximage_src_stop (GstBaseSrc * basesrc)
292 {
293   GstXImageSrc *src = GST_XIMAGE_SRC (basesrc);
294
295 #ifdef HAVE_XDAMAGE
296   if (src->last_ximage)
297     gst_buffer_unref (GST_BUFFER_CAST (src->last_ximage));
298   src->last_ximage = NULL;
299 #endif
300
301   gst_ximage_src_clear_bufpool (src);
302
303 #ifdef HAVE_XFIXES
304   if (src->cursor_image)
305     XFree (src->cursor_image);
306   src->cursor_image = NULL;
307 #endif
308
309   if (src->xcontext) {
310     g_mutex_lock (&src->x_lock);
311
312 #ifdef HAVE_XDAMAGE
313     if (src->damage_copy_gc != None) {
314       XFreeGC (src->xcontext->disp, src->damage_copy_gc);
315       src->damage_copy_gc = None;
316     }
317     if (src->damage_region != None) {
318       XFixesDestroyRegion (src->xcontext->disp, src->damage_region);
319       src->damage_region = None;
320     }
321     if (src->damage != None) {
322       XDamageDestroy (src->xcontext->disp, src->damage);
323       src->damage = None;
324     }
325 #endif
326
327     ximageutil_xcontext_clear (src->xcontext);
328     src->xcontext = NULL;
329     g_mutex_unlock (&src->x_lock);
330   }
331
332   return TRUE;
333 }
334
335 static gboolean
336 gst_ximage_src_unlock (GstBaseSrc * basesrc)
337 {
338   GstXImageSrc *src = GST_XIMAGE_SRC (basesrc);
339
340   /* Awaken the create() func if it's waiting on the clock */
341   GST_OBJECT_LOCK (src);
342   if (src->clock_id) {
343     GST_DEBUG_OBJECT (src, "Waking up waiting clock");
344     gst_clock_id_unschedule (src->clock_id);
345   }
346   GST_OBJECT_UNLOCK (src);
347
348   return TRUE;
349 }
350
351 static gboolean
352 gst_ximage_src_recalc (GstXImageSrc * src)
353 {
354   if (!src->xcontext)
355     return FALSE;
356
357   /* Maybe later we can check the display hasn't changed size */
358   /* We could use XQueryPointer to get only the current window. */
359   return TRUE;
360 }
361
362 #ifdef HAVE_XFIXES
363 static void
364 composite_pixel (GstXContext * xcontext, guchar * dest, guchar * src)
365 {
366   guint8 r = src[2];
367   guint8 g = src[1];
368   guint8 b = src[0];
369   guint8 a = src[3];
370   guint8 dr, dg, db;
371   guint32 color;
372   gint r_shift, r_max, r_shift_out;
373   gint g_shift, g_max, g_shift_out;
374   gint b_shift, b_max, b_shift_out;
375
376   switch (xcontext->bpp) {
377     case 8:
378       color = *dest;
379       break;
380     case 16:
381       color = GUINT16_FROM_LE (*(guint16 *) (dest));
382       break;
383     case 32:
384       color = GUINT32_FROM_LE (*(guint32 *) (dest));
385       break;
386     default:
387       /* Should not reach here */
388       g_return_if_reached ();
389   }
390
391   /* possible optimisation:
392    * move the code that finds shift and max in the _link function */
393   for (r_shift = 0; !(xcontext->visual->red_mask & (1 << r_shift)); r_shift++);
394   for (g_shift = 0; !(xcontext->visual->green_mask & (1 << g_shift));
395       g_shift++);
396   for (b_shift = 0; !(xcontext->visual->blue_mask & (1 << b_shift)); b_shift++);
397
398   for (r_shift_out = 0; !(xcontext->visual->red_mask & (1 << r_shift_out));
399       r_shift_out++);
400   for (g_shift_out = 0; !(xcontext->visual->green_mask & (1 << g_shift_out));
401       g_shift_out++);
402   for (b_shift_out = 0; !(xcontext->visual->blue_mask & (1 << b_shift_out));
403       b_shift_out++);
404
405
406   r_max = (xcontext->visual->red_mask >> r_shift);
407   b_max = (xcontext->visual->blue_mask >> b_shift);
408   g_max = (xcontext->visual->green_mask >> g_shift);
409
410 #define RGBXXX_R(x)  (((x)>>r_shift) & (r_max))
411 #define RGBXXX_G(x)  (((x)>>g_shift) & (g_max))
412 #define RGBXXX_B(x)  (((x)>>b_shift) & (b_max))
413
414   dr = (RGBXXX_R (color) * 255) / r_max;
415   dg = (RGBXXX_G (color) * 255) / g_max;
416   db = (RGBXXX_B (color) * 255) / b_max;
417
418   dr = (r * a + (0xff - a) * dr) / 0xff;
419   dg = (g * a + (0xff - a) * dg) / 0xff;
420   db = (b * a + (0xff - a) * db) / 0xff;
421
422   color = (((dr * r_max) / 255) << r_shift_out) +
423       (((dg * g_max) / 255) << g_shift_out) +
424       (((db * b_max) / 255) << b_shift_out);
425
426   switch (xcontext->bpp) {
427     case 8:
428       *dest = color;
429       break;
430     case 16:
431       *(guint16 *) (dest) = color;
432       break;
433     case 32:
434       *(guint32 *) (dest) = color;
435       break;
436     default:
437       g_warning ("bpp %d not supported\n", xcontext->bpp);
438   }
439 }
440 #endif
441
442 #ifdef HAVE_XDAMAGE
443 static void
444 copy_buffer (GstBuffer * dest, GstBuffer * src)
445 {
446   GstMapInfo map;
447
448   gst_buffer_map (src, &map, GST_MAP_READ);
449   gst_buffer_fill (dest, 0, map.data, map.size);
450   gst_buffer_unmap (src, &map);
451 }
452 #endif
453
454 /* Retrieve an XImageSrcBuffer, preferably from our
455  * pool of existing images and populate it from the window */
456 static GstBuffer *
457 gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc)
458 {
459   GstBuffer *ximage = NULL;
460   GstMetaXImage *meta;
461
462   g_mutex_lock (&ximagesrc->pool_lock);
463   while (ximagesrc->buffer_pool != NULL) {
464     ximage = ximagesrc->buffer_pool->data;
465
466     meta = GST_META_XIMAGE_GET (ximage);
467
468     ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool,
469         ximagesrc->buffer_pool);
470
471     if ((meta->width == ximagesrc->width) ||
472         (meta->height == ximagesrc->height))
473       break;
474
475     gst_ximage_buffer_free (ximage);
476     ximage = NULL;
477   }
478   g_mutex_unlock (&ximagesrc->pool_lock);
479
480   if (ximage == NULL) {
481     GST_DEBUG_OBJECT (ximagesrc, "creating image (%dx%d)",
482         ximagesrc->width, ximagesrc->height);
483
484     g_mutex_lock (&ximagesrc->x_lock);
485     ximage = gst_ximageutil_ximage_new (ximagesrc->xcontext,
486         GST_ELEMENT (ximagesrc), ximagesrc->width, ximagesrc->height,
487         (BufferReturnFunc) (gst_ximage_src_return_buf));
488     if (ximage == NULL) {
489       GST_ELEMENT_ERROR (ximagesrc, RESOURCE, WRITE, (NULL),
490           ("could not create a %dx%d ximage", ximagesrc->width,
491               ximagesrc->height));
492       g_mutex_unlock (&ximagesrc->x_lock);
493       return NULL;
494     }
495
496     g_mutex_unlock (&ximagesrc->x_lock);
497   }
498
499   g_return_val_if_fail (GST_IS_XIMAGE_SRC (ximagesrc), NULL);
500
501   meta = GST_META_XIMAGE_GET (ximage);
502
503 #ifdef HAVE_XDAMAGE
504   if (ximagesrc->have_xdamage && ximagesrc->use_damage &&
505       ximagesrc->last_ximage != NULL) {
506     XEvent ev;
507
508     /* have_frame is TRUE when either the entire screen has been
509      * grabbed or when the last image has been copied */
510     gboolean have_frame = FALSE;
511
512     GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XDamage");
513
514     do {
515       XNextEvent (ximagesrc->xcontext->disp, &ev);
516
517       if (ev.type == ximagesrc->damage_event_base + XDamageNotify) {
518         XserverRegion parts;
519         XRectangle *rects;
520         int nrects;
521
522         parts = XFixesCreateRegion (ximagesrc->xcontext->disp, 0, 0);
523         XDamageSubtract (ximagesrc->xcontext->disp, ximagesrc->damage, None,
524             parts);
525         /* Now copy out all of the damaged rectangles. */
526         rects = XFixesFetchRegion (ximagesrc->xcontext->disp, parts, &nrects);
527         if (rects != NULL) {
528           int i;
529
530           if (!have_frame) {
531             GST_LOG_OBJECT (ximagesrc,
532                 "Copying from last frame ximage->size: %" G_GSIZE_FORMAT,
533                 gst_buffer_get_size (ximage));
534             copy_buffer (ximage, ximagesrc->last_ximage);
535             have_frame = TRUE;
536           }
537           for (i = 0; i < nrects; i++) {
538             GST_LOG_OBJECT (ximagesrc,
539                 "Damaged sub-region @ %d,%d size %dx%d reported",
540                 rects[i].x, rects[i].y, rects[i].width, rects[i].height);
541
542             /* if we only want a small area, clip this damage region to
543              * area we want */
544             if (ximagesrc->endx > ximagesrc->startx &&
545                 ximagesrc->endy > ximagesrc->starty) {
546               /* see if damage area intersects */
547               if (rects[i].x + rects[i].width - 1 < ximagesrc->startx ||
548                   rects[i].x > ximagesrc->endx) {
549                 /* trivial reject */
550               } else if (rects[i].y + rects[i].height - 1 < ximagesrc->starty ||
551                   rects[i].y > ximagesrc->endy) {
552                 /* trivial reject */
553               } else {
554                 /* find intersect region */
555                 int startx, starty, width, height;
556
557                 startx = (rects[i].x < ximagesrc->startx) ? ximagesrc->startx :
558                     rects[i].x;
559                 starty = (rects[i].y < ximagesrc->starty) ? ximagesrc->starty :
560                     rects[i].y;
561                 width = (rects[i].x + rects[i].width - 1 < ximagesrc->endx) ?
562                     rects[i].x + rects[i].width - startx :
563                     ximagesrc->endx - startx + 1;
564                 height = (rects[i].y + rects[i].height - 1 < ximagesrc->endy) ?
565                     rects[i].y + rects[i].height - starty : ximagesrc->endy -
566                     starty + 1;
567
568                 GST_LOG_OBJECT (ximagesrc,
569                     "Retrieving damaged sub-region @ %d,%d size %dx%d as intersect region",
570                     startx, starty, width, height);
571                 XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
572                     startx, starty, width, height, AllPlanes, ZPixmap,
573                     meta->ximage, startx - ximagesrc->startx,
574                     starty - ximagesrc->starty);
575               }
576             } else {
577
578               GST_LOG_OBJECT (ximagesrc,
579                   "Retrieving damaged sub-region @ %d,%d size %dx%d",
580                   rects[i].x, rects[i].y, rects[i].width, rects[i].height);
581
582               XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
583                   rects[i].x, rects[i].y,
584                   rects[i].width, rects[i].height,
585                   AllPlanes, ZPixmap, meta->ximage, rects[i].x, rects[i].y);
586             }
587           }
588           free (rects);
589         }
590       }
591     } while (XPending (ximagesrc->xcontext->disp));
592     if (!have_frame) {
593       GST_LOG_OBJECT (ximagesrc,
594           "Copying from last frame ximage->size: %" G_GSIZE_FORMAT,
595           gst_buffer_get_size (ximage));
596       copy_buffer (ximage, ximagesrc->last_ximage);
597     }
598 #ifdef HAVE_XFIXES
599     /* re-get area where last mouse pointer was  but only if in our clipping
600      * bounds */
601     if (ximagesrc->cursor_image) {
602       gint x, y, width, height;
603
604       x = ximagesrc->cursor_image->x - ximagesrc->cursor_image->xhot;
605       y = ximagesrc->cursor_image->y - ximagesrc->cursor_image->yhot;
606       width = ximagesrc->cursor_image->width;
607       height = ximagesrc->cursor_image->height;
608
609       /* bounds checking */
610       if (x < 0)
611         x = 0;
612       if (y < 0)
613         y = 0;
614       if (x + width > ximagesrc->xcontext->width)
615         width = ximagesrc->xcontext->width - x;
616       if (y + height > ximagesrc->xcontext->height)
617         height = ximagesrc->xcontext->height - y;
618       g_assert (x >= 0);
619       g_assert (y >= 0);
620       GST_DEBUG_OBJECT (ximagesrc,
621           "Cursor was at (%d,%d) width: %d, height: %d and our range is: (%d,%d) - (%d,%d)",
622           x, y, width, height, ximagesrc->startx, ximagesrc->starty,
623           ximagesrc->endx, ximagesrc->endy);
624       /* only get where cursor last was, if it is in our range */
625       if (ximagesrc->endx > ximagesrc->startx &&
626           ximagesrc->endy > ximagesrc->starty) {
627         /* check bounds */
628         if (x + width < ximagesrc->startx || x > ximagesrc->endx) {
629           /* trivial reject */
630         } else if (y + height < ximagesrc->starty || y > ximagesrc->endy) {
631           /* trivial reject */
632         } else {
633           /* find intersect region */
634           int startx, starty, iwidth, iheight;
635
636           startx = (x < ximagesrc->startx) ? ximagesrc->startx : x;
637           starty = (y < ximagesrc->starty) ? ximagesrc->starty : y;
638           iwidth = (x + width < ximagesrc->endx) ?
639               x + width - startx : ximagesrc->endx - startx;
640           iheight = (y + height < ximagesrc->endy) ?
641               y + height - starty : ximagesrc->endy - starty;
642           GST_DEBUG_OBJECT (ximagesrc, "Removing cursor from %d,%d", x, y);
643           XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
644               startx, starty, iwidth, iheight, AllPlanes, ZPixmap,
645               meta->ximage, startx - ximagesrc->startx,
646               starty - ximagesrc->starty);
647         }
648       } else {
649
650         GST_DEBUG_OBJECT (ximagesrc, "Removing cursor from %d,%d", x, y);
651         XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
652             x, y, width, height, AllPlanes, ZPixmap, meta->ximage, x, y);
653       }
654     }
655 #endif
656
657
658   } else {
659 #endif
660
661 #ifdef HAVE_XSHM
662     if (ximagesrc->xcontext->use_xshm) {
663       GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XShm");
664       XShmGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
665           meta->ximage, ximagesrc->startx, ximagesrc->starty, AllPlanes);
666
667     } else
668 #endif /* HAVE_XSHM */
669     {
670       GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XGetImage");
671       if (ximagesrc->remote) {
672         XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
673             ximagesrc->startx, ximagesrc->starty, ximagesrc->width,
674             ximagesrc->height, AllPlanes, ZPixmap, meta->ximage, 0, 0);
675       } else {
676         meta->ximage =
677             XGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
678             ximagesrc->startx, ximagesrc->starty, ximagesrc->width,
679             ximagesrc->height, AllPlanes, ZPixmap);
680       }
681     }
682 #ifdef HAVE_XDAMAGE
683   }
684 #endif
685
686 #ifdef HAVE_XFIXES
687   if (ximagesrc->show_pointer && ximagesrc->have_xfixes) {
688
689     GST_DEBUG_OBJECT (ximagesrc, "Using XFixes to draw cursor");
690     /* get cursor */
691     if (ximagesrc->cursor_image)
692       XFree (ximagesrc->cursor_image);
693     ximagesrc->cursor_image = XFixesGetCursorImage (ximagesrc->xcontext->disp);
694     if (ximagesrc->cursor_image != NULL) {
695       int cx, cy, i, j, count;
696       int startx, starty, iwidth, iheight;
697       gboolean cursor_in_image = TRUE;
698
699       cx = ximagesrc->cursor_image->x - ximagesrc->cursor_image->xhot;
700       if (cx < 0)
701         cx = 0;
702       cy = ximagesrc->cursor_image->y - ximagesrc->cursor_image->yhot;
703       if (cy < 0)
704         cy = 0;
705       count = ximagesrc->cursor_image->width * ximagesrc->cursor_image->height;
706
707       /* only get where cursor last was, if it is in our range */
708       if (ximagesrc->endx > ximagesrc->startx &&
709           ximagesrc->endy > ximagesrc->starty) {
710         /* check bounds */
711         if (cx + ximagesrc->cursor_image->width < ximagesrc->startx ||
712             cx > ximagesrc->endx) {
713           /* trivial reject */
714           cursor_in_image = FALSE;
715         } else if (cy + ximagesrc->cursor_image->height < ximagesrc->starty ||
716             cy > ximagesrc->endy) {
717           /* trivial reject */
718           cursor_in_image = FALSE;
719         } else {
720           /* find intersect region */
721
722           startx = (cx < ximagesrc->startx) ? ximagesrc->startx : cx;
723           starty = (cy < ximagesrc->starty) ? ximagesrc->starty : cy;
724           iwidth = (cx + ximagesrc->cursor_image->width < ximagesrc->endx) ?
725               cx + ximagesrc->cursor_image->width - startx :
726               ximagesrc->endx - startx;
727           iheight = (cy + ximagesrc->cursor_image->height < ximagesrc->endy) ?
728               cy + ximagesrc->cursor_image->height - starty :
729               ximagesrc->endy - starty;
730         }
731       } else {
732         startx = cx;
733         starty = cy;
734         iwidth = ximagesrc->cursor_image->width;
735         iheight = ximagesrc->cursor_image->height;
736       }
737
738       if (cursor_in_image) {
739         GST_DEBUG_OBJECT (ximagesrc, "Cursor is in image so trying to draw it");
740         for (i = 0; i < count; i++)
741           ximagesrc->cursor_image->pixels[i] =
742               GUINT_TO_LE (ximagesrc->cursor_image->pixels[i]);
743         /* copy those pixels across */
744         for (j = starty;
745             j < starty + iheight && j < ximagesrc->starty + ximagesrc->height;
746             j++) {
747           for (i = startx;
748               i < startx + iwidth && i < ximagesrc->startx + ximagesrc->width;
749               i++) {
750             guint8 *src, *dest;
751
752             src =
753                 (guint8 *) & (ximagesrc->cursor_image->pixels[((j -
754                             cy) * ximagesrc->cursor_image->width + (i - cx))]);
755             dest =
756                 (guint8 *) & (meta->ximage->data[((j -
757                             ximagesrc->starty) * ximagesrc->width + (i -
758                             ximagesrc->startx)) * (ximagesrc->xcontext->bpp /
759                         8)]);
760
761             composite_pixel (ximagesrc->xcontext, (guint8 *) dest,
762                 (guint8 *) src);
763           }
764         }
765       }
766     }
767   }
768 #endif
769 #ifdef HAVE_XDAMAGE
770   if (ximagesrc->have_xdamage && ximagesrc->use_damage) {
771     /* need to ref ximage to put in last_ximage */
772     gst_buffer_ref (ximage);
773     if (ximagesrc->last_ximage) {
774       gst_buffer_unref (ximagesrc->last_ximage);
775     }
776     ximagesrc->last_ximage = ximage;
777     GST_LOG_OBJECT (ximagesrc, "reffing current buffer for last_ximage");
778   }
779 #endif
780   return ximage;
781 }
782
783 static GstFlowReturn
784 gst_ximage_src_create (GstPushSrc * bs, GstBuffer ** buf)
785 {
786   GstXImageSrc *s = GST_XIMAGE_SRC (bs);
787   GstBuffer *image;
788   GstClockTime base_time;
789   GstClockTime next_capture_ts;
790   GstClockTime dur;
791   gint64 next_frame_no;
792
793   if (!gst_ximage_src_recalc (s)) {
794     GST_ELEMENT_ERROR (s, RESOURCE, FAILED,
795         (_("Changing resolution at runtime is not yet supported.")), (NULL));
796     return GST_FLOW_ERROR;
797   }
798
799   if (s->fps_n <= 0 || s->fps_d <= 0)
800     return GST_FLOW_NOT_NEGOTIATED;     /* FPS must be > 0 */
801
802   /* Now, we might need to wait for the next multiple of the fps
803    * before capturing */
804
805   GST_OBJECT_LOCK (s);
806   if (GST_ELEMENT_CLOCK (s) == NULL) {
807     GST_OBJECT_UNLOCK (s);
808     GST_ELEMENT_ERROR (s, RESOURCE, FAILED,
809         (_("Cannot operate without a clock")), (NULL));
810     return GST_FLOW_ERROR;
811   }
812
813   base_time = GST_ELEMENT_CAST (s)->base_time;
814   next_capture_ts = gst_clock_get_time (GST_ELEMENT_CLOCK (s));
815   next_capture_ts -= base_time;
816
817   /* Figure out which 'frame number' position we're at, based on the cur time
818    * and frame rate */
819   next_frame_no = gst_util_uint64_scale (next_capture_ts,
820       s->fps_n, GST_SECOND * s->fps_d);
821   if (next_frame_no == s->last_frame_no) {
822     GstClockID id;
823     GstClockReturn ret;
824
825     /* Need to wait for the next frame */
826     next_frame_no += 1;
827
828     /* Figure out what the next frame time is */
829     next_capture_ts = gst_util_uint64_scale (next_frame_no,
830         s->fps_d * GST_SECOND, s->fps_n);
831
832     id = gst_clock_new_single_shot_id (GST_ELEMENT_CLOCK (s),
833         next_capture_ts + base_time);
834     s->clock_id = id;
835
836     /* release the object lock while waiting */
837     GST_OBJECT_UNLOCK (s);
838
839     GST_DEBUG_OBJECT (s, "Waiting for next frame time %" G_GUINT64_FORMAT,
840         next_capture_ts);
841     ret = gst_clock_id_wait (id, NULL);
842     GST_OBJECT_LOCK (s);
843
844     gst_clock_id_unref (id);
845     s->clock_id = NULL;
846     if (ret == GST_CLOCK_UNSCHEDULED) {
847       /* Got woken up by the unlock function */
848       GST_OBJECT_UNLOCK (s);
849       return GST_FLOW_FLUSHING;
850     }
851     /* Duration is a complete 1/fps frame duration */
852     dur = gst_util_uint64_scale_int (GST_SECOND, s->fps_d, s->fps_n);
853   } else {
854     GstClockTime next_frame_ts;
855
856     GST_DEBUG_OBJECT (s, "No need to wait for next frame time %"
857         G_GUINT64_FORMAT " next frame = %" G_GINT64_FORMAT " prev = %"
858         G_GINT64_FORMAT, next_capture_ts, next_frame_no, s->last_frame_no);
859     next_frame_ts = gst_util_uint64_scale (next_frame_no + 1,
860         s->fps_d * GST_SECOND, s->fps_n);
861     /* Frame duration is from now until the next expected capture time */
862     dur = next_frame_ts - next_capture_ts;
863   }
864   s->last_frame_no = next_frame_no;
865   GST_OBJECT_UNLOCK (s);
866
867   image = gst_ximage_src_ximage_get (s);
868   if (!image)
869     return GST_FLOW_ERROR;
870
871   *buf = image;
872   GST_BUFFER_DTS (*buf) = GST_CLOCK_TIME_NONE;
873   GST_BUFFER_PTS (*buf) = next_capture_ts;
874   GST_BUFFER_DURATION (*buf) = dur;
875
876   return GST_FLOW_OK;
877 }
878
879 static void
880 gst_ximage_src_set_property (GObject * object, guint prop_id,
881     const GValue * value, GParamSpec * pspec)
882 {
883   GstXImageSrc *src = GST_XIMAGE_SRC (object);
884
885   switch (prop_id) {
886     case PROP_DISPLAY_NAME:
887
888       g_free (src->display_name);
889       src->display_name = g_strdup (g_value_get_string (value));
890       break;
891     case PROP_SCREEN_NUM:
892       src->screen_num = g_value_get_uint (value);
893       break;
894     case PROP_SHOW_POINTER:
895       src->show_pointer = g_value_get_boolean (value);
896       break;
897     case PROP_USE_DAMAGE:
898       src->use_damage = g_value_get_boolean (value);
899       break;
900     case PROP_STARTX:
901       src->startx = g_value_get_uint (value);
902       break;
903     case PROP_STARTY:
904       src->starty = g_value_get_uint (value);
905       break;
906     case PROP_ENDX:
907       src->endx = g_value_get_uint (value);
908       break;
909     case PROP_ENDY:
910       src->endy = g_value_get_uint (value);
911       break;
912     case PROP_REMOTE:
913       src->remote = g_value_get_boolean (value);
914       break;
915     case PROP_XID:
916       if (src->xcontext != NULL) {
917         g_warning ("ximagesrc window ID must be set before opening display");
918         break;
919       }
920       src->xid = g_value_get_uint64 (value);
921       break;
922     case PROP_XNAME:
923       if (src->xcontext != NULL) {
924         g_warning ("ximagesrc window name must be set before opening display");
925         break;
926       }
927       g_free (src->xname);
928       src->xname = g_strdup (g_value_get_string (value));
929       break;
930     default:
931       break;
932   }
933 }
934
935 static void
936 gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value,
937     GParamSpec * pspec)
938 {
939   GstXImageSrc *src = GST_XIMAGE_SRC (object);
940
941   switch (prop_id) {
942     case PROP_DISPLAY_NAME:
943       if (src->xcontext)
944         g_value_set_string (value, DisplayString (src->xcontext->disp));
945       else
946         g_value_set_string (value, src->display_name);
947
948       break;
949     case PROP_SCREEN_NUM:
950       g_value_set_uint (value, src->screen_num);
951       break;
952     case PROP_SHOW_POINTER:
953       g_value_set_boolean (value, src->show_pointer);
954       break;
955     case PROP_USE_DAMAGE:
956       g_value_set_boolean (value, src->use_damage);
957       break;
958     case PROP_STARTX:
959       g_value_set_uint (value, src->startx);
960       break;
961     case PROP_STARTY:
962       g_value_set_uint (value, src->starty);
963       break;
964     case PROP_ENDX:
965       g_value_set_uint (value, src->endx);
966       break;
967     case PROP_ENDY:
968       g_value_set_uint (value, src->endy);
969       break;
970     case PROP_REMOTE:
971       g_value_set_boolean (value, src->remote);
972       break;
973     case PROP_XID:
974       g_value_set_uint64 (value, src->xid);
975       break;
976     case PROP_XNAME:
977       g_value_set_string (value, src->xname);
978       break;
979     default:
980       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
981       break;
982   }
983 }
984
985 static void
986 gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc)
987 {
988   g_mutex_lock (&ximagesrc->pool_lock);
989   while (ximagesrc->buffer_pool != NULL) {
990     GstBuffer *ximage = ximagesrc->buffer_pool->data;
991
992     gst_ximage_buffer_free (ximage);
993
994     ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool,
995         ximagesrc->buffer_pool);
996   }
997   g_mutex_unlock (&ximagesrc->pool_lock);
998 }
999
1000 static void
1001 gst_ximage_src_dispose (GObject * object)
1002 {
1003   /* Drop references in the buffer_pool */
1004   gst_ximage_src_clear_bufpool (GST_XIMAGE_SRC (object));
1005
1006   G_OBJECT_CLASS (parent_class)->dispose (object);
1007 }
1008
1009 static void
1010 gst_ximage_src_finalize (GObject * object)
1011 {
1012   GstXImageSrc *src = GST_XIMAGE_SRC (object);
1013
1014   if (src->xcontext)
1015     ximageutil_xcontext_clear (src->xcontext);
1016
1017   g_free (src->xname);
1018   g_mutex_clear (&src->pool_lock);
1019   g_mutex_clear (&src->x_lock);
1020
1021   G_OBJECT_CLASS (parent_class)->finalize (object);
1022 }
1023
1024 static GstCaps *
1025 gst_ximage_src_get_caps (GstBaseSrc * bs, GstCaps * filter)
1026 {
1027   GstXImageSrc *s = GST_XIMAGE_SRC (bs);
1028   GstXContext *xcontext;
1029   gint width, height;
1030   GstVideoFormat format;
1031   guint32 alpha_mask;
1032
1033   if ((!s->xcontext) && (!gst_ximage_src_open_display (s, s->display_name)))
1034     return gst_pad_get_pad_template_caps (GST_BASE_SRC (s)->srcpad);
1035
1036   if (!gst_ximage_src_recalc (s))
1037     return gst_pad_get_pad_template_caps (GST_BASE_SRC (s)->srcpad);
1038
1039   xcontext = s->xcontext;
1040   width = s->xcontext->width;
1041   height = s->xcontext->height;
1042   if (s->xwindow != 0) {
1043     XWindowAttributes attrs;
1044     int status = XGetWindowAttributes (s->xcontext->disp, s->xwindow, &attrs);
1045     if (status) {
1046       width = attrs.width;
1047       height = attrs.height;
1048     }
1049   }
1050
1051   /* property comments say 0 means right/bottom, means we can't capture
1052      the top left pixel alone */
1053   if (s->endx == 0)
1054     s->endx = width - 1;
1055   if (s->endy == 0)
1056     s->endy = height - 1;
1057
1058   if (s->endx >= s->startx && s->endy >= s->starty) {
1059     /* this means user has put in values */
1060     if (s->startx < xcontext->width && s->endx < xcontext->width &&
1061         s->starty < xcontext->height && s->endy < xcontext->height) {
1062       /* values are fine */
1063       s->width = width = s->endx - s->startx + 1;
1064       s->height = height = s->endy - s->starty + 1;
1065     } else {
1066       GST_WARNING
1067           ("User put in co-ordinates overshooting the X resolution, setting to full screen");
1068       s->startx = 0;
1069       s->starty = 0;
1070       s->endx = width - 1;
1071       s->endy = height - 1;
1072     }
1073   } else {
1074     GST_WARNING ("User put in bogus co-ordinates, setting to full screen");
1075     s->startx = 0;
1076     s->starty = 0;
1077     s->endx = width - 1;
1078     s->endy = height - 1;
1079   }
1080   GST_DEBUG ("width = %d, height=%d", width, height);
1081
1082   /* extrapolate alpha mask */
1083   if (xcontext->depth == 32) {
1084     alpha_mask = ~(xcontext->r_mask_output
1085         | xcontext->g_mask_output | xcontext->b_mask_output);
1086   } else {
1087     alpha_mask = 0;
1088   }
1089
1090   format =
1091       gst_video_format_from_masks (xcontext->depth, xcontext->bpp,
1092       xcontext->endianness, xcontext->r_mask_output, xcontext->g_mask_output,
1093       xcontext->b_mask_output, alpha_mask);
1094
1095   return gst_caps_new_simple ("video/x-raw",
1096       "format", G_TYPE_STRING, gst_video_format_to_string (format),
1097       "width", G_TYPE_INT, width,
1098       "height", G_TYPE_INT, height,
1099       "framerate", GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1,
1100       "pixel-aspect-ratio", GST_TYPE_FRACTION, xcontext->par_n, xcontext->par_d,
1101       NULL);
1102 }
1103
1104 static gboolean
1105 gst_ximage_src_set_caps (GstBaseSrc * bs, GstCaps * caps)
1106 {
1107   GstXImageSrc *s = GST_XIMAGE_SRC (bs);
1108   GstStructure *structure;
1109   const GValue *new_fps;
1110
1111   /* If not yet opened, disallow setcaps until later */
1112   if (!s->xcontext)
1113     return FALSE;
1114
1115   /* The only thing that can change is the framerate downstream wants */
1116   structure = gst_caps_get_structure (caps, 0);
1117   new_fps = gst_structure_get_value (structure, "framerate");
1118   if (!new_fps)
1119     return FALSE;
1120
1121   /* Store this FPS for use when generating buffers */
1122   s->fps_n = gst_value_get_fraction_numerator (new_fps);
1123   s->fps_d = gst_value_get_fraction_denominator (new_fps);
1124
1125   GST_DEBUG_OBJECT (s, "peer wants %d/%d fps", s->fps_n, s->fps_d);
1126
1127   return TRUE;
1128 }
1129
1130 static GstCaps *
1131 gst_ximage_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
1132 {
1133   gint i;
1134   GstStructure *structure;
1135
1136   caps = gst_caps_make_writable (caps);
1137
1138   for (i = 0; i < gst_caps_get_size (caps); ++i) {
1139     structure = gst_caps_get_structure (caps, i);
1140
1141     gst_structure_fixate_field_nearest_fraction (structure, "framerate", 25, 1);
1142   }
1143   caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
1144
1145   return caps;
1146 }
1147
1148 static void
1149 gst_ximage_src_class_init (GstXImageSrcClass * klass)
1150 {
1151   GObjectClass *gc = G_OBJECT_CLASS (klass);
1152   GstElementClass *ec = GST_ELEMENT_CLASS (klass);
1153   GstBaseSrcClass *bc = GST_BASE_SRC_CLASS (klass);
1154   GstPushSrcClass *push_class = GST_PUSH_SRC_CLASS (klass);
1155
1156   gc->set_property = gst_ximage_src_set_property;
1157   gc->get_property = gst_ximage_src_get_property;
1158   gc->dispose = gst_ximage_src_dispose;
1159   gc->finalize = gst_ximage_src_finalize;
1160
1161   g_object_class_install_property (gc, PROP_DISPLAY_NAME,
1162       g_param_spec_string ("display-name", "Display", "X Display Name", NULL,
1163           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1164   g_object_class_install_property (gc, PROP_SCREEN_NUM,
1165       g_param_spec_uint ("screen-num", "Screen number", "X Screen Number",
1166           0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1167   g_object_class_install_property (gc, PROP_SHOW_POINTER,
1168       g_param_spec_boolean ("show-pointer", "Show Mouse Pointer",
1169           "Show mouse pointer (if XFixes extension enabled)", TRUE,
1170           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1171   /**
1172    * GstXImageSrc:use-damage:
1173    *
1174    * Use XDamage (if the XDamage extension is enabled)
1175    */
1176   g_object_class_install_property (gc, PROP_USE_DAMAGE,
1177       g_param_spec_boolean ("use-damage", "Use XDamage",
1178           "Use XDamage (if XDamage extension enabled)", TRUE,
1179           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1180   /**
1181    * GstXImageSrc:startx:
1182    *
1183    * X coordinate of top left corner of area to be recorded
1184    * (0 for top left of screen)
1185    */
1186   g_object_class_install_property (gc, PROP_STARTX,
1187       g_param_spec_uint ("startx", "Start X co-ordinate",
1188           "X coordinate of top left corner of area to be recorded (0 for top left of screen)",
1189           0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1190   /**
1191    * GstXImageSrc:starty:
1192    *
1193    * Y coordinate of top left corner of area to be recorded
1194    * (0 for top left of screen)
1195    */
1196   g_object_class_install_property (gc, PROP_STARTY,
1197       g_param_spec_uint ("starty", "Start Y co-ordinate",
1198           "Y coordinate of top left corner of area to be recorded (0 for top left of screen)",
1199           0, G_MAXINT, 0, G_PARAM_READWRITE));
1200   /**
1201    * GstXImageSrc:endx:
1202    *
1203    * X coordinate of bottom right corner of area to be recorded
1204    * (0 for bottom right of screen)
1205    */
1206   g_object_class_install_property (gc, PROP_ENDX,
1207       g_param_spec_uint ("endx", "End X",
1208           "X coordinate of bottom right corner of area to be recorded (0 for bottom right of screen)",
1209           0, G_MAXINT, 0, G_PARAM_READWRITE));
1210   /**
1211    * GstXImageSrc:endy:
1212    *
1213    * Y coordinate of bottom right corner of area to be recorded
1214    * (0 for bottom right of screen)
1215    */
1216   g_object_class_install_property (gc, PROP_ENDY,
1217       g_param_spec_uint ("endy", "End Y",
1218           "Y coordinate of bottom right corner of area to be recorded (0 for bottom right of screen)",
1219           0, G_MAXINT, 0, G_PARAM_READWRITE));
1220
1221   /**
1222    * GstXImageSrc:remote:
1223    *
1224    * Whether the X display is remote. The element will try to use alternate calls
1225    * known to work better with remote displays.
1226    */
1227   g_object_class_install_property (gc, PROP_REMOTE,
1228       g_param_spec_boolean ("remote", "Remote dispay",
1229           "Whether the display is remote", FALSE,
1230           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1231   /**
1232    * GstXImageSrc:xid:
1233    *
1234    * The XID of the window to capture. 0 for the root window (default).
1235    */
1236   g_object_class_install_property (gc, PROP_XID,
1237       g_param_spec_uint64 ("xid", "Window XID",
1238           "Window XID to capture from", 0, G_MAXUINT64, 0,
1239           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1240   /**
1241    * GstXImageSrc:xname:
1242    *
1243    * The name of the window to capture, if any.
1244    */
1245   g_object_class_install_property (gc, PROP_XNAME,
1246       g_param_spec_string ("xname", "Window name",
1247           "Window name to capture from", NULL,
1248           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1249
1250   gst_element_class_set_static_metadata (ec, "Ximage video source",
1251       "Source/Video",
1252       "Creates a screenshot video stream",
1253       "Lutz Mueller <lutz@users.sourceforge.net>, "
1254       "Jan Schmidt <thaytan@mad.scientist.com>, "
1255       "Zaheer Merali <zaheerabbas at merali dot org>");
1256   gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&t));
1257
1258   bc->fixate = gst_ximage_src_fixate;
1259   bc->get_caps = gst_ximage_src_get_caps;
1260   bc->set_caps = gst_ximage_src_set_caps;
1261   bc->start = gst_ximage_src_start;
1262   bc->stop = gst_ximage_src_stop;
1263   bc->unlock = gst_ximage_src_unlock;
1264   push_class->create = gst_ximage_src_create;
1265 }
1266
1267 static void
1268 gst_ximage_src_init (GstXImageSrc * ximagesrc)
1269 {
1270   gst_base_src_set_format (GST_BASE_SRC (ximagesrc), GST_FORMAT_TIME);
1271   gst_base_src_set_live (GST_BASE_SRC (ximagesrc), TRUE);
1272
1273   g_mutex_init (&ximagesrc->pool_lock);
1274   g_mutex_init (&ximagesrc->x_lock);
1275   ximagesrc->show_pointer = TRUE;
1276   ximagesrc->use_damage = TRUE;
1277   ximagesrc->startx = 0;
1278   ximagesrc->starty = 0;
1279   ximagesrc->endx = 0;
1280   ximagesrc->endy = 0;
1281   ximagesrc->remote = FALSE;
1282 }
1283
1284 static gboolean
1285 plugin_init (GstPlugin * plugin)
1286 {
1287   gboolean ret;
1288
1289   GST_DEBUG_CATEGORY_INIT (gst_debug_ximage_src, "ximagesrc", 0,
1290       "ximagesrc element debug");
1291
1292   ret = gst_element_register (plugin, "ximagesrc", GST_RANK_NONE,
1293       GST_TYPE_XIMAGE_SRC);
1294
1295   return ret;
1296 }
1297
1298 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1299     GST_VERSION_MINOR,
1300     ximagesrc,
1301     "X11 video input plugin using standard Xlib calls",
1302     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);