gstximagesrc.c 36.1 KB
Newer Older
1
/* GStreamer
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
2 3
 *
 * Copyright (C) 2006 Zaheer Merali <zaheerabbas at merali dot org>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

21 22 23 24 25 26
/**
 * SECTION:element-ximagesrc
 *
 * This element captures your X Display and creates raw RGB video.  It uses
 * the XDamage extension if available to only capture areas of the screen that
 * have changed since the last frame.  It uses the XFixes extension if
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
27 28
 * available to also capture your mouse pointer.  By default it will fixate to
 * 25 frames per second.
29 30
 *
 * <refsect2>
31
 * <title>Example pipelines</title>
32 33 34
 * |[
 * gst-launch ximagesrc ! video/x-raw-rgb,framerate=5/1 ! ffmpegcolorspace ! theoraenc ! oggmux ! filesink location=desktop.ogg
 * ]| Encodes your X display to an Ogg theora video at 5 frames per second.
35 36 37
 * </refsect2>
 */

38 39 40
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
41
#include "gstximagesrc.h"
42 43 44 45 46 47 48

#include <string.h>
#include <stdlib.h>

#include <X11/Xlib.h>
#include <X11/Xutil.h>

49 50 51
#include <gst/gst.h>
#include <gst/gst-i18n-plugin.h>

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
52 53
GST_DEBUG_CATEGORY_STATIC (gst_debug_ximage_src);
#define GST_CAT_DEFAULT gst_debug_ximage_src
54 55 56 57 58

static GstStaticPadTemplate t =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
    GST_STATIC_CAPS ("video/x-raw-rgb, "
        "framerate = (fraction) [ 0, MAX ], "
59 60
        "width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], "
        "pixel-aspect-ratio = (fraction) [ 0, MAX ]"));
61 62 63 64 65

enum
{
  PROP_0,
  PROP_DISPLAY_NAME,
66
  PROP_SCREEN_NUM,
67
  PROP_SHOW_POINTER,
68 69 70 71 72
  PROP_USE_DAMAGE,
  PROP_STARTX,
  PROP_STARTY,
  PROP_ENDX,
  PROP_ENDY
73 74
};

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
75
GST_BOILERPLATE (GstXImageSrc, gst_ximage_src, GstPushSrc, GST_TYPE_PUSH_SRC);
76

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
77 78
static void gst_ximage_src_fixate (GstPad * pad, GstCaps * caps);
static void gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc);
79 80 81

