mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-19 08:11:16 +00:00
ff5dc55c8a
This prevents recursion on error. This used to happen as we don't change the state when something fails. We end up running and failing in the same state forever.
672 lines
20 KiB
C
672 lines
20 KiB
C
/* GStreamer
|
|
* Copyright (C) <2007> Thijs Vermeir <thijsvermeir@gmail.com>
|
|
* Copyright (C) <2006> Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>
|
|
* Copyright (C) <2004> David A. Schleef <ds@schleef.org>
|
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
|
*
|
|
* 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., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstrfbsrc.h"
|
|
|
|
#include <gst/video/video.h>
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_X11
|
|
#include <X11/Xlib.h>
|
|
#endif
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_HOST,
|
|
PROP_PORT,
|
|
PROP_VERSION,
|
|
PROP_PASSWORD,
|
|
PROP_OFFSET_X,
|
|
PROP_OFFSET_Y,
|
|
PROP_WIDTH,
|
|
PROP_HEIGHT,
|
|
PROP_INCREMENTAL,
|
|
PROP_USE_COPYRECT,
|
|
PROP_SHARED,
|
|
PROP_VIEWONLY
|
|
};
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (rfbsrc_debug);
|
|
GST_DEBUG_CATEGORY (rfbdecoder_debug);
|
|
#define GST_CAT_DEFAULT rfbsrc_debug
|
|
|
|
static GstStaticPadTemplate gst_rfb_src_template =
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("RGB")
|
|
"; " GST_VIDEO_CAPS_MAKE ("BGR")
|
|
"; " GST_VIDEO_CAPS_MAKE ("RGBx")
|
|
"; " GST_VIDEO_CAPS_MAKE ("BGRx")
|
|
"; " GST_VIDEO_CAPS_MAKE ("xRGB")
|
|
"; " GST_VIDEO_CAPS_MAKE ("xBGR")));
|
|
|
|
static void gst_rfb_src_finalize (GObject * object);
|
|
static void gst_rfb_src_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static void gst_rfb_src_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
|
|
static GstCaps *gst_rfb_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
|
|
static gboolean gst_rfb_src_start (GstBaseSrc * bsrc);
|
|
static gboolean gst_rfb_src_stop (GstBaseSrc * bsrc);
|
|
static gboolean gst_rfb_src_event (GstBaseSrc * bsrc, GstEvent * event);
|
|
static GstFlowReturn gst_rfb_src_create (GstPushSrc * psrc,
|
|
GstBuffer ** outbuf);
|
|
|
|
#define gst_rfb_src_parent_class parent_class
|
|
G_DEFINE_TYPE (GstRfbSrc, gst_rfb_src, GST_TYPE_PUSH_SRC);
|
|
|
|
static void
|
|
gst_rfb_src_class_init (GstRfbSrcClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstBaseSrcClass *gstbasesrc_class;
|
|
GstElementClass *gstelement_class;
|
|
GstPushSrcClass *gstpushsrc_class;
|
|
|
|
|
|
GST_DEBUG_CATEGORY_INIT (rfbsrc_debug, "rfbsrc", 0, "rfb src element");
|
|
GST_DEBUG_CATEGORY_INIT (rfbdecoder_debug, "rfbdecoder", 0, "rfb decoder");
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
gstbasesrc_class = (GstBaseSrcClass *) klass;
|
|
gstpushsrc_class = (GstPushSrcClass *) klass;
|
|
|
|
gobject_class->finalize = gst_rfb_src_finalize;
|
|
gobject_class->set_property = gst_rfb_src_set_property;
|
|
gobject_class->get_property = gst_rfb_src_get_property;
|
|
|
|
g_object_class_install_property (gobject_class, PROP_HOST,
|
|
g_param_spec_string ("host", "Host to connect to", "Host to connect to",
|
|
"127.0.0.1", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_PORT,
|
|
g_param_spec_int ("port", "Port", "Port",
|
|
1, 65535, 5900, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_VERSION,
|
|
g_param_spec_string ("version", "RFB protocol version",
|
|
"RFB protocol version", "3.3",
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_PASSWORD,
|
|
g_param_spec_string ("password", "Password for authentication",
|
|
"Password for authentication", "",
|
|
G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_OFFSET_X,
|
|
g_param_spec_int ("offset-x", "x offset for screen scrapping",
|
|
"x offset for screen scrapping", 0, 65535, 0,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_OFFSET_Y,
|
|
g_param_spec_int ("offset-y", "y offset for screen scrapping",
|
|
"y offset for screen scrapping", 0, 65535, 0,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_WIDTH,
|
|
g_param_spec_int ("width", "width of screen", "width of screen", 0, 65535,
|
|
0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_HEIGHT,
|
|
g_param_spec_int ("height", "height of screen", "height of screen", 0,
|
|
65535, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_INCREMENTAL,
|
|
g_param_spec_boolean ("incremental", "Incremental updates",
|
|
"Incremental updates", TRUE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_USE_COPYRECT,
|
|
g_param_spec_boolean ("use-copyrect", "Use copyrect encoding",
|
|
"Use copyrect encoding", FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_SHARED,
|
|
g_param_spec_boolean ("shared", "Share desktop with other clients",
|
|
"Share desktop with other clients", TRUE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
g_object_class_install_property (gobject_class, PROP_VIEWONLY,
|
|
g_param_spec_boolean ("view-only", "Only view the desktop",
|
|
"only view the desktop", FALSE,
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_rfb_src_fixate);
|
|
gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_rfb_src_start);
|
|
gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_rfb_src_stop);
|
|
gstbasesrc_class->event = GST_DEBUG_FUNCPTR (gst_rfb_src_event);
|
|
gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_rfb_src_create);
|
|
|
|
gstelement_class = GST_ELEMENT_CLASS (klass);
|
|
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&gst_rfb_src_template);
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class, "Rfb source",
|
|
"Source/Video",
|
|
"Creates a rfb video stream",
|
|
"David A. Schleef <ds@schleef.org>, "
|
|
"Andre Moreira Magalhaes <andre.magalhaes@indt.org.br>, "
|
|
"Thijs Vermeir <thijsvermeir@gmail.com>");
|
|
}
|
|
|
|
static void
|
|
gst_rfb_src_init (GstRfbSrc * src)
|
|
{
|
|
GstBaseSrc *bsrc = GST_BASE_SRC (src);
|
|
|
|
gst_pad_use_fixed_caps (GST_BASE_SRC_PAD (bsrc));
|
|
gst_base_src_set_live (bsrc, TRUE);
|
|
gst_base_src_set_format (bsrc, GST_FORMAT_TIME);
|
|
|
|
src->host = g_strdup ("127.0.0.1");
|
|
src->port = 5900;
|
|
src->version_major = 3;
|
|
src->version_minor = 3;
|
|
|
|
src->incremental_update = TRUE;
|
|
|
|
src->view_only = FALSE;
|
|
|
|
src->pool = NULL;
|
|
|
|
src->decoder = rfb_decoder_new ();
|
|
|
|
}
|
|
|
|
static void
|
|
gst_rfb_src_finalize (GObject * object)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (object);
|
|
|
|
g_free (src->host);
|
|
if (src->pool) {
|
|
gst_object_unref (src->pool);
|
|
src->pool = NULL;
|
|
}
|
|
if (src->decoder) {
|
|
rfb_decoder_free (src->decoder);
|
|
src->decoder = NULL;
|
|
}
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gst_rfb_property_set_version (GstRfbSrc * src, gchar * value)
|
|
{
|
|
gchar *major;
|
|
gchar *minor;
|
|
|
|
g_return_if_fail (src != NULL);
|
|
g_return_if_fail (value != NULL);
|
|
|
|
major = g_strdup (value);
|
|
minor = g_strrstr (value, ".");
|
|
|
|
g_return_if_fail (minor != NULL);
|
|
|
|
*minor++ = 0;
|
|
|
|
g_return_if_fail (g_ascii_isdigit (*major) == TRUE);
|
|
g_return_if_fail (g_ascii_isdigit (*minor) == TRUE);
|
|
|
|
src->version_major = g_ascii_digit_value (*major);
|
|
src->version_minor = g_ascii_digit_value (*minor);
|
|
|
|
GST_DEBUG ("Version major : %d", src->version_major);
|
|
GST_DEBUG ("Version minor : %d", src->version_minor);
|
|
|
|
g_free (major);
|
|
g_free (value);
|
|
}
|
|
|
|
static gchar *
|
|
gst_rfb_property_get_version (GstRfbSrc * src)
|
|
{
|
|
return g_strdup_printf ("%d.%d", src->version_major, src->version_minor);
|
|
}
|
|
|
|
static void
|
|
gst_rfb_src_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_HOST:
|
|
src->host = g_strdup (g_value_get_string (value));
|
|
break;
|
|
case PROP_PORT:
|
|
src->port = g_value_get_int (value);
|
|
break;
|
|
case PROP_VERSION:
|
|
gst_rfb_property_set_version (src, g_strdup (g_value_get_string (value)));
|
|
break;
|
|
case PROP_PASSWORD:
|
|
g_free (src->decoder->password);
|
|
src->decoder->password = g_strdup (g_value_get_string (value));
|
|
break;
|
|
case PROP_OFFSET_X:
|
|
src->decoder->offset_x = g_value_get_int (value);
|
|
break;
|
|
case PROP_OFFSET_Y:
|
|
src->decoder->offset_y = g_value_get_int (value);
|
|
break;
|
|
case PROP_WIDTH:
|
|
src->decoder->rect_width = g_value_get_int (value);
|
|
break;
|
|
case PROP_HEIGHT:
|
|
src->decoder->rect_height = g_value_get_int (value);
|
|
break;
|
|
case PROP_INCREMENTAL:
|
|
src->incremental_update = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_USE_COPYRECT:
|
|
src->decoder->use_copyrect = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_SHARED:
|
|
src->decoder->shared_flag = g_value_get_boolean (value);
|
|
break;
|
|
case PROP_VIEWONLY:
|
|
src->view_only = g_value_get_boolean (value);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_rfb_src_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (object);
|
|
gchar *version;
|
|
|
|
switch (prop_id) {
|
|
case PROP_HOST:
|
|
g_value_set_string (value, src->host);
|
|
break;
|
|
case PROP_PORT:
|
|
g_value_set_int (value, src->port);
|
|
break;
|
|
case PROP_VERSION:
|
|
version = gst_rfb_property_get_version (src);
|
|
g_value_set_string (value, version);
|
|
g_free (version);
|
|
break;
|
|
case PROP_OFFSET_X:
|
|
g_value_set_int (value, src->decoder->offset_x);
|
|
break;
|
|
case PROP_OFFSET_Y:
|
|
g_value_set_int (value, src->decoder->offset_y);
|
|
break;
|
|
case PROP_WIDTH:
|
|
g_value_set_int (value, src->decoder->rect_width);
|
|
break;
|
|
case PROP_HEIGHT:
|
|
g_value_set_int (value, src->decoder->rect_height);
|
|
break;
|
|
case PROP_INCREMENTAL:
|
|
g_value_set_boolean (value, src->incremental_update);
|
|
break;
|
|
case PROP_USE_COPYRECT:
|
|
g_value_set_boolean (value, src->decoder->use_copyrect);
|
|
break;
|
|
case PROP_SHARED:
|
|
g_value_set_boolean (value, src->decoder->shared_flag);
|
|
break;
|
|
case PROP_VIEWONLY:
|
|
g_value_set_boolean (value, src->view_only);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GstCaps *
|
|
gst_rfb_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (bsrc);
|
|
RfbDecoder *decoder;
|
|
GstStructure *structure;
|
|
guint i;
|
|
|
|
decoder = src->decoder;
|
|
|
|
GST_DEBUG_OBJECT (src, "fixating caps %" GST_PTR_FORMAT, caps);
|
|
|
|
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_int (structure,
|
|
"width", decoder->rect_width);
|
|
gst_structure_fixate_field_nearest_int (structure,
|
|
"height", decoder->rect_height);
|
|
gst_structure_fixate_field (structure, "format");
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (src, "fixated caps %" GST_PTR_FORMAT, caps);
|
|
|
|
caps = GST_BASE_SRC_CLASS (parent_class)->fixate (bsrc, caps);
|
|
|
|
return caps;
|
|
}
|
|
|
|
static void
|
|
gst_rfb_negotiate_pool (GstRfbSrc * src, GstCaps * caps)
|
|
{
|
|
GstQuery *query;
|
|
GstBufferPool *pool = NULL;
|
|
guint size, min, max;
|
|
GstStructure *config;
|
|
|
|
/* find a pool for the negotiated caps now */
|
|
query = gst_query_new_allocation (caps, TRUE);
|
|
|
|
if (!gst_pad_peer_query (GST_BASE_SRC_PAD (src), query)) {
|
|
/* not a problem, we use the defaults of query */
|
|
GST_DEBUG_OBJECT (src, "could not get downstream ALLOCATION hints");
|
|
}
|
|
|
|
if (gst_query_get_n_allocation_pools (query) > 0) {
|
|
/* we got configuration from our peer, parse them */
|
|
gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
|
|
} else {
|
|
GST_DEBUG_OBJECT (src, "didn't get downstream pool hints");
|
|
size = GST_BASE_SRC (src)->blocksize;
|
|
min = max = 0;
|
|
}
|
|
|
|
if (pool == NULL) {
|
|
/* we did not get a pool, make one ourselves then */
|
|
pool = gst_video_buffer_pool_new ();
|
|
}
|
|
|
|
if (src->pool)
|
|
gst_object_unref (src->pool);
|
|
src->pool = pool;
|
|
|
|
config = gst_buffer_pool_get_config (pool);
|
|
gst_buffer_pool_config_set_params (config, caps, size, min, max);
|
|
gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
|
|
|
|
gst_buffer_pool_set_config (pool, config);
|
|
// and activate
|
|
gst_buffer_pool_set_active (pool, TRUE);
|
|
|
|
gst_query_unref (query);
|
|
}
|
|
|
|
static gboolean
|
|
gst_rfb_src_start (GstBaseSrc * bsrc)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (bsrc);
|
|
RfbDecoder *decoder;
|
|
GstCaps *caps;
|
|
GstVideoInfo vinfo;
|
|
GstVideoFormat vformat;
|
|
guint32 red_mask, green_mask, blue_mask;
|
|
gchar *stream_id = NULL;
|
|
GstEvent *stream_start = NULL;
|
|
|
|
decoder = src->decoder;
|
|
|
|
GST_DEBUG_OBJECT (src, "connecting to host %s on port %d",
|
|
src->host, src->port);
|
|
if (!rfb_decoder_connect_tcp (decoder, src->host, src->port)) {
|
|
if (decoder->error != NULL) {
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ,
|
|
("Could not connect to VNC server %s on port %d: %s", src->host,
|
|
src->port, decoder->error->message), (NULL));
|
|
} else {
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ,
|
|
("Could not connect to VNC server %s on port %d", src->host,
|
|
src->port), (NULL));
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
while (!decoder->inited) {
|
|
if (!rfb_decoder_iterate (decoder)) {
|
|
if (decoder->error != NULL) {
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ,
|
|
("Failed to setup VNC connection to host %s on port %d: %s",
|
|
src->host, src->port, decoder->error->message), (NULL));
|
|
} else {
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ,
|
|
("Failed to setup VNC connection to host %s on port %d", src->host,
|
|
src->port), (NULL));
|
|
}
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
stream_id = gst_pad_create_stream_id_printf (GST_BASE_SRC_PAD (bsrc),
|
|
GST_ELEMENT (src), "%s:%d", src->host, src->port);
|
|
stream_start = gst_event_new_stream_start (stream_id);
|
|
g_free (stream_id);
|
|
gst_pad_push_event (GST_BASE_SRC_PAD (bsrc), stream_start);
|
|
|
|
decoder->rect_width =
|
|
(decoder->rect_width ? decoder->rect_width : decoder->width);
|
|
decoder->rect_height =
|
|
(decoder->rect_height ? decoder->rect_height : decoder->height);
|
|
|
|
g_object_set (bsrc, "blocksize",
|
|
src->decoder->width * src->decoder->height * (decoder->bpp / 8), NULL);
|
|
|
|
decoder->frame = g_malloc (bsrc->blocksize);
|
|
if (decoder->use_copyrect) {
|
|
decoder->prev_frame = g_malloc (bsrc->blocksize);
|
|
}
|
|
decoder->decoder_private = src;
|
|
|
|
/* calculate some many used values */
|
|
decoder->bytespp = decoder->bpp / 8;
|
|
decoder->line_size = decoder->rect_width * decoder->bytespp;
|
|
|
|
GST_DEBUG_OBJECT (src, "setting caps width to %d and height to %d",
|
|
decoder->rect_width, decoder->rect_height);
|
|
|
|
red_mask = decoder->red_max << decoder->red_shift;
|
|
green_mask = decoder->green_max << decoder->green_shift;
|
|
blue_mask = decoder->blue_max << decoder->blue_shift;
|
|
|
|
vformat = gst_video_format_from_masks (decoder->depth, decoder->bpp,
|
|
decoder->big_endian ? G_BIG_ENDIAN : G_LITTLE_ENDIAN,
|
|
red_mask, green_mask, blue_mask, 0);
|
|
|
|
gst_video_info_init (&vinfo);
|
|
|
|
gst_video_info_set_format (&vinfo, vformat, decoder->rect_width,
|
|
decoder->rect_height);
|
|
|
|
caps = gst_video_info_to_caps (&vinfo);
|
|
|
|
gst_pad_set_caps (GST_BASE_SRC_PAD (bsrc), caps);
|
|
|
|
gst_rfb_negotiate_pool (src, caps);
|
|
|
|
gst_caps_unref (caps);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rfb_src_stop (GstBaseSrc * bsrc)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (bsrc);
|
|
|
|
if (src->decoder->connection) {
|
|
g_object_unref (src->decoder->connection);
|
|
src->decoder->connection = NULL;
|
|
}
|
|
|
|
if (src->decoder->frame) {
|
|
g_free (src->decoder->frame);
|
|
src->decoder->frame = NULL;
|
|
}
|
|
|
|
if (src->decoder->prev_frame) {
|
|
g_free (src->decoder->prev_frame);
|
|
src->decoder->prev_frame = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstFlowReturn
|
|
gst_rfb_src_create (GstPushSrc * psrc, GstBuffer ** outbuf)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (psrc);
|
|
RfbDecoder *decoder = src->decoder;
|
|
GstMapInfo info;
|
|
GstFlowReturn ret;
|
|
|
|
rfb_decoder_send_update_request (decoder, src->incremental_update,
|
|
decoder->offset_x, decoder->offset_y, decoder->rect_width,
|
|
decoder->rect_height);
|
|
|
|
while (decoder->state != NULL) {
|
|
if (!rfb_decoder_iterate (decoder)) {
|
|
if (decoder->error != NULL) {
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ,
|
|
("Error on VNC connection to host %s on port %d: %s",
|
|
src->host, src->port, decoder->error->message), (NULL));
|
|
} else {
|
|
GST_ELEMENT_ERROR (src, RESOURCE, READ,
|
|
("Error on setup VNC connection to host %s on port %d", src->host,
|
|
src->port), (NULL));
|
|
}
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
}
|
|
|
|
/* Create the buffer. */
|
|
ret = gst_buffer_pool_acquire_buffer (src->pool, outbuf, NULL);
|
|
|
|
if (G_UNLIKELY (ret != GST_FLOW_OK)) {
|
|
return GST_FLOW_ERROR;
|
|
}
|
|
|
|
gst_buffer_map (*outbuf, &info, GST_MAP_WRITE);
|
|
|
|
memcpy (info.data, decoder->frame, info.size);
|
|
|
|
GST_BUFFER_PTS (*outbuf) =
|
|
gst_clock_get_time (GST_ELEMENT_CLOCK (src)) -
|
|
GST_ELEMENT_CAST (src)->base_time;
|
|
|
|
gst_buffer_unmap (*outbuf, &info);
|
|
|
|
return GST_FLOW_OK;
|
|
}
|
|
|
|
static gboolean
|
|
gst_rfb_src_event (GstBaseSrc * bsrc, GstEvent * event)
|
|
{
|
|
GstRfbSrc *src = GST_RFB_SRC (bsrc);
|
|
gdouble x, y;
|
|
gint button;
|
|
const GstStructure *structure;
|
|
const gchar *event_type;
|
|
gboolean key_event, key_press;
|
|
|
|
key_event = FALSE;
|
|
|
|
switch (GST_EVENT_TYPE (event)) {
|
|
case GST_EVENT_NAVIGATION:
|
|
|
|
/* if in view_only mode ignore the navigation event */
|
|
if (src->view_only)
|
|
break;
|
|
|
|
structure = gst_event_get_structure (event);
|
|
event_type = gst_structure_get_string (structure, "event");
|
|
|
|
if (strcmp (event_type, "key-press") == 0) {
|
|
key_event = key_press = TRUE;
|
|
} else if (strcmp (event_type, "key-release") == 0) {
|
|
key_event = TRUE;
|
|
key_press = FALSE;
|
|
}
|
|
|
|
if (key_event) {
|
|
#ifdef HAVE_X11
|
|
const gchar *key;
|
|
KeySym key_sym;
|
|
|
|
key = gst_structure_get_string (structure, "key");
|
|
key_sym = XStringToKeysym (key);
|
|
|
|
if (key_sym != NoSymbol)
|
|
rfb_decoder_send_key_event (src->decoder, key_sym, key_press);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
gst_structure_get_double (structure, "pointer_x", &x);
|
|
gst_structure_get_double (structure, "pointer_y", &y);
|
|
gst_structure_get_int (structure, "button", &button);
|
|
|
|
/* we need to take care of the offset's */
|
|
x += src->decoder->offset_x;
|
|
y += src->decoder->offset_y;
|
|
|
|
if (strcmp (event_type, "mouse-move") == 0) {
|
|
GST_LOG_OBJECT (src, "sending mouse-move event "
|
|
"button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y);
|
|
rfb_decoder_send_pointer_event (src->decoder, src->button_mask,
|
|
(gint) x, (gint) y);
|
|
} else if (strcmp (event_type, "mouse-button-release") == 0) {
|
|
src->button_mask &= ~(1 << (button - 1));
|
|
GST_LOG_OBJECT (src, "sending mouse-button-release event "
|
|
"button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y);
|
|
rfb_decoder_send_pointer_event (src->decoder, src->button_mask,
|
|
(gint) x, (gint) y);
|
|
} else if (strcmp (event_type, "mouse-button-press") == 0) {
|
|
src->button_mask |= (1 << (button - 1));
|
|
GST_LOG_OBJECT (src, "sending mouse-button-press event "
|
|
"button_mask=%d, x=%d, y=%d", src->button_mask, (gint) x, (gint) y);
|
|
rfb_decoder_send_pointer_event (src->decoder, src->button_mask,
|
|
(gint) x, (gint) y);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
plugin_init (GstPlugin * plugin)
|
|
{
|
|
return gst_element_register (plugin, "rfbsrc", GST_RANK_NONE,
|
|
GST_TYPE_RFB_SRC);
|
|
}
|
|
|
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
|
GST_VERSION_MINOR,
|
|
rfbsrc,
|
|
"Connects to a VNC server and decodes RFB stream",
|
|
plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
|