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