/* Called when a buffer is returned from the pipeline */
static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
82 83
gst_ximage_src_return_buf (GstXImageSrc * ximagesrc,
    GstXImageSrcBuffer * ximage)
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
{
  /* If our geometry changed we can't reuse that image. */
  if ((ximage->width != ximagesrc->width) ||
      (ximage->height != ximagesrc->height)) {
    GST_DEBUG_OBJECT (ximagesrc,
        "destroy image %p as its size changed %dx%d vs current %dx%d",
        ximage, ximage->width, ximage->height,
        ximagesrc->width, ximagesrc->height);
    g_mutex_lock (ximagesrc->x_lock);
    gst_ximageutil_ximage_destroy (ximagesrc->xcontext, ximage);
    g_mutex_unlock (ximagesrc->x_lock);
  } else {
    /* In that case we can reuse the image and add it to our image pool. */
    GST_LOG_OBJECT (ximagesrc, "recycling image %p in pool", ximage);
    /* need to increment the refcount again to recycle */
    gst_buffer_ref (GST_BUFFER (ximage));
    g_mutex_lock (ximagesrc->pool_lock);
    ximagesrc->buffer_pool = g_slist_prepend (ximagesrc->buffer_pool, ximage);
    g_mutex_unlock (ximagesrc->pool_lock);
  }
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
107
gst_ximage_src_open_display (GstXImageSrc * s, const gchar * name)
108
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
109
  g_return_val_if_fail (GST_IS_XIMAGE_SRC (s), FALSE);
110 111 112 113 114 115

  if (s->xcontext != NULL)
    return TRUE;

  g_mutex_lock (s->x_lock);
  s->xcontext = ximageutil_xcontext_get (GST_ELEMENT (s), name);
116 117 118 119 120 121 122
  if (s->xcontext == NULL) {
    g_mutex_unlock (s->x_lock);
    GST_ELEMENT_ERROR (s, RESOURCE, OPEN_READ,
        ("Could not open X display for reading"),
        ("NULL returned from getting xcontext"));
    return FALSE;
  }
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
  s->width = s->xcontext->width;
  s->height = s->xcontext->height;

  /* Always capture root window, for now */
  s->xwindow = s->xcontext->root;

#ifdef HAVE_XFIXES
  /* check if xfixes supported */
  {
    int error_base;

    if (XFixesQueryExtension (s->xcontext->disp, &s->fixes_event_base,
            &error_base)) {
      s->have_xfixes = TRUE;
      GST_DEBUG_OBJECT (s, "X Server supports XFixes");
    } else {
139

140 141 142 143 144 145 146 147 148 149
      GST_DEBUG_OBJECT (s, "X Server does not support XFixes");
    }
  }

#ifdef HAVE_XDAMAGE
  /* check if xdamage is supported */
  {
    int error_base;
    long evmask = NoEventMask;

150 151 152 153 154
    s->have_xdamage = FALSE;
    s->damage = None;
    s->damage_copy_gc = None;
    s->damage_region = None;

155 156 157
    if (XDamageQueryExtension (s->xcontext->disp, &s->damage_event_base,
            &error_base)) {
      s->damage =
158
          XDamageCreate (s->xcontext->disp, s->xwindow, XDamageReportNonEmpty);
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
      if (s->damage != None) {
        s->damage_region = XFixesCreateRegion (s->xcontext->disp, NULL, 0);
        if (s->damage_region != None) {
          XGCValues values;

          GST_DEBUG_OBJECT (s, "Using XDamage extension");
          values.subwindow_mode = IncludeInferiors;
          s->damage_copy_gc = XCreateGC (s->xcontext->disp,
              s->xwindow, GCSubwindowMode, &values);
          XSelectInput (s->xcontext->disp, s->xwindow, evmask);

          s->have_xdamage = TRUE;
        } else {
          XDamageDestroy (s->xcontext->disp, s->damage);
          s->damage = None;
        }
      } else
        GST_DEBUG_OBJECT (s, "Could not attach to XDamage");
    } else {
      GST_DEBUG_OBJECT (s, "X Server does not have XDamage extension");
    }
  }
#endif
#endif

  g_mutex_unlock (s->x_lock);

  if (s->xcontext == NULL)
    return FALSE;

  return TRUE;
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
193
gst_ximage_src_start (GstBaseSrc * basesrc)
194
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
195
  GstXImageSrc *s = GST_XIMAGE_SRC (basesrc);
196 197

  s->last_frame_no = -1;
198
#ifdef HAVE_XDAMAGE
199 200
  if (s->last_ximage)
    gst_buffer_unref (GST_BUFFER_CAST (s->last_ximage));
201 202
  s->last_ximage = NULL;
#endif
203
  return gst_ximage_src_open_display (s, s->display_name);
204 205 206
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
207
gst_ximage_src_stop (GstBaseSrc * basesrc)
208
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
209
  GstXImageSrc *src = GST_XIMAGE_SRC (basesrc);
210

211
#ifdef HAVE_XDAMAGE
212 213 214
  if (src->last_ximage)
    gst_buffer_unref (GST_BUFFER_CAST (src->last_ximage));
  src->last_ximage = NULL;
215
#endif
216

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
217
  gst_ximage_src_clear_bufpool (src);
218

219
#ifdef HAVE_XFIXES
220 221 222
  if (src->cursor_image)
    XFree (src->cursor_image);
  src->cursor_image = NULL;
223
#endif
224

225 226
  if (src->xcontext) {
    g_mutex_lock (src->x_lock);
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

#ifdef HAVE_XDAMAGE
    if (src->damage_copy_gc != None) {
      XFreeGC (src->xcontext->disp, src->damage_copy_gc);
      src->damage_copy_gc = None;
    }
    if (src->damage_region != None) {
      XFixesDestroyRegion (src->xcontext->disp, src->damage_region);
      src->damage_region = None;
    }
    if (src->damage != None) {
      XDamageDestroy (src->xcontext->disp, src->damage);
      src->damage = None;
    }
#endif

243 244 245 246 247 248 249 250 251
    ximageutil_xcontext_clear (src->xcontext);
    src->xcontext = NULL;
    g_mutex_unlock (src->x_lock);
  }

  return TRUE;
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
252
gst_ximage_src_unlock (GstBaseSrc * basesrc)
253
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
254
  GstXImageSrc *src = GST_XIMAGE_SRC (basesrc);
255 256 257 258 259 260 261 262 263 264 265 266 267

  /* Awaken the create() func if it's waiting on the clock */
  GST_OBJECT_LOCK (src);
  if (src->clock_id) {
    GST_DEBUG_OBJECT (src, "Waking up waiting clock");
    gst_clock_id_unschedule (src->clock_id);
  }
  GST_OBJECT_UNLOCK (src);

  return TRUE;
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
268
gst_ximage_src_recalc (GstXImageSrc * src)
269 270 271 272
{
  if (!src->xcontext)
    return FALSE;

273
  /* Maybe later we can check the display hasn't changed size */
274 275 276 277 278 279 280 281 282 283 284 285
  /* We could use XQueryPointer to get only the current window. */
  return TRUE;
}

#ifdef HAVE_XFIXES
static void
composite_pixel (GstXContext * xcontext, guchar * dest, guchar * src)
{
  guint8 r = src[2];
  guint8 g = src[1];
  guint8 b = src[0];
  guint8 a = src[3];
286
  guint8 dr, dg, db;
287
  guint32 color;
288 289 290
  gint r_shift, r_max, r_shift_out;
  gint g_shift, g_max, g_shift_out;
  gint b_shift, b_max, b_shift_out;
291 292 293 294 295 296

  switch (xcontext->bpp) {
    case 8:
      color = *dest;
      break;
    case 16:
297
      color = GUINT16_FROM_LE (*(guint16 *) (dest));
298 299 300 301 302
      break;
    case 32:
      color = GUINT32_FROM_LE (*(guint32 *) (dest));
      break;
    default:
303
      /* Should not reach here */
304
      g_return_if_reached ();
305 306
  }

307 308
  /* possible optimisation:
   * move the code that finds shift and max in the _link function */
309 310 311 312 313
  for (r_shift = 0; !(xcontext->visual->red_mask & (1 << r_shift)); r_shift++);
  for (g_shift = 0; !(xcontext->visual->green_mask & (1 << g_shift));
      g_shift++);
  for (b_shift = 0; !(xcontext->visual->blue_mask & (1 << b_shift)); b_shift++);

314 315 316 317 318 319 320 321
  for (r_shift_out = 0; !(xcontext->visual->red_mask & (1 << r_shift_out));
      r_shift_out++);
  for (g_shift_out = 0; !(xcontext->visual->green_mask & (1 << g_shift_out));
      g_shift_out++);
  for (b_shift_out = 0; !(xcontext->visual->blue_mask & (1 << b_shift_out));
      b_shift_out++);


322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
  r_max = (xcontext->visual->red_mask >> r_shift);
  b_max = (xcontext->visual->blue_mask >> b_shift);
  g_max = (xcontext->visual->green_mask >> g_shift);

#define RGBXXX_R(x)  (((x)>>r_shift) & (r_max))
#define RGBXXX_G(x)  (((x)>>g_shift) & (g_max))
#define RGBXXX_B(x)  (((x)>>b_shift) & (b_max))

  dr = (RGBXXX_R (color) * 255) / r_max;
  dg = (RGBXXX_G (color) * 255) / g_max;
  db = (RGBXXX_B (color) * 255) / b_max;

  dr = (r * a + (0xff - a) * dr) / 0xff;
  dg = (g * a + (0xff - a) * dg) / 0xff;
  db = (b * a + (0xff - a) * db) / 0xff;

338 339 340
  color = (((dr * r_max) / 255) << r_shift_out) +
      (((dg * g_max) / 255) << g_shift_out) +
      (((db * b_max) / 255) << b_shift_out);
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360

  switch (xcontext->bpp) {
    case 8:
      *dest = color;
      break;
    case 16:
      *(guint16 *) (dest) = color;
      break;
    case 32:
      *(guint32 *) (dest) = color;
      break;
    default:
      g_warning ("bpp %d not supported\n", xcontext->bpp);
  }
}
#endif

/* Retrieve an XImageSrcBuffer, preferably from our
 * pool of existing images and populate it from the window */
static GstXImageSrcBuffer *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
361
gst_ximage_src_ximage_get (GstXImageSrc * ximagesrc)
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
{
  GstXImageSrcBuffer *ximage = NULL;

  g_mutex_lock (ximagesrc->pool_lock);
  while (ximagesrc->buffer_pool != NULL) {
    ximage = ximagesrc->buffer_pool->data;

    if ((ximage->width != ximagesrc->width) ||
        (ximage->height != ximagesrc->height)) {
      gst_ximage_buffer_free (ximage);
    }

    ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool,
        ximagesrc->buffer_pool);
  }
  g_mutex_unlock (ximagesrc->pool_lock);

  if (ximage == NULL) {
    GstXContext *xcontext;
381
    GstCaps *caps = NULL;
382 383 384 385 386 387 388

    GST_DEBUG_OBJECT (ximagesrc, "creating image (%dx%d)",
        ximagesrc->width, ximagesrc->height);

    g_mutex_lock (ximagesrc->x_lock);
    ximage = gst_ximageutil_ximage_new (ximagesrc->xcontext,
        GST_ELEMENT (ximagesrc), ximagesrc->width, ximagesrc->height,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
389
        (BufferReturnFunc) (gst_ximage_src_return_buf));
390 391
    if (ximage == NULL) {
      GST_ELEMENT_ERROR (ximagesrc, RESOURCE, WRITE, (NULL),
392 393
          ("could not create a %dx%d ximage", ximagesrc->width,
              ximagesrc->height));
394 395 396 397 398 399
      g_mutex_unlock (ximagesrc->x_lock);
      return NULL;
    }

    xcontext = ximagesrc->xcontext;

400

401 402 403 404 405 406 407
    caps = gst_caps_new_simple ("video/x-raw-rgb",
        "bpp", G_TYPE_INT, xcontext->bpp,
        "depth", G_TYPE_INT, xcontext->depth,
        "endianness", G_TYPE_INT, xcontext->endianness,
        "red_mask", G_TYPE_INT, xcontext->r_mask_output,
        "green_mask", G_TYPE_INT, xcontext->g_mask_output,
        "blue_mask", G_TYPE_INT, xcontext->b_mask_output,
408 409
        "width", G_TYPE_INT, ximagesrc->width,
        "height", G_TYPE_INT, ximagesrc->height,
410
        "framerate", GST_TYPE_FRACTION, ximagesrc->fps_n, ximagesrc->fps_d,
411 412 413
        "pixel-aspect-ratio", GST_TYPE_FRACTION,
        gst_value_get_fraction_numerator (xcontext->par),
        gst_value_get_fraction_denominator (xcontext->par), NULL);
414 415

    gst_buffer_set_caps (GST_BUFFER (ximage), caps);
416
    g_mutex_unlock (ximagesrc->x_lock);
417 418

    gst_caps_unref (caps);
419 420
  }

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
421
  g_return_val_if_fail (GST_IS_XIMAGE_SRC (ximagesrc), NULL);
422
#ifdef HAVE_XDAMAGE
423 424
  if (ximagesrc->have_xdamage && ximagesrc->use_damage &&
      ximagesrc->last_ximage != NULL) {
425 426
    XEvent ev;

427 428 429 430
    /* have_frame is TRUE when either the entire screen has been
     * grabbed or when the last image has been copied */
    gboolean have_frame = FALSE;

431 432 433 434 435
    GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XDamage");

    do {
      XNextEvent (ximagesrc->xcontext->disp, &ev);

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
      if (ev.type == ximagesrc->damage_event_base + XDamageNotify) {
        XserverRegion parts;
        XRectangle *rects;
        int nrects;

        parts = XFixesCreateRegion (ximagesrc->xcontext->disp, 0, 0);
        XDamageSubtract (ximagesrc->xcontext->disp, ximagesrc->damage, None,
            parts);
        /* Now copy out all of the damaged rectangles. */
        rects = XFixesFetchRegion (ximagesrc->xcontext->disp, parts, &nrects);
        if (rects != NULL) {
          int i;

          if (!have_frame) {
            GST_LOG_OBJECT (ximagesrc,
                "Copying from last frame ximage->size: %d",
                GST_BUFFER_SIZE (GST_BUFFER (ximage)));
            memcpy (GST_BUFFER_DATA (GST_BUFFER (ximage)),
                GST_BUFFER_DATA (GST_BUFFER (ximagesrc->last_ximage)),
                GST_BUFFER_SIZE (GST_BUFFER (ximage)));
            have_frame = TRUE;
457
          }
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
          for (i = 0; i < nrects; i++) {
            GST_LOG_OBJECT (ximagesrc,
                "Damaged sub-region @ %d,%d size %dx%d reported",
                rects[i].x, rects[i].y, rects[i].width, rects[i].height);

            /* if we only want a small area, clip this damage region to
             * area we want */
            if (ximagesrc->endx > ximagesrc->startx &&
                ximagesrc->endy > ximagesrc->starty) {
              /* see if damage area intersects */
              if (rects[i].x + rects[i].width < ximagesrc->startx ||
                  rects[i].x > ximagesrc->endx) {
                /* trivial reject */
              } else if (rects[i].y + rects[i].height < ximagesrc->starty ||
                  rects[i].y > ximagesrc->endy) {
                /* trivial reject */
              } else {
                /* find intersect region */
                int startx, starty, width, height;

                startx = (rects[i].x < ximagesrc->startx) ? ximagesrc->startx :
                    rects[i].x;
                starty = (rects[i].y < ximagesrc->starty) ? ximagesrc->starty :
                    rects[i].y;
                width = (rects[i].x + rects[i].width < ximagesrc->endx) ?
                    rects[i].x + rects[i].width - startx :
                    ximagesrc->endx - startx;
                height = (rects[i].y + rects[i].height < ximagesrc->endy) ?
                    rects[i].y + rects[i].height - starty : ximagesrc->endy -
                    starty;

489
                GST_LOG_OBJECT (ximagesrc,
490 491 492 493 494 495
                    "Retrieving damaged sub-region @ %d,%d size %dx%d as intersect region",
                    startx, starty, width, height);
                XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
                    startx, starty, width, height, AllPlanes, ZPixmap,
                    ximage->ximage, startx - ximagesrc->startx,
                    starty - ximagesrc->starty);
496
              }
497
            } else {
498

499
              GST_LOG_OBJECT (ximagesrc,
500 501 502
                  "Retrieving damaged sub-region @ %d,%d size %dx%d",
                  rects[i].x, rects[i].y, rects[i].width, rects[i].height);

503
              XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
504 505 506
                  rects[i].x, rects[i].y,
                  rects[i].width, rects[i].height,
                  AllPlanes, ZPixmap, ximage->ximage, rects[i].x, rects[i].y);
507
            }
508
          }
509
          free (rects);
510 511 512
        }
      }
    } while (XPending (ximagesrc->xcontext->disp));
