/**
* SECTION:element-gstmtdataplugins
*
* The element tries to infer information from the driver camera to see if they are distracted and/or sleepy.
*
*
* Example launch line
* |[
* gst-launch-1.0 -v videotestsrc ! video/x-raw ! mtddriversafety detect-drowsiness=true detect-distraction=true driver-location=right ! appsink
* ]|
* This pipeline is a bin that first detects the face, then detects the facial landmarks on the face, then it uses some of the landmarks to see if the driver is sleepy and/or distracted.
*
*/
#pragma region includes
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include
#include
//#include
#include "gstmtdsmartrecord.h"
#include "nlohmann/json.hpp"
#include
#include
#include
#include // or make#include for C++17 and up
namespace fs = std::experimental::filesystem;
using namespace std;
using json = nlohmann::json;
#pragma endregion
GST_DEBUG_CATEGORY_STATIC(gst_mtdataplugins_debug_category);
#define GST_CAT_DEFAULT gst_mtdataplugins_debug_category
#pragma region prototypes and forward declarations
static void gst_mtdata_smartrecord_init(GstMtdSmartRecord *self);
void gst_mtdata_smartrecord_finalize(GObject *object);
void gst_mtdata_smartrecord_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
void gst_mtdata_smartrecord_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
#pragma endregion
#define SMARTRECORD_H264_CAPS "video/x-h264, stream-format={byte-stream,avc}"
#define SMARTRECORD_H265_CAPS "video/x-h265, stream-format={byte-stream,hvc1}"
#ifdef ADD_AUDIO
#define SMARTRECORD_AUDIO_CAPS "audio/x-raw, rate=[1, 2147483647], channels=[1, 2147483647], layout=interleaved"
#endif
#define DEFAULT_VIDEO_RECORD_CACHE_IN_SEC 30
#define DEFAULT_VIDEO_RECORD_DURATION_IN_SEC 30
#define DEFAULT_VIDEO_RECORD_TRIGGER_TIME_BEFORE_EVENT_SEC 15
#define DEFAULT_VIDEO_RECORD_TRIGGER_TIME_AFTER_EVENT_SEC 5
#define DEFAULT_FILE_PATH "/data/videos/"
#define DEFAULT_FILE_PREFIX "smartrecord"
#define DEFAULT_LOCK_FILE_PATH "/data/videos/locked/"
G_DEFINE_TYPE_WITH_CODE(GstMtdSmartRecord, gst_mtdata_smartrecord,
GST_TYPE_BIN,
GST_DEBUG_CATEGORY_INIT(gst_mtdataplugins_debug_category, "mtdsmartrecord",
0, "debug category for mtdsmartrecord element"));
static GstStaticPadTemplate gst_mtdata_smartrecord_sink_template =
GST_STATIC_PAD_TEMPLATE("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS(SMARTRECORD_H264_CAPS ";" SMARTRECORD_H265_CAPS));
#ifdef ADD_AUDIO
static GstStaticPadTemplate gst_mtdata_smartrecord_audio_sink_template =
GST_STATIC_PAD_TEMPLATE("audio_0",
GST_PAD_SINK,
GST_PAD_SOMETIMES,
GST_STATIC_CAPS(SMARTRECORD_AUDIO_CAPS));
#endif
enum
{
PROP_0,
PROP_VIDEO_RECORD_TRIGGER,
PROP_TIME_BEFORE_EVENT_SEC,
PROP_TIME_AFTER_EVENT_SEC,
PROP_FILE_PATH,
PROP_FILE_PREFIX,
PROP_FILE_LOCK_PREFIX,
};
static void *RecordCompleteCallback(NvDsSRRecordingInfo *pNvDsInfo, void *pSRData)
{
GST_INFO("RecordCompleteCallback");
if (pSRData != NULL)
{
SRData *pSRD = (SRData *)pSRData;
if (!fs::is_directory(pSRD->lock_file_path) || !fs::exists(pSRD->lock_file_path))
{ // Check if src folder exists
fs::create_directory(pSRD->lock_file_path); // create src folder
}
// move file
string from(pNvDsInfo->dirpath);
from += "/";
from += pNvDsInfo->filename;
string to(pSRD->lock_file_path);
to += "/";
to += pNvDsInfo->filename;
if (std::rename(from.c_str(), to.c_str()) < 0)
{
// GST_ERROR("error moving file from %s to %s", from.c_str(), to.c_str());
int t = 0;
}
}
return NULL;
}
static void
gst_mtdata_smartrecord_class_init(GstMtdSmartRecordClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
GstBinClass *base_bin_class = GST_BIN_CLASS(klass);
gst_element_class_add_static_pad_template(GST_ELEMENT_CLASS(klass), &gst_mtdata_smartrecord_sink_template);
#ifdef ADD_AUDIO
gst_element_class_add_static_pad_template(GST_ELEMENT_CLASS(klass), &gst_mtdata_smartrecord_audio_sink_template);
#endif
gst_element_class_set_static_metadata(GST_ELEMENT_CLASS(klass),
"MTData Smart Record element uses the NVIdia smart record element internally, this is a wrapper to make it work within gstdaemon", "Generic", "MTData Smart Record",
"MTData ");
gobject_class->finalize = GST_DEBUG_FUNCPTR(gst_mtdata_smartrecord_finalize);
gobject_class->set_property = GST_DEBUG_FUNCPTR(gst_mtdata_smartrecord_set_property);
gobject_class->get_property = GST_DEBUG_FUNCPTR(gst_mtdata_smartrecord_get_property);
g_object_class_install_property(gobject_class, PROP_VIDEO_RECORD_TRIGGER,
g_param_spec_boolean("video-record-trigger", "video-record-trigger", "trigger event to start writing video by setting this to true. automatically switches back to false after trigger is detected: [false|true] ",
false, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_class, PROP_TIME_BEFORE_EVENT_SEC,
g_param_spec_uint("time-before-event-sec", "time-before-event-sec", "number of seconds to go 'back in time' when an event is triggered: [0-120] ",
0, 120, DEFAULT_VIDEO_RECORD_TRIGGER_TIME_BEFORE_EVENT_SEC,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT)));
g_object_class_install_property(gobject_class, PROP_TIME_AFTER_EVENT_SEC,
g_param_spec_uint("time-after-event-sec", "time-after-event-sec", "number of seconds to add to the file after the last event: [0-120] ",
0, 120, DEFAULT_VIDEO_RECORD_TRIGGER_TIME_AFTER_EVENT_SEC,
(GParamFlags)(G_PARAM_READWRITE | G_PARAM_CONSTRUCT)));
g_object_class_install_property(gobject_class, PROP_FILE_PATH,
g_param_spec_string("file-path", "file-path", "video file location path [ /data/videos/ ] ",
DEFAULT_FILE_PATH, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_class, PROP_FILE_PREFIX,
g_param_spec_string("file-prefix", "file-prefix", "file prefix [ manditory parameter ] ",
DEFAULT_FILE_PREFIX, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
g_object_class_install_property(gobject_class, PROP_FILE_LOCK_PREFIX,
g_param_spec_string("file-lock-prefix", "file-lock-prefix", "video location where the files are moved to for upload [ /data/videos/locked/ ] ",
DEFAULT_LOCK_FILE_PATH, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)));
}
void gst_mtdata_smartrecord_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
{
GstMtdSmartRecord *self = GST_MTDSMARTRECORD(object);
GST_DEBUG_OBJECT(self, "set_property");
gchar *tmp_value;
gboolean tmp_bool;
uint tmp_uint;
switch (property_id)
{
case PROP_VIDEO_RECORD_TRIGGER:
tmp_bool = g_value_get_boolean(value);
if (tmp_bool && tmp_bool != self->sr_data.video_record_trigger)
{
self->sr_data.video_record_trigger = tmp_bool;
if (self->sr_data.video_record_trigger)
{
GST_INFO("triggering video recording");
self->sr_data.video_record_trigger = false;
NvDsSRSessionId sessionId;
NvDsSRStart(self->sr_data.m_pContext,
&(self->sr_data.sessionId),
self->sr_data.capture_time_before_event_sec, // time before the event
self->sr_data.capture_time_before_event_sec + self->sr_data.capture_time_after_event_sec, // total time
&(self->sr_data));
}
}
break;
case PROP_TIME_BEFORE_EVENT_SEC:
tmp_uint = g_value_get_uint(value);
self->sr_data.capture_time_before_event_sec = tmp_uint;
break;
case PROP_TIME_AFTER_EVENT_SEC:
tmp_uint = g_value_get_uint(value);
self->sr_data.capture_time_after_event_sec = tmp_uint;
break;
case PROP_FILE_PATH:
tmp_value = g_strdup(g_value_get_string(value));
if (tmp_value != NULL)
{
if (self->sr_data.file_path != NULL)
g_free(self->sr_data.file_path);
self->sr_data.file_path = g_strdup(tmp_value);
g_free(tmp_value);
}
break;
case PROP_FILE_PREFIX:
tmp_value = g_strdup(g_value_get_string(value));
if (tmp_value != NULL)
{
if (self->sr_data.file_prefix != NULL)
g_free(self->sr_data.file_prefix);
self->sr_data.file_prefix = g_strdup(tmp_value);
g_free(tmp_value);
}
break;
case PROP_FILE_LOCK_PREFIX:
tmp_value = g_strdup(g_value_get_string(value));
if (tmp_value != NULL)
{
if (self->sr_data.lock_file_path != NULL)
g_free(self->sr_data.lock_file_path);
self->sr_data.lock_file_path = g_strdup(tmp_value);
g_free(tmp_value);
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
void gst_mtdata_smartrecord_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
{
GstMtdSmartRecord *self = GST_MTDSMARTRECORD(object);
GST_DEBUG_OBJECT(self, "get_property");
switch (property_id)
{
case PROP_VIDEO_RECORD_TRIGGER:
g_value_set_boolean(value, self->sr_data.video_record_trigger);
break;
case PROP_TIME_BEFORE_EVENT_SEC:
g_value_set_uint(value, self->sr_data.capture_time_before_event_sec);
break;
case PROP_TIME_AFTER_EVENT_SEC:
g_value_set_uint(value, self->sr_data.capture_time_after_event_sec);
break;
case PROP_FILE_PATH:
g_value_set_string(value, self->sr_data.file_path);
break;
case PROP_FILE_PREFIX:
g_value_set_string(value, self->sr_data.file_prefix);
break;
case PROP_FILE_LOCK_PREFIX:
g_value_set_string(value, self->sr_data.lock_file_path);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
break;
}
}
static void gst_mtdata_smartrecord_init(GstMtdSmartRecord *self)
{
GstPad *sinkpad, *sinkgpad;
// GstPad *audio_sinkpad, *audio_sinkgpad;
// set default parameter values
self->sr_data.video_record_trigger = false;
self->sr_data.capture_time_before_event_sec = DEFAULT_VIDEO_RECORD_TRIGGER_TIME_BEFORE_EVENT_SEC;
self->sr_data.capture_time_after_event_sec = DEFAULT_VIDEO_RECORD_TRIGGER_TIME_AFTER_EVENT_SEC;
self->sr_data.file_path = g_strdup(DEFAULT_FILE_PATH);
self->sr_data.file_prefix = g_strdup(DEFAULT_FILE_PREFIX);
self->sr_data.lock_file_path = g_strdup(DEFAULT_LOCK_FILE_PATH);
// create smartrecord and setup environment for it
self->sr_data.m_initParams.containerType = NVDSSR_CONTAINER_MP4;
// Set single callback listener. Unique clients are identifed using client_data provided on Start session
self->sr_data.m_initParams.callback = RecordCompleteCallback;
// Set both width and height params to zero = no-transcode
self->sr_data.m_initParams.width = 0;
self->sr_data.m_initParams.height = 0;
// Filename prefix uses bintr name by default
self->sr_data.m_initParams.fileNamePrefix = const_cast(self->sr_data.file_prefix);
self->sr_data.m_initParams.dirpath = const_cast(self->sr_data.file_path);
self->sr_data.m_initParams.defaultDuration = DEFAULT_VIDEO_RECORD_DURATION_IN_SEC;
self->sr_data.m_initParams.cacheSize = DEFAULT_VIDEO_RECORD_CACHE_IN_SEC;
if (NvDsSRCreate(&(self->sr_data.m_pContext), &(self->sr_data.m_initParams)) != NVDSSR_STATUS_OK)
{
GST_ERROR("Failed to create Smart Record Context ");
return;
}
if (!self->sr_data.m_pContext->recordbin)
{
GST_ERROR("Could not create nv smart recorder. Exiting.");
return;
}
gst_bin_add_many(GST_BIN(self), self->sr_data.m_pContext->recordbin, NULL);
// sink ghost pad
sinkpad = gst_element_get_static_pad(self->sr_data.m_pContext->recordbin, "sink");
g_return_if_fail(sinkpad);
self->sr_data.sinkgpad = gst_ghost_pad_new("sink", sinkpad);
gst_pad_set_active(self->sr_data.sinkgpad, TRUE);
gst_element_add_pad(GST_ELEMENT(&(self->base_mtdataplugins)), self->sr_data.sinkgpad);
gst_object_unref(sinkpad);
#ifdef ADD_AUDIO
// audio sink ghost pad
audio_sinkpad = gst_element_get_static_pad(self->sr_data.m_pContext->recordbin, "asink");
g_return_if_fail(audio_sinkpad);
self->sr_data.audio_sinkgpad = gst_ghost_pad_new("audio_0", audio_sinkpad);
gst_pad_set_active(self->sr_data.audio_sinkgpad, TRUE);
gst_element_add_pad(GST_ELEMENT(&(self->base_mtdataplugins)), self->sr_data.audio_sinkgpad);
gst_object_unref(audio_sinkpad);
#endif
GST_INFO("----end of init ----");
}
void gst_mtdata_smartrecord_finalize(GObject *object)
{
GstMtdSmartRecord *self = GST_MTDSMARTRECORD(object);
GST_DEBUG_OBJECT(self, "finalize");
// clean up object here
if (self->sr_data.file_path != NULL)
g_free(self->sr_data.file_path);
if (self->sr_data.file_prefix != NULL)
g_free(self->sr_data.file_prefix);
if (self->sr_data.m_pContext)
NvDsSRDestroy(self->sr_data.m_pContext);
G_OBJECT_CLASS(gst_mtdata_smartrecord_parent_class)->finalize(object);
}
gboolean
register_mtd_smartrecord_element(GstPlugin *plugin)
{
GST_DEBUG_CATEGORY_INIT(gst_mtdataplugins_debug_category, "mtdsmartrecord", 0, "MTData Smart Record");
return gst_element_register(plugin, "mtdsmartrecord", GST_RANK_NONE, GST_TYPE_MTDSMARTRECORD);
}