* <refsect2>
* <title>Example pipelines</title>
* |[
- * gst-launch ximagesrc ! video/x-raw-rgb,framerate=5/1 ! ffmpegcolorspace ! theoraenc ! oggmux ! filesink location=desktop.ogg
+ * gst-launch-1.0 ximagesrc ! video/x-raw,framerate=5/1 ! videoconvert ! theoraenc ! oggmux ! filesink location=desktop.ogg
* ]| Encodes your X display to an Ogg theora video at 5 frames per second.
* </refsect2>
*/
#include <gst/gst.h>
#include <gst/gst-i18n-plugin.h>
+#include <gst/video/video.h>
+
+#include "gst/glib-compat-private.h"
GST_DEBUG_CATEGORY_STATIC (gst_debug_ximage_src);
#define GST_CAT_DEFAULT gst_debug_ximage_src
static GstStaticPadTemplate t =
GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("video/x-raw-rgb, "
+ GST_STATIC_CAPS ("video/x-raw, "
"framerate = (fraction) [ 0, MAX ], "
"width = (int) [ 1, MAX ], " "height = (int) [ 1, MAX ], "
"pixel-aspect-ratio = (fraction) [ 0, MAX ]"));
PROP_ENDX,
PROP_ENDY,
PROP_REMOTE,
+ PROP_XID,
+ PROP_XNAME,
};
-GST_BOILERPLATE (GstXImageSrc, gst_ximage_src, GstPushSrc, GST_TYPE_PUSH_SRC);
+#define gst_ximage_src_parent_class parent_class
+G_DEFINE_TYPE (GstXImageSrc, gst_ximage_src, GST_TYPE_PUSH_SRC);
-static void gst_ximage_src_fixate (GstPad * pad, GstCaps * caps);
+static GstCaps *gst_ximage_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
static void gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc);
/* Called when a buffer is returned from the pipeline */
GST_DEBUG_OBJECT (ximagesrc,
"destroy image %p as its size changed %dx%d vs current %dx%d",
ximage, meta->width, meta->height, ximagesrc->width, ximagesrc->height);
- g_mutex_lock (ximagesrc->x_lock);
+ g_mutex_lock (&ximagesrc->x_lock);
gst_ximageutil_ximage_destroy (ximagesrc->xcontext, ximage);
- g_mutex_unlock (ximagesrc->x_lock);
+ 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 (ximage);
- g_mutex_lock (ximagesrc->pool_lock);
+ g_mutex_lock (&ximagesrc->pool_lock);
+ GST_BUFFER_FLAGS (GST_BUFFER (ximage)) = 0; /* clear out any flags from the previous use */
ximagesrc->buffer_pool = g_slist_prepend (ximagesrc->buffer_pool, ximage);
- g_mutex_unlock (ximagesrc->pool_lock);
+ g_mutex_unlock (&ximagesrc->pool_lock);
+ }
+}
+
+static Window
+gst_ximage_src_find_window (GstXImageSrc * src, Window root, const char *name)
+{
+ Window *children;
+ Window window = 0, root_return, parent_return;
+ unsigned int nchildren;
+ char *tmpname;
+ int n, status;
+
+ status = XFetchName (src->xcontext->disp, root, &tmpname);
+ if (status && !strcmp (name, tmpname))
+ return root;
+
+ status =
+ XQueryTree (src->xcontext->disp, root, &root_return, &parent_return,
+ &children, &nchildren);
+ if (!status || !children)
+ return (Window) 0;
+
+ for (n = 0; n < nchildren; ++n) {
+ window = gst_ximage_src_find_window (src, children[n], name);
+ if (window != 0)
+ break;
}
+
+ XFree (children);
+ return window;
}
static gboolean
if (s->xcontext != NULL)
return TRUE;
- g_mutex_lock (s->x_lock);
+ g_mutex_lock (&s->x_lock);
s->xcontext = ximageutil_xcontext_get (GST_ELEMENT (s), name);
if (s->xcontext == NULL) {
- g_mutex_unlock (s->x_lock);
+ 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"));
s->width = s->xcontext->width;
s->height = s->xcontext->height;
- /* Always capture root window, for now */
s->xwindow = s->xcontext->root;
+ if (s->xid != 0 || s->xname) {
+ int status;
+ XWindowAttributes attrs;
+ Window window;
+
+ if (s->xid != 0) {
+ status = XGetWindowAttributes (s->xcontext->disp, s->xid, &attrs);
+ if (status) {
+ GST_DEBUG_OBJECT (s, "Found window XID %" G_GUINT64_FORMAT, s->xid);
+ s->xwindow = s->xid;
+ goto window_found;
+ } else {
+ GST_WARNING_OBJECT (s, "Failed to get window %" G_GUINT64_FORMAT
+ " attributes", s->xid);
+ }
+ }
+
+ if (s->xname) {
+ GST_DEBUG_OBJECT (s, "Looking for window %s", s->xname);
+ window = gst_ximage_src_find_window (s, s->xcontext->root, s->xname);
+ if (window != 0) {
+ GST_DEBUG_OBJECT (s, "Found window named %s, ", s->xname);
+ status = XGetWindowAttributes (s->xcontext->disp, window, &attrs);
+ if (status) {
+ s->xwindow = window;
+ goto window_found;
+ } else {
+ GST_WARNING_OBJECT (s, "Failed to get window attributes for "
+ "window named %s", s->xname);
+ }
+ }
+ }
+
+ GST_INFO_OBJECT (s, "Using root window");
+ goto use_root_window;
+
+ window_found:
+ g_assert (s->xwindow != 0);
+ s->width = attrs.width;
+ s->height = attrs.height;
+ GST_INFO_OBJECT (s, "Using default window size of %dx%d",
+ s->width, s->height);
+ }
+use_root_window:
#ifdef HAVE_XFIXES
/* check if xfixes supported */
#endif
#endif
- g_mutex_unlock (s->x_lock);
+ g_mutex_unlock (&s->x_lock);
if (s->xcontext == NULL)
return FALSE;
#endif
if (src->xcontext) {
- g_mutex_lock (src->x_lock);
+ g_mutex_lock (&src->x_lock);
#ifdef HAVE_XDAMAGE
if (src->damage_copy_gc != None) {
ximageutil_xcontext_clear (src->xcontext);
src->xcontext = NULL;
- g_mutex_unlock (src->x_lock);
+ g_mutex_unlock (&src->x_lock);
}
return TRUE;
}
#endif
+#ifdef HAVE_XDAMAGE
+static void
+copy_buffer (GstBuffer * dest, GstBuffer * src)
+{
+ GstMapInfo map;
+
+ gst_buffer_map (src, &map, GST_MAP_READ);
+ gst_buffer_fill (dest, 0, map.data, map.size);
+ gst_buffer_unmap (src, &map);
+}
+#endif
+
/* Retrieve an XImageSrcBuffer, preferably from our
* pool of existing images and populate it from the window */
static GstBuffer *
GstBuffer *ximage = NULL;
GstMetaXImage *meta;
- g_mutex_lock (ximagesrc->pool_lock);
+ g_mutex_lock (&ximagesrc->pool_lock);
while (ximagesrc->buffer_pool != NULL) {
ximage = ximagesrc->buffer_pool->data;
ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool,
ximagesrc->buffer_pool);
}
- g_mutex_unlock (ximagesrc->pool_lock);
+ g_mutex_unlock (&ximagesrc->pool_lock);
if (ximage == NULL) {
- GstXContext *xcontext;
- GstCaps *caps = NULL;
-
GST_DEBUG_OBJECT (ximagesrc, "creating image (%dx%d)",
ximagesrc->width, ximagesrc->height);
- g_mutex_lock (ximagesrc->x_lock);
+ g_mutex_lock (&ximagesrc->x_lock);
ximage = gst_ximageutil_ximage_new (ximagesrc->xcontext,
GST_ELEMENT (ximagesrc), ximagesrc->width, ximagesrc->height,
(BufferReturnFunc) (gst_ximage_src_return_buf));
GST_ELEMENT_ERROR (ximagesrc, RESOURCE, WRITE, (NULL),
("could not create a %dx%d ximage", ximagesrc->width,
ximagesrc->height));
- g_mutex_unlock (ximagesrc->x_lock);
+ g_mutex_unlock (&ximagesrc->x_lock);
return NULL;
}
- xcontext = ximagesrc->xcontext;
-
-
- 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,
- "width", G_TYPE_INT, ximagesrc->width,
- "height", G_TYPE_INT, ximagesrc->height,
- "framerate", GST_TYPE_FRACTION, ximagesrc->fps_n, ximagesrc->fps_d,
- "pixel-aspect-ratio", GST_TYPE_FRACTION,
- gst_value_get_fraction_numerator (xcontext->par),
- gst_value_get_fraction_denominator (xcontext->par), NULL);
-
- gst_buffer_set_caps (ximage, caps);
- g_mutex_unlock (ximagesrc->x_lock);
-
- gst_caps_unref (caps);
+ g_mutex_unlock (&ximagesrc->x_lock);
}
g_return_val_if_fail (GST_IS_XIMAGE_SRC (ximagesrc), NULL);
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)));
+ "Copying from last frame ximage->size: %" G_GSIZE_FORMAT,
+ gst_buffer_get_size (ximage));
+ copy_buffer (ximage, ximagesrc->last_ximage);
have_frame = TRUE;
}
for (i = 0; i < nrects; i++) {
} while (XPending (ximagesrc->xcontext->disp));
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)));
+ "Copying from last frame ximage->size: %" G_GSIZE_FORMAT,
+ gst_buffer_get_size (ximage));
+ copy_buffer (ximage, ximagesrc->last_ximage);
}
#ifdef HAVE_XFIXES
/* re-get area where last mouse pointer was but only if in our clipping
#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));
+ gst_buffer_ref (ximage);
if (ximagesrc->last_ximage) {
- gst_buffer_unref (GST_BUFFER (ximagesrc->last_ximage));
+ gst_buffer_unref (ximagesrc->last_ximage);
}
ximagesrc->last_ximage = ximage;
GST_LOG_OBJECT (ximagesrc, "reffing current buffer for last_ximage");
if (ret == GST_CLOCK_UNSCHEDULED) {
/* Got woken up by the unlock function */
GST_OBJECT_UNLOCK (s);
- return GST_FLOW_WRONG_STATE;
+ return GST_FLOW_FLUSHING;
}
/* Duration is a complete 1/fps frame duration */
dur = gst_util_uint64_scale_int (GST_SECOND, s->fps_d, s->fps_n);
if (!image)
return GST_FLOW_ERROR;
- *buf = GST_BUFFER (image);
+ *buf = image;
GST_BUFFER_TIMESTAMP (*buf) = next_capture_ts;
GST_BUFFER_DURATION (*buf) = dur;
case PROP_REMOTE:
src->remote = g_value_get_boolean (value);
break;
+ case PROP_XID:
+ if (src->xcontext != NULL) {
+ g_warning ("ximagesrc window ID must be set before opening display");
+ break;
+ }
+ src->xid = g_value_get_uint64 (value);
+ break;
+ case PROP_XNAME:
+ if (src->xcontext != NULL) {
+ g_warning ("ximagesrc window name must be set before opening display");
+ break;
+ }
+ g_free (src->xname);
+ src->xname = g_strdup (g_value_get_string (value));
+ break;
default:
break;
}
case PROP_REMOTE:
g_value_set_boolean (value, src->remote);
break;
+ case PROP_XID:
+ g_value_set_uint64 (value, src->xid);
+ break;
+ case PROP_XNAME:
+ g_value_set_string (value, src->xname);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
static void
gst_ximage_src_clear_bufpool (GstXImageSrc * ximagesrc)
{
- g_mutex_lock (ximagesrc->pool_lock);
+ g_mutex_lock (&ximagesrc->pool_lock);
while (ximagesrc->buffer_pool != NULL) {
GstBuffer *ximage = ximagesrc->buffer_pool->data;
ximagesrc->buffer_pool = g_slist_delete_link (ximagesrc->buffer_pool,
ximagesrc->buffer_pool);
}
- g_mutex_unlock (ximagesrc->pool_lock);
-}
-
-static void
-gst_ximage_src_base_init (gpointer g_class)
-{
- GstElementClass *ec = GST_ELEMENT_CLASS (g_class);
-
- 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>");
- gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&t));
+ g_mutex_unlock (&ximagesrc->pool_lock);
}
static void
if (src->xcontext)
ximageutil_xcontext_clear (src->xcontext);
- g_mutex_free (src->pool_lock);
- g_mutex_free (src->x_lock);
+ g_free (src->xname);
+ g_mutex_clear (&src->pool_lock);
+ g_mutex_clear (&src->x_lock);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstCaps *
-gst_ximage_src_get_caps (GstBaseSrc * bs)
+gst_ximage_src_get_caps (GstBaseSrc * bs, GstCaps * filter)
{
GstXImageSrc *s = GST_XIMAGE_SRC (bs);
GstXContext *xcontext;
gint width, height;
+ GstVideoFormat format;
if ((!s->xcontext) && (!gst_ximage_src_open_display (s, s->display_name)))
- return
- gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC
- (s)->srcpad));
+ return gst_pad_get_pad_template_caps (GST_BASE_SRC (s)->srcpad);
if (!gst_ximage_src_recalc (s))
- return
- gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC
- (s)->srcpad));
+ return gst_pad_get_pad_template_caps (GST_BASE_SRC (s)->srcpad);
xcontext = s->xcontext;
-
- width = xcontext->width;
- height = xcontext->height;
+ width = s->xcontext->width;
+ height = s->xcontext->height;
+ if (s->xwindow != 0) {
+ XWindowAttributes attrs;
+ int status = XGetWindowAttributes (s->xcontext->disp, s->xwindow, &attrs);
+ if (status) {
+ width = attrs.width;
+ height = attrs.height;
+ }
+ }
/* property comments say 0 means right/bottom, means we can't capture
the top left pixel alone */
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 &&
- s->startx >= 0 && s->starty >= 0) {
+ s->starty < xcontext->height && s->endy < xcontext->height) {
/* values are fine */
s->width = width = s->endx - s->startx + 1;
s->height = height = s->endy - s->starty + 1;
s->endy = height - 1;
}
GST_DEBUG ("width = %d, height=%d", width, height);
- 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,
- "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,
+
+ format =
+ gst_video_format_from_masks (xcontext->depth, xcontext->bpp,
+ xcontext->endianness, xcontext->r_mask_output, xcontext->g_mask_output,
+ xcontext->b_mask_output, 0);
+
+ return gst_caps_new_simple ("video/x-raw",
+ "format", G_TYPE_STRING, gst_video_format_to_string (format),
"width", G_TYPE_INT, width,
"height", G_TYPE_INT, height,
"framerate", GST_TYPE_FRACTION_RANGE, 1, G_MAXINT, G_MAXINT, 1,
return TRUE;
}
-static void
-gst_ximage_src_fixate (GstPad * pad, GstCaps * caps)
+static GstCaps *
+gst_ximage_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
{
gint i;
GstStructure *structure;
+ caps = gst_caps_make_writable (caps);
+
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);
}
+ caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
+
+ return caps;
}
static void
gst_ximage_src_class_init (GstXImageSrcClass * klass)
{
GObjectClass *gc = G_OBJECT_CLASS (klass);
+ GstElementClass *ec = GST_ELEMENT_CLASS (klass);
GstBaseSrcClass *bc = GST_BASE_SRC_CLASS (klass);
GstPushSrcClass *push_class = GST_PUSH_SRC_CLASS (klass);
g_param_spec_boolean ("remote", "Remote dispay",
"Whether the display is remote", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstXImageSrc:xid
+ *
+ * The XID of the window to capture. 0 for the root window (default).
+ *
+ * Since: 0.10.31
+ **/
+ g_object_class_install_property (gc, PROP_XID,
+ g_param_spec_uint64 ("xid", "Window XID",
+ "Window XID to capture from", 0, G_MAXUINT64, 0,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ /**
+ * GstXImageSrc:xname
+ *
+ * The name of the window to capture, if any.
+ *
+ * Since: 0.10.31
+ **/
+ g_object_class_install_property (gc, PROP_XNAME,
+ g_param_spec_string ("xname", "Window name",
+ "Window name to capture from", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
- parent_class = g_type_class_peek_parent (klass);
+ gst_element_class_set_static_metadata (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>");
+ gst_element_class_add_pad_template (ec, gst_static_pad_template_get (&t));
- push_class->create = gst_ximage_src_create;
+ bc->fixate = gst_ximage_src_fixate;
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;
+ push_class->create = gst_ximage_src_create;
}
static void
-gst_ximage_src_init (GstXImageSrc * ximagesrc, GstXImageSrcClass * klass)
+gst_ximage_src_init (GstXImageSrc * ximagesrc)
{
gst_base_src_set_format (GST_BASE_SRC (ximagesrc), GST_FORMAT_TIME);
gst_base_src_set_live (GST_BASE_SRC (ximagesrc), TRUE);
- gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (ximagesrc),
- gst_ximage_src_fixate);
- ximagesrc->pool_lock = g_mutex_new ();
- ximagesrc->x_lock = g_mutex_new ();
+ g_mutex_init (&ximagesrc->pool_lock);
+ g_mutex_init (&ximagesrc->x_lock);
ximagesrc->show_pointer = TRUE;
ximagesrc->use_damage = TRUE;
ximagesrc->startx = 0;
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
- "ximagesrc",
+ ximagesrc,
"X11 video input plugin using standard Xlib calls",
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);