513 514 515 516 517 518 519 520
    if (!have_frame) {
      GST_LOG_OBJECT (ximagesrc,
          "Copying from last frame ximage->size: %d",
          GST_BUFFER_SIZE (GST_BUFFER (ximage)));
      memcpy (GST_BUFFER_DATA (GST_BUFFER (ximage)),
          GST_BUFFER_DATA (GST_BUFFER (ximagesrc->last_ximage)),
          GST_BUFFER_SIZE (GST_BUFFER (ximage)));
    }
521
#ifdef HAVE_XFIXES
522 523
    /* re-get area where last mouse pointer was  but only if in our clipping
     * bounds */
524 525 526 527 528 529 530 531 532 533 534 535 536
    if (ximagesrc->cursor_image) {
      gint x, y, width, height;

      x = ximagesrc->cursor_image->x - ximagesrc->cursor_image->xhot;
      y = ximagesrc->cursor_image->y - ximagesrc->cursor_image->yhot;
      width = ximagesrc->cursor_image->width;
      height = ximagesrc->cursor_image->height;

      /* bounds checking */
      if (x < 0)
        x = 0;
      if (y < 0)
        y = 0;
537 538 539 540
      if (x + width > ximagesrc->xcontext->width)
        width = ximagesrc->xcontext->width - x;
      if (y + height > ximagesrc->xcontext->height)
        height = ximagesrc->xcontext->height - y;
541 542
      g_assert (x >= 0);
      g_assert (y >= 0);
543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
      GST_DEBUG_OBJECT (ximagesrc,
          "Cursor was at (%d,%d) width: %d, height: %d and our range is: (%d,%d) - (%d,%d)",
          x, y, width, height, ximagesrc->startx, ximagesrc->starty,
          ximagesrc->endx, ximagesrc->endy);
      /* only get where cursor last was, if it is in our range */
      if (ximagesrc->endx > ximagesrc->startx &&
          ximagesrc->endy > ximagesrc->starty) {
        /* check bounds */
        if (x + width < ximagesrc->startx || x > ximagesrc->endx) {
          /* trivial reject */
        } else if (y + height < ximagesrc->starty || y > ximagesrc->endy) {
          /* trivial reject */
        } else {
          /* find intersect region */
          int startx, starty, iwidth, iheight;

          startx = (x < ximagesrc->startx) ? ximagesrc->startx : x;
          starty = (y < ximagesrc->starty) ? ximagesrc->starty : y;
          iwidth = (x + width < ximagesrc->endx) ?
              x + width - startx : ximagesrc->endx - startx;
          iheight = (y + height < ximagesrc->endy) ?
              y + height - starty : ximagesrc->endy - starty;
          GST_DEBUG_OBJECT (ximagesrc, "Removing cursor from %d,%d", x, y);
          XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
              startx, starty, iwidth, iheight, AllPlanes, ZPixmap,
              ximage->ximage, startx - ximagesrc->startx,
              starty - ximagesrc->starty);
        }
      } else {
572

573 574 575 576
        GST_DEBUG_OBJECT (ximagesrc, "Removing cursor from %d,%d", x, y);
        XGetSubImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
            x, y, width, height, AllPlanes, ZPixmap, ximage->ximage, x, y);
      }
