Merge branch 'joshdoe:master' into master
This commit is contained in:
commit
cc1efa8ec2
@ -117,6 +117,11 @@ macro_log_feature(SAPERA_FOUND "Teledyne DALSA Sapera" "Required to build Teledy
|
||||
find_package(XCLIB)
|
||||
macro_log_feature(XCLIB_FOUND "EPIX PIXCI" "Required to build EPIX PIXCI source element" "http://www.epixinc.com/" FALSE)
|
||||
|
||||
if (WIN32)
|
||||
# Windows distributions of GStreamer include ZLIB
|
||||
set(ZLIB_ROOT ${GSTREAMER_ROOT})
|
||||
endif ()
|
||||
find_package(ZLIB)
|
||||
|
||||
# Setup common environment
|
||||
include_directories(
|
||||
|
||||
@ -16,29 +16,34 @@ if (NOT Pleora_DIR)
|
||||
set (Pleora_DIR $ENV{PUREGEV_ROOT} CACHE PATH "Directory containing Pleora SDK includes and libraries")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||
if (WIN32 AND CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||
set(_LIB_SUFFIX "64")
|
||||
else ()
|
||||
set(_LIB_SUFFIX "")
|
||||
endif ()
|
||||
|
||||
set(_Pleora_PATHS PATHS
|
||||
set (_Pleora_PATHS PATHS
|
||||
"${Pleora_DIR}"
|
||||
"C:/Program Files/Pleora Technologies Inc/eBUS SDK/Includes"
|
||||
"C:/Program Files (x86)/Pleora Technologies Inc/eBUS SDK/Includes")
|
||||
|
||||
find_path (Pleora_INCLUDE_DIR PvBase.h
|
||||
PATHS ${_Pleora_PATHS}
|
||||
PATH_SUFFIXES Includes)
|
||||
PATH_SUFFIXES Includes include)
|
||||
message (STATUS "Found Pleora include dir in ${Pleora_INCLUDE_DIR}")
|
||||
|
||||
find_path (Pleora_LIBRARY_DIR PvBase${_LIB_SUFFIX}.lib
|
||||
find_path (Pleora_LIBRARY_DIR NAMES libPvBase.so "PvBase${_LIB_NAME}"
|
||||
PATHS ${_Pleora_PATHS}
|
||||
PATH_SUFFIXES Libraries)
|
||||
PATH_SUFFIXES Libraries lib)
|
||||
|
||||
find_library (Pleora_LIBRARY_BASE PvBase${_LIB_SUFFIX} ${Pleora_LIBRARY_DIR})
|
||||
find_library (Pleora_LIBRARY_DEVICE PvDevice${_LIB_SUFFIX} ${Pleora_LIBRARY_DIR})
|
||||
message (STATUS "Found Pleora library in ${Pleora_LIBRARY_DIR}")
|
||||
|
||||
set (Pleora_LIBRARIES ${Pleora_LIBRARY_BASE} ${Pleora_LIBRARY_DEVICE})
|
||||
find_library (Pleora_LIBRARY_BASE "PvBase${_LIB_SUFFIX}" ${Pleora_LIBRARY_DIR})
|
||||
find_library (Pleora_LIBRARY_DEVICE "PvDevice${_LIB_SUFFIX}" ${Pleora_LIBRARY_DIR})
|
||||
find_library (Pleora_LIBRARY_PERSISTENCE "PvPersistence${_LIB_SUFFIX}" ${Pleora_LIBRARY_DIR})
|
||||
find_library (Pleora_LIBRARY_VIRTUAL_DEVICE "PvVirtualDevice${_LIB_SUFFIX}" ${Pleora_LIBRARY_DIR})
|
||||
|
||||
set (Pleora_LIBRARIES ${Pleora_LIBRARY_BASE} ${Pleora_LIBRARY_DEVICE} ${Pleora_LIBRARY_PERSISTENCE} ${Pleora_LIBRARY_VIRTUAL_DEVICE})
|
||||
|
||||
if (Pleora_INCLUDE_DIR)
|
||||
file(STRINGS "${Pleora_INCLUDE_DIR}/PvVersion.h" _pleora_VERSION_CONTENTS REGEX "#define NVERSION_STRING")
|
||||
|
||||
@ -29,7 +29,9 @@ find_path (PYLON_INCLUDE_DIR pylonc/PylonC.h
|
||||
find_library (_PylonCLib NAMES PylonC_MD_VC120 pylonc
|
||||
PATHS
|
||||
"${PYLON_DIR}/Development/lib/x64"
|
||||
"${PYLON_DIR}/lib64")
|
||||
"${PYLON_DIR}/lib64"
|
||||
"${PYLON_DIR}/lib"
|
||||
)
|
||||
|
||||
set (PYLON_LIBRARIES ${_PylonCLib})
|
||||
|
||||
|
||||
@ -1,17 +0,0 @@
|
||||
FILE(TO_CMAKE_PATH "$ENV{ZLIB_DIR}" TRY1_DIR)
|
||||
FILE(TO_CMAKE_PATH "${ZLIB_DIR}" TRY2_DIR)
|
||||
FILE(GLOB ZLIB_DIR ${TRY1_DIR} ${TRY2_DIR})
|
||||
|
||||
FIND_PATH(ZLIB_INCLUDE_DIR zlib.h
|
||||
PATHS ${ZLIB_DIR}/include /usr/local/include /usr/include
|
||||
ENV INCLUDE DOC "Directory containing zlib.h include file")
|
||||
|
||||
FIND_LIBRARY(ZLIB_LIBRARY NAMES z
|
||||
PATHS ${ZLIB_DIR}/bin ${ZLIB_DIR}/win32/bin ${ZLIB_DIR}/lib ${ZLIB_DIR}/win32/lib /usr/local/lib /usr/lib
|
||||
ENV LIB
|
||||
DOC "zlib library to link with"
|
||||
NO_SYSTEM_ENVIRONMENT_PATH)
|
||||
|
||||
IF (ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY)
|
||||
SET(ZLIB_FOUND TRUE)
|
||||
ENDIF (ZLIB_INCLUDE_DIR AND ZLIB_LIBRARY)
|
||||
64
common/get_unix_ns.h
Normal file
64
common/get_unix_ns.h
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef _GET_UNIX_NS_H_
|
||||
#define _GET_UNIX_NS_H_
|
||||
|
||||
#include <gmodule.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
typedef struct _MYFILETIME
|
||||
{
|
||||
guint32 dwLowDateTime;
|
||||
guint32 dwHighDateTime;
|
||||
} MYFILETIME;
|
||||
typedef void (*GetSystemTimeFunc) (MYFILETIME * lpSystemTimeAsFileTime);
|
||||
|
||||
static guint64
|
||||
get_unix_ns ()
|
||||
{
|
||||
MYFILETIME ftime;
|
||||
LARGE_INTEGER ltime;
|
||||
static GetSystemTimeFunc time_func = NULL;
|
||||
if (!time_func) {
|
||||
GModule *module;
|
||||
module = g_module_open ("Kernel32.dll", G_MODULE_BIND_LAZY);
|
||||
if (module) {
|
||||
if (!g_module_symbol (module, "GetSystemTimePreciseAsFileTime",
|
||||
(gpointer *) & time_func) || time_func == NULL) {
|
||||
GST_WARNING
|
||||
("Couldn't find GetSystemTimePreciseAsFileTime, falling back to GetSystemTimeAsFileTime");
|
||||
if (!g_module_symbol (module, "GetSystemTimeAsFileTime",
|
||||
(gpointer *) & time_func) || time_func == NULL) {
|
||||
GST_WARNING
|
||||
("Couldn't find GetSystemTimeAsFileTime, something is very wrong");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//GetSystemTimePreciseAsFileTime(&ftime);
|
||||
time_func (&ftime);
|
||||
ltime.HighPart = ftime.dwHighDateTime;
|
||||
ltime.LowPart = ftime.dwLowDateTime;
|
||||
ltime.QuadPart -= 11644473600000 * 10000;
|
||||
return ltime.QuadPart * 100;
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
|
||||
#ifdef __unix__
|
||||
static guint64
|
||||
get_unix_ns ()
|
||||
{
|
||||
struct timespec spec;
|
||||
|
||||
clock_gettime (CLOCK_REALTIME, &spec);
|
||||
return (guint64) spec.tv_sec * 1000000000L + (guint64) spec.tv_nsec;
|
||||
}
|
||||
#endif /* __unix__ */
|
||||
|
||||
|
||||
#endif /* _GET_UNIX_NS_H_ */
|
||||
@ -61,6 +61,12 @@ enum
|
||||
PROP_HIGHOUT,
|
||||
PROP_AUTO,
|
||||
PROP_INTERVAL,
|
||||
PROP_LOWER_SATURATION,
|
||||
PROP_UPPER_SATURATION,
|
||||
PROP_ROI_X,
|
||||
PROP_ROI_Y,
|
||||
PROP_ROI_WIDTH,
|
||||
PROP_ROI_HEIGHT,
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
@ -72,6 +78,12 @@ static GParamSpec *properties[PROP_LAST];
|
||||
#define DEFAULT_PROP_HIGHOUT 255
|
||||
#define DEFAULT_PROP_AUTO 0
|
||||
#define DEFAULT_PROP_INTERVAL (GST_SECOND / 2)
|
||||
#define DEFAULT_PROP_LOW_SAT 0.01
|
||||
#define DEFAULT_PROP_HIGH_SAT 0.01
|
||||
#define DEFAULT_PROP_ROI_X -1
|
||||
#define DEFAULT_PROP_ROI_Y -1
|
||||
#define DEFAULT_PROP_ROI_WIDTH 0
|
||||
#define DEFAULT_PROP_ROI_HEIGHT 0
|
||||
|
||||
/* the capabilities of the inputs and outputs */
|
||||
static GstStaticPadTemplate gst_videolevels_src_template =
|
||||
@ -224,6 +236,30 @@ gst_videolevels_class_init (GstVideoLevelsClass * klass)
|
||||
g_param_spec_uint64 ("interval", "Interval",
|
||||
"Interval of time between adjustments (in nanoseconds)", 1,
|
||||
G_MAXUINT64, DEFAULT_PROP_INTERVAL, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_LOWER_SATURATION,
|
||||
g_param_spec_double ("lower-saturation", "Lower saturation",
|
||||
"The fraction of the histogram to saturate on the low end when auto is enabled",
|
||||
0, 0.99, DEFAULT_PROP_LOW_SAT, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_UPPER_SATURATION,
|
||||
g_param_spec_double ("upper-saturation", "Upper saturation",
|
||||
"The fraction of the histogram to saturate on the upper end when auto is enabled",
|
||||
0, 0.99, DEFAULT_PROP_HIGH_SAT, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_ROI_X,
|
||||
g_param_spec_int ("roi-x", "ROI x",
|
||||
"Starting column of the ROI when auto is enabled (-1 centers ROI)",
|
||||
-1, G_MAXINT, DEFAULT_PROP_ROI_X, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_ROI_Y,
|
||||
g_param_spec_int ("roi-y", "ROI y",
|
||||
"Starting row of the ROI when auto is enabled (-1 centers ROI)",
|
||||
-1, G_MAXINT, DEFAULT_PROP_ROI_Y, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_ROI_WIDTH,
|
||||
g_param_spec_int ("roi-width", "ROI width",
|
||||
"Width of the ROI when auto is enabled (0 uses 1/2 of the image width)",
|
||||
0, G_MAXINT, DEFAULT_PROP_ROI_WIDTH, G_PARAM_READWRITE));
|
||||
g_object_class_install_property (gobject_class, PROP_ROI_HEIGHT,
|
||||
g_param_spec_int ("roi-height", "ROI height",
|
||||
"Height of the ROI when auto is enabled (0 uses 1/2 of the image height)",
|
||||
0, G_MAXINT, DEFAULT_PROP_ROI_HEIGHT, G_PARAM_READWRITE));
|
||||
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_videolevels_sink_template));
|
||||
@ -305,6 +341,30 @@ gst_videolevels_set_property (GObject * object, guint prop_id,
|
||||
videolevels->interval = g_value_get_uint64 (value);
|
||||
videolevels->last_auto_timestamp = GST_CLOCK_TIME_NONE;
|
||||
break;
|
||||
case PROP_LOWER_SATURATION:
|
||||
videolevels->lower_pix_sat = g_value_get_double (value);
|
||||
gst_videolevels_calculate_lut (videolevels);
|
||||
break;
|
||||
case PROP_UPPER_SATURATION:
|
||||
videolevels->upper_pix_sat = g_value_get_double (value);
|
||||
gst_videolevels_calculate_lut (videolevels);
|
||||
break;
|
||||
case PROP_ROI_X:
|
||||
videolevels->roi_x = g_value_get_int (value);
|
||||
videolevels->check_roi = TRUE;
|
||||
break;
|
||||
case PROP_ROI_Y:
|
||||
videolevels->roi_y = g_value_get_int (value);
|
||||
videolevels->check_roi = TRUE;
|
||||
break;
|
||||
case PROP_ROI_WIDTH:
|
||||
videolevels->roi_width = g_value_get_int (value);
|
||||
videolevels->check_roi = TRUE;
|
||||
break;
|
||||
case PROP_ROI_HEIGHT:
|
||||
videolevels->roi_height = g_value_get_int (value);
|
||||
videolevels->check_roi = TRUE;
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@ -325,7 +385,7 @@ gst_videolevels_get_property (GObject * object, guint prop_id, GValue * value,
|
||||
{
|
||||
GstVideoLevels *videolevels = GST_VIDEOLEVELS (object);
|
||||
|
||||
GST_DEBUG_OBJECT (videolevels, "getting property %s", pspec->name);
|
||||
GST_LOG_OBJECT (videolevels, "getting property %s", pspec->name);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_LOWIN:
|
||||
@ -346,6 +406,24 @@ gst_videolevels_get_property (GObject * object, guint prop_id, GValue * value,
|
||||
case PROP_INTERVAL:
|
||||
g_value_set_uint64 (value, videolevels->interval);
|
||||
break;
|
||||
case PROP_LOWER_SATURATION:
|
||||
g_value_set_double (value, videolevels->lower_pix_sat);
|
||||
break;
|
||||
case PROP_UPPER_SATURATION:
|
||||
g_value_set_double (value, videolevels->upper_pix_sat);
|
||||
break;
|
||||
case PROP_ROI_X:
|
||||
g_value_set_int (value, videolevels->roi_x);
|
||||
break;
|
||||
case PROP_ROI_Y:
|
||||
g_value_set_int (value, videolevels->roi_y);
|
||||
break;
|
||||
case PROP_ROI_WIDTH:
|
||||
g_value_set_int (value, videolevels->roi_width);
|
||||
break;
|
||||
case PROP_ROI_HEIGHT:
|
||||
g_value_set_int (value, videolevels->roi_height);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
@ -571,6 +649,8 @@ gst_videolevels_set_caps (GstBaseTransform * trans, GstCaps * incaps,
|
||||
|
||||
levels->nbins = MIN (4096, 1 << levels->bpp_in);
|
||||
|
||||
levels->check_roi = TRUE;
|
||||
|
||||
res = gst_videolevels_calculate_lut (levels);
|
||||
|
||||
return res;
|
||||
@ -689,13 +769,18 @@ gst_videolevels_reset (GstVideoLevels * videolevels)
|
||||
videolevels->upper_input = DEFAULT_PROP_HIGHIN;
|
||||
videolevels->lower_output = DEFAULT_PROP_LOWOUT;
|
||||
videolevels->upper_output = DEFAULT_PROP_HIGHOUT;
|
||||
videolevels->lower_pix_sat = DEFAULT_PROP_LOW_SAT;
|
||||
videolevels->upper_pix_sat = DEFAULT_PROP_HIGH_SAT;
|
||||
videolevels->roi_x = DEFAULT_PROP_ROI_X;
|
||||
videolevels->roi_y = DEFAULT_PROP_ROI_Y;
|
||||
videolevels->roi_width = DEFAULT_PROP_ROI_WIDTH;
|
||||
videolevels->roi_height = DEFAULT_PROP_ROI_HEIGHT;
|
||||
|
||||
videolevels->auto_adjust = DEFAULT_PROP_AUTO;
|
||||
videolevels->interval = DEFAULT_PROP_INTERVAL;
|
||||
videolevels->last_auto_timestamp = GST_CLOCK_TIME_NONE;
|
||||
|
||||
videolevels->lower_pix_sat = 0.01f;
|
||||
videolevels->upper_pix_sat = 0.01f;
|
||||
videolevels->check_roi = TRUE;
|
||||
videolevels->last_auto_timestamp = GST_CLOCK_TIME_NONE;
|
||||
|
||||
/* if GRAY8, this will be set in set_info */
|
||||
videolevels->nbins = 4096;
|
||||
@ -805,23 +890,29 @@ gst_videolevels_calculate_histogram (GstVideoLevels * videolevels,
|
||||
GST_LOG_OBJECT (videolevels, "Calculating histogram");
|
||||
if (videolevels->bpp_in > 8) {
|
||||
if (endianness == G_BYTE_ORDER) {
|
||||
for (r = 0; r < videolevels->height; r++) {
|
||||
for (c = 0; c < videolevels->width; c++) {
|
||||
for (r = videolevels->roi_y;
|
||||
r < videolevels->roi_y + videolevels->roi_height; r++) {
|
||||
for (c = videolevels->roi_x;
|
||||
c < videolevels->roi_x + videolevels->roi_width; c++) {
|
||||
hist[GINT_CLAMP (data[c + r * stride / 2] * factor, 0, nbins - 1)]++;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (r = 0; r < videolevels->height; r++) {
|
||||
for (c = 0; c < videolevels->width; c++) {
|
||||
hist[GINT_CLAMP (GUINT16_FROM_BE (data[c +
|
||||
r * stride / 2]) * factor, 0, nbins - 1)]++;
|
||||
for (r = videolevels->roi_y;
|
||||
r < videolevels->roi_y + videolevels->roi_height; r++) {
|
||||
for (c = videolevels->roi_x;
|
||||
c < videolevels->roi_x + videolevels->roi_width; c++) {
|
||||
hist[GINT_CLAMP (GUINT16_FROM_BE (data[c + r * stride / 2]) * factor,
|
||||
0, nbins - 1)]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
guint8 *data8 = (guint8 *) data;
|
||||
for (r = 0; r < videolevels->height; r++) {
|
||||
for (c = 0; c < videolevels->width; c++) {
|
||||
for (r = videolevels->roi_y;
|
||||
r < videolevels->roi_y + videolevels->roi_height; r++) {
|
||||
for (c = videolevels->roi_x;
|
||||
c < videolevels->roi_x + videolevels->roi_width; c++) {
|
||||
hist[GINT_CLAMP (data8[c + r * stride / 2] * factor, 0, nbins - 1)]++;
|
||||
}
|
||||
}
|
||||
@ -830,6 +921,38 @@ gst_videolevels_calculate_histogram (GstVideoLevels * videolevels,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gst_videolevels_check_roi (GstVideoLevels * filt)
|
||||
{
|
||||
GST_DEBUG_OBJECT (filt, "ROI before check is (%d, %d, %d, %d)", filt->roi_x,
|
||||
filt->roi_y, filt->roi_width, filt->roi_height);
|
||||
|
||||
/* adjust ROI if defaults are set */
|
||||
if (filt->roi_width <= 0) {
|
||||
filt->roi_width = filt->width / 2;
|
||||
}
|
||||
if (filt->roi_height <= 0) {
|
||||
filt->roi_height = filt->height / 2;
|
||||
}
|
||||
if (filt->roi_x <= -1) {
|
||||
filt->roi_x = (filt->width - filt->roi_width) / 2;
|
||||
}
|
||||
if (filt->roi_y <= -1) {
|
||||
filt->roi_y = (filt->height - filt->roi_height) / 2;
|
||||
}
|
||||
|
||||
/* ensure ROI is within image */
|
||||
filt->roi_x = GINT_CLAMP (filt->roi_x, 0, filt->width - 1);
|
||||
filt->roi_y = GINT_CLAMP (filt->roi_y, 0, filt->height - 1);
|
||||
filt->roi_width = GINT_CLAMP (filt->roi_width, 1, filt->width - filt->roi_x);
|
||||
filt->roi_height =
|
||||
GINT_CLAMP (filt->roi_height, 1, filt->height - filt->roi_y);
|
||||
|
||||
GST_DEBUG_OBJECT (filt, "ROI after check is (%d, %d, %d, %d)", filt->roi_x,
|
||||
filt->roi_y, filt->roi_width, filt->roi_height);
|
||||
}
|
||||
|
||||
/**
|
||||
* gst_videolevels_auto_adjust
|
||||
* @videolevels: #GstVideoLevels
|
||||
@ -840,48 +963,54 @@ gst_videolevels_calculate_histogram (GstVideoLevels * videolevels,
|
||||
* Returns: TRUE on success
|
||||
*/
|
||||
gboolean
|
||||
gst_videolevels_auto_adjust (GstVideoLevels * videolevels, guint16 * data)
|
||||
gst_videolevels_auto_adjust (GstVideoLevels * filt, guint16 * data)
|
||||
{
|
||||
guint npixsat;
|
||||
guint sum;
|
||||
gint i;
|
||||
gint size;
|
||||
gint pixel_count;
|
||||
gint minVal = 0;
|
||||
gint maxVal = (1 << videolevels->bpp_in) - 1;
|
||||
float factor = maxVal / (videolevels->nbins - 1.0f);
|
||||
gst_videolevels_calculate_histogram (videolevels, data);
|
||||
gint maxVal = (1 << filt->bpp_in) - 1;
|
||||
float factor = maxVal / (filt->nbins - 1.0f);
|
||||
|
||||
size = videolevels->width * videolevels->height;
|
||||
if (filt->check_roi) {
|
||||
gst_videolevels_check_roi (filt);
|
||||
filt->check_roi = FALSE;
|
||||
}
|
||||
|
||||
gst_videolevels_calculate_histogram (filt, data);
|
||||
|
||||
pixel_count = filt->roi_width * filt->roi_height;
|
||||
|
||||
/* pixels to saturate on low end */
|
||||
npixsat = (guint) (videolevels->lower_pix_sat * size);
|
||||
npixsat = (guint) (filt->lower_pix_sat * pixel_count);
|
||||
sum = 0;
|
||||
for (i = 0; i < videolevels->nbins; i++) {
|
||||
sum += videolevels->histogram[i];
|
||||
for (i = 0; i < filt->nbins; i++) {
|
||||
sum += filt->histogram[i];
|
||||
if (sum > npixsat) {
|
||||
videolevels->lower_input = (gint) CLAMP (i * factor, minVal, maxVal);
|
||||
filt->lower_input = (gint) CLAMP (i * factor, minVal, maxVal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* pixels to saturate on high end */
|
||||
npixsat = (guint) (videolevels->upper_pix_sat * size);
|
||||
npixsat = (guint) (filt->upper_pix_sat * pixel_count);
|
||||
sum = 0;
|
||||
for (i = videolevels->nbins - 1; i >= 0; i--) {
|
||||
sum += videolevels->histogram[i];
|
||||
for (i = filt->nbins - 1; i >= 0; i--) {
|
||||
sum += filt->histogram[i];
|
||||
if (sum > npixsat) {
|
||||
videolevels->upper_input = (gint) CLAMP (i * factor, minVal, maxVal);
|
||||
filt->upper_input = (gint) CLAMP (i * factor, minVal, maxVal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gst_videolevels_calculate_lut (videolevels);
|
||||
gst_videolevels_calculate_lut (filt);
|
||||
|
||||
GST_LOG_OBJECT (videolevels, "Contrast stretch with npixsat=%d, (%d, %d)",
|
||||
npixsat, videolevels->lower_input, videolevels->upper_input);
|
||||
GST_LOG_OBJECT (filt, "Contrast stretch with npixsat=%d, (%d, %d)",
|
||||
npixsat, filt->lower_input, filt->upper_input);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (videolevels), properties[PROP_LOWIN]);
|
||||
g_object_notify_by_pspec (G_OBJECT (videolevels), properties[PROP_HIGHIN]);
|
||||
g_object_notify_by_pspec (G_OBJECT (filt), properties[PROP_LOWIN]);
|
||||
g_object_notify_by_pspec (G_OBJECT (filt), properties[PROP_HIGHIN]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -891,6 +1020,7 @@ gst_videolevels_check_passthrough (GstVideoLevels * levels)
|
||||
{
|
||||
gboolean passthrough;
|
||||
if (levels->bpp_in == 8 &&
|
||||
levels->auto_adjust == GST_VIDEOLEVELS_AUTO_OFF &&
|
||||
levels->lower_input == levels->lower_output &&
|
||||
levels->upper_input == levels->upper_output) {
|
||||
passthrough = TRUE;
|
||||
|
||||
@ -83,14 +83,19 @@ struct _GstVideoLevels
|
||||
gint upper_input;
|
||||
gint lower_output;
|
||||
gint upper_output;
|
||||
gfloat lower_pix_sat;
|
||||
gfloat upper_pix_sat;
|
||||
gint roi_x;
|
||||
gint roi_y;
|
||||
gint roi_width;
|
||||
gint roi_height;
|
||||
|
||||
/* tables */
|
||||
gpointer lookup_table;
|
||||
|
||||
GstVideoLevelsAuto auto_adjust;
|
||||
guint64 interval;
|
||||
gfloat lower_pix_sat;
|
||||
gfloat upper_pix_sat;
|
||||
gboolean check_roi;
|
||||
gint nbins;
|
||||
gint * histogram;
|
||||
|
||||
|
||||
@ -23,7 +23,7 @@ target_link_libraries (${libname}
|
||||
${GSTREAMER_LIBRARY}
|
||||
${GSTREAMER_BASE_LIBRARY}
|
||||
${GSTREAMER_VIDEO_LIBRARY}
|
||||
${GSTREAMER_INCLUDE_DIR}/../../lib/z.lib
|
||||
${ZLIB_LIBRARIES}
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
|
||||
@ -42,6 +42,7 @@
|
||||
#include <gst/video/video.h>
|
||||
|
||||
#include "genicampixelformat.h"
|
||||
#include "get_unix_ns.h"
|
||||
|
||||
#include "unzip.h"
|
||||
|
||||
@ -56,24 +57,61 @@
|
||||
// "C:\\BitFlow SDK 6.20\\Bin64\\BFGTL.cti";
|
||||
// "C:\\Program Files\\Basler\\pylon 5\\Runtime\\x64\\ProducerGEV.cti";
|
||||
|
||||
// EVT
|
||||
#define GENAPI_WIDTH 0xA000
|
||||
#define GENAPI_HEIGHT 0xA004
|
||||
#define GENAPI_PIXFMT 0xA008
|
||||
#define GENAPI_PAYLOAD_SIZE 0xD008
|
||||
#define GENAPI_ACQMODE 0xB000
|
||||
#define GENAPI_ACQSTART 0xB004
|
||||
#define GENAPI_ACQSTOP 0xB008
|
||||
#define CTI_PATH "C:\\Program Files\\EVT\\eSDK\\bin\\EmergentGenTL.cti"
|
||||
static void
|
||||
initialize_evt_addresses (GstGenTlProducer * producer)
|
||||
{
|
||||
memset (producer, 0, sizeof (producer));
|
||||
producer->cti_path =
|
||||
g_strdup ("C:\\Program Files\\EVT\\eSDK\\bin\\EmergentGenTL.cti");
|
||||
producer->acquisition_mode_value = 0;
|
||||
producer->width = 0xA000;
|
||||
producer->height = 0xA004;
|
||||
producer->pixel_format = 0xA008;
|
||||
producer->payload_size = 0xD008;
|
||||
producer->acquisition_mode = 0xB000;
|
||||
producer->acquisition_start = 0xB004;
|
||||
producer->acquisition_stop = 0xB008;
|
||||
producer->tick_frequency_low = 0x0940;
|
||||
producer->tick_frequency_high = 0x093C;
|
||||
producer->timestamp_control_latch = 0x944;
|
||||
producer->timestamp_low = 0x094C;
|
||||
producer->timestamp_high = 0x0948;
|
||||
}
|
||||
|
||||
// Basler
|
||||
//#define GENAPI_WIDTH 0x30204
|
||||
//#define GENAPI_HEIGHT 0x30224
|
||||
//#define GENAPI_PAYLOAD_SIZE 0x10088
|
||||
//#define GENAPI_ACQMODE 0x40004
|
||||
//#define GENAPI_ACQSTART 0x40024
|
||||
//#define GENAPI_ACQSTOP 0x40044
|
||||
//#define CTI_PATH "C:\\Program Files\\Basler\\pylon 6\\Runtime\\x64\\ProducerGEV.cti"
|
||||
static void
|
||||
initialize_basler_addresses (GstGenTlProducer * producer)
|
||||
{
|
||||
memset (producer, 0, sizeof (producer));
|
||||
producer->cti_path =
|
||||
g_strdup
|
||||
("C:\\Program Files\\Basler\\pylon 5\\Runtime\\x64\\ProducerGEV.cti");
|
||||
producer->acquisition_mode_value = 2;
|
||||
producer->width = 0x30204;
|
||||
producer->height = 0x30224;
|
||||
producer->pixel_format = 0x30024;
|
||||
producer->payload_size = 0x10088;
|
||||
producer->acquisition_mode = 0x40004;
|
||||
producer->acquisition_start = 0x40024;
|
||||
producer->acquisition_stop = 0x40044;
|
||||
}
|
||||
|
||||
#define GST_TYPE_GENTLSRC_PRODUCER (gst_gentlsrc_producer_get_type())
|
||||
static GType
|
||||
gst_gentlsrc_producer_get_type (void)
|
||||
{
|
||||
static GType gentlsrc_producer_type = 0;
|
||||
static const GEnumValue gentlsrc_producer[] = {
|
||||
{GST_GENTLSRC_PRODUCER_BASLER, "Basler producer", "basler"},
|
||||
{GST_GENTLSRC_PRODUCER_EVT, "EVT producer", "evt"},
|
||||
{0, NULL, NULL},
|
||||
};
|
||||
|
||||
if (!gentlsrc_producer_type) {
|
||||
gentlsrc_producer_type =
|
||||
g_enum_register_static ("GstGenTlSrcProducer", gentlsrc_producer);
|
||||
}
|
||||
return gentlsrc_producer_type;
|
||||
}
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_gentlsrc_debug);
|
||||
#define GST_CAT_DEFAULT gst_gentlsrc_debug
|
||||
@ -96,10 +134,12 @@ static gboolean gst_gentlsrc_unlock_stop (GstBaseSrc * src);
|
||||
static GstFlowReturn gst_gentlsrc_create (GstPushSrc * src, GstBuffer ** buf);
|
||||
|
||||
static gchar *gst_gentlsrc_get_error_string (GstGenTlSrc * src);
|
||||
static void gst_gentlsrc_cleanup_tl (GstGenTlSrc * src);
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_PRODUCER,
|
||||
PROP_INTERFACE_INDEX,
|
||||
PROP_INTERFACE_ID,
|
||||
PROP_DEVICE_INDEX,
|
||||
@ -107,9 +147,11 @@ enum
|
||||
PROP_STREAM_INDEX,
|
||||
PROP_STREAM_ID,
|
||||
PROP_NUM_CAPTURE_BUFFERS,
|
||||
PROP_TIMEOUT
|
||||
PROP_TIMEOUT,
|
||||
PROP_ATTRIBUTES
|
||||
};
|
||||
|
||||
#define DEFAULT_PROP_PRODUCER GST_GENTLSRC_PRODUCER_BASLER
|
||||
#define DEFAULT_PROP_INTERFACE_INDEX 0
|
||||
#define DEFAULT_PROP_INTERFACE_ID ""
|
||||
#define DEFAULT_PROP_DEVICE_INDEX 0
|
||||
@ -118,6 +160,7 @@ enum
|
||||
#define DEFAULT_PROP_STREAM_ID ""
|
||||
#define DEFAULT_PROP_NUM_CAPTURE_BUFFERS 3
|
||||
#define DEFAULT_PROP_TIMEOUT 1000
|
||||
#define DEFAULT_PROP_ATTRIBUTES ""
|
||||
|
||||
/* pad templates */
|
||||
|
||||
@ -140,6 +183,13 @@ static GstStaticPadTemplate gst_gentlsrc_src_template =
|
||||
goto error; \
|
||||
}
|
||||
|
||||
#define HANDLE_GTL_WARNING(arg) \
|
||||
if (ret != GC_ERR_SUCCESS) { \
|
||||
GST_ELEMENT_WARNING (src, LIBRARY, FAILED, \
|
||||
(arg ": %s", gst_gentlsrc_get_error_string (src)), (NULL)); \
|
||||
goto error; \
|
||||
}
|
||||
|
||||
PGCGetInfo GTL_GCGetInfo;
|
||||
PGCGetLastError GTL_GCGetLastError;
|
||||
PGCInitLib GTL_GCInitLib;
|
||||
@ -197,7 +247,7 @@ gboolean
|
||||
gst_gentlsrc_bind_functions (GstGenTlSrc * src)
|
||||
{
|
||||
GModule *module;
|
||||
const char cti_path[] = CTI_PATH;
|
||||
const char *cti_path = src->producer.cti_path;
|
||||
gboolean ret = TRUE;
|
||||
|
||||
GST_DEBUG_OBJECT (src, "Trying to bind functions from '%s'", cti_path);
|
||||
@ -302,6 +352,12 @@ gst_gentlsrc_class_init (GstGenTlSrcClass * klass)
|
||||
gstpushsrc_class->create = GST_DEBUG_FUNCPTR (gst_gentlsrc_create);
|
||||
|
||||
/* Install GObject properties */
|
||||
g_object_class_install_property (gobject_class, PROP_PRODUCER,
|
||||
g_param_spec_enum ("producer", "Producer", "GenTL producer",
|
||||
GST_TYPE_GENTLSRC_PRODUCER, DEFAULT_PROP_PRODUCER,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE |
|
||||
GST_PARAM_MUTABLE_PLAYING));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_INTERFACE_INDEX,
|
||||
g_param_spec_uint ("interface-index", "Interface index",
|
||||
"Interface index number, zero-based, overridden by interface-id",
|
||||
@ -348,12 +404,22 @@ gst_gentlsrc_class_init (GstGenTlSrcClass * klass)
|
||||
"Timeout (ms)",
|
||||
"Timeout in ms (0 to use default)", 0, G_MAXINT,
|
||||
DEFAULT_PROP_TIMEOUT, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
|
||||
g_object_class_install_property (G_OBJECT_CLASS (klass),
|
||||
PROP_ATTRIBUTES, g_param_spec_string ("attributes",
|
||||
"Attributes", "Attributes to change, comma separated key=value pairs",
|
||||
DEFAULT_PROP_ATTRIBUTES, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE));
|
||||
|
||||
klass->hTL = NULL;
|
||||
g_mutex_init (&klass->tl_mutex);
|
||||
klass->tl_refcount = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_gentlsrc_reset (GstGenTlSrc * src)
|
||||
{
|
||||
src->gentl_latched_ticks = 0;
|
||||
src->unix_latched_time = 0;
|
||||
|
||||
src->error_string[0] = 0;
|
||||
src->last_frame_count = 0;
|
||||
src->total_dropped_frames = 0;
|
||||
@ -374,10 +440,12 @@ gst_gentlsrc_init (GstGenTlSrc * src)
|
||||
gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME);
|
||||
|
||||
/* initialize member variables */
|
||||
src->producer_prop = DEFAULT_PROP_PRODUCER;
|
||||
src->interface_index = DEFAULT_PROP_INTERFACE_INDEX;
|
||||
src->interface_id = g_strdup (DEFAULT_PROP_INTERFACE_ID);
|
||||
src->num_capture_buffers = DEFAULT_PROP_NUM_CAPTURE_BUFFERS;
|
||||
src->timeout = DEFAULT_PROP_TIMEOUT;
|
||||
src->attributes = g_strdup (DEFAULT_PROP_ATTRIBUTES);
|
||||
|
||||
src->stop_requested = FALSE;
|
||||
src->caps = NULL;
|
||||
@ -399,6 +467,9 @@ gst_gentlsrc_set_property (GObject * object, guint property_id,
|
||||
src = GST_GENTL_SRC (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_PRODUCER:
|
||||
src->producer_prop = g_value_get_enum (value);
|
||||
break;
|
||||
case PROP_INTERFACE_INDEX:
|
||||
src->interface_index = g_value_get_uint (value);
|
||||
break;
|
||||
@ -426,6 +497,11 @@ gst_gentlsrc_set_property (GObject * object, guint property_id,
|
||||
case PROP_TIMEOUT:
|
||||
src->timeout = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_ATTRIBUTES:
|
||||
if (src->attributes)
|
||||
g_free (src->attributes);
|
||||
src->attributes = g_strdup (g_value_get_string (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@ -442,6 +518,9 @@ gst_gentlsrc_get_property (GObject * object, guint property_id,
|
||||
src = GST_GENTL_SRC (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_PRODUCER:
|
||||
g_value_set_enum (value, src->producer_prop);
|
||||
break;
|
||||
case PROP_INTERFACE_INDEX:
|
||||
g_value_set_uint (value, src->interface_index);
|
||||
break;
|
||||
@ -466,6 +545,9 @@ gst_gentlsrc_get_property (GObject * object, guint property_id,
|
||||
case PROP_TIMEOUT:
|
||||
g_value_set_int (value, src->timeout);
|
||||
break;
|
||||
case PROP_ATTRIBUTES:
|
||||
g_value_set_string (value, src->attributes);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@ -718,7 +800,9 @@ gst_gentlsrc_get_payload_size (GstGenTlSrc * src)
|
||||
guint32 val = 0;
|
||||
size_t datasize = 4;
|
||||
// TODO: use node map
|
||||
ret = GTL_GCReadPort (src->hDevPort, GENAPI_PAYLOAD_SIZE, &val, &datasize);
|
||||
ret =
|
||||
GTL_GCReadPort (src->hDevPort, src->producer.payload_size, &val,
|
||||
&datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get payload size");
|
||||
payload_size = GUINT32_FROM_BE (val);
|
||||
GST_DEBUG_OBJECT (src, "Payload size defined by node map: %d",
|
||||
@ -771,52 +855,182 @@ error:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
static guint64
|
||||
gst_gentlsrc_get_gev_tick_frequency (GstGenTlSrc * src)
|
||||
{
|
||||
GstGenTlSrc *src = GST_GENTL_SRC (bsrc);
|
||||
GC_ERROR ret;
|
||||
uint32_t i, num_ifaces, num_devs;
|
||||
guint32 width, height, stride;
|
||||
GstVideoInfo vinfo;
|
||||
|
||||
GST_DEBUG_OBJECT (src, "start");
|
||||
if (!src->producer.tick_frequency_high || !src->producer.tick_frequency_low)
|
||||
return 0;
|
||||
|
||||
/* bind functions from CTI */
|
||||
/* TODO: Enumerate CTI files in env var GENTL_GENTL64_PATH */
|
||||
if (!gst_gentlsrc_bind_functions (src)) {
|
||||
GST_ELEMENT_ERROR (src, LIBRARY, INIT,
|
||||
("GenTL CTI could not be opened: %s", g_module_error ()), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
guint32 freq_low, freq_high;
|
||||
size_t datasize = 4;
|
||||
ret = GTL_GCReadPort (src->hDevPort, src->producer.tick_frequency_low, &freq_low, &datasize); // GevTimestampTickFrequencyLow
|
||||
HANDLE_GTL_ERROR ("Failed to get GevTimestampTickFrequencyLow");
|
||||
ret = GTL_GCReadPort (src->hDevPort, src->producer.tick_frequency_high, &freq_high, &datasize); // GevTimestampTickFrequencyHigh
|
||||
HANDLE_GTL_ERROR ("Failed to get GevTimestampTickFrequencyHigh");
|
||||
|
||||
/* initialize library and print info */
|
||||
ret = GTL_GCInitLib ();
|
||||
HANDLE_GTL_ERROR ("GenTL Producer library could not be initialized");
|
||||
guint64 tick_frequency =
|
||||
GUINT64_FROM_BE ((guint64) freq_low << 32 | freq_high);
|
||||
GST_DEBUG_OBJECT (src, "GEV Timestamp tick frequency is %llu",
|
||||
tick_frequency);
|
||||
|
||||
gst_gentl_print_gentl_impl_info (src);
|
||||
return tick_frequency;
|
||||
|
||||
/* open GenTL, print info, and update interface list */
|
||||
ret = GTL_TLOpen (&src->hTL);
|
||||
HANDLE_GTL_ERROR ("System module failed to open");
|
||||
error:
|
||||
return 0;
|
||||
}
|
||||
|
||||
gst_gentl_print_system_info (src);
|
||||
static guint64
|
||||
gst_gentlsrc_get_gev_timestamp_ticks (GstGenTlSrc * src)
|
||||
{
|
||||
GC_ERROR ret;
|
||||
size_t datasize = 4;
|
||||
guint32 val, ts_low, ts_high;
|
||||
|
||||
ret = GTL_TLUpdateInterfaceList (src->hTL, NULL, src->timeout);
|
||||
HANDLE_GTL_ERROR ("Failed to update interface list within timeout");
|
||||
val = GUINT32_TO_BE (2);
|
||||
datasize = sizeof (val);
|
||||
ret = GTL_GCWritePort (src->hDevPort, src->producer.timestamp_control_latch, &val, &datasize); // GevTimestampControlLatch
|
||||
HANDLE_GTL_WARNING ("Failed to latch timestamp GevTimestampControlLatch");
|
||||
|
||||
/* print info for all interfaces and open specified interface */
|
||||
ret = GTL_TLGetNumInterfaces (src->hTL, &num_ifaces);
|
||||
HANDLE_GTL_ERROR ("Failed to get number of interfaces");
|
||||
if (num_ifaces > 0) {
|
||||
GST_DEBUG_OBJECT (src, "Found %d GenTL interfaces", num_ifaces);
|
||||
for (i = 0; i < num_ifaces; ++i) {
|
||||
gst_gentl_print_interface_info (src, i);
|
||||
}
|
||||
ret = GTL_GCReadPort (src->hDevPort, src->producer.timestamp_low, &ts_low, &datasize); // GevTimestampValueLow
|
||||
HANDLE_GTL_WARNING ("Failed to get GevTimestampValueLow");
|
||||
ret = GTL_GCReadPort (src->hDevPort, src->producer.timestamp_high, &ts_high, &datasize); // GevTimestampValueHigh
|
||||
HANDLE_GTL_WARNING ("Failed to get GevTimestampValueHigh");
|
||||
guint64 ticks = GUINT64_FROM_BE ((guint64) ts_low << 32 | ts_high);
|
||||
GST_LOG_OBJECT (src, "Timestamp ticks are %llu", ticks);
|
||||
|
||||
return ticks;
|
||||
|
||||
error:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_gentlsrc_src_latch_timestamps (GstGenTlSrc * src)
|
||||
{
|
||||
guint64 unix_ts, gev_ts;
|
||||
|
||||
unix_ts = get_unix_ns ();
|
||||
gev_ts = gst_gentlsrc_get_gev_timestamp_ticks (src);
|
||||
|
||||
if (gev_ts != 0) {
|
||||
src->unix_latched_time = unix_ts;
|
||||
src->gentl_latched_ticks = gev_ts;
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (src, LIBRARY, FAILED, ("No interfaces found"), (NULL));
|
||||
goto error;
|
||||
GST_WARNING_OBJECT (src, "Failed to latch GEV time, using old latch value");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_gentlsrc_set_attributes (GstGenTlSrc * src)
|
||||
{
|
||||
gchar **pairs;
|
||||
int i;
|
||||
guint32 val;
|
||||
size_t datasize;
|
||||
GC_ERROR ret;
|
||||
|
||||
if (!src->attributes || src->attributes == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (src, "Trying to set following attributes: '%s'",
|
||||
src->attributes);
|
||||
|
||||
pairs = g_strsplit (src->attributes, ";", 0);
|
||||
|
||||
for (i = 0;; i++) {
|
||||
gchar **pair;
|
||||
|
||||
if (!pairs[i])
|
||||
break;
|
||||
|
||||
pair = g_strsplit (pairs[i], "=", 2);
|
||||
|
||||
if (!pair[0] || !pair[1]) {
|
||||
GST_WARNING_OBJECT (src, "Failed to parse attribute/value: '%s'", pair);
|
||||
continue;
|
||||
}
|
||||
|
||||
GST_DEBUG_OBJECT (src, "Setting attribute, '%s'='%s'", pair[0], pair[1]);
|
||||
|
||||
val = GUINT32_TO_BE (atoi (pair[1]));
|
||||
datasize = sizeof (val);
|
||||
ret =
|
||||
GTL_GCWritePort (src->hDevPort, strtol (pair[0], NULL, 16), &val,
|
||||
&datasize);
|
||||
if (ret != GC_ERR_SUCCESS) {
|
||||
GST_WARNING_OBJECT (src, "Failed to set attribute: %s",
|
||||
gst_gentlsrc_get_error_string (src));
|
||||
}
|
||||
g_strfreev (pair);
|
||||
}
|
||||
g_strfreev (pairs);
|
||||
|
||||
if (src->attributes) {
|
||||
g_free (src->attributes);
|
||||
src->attributes = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_gentlsrc_open_tl (GstGenTlSrc * src)
|
||||
{
|
||||
GstGenTlSrcClass *klass = GST_GENTL_SRC_GET_CLASS (src);
|
||||
GC_ERROR ret;
|
||||
uint32_t i, num_ifaces;
|
||||
|
||||
/* open framegrabber if it isn't already opened */
|
||||
if (klass->tl_refcount > 0) {
|
||||
GST_DEBUG_OBJECT (src,
|
||||
"Framegrabber interface already opened in this process, reusing");
|
||||
src->hTL = klass->hTL;
|
||||
klass->tl_refcount++;
|
||||
} else {
|
||||
/* initialize library and print info */
|
||||
ret = GTL_GCInitLib ();
|
||||
//HANDLE_GTL_ERROR ("GenTL Producer library could not be initialized");
|
||||
|
||||
gst_gentl_print_gentl_impl_info (src);
|
||||
|
||||
/* open GenTL, print info, and update interface list */
|
||||
ret = GTL_TLOpen (&src->hTL);
|
||||
HANDLE_GTL_ERROR ("System module failed to open");
|
||||
|
||||
gst_gentl_print_system_info (src);
|
||||
|
||||
ret = GTL_TLUpdateInterfaceList (src->hTL, NULL, src->timeout);
|
||||
HANDLE_GTL_ERROR ("Failed to update interface list within timeout");
|
||||
|
||||
/* print info for all interfaces and open specified interface */
|
||||
ret = GTL_TLGetNumInterfaces (src->hTL, &num_ifaces);
|
||||
HANDLE_GTL_ERROR ("Failed to get number of interfaces");
|
||||
if (num_ifaces > 0) {
|
||||
GST_DEBUG_OBJECT (src, "Found %d GenTL interfaces", num_ifaces);
|
||||
for (i = 0; i < num_ifaces; ++i) {
|
||||
gst_gentl_print_interface_info (src, i);
|
||||
}
|
||||
} else {
|
||||
GST_ELEMENT_ERROR (src, LIBRARY, FAILED, ("No interfaces found"), (NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
klass->hTL = src->hTL;
|
||||
klass->tl_refcount++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_gentlsrc_open_interface (GstGenTlSrc * src)
|
||||
{
|
||||
GstGenTlSrcClass *klass = GST_GENTL_SRC_GET_CLASS (src);
|
||||
GC_ERROR ret;
|
||||
|
||||
if (!src->interface_id || src->interface_id[0] == 0) {
|
||||
size_t id_size;
|
||||
@ -839,6 +1053,54 @@ gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
ret = GTL_TLOpenInterface (src->hTL, src->interface_id, &src->hIF);
|
||||
HANDLE_GTL_ERROR ("Interface module failed to open");
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
{
|
||||
GstGenTlSrc *src = GST_GENTL_SRC (bsrc);
|
||||
GstGenTlSrcClass *klass = GST_GENTL_SRC_GET_CLASS (src);
|
||||
GC_ERROR ret;
|
||||
uint32_t i, num_devs;
|
||||
guint32 width, height, stride;
|
||||
GstVideoInfo vinfo;
|
||||
|
||||
GST_DEBUG_OBJECT (src, "start");
|
||||
|
||||
if (src->producer_prop == GST_GENTLSRC_PRODUCER_BASLER) {
|
||||
initialize_basler_addresses (&src->producer);
|
||||
} else if (src->producer_prop == GST_GENTLSRC_PRODUCER_EVT) {
|
||||
initialize_evt_addresses (&src->producer);
|
||||
} else {
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
/* bind functions from CTI */
|
||||
/* TODO: Enumerate CTI files in env var GENTL_GENTL64_PATH */
|
||||
if (!gst_gentlsrc_bind_functions (src)) {
|
||||
GST_ELEMENT_ERROR (src, LIBRARY, INIT,
|
||||
("GenTL CTI could not be opened: %s", g_module_error ()), (NULL));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
g_mutex_lock (&klass->tl_mutex);
|
||||
|
||||
if (!gst_gentlsrc_open_tl (src)) {
|
||||
g_mutex_unlock (&klass->tl_mutex);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!gst_gentlsrc_open_interface (src)) {
|
||||
g_mutex_unlock (&klass->tl_mutex);
|
||||
goto error;
|
||||
}
|
||||
|
||||
g_mutex_unlock (&klass->tl_mutex);
|
||||
|
||||
ret = GTL_IFUpdateDeviceList (src->hIF, NULL, src->timeout);
|
||||
HANDLE_GTL_ERROR ("Failed to update device list within timeout");
|
||||
|
||||
@ -927,7 +1189,7 @@ gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY,
|
||||
("file url not supported yet"), (NULL));
|
||||
goto error;
|
||||
} else if (g_str_has_prefix (url, "local")) {
|
||||
} else if (g_ascii_strncasecmp (url, "local", 5) == 0) {
|
||||
GError *err = NULL;
|
||||
GMatchInfo *matchInfo;
|
||||
GRegex *regex;
|
||||
@ -938,7 +1200,7 @@ gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
|
||||
regex =
|
||||
g_regex_new
|
||||
("local:(?:///)?(?<filename>[^;]+);(?<address>[^;]+);(?<length>[^?]+)(?:[?]SchemaVersion=([^&]+))?",
|
||||
("[lL]ocal:(?:///)?(?<filename>[^;]+);(?<address>[^;]+);(?<length>[^?]+)(?:[?]SchemaVersion=([^&]+))?",
|
||||
(GRegexCompileFlags) 0, (GRegexMatchFlags) 0, &err);
|
||||
if (!regex) {
|
||||
goto error;
|
||||
@ -967,6 +1229,7 @@ gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
gchar *xml;
|
||||
|
||||
zipfilepath = g_build_filename (g_get_tmp_dir (), filename, NULL);
|
||||
GST_DEBUG_OBJECT (src, "Writing XML ZIP file to %s", zipfilepath);
|
||||
if (!g_file_set_contents (zipfilepath, buf, len, &err)) {
|
||||
GST_ELEMENT_ERROR (src, RESOURCE, TOO_LAZY,
|
||||
("Failed to write zipped XML to %s", zipfilepath), (NULL));
|
||||
@ -1012,6 +1275,7 @@ gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
g_free (zipfilepath);
|
||||
|
||||
zipfilepath = g_build_filename (g_get_tmp_dir (), xmlfilename, NULL);
|
||||
GST_DEBUG_OBJECT (src, "Writing XML file to %s", zipfilepath);
|
||||
g_file_set_contents (zipfilepath, xml, fileinfo.uncompressed_size,
|
||||
&err);
|
||||
g_free (zipfilepath);
|
||||
@ -1044,24 +1308,40 @@ gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
}
|
||||
}
|
||||
|
||||
src->tick_frequency = gst_gentlsrc_get_gev_tick_frequency (src);
|
||||
|
||||
gst_gentlsrc_set_attributes (src);
|
||||
|
||||
{
|
||||
// TODO: use GenTl node map for this
|
||||
guint32 val = 0;
|
||||
size_t datasize = 4;
|
||||
ret = GTL_GCReadPort (src->hDevPort, GENAPI_WIDTH, &val, &datasize);
|
||||
ret = GTL_GCReadPort (src->hDevPort, src->producer.width, &val, &datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get width");
|
||||
width = GUINT32_FROM_BE (val);
|
||||
ret = GTL_GCReadPort (src->hDevPort, GENAPI_HEIGHT, &val, &datasize);
|
||||
ret = GTL_GCReadPort (src->hDevPort, src->producer.height, &val, &datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get height");
|
||||
height = GUINT32_FROM_BE (val);
|
||||
GST_DEBUG_OBJECT (src, "Width and height %dx%d", width, height);
|
||||
|
||||
ret = GTL_GCReadPort (src->hDevPort, GENAPI_PIXFMT, &val, &datasize);
|
||||
ret =
|
||||
GTL_GCReadPort (src->hDevPort, src->producer.pixel_format, &val,
|
||||
&datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get height");
|
||||
const char *genicam_pixfmt;
|
||||
guint32 pixfmt_enum = GUINT32_FROM_BE (val);
|
||||
switch (pixfmt_enum) {
|
||||
|
||||
case 0x1: // Basler Ace
|
||||
case 0x01080001:
|
||||
genicam_pixfmt = "Mono8";
|
||||
break;
|
||||
case 0x5: // Basler Ace
|
||||
case 0x01100005:
|
||||
genicam_pixfmt = "Mono12";
|
||||
break;
|
||||
case 0x1100010: // Basler Ace
|
||||
genicam_pixfmt = "BayerGR12";
|
||||
break;
|
||||
case 0x01080009:
|
||||
genicam_pixfmt = "BayerRG8";
|
||||
break;
|
||||
@ -1132,15 +1412,19 @@ gst_gentlsrc_start (GstBaseSrc * bsrc)
|
||||
/* set AcquisitionMode to Continuous */
|
||||
// TODO: "Continuous" value can have different integer values, we need
|
||||
// to look it up in the node map (EVT is 0, Basler is 2)
|
||||
val = GUINT32_TO_BE (0);
|
||||
val = GUINT32_TO_BE (src->producer.acquisition_mode_value);
|
||||
datasize = sizeof (val);
|
||||
ret = GTL_GCWritePort (src->hDevPort, GENAPI_ACQMODE, &val, &datasize);
|
||||
ret =
|
||||
GTL_GCWritePort (src->hDevPort, src->producer.acquisition_mode, &val,
|
||||
&datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to start device acquisition");
|
||||
|
||||
/* send AcquisitionStart command */
|
||||
val = GUINT32_TO_BE (1);
|
||||
datasize = sizeof (val);
|
||||
ret = GTL_GCWritePort (src->hDevPort, GENAPI_ACQSTART, &val, &datasize);
|
||||
ret =
|
||||
GTL_GCWritePort (src->hDevPort, src->producer.acquisition_start, &val,
|
||||
&datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to start device acquisition");
|
||||
}
|
||||
|
||||
@ -1169,16 +1453,33 @@ error:
|
||||
src->hIF = NULL;
|
||||
}
|
||||
|
||||
if (src->hTL) {
|
||||
GTL_TLClose (src->hTL);
|
||||
src->hTL = NULL;
|
||||
}
|
||||
|
||||
GTL_GCCloseLib ();
|
||||
gst_gentlsrc_cleanup_tl (src);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_gentlsrc_cleanup_tl (GstGenTlSrc * src)
|
||||
{
|
||||
GstGenTlSrcClass *klass = GST_GENTL_SRC_GET_CLASS (src);
|
||||
if (src->hTL) {
|
||||
g_mutex_lock (&klass->tl_mutex);
|
||||
GST_DEBUG_OBJECT (src, "Framegrabber open with refcount=%d",
|
||||
klass->tl_refcount);
|
||||
klass->tl_refcount--;
|
||||
if (klass->tl_refcount == 0) {
|
||||
GST_DEBUG_OBJECT (src, "Framegrabber ref dropped to 0, closing");
|
||||
GTL_TLClose (src->hTL);
|
||||
src->hTL = NULL;
|
||||
}
|
||||
g_mutex_unlock (&klass->tl_mutex);
|
||||
src->hTL = NULL;
|
||||
|
||||
// don't close library, otherwise we can't reopen in the same process
|
||||
//GTL_GCCloseLib ();
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_gentlsrc_stop (GstBaseSrc * bsrc)
|
||||
{
|
||||
@ -1191,7 +1492,8 @@ gst_gentlsrc_stop (GstBaseSrc * bsrc)
|
||||
guint32 val = GUINT32_TO_BE (1);
|
||||
gsize datasize = sizeof (val);
|
||||
GC_ERROR ret =
|
||||
GTL_GCWritePort (src->hDevPort, GENAPI_ACQSTOP, &val, &datasize);
|
||||
GTL_GCWritePort (src->hDevPort, src->producer.acquisition_stop, &val,
|
||||
&datasize);
|
||||
|
||||
GTL_DSStopAcquisition (src->hDS, ACQ_STOP_FLAGS_DEFAULT);
|
||||
GTL_DSFlushQueue (src->hDS, ACQ_QUEUE_INPUT_TO_OUTPUT);
|
||||
@ -1210,12 +1512,7 @@ gst_gentlsrc_stop (GstBaseSrc * bsrc)
|
||||
src->hIF = NULL;
|
||||
}
|
||||
|
||||
if (src->hTL) {
|
||||
GTL_TLClose (src->hTL);
|
||||
src->hTL = NULL;
|
||||
}
|
||||
|
||||
GTL_GCCloseLib ();
|
||||
gst_gentlsrc_cleanup_tl (src);
|
||||
|
||||
GST_DEBUG_OBJECT (src, "Closed data stream, device, interface, and library");
|
||||
|
||||
@ -1298,6 +1595,8 @@ gst_gentlsrc_unlock_stop (GstBaseSrc * bsrc)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstStaticCaps unix_reference = GST_STATIC_CAPS ("timestamp/x-unix");
|
||||
|
||||
static GstBuffer *
|
||||
gst_gentlsrc_get_buffer (GstGenTlSrc * src)
|
||||
{
|
||||
@ -1311,18 +1610,43 @@ gst_gentlsrc_get_buffer (GstGenTlSrc * src)
|
||||
bool8_t buffer_is_incomplete, is_acquiring;
|
||||
guint8 *data_ptr;
|
||||
GstMapInfo minfo;
|
||||
GstClockTime unix_ts;
|
||||
uint64_t buf_timestamp_ticks;
|
||||
|
||||
datasize = sizeof (new_buffer_data);
|
||||
ret =
|
||||
GTL_EventGetData (src->hNewBufferEvent, &new_buffer_data, &datasize,
|
||||
src->timeout);
|
||||
HANDLE_GTL_ERROR ("Failed to get New Buffer event within timeout period");
|
||||
|
||||
datasize = sizeof (payload_type);
|
||||
/* sometimes we get non-image payloads, try several times for an image */
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
datasize = sizeof (new_buffer_data);
|
||||
ret =
|
||||
GTL_EventGetData (src->hNewBufferEvent, &new_buffer_data, &datasize,
|
||||
src->timeout);
|
||||
HANDLE_GTL_ERROR ("Failed to get New Buffer event within timeout period");
|
||||
|
||||
datasize = sizeof (payload_type);
|
||||
ret =
|
||||
GTL_DSGetBufferInfo (src->hDS, new_buffer_data.BufferHandle,
|
||||
BUFFER_INFO_PAYLOADTYPE, &datatype, &payload_type, &datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get payload type");
|
||||
if (payload_type != PAYLOAD_TYPE_IMAGE) {
|
||||
GST_WARNING_OBJECT (src, "Non-image payload type, trying again");
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (payload_type != PAYLOAD_TYPE_IMAGE) {
|
||||
GST_ELEMENT_ERROR (src, STREAM, TOO_LAZY,
|
||||
("Unsupported payload type: %d", payload_type), (NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
datasize = sizeof (buf_timestamp_ticks);
|
||||
ret =
|
||||
GTL_DSGetBufferInfo (src->hDS, new_buffer_data.BufferHandle,
|
||||
BUFFER_INFO_PAYLOADTYPE, &datatype, &payload_type, &datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get payload type");
|
||||
BUFFER_INFO_TIMESTAMP, &datatype, &buf_timestamp_ticks, &datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get buffer timestamp");
|
||||
GST_LOG_OBJECT (src, "Buffer GentTL timestamp: %llu", buf_timestamp_ticks);
|
||||
|
||||
datasize = sizeof (frame_id);
|
||||
ret =
|
||||
@ -1335,6 +1659,9 @@ gst_gentlsrc_get_buffer (GstGenTlSrc * src)
|
||||
GTL_DSGetBufferInfo (src->hDS, new_buffer_data.BufferHandle,
|
||||
BUFFER_INFO_IS_INCOMPLETE, &datatype, &buffer_is_incomplete, &datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get complete flag");
|
||||
if (buffer_is_incomplete) {
|
||||
GST_WARNING_OBJECT (src, "Buffer is incomplete");
|
||||
}
|
||||
|
||||
datasize = sizeof (buffer_size);
|
||||
ret =
|
||||
@ -1348,11 +1675,6 @@ gst_gentlsrc_get_buffer (GstGenTlSrc * src)
|
||||
BUFFER_INFO_BASE, &datatype, &data_ptr, &datasize);
|
||||
HANDLE_GTL_ERROR ("Failed to get buffer pointer");
|
||||
|
||||
if (payload_type != PAYLOAD_TYPE_IMAGE) {
|
||||
GST_ELEMENT_ERROR (src, STREAM, TOO_LAZY,
|
||||
("Unsupported payload type: %d", payload_type), (NULL));
|
||||
goto error;
|
||||
}
|
||||
// TODO: what if strides aren't same?
|
||||
|
||||
buf = gst_buffer_new_allocate (NULL, buffer_size, NULL);
|
||||
@ -1361,7 +1683,7 @@ gst_gentlsrc_get_buffer (GstGenTlSrc * src)
|
||||
("Failed to allocate buffer"), (NULL));
|
||||
goto error;
|
||||
}
|
||||
|
||||
// TODO: try to eliminate this memcpy by using gst_buffer_new_wrapped_full
|
||||
gst_buffer_map (buf, &minfo, GST_MAP_WRITE);
|
||||
orc_memcpy (minfo.data, (void *) data_ptr, minfo.size);
|
||||
gst_buffer_unmap (buf, &minfo);
|
||||
@ -1369,6 +1691,26 @@ gst_gentlsrc_get_buffer (GstGenTlSrc * src)
|
||||
GTL_DSQueueBuffer (src->hDS, new_buffer_data.BufferHandle);
|
||||
HANDLE_GTL_ERROR ("Failed to queue buffer");
|
||||
|
||||
GST_BUFFER_OFFSET (buf) = frame_id;
|
||||
|
||||
if (src->tick_frequency) {
|
||||
gint64 nanoseconds_after_latch;
|
||||
gint64 ticks_after_latch;
|
||||
|
||||
/* resync system clock and buffer clock periodically */
|
||||
if (GST_CLOCK_DIFF (src->unix_latched_time, get_unix_ns ()) > GST_SECOND) {
|
||||
gst_gentlsrc_src_latch_timestamps (src);
|
||||
}
|
||||
|
||||
ticks_after_latch = buf_timestamp_ticks - src->gentl_latched_ticks;
|
||||
nanoseconds_after_latch = (gint64)
|
||||
(ticks_after_latch * ((double) GST_SECOND / src->tick_frequency));
|
||||
unix_ts = src->unix_latched_time + nanoseconds_after_latch;
|
||||
GST_LOG_OBJECT (src, "Adding Unix timestamp: %llu", unix_ts);
|
||||
gst_buffer_add_reference_timestamp_meta (buf,
|
||||
gst_static_caps_get (&unix_reference), unix_ts, GST_CLOCK_TIME_NONE);
|
||||
}
|
||||
|
||||
return buf;
|
||||
|
||||
error:
|
||||
@ -1388,6 +1730,8 @@ gst_gentlsrc_create (GstPushSrc * psrc, GstBuffer ** buf)
|
||||
|
||||
GST_LOG_OBJECT (src, "create");
|
||||
|
||||
gst_gentlsrc_set_attributes (src);
|
||||
|
||||
*buf = gst_gentlsrc_get_buffer (src);
|
||||
if (!*buf) {
|
||||
return GST_FLOW_ERROR;
|
||||
@ -1426,7 +1770,6 @@ gst_gentlsrc_create (GstPushSrc * psrc, GstBuffer ** buf)
|
||||
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) {
|
||||
|
||||
@ -34,10 +34,46 @@ G_BEGIN_DECLS
|
||||
#define GST_GENTL_SRC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_GENTL_SRC,GstGenTlSrcClass))
|
||||
#define GST_IS_GENTL_SRC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_GENTL_SRC))
|
||||
#define GST_IS_GENTL_SRC_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_GENTL_SRC))
|
||||
#define GST_GENTL_SRC_GET_CLASS(klass) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((klass), GST_TYPE_GENTL_SRC, GstGenTlSrcClass))
|
||||
|
||||
typedef struct _GstGenTlSrc GstGenTlSrc;
|
||||
typedef struct _GstGenTlSrcClass GstGenTlSrcClass;
|
||||
|
||||
typedef struct _GstGenTlProducer GstGenTlProducer;
|
||||
struct _GstGenTlProducer
|
||||
{
|
||||
gchar* cti_path;
|
||||
guint32 acquisition_mode_value;
|
||||
|
||||
guint64 width;
|
||||
guint64 height;
|
||||
guint64 pixel_format;
|
||||
guint64 payload_size;
|
||||
guint64 acquisition_mode;
|
||||
guint64 acquisition_start;
|
||||
guint64 acquisition_stop;
|
||||
guint64 tick_frequency_low;
|
||||
guint64 tick_frequency_high;
|
||||
guint64 timestamp_control_latch;
|
||||
guint64 timestamp_low;
|
||||
guint64 timestamp_high;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* GstGenTlSrcProducer:
|
||||
* @GST_GENTLSRC_PRODUCER_BASLER: Basler producer
|
||||
* @GST_GENTLSRC_PRODUCER_EVT: EVT producer
|
||||
*
|
||||
* Producer to use.
|
||||
*/
|
||||
typedef enum {
|
||||
GST_GENTLSRC_PRODUCER_BASLER,
|
||||
GST_GENTLSRC_PRODUCER_EVT,
|
||||
} GstGenTlSrcProducer;
|
||||
|
||||
|
||||
struct _GstGenTlSrc
|
||||
{
|
||||
GstPushSrc base_gentlsrc;
|
||||
@ -50,8 +86,10 @@ struct _GstGenTlSrc
|
||||
PORT_HANDLE hDevPort;
|
||||
EVENT_HANDLE hNewBufferEvent;
|
||||
char error_string[MAX_ERROR_STRING_LEN];
|
||||
GstGenTlProducer producer;
|
||||
|
||||
/* properties */
|
||||
GstGenTlSrcProducer producer_prop;
|
||||
guint interface_index;
|
||||
gchar *interface_id;
|
||||
guint device_index;
|
||||
@ -60,11 +98,16 @@ struct _GstGenTlSrc
|
||||
gchar *stream_id;
|
||||
guint num_capture_buffers;
|
||||
gint timeout;
|
||||
gchar* attributes;
|
||||
|
||||
GstClockTime acq_start_time;
|
||||
guint32 last_frame_count;
|
||||
guint32 total_dropped_frames;
|
||||
|
||||
guint64 tick_frequency;
|
||||
guint64 unix_latched_time;
|
||||
guint64 gentl_latched_ticks;
|
||||
|
||||
GstCaps *caps;
|
||||
gint height;
|
||||
gint gst_stride;
|
||||
@ -75,6 +118,9 @@ struct _GstGenTlSrc
|
||||
struct _GstGenTlSrcClass
|
||||
{
|
||||
GstPushSrcClass base_gentlsrc_class;
|
||||
TL_HANDLE hTL;
|
||||
GMutex tl_mutex;
|
||||
guint tl_refcount;
|
||||
};
|
||||
|
||||
GType gst_gentlsrc_get_type (void);
|
||||
|
||||
@ -799,11 +799,11 @@ gst_kayasrc_stream_buffer_callback (STREAM_BUFFER_HANDLE buffer_handle,
|
||||
GST_BUFFER_OFFSET (buf) = src->frame_count;
|
||||
src->frame_count++;
|
||||
|
||||
if (src->kaya_base == GST_CLOCK_TIME_NONE) {
|
||||
/* assume delay between these two calls is negligible */
|
||||
src->kaya_base = KYFG_GetGrabberValueInt (src->cam_handle, "Timestamp");
|
||||
src->unix_base = g_get_real_time () * 1000;
|
||||
}
|
||||
//if (src->kaya_base == GST_CLOCK_TIME_NONE) {
|
||||
// assume delay between these two calls is negligible
|
||||
src->kaya_base = KYFG_GetGrabberValueInt (src->cam_handle, "Timestamp");
|
||||
src->unix_base = g_get_real_time () * 1000;
|
||||
//}
|
||||
#if GST_CHECK_VERSION(1,14,0)
|
||||
{
|
||||
GstClockTime unix_ts = src->unix_base + (timestamp - src->kaya_base);
|
||||
|
||||
@ -2,6 +2,10 @@ if (ENABLE_KLV)
|
||||
add_definitions(-DGST_PLUGINS_VISION_ENABLE_KLV)
|
||||
endif ()
|
||||
|
||||
if (UNIX)
|
||||
add_definitions(-D_UNIX_)
|
||||
endif ()
|
||||
|
||||
add_definitions(-D_XKEYCHECK_H)
|
||||
|
||||
set (SOURCES
|
||||
@ -42,6 +46,7 @@ set (LIBRARIES
|
||||
${GSTREAMER_LIBRARY}
|
||||
${GSTREAMER_BASE_LIBRARY}
|
||||
${GSTREAMER_VIDEO_LIBRARY}
|
||||
${Pleora_LIBRARIES}
|
||||
)
|
||||
|
||||
if (ENABLE_KLV)
|
||||
|
||||
@ -255,9 +255,6 @@ gst_pleorasink_init (GstPleoraSink * sink)
|
||||
sink->source = new GstStreamingChannelSource ();
|
||||
sink->source->SetSink (sink);
|
||||
sink->device = new PvSoftDeviceGEV ();
|
||||
|
||||
g_mutex_init (&sink->mutex);
|
||||
g_cond_init (&sink->cond);
|
||||
}
|
||||
|
||||
void
|
||||
@ -396,9 +393,6 @@ gst_pleorasink_dispose (GObject * object)
|
||||
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);
|
||||
@ -695,7 +689,6 @@ 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");
|
||||
@ -713,10 +706,7 @@ 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;
|
||||
}
|
||||
|
||||
@ -62,8 +62,6 @@ struct _GstPleoraSink
|
||||
gboolean camera_connected;
|
||||
GstVideoInfo vinfo;
|
||||
|
||||
GMutex mutex;
|
||||
GCond cond;
|
||||
gboolean acquisition_started;
|
||||
gboolean stop_requested;
|
||||
|
||||
|
||||
@ -29,10 +29,24 @@ GST_DEBUG_CATEGORY_EXTERN (pleorasink_debug);
|
||||
#define KLV_CHUNKID 0xFEDC
|
||||
|
||||
GstStreamingChannelSource::GstStreamingChannelSource ()
|
||||
: mAcquisitionBuffer (NULL), mBufferCount (0), mBufferValid (FALSE),
|
||||
mChunkModeActive(TRUE), mChunkKlvEnabled(TRUE), mKlvChunkSize(0)
|
||||
: mBufferCount (0),
|
||||
mChunkModeActive(TRUE), mChunkKlvEnabled(TRUE), mKlvChunkSize(0),
|
||||
mStreamingStarted(false)
|
||||
{
|
||||
mInputQueue = g_async_queue_new ();
|
||||
mOutputQueue = g_async_queue_new ();
|
||||
}
|
||||
|
||||
void GstStreamingChannelSource::OnStreamingStart()
|
||||
{
|
||||
GST_DEBUG_OBJECT(mSink, "Controller has requested streaming start");
|
||||
mStreamingStarted = true;
|
||||
}
|
||||
|
||||
void GstStreamingChannelSource::OnStreamingStop()
|
||||
{
|
||||
GST_DEBUG_OBJECT(mSink, "Controller has requested streaming stop");
|
||||
mStreamingStarted = false;
|
||||
}
|
||||
|
||||
void
|
||||
@ -128,8 +142,12 @@ gboolean GstStreamingChannelSource::GetKlvEnabled()
|
||||
PvBuffer * GstStreamingChannelSource::AllocBuffer ()
|
||||
{
|
||||
if (mBufferCount < mSink->num_internal_buffers) {
|
||||
GST_LOG_OBJECT(mSink, "Allocating buffer #%d", mBufferCount);
|
||||
PvBuffer *buf = new PvBuffer;
|
||||
buf->SetID(mBufferCount);
|
||||
mBufferCount++;
|
||||
return new PvBuffer;
|
||||
|
||||
return buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -142,39 +160,21 @@ void GstStreamingChannelSource::FreeBuffer (PvBuffer * aBuffer)
|
||||
|
||||
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;
|
||||
GST_LOG_OBJECT(mSink, "Pushing buffer #%d to input queue", aBuffer->GetID());
|
||||
g_async_queue_push(mInputQueue, aBuffer);
|
||||
return PvResult::Code::OK;
|
||||
}
|
||||
|
||||
PvResult GstStreamingChannelSource::RetrieveBuffer (PvBuffer ** aBuffer)
|
||||
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;
|
||||
}
|
||||
guint64 timeout_ms = 50;
|
||||
*aBuffer = (PvBuffer*)(g_async_queue_timeout_pop(mOutputQueue, timeout_ms * 1000));
|
||||
if (!*aBuffer) {
|
||||
GST_WARNING_OBJECT(mSink, "No buffers available in output queue after %llu ms, possibly slow video framerate", timeout_ms);
|
||||
return PvResult::Code::NO_AVAILABLE_DATA;
|
||||
}
|
||||
// Remove buffer from 1-deep pipeline
|
||||
*aBuffer = mAcquisitionBuffer;
|
||||
mAcquisitionBuffer = NULL;
|
||||
mBufferValid = FALSE;
|
||||
g_mutex_unlock (&mSink->mutex);
|
||||
|
||||
GST_LOG_OBJECT (mSink, "Returning buffer #%llu from output queue to GEV streaming thread", (*aBuffer)->GetID());
|
||||
return PvResult::Code::OK;
|
||||
}
|
||||
|
||||
@ -237,22 +237,21 @@ GstStreamingChannelSource::SetBuffer (GstBuffer * buf)
|
||||
{
|
||||
GByteArray * klv_byte_array = NULL;
|
||||
|
||||
GST_LOG_OBJECT (mSink, "SetBuffer");
|
||||
PvBuffer* pvBuffer;
|
||||
|
||||
g_mutex_lock (&mSink->mutex);
|
||||
|
||||
if (mAcquisitionBuffer == NULL) {
|
||||
GST_WARNING_OBJECT (mSink, "No PvBuffer available to fill, dropping frame");
|
||||
g_mutex_unlock (&mSink->mutex);
|
||||
guint64 timeout_ms = 50;
|
||||
pvBuffer = (PvBuffer*)(g_async_queue_timeout_pop (mInputQueue, timeout_ms * 1000));
|
||||
if (!pvBuffer) {
|
||||
if (mStreamingStarted) {
|
||||
GST_WARNING_OBJECT(mSink, "No free buffers, dropping frame. No consumers connected, or insufficient network bandwidth. Try increasing num-internal-buffers and/or packet-size.");
|
||||
}
|
||||
else {
|
||||
GST_LOG_OBJECT(mSink, "Dropping frame as no controller has requested streaming to start");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBufferValid) {
|
||||
GST_WARNING_OBJECT (mSink,
|
||||
"Buffer already filled, dropping incoming frame");
|
||||
g_mutex_unlock (&mSink->mutex);
|
||||
return;
|
||||
}
|
||||
GST_LOG_OBJECT(mSink, "Got buffer #%llu from input queue to fill with video data", pvBuffer->GetID());
|
||||
|
||||
if (mChunkKlvEnabled) {
|
||||
klv_byte_array = GetKlvByteArray (buf);
|
||||
@ -263,31 +262,31 @@ GstStreamingChannelSource::SetBuffer (GstBuffer * buf)
|
||||
}
|
||||
}
|
||||
|
||||
ResizeBufferIfNeeded (mAcquisitionBuffer);
|
||||
ResizeBufferIfNeeded (pvBuffer);
|
||||
|
||||
/* TODO: avoid memcpy (when strides align) by attaching to PvBuffer */
|
||||
GstMapInfo minfo;
|
||||
gst_buffer_map (buf, &minfo, GST_MAP_READ);
|
||||
|
||||
|
||||
guint8 *dst = mAcquisitionBuffer->GetDataPointer ();
|
||||
guint8 *dst = pvBuffer->GetDataPointer ();
|
||||
if (!dst) {
|
||||
GST_ERROR_OBJECT (mSink, "Have buffer to fill, but data pointer is invalid");
|
||||
g_mutex_unlock (&mSink->mutex);
|
||||
//g_mutex_unlock (&mSink->mutex);
|
||||
return;
|
||||
}
|
||||
g_assert (mAcquisitionBuffer->GetSize () >= minfo.size);
|
||||
g_assert (pvBuffer->GetSize () >= minfo.size);
|
||||
/* TODO: fix stride if needed */
|
||||
memcpy (dst, minfo.data, minfo.size);
|
||||
|
||||
gst_buffer_unmap (buf, &minfo);
|
||||
|
||||
mAcquisitionBuffer->ResetChunks();
|
||||
mAcquisitionBuffer->SetChunkLayoutID(CHUNKLAYOUTID);
|
||||
pvBuffer->ResetChunks();
|
||||
pvBuffer->SetChunkLayoutID(CHUNKLAYOUTID);
|
||||
|
||||
if (mChunkKlvEnabled && klv_byte_array && klv_byte_array->len > 0) {
|
||||
PvResult pvRes;
|
||||
pvRes = mAcquisitionBuffer->AddChunk (KLV_CHUNKID, (uint8_t*)klv_byte_array->data, klv_byte_array->len);
|
||||
pvRes = pvBuffer->AddChunk (KLV_CHUNKID, (uint8_t*)klv_byte_array->data, klv_byte_array->len);
|
||||
if (pvRes.IsOK ()) {
|
||||
GST_LOG_OBJECT (mSink, "Added KLV as chunk data (len=%d)", klv_byte_array->len);
|
||||
} else {
|
||||
@ -301,10 +300,8 @@ GstStreamingChannelSource::SetBuffer (GstBuffer * buf)
|
||||
g_byte_array_unref (klv_byte_array);
|
||||
}
|
||||
|
||||
mBufferValid = TRUE;
|
||||
g_cond_signal (&mSink->cond);
|
||||
|
||||
g_mutex_unlock (&mSink->mutex);
|
||||
GST_LOG_OBJECT(mSink, "Pushing buffer #%d to output queue", pvBuffer->GetID());
|
||||
g_async_queue_push(mOutputQueue, pvBuffer);
|
||||
}
|
||||
|
||||
GByteArray * GstStreamingChannelSource::GetKlvByteArray (GstBuffer * buf)
|
||||
|
||||
@ -26,6 +26,9 @@ class GstStreamingChannelSource:public PvStreamingChannelSourceDefault
|
||||
public:
|
||||
GstStreamingChannelSource ();
|
||||
|
||||
void OnStreamingStart();
|
||||
void OnStreamingStop();
|
||||
|
||||
void SetSink (GstPleoraSink * sink);
|
||||
void SetCaps (GstCaps * caps);
|
||||
void ResizeBufferIfNeeded (PvBuffer * aBuffer);
|
||||
@ -55,8 +58,8 @@ public:
|
||||
|
||||
private:
|
||||
GstPleoraSink * mSink;
|
||||
PvBuffer *mAcquisitionBuffer;
|
||||
gboolean mBufferValid;
|
||||
GAsyncQueue* mInputQueue;
|
||||
GAsyncQueue* mOutputQueue;
|
||||
gint mBufferCount;
|
||||
|
||||
gint mWidth;
|
||||
@ -67,4 +70,6 @@ private:
|
||||
bool mChunkKlvEnabled;
|
||||
|
||||
gint mKlvChunkSize;
|
||||
|
||||
bool mStreamingStarted;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user