diff --git a/configure.ac b/configure.ac index 003c17c549..1da689f674 100644 --- a/configure.ac +++ b/configure.ac @@ -1791,6 +1791,14 @@ AG_GST_CHECK_FEATURE(OPENJPEG, [openjpeg library], openjpeg, [ AC_SUBST(OPENJPEG_LIBS) ]) +dnl *** OpenNI2 *** +translit(dnm, m, l) AM_CONDITIONAL(USE_OPENNI2, true) +AG_GST_CHECK_FEATURE(OPENNI2, [openni2 library], openni2, [ + PKG_CHECK_MODULES(OPENNI2, libopenni2 >= 0.26, HAVE_OPENNI2="yes", [ HAVE_OPENNI2="no" ] ) + AC_SUBST(OPENNI2_CFLAGS) + AC_SUBST(OPENNI2_LIBS) +]) + dnl *** Opus *** translit(dnm, m, l) AM_CONDITIONAL(USE_OPUS, true) AG_GST_CHECK_FEATURE(OPUS, [opus], opus, [ @@ -2239,6 +2247,7 @@ AM_CONDITIONAL(USE_OFA, false) AM_CONDITIONAL(USE_OPENAL, false) AM_CONDITIONAL(USE_OPENCV, false) AM_CONDITIONAL(USE_OPENJPEG, false) +AM_CONDITIONAL(USE_OPENNI2, false) AM_CONDITIONAL(USE_OPUS, false) AM_CONDITIONAL(USE_PVR, false) AM_CONDITIONAL(USE_RSVG, false) @@ -2507,6 +2516,7 @@ ext/openal/Makefile ext/opencv/Makefile ext/openexr/Makefile ext/openjpeg/Makefile +ext/openni2/Makefile ext/opus/Makefile ext/rsvg/Makefile ext/resindvd/Makefile diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index 833ffddc7b..8758cf39e7 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -99,6 +99,7 @@ EXTRA_HFILES = \ $(top_srcdir)/ext/opencv/gstpyramidsegment.h \ $(top_srcdir)/ext/opencv/gsttemplatematch.h \ $(top_srcdir)/ext/opencv/gsttextoverlay.h \ + $(top_srcdir)/ext/openni2/gstopenni2src.h \ $(top_srcdir)/ext/rsvg/gstrsvgdec.h \ $(top_srcdir)/ext/rsvg/gstrsvgoverlay.h \ $(top_srcdir)/ext/spandsp/gstspanplc.h \ diff --git a/ext/Makefile.am b/ext/Makefile.am index bfae3d1d31..951d2d5129 100644 --- a/ext/Makefile.am +++ b/ext/Makefile.am @@ -232,6 +232,12 @@ else OPENEXR_DIR = endif +if USE_OPENNI2 +OPENNI2_DIR=openni2 +else +OPENNI2_DIR= +endif + if USE_OPENJPEG OPENJPEG_DIR = openjpeg else @@ -425,6 +431,7 @@ SUBDIRS=\ $(OPENCV_DIR) \ $(OPENEXR_DIR) \ $(OPENJPEG_DIR) \ + $(OPENNI2_DIR) \ $(OPUS_DIR) \ $(RSVG_DIR) \ $(SBC_DIR) \ @@ -484,6 +491,7 @@ DIST_SUBDIRS = \ openal \ opencv \ openexr \ + openni2 \ openjpeg \ opus \ rsvg \ diff --git a/ext/openni2/Makefile.am b/ext/openni2/Makefile.am new file mode 100644 index 0000000000..f42477e1a8 --- /dev/null +++ b/ext/openni2/Makefile.am @@ -0,0 +1,22 @@ +plugin_LTLIBRARIES = libgstopenni2.la + +# sources used to compile this plug-in +libgstopenni2_la_SOURCES = gstopenni2.cpp \ + gstopenni2src.cpp + +libgstopenni2_la_CXXFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CXXFLAGS) $(OPENNI2_CFLAGS) + +# flags used to compile this facedetect +# add other _CFLAGS and _LIBS as needed +# +libgstopenni2_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) $(OPENNI2_CFLAGS) + +libgstopenni2_la_LIBADD = $(GST_LIBS) $(GST_BASE_LIBS) $(OPENNI2_LIBS) \ + $(GSTPB_BASE_LIBS) -lgstvideo-$(GST_API_VERSION) + +libgstopenni2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstopenni2_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +# headers we need but don't want installed +noinst_HEADERS = gstopenni2src.h \ No newline at end of file diff --git a/ext/openni2/gstopenni2.cpp b/ext/openni2/gstopenni2.cpp new file mode 100644 index 0000000000..ee396cd3a8 --- /dev/null +++ b/ext/openni2/gstopenni2.cpp @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) 2013 Miguel Casas-Sanchez + * + * gstopenni2.c: plugin registration + * + * 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. + */ + +/** + * SECTION:plugin-openni2src + * + * OpenNI2 is a library to access 3D sensors such as those based on PrimeSense + * depth sensor. Examples of such sensors are the Kinect used in Microsoft Xbox + * consoles and Asus WAVI Xtion. Notably recordings of 3D sessions can also be + * replayed as the original devices. See www.openni.org for more details. + * + * OpenNI2 can be downloaded from source, compiled and installed in Linux, Mac + * and Windows devices(https://github.com/OpenNI/OpenNI2). However is better to + * rely on Debian packages as part of the PCL library (or http://goo.gl/0o87EB). + * More concretely on the "libopenni2-dev" and "libopenni2" packages - that can + * be downloaded in http://goo.gl/2H6SZ6. + * + * + * Examples + * + * Some recorded .oni files are available at: + * + * http://people.cs.pitt.edu/~chang/1635/proj11/kinectRecord + * + * + * + */ + + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include "gstopenni2src.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + if (!gst_openni2src_plugin_init (plugin)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + openni2, + "GStreamer Openni2 Plugins", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/ext/openni2/gstopenni2src.cpp b/ext/openni2/gstopenni2src.cpp new file mode 100644 index 0000000000..33ad4d9551 --- /dev/null +++ b/ext/openni2/gstopenni2src.cpp @@ -0,0 +1,635 @@ +/* + * GStreamer OpenNI2 device source element + * Copyright (C) 2013 Miguel Casas-Sanchez + + * 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. + */ + +/** + * SECTION:element-openni2src + * + * + * Examples + * + * Some recorded .oni files are available at: + * + * http://people.cs.pitt.edu/~chang/1635/proj11/kinectRecord + * + * + * + LD_LIBRARY_PATH=/usr/lib/OpenNI2/Drivers/ gst-launch-1.0 --gst-debug=openni2src:5 openni2src location='Downloads/mr.oni' sourcetype=depth ! videoconvert ! ximagesink + * + * + LD_LIBRARY_PATH=/usr/lib/OpenNI2/Drivers/ gst-launch-1.0 --gst-debug=openni2src:5 openni2src location='Downloads/mr.oni' sourcetype=color ! videoconvert ! ximagesink + * + * + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstopenni2src.h" +\ +GST_DEBUG_CATEGORY_STATIC (openni2src_debug); +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw, " + "format = (string) {RGBA, RGB, GRAY16_LE} " + "framerate = (fraction) [0/1, MAX], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]") + ); + +static GstElementClass *parent_class = NULL; + +enum +{ + PROP_0, + PROP_LOCATION, + PROP_SOURCETYPE +}; +typedef enum +{ + SOURCETYPE_DEPTH, + SOURCETYPE_COLOR, + SOURCETYPE_BOTH +} GstOpenni2SourceType; +#define DEFAULT_SOURCETYPE SOURCETYPE_DEPTH + +#define SAMPLE_READ_WAIT_TIMEOUT 2000 /* 2000ms */ + +#define GST_TYPE_OPENNI2_SRC_SOURCETYPE (gst_openni2_src_sourcetype_get_type ()) +static GType +gst_openni2_src_sourcetype_get_type (void) +{ + static GType etype = 0; + if (etype == 0) { + static const GEnumValue values[] = { + {SOURCETYPE_DEPTH, "Get depth readings", "depth"}, + {SOURCETYPE_COLOR, "Get color readings", "color"}, + {SOURCETYPE_BOTH, "Get color and depth (as alpha) readings - EXPERIMENTAL", + "both"}, + {0, NULL, NULL}, + }; + etype = g_enum_register_static ("GstOpenni2SrcSourcetype", values); + } + return etype; +} + +/* GObject methods */ +static void gst_openni2_src_dispose (GObject * object); +static void gst_openni2_src_finalize (GObject * gobject); +static void gst_openni2_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_openni2_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +/* basesrc methods */ +static gboolean gst_openni2_src_start (GstBaseSrc * bsrc); +static gboolean gst_openni2_src_stop (GstBaseSrc * bsrc); +static GstCaps *gst_openni2_src_get_caps (GstBaseSrc * src, GstCaps * filter); + +/* element methods */ +static GstStateChangeReturn gst_openni2_src_change_state (GstElement * element, + GstStateChange transition); + +/* pushsrc method */ +static GstFlowReturn gst_openni2src_fill (GstPushSrc * src, + GstBuffer * buf); + +/* OpenNI2 interaction methods */ +static gboolean openni2_initialise_library (); +static GstFlowReturn openni2_initialise_devices (GstOpenni2Src * src); +static GstFlowReturn openni2_read_gstbuffer (GstOpenni2Src * src, + GstBuffer * buf); +static void openni2_finalise (GstOpenni2Src * src); + +G_DEFINE_TYPE (GstOpenni2Src, gst_openni2_src, GST_TYPE_PUSH_SRC) + +static void +gst_openni2_src_class_init (GstOpenni2SrcClass * klass) +{ + GObjectClass *gobject_class; + GstPushSrcClass *pushsrc_class; + GstBaseSrcClass *basesrc_class; + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + + gobject_class = (GObjectClass *) klass; + basesrc_class = (GstBaseSrcClass *) klass; + pushsrc_class = (GstPushSrcClass *) klass; + parent_class = (GstElementClass *) g_type_class_peek_parent (klass); + + gobject_class->dispose = gst_openni2_src_dispose; + gobject_class->finalize = gst_openni2_src_finalize; + gobject_class->set_property = gst_openni2_src_set_property; + gobject_class->get_property = gst_openni2_src_get_property; + g_object_class_install_property + (gobject_class, PROP_LOCATION, + g_param_spec_string ("location", "Location", + "Source uri, can be a file or a device.", "", + (GParamFlags) + (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_SOURCETYPE, + g_param_spec_enum ("sourcetype", + "Device source type", + "Type of readings to get from the source", + GST_TYPE_OPENNI2_SRC_SOURCETYPE, DEFAULT_SOURCETYPE, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + + basesrc_class->start = GST_DEBUG_FUNCPTR (gst_openni2_src_start); + basesrc_class->stop = GST_DEBUG_FUNCPTR (gst_openni2_src_stop); + basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_openni2_src_get_caps); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&srctemplate)); + + gst_element_class_set_static_metadata (element_class, "Openni2 client source", + "Source/Device", + "Extract readings from an OpenNI supported device (Kinect etc). ", + "Miguel Casas-Sanchez "); + + element_class->change_state = gst_openni2_src_change_state; + + pushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_openni2src_fill); + + GST_DEBUG_CATEGORY_INIT (openni2src_debug, "openni2src", 0, + "OpenNI2 Device Source"); + + /* OpenNI2 initialisation inside this function */ + openni2_initialise_library(); +} + +static void +gst_openni2_src_init (GstOpenni2Src * ni2src) +{ + gst_base_src_set_format (GST_BASE_SRC (ni2src), GST_FORMAT_BYTES); +} + +static void +gst_openni2_src_dispose (GObject * object) +{ + GstOpenni2Src *ni2src = GST_OPENNI2_SRC (object); + if (ni2src->gst_caps) + gst_caps_unref (ni2src->gst_caps); + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gst_openni2_src_finalize (GObject * gobject) +{ + GstOpenni2Src *ni2src = GST_OPENNI2_SRC (gobject); + + openni2_finalise (ni2src); + + if (ni2src->uri_name) { + g_free (ni2src->uri_name); + ni2src->uri_name = NULL; + } + if (ni2src->gst_caps) + gst_caps_unref(ni2src->gst_caps); + ni2src->gst_caps = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (gobject); +} + +static void +gst_openni2_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstOpenni2Src *openni2src = GST_OPENNI2_SRC (object); + + GST_OBJECT_LOCK (openni2src); + switch (prop_id) { + case PROP_LOCATION: + if (!g_value_get_string (value)) { + GST_WARNING ("location property cannot be NULL"); + break; + } + + if (openni2src->uri_name != NULL) { + g_free (openni2src->uri_name); + openni2src->uri_name = NULL; + } + openni2src->uri_name = g_value_dup_string (value); + /* Action! */ + openni2_initialise_devices (openni2src); + break; + case PROP_SOURCETYPE: + openni2src->sourcetype = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + + GST_OBJECT_UNLOCK (openni2src); +} + +static void +gst_openni2_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstOpenni2Src *openni2src = GST_OPENNI2_SRC (object); + + GST_OBJECT_LOCK (openni2src); + switch (prop_id) { + case PROP_LOCATION: + g_value_set_string (value, openni2src->uri_name); + break; + case PROP_SOURCETYPE: + g_value_set_enum (value, openni2src->sourcetype); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (openni2src); +} + +/* Interesting info from gstv4l2src.c: + * "start and stop are not symmetric -- start will open the device, but not + * start capture. it's setcaps that will start capture, which is called via + * basesrc's negotiate method. stop will both stop capture and close t device." + */ +static gboolean +gst_openni2_src_start (GstBaseSrc * bsrc) +{ + GstOpenni2Src *src = GST_OPENNI2_SRC (bsrc); + openni::Status rc = openni::STATUS_OK; + if (src->depth.isValid ()){ + rc = src->depth.start(); + if (rc != openni::STATUS_OK){ + GST_ERROR_OBJECT( src, "Couldn't start the depth stream\n%s\n", + openni::OpenNI::getExtendedError()); + return FALSE; + } + } + if (src->color.isValid ()){ + rc = src->color.start(); + if (rc != openni::STATUS_OK){ + GST_ERROR_OBJECT( src, "Couldn't start the color stream\n%s\n", + openni::OpenNI::getExtendedError()); + return FALSE; + } + } + return TRUE; +} + +static gboolean +gst_openni2_src_stop (GstBaseSrc * bsrc) +{ + GstOpenni2Src *src = GST_OPENNI2_SRC (bsrc); + + if (src->depth.isValid ()) + src->depth.stop(); + if (src->color.isValid ()) + src->color.stop(); + return TRUE; +} + +static GstCaps * +gst_openni2_src_get_caps (GstBaseSrc * src, GstCaps * filter) +{ + GstOpenni2Src *ni2src; + GstCaps *caps; + ni2src = GST_OPENNI2_SRC (src); + GST_OBJECT_LOCK (ni2src); + if (ni2src->gst_caps) { + GST_OBJECT_UNLOCK (ni2src); + return (filter) + ? gst_caps_intersect_full (filter, ni2src->gst_caps, GST_CAPS_INTERSECT_FIRST) + : gst_caps_ref (ni2src->gst_caps); + } + + // If we are here, we need to compose the caps and return them. + caps = gst_caps_new_empty(); + if (ni2src->colorpixfmt != openni::PIXEL_FORMAT_RGB888) + return caps; /* Uh oh, not RGB :? Not supported. */ + + if (ni2src->depth.isValid () && ni2src->color.isValid () && + ni2src->sourcetype == SOURCETYPE_BOTH) { + caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "RGBA", + "framerate", GST_TYPE_FRACTION, ni2src->fps, 1, + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + "width", G_TYPE_INT, ni2src->width, + "height", G_TYPE_INT, ni2src->height, + NULL); + } else if (ni2src->depth.isValid () && ni2src->sourcetype == SOURCETYPE_DEPTH) { + caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "GRAY16_LE", + "framerate", GST_TYPE_FRACTION, ni2src->fps, 1, + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + "width", G_TYPE_INT, ni2src->width, + "height", G_TYPE_INT, ni2src->height, + NULL); + } else if (ni2src->color.isValid () && ni2src->sourcetype == SOURCETYPE_COLOR) { + caps = gst_caps_new_simple ("video/x-raw", + "format", G_TYPE_STRING, "RGB", + "framerate", GST_TYPE_FRACTION, ni2src->fps, 1, + "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, + "width", G_TYPE_INT, ni2src->width, + "height", G_TYPE_INT, ni2src->height, + NULL); + } + GST_INFO_OBJECT (ni2src, "probed caps: %" GST_PTR_FORMAT, caps); + ni2src->gst_caps = gst_caps_ref(caps); + GST_OBJECT_UNLOCK (ni2src); + return (filter) + ? gst_caps_intersect_full (filter, ni2src->gst_caps, GST_CAPS_INTERSECT_FIRST) + : gst_caps_ref (ni2src->gst_caps); +} + +static GstStateChangeReturn +gst_openni2_src_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_FAILURE; + GstOpenni2Src *src = GST_OPENNI2_SRC (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + if (!src->uri_name) { + GST_ERROR_OBJECT (src, "Invalid location"); + return ret; + } + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) { + return ret; + } + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + gst_openni2_src_stop(GST_BASE_SRC(src)); + if (src->gst_caps) { + gst_caps_unref (src->gst_caps); + src->gst_caps = NULL; + } + break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + default: + break; + } + + return ret; +} + + +static GstFlowReturn +gst_openni2src_fill (GstPushSrc * src, GstBuffer * buf) +{ + GstOpenni2Src *ni2src = GST_OPENNI2_SRC (src); + return openni2_read_gstbuffer (ni2src, buf); +} + +gboolean +gst_openni2src_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "openni2src", GST_RANK_NONE, + GST_TYPE_OPENNI2_SRC); +} + + +static gboolean +openni2_initialise_library () +{ + openni::Status rc = openni::STATUS_OK; + rc = openni::OpenNI::initialize (); + if (rc != openni::STATUS_OK) { + GST_ERROR("Initialization failed: %s", openni::OpenNI::getExtendedError ()); + openni::OpenNI::shutdown (); + return GST_FLOW_ERROR; + } + return (rc == openni::STATUS_OK); +} + +GstFlowReturn +openni2_initialise_devices (GstOpenni2Src * src) +{ + openni::Status rc = openni::STATUS_OK; + const char *deviceURI = openni::ANY_DEVICE; + if (src->uri_name) + deviceURI = src->uri_name; + + /** OpenNI2 open device or file **/ + rc = src->device.open (deviceURI); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "Device (%s) open failed: %s", deviceURI, + openni::OpenNI::getExtendedError ()); + openni::OpenNI::shutdown (); + return GST_FLOW_ERROR; + } + + /** depth sensor **/ + rc = src->depth.create (src->device, openni::SENSOR_DEPTH); + if (rc == openni::STATUS_OK) { + rc = src->depth.start (); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "%s", openni::OpenNI::getExtendedError ()); + src->depth.destroy (); + } + } else + GST_WARNING_OBJECT (src, "Couldn't find depth stream: %s", + openni::OpenNI::getExtendedError ()); + + /** color sensor **/ + rc = src->color.create (src->device, openni::SENSOR_COLOR); + if (rc == openni::STATUS_OK) { + rc = src->color.start (); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "Couldn't start color stream: %s ", + openni::OpenNI::getExtendedError ()); + src->color.destroy (); + } + } else + GST_WARNING_OBJECT (src, "Couldn't find color stream: %s", + openni::OpenNI::getExtendedError ()); + + + if (!src->depth.isValid () && !src->color.isValid ()) { + GST_ERROR_OBJECT (src, "No valid streams. Exiting\n"); + openni::OpenNI::shutdown (); + return GST_FLOW_ERROR; + } + + /** Get resolution and make sure is valid **/ + if (src->depth.isValid () && src->color.isValid ()) { + src->depthVideoMode = src->depth.getVideoMode (); + src->colorVideoMode = src->color.getVideoMode (); + + int depthWidth = src->depthVideoMode.getResolutionX (); + int depthHeight = src->depthVideoMode.getResolutionY (); + int colorWidth = src->colorVideoMode.getResolutionX (); + int colorHeight = src->colorVideoMode.getResolutionY (); + + if (depthWidth == colorWidth && depthHeight == colorHeight) { + src->width = depthWidth; + src->height = depthHeight; + src->fps = src->depthVideoMode.getFps(); + src->colorpixfmt = src->colorVideoMode.getPixelFormat(); + src->depthpixfmt = src->depthVideoMode.getPixelFormat(); + } else { + GST_ERROR_OBJECT (src, "Error - expect color and depth to be" + " in same resolution: D: %dx%d vs C: %dx%d", + depthWidth, depthHeight, colorWidth, colorHeight); + return GST_FLOW_ERROR; + } + GST_INFO_OBJECT (src, "DEPTH&COLOR resolution: %dx%d", + src->width, src->height); + } else if (src->depth.isValid ()) { + src->depthVideoMode = src->depth.getVideoMode (); + src->width = src->depthVideoMode.getResolutionX (); + src->height = src->depthVideoMode.getResolutionY (); + src->fps = src->depthVideoMode.getFps(); + src->depthpixfmt = src->depthVideoMode.getPixelFormat(); + GST_INFO_OBJECT (src, "DEPTH resolution: %dx%d", src->width, src->height); + } else if (src->color.isValid ()) { + src->colorVideoMode = src->color.getVideoMode (); + src->width = src->colorVideoMode.getResolutionX (); + src->height = src->colorVideoMode.getResolutionY (); + src->fps = src->colorVideoMode.getFps(); + src->colorpixfmt = src->colorVideoMode.getPixelFormat(); + GST_INFO_OBJECT (src, "COLOR resolution: %dx%d", src->width, src->height); + } else { + GST_ERROR_OBJECT (src, "Expected at least one of the streams to be valid."); + return GST_FLOW_ERROR; + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +openni2_read_gstbuffer (GstOpenni2Src * src, GstBuffer * buf) +{ + openni::Status rc = openni::STATUS_OK; + openni::VideoStream * pStream = &(src->depth); + int changedStreamDummy; + + /* Block until we get some data */ + rc = openni::OpenNI::waitForAnyStream (&pStream, 1, &changedStreamDummy, + SAMPLE_READ_WAIT_TIMEOUT); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "Frame read timeout: %s", + openni::OpenNI::getExtendedError ()); + return GST_FLOW_ERROR; + } + + GstMapInfo info; + if (src->depth.isValid () && src->color.isValid () && + src->sourcetype == SOURCETYPE_BOTH) { + rc = src->depth.readFrame (&src->depthFrame); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "Frame read error: %s", + openni::OpenNI::getExtendedError ()); + return GST_FLOW_ERROR; + } + rc = src->color.readFrame (&src->colorFrame); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "Frame read error: %s", + openni::OpenNI::getExtendedError ()); + return GST_FLOW_ERROR; + } + if ((src->colorFrame.getStrideInBytes() != src->colorFrame.getWidth()) || + (src->depthFrame.getStrideInBytes() != 2*src->depthFrame.getWidth())) { + // This case is not handled - yet :B + GST_ERROR_OBJECT(src, "Stride does not coincide with width"); + return GST_FLOW_ERROR; + } + + int framesize = src->colorFrame.getDataSize() + src->depthFrame.getDataSize()/2; + buf = gst_buffer_new_and_alloc(framesize); + /* Copy colour information */ + gst_buffer_map(buf, &info, (GstMapFlags)(GST_MAP_WRITE)); + memcpy(info.data, src->colorFrame.getData(), src->colorFrame.getDataSize()); + guint8* pData = info.data + src->colorFrame.getDataSize(); + /* Add depth as 8bit alpha channel, depth is 16bit samples. */ + guint16* pDepth = (guint16*) src->depthFrame.getData(); + for( int i=0; i < src->depthFrame.getDataSize()/2; ++i) + pData[i] = pDepth[i] >> 8; + GST_WARNING_OBJECT (src, "sending buffer (%d+%d)B [%08llu]", + src->colorFrame.getDataSize(), + src->depthFrame.getDataSize (), + (long long) src->depthFrame.getTimestamp ()); + gst_buffer_unmap(buf, &info); + } else if (src->depth.isValid () && src->sourcetype == SOURCETYPE_DEPTH) { + rc = src->depth.readFrame (&src->depthFrame); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "Frame read error: %s", + openni::OpenNI::getExtendedError ()); + return GST_FLOW_ERROR; + } + if (src->depthFrame.getStrideInBytes() != 2*src->depthFrame.getWidth()) { + // This case is not handled - yet :B + GST_ERROR_OBJECT(src, "Stride does not coincide with width"); + return GST_FLOW_ERROR; + } + + int framesize = src->depthFrame.getDataSize(); + buf = gst_buffer_new_and_alloc(framesize); + gst_buffer_map(buf, &info, (GstMapFlags)(GST_MAP_WRITE)); + memcpy(info.data, src->depthFrame.getData(), framesize); + GST_BUFFER_PTS(buf) = src->depthFrame.getTimestamp() * 1000; + GST_WARNING_OBJECT (src, "sending buffer (%dx%d)=%dB [%08llu]", + src->depthFrame.getWidth (), + src->depthFrame.getHeight (), + src->depthFrame.getDataSize (), + (long long) src->depthFrame.getTimestamp ()); + gst_buffer_unmap(buf, &info); + } else if (src->color.isValid () && src->sourcetype == SOURCETYPE_COLOR) { + rc = src->color.readFrame (&src->colorFrame); + if (rc != openni::STATUS_OK) { + GST_ERROR_OBJECT (src, "Frame read error: %s", + openni::OpenNI::getExtendedError ()); + return GST_FLOW_ERROR; + } + if (src->colorFrame.getStrideInBytes() != src->colorFrame.getWidth()) { + // This case is not handled - yet :B + GST_ERROR_OBJECT(src, "Stride does not coincide with width"); + return GST_FLOW_ERROR; + } + int framesize = src->colorFrame.getDataSize(); + buf = gst_buffer_new_and_alloc(framesize); + gst_buffer_map(buf, &info, (GstMapFlags)(GST_MAP_WRITE)); + memcpy(info.data, src->depthFrame.getData(), framesize); + GST_BUFFER_PTS(buf) = src->colorFrame.getTimestamp() * 1000; + GST_WARNING_OBJECT (src, "sending buffer (%dx%d)=%dB [%08llu]", + src->colorFrame.getWidth (), + src->colorFrame.getHeight (), + src->colorFrame.getDataSize (), + (long long) src->colorFrame.getTimestamp ()); + gst_buffer_unmap(buf, &info); + } + return GST_FLOW_OK; +} + +static void +openni2_finalise (GstOpenni2Src * src) +{ + src->depth.destroy(); + src->color.destroy(); + openni::OpenNI::shutdown (); +} diff --git a/ext/openni2/gstopenni2src.h b/ext/openni2/gstopenni2src.h new file mode 100644 index 0000000000..f3fd57baec --- /dev/null +++ b/ext/openni2/gstopenni2src.h @@ -0,0 +1,78 @@ +/* + * GStreamer + * Copyright (C) 2013 Miguel Casas-Sanchez + * + * 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. + */ + +#ifndef __GST_OPENNI2_SRC_H__ +#define __GST_OPENNI2_SRC_H__ + +#include +#include +#include + +#include +#include +#include + +G_BEGIN_DECLS +#define GST_TYPE_OPENNI2_SRC \ + (gst_openni2_src_get_type()) +#define GST_OPENNI2_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_OPENNI2_SRC,GstOpenni2Src)) +#define GST_OPENNI2_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_OPENNI2_SRC,GstOpenni2SrcClass)) +#define GST_IS_OPENNI2_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_OPENNI2_SRC)) +#define GST_IS_OPENNI2_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_OPENNI2_SRC)) +typedef struct _GstOpenni2Src GstOpenni2Src; +typedef struct _GstOpenni2SrcClass GstOpenni2SrcClass; + +typedef enum +{ + GST_OPENNI2_SRC_FILE_TRANSFER, + GST_OPENNI2_SRC_NEXT_PROGRAM_CHAIN, + GST_OPENNI2_SRC_INVALID_DATA +} GstOpenni2State; + +struct _GstOpenni2Src +{ + GstPushSrc element; + + GstOpenni2State state; + gchar *uri_name; + gint sourcetype; + GstCaps *gst_caps; + + /* OpenNI2 variables */ + openni::Device device; + openni::VideoStream depth, color; + openni::VideoMode depthVideoMode, colorVideoMode; + openni::PixelFormat depthpixfmt, colorpixfmt; + int width, height, fps; + openni::VideoFrameRef depthFrame, colorFrame; + +}; + +struct _GstOpenni2SrcClass +{ + GstPushSrcClass parent_class; +}; + +GType gst_openni2_src_get_type (void); +gboolean gst_openni2src_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_OPENNI2_SRC_H__ */