577 578 579 580
    }
#endif


581 582 583 584 585 586 587
  } else {
#endif

#ifdef HAVE_XSHM
    if (ximagesrc->xcontext->use_xshm) {
      GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XShm");
      XShmGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
588
          ximage->ximage, ximagesrc->startx, ximagesrc->starty, AllPlanes);
589 590 591 592 593 594

    } else
#endif /* HAVE_XSHM */
    {
      GST_DEBUG_OBJECT (ximagesrc, "Retrieving screen using XGetImage");
      ximage->ximage = XGetImage (ximagesrc->xcontext->disp, ximagesrc->xwindow,
595 596
          ximagesrc->startx, ximagesrc->starty, ximagesrc->width,
          ximagesrc->height, AllPlanes, ZPixmap);
597 598 599 600 601 602 603 604 605 606
    }
#ifdef HAVE_XDAMAGE
  }
#endif

#ifdef HAVE_XFIXES
  if (ximagesrc->show_pointer && ximagesrc->have_xfixes) {

    GST_DEBUG_OBJECT (ximagesrc, "Using XFixes to draw cursor");
    /* get cursor */
607 608
    if (ximagesrc->cursor_image)
      XFree (ximagesrc->cursor_image);
609 610
    ximagesrc->cursor_image = XFixesGetCursorImage (ximagesrc->xcontext->disp);
    if (ximagesrc->cursor_image != NULL) {
611
      int cx, cy, i, j, count;
612 613
      int startx, starty, iwidth, iheight;
      gboolean cursor_in_image = TRUE;
614

615
      cx = ximagesrc->cursor_image->x - ximagesrc->cursor_image->xhot;
616 617
      if (cx < 0)
        cx = 0;
618
      cy = ximagesrc->cursor_image->y - ximagesrc->cursor_image->yhot;
619 620
      if (cy < 0)
        cy = 0;
621
      count = ximagesrc->cursor_image->width * ximagesrc->cursor_image->height;
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659

      /* only get where cursor last was, if it is in our range */
      if (ximagesrc->endx > ximagesrc->startx &&
          ximagesrc->endy > ximagesrc->starty) {
        /* check bounds */
        if (cx + ximagesrc->cursor_image->width < ximagesrc->startx ||
            cx > ximagesrc->endx) {
          /* trivial reject */
          cursor_in_image = FALSE;
        } else if (cy + ximagesrc->cursor_image->height < ximagesrc->starty ||
            cy > ximagesrc->endy) {
          /* trivial reject */
          cursor_in_image = FALSE;
        } else {
          /* find intersect region */

          startx = (cx < ximagesrc->startx) ? ximagesrc->startx : cx;
          starty = (cy < ximagesrc->starty) ? ximagesrc->starty : cy;
          iwidth = (cx + ximagesrc->cursor_image->width < ximagesrc->endx) ?
              cx + ximagesrc->cursor_image->width - startx :
              ximagesrc->endx - startx;
          iheight = (cy + ximagesrc->cursor_image->height < ximagesrc->endy) ?
              cy + ximagesrc->cursor_image->height - starty :
              ximagesrc->endy - starty;
        }
      } else {
        startx = cx;
        starty = cy;
        iwidth = ximagesrc->cursor_image->width;
        iheight = ximagesrc->cursor_image->height;
      }

      if (cursor_in_image) {
        GST_DEBUG_OBJECT (ximagesrc, "Cursor is in image so trying to draw it");
        for (i = 0; i < count; i++)
          ximagesrc->cursor_image->pixels[i] =
              GUINT_TO_LE (ximagesrc->cursor_image->pixels[i]);
        /* copy those pixels across */
660 661
        for (j = starty;
            j < starty + iheight && j < ximagesrc->starty + ximagesrc->height;
662
            j++) {
663 664
          for (i = startx;
              i < startx + iwidth && i < ximagesrc->startx + ximagesrc->width;
665
              i++) {
666 667 668 669 670 671 672 673 674 675 676 677 678 679
            guint8 *src, *dest;

            src =
                (guint8 *) & (ximagesrc->cursor_image->pixels[((j -
                            cy) * ximagesrc->cursor_image->width + (i - cx))]);
            dest =
                (guint8 *) & (ximage->ximage->data[((j -
                            ximagesrc->starty) * ximagesrc->width + (i -
                            ximagesrc->startx)) * (ximagesrc->xcontext->bpp /
                        8)]);

            composite_pixel (ximagesrc->xcontext, (guint8 *) dest,
                (guint8 *) src);
          }
680 681 682 683 684
        }
      }
    }
  }
