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