From 995bcca23bafd4cff2a7c1e789e50aa262698284 Mon Sep 17 00:00:00 2001 From: "Joshua M. Doe" Date: Thu, 31 Oct 2019 10:53:23 -0400 Subject: [PATCH] pleorasink: add GigE Vision sink for eBUS GEV Tx API Most basic working implementation --- sys/pleora/CMakeLists.txt | 11 +- sys/pleora/gstpleora.cpp | 35 ++ sys/pleora/gstpleorasink.cpp | 522 ++++++++++++++++++++++++++ sys/pleora/gstpleorasink.h | 74 ++++ sys/pleora/gstpleorasrc.cpp | 24 +- sys/pleora/streamingchannelsource.cpp | 205 ++++++++++ sys/pleora/streamingchannelsource.h | 53 +++ 7 files changed, 901 insertions(+), 23 deletions(-) create mode 100644 sys/pleora/gstpleora.cpp create mode 100644 sys/pleora/gstpleorasink.cpp create mode 100644 sys/pleora/gstpleorasink.h create mode 100644 sys/pleora/streamingchannelsource.cpp create mode 100644 sys/pleora/streamingchannelsource.h diff --git a/sys/pleora/CMakeLists.txt b/sys/pleora/CMakeLists.txt index 5d25c48..274aba8 100644 --- a/sys/pleora/CMakeLists.txt +++ b/sys/pleora/CMakeLists.txt @@ -1,10 +1,15 @@ add_definitions(-D_XKEYCHECK_H) set (SOURCES - gstpleorasrc.cpp) + gstpleora.cpp + gstpleorasink.cpp + gstpleorasrc.cpp + streamingchannelsource.cpp) set (HEADERS - gstpleorasrc.h) + gstpleorasink.h + gstpleorasrc.h + streamingchannelsource.h) include_directories (AFTER ${PLEORA_INCLUDE_DIR}) @@ -27,4 +32,4 @@ target_link_libraries (${libname} set (pdbfile "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}/${libname}.pdb") install (FILES ${pdbfile} DESTINATION lib/gstreamer-1.0 COMPONENT pdb) install(TARGETS ${libname} - LIBRARY DESTINATION lib/gstreamer-1.0) \ No newline at end of file + LIBRARY DESTINATION lib/gstreamer-1.0) diff --git a/sys/pleora/gstpleora.cpp b/sys/pleora/gstpleora.cpp new file mode 100644 index 0000000..2e3466e --- /dev/null +++ b/sys/pleora/gstpleora.cpp @@ -0,0 +1,35 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstpleorasink.h" +#include "gstpleorasrc.h" + +#define GST_CAT_DEFAULT gst_pleora_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static gboolean +plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "pleora", 0, + "debug category for pleora plugin"); + + if (!gst_element_register (plugin, "pleorasink", GST_RANK_NONE, + gst_pleorasink_get_type ())) { + return FALSE; + } + + if (!gst_element_register (plugin, "pleorasrc", GST_RANK_NONE, + gst_pleorasrc_get_type ())) { + return FALSE; + } + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + pleora, + "Pleora eBUS video sink/source", + plugin_init, GST_PACKAGE_VERSION, GST_PACKAGE_LICENSE, GST_PACKAGE_NAME, + GST_PACKAGE_ORIGIN) diff --git a/sys/pleora/gstpleorasink.cpp b/sys/pleora/gstpleorasink.cpp new file mode 100644 index 0000000..3c0658d --- /dev/null +++ b/sys/pleora/gstpleorasink.cpp @@ -0,0 +1,522 @@ +/* GStreamer + * Copyright (C) 2011 FIXME + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ +/** + * SECTION:element-gstpleorasink + * + * The pleorasink element is a sink for Pleora eBUS SDK to output a GigE Vision video stream. + * + * + * Example launch line + * |[ + * gst-launch -v videotestsrc ! pleorasink + * ]| + * Outputs test pattern using Pleora eBUS SDK GigE Vision Tx. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include + +#include "gstpleorasink.h" +#include "streamingchannelsource.h" + + +/* GObject prototypes */ +static void gst_pleorasink_set_property (GObject * object, + guint property_id, const GValue * value, GParamSpec * pspec); +static void gst_pleorasink_get_property (GObject * object, + guint property_id, GValue * value, GParamSpec * pspec); +static void gst_pleorasink_dispose (GObject * object); + +/* GstBaseSink prototypes */ +static gboolean gst_pleorasink_start (GstBaseSink * basesink); +static gboolean gst_pleorasink_stop (GstBaseSink * basesink); +static GstCaps *gst_pleorasink_get_caps (GstBaseSink * basesink, + GstCaps * filter_caps); +static gboolean gst_pleorasink_set_caps (GstBaseSink * basesink, + GstCaps * caps); +static GstFlowReturn gst_pleorasink_render (GstBaseSink * basesink, + GstBuffer * buffer); +static gboolean gst_pleorasink_unlock (GstBaseSink * basesink); +static gboolean gst_pleorasink_unlock_stop (GstBaseSink * basesink); + +enum +{ + PROP_0, + PROP_NUM_INTERNAL_BUFFERS, + PROP_ADDRESS, + PROP_MANUFACTURER, + PROP_MODEL, + PROP_VERSION, + PROP_INFO, + PROP_SERIAL, + PROP_MAC +}; + +#define DEFAULT_PROP_NUM_INTERNAL_BUFFERS 3 +#define DEFAULT_PROP_ADDRESS "" +#define DEFAULT_PROP_MANUFACTURER "Pleora" +#define DEFAULT_PROP_MODEL "eBUS GStreamer" +#define DEFAULT_PROP_VERSION "0.1" +#define DEFAULT_PROP_INFO "Pleora eBUS GStreamer Sink" +#define DEFAULT_PROP_SERIAL "0001" +#define DEFAULT_PROP_MAC "" + +/* pad templates */ + +static GstStaticPadTemplate gst_pleorasink_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE + ("{ GRAY8, GRAY16_LE, RGB, RGBA, BGR, BGRA }")) + ); + +/* class initialization */ + +/* setup debug */ +GST_DEBUG_CATEGORY_STATIC (pleorasink_debug); +#define GST_CAT_DEFAULT pleorasink_debug + +G_DEFINE_TYPE (GstPleoraSink, gst_pleorasink, GST_TYPE_BASE_SINK); + +static void +gst_pleorasink_class_init (GstPleoraSinkClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "pleorasink", 0, + "Pleora eBUS SDK sink"); + + gobject_class->set_property = gst_pleorasink_set_property; + gobject_class->get_property = gst_pleorasink_get_property; + gobject_class->dispose = gst_pleorasink_dispose; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_pleorasink_sink_template)); + + gst_element_class_set_details_simple (gstelement_class, + "Pleora eBUS GEV Tx Sink", "Sink/Video", + "Pleora eBUS SDK sink to output GigE Vision video", + "Joshua M. Doe "); + + gstbasesink_class->start = GST_DEBUG_FUNCPTR (gst_pleorasink_start); + gstbasesink_class->stop = GST_DEBUG_FUNCPTR (gst_pleorasink_stop); + gstbasesink_class->set_caps = GST_DEBUG_FUNCPTR (gst_pleorasink_set_caps); + gstbasesink_class->render = GST_DEBUG_FUNCPTR (gst_pleorasink_render); + gstbasesink_class->unlock = GST_DEBUG_FUNCPTR (gst_pleorasink_unlock); + gstbasesink_class->unlock_stop = + GST_DEBUG_FUNCPTR (gst_pleorasink_unlock_stop); + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_NUM_INTERNAL_BUFFERS, g_param_spec_int ("num-internal-buffers", + "Number of internal buffers", + "Number of buffers for the internal queue", 0, 64, + DEFAULT_PROP_NUM_INTERNAL_BUFFERS, + (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); + g_object_class_install_property (gobject_class, PROP_ADDRESS, + g_param_spec_string ("address", "IP address", + "The IP address of the network interface to bind to (default is first found)", + DEFAULT_PROP_ADDRESS, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + + g_object_class_install_property (gobject_class, PROP_MANUFACTURER, + g_param_spec_string ("manufacturer", "Manufacturer", + "Manufacturer of the virtual camera", + DEFAULT_PROP_MANUFACTURER, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + g_object_class_install_property (gobject_class, PROP_MODEL, + g_param_spec_string ("model", "Model", + "Model of the virtual camera", + DEFAULT_PROP_MODEL, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + g_object_class_install_property (gobject_class, PROP_VERSION, + g_param_spec_string ("version", "Version", + "Version of the virtual camera", + DEFAULT_PROP_VERSION, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + g_object_class_install_property (gobject_class, PROP_INFO, + g_param_spec_string ("info", "Info", + "Info of the virtual camera", + DEFAULT_PROP_INFO, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + g_object_class_install_property (gobject_class, PROP_SERIAL, + g_param_spec_string ("serial", "Serial", + "Serial of the virtual camera", + DEFAULT_PROP_SERIAL, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); + g_object_class_install_property (gobject_class, PROP_MAC, + g_param_spec_string ("mac", "MAC address", + "MAC address of the network interface to bind to (default is first found)", + DEFAULT_PROP_MAC, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | + GST_PARAM_MUTABLE_READY))); +} + +static void +gst_pleorasink_init (GstPleoraSink * sink) +{ + /* properties */ + sink->num_internal_buffers = DEFAULT_PROP_NUM_INTERNAL_BUFFERS; + sink->address = g_strdup (DEFAULT_PROP_ADDRESS); + sink->manufacturer = g_strdup (DEFAULT_PROP_MANUFACTURER); + sink->model = g_strdup (DEFAULT_PROP_MODEL); + sink->version = g_strdup (DEFAULT_PROP_VERSION); + sink->info = g_strdup (DEFAULT_PROP_INFO); + sink->serial = g_strdup (DEFAULT_PROP_SERIAL); + sink->mac = g_strdup (DEFAULT_PROP_MAC); + + sink->camera_connected = FALSE; + + sink->acquisition_started = FALSE; + sink->stop_requested = FALSE; + + sink->source = new GstStreamingChannelSource (); + sink->source->SetSink (sink); + sink->device = new PvSoftDeviceGEV (); + + g_mutex_init (&sink->mutex); + g_cond_init (&sink->cond); +} + +void +gst_pleorasink_set_property (GObject * object, guint property_id, + const GValue * value, GParamSpec * pspec) +{ + GstPleoraSink *sink; + + g_return_if_fail (GST_IS_PLEORASINK (object)); + sink = GST_PLEORASINK (object); + + switch (property_id) { + case PROP_NUM_INTERNAL_BUFFERS: + sink->num_internal_buffers = g_value_get_int (value); + break; + case PROP_ADDRESS: + g_free (sink->address); + sink->address = g_strdup (g_value_get_string (value)); + break; + case PROP_MANUFACTURER: + g_free (sink->manufacturer); + sink->manufacturer = g_strdup (g_value_get_string (value)); + break; + case PROP_MODEL: + g_free (sink->model); + sink->model = g_strdup (g_value_get_string (value)); + break; + case PROP_VERSION: + g_free (sink->version); + sink->version = g_strdup (g_value_get_string (value)); + break; + case PROP_INFO: + g_free (sink->info); + sink->info = g_strdup (g_value_get_string (value)); + break; + case PROP_SERIAL: + g_free (sink->serial); + sink->serial = g_strdup (g_value_get_string (value)); + break; + case PROP_MAC: + g_free (sink->mac); + sink->mac = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +gst_pleorasink_get_property (GObject * object, guint property_id, + GValue * value, GParamSpec * pspec) +{ + GstPleoraSink *sink; + + g_return_if_fail (GST_IS_PLEORASINK (object)); + sink = GST_PLEORASINK (object); + + switch (property_id) { + case PROP_NUM_INTERNAL_BUFFERS: + g_value_set_int (value, sink->num_internal_buffers); + break; + case PROP_ADDRESS: + g_value_set_string (value, sink->address); + break; + case PROP_MANUFACTURER: + g_value_set_string (value, sink->manufacturer); + break; + case PROP_MODEL: + g_value_set_string (value, sink->model); + break; + case PROP_VERSION: + g_value_set_string (value, sink->version); + break; + case PROP_INFO: + g_value_set_string (value, sink->info); + break; + case PROP_SERIAL: + g_value_set_string (value, sink->serial); + break; + case PROP_MAC: + g_value_set_string (value, sink->mac); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +void +gst_pleorasink_dispose (GObject * object) +{ + GstPleoraSink *sink; + + g_return_if_fail (GST_IS_PLEORASINK (object)); + sink = GST_PLEORASINK (object); + + /* clean up as possible. may be called multiple times */ + if (sink->device) { + delete sink->device; + sink->device = NULL; + } + if (sink->source) { + delete sink->source; + sink->source = NULL; + } + + g_mutex_clear (&sink->mutex); + g_cond_clear (&sink->cond); + + g_free (sink->address); + + G_OBJECT_CLASS (gst_pleorasink_parent_class)->dispose (object); +} + +gboolean +gst_pleorasink_select_interface (GstPleoraSink * sink) +{ + PvSystem lSystem; + const PvNetworkAdapter *selected_nic = NULL; + gchar *desired_mac = NULL; + gchar *found_mac = NULL; + + /* we'll compare uppercase version of MAC */ + desired_mac = g_ascii_strup (sink->mac, -1); + + for (guint32 i = 0; i < lSystem.GetInterfaceCount (); i++) { + const PvNetworkAdapter *lNIC = NULL; + lNIC = dynamic_cast < const PvNetworkAdapter *>(lSystem.GetInterface (i)); + if ((lNIC == NULL) || + (lNIC->GetIPAddressCount () == 0) || + (lNIC->GetIPAddress (0) == "0.0.0.0")) { + continue; + } + + /* we'll compare uppercase version of MAC */ + found_mac = g_ascii_strup (lNIC->GetMACAddress ().GetAscii (), -1); + if (g_strcmp0 (sink->mac, "") == 0 && g_strcmp0 (sink->address, "") == 0) { + /* no MAC or IP set, use first found */ + GST_DEBUG_OBJECT (sink, "Selecting first interface we found"); + selected_nic = lNIC; + + /* set properties */ + g_free (sink->mac); + sink->mac = g_strdup (lNIC->GetMACAddress ().GetAscii ()); + g_free (sink->address); + sink->address = g_strdup (lNIC->GetIPAddress (0).GetAscii ()); + } else if (g_strcmp0 (desired_mac, found_mac) == 0) { + GST_DEBUG_OBJECT (sink, "Selecting interface from MAC '%s'", sink->mac); + selected_nic = lNIC; + + /* set properties */ + g_free (sink->address); + sink->address = g_strdup (lNIC->GetIPAddress (0).GetAscii ()); + } else if (g_strcmp0 (sink->address, + lNIC->GetIPAddress (0).GetAscii ()) == 0) { + GST_DEBUG_OBJECT (sink, "Selecting interface from IP '%s'", + sink->address); + selected_nic = lNIC; + + /* set properties */ + g_free (sink->mac); + sink->mac = g_strdup (lNIC->GetMACAddress ().GetAscii ()); + } + g_free (found_mac); + + if (selected_nic) { + break; + } + } + g_free (desired_mac); + + if (selected_nic == NULL) { + if (g_strcmp0 (sink->mac, "") != 0) { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + ("Failed to find network interface by MAC address '%s'", sink->mac), + (NULL)); + } else if (g_strcmp0 (sink->address, "") != 0) { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + ("Failed to find network interface by IP address '%s'", + sink->address), (NULL)); + } else { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + ("Failed to find any network interfaces"), (NULL)); + } + return FALSE; + } else { + GST_DEBUG_OBJECT (sink, + "Selecting network interface '%s', MAC: %s, IP: %s, Subnet: %s", + selected_nic->GetDescription ().GetAscii (), + selected_nic->GetMACAddress ().GetAscii (), + selected_nic->GetIPAddress (0).GetAscii (), + selected_nic->GetSubnetMask (0).GetAscii ()); + return TRUE; + } +} + +gboolean +gst_pleorasink_start (GstBaseSink * basesink) +{ + GstPleoraSink *sink = GST_PLEORASINK (basesink); + + IPvSoftDeviceGEVInfo *info = sink->device->GetInfo (); + if (info) { + info->SetManufacturerName (sink->manufacturer); + info->SetModelName (sink->model); + info->SetDeviceVersion (sink->version); + info->SetManufacturerInformation (sink->info); + info->SetSerialNumber (sink->serial); + } + + return TRUE; +} + +gboolean +gst_pleorasink_stop (GstBaseSink * basesink) +{ + GstPleoraSink *sink = GST_PLEORASINK (basesink); + + sink->device->Stop (); + + sink->camera_connected = FALSE; + + sink->acquisition_started = FALSE; + sink->stop_requested = FALSE; + + return TRUE; +} + +gboolean +gst_pleorasink_set_caps (GstBaseSink * basesink, GstCaps * caps) +{ + GstPleoraSink *sink = GST_PLEORASINK (basesink); + PvResult pvRes; + + GST_DEBUG_OBJECT (sink, "Caps being set"); + + gst_video_info_from_caps (&sink->vinfo, caps); + + sink->source->SetCaps (caps); + + GST_DEBUG_OBJECT (sink, "Adding stream to device"); + pvRes = sink->device->AddStream (sink->source); + if (!pvRes.IsOK ()) { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + ("Failed to add stream to device (%s)", + pvRes.GetDescription ().GetAscii ()), (NULL)); + return FALSE; + } + + GST_DEBUG_OBJECT (sink, "Searching for interface"); + if (!gst_pleorasink_select_interface (sink)) { + /* error already sent */ + return FALSE; + } + + GST_DEBUG_OBJECT (sink, "Starting device"); + pvRes = sink->device->Start (sink->mac); + if (!pvRes.IsOK ()) { + GST_ELEMENT_ERROR (sink, RESOURCE, SETTINGS, + ("Failed to start device (%s)", + pvRes.GetDescription ().GetAscii ()), (NULL)); + return FALSE; + } + + sink->acquisition_started = TRUE; + sink->stop_requested = FALSE; + + return TRUE; +} + +GstFlowReturn +gst_pleorasink_render (GstBaseSink * basesink, GstBuffer * buffer) +{ + GstPleoraSink *sink = GST_PLEORASINK (basesink); + GST_LOG_OBJECT (sink, "Rendering buffer"); + + if (sink->stop_requested) { + GST_DEBUG_OBJECT (sink, "stop requested, flushing"); + return GST_FLOW_FLUSHING; + } + + /* TODO: should we ever error out? */ + sink->source->SetBuffer (buffer); + + return GST_FLOW_OK; +} + +gboolean +gst_pleorasink_unlock (GstBaseSink * basesink) +{ + GstPleoraSink *sink = GST_PLEORASINK (basesink); + + g_mutex_lock (&sink->mutex); + sink->stop_requested = TRUE; + g_cond_signal (&sink->cond); + g_mutex_unlock (&sink->mutex); + + return TRUE; +} + +gboolean +gst_pleorasink_unlock_stop (GstBaseSink * basesink) +{ + GstPleoraSink *sink = GST_PLEORASINK (basesink); + + sink->stop_requested = FALSE; + + return TRUE; +} diff --git a/sys/pleora/gstpleorasink.h b/sys/pleora/gstpleorasink.h new file mode 100644 index 0000000..94821ad --- /dev/null +++ b/sys/pleora/gstpleorasink.h @@ -0,0 +1,74 @@ +/* GStreamer + * Copyright (C) 2011 FIXME + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GST_PLEORASINK_H_ +#define _GST_PLEORASINK_H_ + +#include +#include + +class GstStreamingChannelSource; +class PvSoftDeviceGEV; + +G_BEGIN_DECLS + +#define GST_TYPE_PLEORASINK (gst_pleorasink_get_type()) +#define GST_PLEORASINK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLEORASINK,GstPleoraSink)) +#define GST_PLEORASINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLEORASINK,GstPleoraSinkClass)) +#define GST_IS_PLEORASINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLEORASINK)) +#define GST_IS_PLEORASINK_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLEORASINK)) + +typedef struct _GstPleoraSink GstPleoraSink; +typedef struct _GstPleoraSinkClass GstPleoraSinkClass; + +struct _GstPleoraSink +{ + GstBaseSink base; + + GstStreamingChannelSource *source; + PvSoftDeviceGEV *device; + + gint num_internal_buffers; + gchar *address; + gchar *manufacturer; + gchar *model; + gchar *version; + gchar *info; + gchar *serial; + gchar *mac; + + gboolean camera_connected; + GstVideoInfo vinfo; + + GMutex mutex; + GCond cond; + gboolean acquisition_started; + gboolean stop_requested; +}; + +struct _GstPleoraSinkClass +{ + GstBaseSinkClass base_class; +}; + +GType gst_pleorasink_get_type (void); + +G_END_DECLS + +#endif /* _GST_PLEORASINK_H_ */ diff --git a/sys/pleora/gstpleorasrc.cpp b/sys/pleora/gstpleorasrc.cpp index 9732623..480fba0 100644 --- a/sys/pleora/gstpleorasrc.cpp +++ b/sys/pleora/gstpleorasrc.cpp @@ -138,6 +138,9 @@ gst_pleorasrc_class_init (GstPleoraSrcClass * klass) GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass); + GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "pleorasrc", 0, + "Pleora eBUS SDK source"); + gobject_class->set_property = gst_pleorasrc_set_property; gobject_class->get_property = gst_pleorasrc_get_property; gobject_class->dispose = gst_pleorasrc_dispose; @@ -1665,23 +1668,4 @@ gst_pleorasrc_create (GstPushSrc * psrc, GstBuffer ** buf) } return GST_FLOW_OK; -} - - -static gboolean -plugin_init (GstPlugin * plugin) -{ - GST_DEBUG_CATEGORY_INIT (gst_pleorasrc_debug, "pleorasrc", 0, - "debug category for pleorasrc element"); - gst_element_register (plugin, "pleorasrc", GST_RANK_NONE, - gst_pleorasrc_get_type ()); - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - pleora, - "Pleora eBUS video source", - plugin_init, GST_PACKAGE_VERSION, GST_PACKAGE_LICENSE, GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN) +} \ No newline at end of file diff --git a/sys/pleora/streamingchannelsource.cpp b/sys/pleora/streamingchannelsource.cpp new file mode 100644 index 0000000..97160f2 --- /dev/null +++ b/sys/pleora/streamingchannelsource.cpp @@ -0,0 +1,205 @@ +/* GStreamer + * Copyright (C) 2011 FIXME + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#include "streamingchannelsource.h" + +GstStreamingChannelSource::GstStreamingChannelSource () +: mAcquisitionBuffer (NULL), mBufferCount (0), mBufferValid (FALSE) +{ + +} + +void +GstStreamingChannelSource::GetWidthInfo (uint32_t & aMin, uint32_t & aMax, + uint32_t & aInc) const +{ + aMin = mWidth; + aMax = aMin; + aInc = 2; +} + +void +GstStreamingChannelSource::GetHeightInfo (uint32_t & aMin, uint32_t & aMax, + uint32_t & aInc) const +{ + aMin = mHeight; + aMax = aMin; + aInc = 2; +} + +PvResult GstStreamingChannelSource::GetSupportedPixelType (int aIndex, + PvPixelType & aPixelType) const +{ + if (aIndex != 0) { + return PvResult::Code::INVALID_PARAMETER; + } + + aPixelType = mPixelType; + return PvResult::Code::OK; +} + +PvBuffer * GstStreamingChannelSource::AllocBuffer () +{ + if (mBufferCount < mSink->num_internal_buffers) { + mBufferCount++; + return new PvBuffer; + } + return NULL; +} + +void GstStreamingChannelSource::FreeBuffer (PvBuffer * aBuffer) +{ + delete aBuffer; + mBufferCount--; +} + +PvResult GstStreamingChannelSource::QueueBuffer (PvBuffer * aBuffer) +{ + g_mutex_lock (&mSink->mutex); + if (mAcquisitionBuffer == NULL) { + // No buffer queued, accept it + mAcquisitionBuffer = aBuffer; + mBufferValid = FALSE; + g_mutex_unlock (&mSink->mutex); + return PvResult::Code::OK; + } + g_mutex_unlock (&mSink->mutex); + return PvResult::Code::BUSY; +} + +PvResult GstStreamingChannelSource::RetrieveBuffer (PvBuffer ** aBuffer) +{ + gint64 end_time; + + g_mutex_lock (&mSink->mutex); + // WAIT for buffer + end_time = g_get_monotonic_time () + 50 * G_TIME_SPAN_MILLISECOND; + while ((mAcquisitionBuffer == NULL || !mBufferValid) + && !mSink->stop_requested) { + if (!g_cond_wait_until (&mSink->cond, &mSink->mutex, end_time)) { + // No buffer queued for acquisition + g_mutex_unlock (&mSink->mutex); + return PvResult::Code::NO_AVAILABLE_DATA; + } + } + // Remove buffer from 1-deep pipeline + *aBuffer = mAcquisitionBuffer; + mAcquisitionBuffer = NULL; + mBufferValid = FALSE; + g_mutex_unlock (&mSink->mutex); + + return PvResult::Code::OK; +} + +void +GstStreamingChannelSource::SetSink (GstPleoraSink * sink) +{ + mSink = sink; +} + +void +GstStreamingChannelSource::SetCaps (GstCaps * caps) +{ + GstVideoInfo vinfo; + gst_video_info_from_caps (&vinfo, caps); + + switch (GST_VIDEO_INFO_FORMAT (&vinfo)) { + case GST_VIDEO_FORMAT_GRAY8: + mPixelType = PvPixelMono8; + break; + case GST_VIDEO_FORMAT_GRAY16_LE: + mPixelType = PvPixelMono16; + break; + case GST_VIDEO_FORMAT_RGB: + mPixelType = PvPixelRGB8; + break; + case GST_VIDEO_FORMAT_RGBA: + mPixelType = PvPixelRGBa8; + break; + case GST_VIDEO_FORMAT_BGR: + mPixelType = PvPixelBGR8; + break; + case GST_VIDEO_FORMAT_BGRA: + mPixelType = PvPixelBGRa8; + break; + default: + mPixelType = PvPixelUndefined; + break; + } + + mWidth = GST_VIDEO_INFO_WIDTH (&vinfo); + mHeight = GST_VIDEO_INFO_HEIGHT (&vinfo); +} + +void +GstStreamingChannelSource::ResizeBufferIfNeeded (PvBuffer * aBuffer) +{ + uint32_t lRequiredChunkSize = 0; + PvImage *lImage = aBuffer->GetImage (); + if ((lImage->GetWidth () != mWidth) || + (lImage->GetHeight () != mHeight) || + (lImage->GetPixelType () != mPixelType) || + (lImage->GetMaximumChunkLength () != lRequiredChunkSize)) { + lImage->Alloc (mWidth, mHeight, mPixelType, 0, 0, lRequiredChunkSize); + } +} + +void +GstStreamingChannelSource::SetBuffer (GstBuffer * buf) +{ + GST_ERROR ("Set buffer"); + + g_mutex_lock (&mSink->mutex); + + if (mAcquisitionBuffer == NULL) { + GST_WARNING_OBJECT (mSink, "No PvBuffer available to fill, dropping frame"); + g_mutex_unlock (&mSink->mutex); + return; + } + + if (mBufferValid) { + GST_WARNING_OBJECT (mSink, + "Buffer already filled, dropping incoming frame"); + g_mutex_unlock (&mSink->mutex); + return; + } + + /* TODO: avoid memcpy (when strides align) by attaching to PvBuffer */ + GstMapInfo minfo; + gst_buffer_map (buf, &minfo, GST_MAP_READ); + + ResizeBufferIfNeeded (mAcquisitionBuffer); + + guint8 *dst = mAcquisitionBuffer->GetDataPointer (); + if (!dst) { + GST_ERROR ("Have buffer to fill, but data pointer is invalid"); + g_mutex_unlock (&mSink->mutex); + return; + } + g_assert (mAcquisitionBuffer->GetSize () >= minfo.size); + /* TODO: fix stride if needed */ + memcpy (dst, minfo.data, minfo.size); + + gst_buffer_unmap (buf, &minfo); + + mBufferValid = TRUE; + g_cond_signal (&mSink->cond); + + g_mutex_unlock (&mSink->mutex); +} \ No newline at end of file diff --git a/sys/pleora/streamingchannelsource.h b/sys/pleora/streamingchannelsource.h new file mode 100644 index 0000000..f0ed619 --- /dev/null +++ b/sys/pleora/streamingchannelsource.h @@ -0,0 +1,53 @@ +/* GStreamer + * Copyright (C) 2011 FIXME + * + * 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 Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ + +#include + +#include "gstpleorasink.h" + +class GstStreamingChannelSource:public PvStreamingChannelSourceDefault +{ +public: + GstStreamingChannelSource (); + + void SetSink (GstPleoraSink * sink); + void SetCaps (GstCaps * caps); + void ResizeBufferIfNeeded (PvBuffer * aBuffer); + void SetBuffer (GstBuffer * buf); + + PvBuffer *AllocBuffer (); + void FreeBuffer (PvBuffer * aBuffer); + + PvResult QueueBuffer (PvBuffer * aBuffer); + PvResult RetrieveBuffer (PvBuffer ** aBuffer); + + void GetWidthInfo (uint32_t & aMin, uint32_t & aMax, uint32_t & aInc) const; + void GetHeightInfo (uint32_t & aMin, uint32_t & aMax, uint32_t & aInc) const; + PvResult GetSupportedPixelType (int aIndex, PvPixelType & aPixelType) const; + +private: + GstPleoraSink * mSink; + PvBuffer *mAcquisitionBuffer; + gboolean mBufferValid; + gint mBufferCount; + + gint mWidth; + gint mHeight; + PvPixelType mPixelType; +}; \ No newline at end of file