#endif
685 686 687 688 689 690 691 692 693 694 695
#ifdef HAVE_XDAMAGE
  if (ximagesrc->have_xdamage && ximagesrc->use_damage) {
    /* need to ref ximage to put in last_ximage */
    gst_buffer_ref (GST_BUFFER (ximage));
    if (ximagesrc->last_ximage) {
      gst_buffer_unref (GST_BUFFER (ximagesrc->last_ximage));
    }
    ximagesrc->last_ximage = ximage;
    GST_LOG_OBJECT (ximagesrc, "reffing current buffer for last_ximage");
  }
#endif
696 697 698 699
  return ximage;
}

static GstFlowReturn
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
700
gst_ximage_src_create (GstPushSrc * bs, GstBuffer ** buf)
701
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
702
  GstXImageSrc *s = GST_XIMAGE_SRC (bs);
703 704 705 706 707 708
  GstXImageSrcBuffer *image;
  GstClockTime base_time;
  GstClockTime next_capture_ts;
  GstClockTime dur;
  gint64 next_frame_no;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
709
  if (!gst_ximage_src_recalc (s)) {
710
    GST_ELEMENT_ERROR (s, RESOURCE, FAILED,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
711
        (_("Changing resolution at runtime is not yet supported.")), (NULL));
712 713 714 715 716 717
    return GST_FLOW_ERROR;
  }

  if (s->fps_n <= 0 || s->fps_d <= 0)
    return GST_FLOW_NOT_NEGOTIATED;     /* FPS must be > 0 */

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
718
  /* Now, we might need to wait for the next multiple of the fps
719 720 721
   * before capturing */

  GST_OBJECT_LOCK (s);
