/* 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-gstpleorasrc * * The pleorasrc element is a source for Pleora eBUS * * * Example launch line * |[ * gst-launch -v pleorasrc ! videoconvert ! autovideosink * ]| * Shows video from the default Pleora device * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "gstpleorasrc.h" #include #include #include #include #include #include #include GST_DEBUG_CATEGORY_STATIC (gst_pleorasrc_debug); #define GST_CAT_DEFAULT gst_pleorasrc_debug /* prototypes */ static void gst_pleorasrc_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec); static void gst_pleorasrc_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec); static void gst_pleorasrc_dispose (GObject * object); static void gst_pleorasrc_finalize (GObject * object); static gboolean gst_pleorasrc_start (GstBaseSrc * src); static gboolean gst_pleorasrc_stop (GstBaseSrc * src); static GstCaps *gst_pleorasrc_get_caps (GstBaseSrc * src, GstCaps * filter); static gboolean gst_pleorasrc_set_caps (GstBaseSrc * src, GstCaps * caps); static gboolean gst_pleorasrc_unlock (GstBaseSrc * src); static gboolean gst_pleorasrc_unlock_stop (GstBaseSrc * src); static GstFlowReturn gst_pleorasrc_create (GstPushSrc * src, GstBuffer ** buf); static PvBuffer *gst_pleorasrc_get_pvbuffer (GstPleoraSrc * src); enum { PROP_0, PROP_DEVICE, PROP_DEVICE_INDEX, PROP_NUM_CAPTURE_BUFFERS, PROP_TIMEOUT, PROP_DETECTION_TIMEOUT, PROP_MULTICAST_GROUP, PROP_PORT, PROP_RECEIVER_ONLY, PROP_PACKET_SIZE, PROP_CONFIG_FILE, PROP_CONFIG_FILE_CONNECT }; #define DEFAULT_PROP_DEVICE "" #define DEFAULT_PROP_DEVICE_INDEX 0 #define DEFAULT_PROP_NUM_CAPTURE_BUFFERS 3 #define DEFAULT_PROP_TIMEOUT 1000 #define DEFAULT_PROP_DETECTION_TIMEOUT 1000 #define DEFAULT_PROP_MULTICAST_GROUP "0.0.0.0" #define DEFAULT_PROP_PORT 1042 #define DEFAULT_PROP_RECEIVER_ONLY FALSE #define DEFAULT_PROP_PACKET_SIZE 0 #define DEFAULT_PROP_CONFIG_FILE "" #define DEFAULT_PROP_CONFIG_FILE_CONNECT TRUE #define VIDEO_CAPS_MAKE_BAYER8(format) \ "video/x-bayer, " \ "format = (string) " format ", " \ "width = " GST_VIDEO_SIZE_RANGE ", " \ "height = " GST_VIDEO_SIZE_RANGE ", " \ "framerate = " GST_VIDEO_FPS_RANGE #define VIDEO_CAPS_MAKE_BAYER16(format) \ "video/x-bayer, " \ "format = (string) " format ", " \ "endianness = (int) 1234, " \ "bpp = (int) {16, 14, 12, 10}, " \ "width = " GST_VIDEO_SIZE_RANGE ", " \ "height = " GST_VIDEO_SIZE_RANGE ", " \ "framerate = " GST_VIDEO_FPS_RANGE /* pad templates */ static GstStaticPadTemplate gst_pleorasrc_src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS (VIDEO_CAPS_MAKE_BAYER16 ("{ bggr16, grbg16, rggb16, gbrg16 }") ";" VIDEO_CAPS_MAKE_BAYER8 ("{ bggr, grbg, rggb, gbrg }") ";" GST_VIDEO_CAPS_MAKE ("{ GRAY16_LE, GRAY16_BE, GRAY8, UYVY, YUY2, RGB }") ) ); /* class initialization */ G_DEFINE_TYPE (GstPleoraSrc, gst_pleorasrc, GST_TYPE_PUSH_SRC); static void gst_pleorasrc_class_init (GstPleoraSrcClass * klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass); GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass); gobject_class->set_property = gst_pleorasrc_set_property; gobject_class->get_property = gst_pleorasrc_get_property; gobject_class->dispose = gst_pleorasrc_dispose; gobject_class->finalize = gst_pleorasrc_finalize; gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get (&gst_pleorasrc_src_template)); gst_element_class_set_static_metadata (gstelement_class, "Pleora Video Source", "Source/Video", "Pleora eBUS video source", "Joshua M. Doe "); gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_pleorasrc_start); gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_pleorasrc_stop); gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_pleorasrc_get_caps); gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_pleorasrc_set_caps); gstbasesrc_class->unlock = GST_DEBUG_FUNCPTR (gst_pleorasrc_unlock); gstbasesrc_class->unlock_stop = GST_DEBUG_FUNCPTR (gst_pleorasrc_unlock_stop); gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_pleorasrc_create); /* Install GObject properties */ g_object_class_install_property (gobject_class, PROP_DEVICE, g_param_spec_string ("device", "Device ID", "Device ID. For GEV use MAC, IP, or user id. For U3V, use GUID or user id.", DEFAULT_PROP_DEVICE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); g_object_class_install_property (gobject_class, PROP_DEVICE_INDEX, g_param_spec_int ("device-index", "Device index", "Index of device, use -1 to enumerate all and select last", -1, G_MAXINT, DEFAULT_PROP_DEVICE_INDEX, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (gobject_class, PROP_NUM_CAPTURE_BUFFERS, g_param_spec_uint ("num-capture-buffers", "Number of capture buffers", "Number of capture buffers", 1, G_MAXUINT, DEFAULT_PROP_NUM_CAPTURE_BUFFERS, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMEOUT, g_param_spec_int ("timeout", "Timeout (ms)", "Timeout in ms (0 to use default)", 0, G_MAXINT, DEFAULT_PROP_TIMEOUT, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DETECTION_TIMEOUT, g_param_spec_int ("detection-timeout", "Detection Timeout (ms)", "Timeout in ms to detect GigE cameras", 100, 60000, DEFAULT_PROP_DETECTION_TIMEOUT, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); g_object_class_install_property (gobject_class, PROP_MULTICAST_GROUP, g_param_spec_string ("multicast-group", "Multicast group IP address", "The address of the multicast group to join (default is unicast)", DEFAULT_PROP_MULTICAST_GROUP, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PORT, g_param_spec_int ("port", "Multicast port", "The port of the multicast group.", 0, 65535, DEFAULT_PROP_PORT, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_RECEIVER_ONLY, g_param_spec_boolean ("receiver-only", "Receiver only", "Only open video stream, don't open as controller", DEFAULT_PROP_RECEIVER_ONLY, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_PACKET_SIZE, g_param_spec_int ("packet-size", "Packet size", "Packet size (0 to auto negotiate)", 0, 65535, DEFAULT_PROP_PACKET_SIZE, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); g_object_class_install_property (gobject_class, PROP_CONFIG_FILE, g_param_spec_string ("config-file", "Config file", "Filepath of the configuration file (*.pvcfg)", DEFAULT_PROP_CONFIG_FILE, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY))); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_FILE_CONNECT, g_param_spec_boolean ("config-file-connect", "Connect using config file", "Connects to and configures camera from config-file, if false " "connects using properties and then restores configuration", DEFAULT_PROP_CONFIG_FILE_CONNECT, (GParamFlags) (G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE))); } static void gst_pleorasrc_reset (GstPleoraSrc * src) { src->device = NULL; src->stream = NULL; src->pipeline = NULL; src->last_frame_count = 0; src->total_dropped_frames = 0; if (src->caps) { gst_caps_unref (src->caps); src->caps = NULL; } src->pv_pixel_type = PvPixelUndefined; src->width = 0; src->height = 0; } static void gst_pleorasrc_init (GstPleoraSrc * src) { /* set source as live (no preroll) */ gst_base_src_set_live (GST_BASE_SRC (src), TRUE); /* override default of BYTES to operate in time mode */ gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); /* initialize member variables */ src->device_id = g_strdup (DEFAULT_PROP_DEVICE); src->device_index = DEFAULT_PROP_DEVICE_INDEX; src->num_capture_buffers = DEFAULT_PROP_NUM_CAPTURE_BUFFERS; src->timeout = DEFAULT_PROP_TIMEOUT; src->detection_timeout = DEFAULT_PROP_DETECTION_TIMEOUT; src->multicast_group = g_strdup (DEFAULT_PROP_MULTICAST_GROUP); src->port = DEFAULT_PROP_PORT; src->receiver_only = DEFAULT_PROP_RECEIVER_ONLY; src->config_file = g_strdup (DEFAULT_PROP_CONFIG_FILE); src->config_file_connect = DEFAULT_PROP_CONFIG_FILE_CONNECT; src->stop_requested = FALSE; src->caps = NULL; src->pvbuffer = NULL; gst_pleorasrc_reset (src); } void gst_pleorasrc_set_property (GObject * object, guint property_id, const GValue * value, GParamSpec * pspec) { GstPleoraSrc *src; src = GST_PLEORA_SRC (object); switch (property_id) { case PROP_DEVICE: g_free (src->device_id); src->device_id = g_strdup (g_value_get_string (value)); break; case PROP_DEVICE_INDEX: src->device_index = g_value_get_int (value); break; case PROP_NUM_CAPTURE_BUFFERS: src->num_capture_buffers = g_value_get_uint (value); break; case PROP_TIMEOUT: src->timeout = g_value_get_int (value); break; case PROP_DETECTION_TIMEOUT: src->detection_timeout = g_value_get_int (value); break; case PROP_MULTICAST_GROUP: g_free (src->multicast_group); src->multicast_group = g_strdup (g_value_get_string (value)); break; case PROP_PORT: src->port = g_value_get_int (value); break; case PROP_RECEIVER_ONLY: src->receiver_only = g_value_get_boolean (value); break; case PROP_PACKET_SIZE: src->packet_size = g_value_get_int (value); break; case PROP_CONFIG_FILE: g_free (src->config_file); src->config_file = g_strdup (g_value_get_string (value)); break; case PROP_CONFIG_FILE_CONNECT: src->config_file_connect = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } void gst_pleorasrc_get_property (GObject * object, guint property_id, GValue * value, GParamSpec * pspec) { GstPleoraSrc *src; g_return_if_fail (GST_IS_PLEORA_SRC (object)); src = GST_PLEORA_SRC (object); switch (property_id) { case PROP_DEVICE: g_value_set_string (value, src->device_id); break; case PROP_DEVICE_INDEX: g_value_set_int (value, src->device_index); break; case PROP_NUM_CAPTURE_BUFFERS: g_value_set_uint (value, src->num_capture_buffers); break; case PROP_TIMEOUT: g_value_set_int (value, src->timeout); break; case PROP_DETECTION_TIMEOUT: g_value_set_int (value, src->detection_timeout); break; case PROP_MULTICAST_GROUP: g_value_set_string (value, src->multicast_group); break; case PROP_PORT: g_value_set_int (value, src->port); break; case PROP_RECEIVER_ONLY: g_value_set_boolean (value, src->receiver_only); break; case PROP_PACKET_SIZE: g_value_set_int (value, src->packet_size); break; case PROP_CONFIG_FILE: g_value_set_string (value, src->config_file); break; case PROP_CONFIG_FILE_CONNECT: g_value_set_boolean (value, src->config_file_connect); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } void gst_pleorasrc_dispose (GObject * object) { GstPleoraSrc *src; g_return_if_fail (GST_IS_PLEORA_SRC (object)); src = GST_PLEORA_SRC (object); /* clean up as possible. may be called multiple times */ G_OBJECT_CLASS (gst_pleorasrc_parent_class)->dispose (object); } void gst_pleorasrc_finalize (GObject * object) { GstPleoraSrc *src; g_return_if_fail (GST_IS_PLEORA_SRC (object)); src = GST_PLEORA_SRC (object); /* clean up object here */ if (src->device) { g_free (src->device); src->device = NULL; } if (src->caps) { gst_caps_unref (src->caps); src->caps = NULL; } G_OBJECT_CLASS (gst_pleorasrc_parent_class)->finalize (object); } static const gchar * usb_speed_str (PvUSBSpeed speed) { static const gchar *array[] = { "Unknown", "Low", "Full", "High", "Super" }; if (speed >= sizeof (array) / sizeof (const gchar *) || speed < 0) { speed = PvUSBSpeedUnknown; } return array[speed]; } static const void gst_pleorasrc_print_device_info (GstPleoraSrc * src, const PvDeviceInfo * device_info) { GST_DEBUG_OBJECT (src, "Found device '%s'", device_info->GetDisplayID ().GetAscii ()); const PvDeviceInfoGEV *device_info_GEV = dynamic_cast < const PvDeviceInfoGEV * >(device_info); const PvDeviceInfoU3V *device_info_U3V = dynamic_cast < const PvDeviceInfoU3V * >(device_info); const PvDeviceInfoUSB *device_info_USB = dynamic_cast < const PvDeviceInfoUSB * >(device_info); const PvDeviceInfoPleoraProtocol *device_info_pleora = dynamic_cast < const PvDeviceInfoPleoraProtocol * >(device_info); const PvNetworkAdapter *iface_nic = dynamic_cast < const PvNetworkAdapter * >(device_info->GetInterface ()); const PvUSBHostController *iface_usb = dynamic_cast < const PvUSBHostController * >(device_info->GetInterface ()); if (iface_nic != NULL) { #if VERSION_MAJOR == 4 #define PLEORA_GET_PARAM #else #define PLEORA_GET_PARAM 0 #endif GST_DEBUG_OBJECT (src, "Device found on network interface '%s', MAC: %s, IP: %s, Subnet: %s", iface_nic->GetDescription ().GetAscii (), iface_nic->GetMACAddress ().GetAscii (), iface_nic->GetIPAddress (PLEORA_GET_PARAM).GetAscii (), iface_nic->GetSubnetMask (PLEORA_GET_PARAM).GetAscii ()); } else if (iface_usb != NULL) { GST_DEBUG_OBJECT (src, "Device found on USB interface, VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%02X, '%s', %s Speed", iface_usb->GetVendorID (), iface_usb->GetDeviceID (), iface_usb->GetSubsystemID (), iface_usb->GetRevision (), iface_usb->GetName ().GetAscii (), usb_speed_str (iface_usb->GetSpeed ())); } if (device_info_GEV != NULL) { GST_DEBUG_OBJECT (src, "GEV device: MAC: %s, IP: %s, S/N: %s", device_info_GEV->GetMACAddress ().GetAscii (), device_info_GEV->GetIPAddress ().GetAscii (), device_info_GEV->GetSerialNumber ().GetAscii ()); } else if (device_info_U3V != NULL) { GST_DEBUG_OBJECT (src, "U3V device: GUID: %s, S/N: %s", device_info_U3V->GetDeviceGUID ().GetAscii (), device_info_U3V->GetSerialNumber ().GetAscii (), device_info_U3V->GetInterface ()); } else if (device_info_USB != NULL) { GST_DEBUG_OBJECT (src, "Unidentified USB device"); } else if (device_info_pleora != NULL) { GST_DEBUG_OBJECT (src, "Pleora device: MAC: %s, IP: %s, S/N: %s", device_info_pleora->GetMACAddress ().GetAscii (), device_info_pleora->GetIPAddress ().GetAscii (), device_info_pleora->GetSerialNumber ().GetAscii ()); } } static const PvDeviceInfo * gst_pleorasrc_get_device_info (GstPleoraSrc * src, PvSystem & lSystem) { PvResult pvRes; const PvDeviceInfo *device_info = NULL; // time allowed to detect GEV cameras lSystem.SetDetectionTimeout (src->detection_timeout); if (g_strcmp0 (src->device_id, "") != 0) { GST_DEBUG_OBJECT (src, "Finding device based on ID: %s", src->device_id); pvRes = lSystem.FindDevice (src->device_id, &device_info); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("Failed to find device ID '%s': %s", src->device_id, pvRes.GetDescription ().GetAscii ()), (NULL)); return NULL; } } else if (src->device_index >= 0) { GST_DEBUG_OBJECT (src, "Finding device based on index: %d", src->device_index); /* Find will block for detection_timeout */ pvRes = lSystem.Find (); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("Error finding devices: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); return NULL; } if (lSystem.GetDeviceCount () < 1) { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("No Pleora-compatible devices found"), (NULL)); return NULL; } if (src->device_index >= lSystem.GetDeviceCount ()) { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("Device index specified (%d) does not exist, out of range [0, %d)", src->device_index, lSystem.GetDeviceCount ()), (NULL)); return NULL; } device_info = lSystem.GetDeviceInfo (src->device_index); if (device_info == NULL) { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("Failed to find device index %d", src->device_index), (NULL)); return NULL; } } else { guint32 device_count; GST_DEBUG_OBJECT (src, "Enumerating devices and choosing last one"); pvRes = lSystem.Find (); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("Error finding devices: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); return NULL; } device_count = lSystem.GetDeviceCount (); if (device_count < 1) { GST_ELEMENT_ERROR (src, RESOURCE, NOT_FOUND, ("No Pleora-compatible devices found"), (NULL)); return NULL; } GST_DEBUG_OBJECT (src, "Found a total of %d device(s)", device_count); for (uint32_t x = 0; x < device_count; x++) { device_info = lSystem.GetDeviceInfo (x); gst_pleorasrc_print_device_info (src, device_info); } #if 0 // iterate through all interfaces on system uint32_t lInterfaceCount = lSystem.GetInterfaceCount (); for (uint32_t x = 0; x < lInterfaceCount; x++) { const PvInterface *lInterface = lSystem.GetInterface (x); GST_DEBUG_OBJECT (src, "Found interface %d: '%s', '%s'", x, lInterface->GetName ().GetAscii (), lInterface->GetDisplayID ().GetAscii ()); const PvNetworkAdapter *lNIC = dynamic_cast < const PvNetworkAdapter * >(lInterface); if (lNIC != NULL) { GST_DEBUG_OBJECT (src, "MAC: %s, IP: %s, Subnet: %s", lNIC->GetMACAddress ().GetAscii (), lNIC->GetIPAddress ().GetAscii (), lNIC->GetSubnetMask ().GetAscii ()); } const PvUSBHostController *lUSB = dynamic_cast < const PvUSBHostController * >(lInterface); if (lUSB != NULL) { GST_DEBUG_OBJECT (src, "USB '%s'", lUSB->GetName ().GetAscii ()); } // iterate through all devices on interface uint32_t lDeviceCount = lInterface->GetDeviceCount (); if (lDeviceCount == 0) { GST_DEBUG_OBJECT (src, "No devices found on this interface"); } for (uint32_t y = 0; y < lDeviceCount; y++) { const PvDeviceInfo *devinfo = lInterface->GetDeviceInfo (y); gst_pleorasrc_print_device_info (src, devinfo); // select last device device_info = devinfo; } } #endif } return device_info; } static gboolean gst_pleorasrc_restore_device_from_config (GstPleoraSrc * src) { PvConfigurationReader lConfigReader; PvResult pvRes; GST_DEBUG_OBJECT (src, "Loading config file (%s)", src->config_file); pvRes = lConfigReader.Load (src->config_file); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to load config file (%s): %s", src->config_file, pvRes.GetDescription ().GetAscii ()), (NULL)); return FALSE; } if (src->device == NULL) { PvDeviceGEV *lDeviceGEV = new PvDeviceGEV (); PvDeviceU3V *lDeviceU3V = new PvDeviceU3V (); GST_DEBUG_OBJECT (src, "Restoring device connection and settings from config file"); pvRes = lConfigReader.Restore (0, lDeviceGEV); if (pvRes.IsOK ()) { src->device = lDeviceGEV; delete lDeviceU3V; } else { pvRes = lConfigReader.Restore (0, lDeviceU3V); if (pvRes.IsOK ()) { src->device = lDeviceU3V; delete lDeviceGEV; } else { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to restore device from config file (%s): %s", src->config_file, pvRes.GetDescription ().GetAscii ()), (NULL)); return FALSE; } } } else { GST_DEBUG_OBJECT (src, "Restoring device settings from config file"); pvRes = lConfigReader.Restore (0, src->device); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to restore device from config file (%s): %s", src->config_file, pvRes.GetDescription ().GetAscii ()), (NULL)); return FALSE; } } return TRUE; } static gboolean gst_pleorasrc_restore_stream_from_config (GstPleoraSrc * src) { PvConfigurationReader lConfigReader; PvResult pvRes; GST_DEBUG_OBJECT (src, "Loading config file (%s)", src->config_file); pvRes = lConfigReader.Load (src->config_file); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to load config file (%s): %s", src->config_file, pvRes.GetDescription ().GetAscii ()), (NULL)); return FALSE; } if (src->stream == NULL) { PvStreamGEV *lStreamGEV = new PvStreamGEV (); PvStreamU3V *lStreamU3V = new PvStreamU3V (); GST_DEBUG_OBJECT (src, "Restoring stream connection and settings from config file"); pvRes = lConfigReader.Restore (0, lStreamGEV); if (pvRes.IsOK ()) { src->stream = lStreamGEV; delete lStreamU3V; } else { pvRes = lConfigReader.Restore (0, lStreamU3V); if (pvRes.IsOK ()) { src->stream = lStreamU3V; delete lStreamGEV; } else { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to restore stream from config file (%s): %s", src->config_file, pvRes.GetDescription ().GetAscii ()), (NULL)); return FALSE; } } } else { GST_DEBUG_OBJECT (src, "Restoring stream settings from config file"); pvRes = lConfigReader.Restore (0, src->stream); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to restore stream from config file (%s): %s", src->config_file, pvRes.GetDescription ().GetAscii ()), (NULL)); return FALSE; } } return TRUE; } static gboolean gst_pleorasrc_setup_stream (GstPleoraSrc * src) { PvResult pvRes; const PvDeviceInfo *device_info; PvSystem lSystem; if (g_strcmp0 (src->config_file, DEFAULT_PROP_CONFIG_FILE) != 0 && src->config_file_connect) { if (!gst_pleorasrc_restore_device_from_config (src)) { /* error already sent */ return FALSE; } if (!gst_pleorasrc_restore_stream_from_config (src)) { /* error already sent */ return FALSE; } } else { /* PvSystem creates device info, so we must persist it across this call */ device_info = gst_pleorasrc_get_device_info (src, lSystem); if (device_info == NULL) { /* error already sent */ return FALSE; } GST_DEBUG_OBJECT (src, "Info for device that will be opened:"); gst_pleorasrc_print_device_info (src, device_info); /* open device (for GEV, opening device means we're a controller */ if (!src->receiver_only) { GST_DEBUG_OBJECT (src, "Trying to connect to device '%s' as controller", device_info->GetDisplayID ().GetAscii ()); src->device = PvDevice::CreateAndConnect (device_info, &pvRes); if (src->device == NULL) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Unable to connect to device as controller: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); return FALSE; } GST_DEBUG_OBJECT (src, "Connected to device as controller"); } /* open stream */ if (device_info->GetType () == PvDeviceInfoTypeGEV || device_info->GetType () == PvDeviceInfoTypePleoraProtocol) { PvStreamGEV *stream = new PvStreamGEV; if (g_strcmp0 (src->multicast_group, DEFAULT_PROP_MULTICAST_GROUP) != 0) { GST_DEBUG_OBJECT (src, "Opening device in multicast mode, %s:%d", src->multicast_group, src->port); pvRes = stream->Open (device_info->GetConnectionID (), src->multicast_group, src->port); } else { GST_DEBUG_OBJECT (src, "Opening device in unicast mode"); pvRes = stream->Open (device_info->GetConnectionID ()); } src->stream = stream; } else { src->stream = PvStream::CreateAndOpen (device_info->GetConnectionID (), &pvRes); } if (src->stream == NULL || !pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Failed to open stream: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); goto stream_failed; } GST_DEBUG_OBJECT (src, "Stream created for device"); /* if acting as a GigE controller, configure stream */ PvDeviceGEV *lDeviceGEV = dynamic_cast < PvDeviceGEV * >(src->device); if (!src->receiver_only && lDeviceGEV != NULL) { #if VERSION_MAJOR == 4 PvStreamGEV *lStreamGEV = static_cast < PvStreamGEV * >(src->stream); #else const PvStreamGEV *lStreamGEV = static_cast < PvStreamGEV * >(src->stream); #endif /* negotiate or set packet size */ if (src->packet_size == 0) { /* Negotiate packet size, use safe default if it fails */ pvRes = lDeviceGEV->NegotiatePacketSize (0, 1476); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to negotiate packet size: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); goto stream_config_failed; } } else { pvRes = lDeviceGEV->SetPacketSize (src->packet_size); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to set packet size to %d: %s", src->packet_size, pvRes.GetDescription ().GetAscii ()), (NULL)); goto stream_config_failed; } } /* Configure device streaming destination */ pvRes = lDeviceGEV->SetStreamDestination (lStreamGEV->GetLocalIPAddress (), lStreamGEV->GetLocalPort ()); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Failed to set stream destination: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); goto stream_config_failed; } } } /* load config file if specified */ if (g_strcmp0 (src->config_file, DEFAULT_PROP_CONFIG_FILE) != 0 && !src->config_file_connect) { if (!gst_pleorasrc_restore_device_from_config (src)) { goto stream_config_failed; } if (!gst_pleorasrc_restore_stream_from_config (src)) { goto stream_config_failed; } } src->pipeline = new PvPipeline (src->stream); if (src->pipeline == NULL) { GST_ELEMENT_ERROR (src, RESOURCE, OPEN_READ, ("Unable to create pipeline from stream"), (NULL)); goto stream_config_failed; } pvRes = src->pipeline->SetBufferCount (src->num_capture_buffers); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, SETTINGS, ("Unable to set buffer count: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); goto pipeline_config_failed; } return TRUE; pipeline_config_failed: if (src->pipeline) { delete src->pipeline; src->pipeline = NULL; } stream_config_failed: if (src->stream) { src->stream->Close (); PvStream::Free (src->stream); src->stream = NULL; } stream_failed: if (src->device) { src->device->Disconnect (); PvDevice::Free (src->device); src->device = NULL; } return FALSE; } // borrowed from Aravis, arvmisc.c #define MAKE_FOURCC(a,b,c,d) ((guint32)((a)|(b)<<8|(c)<<16|(d)<<24)) typedef struct { PvPixelType pixel_type; const char *gst_caps_string; const char *name; const char *format; int bpp; int depth; guint32 fourcc; } GstPleoraCapsInfos; GstPleoraCapsInfos gst_caps_infos[] = { { PvPixelMono8, "video/x-raw, format=(string)GRAY8", "video/x-raw", "GRAY8", 8, 8, 0}, { PvPixelMono10, "video/x-raw, format=(string)GRAY16_LE, bpp=(int)10", "video/x-raw", "GRAY16_LE", 10, 16, 0}, { PvPixelMono12, "video/x-raw, format=(string)GRAY16_LE, bpp=(int)12", "video/x-raw", "GRAY16_LE", 12, 16, 0}, { PvPixelMono14, "video/x-raw, format=(string)GRAY16_LE, bpp=(int)14", "video/x-raw", "GRAY16_LE", 14, 16, 0}, { PvPixelMono16, "video/x-raw, format=(string)GRAY16_LE", "video/x-raw", "GRAY16_LE", 16, 16, 0}, { PvPixelBayerGR8, "video/x-bayer, format=(string)grbg", "video/x-bayer", "grbg", 8, 8, MAKE_FOURCC ('g', 'r', 'b', 'g') }, { PvPixelBayerRG8, "video/x-bayer, format=(string)rggb", "video/x-bayer", "rggb", 8, 8, MAKE_FOURCC ('r', 'g', 'g', 'b') }, { PvPixelBayerGB8, "video/x-bayer, format=(string)gbrg", "video/x-bayer", "gbrg", 8, 8, MAKE_FOURCC ('g', 'b', 'r', 'g') }, { PvPixelBayerBG8, "video/x-bayer, format=(string)bggr", "video/x-bayer", "bggr", 8, 8, MAKE_FOURCC ('b', 'g', 'g', 'r') }, /* The caps for non 8-bit bayer formats has not been agreed upon yet. * This feature is discussed in bug https://bugzilla.gnome.org/show_bug.cgi?id=693666 .*/ { PvPixelBayerGR10, "video/x-bayer, format=(string)grbg16, bpp=(int)10", "video/x-bayer", "grbg", 10, 16, 0}, { PvPixelBayerRG10, "video/x-bayer, format=(string)rggb16, bpp=(int)10", "video/x-bayer", "rggb", 10, 16, 0}, { PvPixelBayerGB10, "video/x-bayer, format=(string)gbrg16, bpp=(int)10", "video/x-bayer", "gbrg", 10, 16, 0}, { PvPixelBayerBG10, "video/x-bayer, format=(string)bggr16, bpp=(int)10", "video/x-bayer", "bggr", 10, 16, 0}, { PvPixelBayerGR12, "video/x-bayer, format=(string)grbg16, bpp=(int)12", "video/x-bayer", "grbg", 12, 16, 0}, { PvPixelBayerRG12, "video/x-bayer, format=(string)rggb16, bpp=(int)12", "video/x-bayer", "rggb", 12, 16, 0}, { PvPixelBayerGB12, "video/x-bayer, format=(string)gbrg16, bpp=(int)12", "video/x-bayer", "gbrg", 12, 16, 0}, { PvPixelBayerBG12, "video/x-bayer, format=(string)bggr16, bpp=(int)12", "video/x-bayer", "bggr", 12, 16, 0}, { PvPixelBayerGR16, "video/x-bayer, format=(string)grbg16, bpp=(int)16", "video/x-bayer", "grbg", 16, 16, 0}, { PvPixelBayerRG16, "video/x-bayer, format=(string)rggb16, bpp=(int)16", "video/x-bayer", "rggb", 16, 16, 0}, { PvPixelBayerGB16, "video/x-bayer, format=(string)gbrg16, bpp=(int)16", "video/x-bayer", "gbrg", 16, 16, 0}, { PvPixelBayerBG16, "video/x-bayer, format=(string)bggr16, bpp=(int)16", "video/x-bayer", "bggr", 16, 16, 0}, { PvPixelYUV422_8_UYVY, "video/x-raw, format=(string)UYVY", "video/x-raw", "UYVY", 0, 0, MAKE_FOURCC ('U', 'Y', 'V', 'Y') }, { PvPixelYUV411_8_UYYVYY, "video/x-raw, format=(string)IYU1", "video/x-raw", "IYU1", 0, 0, MAKE_FOURCC ('I', 'Y', 'U', '1') }, { PvPixelYUV8_UYV, "video/x-raw, format=(string)IYU2", "video/x-raw", "IYU2", 0, 0, MAKE_FOURCC ('I', 'Y', 'U', '2') }, { PvPixelYUV422_8, "video/x-raw, format=(string)YUY2", "video/x-raw", "YUY2", 0, 0, MAKE_FOURCC ('Y', 'U', 'Y', '2') }, { PvPixelRGB8, "video/x-raw, format=(string)RGB", "video/x-raw", "RGB", 24, 24, 0}, { PvPixelRGBa8, "video/x-raw, format=(string)RGBx", "video/x-raw", "RGBx", 32, 32, 0}, }; /** * arv_pixel_format_to_gst_caps_string: * @pixel_format: a pixel format * Return value: a gstreamer caps string describing the given @pixel_format. */ const char * gst_pleorasrc_pixel_type_to_gst_caps_string (PvPixelType pixel_type) { int i; for (i = 0; i < G_N_ELEMENTS (gst_caps_infos); i++) if (gst_caps_infos[i].pixel_type == pixel_type) break; if (i == G_N_ELEMENTS (gst_caps_infos)) { GST_WARNING ("Pixel type not currently supported: %d", pixel_type); return NULL; } GST_LOG ("Matched pixel type %d to caps %s", pixel_type, gst_caps_infos[i].gst_caps_string); return gst_caps_infos[i].gst_caps_string; } PvPixelType gst_pleorasrc_pixel_type_from_gst_caps (const char *name, const char *format, int bpp, int depth) { unsigned int i; g_return_val_if_fail (name != NULL, PvPixelUndefined); for (i = 0; i < G_N_ELEMENTS (gst_caps_infos); i++) { if (strcmp (name, gst_caps_infos[i].name) != 0 || (depth > 0 && depth != gst_caps_infos[i].depth) || (bpp > 0 && bpp != gst_caps_infos[i].bpp)) continue; if (strcmp (name, "video/x-raw") == 0 && strcmp (format, gst_caps_infos[i].format) == 0) return gst_caps_infos[i].pixel_type; if (strcmp (name, "video/x-bayer") == 0 && strcmp (format, gst_caps_infos[i].format) == 0) return gst_caps_infos[i].pixel_type; } return PvPixelUndefined; } static gboolean gst_pleorasrc_start (GstBaseSrc * bsrc) { GstPleoraSrc *src = GST_PLEORA_SRC (bsrc); PvResult pvRes; GST_DEBUG_OBJECT (src, "start"); if (!gst_pleorasrc_setup_stream (src)) { /* error already sent */ goto error; } /* Note: the pipeline must be initialized before we start acquisition */ GST_DEBUG_OBJECT (src, "Starting pipeline"); pvRes = src->pipeline->Start (); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to start pipeline: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); goto error; } /* command stream to start */ if (!src->receiver_only) { PvGenParameterArray *lDeviceParams = src->device->GetParameters (); PvGenCommand *start_cmd = dynamic_cast < PvGenCommand * >(lDeviceParams->Get ("AcquisitionStart")); GST_DEBUG_OBJECT (src, "Opened as controller, so send AcquisitionStart command"); if (start_cmd == NULL) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to get device AcquisitionStart parameter"), (NULL)); goto error; } pvRes = src->device->StreamEnable (); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to enable stream: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); goto error; } pvRes = start_cmd->Execute (); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to start acquisition: %s", pvRes.GetDescription ().GetAscii ()), (NULL)); goto error; } } /* grab first buffer so we can set caps before _create */ src->pvbuffer = gst_pleorasrc_get_pvbuffer (src); if (!src->pvbuffer) { goto error; } return TRUE; error: if (src->pipeline) { delete src->pipeline; src->pipeline = NULL; } if (src->stream) { src->stream->Close (); PvStream::Free (src->stream); src->stream = NULL; } if (src->device) { src->device->Disconnect (); PvDevice::Free (src->device); src->device = NULL; } return FALSE; } static gboolean gst_pleorasrc_stop (GstBaseSrc * bsrc) { GstPleoraSrc *src = GST_PLEORA_SRC (bsrc); GST_DEBUG_OBJECT (src, "stop"); if (!src->receiver_only) { PvGenParameterArray *lDeviceParams = src->device->GetParameters (); PvGenCommand *lStop = dynamic_cast < PvGenCommand * >(lDeviceParams->Get ("AcquisitionStop")); lStop->Execute (); src->device->StreamDisable (); } src->pipeline->Stop (); if (src->pipeline) { delete src->pipeline; src->pipeline = NULL; } if (src->stream) { src->stream->Close (); PvStream::Free (src->stream); src->stream = NULL; } if (src->device) { src->device->Disconnect (); PvDevice::Free (src->device); src->device = NULL; } gst_pleorasrc_reset (src); return TRUE; } static GstCaps * gst_pleorasrc_get_caps (GstBaseSrc * bsrc, GstCaps * filter) { GstPleoraSrc *src = GST_PLEORA_SRC (bsrc); GstCaps *caps; if (src->caps == NULL) { caps = gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD (src)); } else { caps = gst_caps_copy (src->caps); } GST_DEBUG_OBJECT (src, "The caps before filtering are %" GST_PTR_FORMAT, caps); if (filter && caps) { GstCaps *tmp = gst_caps_intersect (caps, filter); gst_caps_unref (caps); caps = tmp; } GST_DEBUG_OBJECT (src, "The caps after filtering are %" GST_PTR_FORMAT, caps); return caps; } static gboolean gst_pleorasrc_set_caps (GstBaseSrc * bsrc, GstCaps * caps) { GstPleoraSrc *src = GST_PLEORA_SRC (bsrc); GstVideoInfo vinfo; GstStructure *s = gst_caps_get_structure (caps, 0); GST_DEBUG_OBJECT (src, "The caps being set are %" GST_PTR_FORMAT, caps); gst_video_info_from_caps (&vinfo, caps); if (GST_VIDEO_INFO_FORMAT (&vinfo) != GST_VIDEO_FORMAT_UNKNOWN) { src->height = GST_VIDEO_INFO_HEIGHT (&vinfo); src->gst_stride = GST_VIDEO_INFO_COMP_STRIDE (&vinfo, 0); } else { goto unsupported_caps; } return TRUE; unsupported_caps: GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Unsupported caps: %" GST_PTR_FORMAT, caps), (NULL)); return FALSE; } static gboolean gst_pleorasrc_unlock (GstBaseSrc * bsrc) { GstPleoraSrc *src = GST_PLEORA_SRC (bsrc); GST_LOG_OBJECT (src, "unlock"); src->stop_requested = TRUE; return TRUE; } static gboolean gst_pleorasrc_unlock_stop (GstBaseSrc * bsrc) { GstPleoraSrc *src = GST_PLEORA_SRC (bsrc); GST_LOG_OBJECT (src, "unlock_stop"); src->stop_requested = FALSE; return TRUE; } //static GstBuffer * //gst_pleorasrc_create_buffer_from_pvimage (GstPleoraSrc * src, // PvImage * pvimage) //{ // GstMapInfo minfo; // GstBuffer *buf; // // /* TODO: use allocator or use from pool */ // buf = gst_buffer_new_and_alloc (src->height * src->gst_stride); // // /* Copy image to buffer from surface */ // gst_buffer_map (buf, &minfo, GST_MAP_WRITE); // GST_LOG_OBJECT (src, // "GstBuffer size=%d, gst_stride=%d, buffer_num=%d, frame_count=%d, num_frames_on_queue=%d", // minfo.size, src->gst_stride, circ_handle->BufferNumber, // circ_handle->FrameCount, circ_handle->NumItemsOnQueue); // GST_LOG_OBJECT (src, "Buffer timestamp %02d:%02d:%02d.%06d", // circ_handle->HiResTimeStamp.hour, circ_handle->HiResTimeStamp.min, // circ_handle->HiResTimeStamp.sec, circ_handle->HiResTimeStamp.usec); // // /* TODO: use orc_memcpy */ // if (src->gst_stride == src->bf_stride) { // memcpy (minfo.data, ((guint8 *) circ_handle->pBufData), minfo.size); // } else { // int i; // GST_LOG_OBJECT (src, "Image strides not identical, copy will be slower."); // for (i = 0; i < src->height; i++) { // memcpy (minfo.data + i * src->gst_stride, // ((guint8 *) circ_handle->pBufData) + // i * src->bf_stride, src->bf_stride); // } // } // gst_buffer_unmap (buf, &minfo); // // return buf; //} typedef struct { GstPleoraSrc *src; PvBuffer *buffer; } VideoFrame; static void pvbuffer_release (void *data) { VideoFrame *frame = (VideoFrame *) data; if (frame->src->pipeline) { // TODO: should use a mutex in case _stop is being called at the same time frame->src->pipeline->ReleaseBuffer (frame->buffer); } } static PvBuffer * gst_pleorasrc_get_pvbuffer (GstPleoraSrc * src) { PvResult pvRes, opRes; PvBuffer *pvbuffer; PvImage *pvimage; while (TRUE) { pvRes = src->pipeline->RetrieveNextBuffer (&pvbuffer, src->timeout, &opRes); if (!pvRes.IsOK ()) { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Failed to retrieve buffer in timeout (%d ms): 0x%04x, '%s'", src->timeout, pvRes.GetCode (), pvRes.GetDescription ().GetAscii ()), (NULL)); return NULL; } /* continue if we get a bad frame */ if (!opRes.IsOK ()) { GST_WARNING_OBJECT (src, "Failed to get buffer: 0x%04x, '%s'", opRes.GetCode (), opRes.GetDescription ().GetAscii ()); continue; } if (pvbuffer->GetPayloadType () != PvPayloadTypeImage) { /* TODO: are non-image buffers normal? */ GST_ERROR_OBJECT (src, "Got buffer with non-image data"); GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Got buffer with non-image data"), (NULL)); src->pipeline->ReleaseBuffer (pvbuffer); return NULL; } break; } pvimage = pvbuffer->GetImage (); if (src->pv_pixel_type != pvimage->GetPixelType () || src->width != pvimage->GetWidth () || src->height != pvimage->GetHeight ()) { const char *caps_string = gst_pleorasrc_pixel_type_to_gst_caps_string (pvimage->GetPixelType ()); if (caps_string != NULL) { GstStructure *structure; GstCaps *caps; caps = gst_caps_new_empty (); structure = gst_structure_from_string (caps_string, NULL); gst_structure_set (structure, "width", G_TYPE_INT, pvimage->GetWidth (), "height", G_TYPE_INT, pvimage->GetHeight (), "framerate", GST_TYPE_FRACTION, 30, 1, NULL); gst_caps_append_structure (caps, structure); if (src->caps) { gst_caps_unref (src->caps); } src->caps = caps; gst_base_src_set_caps (GST_BASE_SRC (src), src->caps); src->pv_pixel_type = pvimage->GetPixelType (); src->width = pvimage->GetWidth (); src->height = pvimage->GetHeight (); guint32 pixel_bpp = PvGetPixelBitCount (pvimage->GetPixelType ()); src->pleora_stride = (pvimage->GetWidth () * pixel_bpp) / 8; } else { GST_ELEMENT_ERROR (src, RESOURCE, FAILED, ("Pixel type %d not supported", pvimage->GetPixelType ()), (NULL)); src->pipeline->ReleaseBuffer (pvbuffer); return NULL; } } return pvbuffer; } static GstFlowReturn gst_pleorasrc_create (GstPushSrc * psrc, GstBuffer ** buf) { GstPleoraSrc *src = GST_PLEORA_SRC (psrc); PvResult pvRes; GstClock *clock; GstClockTime clock_time; PvBuffer *pvbuffer; PvImage *pvimage; GST_LOG_OBJECT (src, "create"); if (src->pvbuffer) { /* we have a buffer from _start to handle */ pvbuffer = src->pvbuffer; src->pvbuffer = NULL; } else { pvbuffer = gst_pleorasrc_get_pvbuffer (src); } if (!pvbuffer) { /* error already posted */ return GST_FLOW_ERROR; } pvimage = pvbuffer->GetImage (); gpointer data = pvimage->GetDataPointer (); if (src->pleora_stride == src->gst_stride) { VideoFrame *vf = g_new0 (VideoFrame, 1); vf->src = src; vf->buffer = pvbuffer; gsize data_size = pvimage->GetImageSize (); *buf = gst_buffer_new_wrapped_full ((GstMemoryFlags) GST_MEMORY_FLAG_READONLY, (gpointer) data, data_size, 0, data_size, vf, (GDestroyNotify) pvbuffer_release); } else { GstMapInfo minfo; GST_LOG_OBJECT (src, "Row stride not aligned, copying %d -> %d", src->pleora_stride, src->gst_stride); *buf = gst_buffer_new_and_alloc (src->height * src->gst_stride); guint8 *s = (guint8 *) data; guint8 *d; gst_buffer_map (*buf, &minfo, GST_MAP_WRITE); d = minfo.data; g_assert (minfo.size >= src->pleora_stride * src->height); for (int i = 0; i < src->height; i++) memcpy (d + i * src->gst_stride, s + i * src->pleora_stride, src->pleora_stride); gst_buffer_unmap (*buf, &minfo); src->pipeline->ReleaseBuffer (pvbuffer); } clock = gst_element_get_clock (GST_ELEMENT (src)); clock_time = gst_clock_get_time (clock); gst_object_unref (clock); /* check for dropped frames and disrupted signal */ //dropped_frames = (circ_handle.FrameCount - src->last_frame_count) - 1; //if (dropped_frames > 0) { // src->total_dropped_frames += dropped_frames; // GST_WARNING_OBJECT (src, "Dropped %d frames (%d total)", dropped_frames, // src->total_dropped_frames); //} else if (dropped_frames < 0) { // GST_WARNING_OBJECT (src, "Frame count non-monotonic, signal disrupted?"); //} //src->last_frame_count = circ_handle.FrameCount; /* create GstBuffer then release circ buffer back to acquisition */ //*buf = gst_pleorasrc_create_buffer_from_circ_handle (src, &circ_handle); //ret = // BiCirStatusSet (src->board, &src->buffer_array, circ_handle, BIAVAILABLE); //if (ret != BI_OK) { // GST_ELEMENT_ERROR (src, RESOURCE, FAILED, // ("Failed to release buffer: %s", gst_pleorasrc_get_error_string (src, // ret)), (NULL)); // return GST_FLOW_ERROR; //} /* TODO: understand why timestamps for circ_handle are sometimes 0 */ //GST_BUFFER_TIMESTAMP (*buf) = // GST_CLOCK_DIFF (gst_element_get_base_time (GST_ELEMENT (src)), // src->acq_start_time + circ_handle.HiResTimeStamp.totalSec * GST_SECOND); GST_BUFFER_TIMESTAMP (*buf) = GST_CLOCK_DIFF (gst_element_get_base_time (GST_ELEMENT (src)), clock_time); //GST_BUFFER_OFFSET (*buf) = circ_handle.FrameCount - 1; if (src->stop_requested) { if (*buf != NULL) { gst_buffer_unref (*buf); *buf = NULL; } return GST_FLOW_FLUSHING; } 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)