diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index 671a27c10..46f440549 100755 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -25,15 +25,13 @@ IF(ENABLE_ARM64) set(VCPKG_OVERLAY_TRIPLETS ../vcpkg/triplets/community/arm64-linux.cmake) ENDIF(ENABLE_ARM64) -#use /MP only for language CXX (and not CUDA) and MSVC for both targets - -add_compile_options($<$:/MP>) - -set(CMAKE_CXX_STANDARD 14) - project(APRAPIPES) +set(CMAKE_CXX_STANDARD 17) +#use /MP only for language CXX (and not CUDA) and MSVC for both targets + +add_compile_options($<$:/MP>) IF(ENABLE_LINUX) set(ENV{PKG_CONFIG_PATH} "${CMAKE_SOURCE_DIR}/../thirdparty/gst-build/gst-build-1.16/outInstall/lib/x86_64-linux-gnu/pkgconfig/") @@ -71,6 +69,8 @@ ENDIF(NOT ENABLE_ARM64 AND NOT ENABLE_WINDOWS) find_package(Boost COMPONENTS system thread filesystem serialization log chrono unit_test_framework REQUIRED) find_package(OpenCV CONFIG REQUIRED) +find_package(redis++ CONFIG REQUIRED) +find_package(hiredis CONFIG REQUIRED) find_package(BZip2 REQUIRED) find_package(ZLIB REQUIRED) find_package(liblzma REQUIRED) @@ -266,9 +266,10 @@ SET(IP_FILES src/BrightnessContrastControlXform.cpp src/VirtualPTZ.cpp src/WebCamSource.cpp + src/RedisDBReader.cpp + src/EventSource.cpp src/FaceDetectorXform.cpp src/TextOverlayXForm.cpp - ) @@ -298,6 +299,8 @@ SET(IP_FILES_H include/BrightnessContrastControlXform.h include/VirtualPTZ.h include/WebCamSource.h + include/RedisDBReader.h + include/EventSource.h include/ApraFaceInfo.h include/FaceDetectsInfo.h include/FaceDetectorXform.h @@ -545,6 +548,8 @@ SET(UT_FILES test/brightness_contrast_tests.cpp test/virtualptz_tests.cpp test/webcam_source_tests.cpp + test/redisreader_tests.cpp + test/eventsource_tests.cpp test/facedetectorXform_tests.cpp test/sound_record_tests.cpp test/pullstratergy_tests.cpp diff --git a/base/include/EventSource.h b/base/include/EventSource.h new file mode 100644 index 000000000..f1d862906 --- /dev/null +++ b/base/include/EventSource.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Module.h" +#include + + +class EventSource +{ +public: + EventSource(); + virtual ~EventSource(); + void setPropsAfterReceivingCallback(); + void callbackWatcher(sw::redis::Redis& redis); + void listenKey(std::string key, std::function callback); + void onChange(std::string key); + bool subscriber_running; +private: + std::map> listenKeys; +}; \ No newline at end of file diff --git a/base/include/RedisDBReader.h b/base/include/RedisDBReader.h new file mode 100644 index 000000000..8684a09c3 --- /dev/null +++ b/base/include/RedisDBReader.h @@ -0,0 +1,13 @@ +#include +#include "GstOnvifRtspSink.h" + +class RedisDBReader +{ +public: + void *readValueFromVideoEncoderByField(sw::redis::Redis &redis, std::string encoderName, std::string fieldName); + void *readImageSettingValue(sw::redis::Redis &redis, std::string imagingSetting); + std::vector getUsersList(sw::redis::Redis &redis); + std::vector getSetMembers(sw::redis::Redis &redis, std::string setName); + std::unordered_map getHash(sw::redis::Redis &redis, std::string hashName); + std::string getValueByKeyName(sw::redis::Redis &redis, std::string keyName); +}; \ No newline at end of file diff --git a/base/src/EventSource.cpp b/base/src/EventSource.cpp new file mode 100644 index 000000000..75cd4c7ec --- /dev/null +++ b/base/src/EventSource.cpp @@ -0,0 +1,58 @@ +#include "EventSource.h" +#include + +EventSource::EventSource() +{ +} + +EventSource::~EventSource() +{ + subscriber_running = false; +} + +void EventSource::callbackWatcher(sw::redis::Redis& redis) +{ + + auto subscriber = redis.subscriber(); + subscriber.on_pmessage([&](std::string pattern, std::string channel, std::string msg) + { + onChange(pattern); + }); + + subscriber.psubscribe({"__keyspace@1__:onvif.media.Profiles*", + "__keyspace@1__:onvif.media.VideoEncoder*", + "__keyspace@1__:onvif.media.MetadataConfiguration*", + "__keyspace@1__:onvif.users.User*", + "__keyspace@1__:onvif.media.OSDConfigurations*", + "__keyspace@1__:onvif.media.VideoSourceConfiguration*"}); + subscriber_running = true; + while (subscriber_running) + { + try + { + subscriber.consume(); + } + catch (const sw::redis::TimeoutError &e) + { + continue; + } + catch (const sw::redis::Error &err) + { + std::cout << "caught some other err" << std::endl; + // Handle other exceptions. + } + } +} + +void EventSource::listenKey(std::string key, std::function callback) +{ + listenKeys.emplace(key, callback); + return; +} + +void EventSource::onChange(std::string key) +{ + auto callbackIterator = listenKeys.find(key); + callbackIterator->second(); + return; +} \ No newline at end of file diff --git a/base/src/RedisDBReader.cpp b/base/src/RedisDBReader.cpp new file mode 100644 index 000000000..e311c2390 --- /dev/null +++ b/base/src/RedisDBReader.cpp @@ -0,0 +1,52 @@ +#include "RedisDBReader.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "AIPExceptions.h" +#include "GstOnvifRtspSink.h" + + +std::vector RedisDBReader::getUsersList(sw::redis::Redis &redis) +{ + std::unordered_set user_keys; + std::vector users; + redis.smembers("onvif.users.User", std::inserter(user_keys, user_keys.begin())); + for (const auto& user_key : user_keys) { + std::unordered_map userinfo; + redis.hgetall(user_key, std::inserter(userinfo, userinfo.begin())); + try { + GStreamerOnvifRTSPSinkProps::User user = { userinfo.at("userName"), userinfo.at("password") }; + users.emplace_back(user); + LOG_INFO << "Got username " << user.username; + } catch (std::out_of_range &exc) { + LOG_ERROR << "could not retrieve user information for " << user_key; + } + } + return users; +} + +std::unordered_map RedisDBReader::getHash(sw::redis::Redis &redis, std::string hashName) +{ + std::unordered_map hashdata; + redis.hgetall(hashName,std::inserter(hashdata,hashdata.begin())); + return hashdata; +} + +std::string RedisDBReader::getValueByKeyName(sw::redis::Redis &redis, std::string keyName) +{ + LOG_INFO << "Fetching value for key named " << keyName; + auto val = redis.get(static_cast(keyName)); + return *val; +} + +std::vector RedisDBReader::getSetMembers(sw::redis::Redis &redis, std::string setName) +{ + std::vector members; + redis.smembers(static_cast(setName),std::inserter(members,members.begin())); + return members; +} \ No newline at end of file diff --git a/base/test/eventsource_tests.cpp b/base/test/eventsource_tests.cpp new file mode 100644 index 000000000..4c9c3dee5 --- /dev/null +++ b/base/test/eventsource_tests.cpp @@ -0,0 +1,53 @@ +//read the key from db +//make properties and set props +//define some keys +//instantiate brightness class +// + +#include +#include +#include "EventSource.h" +#include "Logger.h" +#include "AIPExceptions.h" +#include "RedisDBReader.h" +#include "StatSink.h" +#include "GstOnvifRtspSink.h" + +BOOST_AUTO_TEST_SUITE(eventsource_tests) + +BOOST_AUTO_TEST_CASE(eventsourcetest, *boost::unit_test::disabled()) +{ + //Redis DB needs to be populated - prerequisite + LoggerProps logprops; + logprops.logLevel = boost::log::trivial::severity_level::info; + Logger::initLogger(logprops); + + sw::redis::ConnectionOptions connection_options; + connection_options.type = sw::redis::ConnectionType::UNIX; + connection_options.path = "/run/redis/redis.sock"; + connection_options.db = 1; + connection_options.socket_timeout = std::chrono::milliseconds(10000); + sw::redis::Redis redis = sw::redis::Redis(connection_options); + + auto redisReader = boost::shared_ptr(new RedisDBReader()); + auto eventSource = boost::shared_ptr(new EventSource()); + GStreamerOnvifRTSPSinkProps props; + auto rtspSink = boost::shared_ptr(new GStreamerOnvifRTSPSink(props)); + + eventSource->listenKey("__keyspace@1__:onvif.users.User*", [&]() -> void + { + auto userList = redisReader->getUsersList(redis); + if (!userList.empty()) + { + props.userList = userList; + rtspSink->setProps(props); + LOG_INFO << "userList Fetched on callback"; + } + }); + + eventSource->callbackWatcher(redis); + boost::this_thread::sleep_for(boost::chrono::seconds(10000)); + LOG_INFO << "userList Fetched"; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file diff --git a/base/test/redisreader_tests.cpp b/base/test/redisreader_tests.cpp new file mode 100644 index 000000000..3e3a67011 --- /dev/null +++ b/base/test/redisreader_tests.cpp @@ -0,0 +1,124 @@ +#include +#include + +#include "Logger.h" +#include "AIPExceptions.h" +#include "RedisDBReader.h" +#include "StatSink.h" +#include + +#define MAP_STRING_TO_BOOL(str, var) ( \ + std::istringstream(map.find(str)->second) >> std::boolalpha >> var) + +#define MAP_STRING_TO_INT(str, var) ( \ + var = atoi(map.find(str)->second.c_str())) + +#define MAP_STRING_TO_STRING(str, var) ( \ + var = map.find(str)->second) + +BOOST_AUTO_TEST_SUITE(redisreader_tests) + +BOOST_AUTO_TEST_CASE(readertest, *boost::unit_test::disabled()) +{ + struct VideoEncoder + { + bool multicastautostart; + std::string token; + int multicastTTL; + int bitrate; + int framerate; + std::string multicastIPAddress; + bool multicastEnable; + std::string encoding; + int govlength; + std::string name; + int multicastPort; + VideoEncoder(boost::shared_ptr redisReader, sw::redis::Redis &redis, std::string videoEncoderName) + { + update(redisReader, redis, videoEncoderName); + } + + void update(boost::shared_ptr redisReader, sw::redis::Redis &redis, std::string videoEncoderName) + { + char hashName[1000]; + + sprintf(hashName, "onvif.media.VideoEncoder#%s", videoEncoderName.c_str()); + auto map = redisReader->getHash(redis, hashName); + + MAP_STRING_TO_BOOL("multicastautostart", multicastautostart); + + MAP_STRING_TO_STRING("token", token); + + MAP_STRING_TO_INT("multicastTTL", multicastTTL); + + MAP_STRING_TO_INT("bitrate", bitrate); + + MAP_STRING_TO_INT("framerate", framerate); + + MAP_STRING_TO_STRING("multicastIPAddress", multicastIPAddress); + + MAP_STRING_TO_BOOL("multicastEnable", multicastEnable); + + MAP_STRING_TO_STRING("encoding", encoding); + + MAP_STRING_TO_INT("govlength", govlength); + + MAP_STRING_TO_STRING("name", name); + + MAP_STRING_TO_INT("multicastPort", multicastPort); + } + }; + + struct Profile + { + bool fixed; + std::string videoEncoderToken; + std::string videoSourceToken; + std::string name; + std::string token; + Profile(boost::shared_ptr redisReader, sw::redis::Redis &redis, std::string profileName) + { + update(redisReader, redis, profileName); + } + + void update(boost::shared_ptr redisReader, sw::redis::Redis &redis, std::string profileName) + { + char hashName[1000]; + sprintf(hashName, "onvif.media.Profiles#%s", profileName.c_str()); + auto map = redisReader->getHash(redis, hashName); + + MAP_STRING_TO_BOOL("fixed", fixed); + + MAP_STRING_TO_STRING("videoEncoderToken", videoEncoderToken); + + MAP_STRING_TO_STRING("videoSourceToken", videoSourceToken); + + MAP_STRING_TO_STRING("name", name); + + MAP_STRING_TO_STRING("token", token); + } + }; + //Redis DB needs to be populated - prerequisite + LoggerProps logprops; + logprops.logLevel = boost::log::trivial::severity_level::info; + Logger::initLogger(logprops); + + sw::redis::ConnectionOptions connection_options; + connection_options.type = sw::redis::ConnectionType::UNIX; + connection_options.path = "/run/redis/redis.sock"; + connection_options.db = 1; + connection_options.socket_timeout = std::chrono::milliseconds(1000); + sw::redis::Redis redis = sw::redis::Redis(connection_options); + + // auto redisInstance = boost::shared_ptr(new RedisRepositoryController()); + auto redisReader = boost::shared_ptr(new RedisDBReader()); + auto userList = redisReader->getUsersList(redis); + auto videoEncoderRGBCameraH264 = VideoEncoder(redisReader, redis, "rgbcamera_H264"); + LOG_INFO << videoEncoderRGBCameraH264.bitrate; + auto profileFixed0 = Profile(redisReader, redis, "Fixed_0"); + LOG_INFO << profileFixed0.videoEncoderToken; + auto profiles = redisReader->getSetMembers(redis, "onvif.media.Profiles"); + LOG_INFO << "userList Fetched"; +} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file