722 723 724 725 726 727 728
  if (GST_ELEMENT_CLOCK (s) == NULL) {
    GST_OBJECT_UNLOCK (s);
    GST_ELEMENT_ERROR (s, RESOURCE, FAILED,
        (_("Cannot operate without a clock")), (NULL));
    return GST_FLOW_ERROR;
  }

729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
  base_time = GST_ELEMENT_CAST (s)->base_time;
  next_capture_ts = gst_clock_get_time (GST_ELEMENT_CLOCK (s));
  next_capture_ts -= base_time;

  /* Figure out which 'frame number' position we're at, based on the cur time
   * and frame rate */
  next_frame_no = gst_util_uint64_scale (next_capture_ts,
      s->fps_n, GST_SECOND * s->fps_d);
  if (next_frame_no == s->last_frame_no) {
    GstClockID id;
    GstClockReturn ret;

    /* Need to wait for the next frame */
    next_frame_no += 1;

    /* Figure out what the next frame time is */
    next_capture_ts = gst_util_uint64_scale (next_frame_no,
        s->fps_d * GST_SECOND, s->fps_n);

    id = gst_clock_new_single_shot_id (GST_ELEMENT_CLOCK (s),
        next_capture_ts + base_time);
    s->clock_id = id;

    /* release the object lock while waiting */
    GST_OBJECT_UNLOCK (s);

    GST_DEBUG_OBJECT (s, "Waiting for next frame time %" G_GUINT64_FORMAT,
        next_capture_ts);
    ret = gst_clock_id_wait (id, NULL);
    GST_OBJECT_LOCK (s);

    gst_clock_id_unref (id);
    s->clock_id = NULL;
    if (ret == GST_CLOCK_UNSCHEDULED) {
      /* Got woken up by the unlock function */
      GST_OBJECT_UNLOCK (s);
      return GST_FLOW_WRONG_STATE;
    }
    /* Duration is a complete 1/fps frame duration */
    dur = gst_util_uint64_scale_int (GST_SECOND, s->fps_d, s->fps_n);
  } else {
    GstClockTime next_frame_ts;

    GST_DEBUG_OBJECT (s, "No need to wait for next frame time %"
        G_GUINT64_FORMAT " next frame = %" G_GINT64_FORMAT " prev = %"
        G_GINT64_FORMAT, next_capture_ts, next_frame_no, s->last_frame_no);
    next_frame_ts = gst_util_uint64_scale (next_frame_no + 1,
        s->fps_d * GST_SECOND, s->fps_n);
    /* Frame duration is from now until the next expected capture time */
    dur = next_frame_ts - next_capture_ts;
  }
  s->last_frame_no = next_frame_no;
  GST_OBJECT_UNLOCK (s);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
783
  image = gst_ximage_src_ximage_get (s);
784 785 786 787 788 789 790 791 792 793 794
  if (!image)
    return GST_FLOW_ERROR;

  *buf = GST_BUFFER (image);
  GST_BUFFER_TIMESTAMP (*buf) = next_capture_ts;
  GST_BUFFER_DURATION (*buf) = dur;

  return GST_FLOW_OK;
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
795
gst_ximage_src_set_property (GObject * object, guint prop_id,
796 797
    const GValue * value, GParamSpec * pspec)
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
798
  GstXImageSrc *src = GST_XIMAGE_SRC (object);
799 800 801 802 803 804 805 806 807 808

  switch (prop_id) {
    case PROP_DISPLAY_NAME:

      g_free (src->display_name);
      src->display_name = g_strdup (g_value_get_string (value));
      break;
    case PROP_SCREEN_NUM:
      src->screen_num = g_value_get_uint (value);
      break;
809 810 811
    case PROP_SHOW_POINTER:
      src->show_pointer = g_value_get_boolean (value);
      break;
812 813 814
    case PROP_USE_DAMAGE:
      src->use_damage = g_value_get_boolean (value);
      break;
815 816 817 818 819 820 821 822 823 824 825 826
    case PROP_STARTX:
      src->startx = g_value_get_uint (value);
      break;
    case PROP_STARTY:
      src->starty = g_value_get_uint (value);
      break;
    case PROP_ENDX:
      src->endx = g_value_get_uint (value);
      break;
    case PROP_ENDY:
      src->endy = g_value_get_uint (value);
      break;
827 828 829 830 831 832
    default:
      break;
  }
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
833
gst_ximage_src_get_property (GObject * object, guint prop_id, GValue * value,
834 835
    GParamSpec * pspec)
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
836
  GstXImageSrc *src = GST_XIMAGE_SRC (object);
