openni: Add OpenNNI2 plugin
authorMiguel Casas-Sanchez <miguelecasassanchez@gmail.com>
Mon, 2 Dec 2013 10:17:02 +0000 (11:17 +0100)
committerSebastian Dröge <sebastian@centricular.com>
Mon, 2 Dec 2013 10:17:02 +0000 (11:17 +0100)
https://bugzilla.gnome.org/show_bug.cgi?id=708914

configure.ac
docs/plugins/Makefile.am
ext/Makefile.am
ext/openni2/Makefile.am [new file with mode: 0644]
ext/openni2/gstopenni2.cpp [new file with mode: 0644]
ext/openni2/gstopenni2src.cpp [new file with mode: 0644]
ext/openni2/gstopenni2src.h [new file with mode: 0644]

index 003c17c..1da689f 100644 (file)
@@ -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
index 833ffdd..8758cf3 100644 (file)
@@ -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 \
index bfae3d1..951d2d5 100644 (file)
@@ -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 (file)
index 0000000..f42477e
--- /dev/null
@@ -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 (file)
index 0000000..ee396cd
--- /dev/null
@@ -0,0 +1,67 @@
+/* GStreamer
+ * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
+ *
+ * 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.
+ *
+ * <refsect2>
+ * <title>Examples</title>
+ * <para>
+ * Some recorded .oni files are available at:
+ * <programlisting>
+ *  http://people.cs.pitt.edu/~chang/1635/proj11/kinectRecord
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <gst/gst.h>
+#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 (file)
index 0000000..33ad4d9
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * GStreamer OpenNI2 device source element
+ * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
+
+ * 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
+ *
+ * <refsect2>
+ * <title>Examples</title>
+ * <para>
+ * Some recorded .oni files are available at:
+ * <programlisting>
+ *  http://people.cs.pitt.edu/~chang/1635/proj11/kinectRecord
+ * </programlisting>
+ *
+ * <programlisting>
+  LD_LIBRARY_PATH=/usr/lib/OpenNI2/Drivers/ gst-launch-1.0 --gst-debug=openni2src:5   openni2src location='Downloads/mr.oni' sourcetype=depth ! videoconvert ! ximagesink
+ * </programlisting>
+ * <programlisting>
+  LD_LIBRARY_PATH=/usr/lib/OpenNI2/Drivers/ gst-launch-1.0 --gst-debug=openni2src:5   openni2src location='Downloads/mr.oni' sourcetype=color ! videoconvert ! ximagesink
+ * </programlisting>
+ * </para>
+ * </refsect2>
+ */
+
+#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 <miguelecasassanchez@gmail.com>");
+
+  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 (file)
index 0000000..f3fd57b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * GStreamer
+ * Copyright (C) 2013 Miguel Casas-Sanchez <miguelecasassanchez@gmail.com>
+ *
+ * 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 <gst/gst.h>
+#include <stdio.h>
+#include <OpenNI.h>
+
+#include <gst/base/gstbasesrc.h>
+#include <gst/base/gstpushsrc.h>
+#include <gst/video/video.h>
+
+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__ */