837 838 839 840 841 842 843 844 845 846 847 848

  switch (prop_id) {
    case PROP_DISPLAY_NAME:
      if (src->xcontext)
        g_value_set_string (value, DisplayString (src->xcontext->disp));
      else
        g_value_set_string (value, src->display_name);

      break;
    case PROP_SCREEN_NUM:
      g_value_set_uint (value, src->screen_num);
      break;
849 850 851
    case PROP_SHOW_POINTER:
      g_value_set_boolean (value, src->show_pointer);
      break;
852 853
    case PROP_USE_DAMAGE:
      g_value_set_boolean (value, src->use_damage);
854 855 856 857 858 859 860 861 862 863 864 865 866
      break;
    case PROP_STARTX:
      g_value_set_uint (value, src->startx);
      break;
    case PROP_STARTY:
      g_value_set_uint (value, src->starty);
      break;
    case PROP_ENDX:
      g_value_set_uint (value, src->endx);
      break;
    case PROP_ENDY:
      g_value_set_uint (value, src->endy);
      break;
867 868 869 870 871 872 873
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
  }
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
874
gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc)
875 876 877 878 879 880 881 882 883 884 885 886 887 888
{
  g_mutex_lock (ximagesrc->pool_lock);
  while (ximagesrc->buffer_pool != NULL) {
    GstXImageSrcBuffer *ximage = ximagesrc->buffer_pool->data;

    gst_ximage_buffer_free (ximage);

    ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool,
        ximagesrc->buffer_pool);
  }
  g_mutex_unlock (ximagesrc->pool_lock);
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
889
gst_ximage_src_base_init (gpointer g_class)
890 891 892
{
  GstElementClass *ec = GST_ELEMENT_CLASS (g_class);

893 894 895 896 897 898
  gst_element_class_set_details_simple (ec, "Ximage video source",
      "Source/Video",
      "Creates a screenshot video stream",
      "Lutz Mueller <lutz@users.sourceforge.net>, "
      "Jan Schmidt <thaytan@mad.scientist.com>, "
      "Zaheer Merali <zaheerabbas at merali dot org>");
899 900 901 902
  gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&t));
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
903
gst_ximage_src_dispose (GObject * object)
904 905
{
  /* Drop references in the buffer_pool */
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
906
  gst_ximage_src_clear_bufpool (GST_XIMAGE_SRC (object));
907 908

  G_OBJECT_CLASS (parent_class)->dispose (object);
909 910 911
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
912
gst_ximage_src_finalize (GObject * object)
913
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
914
  GstXImageSrc *src = GST_XIMAGE_SRC (object);
915 916 917 918 919 920 921 922 923 924 925

  if (src->xcontext)
    ximageutil_xcontext_clear (src->xcontext);

  g_mutex_free (src->pool_lock);
  g_mutex_free (src->x_lock);

  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static GstCaps *
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
926
gst_ximage_src_get_caps (GstBaseSrc * bs)
927
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
928
  GstXImageSrc *s = GST_XIMAGE_SRC (bs);
929
  GstXContext *xcontext;
930
  gint width, height;
931

932
  if ((!s->xcontext) && (!gst_ximage_src_open_display (s, s->display_name)))
933 934 935
    return
        gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC
            (s)->srcpad));
936

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
937
  if (!gst_ximage_src_recalc (s))
938 939 940
    return
        gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC
            (s)->srcpad));
941 942 943

  xcontext = s->xcontext;

944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
  width = xcontext->width;
  height = xcontext->height;
  if (s->endx > s->startx && s->endy > s->starty) {
    /* this means user has put in values */
    if (s->startx < xcontext->width && s->endx < xcontext->width &&
        s->starty < xcontext->height && s->endy < xcontext->height) {
      /* values are fine */
      s->width = width = s->endx - s->startx;
      s->height = height = s->endy - s->starty;
    } else {
      GST_WARNING
          ("User put in co-ordinates overshooting the X resolution, setting to full screen");
      s->startx = 0;
      s->starty = 0;
      s->endx = 0;
      s->endy = 0;
    }
  } else {
    GST_WARNING ("User put in bogus co-ordinates, setting to full screen");
    s->startx = 0;
    s->starty = 0;
    s->endx = 0;
    s->endy = 0;
  }
  GST_DEBUG ("width = %d, height=%d", width, height);
969 970 971 972
  return gst_caps_new_simple ("video/x-raw-rgb",
      "bpp", G_TYPE_INT, xcontext->bpp,
      "depth", G_TYPE_INT, xcontext->depth,
      "endianness", G_TYPE_INT, xcontext->endianness,
973 974 975
      "red_mask", G_TYPE_INT, xcontext->r_mask_output,
      "green_mask", G_TYPE_INT, xcontext->g_mask_output,
      "blue_mask", G_TYPE_INT, xcontext->b_mask_output,
976 977
      "width", G_TYPE_INT, width,
      "height", G_TYPE_INT, height,
978 979 980
      "framerate", GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1,
      "pixel-aspect-ratio", GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1,
      NULL);
981 982 983
}

static gboolean
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
984
gst_ximage_src_set_caps (GstBaseSrc * bs, GstCaps * caps)
985
{
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
986
  GstXImageSrc *s = GST_XIMAGE_SRC (bs);
987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
  GstStructure *structure;
  const GValue *new_fps;

  /* If not yet opened, disallow setcaps until later */
  if (!s->xcontext)
    return FALSE;

  /* The only thing that can change is the framerate downstream wants */
  structure = gst_caps_get_structure (caps, 0);
  new_fps = gst_structure_get_value (structure, "framerate");
  if (!new_fps)
    return FALSE;

  /* Store this FPS for use when generating buffers */
  s->fps_n = gst_value_get_fraction_numerator (new_fps);
  s->fps_d = gst_value_get_fraction_denominator (new_fps);

  GST_DEBUG_OBJECT (s, "peer wants %d/%d fps", s->fps_n, s->fps_d);

  return TRUE;
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1010
gst_ximage_src_fixate (GstPad * pad, GstCaps * caps)
1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022
{
  gint i;
  GstStructure *structure;

  for (i = 0; i < gst_caps_get_size (caps); ++i) {
    structure = gst_caps_get_structure (caps, i);

    gst_structure_fixate_field_nearest_fraction (structure, "framerate", 25, 1);
  }
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1023
gst_ximage_src_class_init (GstXImageSrcClass * klass)
1024 1025 1026 1027 1028
{
  GObjectClass *gc = G_OBJECT_CLASS (klass);
  GstBaseSrcClass *bc = GST_BASE_SRC_CLASS (klass);
  GstPushSrcClass *push_class = GST_PUSH_SRC_CLASS (klass);

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1029 1030 1031 1032
  gc->set_property = gst_ximage_src_set_property;
  gc->get_property = gst_ximage_src_get_property;
  gc->dispose = gst_ximage_src_dispose;
  gc->finalize = gst_ximage_src_finalize;
1033 1034

  g_object_class_install_property (gc, PROP_DISPLAY_NAME,
1035
      g_param_spec_string ("display-name", "Display", "X Display Name", NULL,
1036 1037
          G_PARAM_READWRITE));
  g_object_class_install_property (gc, PROP_SCREEN_NUM,
1038
      g_param_spec_uint ("screen-num", "Screen number", "X Screen Number",
1039
          0, G_MAXINT, 0, G_PARAM_READWRITE));
1040
  g_object_class_install_property (gc, PROP_SHOW_POINTER,
1041
      g_param_spec_boolean ("show-pointer", "Show Mouse Pointer",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1042
          "Show mouse pointer (if XFixes extension enabled)", TRUE,
1043
          G_PARAM_READWRITE));
1044 1045 1046 1047 1048 1049 1050
  /**
   * GstXImageSrc:use-damage
   *
   * Use XDamage (if the XDamage extension is enabled)
   *
   * Since: 0.10.4
   **/
1051
  g_object_class_install_property (gc, PROP_USE_DAMAGE,
1052
      g_param_spec_boolean ("use-damage", "Use XDamage",
1053 1054
          "Use XDamage (if XDamage extension enabled)", TRUE,
          G_PARAM_READWRITE));
1055 1056 1057 1058 1059 1060 1061 1062
  /**
   * GstXImageSrc:startx
   *
   * X coordinate of top left corner of area to be recorded
   * (0 for top left of screen)
   *
   * Since: 0.10.4
   **/
1063 1064 1065 1066
  g_object_class_install_property (gc, PROP_STARTX,
      g_param_spec_uint ("startx", "Start X co-ordinate",
          "X coordinate of top left corner of area to be recorded (0 for top left of screen)",
          0, G_MAXINT, 0, G_PARAM_READWRITE));
1067 1068 1069 1070 1071 1072 1073 1074
  /**
   * GstXImageSrc:starty
   *
   * Y coordinate of top left corner of area to be recorded
   * (0 for top left of screen)
   *
   * Since: 0.10.4
   **/
1075 1076 1077 1078
  g_object_class_install_property (gc, PROP_STARTY,
      g_param_spec_uint ("starty", "Start Y co-ordinate",
          "Y coordinate of top left corner of area to be recorded (0 for top left of screen)",
          0, G_MAXINT, 0, G_PARAM_READWRITE));
1079 1080 1081 1082 1083 1084 1085 1086
  /**
   * GstXImageSrc:endx
   *
   * X coordinate of bottom right corner of area to be recorded
   * (0 for bottom right of screen)
   *
   * Since: 0.10.4
   **/
1087 1088 1089 1090
  g_object_class_install_property (gc, PROP_ENDX,
      g_param_spec_uint ("endx", "End X",
          "X coordinate of bottom right corner of area to be recorded (0 for bottom right of screen)",
          0, G_MAXINT, 0, G_PARAM_READWRITE));
1091 1092 1093 1094 1095 1096 1097 1098
  /**
   * GstXImageSrc:endy
   *
   * Y coordinate of bottom right corner of area to be recorded
   * (0 for bottom right of screen)
   *
   * Since: 0.10.4
   **/
1099 1100 1101 1102
  g_object_class_install_property (gc, PROP_ENDY,
      g_param_spec_uint ("endy", "End Y",
          "Y coordinate of bottom right corner of area to be recorded (0 for bottom right of screen)",
          0, G_MAXINT, 0, G_PARAM_READWRITE));
1103

1104
  parent_class = g_type_class_peek_parent (klass);
1105

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1106 1107 1108 1109 1110 1111
  push_class->create = gst_ximage_src_create;
  bc->get_caps = gst_ximage_src_get_caps;
  bc->set_caps = gst_ximage_src_set_caps;
  bc->start = gst_ximage_src_start;
  bc->stop = gst_ximage_src_stop;
  bc->unlock = gst_ximage_src_unlock;
1112 1113 1114
}

static void
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1115
gst_ximage_src_init (GstXImageSrc * ximagesrc, GstXImageSrcClass * klass)
1116
{
1117
  gst_base_src_set_format (GST_BASE_SRC (ximagesrc), GST_FORMAT_TIME);
1118 1119
  gst_base_src_set_live (GST_BASE_SRC (ximagesrc), TRUE);
  gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (ximagesrc),
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1120
      gst_ximage_src_fixate);
1121 1122 1123

  ximagesrc->pool_lock = g_mutex_new ();
  ximagesrc->x_lock = g_mutex_new ();
1124
  ximagesrc->show_pointer = TRUE;
1125
  ximagesrc->use_damage = TRUE;
1126 1127 1128 1129
  ximagesrc->startx = 0;
  ximagesrc->starty = 0;
  ximagesrc->endx = 0;
  ximagesrc->endy = 0;
1130 1131 1132 1133 1134 1135 1136
}

static gboolean
plugin_init (GstPlugin * plugin)
{
  gboolean ret;

Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1137
  GST_DEBUG_CATEGORY_INIT (gst_debug_ximage_src, "ximagesrc", 0,
1138 1139 1140
      "ximagesrc element debug");

  ret = gst_element_register (plugin, "ximagesrc", GST_RANK_NONE,
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1141
      GST_TYPE_XIMAGE_SRC);
1142 1143 1144 1145 1146 1147 1148

  return ret;
}

GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
    GST_VERSION_MINOR,
    "ximagesrc",
Thomas Vander Stichele's avatar
Thomas Vander Stichele committed
1149 1150
    "X11 video input plugin using standard Xlib calls",
    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);