提交 61621d77 authored 作者: blu's avatar blu

refactor, bugfix, ptz implementation and more

上级 b92bb722
...@@ -93,5 +93,6 @@ ...@@ -93,5 +93,6 @@
"forward_list": "cpp", "forward_list": "cpp",
"future": "cpp", "future": "cpp",
"maque_mem.h": "c" "maque_mem.h": "c"
} },
"C_Cpp.errorSquiggles": "Disabled"
} }
\ No newline at end of file
...@@ -140,42 +140,37 @@ payload: ...@@ -140,42 +140,37 @@ payload:
```json ```json
{ {
"time": ts, "time":1567669674,
"rid": "<request_rid>", "cmd":"config",
"type": "response", "rid":"001231554A20",
"cata": "config", "data":{
"sn": "A000000Z", "sn":"001231554A20",
"code": 0, "vgw":"192.168.55.104:7123",
"msg": "OK", "mqtt":"admin:vJ3zHqWrHbrqxVMT@evcloudsvc.ilabservice.cloud:11883",
"data": { "upload":"evcloudsvc.ilabservice.cloud:10009",
"vgw": "evcloudsvc.ilabservice.cloud:7123", "features":{
"mqtt": "admin:password@evcloudsvc.ilabservice.cloud:11883", "push":"1",
"upload": "evcloudsvc.ilabservice.cloud:10009", "motion":{
"features": { "enabled":0,
"motion": {
"enable": 1,
"level": 3, // 1 - 6
"region":{ "region":{
"minX": 0.1, "maxX":0.7,
"minY": 0.1, "maxY":0.7,
"maxX": 0.9, "minX":0.3,
"maxY": 0.9 "minY":0.3
}
}, },
"record":{ "level":4
"enable": 1,
"interval": 30, // seconds of one slice
"duration": 24 // hours of local recording
}, },
"recordLen":30,
"ai":{ "ai":{
"enable": 1, "enabled":1,
"face_thresh": 0.75, // 0 - 1 "faceThresh":0.65,
"human_thresh": 0.63, // 0 - 1 "humanThresh":0.63,
"region":{ "region":{
"minX": 0.1, "maxX":1,
"minY": 0.1, "maxY":1,
"maxX": 0.9, "minX":0,
"maxY": 0.9 "minY":0
}
} }
} }
} }
...@@ -196,7 +191,6 @@ REQUEST_VIDEOS_T ...@@ -196,7 +191,6 @@ REQUEST_VIDEOS_T
"cmd": "upload_video", "cmd": "upload_video",
"rid": "<random_str>", "rid": "<random_str>",
"data": { "data": {
{
"start": 1567669674, "start": 1567669674,
"end": 1567668000, "end": 1567668000,
"type": 6 "type": 6
...@@ -223,3 +217,52 @@ REQUEST_VIDEOS_T ...@@ -223,3 +217,52 @@ REQUEST_VIDEOS_T
} }
``` ```
### Report: AI Image
```json
{
"category":"ai_image",
"code":0,
"data":{
"data":"<base64_string>",
"encoding":"base64",
"format":"JPG",
"type":"image"
},
"msg":"new ai capture: pic, idx = 0, toltal = 1",
"rid":"",
"sn":"EVC001231554A20",
"time":1589537073532,
"type":"report"
}
```
### Request: status
```json
{
"time": 1567669674,
"cmd": "status",
"rid": "001231554A20",
"data": {}
}
```
### Request PTZ
```json
{
"time":1567669674,
"cmd":"ptz",
"rid":"001231554A20",
"data":{
"action":"left",
"degree":10
}
}
```
...@@ -200,6 +200,9 @@ mqtthelper_LIB_DEPENDS:STATIC= ...@@ -200,6 +200,9 @@ mqtthelper_LIB_DEPENDS:STATIC=
//Dependencies for target //Dependencies for target
ntp_LIB_DEPENDS:STATIC= ntp_LIB_DEPENDS:STATIC=
//Dependencies for target
ptz_LIB_DEPENDS:STATIC=
//Dependencies for target //Dependencies for target
smart_LIB_DEPENDS:STATIC= smart_LIB_DEPENDS:STATIC=
......
...@@ -24,7 +24,8 @@ add_library(evutils STATIC evutils.cc) ...@@ -24,7 +24,8 @@ add_library(evutils STATIC evutils.cc)
add_library(mqtthelper STATIC ../include/mqtt_helper.cc) add_library(mqtthelper STATIC ../include/mqtt_helper.cc)
add_library(motion STATIC motion.cc) add_library(motion STATIC motion.cc)
add_library(smart STATIC smart.cc) add_library(smart STATIC smart.cc)
add_library(ptz STATIC ptz.cc)
#add_library(util STATIC utils.cpp) #add_library(util STATIC utils.cpp)
add_executable(evcamera main.cc) add_executable(evcamera main.cc)
target_link_libraries(evcamera PUBLIC ntp paho-mqtt3a mqtthelper tcp_client evutils smart motion zmq ${COMMON_LIBS} ${XM_LIBS}) target_link_libraries(evcamera PUBLIC ntp paho-mqtt3a mqtthelper tcp_client evutils smart motion ptz zmq ${COMMON_LIBS} ${XM_LIBS})
[
{
"op":"replace",
"path":"/features/ai/faceThresh",
"value":0.65
},
{
"op":"replace",
"path":"/features/motion/enabled",
"value":0
},
{
"op":"replace",
"path":"/features/motion/level",
"value":4
},
{
"op":"replace",
"path":"/features/motion/region/maxX",
"value":0.7
},
{
"op":"replace",
"path":"/features/motion/region/maxY",
"value":0.7
},
{
"op":"replace",
"path":"/features/motion/region/minX",
"value":0.3
},
{
"op":"replace",
"path":"/features/motion/region/minY",
"value":0.3
},
{
"op":"replace",
"path":"/features/push",
"value":1
},
{
"op":"replace",
"path":"/mqtt",
"value":"admin:vJ3zHqWrHbrqxVMT@evcloudsvc.ilabservice.cloud:11883"
},
{
"op":"replace",
"path":"/vgw",
"value":"192.168.55.104:7123"
},
{
"op":"add",
"path":"/sn",
"value":"001231554A20"
}
]
\ No newline at end of file
...@@ -28,6 +28,11 @@ const string consts::kMsgCmdGetConfig = "get_config"; ...@@ -28,6 +28,11 @@ const string consts::kMsgCmdGetConfig = "get_config";
const string consts::kMsgCmdReboot = "reboot"; const string consts::kMsgCmdReboot = "reboot";
const string consts::kMsgCmdPtz = "ptz"; const string consts::kMsgCmdPtz = "ptz";
const string consts::kMsgType = "type"; const string consts::kMsgType = "type";
const string consts::kMsgTypeImage = "image";
const string consts::kMsgEncode = "encoding";
const string consts::kMsgEncodeBase64 = "base64";
const string consts::kMsgFormat = "format";
const string consts::kMsgFormatJpeg = "JPG";
const string consts::kMsgCata = "category"; const string consts::kMsgCata = "category";
const string consts::kMsgTypeReport = "report"; const string consts::kMsgTypeReport = "report";
const string consts::kMsgTypeResponse = "response"; const string consts::kMsgTypeResponse = "response";
...@@ -44,8 +49,11 @@ const string consts::sub_topic = "evcamera/v1.0/request/"; ...@@ -44,8 +49,11 @@ const string consts::sub_topic = "evcamera/v1.0/request/";
const string consts::pub_topic_response = "evcamera/v1.0/response/"; const string consts::pub_topic_response = "evcamera/v1.0/response/";
const string consts::pub_topic_report = "evcamera/v1.0/report"; const string consts::pub_topic_report = "evcamera/v1.0/report";
const string consts::mqtt_url = "tcp://admin:vJ3zHqWrHbrqxVMT@evcloudsvc.ilabservice.cloud:11883"; const string consts::mqtt_url = "tcp://admin:vJ3zHqWrHbrqxVMT@evcloudsvc.ilabservice.cloud:11883";
const string consts::vgw_addr = "tcp://evcloudsvc.ilabservice.cloud:7123";
const string consts::upload_addr = "tcp://evcloudsvc.ilabservice.cloud:10009";
consts &consts::self(){ consts &consts::self()
{
static consts cons; static consts cons;
return cons; return cons;
} }
...@@ -58,7 +66,7 @@ string msg_field(const json &msg, const string &field) ...@@ -58,7 +66,7 @@ string msg_field(const json &msg, const string &field)
bool is_sdcard_avail(char *path) bool is_sdcard_avail(char *path)
{ {
char p[] = "/sys/block/mmcblk0/size"; char p[] = "/sys/block/mmcblk0/size";
if(path == nullptr){ if(path == nullptr) {
path = p; path = p;
} }
std::ifstream fsize(path); std::ifstream fsize(path);
...@@ -66,7 +74,8 @@ bool is_sdcard_avail(char *path) ...@@ -66,7 +74,8 @@ bool is_sdcard_avail(char *path)
return (fsize >> size) && (size > 0); return (fsize >> size) && (size > 0);
} }
json make_default_config(){ json make_default_cloud_config()
{
return R"( return R"(
{ {
"vgw":"evcloudsvc.ilabservice.cloud:7123", "vgw":"evcloudsvc.ilabservice.cloud:7123",
...@@ -101,9 +110,22 @@ json make_default_config(){ ...@@ -101,9 +110,22 @@ json make_default_config(){
)"_json; )"_json;
} }
void get_mac_addr(char *buf, char *intf){ json make_default_sys_extra_config()
{
return R"(
{
"ptz":{
"x": 177.5,
"y": 0
}
}
)"_json;
}
void get_mac_addr(char *buf, char *intf)
{
char p[] = "/sys/class/net/eth0/address"; char p[] = "/sys/class/net/eth0/address";
if(intf == nullptr){ if(intf == nullptr) {
intf = p; intf = p;
} }
...@@ -111,8 +133,8 @@ void get_mac_addr(char *buf, char *intf){ ...@@ -111,8 +133,8 @@ void get_mac_addr(char *buf, char *intf){
string s; string s;
fsize >>s; fsize >>s;
int i = 0; int i = 0;
for(auto &k:s){ for(auto &k:s) {
if(k != ':'){ if(k != ':') {
*(buf+ i++) = ::toupper(k); *(buf+ i++) = ::toupper(k);
} }
} }
...@@ -123,7 +145,7 @@ bool get_sdcard_megabytes(uint64_t &total, uint64_t &free, char* path ) ...@@ -123,7 +145,7 @@ bool get_sdcard_megabytes(uint64_t &total, uint64_t &free, char* path )
{ {
struct statvfs stat; struct statvfs stat;
char p[] = "/mnt/sd"; char p[] = "/mnt/sd";
if(path == nullptr){ if(path == nullptr) {
path = p; path = p;
} }
...@@ -135,12 +157,13 @@ bool get_sdcard_megabytes(uint64_t &total, uint64_t &free, char* path ) ...@@ -135,12 +157,13 @@ bool get_sdcard_megabytes(uint64_t &total, uint64_t &free, char* path )
/// NOTES: avoid trucating errors /// NOTES: avoid trucating errors
total = (stat.f_blocks/1024.0) * (stat.f_bsize /1024.0); total = (stat.f_blocks/1024.0) * (stat.f_bsize /1024.0);
free = (stat.f_bavail/1024.0) * (stat.f_bsize /1024.0); free = (stat.f_bavail/1024.0) * (stat.f_bsize /1024.0);
spdlog::info("sd: {} {} {} {} {} {} {}", stat.f_bsize, stat.f_blocks, stat.f_bavail, stat.f_bfree ,stat.f_favail, stat.f_ffree, stat.f_frsize); spdlog::info("sd: {} {} {} {} {} {} {}", stat.f_bsize, stat.f_blocks, stat.f_bavail, stat.f_bfree,stat.f_favail, stat.f_ffree, stat.f_frsize);
return true; return true;
} }
/// returns negtive for failure, otherwise success /// returns negtive for failure, otherwise success
int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int timeoutMs) { int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int timeoutMs)
{
int ret = 0; int ret = 0;
*ctx = zmq_ctx_new(); *ctx = zmq_ctx_new();
*s = zmq_socket(*ctx, ZMQ_DEALER); *s = zmq_socket(*ctx, ZMQ_DEALER);
...@@ -151,7 +174,7 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int ...@@ -151,7 +174,7 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret)); zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
ret = 2; ret = 2;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_CNT, &ret, sizeof (ret)); zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_CNT, &ret, sizeof (ret));
if(sndQS!=0){ if(sndQS!=0) {
zmq_setsockopt(*s, ZMQ_SNDHWM, &sndQS, sizeof (sndQS)); zmq_setsockopt(*s, ZMQ_SNDHWM, &sndQS, sizeof (sndQS));
} }
if(timeoutMs != 0) { if(timeoutMs != 0) {
...@@ -166,7 +189,8 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int ...@@ -166,7 +189,8 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int
zmq_close(*s); zmq_close(*s);
zmq_ctx_destroy(*ctx); zmq_ctx_destroy(*ctx);
spdlog::debug("{} failed setsockopts ZMQ_ROUTING_ID to {}: {}", ident, addr, zmq_strerror(zmq_errno())); spdlog::debug("{} failed setsockopts ZMQ_ROUTING_ID to {}: {}", ident, addr, zmq_strerror(zmq_errno()));
}else{ }
else {
ret = zmq_connect(*s, addr.c_str()); ret = zmq_connect(*s, addr.c_str());
if(ret != 0) { if(ret != 0) {
zmq_close(*s); zmq_close(*s);
...@@ -178,7 +202,8 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int ...@@ -178,7 +202,8 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int
return ret; return ret;
} }
int setupRouter(void **ctx, void **s, string addr, int rcvQS){ int setupRouter(void **ctx, void **s, string addr, int rcvQS)
{
int ret = 0; int ret = 0;
*ctx = zmq_ctx_new(); *ctx = zmq_ctx_new();
*s = zmq_socket(*ctx, ZMQ_ROUTER); *s = zmq_socket(*ctx, ZMQ_ROUTER);
...@@ -199,7 +224,8 @@ int setupRouter(void **ctx, void **s, string addr, int rcvQS){ ...@@ -199,7 +224,8 @@ int setupRouter(void **ctx, void **s, string addr, int rcvQS){
return ret; return ret;
} }
int z_recv_multiple(void *s, vector<uint8_t> &buf, int &frames) { int z_recv_multiple(void *s, vector<uint8_t> &buf, int &frames)
{
int64_t more = 1; int64_t more = 1;
size_t more_size = sizeof(more); size_t more_size = sizeof(more);
int ret = 0; int ret = 0;
...@@ -229,12 +255,72 @@ int z_recv_multiple(void *s, vector<uint8_t> &buf, int &frames) { ...@@ -229,12 +255,72 @@ int z_recv_multiple(void *s, vector<uint8_t> &buf, int &frames) {
if(ret < 0 || (frames != 0 && cnt != frames)) { if(ret < 0 || (frames != 0 && cnt != frames)) {
spdlog::error("failed to recv msg: {}", ret < 0? zmq_strerror(ret): "invalid frames"); spdlog::error("failed to recv msg: {}", ret < 0? zmq_strerror(ret): "invalid frames");
return -1; return -1;
}else{ }
else {
frames = cnt; frames = cnt;
} }
return 0; return 0;
} }
namespace _internal_ {
mutex mutConfigFiles;
}
/// mk MUST NOT be null when isMerge is true
string save_json(json &data, string path, bool isMerge, fn_mk_default_json mk)
{
lock_guard<mutex> lk(_internal_::mutConfigFiles);
string rc;
json js;
if(isMerge) {
rc = load_json(js, path, mk);
if(!rc.empty()) {
return rc;
}
try {
js.merge_or_update(data);
}
catch(exception &e) {
rc = fmt::format("failed to merge json: {}, {} + {}", e.what(), js.to_string(), data.to_string());
return rc;
}
}
ofstream ocfg_(path, std::ios::trunc);
if(ocfg_.is_open()) {
ocfg_ << pretty_print(data);
ocfg_.flush();
ocfg_.close();
}
else {
rc = fmt::format("failed to open configuration file: {}", path);
spdlog::error(rc);
}
return rc;
}
string load_json(json &config, string path, fn_mk_default_json mk)
{
lock_guard<mutex> lk(_internal_::mutConfigFiles);
string rc;
try {
ifstream cfg_(path);
config = json::parse(cfg_);
}
catch(exception &e) {
spdlog::error("failed loading config: {}, force to make default config:\n{}", e.what(), config.to_string());
if(mk != nullptr) {
config = mk();
}
else {
rc = "no default make config function provided";
spdlog::error(rc);
}
}
return rc;
}
} }
\ No newline at end of file
...@@ -43,6 +43,11 @@ typedef struct consts{ ...@@ -43,6 +43,11 @@ typedef struct consts{
static const string kMsgCmdReboot; static const string kMsgCmdReboot;
static const string kMsgCmdPtz; static const string kMsgCmdPtz;
static const string kMsgType; static const string kMsgType;
static const string kMsgTypeImage;
static const string kMsgEncode;
static const string kMsgEncodeBase64;
static const string kMsgFormat;
static const string kMsgFormatJpeg;
static const string kMsgCata; static const string kMsgCata;
static const string kMsgTypeReport; static const string kMsgTypeReport;
static const string kMsgTypeResponse; static const string kMsgTypeResponse;
...@@ -60,12 +65,15 @@ typedef struct consts{ ...@@ -60,12 +65,15 @@ typedef struct consts{
static const string pub_topic_response; static const string pub_topic_response;
static const string pub_topic_report; static const string pub_topic_report;
static const string mqtt_url; static const string mqtt_url;
static const string vgw_addr;
static const string upload_addr;
static consts& self(); static consts& self();
}consts; }consts;
extern string msg_field(const json &msg, const string &field); extern string msg_field(const json &msg, const string &field);
extern bool is_sdcard_avail(char *path = nullptr); extern bool is_sdcard_avail(char *path = nullptr);
extern json make_default_config(); extern json make_default_cloud_config();
extern json make_default_sys_extra_config();
extern void get_mac_addr(char *buf, char *intf = nullptr); extern void get_mac_addr(char *buf, char *intf = nullptr);
extern bool get_sdcard_megabytes(uint64_t &total, uint64_t &free, char* path = nullptr); extern bool get_sdcard_megabytes(uint64_t &total, uint64_t &free, char* path = nullptr);
extern int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS=0, int timeoutMs = -1); extern int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS=0, int timeoutMs = -1);
...@@ -82,6 +90,8 @@ private: ...@@ -82,6 +90,8 @@ private:
size_t maxSize; size_t maxSize;
mutex mut; mutex mut;
TN oldestTs; TN oldestTs;
int freeMB = 0;
const int kReservedMB_ = 300;
cb_remove_elem<TN> fn_remove; cb_remove_elem<TN> fn_remove;
unsigned long cntInsert = 0; unsigned long cntInsert = 0;
public: public:
...@@ -93,8 +103,18 @@ public: ...@@ -93,8 +103,18 @@ public:
if(cntInsert % 10 == 0) { if(cntInsert % 10 == 0) {
oldestTs = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count() - 60*2*1000*maxSize; oldestTs = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count() - 60*2*1000*maxSize;
spdlog::info("record: maxSlices {}, oldestTs {}", maxSize, oldestTs); spdlog::info("record: maxSlices {}, oldestTs {}", maxSize, oldestTs);
uint64_t mt, mf;
if(get_sdcard_megabytes(mt, mf)){
freeMB = mf;
}else{
freeMB = -1;
}
} }
++cntInsert; ++cntInsert;
/// ATTENTION: we don't remove old file unless it can't be hold
bool forceRemoveOldFiles = false;
if(forceRemoveOldFiles){
if(elem < oldestTs) { if(elem < oldestTs) {
if(fn != nullptr) { if(fn != nullptr) {
(*fn)(elem); (*fn)(elem);
...@@ -104,6 +124,7 @@ public: ...@@ -104,6 +124,7 @@ public:
} }
return; return;
} }
}
if(list_.size() == 0) { if(list_.size() == 0) {
list_.insert(list_.begin(),elem); list_.insert(list_.begin(),elem);
...@@ -125,7 +146,7 @@ public: ...@@ -125,7 +146,7 @@ public:
list_.insert(itr.base(), elem); list_.insert(itr.base(), elem);
} }
if(list_.size() > maxSize) { if(list_.size() > maxSize||(freeMB != -1 && freeMB <= kReservedMB_)) {
lock_guard<mutex> lg(mut); lock_guard<mutex> lg(mut);
auto ts = *(list_.begin()); auto ts = *(list_.begin());
list_.erase(list_.begin()); list_.erase(list_.begin());
...@@ -200,8 +221,15 @@ public: ...@@ -200,8 +221,15 @@ public:
{ {
return list_; return list_;
} }
size_t capacity(){
return maxSize;
}
}; };
typedef json(*fn_mk_default_json)();
extern string save_json(json &data, string path, bool isMerge, fn_mk_default_json mk = nullptr);
extern string load_json(json &config, string path, fn_mk_default_json mk = nullptr);
} }
#endif #endif
\ No newline at end of file
#ifndef __INTER_TYPES_H__
#define __INTER_TYPES_H__
#include <mqtt_helper.hpp>
typedef struct ev_region_t {
float minx;
float miny;
float maxx;
float maxy;
} ev_region_t;
typedef struct ev_module_config_t {
int stat_current;
ev_region_t region;
bool enabled;
MqttHelper *pClient;
union {
struct {
int interval; // in seconds
} record;
struct {
float face_thresh; //[0,1]
float human_thresh;
int image_report_interval; // max in seconds
} ai;
struct {
int level; // 1 - 6
} motion;
struct {
int fps;
int video_quality;
int bitrate_kb;
int bitrate_type;
int push;
} sys;
} module;
} ev_module_config_t;
#endif
\ No newline at end of file
...@@ -20,6 +20,9 @@ ...@@ -20,6 +20,9 @@
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
#include <mqtt_helper.hpp> #include <mqtt_helper.hpp>
#include <signal.h> #include <signal.h>
#include <jsoncons_ext/jsonpatch/jsonpatch.hpp>
#include "internal_types.h"
#include "ptz.h"
using namespace std; using namespace std;
using namespace jsoncons; using namespace jsoncons;
...@@ -38,6 +41,7 @@ char dev_sn[TERMINAL_SN_SIZE] = {0}; ...@@ -38,6 +41,7 @@ char dev_sn[TERMINAL_SN_SIZE] = {0};
/// default video gateway info /// default video gateway info
int videoQuality = MAQUE_IMG_QUALITY_BETTER; int videoQuality = MAQUE_IMG_QUALITY_BETTER;
int gFrameFPS = 15;
uint64_t frameCntTotal = 0; uint64_t frameCntTotal = 0;
uint32_t frameCntIframe = 0; uint32_t frameCntIframe = 0;
uint32_t frameCntPframe = 0; uint32_t frameCntPframe = 0;
...@@ -83,42 +87,22 @@ OrderedList<long long> *gRecFilesList = nullptr; ...@@ -83,42 +87,22 @@ OrderedList<long long> *gRecFilesList = nullptr;
// //
string strMaQuePath = "/mnt/sd/Config/"; string strMaQuePath = "/mnt/sd/Config/";
/// why using singleton ? because global vriable initialization order between files is undefined! /// why using get function because global vriable initialization order between files is undefined!
/// this avoids getting empty result /// this avoids getting empty result
string strSDCardReservedPaths[] = {consts::self().recFilePath, strMaQuePath}; vector<string> get_sdcard_resered_dirs()
{
/// flags used to stop modules return {consts::recFilePath, strMaQuePath};
/// 0: stopped, 1: requested to stop, 2: runningstatic int }
typedef struct ev_region_t {
float minx;
float miny;
float maxx;
float maxy;
} ev_region_t;
typedef struct ev_module_config_t {
int stat_current;
ev_region_t region;
bool enabled;
union {
struct {
int interval; // in seconds
}record;
struct {
float face_thresh; //[0,1]
float human_thresh;
} ai;
struct motion{
int level; // 1 - 6
} motion;
} module;
} ev_module_config_t;
static ev_module_config_t gConfigRecord = {0}; static ev_module_config_t gConfigRecord = {0};
static ev_module_config_t gConfigAI = {0}; static ev_module_config_t gConfigAI = {0};
static ev_module_config_t gConfigMotion = {0}; static ev_module_config_t gConfigMotion = {0};
static ev_module_config_t gConfigVideo = {0};
string gMqttAddr, gVgwAddr, gUploadAddr;
enum EV_MSG_ERROR_CODE { enum EV_MSG_ERROR_CODE {
EV_MSG_ERROR_NONE, EV_MSG_ERROR_NONE,
EV_MSG_ERROR_INVALID_PARAM,
EV_MSG_ERROR_EXCEPTION, EV_MSG_ERROR_EXCEPTION,
EV_MSG_ERROR_UNSUPPORTED_CMD, EV_MSG_ERROR_UNSUPPORTED_CMD,
EV_MSG_ERROR_CONTENT_SYNTAX, EV_MSG_ERROR_CONTENT_SYNTAX,
...@@ -139,13 +123,6 @@ void clean_up(int sig) ...@@ -139,13 +123,6 @@ void clean_up(int sig)
fclose(args.recFD); fclose(args.recFD);
} }
// while (args.dataq->size() > 0) {
// DataItem elem = args.dataq->front();
// args.dataq->pop();
// MaQueVideoEncFrameInfo_s *pMem = (MaQueVideoEncFrameInfo_s *)elem.ud;
// MaQue_Demo_Mem_release(pMem->handleMem);
// }
//stop_md_bd(); //stop_md_bd();
LibXmMaQue_System_destroy(); LibXmMaQue_System_destroy();
exit(1); exit(1);
...@@ -161,6 +138,22 @@ bool is_big_endian(void) ...@@ -161,6 +138,22 @@ bool is_big_endian(void)
return bint.c[0] == 1; return bint.c[0] == 1;
} }
string save_configuration(json &data)
{
auto rc = save_json(data, configFilePath, false, nullptr);
if(!rc.empty()) {
spdlog::error(rc);
}
return rc;
}
string load_configuration(json &config)
{
auto rc = load_json(config, configFilePath, evutils::make_default_cloud_config);
return rc;
}
MaQueVideoEncodeCfg_s *init_stream_cfg(MaQueVideoEncodeCfg_s *cfg) MaQueVideoEncodeCfg_s *init_stream_cfg(MaQueVideoEncodeCfg_s *cfg)
{ {
cfg->eVidComp = (MaQueVideoCompress_e)-1; cfg->eVidComp = (MaQueVideoCompress_e)-1;
...@@ -281,15 +274,15 @@ void frame_send_entry(void *args) ...@@ -281,15 +274,15 @@ void frame_send_entry(void *args)
int ret = 0; int ret = 0;
int flagFail = 3; // 1 - sub, 2 - dealer , 3 - both int flagFail = 3; // 1 - sub, 2 - dealer , 3 - both
// should never exit // should never exit
while(1){ while(1) {
/// TODO: check enable status /// TODO: check enable status
this_thread::sleep_for(4s); this_thread::sleep_for(4s);
if(false){ if(false) {
continue; continue;
} }
// setup dealer // setup dealer
if(flagFail & 2){ if(flagFail & 2) {
ret = setupDealer(&pDealerCtx, &pDealer, string(dev_sn), vgwUrl, 10); ret = setupDealer(&pDealerCtx, &pDealer, string(dev_sn), vgwUrl, 10);
if(ret < 0) { if(ret < 0) {
spdlog::error("failed to connect cloud vgw: {}", vgwUrl); spdlog::error("failed to connect cloud vgw: {}", vgwUrl);
...@@ -301,7 +294,7 @@ void frame_send_entry(void *args) ...@@ -301,7 +294,7 @@ void frame_send_entry(void *args)
// setup sub // setup sub
if(flagFail &1){ if(flagFail &1) {
pSub = zmq_socket(pPubCtx, ZMQ_SUB); pSub = zmq_socket(pPubCtx, ZMQ_SUB);
if(pSub == nullptr) { if(pSub == nullptr) {
spdlog::error("failed to create frame sending socket"); spdlog::error("failed to create frame sending socket");
...@@ -342,7 +335,7 @@ void frame_send_entry(void *args) ...@@ -342,7 +335,7 @@ void frame_send_entry(void *args)
zmq_close(pSub); zmq_close(pSub);
pSub = nullptr; pSub = nullptr;
} }
if(flagFail &2){ if(flagFail &2) {
zmq_close(pDealer); zmq_close(pDealer);
zmq_ctx_destroy(pDealerCtx); zmq_ctx_destroy(pDealerCtx);
pDealer = nullptr; pDealer = nullptr;
...@@ -351,14 +344,15 @@ void frame_send_entry(void *args) ...@@ -351,14 +344,15 @@ void frame_send_entry(void *args)
} }
} }
void print_ts_files(OrderedList<long long> &_list) { void print_ts_files(OrderedList<long long> &_list)
{
spdlog::info("print file ts {}:", _list.items().size()); spdlog::info("print file ts {}:", _list.items().size());
int cnt = 0; int cnt = 0;
printf("\t"); printf("\t");
for(auto &k:_list.items()){ for(auto &k:_list.items()) {
cnt++; cnt++;
printf("%lld, ", k); printf("%lld, ", k);
if(cnt %6 == 0){ if(cnt %6 == 0) {
printf("\n\t"); printf("\n\t");
} }
} }
...@@ -366,7 +360,8 @@ void print_ts_files(OrderedList<long long> &_list) { ...@@ -366,7 +360,8 @@ void print_ts_files(OrderedList<long long> &_list) {
} }
void load_sd_files(OrderedList<long long> &_list){ void load_sd_files(OrderedList<long long> &_list)
{
auto now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); auto now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
try { try {
string fname, baseName; string fname, baseName;
...@@ -374,11 +369,12 @@ void load_sd_files(OrderedList<long long> &_list){ ...@@ -374,11 +369,12 @@ void load_sd_files(OrderedList<long long> &_list){
fname = entry.path().c_str(); fname = entry.path().c_str();
if(entry.file_size() == 0 || !entry.is_regular_file()||entry.path().extension() != ".h264") { if(entry.file_size() == 0 || !entry.is_regular_file()||entry.path().extension() != ".h264") {
spdlog::warn("load_sd_files skipped {} (empty/directory/!h264)", entry.path().c_str()); spdlog::warn("load_sd_files skipped {} (empty/directory/!h264)", entry.path().c_str());
fs::remove(fname);
continue; continue;
} }
long long ts = stoll(entry.path().stem().string()); long long ts = stoll(entry.path().stem().string());
if(ts < consts::TS_2020){ if(ts < consts::TS_2020) {
/// TODO: files records when offline with no valid time got /// TODO: files records when offline with no valid time got
fs::remove(entry.path()); fs::remove(entry.path());
spdlog::warn("remove records having invalid timestamp: {}", ts); spdlog::warn("remove records having invalid timestamp: {}", ts);
...@@ -388,16 +384,17 @@ void load_sd_files(OrderedList<long long> &_list){ ...@@ -388,16 +384,17 @@ void load_sd_files(OrderedList<long long> &_list){
} }
} }
catch(exception &e) { catch(exception &e) {
spdlog::error("{}:{} loadVideoFiles exception : {}", __FILE__, __LINE__, e.what()); spdlog::error("{}:{} load_sd_files exception : {}", __FILE__, __LINE__, e.what());
} }
} }
void remove_ts_file(long long ts){ void remove_ts_file(long long ts)
string fname = to_string(ts) + ".h264"; {
string fname = consts::recFilePath + to_string(ts) + ".h264";
fs::remove(fname); fs::remove(fname);
} }
void record_video_entry(void *args) void record_video_entry(ev_module_config_t *pArgs)
{ {
int ret = 0; int ret = 0;
// this thread will never exit // this thread will never exit
...@@ -408,7 +405,7 @@ void record_video_entry(void *args) ...@@ -408,7 +405,7 @@ void record_video_entry(void *args)
continue; continue;
} }
uint64_t total = 0, avail = 0; uint64_t total = 0, avail = 0;
if(!get_sdcard_megabytes(total, avail)){ if(!get_sdcard_megabytes(total, avail)) {
spdlog::error("failed to get sd card size"); spdlog::error("failed to get sd card size");
continue; continue;
} }
...@@ -417,7 +414,8 @@ void record_video_entry(void *args) ...@@ -417,7 +414,8 @@ void record_video_entry(void *args)
// calc num of slices // calc num of slices
// reserve 0.5GB space // reserve 0.5GB space
ssize_t maxSlices = (total - 512)/10; // multipled by 1.5 because videos take less space in midnight (variable bitrate)
ssize_t maxSlices = (ssize_t)((total - 512)/10*1.5);
gRecFilesList = new OrderedList<long long>(maxSlices, remove_ts_file); gRecFilesList = new OrderedList<long long>(maxSlices, remove_ts_file);
// load existing videos // load existing videos
load_sd_files(*gRecFilesList); load_sd_files(*gRecFilesList);
...@@ -455,18 +453,19 @@ void record_video_entry(void *args) ...@@ -455,18 +453,19 @@ void record_video_entry(void *args)
if(cnt % 30 == 0) { if(cnt % 30 == 0) {
nowTs = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count(); nowTs = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
} }
if(nowTs - preTs > gConfigRecord.module.record.interval*1000) { if(nowTs - preTs > pArgs->module.record.interval *1000) {
if(fp != nullptr) { if(fp != nullptr) {
if(fp->is_open()) if(fp->is_open())
fp->close(); fp->close();
delete fp; delete fp;
} }
fp = new fstream(consts::recFilePath+to_string(nowTs) + ".h264", std::ios::out|std::ios::binary); fp = new fstream(consts::recFilePath+to_string(nowTs) + ".h264", std::ios::out|std::ios::binary);
if(!fp->is_open()){ if(!fp->is_open()) {
spdlog::error("failed to open record file"); spdlog::error("failed to open record file");
delete fp; delete fp;
fp = nullptr; fp = nullptr;
}else{ }
else {
gRecFilesList->insert(nowTs); gRecFilesList->insert(nowTs);
} }
preTs = nowTs; preTs = nowTs;
...@@ -542,6 +541,146 @@ string verify_config(json &data) ...@@ -542,6 +541,146 @@ string verify_config(json &data)
return str; return str;
} }
/// TODO: we will figure out how to apply change without reboot.
/// this is a temprary implementaion for quich prototyping
string apply_config(json &data)
{
string rc;
try {
/// make defaults
gConfigVideo.module.sys.fps = 15; // secs
gConfigVideo.module.sys.video_quality = MAQUE_IMG_QUALITY_BETTER;
gConfigRecord.module.record.interval = 60 * 2; // 2 minutes interval
gConfigVideo.module.sys.bitrate_kb = 1024; // 1Mbps
gConfigVideo.module.sys.bitrate_type = MAQUE_BITRATE_CTRL_VBR;
gConfigVideo.module.sys.push = 0;
// motion
gConfigMotion.module.motion.level = 3;
gConfigMotion.enabled = 1;
gConfigMotion.region = {0,0,1,1};
// ai
gConfigAI.enabled = 1;
gConfigAI.region = {0,0,1,1};
gConfigAI.module.ai.face_thresh = 0.7;
gConfigAI.module.ai.human_thresh = 0.7;
gConfigAI.module.ai.image_report_interval = 10; // no two reports within 10 seconds
// vgw
if(gJsonConfig.contains(consts::kMsgConfigVgw)) {
gVgwAddr = gJsonConfig[consts::kMsgConfigVgw].as<string>();
}
else {
gVgwAddr = consts::vgw_addr;
}
// mqtt
if(gJsonConfig.contains(consts::kMsgConfigMqtt)) {
gMqttAddr = gJsonConfig[consts::kMsgConfigMqtt].as<string>();
}
else {
gMqttAddr = consts::mqtt_url;
}
// uploader
if(gJsonConfig.contains(consts::kMsgConfigUpload)) {
gUploadAddr = gJsonConfig[consts::kMsgConfigUpload].as<string>();
}
else {
gUploadAddr = consts::upload_addr;
}
if(gJsonConfig.contains(consts::kMsgConfigFeatures)) {
json &cfgFeatures = gJsonConfig[consts::kMsgConfigFeatures];
if(cfgFeatures.contains("logLevel")) {
logLevel = spdlog::level::from_str(cfgFeatures["logLevel"].as<string>());
spdlog::set_level(logLevel);
}
// fps
if(cfgFeatures.contains("fps")) {
gConfigVideo.module.sys.fps = cfgFeatures["fps"].as<int>();
}
else {
spdlog::warn("no fps configuration, using default: {}", gConfigVideo.module.sys.fps);
}
// record
if(cfgFeatures.contains("recordLen")) {
gConfigRecord.module.record.interval = cfgFeatures["recordLen"].as<int>();
}
else {
spdlog::warn("no recordLen configuration, using default: {}", gConfigRecord.module.record.interval);
}
// quality
if(cfgFeatures.contains("videoQuality")) {
gConfigVideo.module.sys.video_quality = cfgFeatures["videoQuality"].as<int>();
}
else {
spdlog::warn("no video quality configuration, using default: {}", gConfigVideo.module.sys.video_quality);
}
// bitrate
if(cfgFeatures.contains("bitrate")) {
gConfigVideo.module.sys.bitrate_kb = cfgFeatures["bitrate"].as<int>();
}
else {
spdlog::warn("no video bitrate configuration, using default: {}", gConfigVideo.module.sys.bitrate_kb);
}
if(cfgFeatures.contains("bitrateType")) {
gConfigVideo.module.sys.bitrate_type = cfgFeatures["bitrateType"].as<int>();
}
else {
spdlog::warn("no video bitrate type configuration, using default: {}", gConfigVideo.module.sys.bitrate_type);
}
if(cfgFeatures.contains("push")) {
gConfigVideo.module.sys.push = cfgFeatures["push"].as<int>();
}
else {
spdlog::warn("no push configuration, using default: {}", gConfigVideo.module.sys.push);
}
// motion
if(cfgFeatures.contains("motion")) {
json &motion = cfgFeatures["motion"];
if(motion.contains("enabled")) {
gConfigMotion.enabled = motion["enabled"].as<int>();
}
if(motion.contains("level")) {
gConfigMotion.module.motion.level = motion["level"].as<int>();
}
if(motion.contains("region") && motion["region"].contains("minX") && motion["region"].contains("minY") && motion["region"].contains("maxX") && motion["region"].contains("maxY")) {
gConfigMotion.region = {motion["region"]["minX"].as<float>(), motion["region"]["minY"].as<float>(), motion["region"]["maxX"].as<float>(), motion["region"]["maxY"].as<float>()};
}
}
spdlog::info("motion configuration: enabled {}, level {}, region ({},{}), ({}, {})", gConfigMotion.enabled, gConfigMotion.module.motion.level, gConfigMotion.region.minx, gConfigMotion.region.miny, gConfigMotion.region.maxx, gConfigMotion.region.maxy);
// ai
if(cfgFeatures.contains("ai")) {
json &ai = cfgFeatures["ai"];
if(ai.contains("enabled")) {
gConfigAI.enabled = ai["enabled"].as<int>();
}
if(ai.contains("faceThresh")) {
gConfigAI.module.ai.face_thresh = ai["faceThresh"].as<float>();
}
if(ai.contains("humanThresh")) {
gConfigAI.module.ai.human_thresh = ai["humanThresh"].as<float>();
}
if(ai.contains("region") && ai["region"].contains("minX") && ai["region"].contains("minY") && ai["region"].contains("maxX") && ai["region"].contains("maxY")) {
gConfigAI.region = {ai["region"]["minX"].as<float>(), ai["region"]["minY"].as<float>(), ai["region"]["maxX"].as<float>(), ai["region"]["maxY"].as<float>()};
}
}
}
}
catch(exception &e) {
/// TODO: save for reporting
rc = fmt::format("failed to apply config: {}, {}", e.what(), data.to_string());
spdlog::error(rc);
}
return rc;
}
/// handles all incoming request from cloud services /// handles all incoming request from cloud services
void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string topic) void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string topic)
{ {
...@@ -552,7 +691,7 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t ...@@ -552,7 +691,7 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
auto js = json::parse(msg); auto js = json::parse(msg);
auto str = verify_request(js); auto str = verify_request(js);
if(!str.empty()) { if(!str.empty()) {
spdlog::warn(str); spdlog::error(str);
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_report, EV_MSG_ERROR_CONTENT_SYNTAX, str, js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), json()); MqttMgr::report_response_args(gMqttClient, consts::pub_topic_report, EV_MSG_ERROR_CONTENT_SYNTAX, str, js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), json());
return; return;
} }
...@@ -563,30 +702,64 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t ...@@ -563,30 +702,64 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
auto data = js[consts::kMsgData].as<json>(); auto data = js[consts::kMsgData].as<json>();
/// TODO: implements cmds /// TODO: implements cmds
if(cmd == consts::kMsgCmdConfig) { if(cmd == consts::kMsgCmdConfig) {
// apply config // verify configuration
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data); auto rv = verify_config(data);
if(!rv.empty()) {
spdlog::error("failed to apply config: {}, {}", rv, data.to_string());
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid,EV_MSG_ERROR_INVALID_PARAM, rv, cmd, rid, data);
}
else {
// calc diff
auto patch = jsonpatch::from_diff(gJsonConfig, data);
if(patch.empty()) {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "no diff to apply", cmd, rid, data);
}
else {
// apply diff to tmp and verify
spdlog::info("json diff: {}", patch.to_string());
auto rc = verify_config(data);
if(!rc.empty()) {
}
else {
rc = apply_config(data);
}
if(!rc.empty()) {
spdlog::error(rc);
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, EV_MSG_ERROR_INVALID_PARAM, rc, cmd, rid, data);
}
else {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "rebooting to apply new config", cmd, rid, data);
/// TODO: using json diff to apply change without reboot
save_configuration(data);
spdlog::info("rebooting to apply new configuration");
system("reboot");
}
}
}
} }
else if(cmd == consts::kMsgCmdUploadVideo) { else if(cmd == consts::kMsgCmdUploadVideo) {
// upload video // upload video
if(gRecFilesList == nullptr){ if(gRecFilesList == nullptr) {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, -1, "no sd card", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data); MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, -1, "no sd card", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data);
}else{ }
else {
auto tss = data["start"].as<long long>(); auto tss = data["start"].as<long long>();
auto tse = data["end"].as<long long>(); auto tse = data["end"].as<long long>();
// guess unit in ms or s // guess unit in ms or s
if(tss/100000000000 == 0){ if(tss/100000000000 == 0) {
tss = tss * 1000; tss = tss * 1000;
tse = tse * 1000; tse = tse * 1000;
} }
long long offsetE = 0, offsetS = 0; long long offsetE = 0, offsetS = 0;
auto res = gRecFilesList->findByRange(tss, tse, offsetS, offsetE); auto res = gRecFilesList->findByRange(tss, tse, offsetS, offsetE);
if(res.empty()){ if(res.empty()) {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, -2, "no video found", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data); MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, -2, "no video found", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data);
}else{ }
else {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", js.contains(consts::consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data); MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", js.contains(consts::consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data);
print_ts_files(*gRecFilesList); print_ts_files(*gRecFilesList);
printf("matched %lld, %lld:\n\t", offsetS, offsetE); printf("matched (%lld, %lld) with offsets %lld, %lld:\n\t", tss, tse, offsetS, offsetE);
for(auto &k: res){ for(auto &k: res) {
printf("%lld, ", k); printf("%lld, ", k);
} }
printf("\n"); printf("\n");
...@@ -595,19 +768,51 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t ...@@ -595,19 +768,51 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
} }
else if(cmd == consts::kMsgCmdGetConfig) { else if(cmd == consts::kMsgCmdGetConfig) {
// response config // response config
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data); MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), gJsonConfig);
} }
else if(cmd == consts::kMsgCmdStatus) { else if(cmd == consts::kMsgCmdStatus) {
// response status // response status
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data); json stat;
stat["sd"] = json();
if(is_sdcard_avail()) {
uint64_t totalMB = 0, freeMB = 0;
if(get_sdcard_megabytes(totalMB, freeMB)) {
stat["sd"]["MBfree"] = freeMB;
stat["sd"]["MBTotal"] = totalMB;
}
}
// get first video ts
stat["record"] = json();
auto rec = gRecFilesList->items();
stat["record"]["capacity"] = gRecFilesList->capacity();
if(rec.size() > 0) {
stat["record"]["num"] = rec.size();
stat["record"]["first"] = *(rec.begin());
stat["record"]["last"] = *((rec.rbegin()));
}
/// TODO: subsystems running status
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", cmd, rid, stat);
} }
else if(cmd == consts::kMsgCmdPtz) { else if(cmd == consts::kMsgCmdPtz) {
// response Ptz control // response Ptz control
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", js.contains(consts::kMsgCmd)?js[consts::kMsgCmd].as<string>():string(""), js.contains(consts::kMsgRid)?js[consts::kMsgRid].as<string>():string(""), data); /// TODO:
}else if(cmd == "list_videos"){ if(!data.contains("action") || !data.contains("degree")) {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, EV_MSG_ERROR_INVALID_PARAM, "invalid param in data field", cmd, rid, data);
return;
}
str = ptz_move(data["action"].as<string>(), data["degree"].as<float>());
if(!str.empty()) {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, EV_MSG_ERROR_INVALID_PARAM, str, cmd, rid, data);
return;
}
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", cmd, rid, data);
}
else if(cmd == "list_videos") {
if(gRecFilesList != nullptr) if(gRecFilesList != nullptr)
print_ts_files(*gRecFilesList); print_ts_files(*gRecFilesList);
else{ else {
spdlog::warn("no local video files"); spdlog::warn("no local video files");
} }
} }
...@@ -635,29 +840,18 @@ MqttHelper* start_mqtt(void *arg) ...@@ -635,29 +840,18 @@ MqttHelper* start_mqtt(void *arg)
return nullptr; return nullptr;
} }
client->subscribe(consts::sub_topic + dev_sn, handle_mqtt_req, false); client->subscribe(consts::sub_topic + dev_sn, handle_mqtt_req, false);
/// TODO:
gConfigAI.pClient = client;
gConfigMotion.pClient = client;
gConfigRecord.pClient = client;
gConfigVideo.pClient = client;
return client; return client;
} }
// void create_sd_directories()
void updateConfigFile(json &config){ {
ofstream ocfg_(configFilePath, std::ios::trunc);
ocfg_ << pretty_print(gJsonConfig);
}
void readConfigFile(json &config){
try{
ifstream cfg_(configFilePath);
gJsonConfig = json::parse(cfg_);
}catch(exception &e){
gJsonConfig = evutils::make_default_config();
spdlog::error("failed get config: {}, force to make default config:\n{}", e.what(), gJsonConfig.to_string());
updateConfigFile(gJsonConfig);
}
}
void create_sd_directories(){
// create record paths // create record paths
for(auto k:strSDCardReservedPaths){ for(auto k:get_sdcard_resered_dirs()) {
spdlog::info("create path: {}", k); spdlog::info("create path: {}", k);
system((string("mkdir -p ") + k).c_str()); system((string("mkdir -p ") + k).c_str());
} }
...@@ -667,6 +861,7 @@ void create_sd_directories(){ ...@@ -667,6 +861,7 @@ void create_sd_directories(){
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int ret = XM_SUCCESS; int ret = XM_SUCCESS;
get_mac_addr(dev_sn); get_mac_addr(dev_sn);
spdlog::info("hi3518ev300, sn: {}, version: {}, BE: {}", dev_sn, evutils::version, is_big_endian()); spdlog::info("hi3518ev300, sn: {}, version: {}, BE: {}", dev_sn, evutils::version, is_big_endian());
...@@ -695,8 +890,13 @@ int main(int argc, char *argv[]) ...@@ -695,8 +890,13 @@ int main(int argc, char *argv[])
} }
/// TODO: load configuration /// TODO: load configuration
readConfigFile(gJsonConfig); load_configuration(gJsonConfig);
verify_config(gJsonConfig); verify_config(gJsonConfig);
auto rc = apply_config(gJsonConfig);
if(!rc.empty()) {
spdlog::error("failed to apply configuration: {}", gJsonConfig.to_string());
spdlog::error("defaults is used instead");
}
auto logLevel_ = getenv("LOG_LEVEL"); auto logLevel_ = getenv("LOG_LEVEL");
if(logLevel_) { if(logLevel_) {
...@@ -708,19 +908,10 @@ int main(int argc, char *argv[]) ...@@ -708,19 +908,10 @@ int main(int argc, char *argv[])
spdlog::set_level(logLevel); spdlog::set_level(logLevel);
} }
auto videoQuality_ = getenv("QUALITY");
if(videoQuality_) {
videoQuality = atoi(videoQuality_);
if(videoQuality >= MAQUE_IMG_QUALITY_NR || videoQuality < 0) {
videoQuality = MAQUE_IMG_QUALITY_BETTER;
}
}
// blocking update time, since time is critical to every other component // blocking update time, since time is critical to every other component
bool bGotTime = false; bool bGotTime = false;
time_t stm; time_t stm;
while(!bGotTime) while(!bGotTime) {
{
if(getNtpTime(&stm) >=0) { if(getNtpTime(&stm) >=0) {
spdlog::info("ntp got time"); spdlog::info("ntp got time");
::stime(&stm); ::stime(&stm);
...@@ -735,7 +926,7 @@ int main(int argc, char *argv[]) ...@@ -735,7 +926,7 @@ int main(int argc, char *argv[])
MaQue_Demo_Mem_Init(); MaQue_Demo_Mem_Init();
MaQueStartParam_s startParam = {MAQUE_VIDEO_STANDARD_PAL, {MAQUE_VIDEO_COMPRESS_H264, MAQUE_VIDEO_COMPRESS_H265}}; MaQueStartParam_s startParam = {MAQUE_VIDEO_STANDARD_PAL, {MAQUE_VIDEO_COMPRESS_H264, MAQUE_VIDEO_COMPRESS_H265}};
memcpy(startParam.aWritableDir, strMaQuePath.c_str(), sizeof(startParam.aWritableDir)); ::memcpy(startParam.aWritableDir, strMaQuePath.c_str(), sizeof(startParam.aWritableDir));
ret = LibXmMaQue_System_startUp(&startParam); ret = LibXmMaQue_System_startUp(&startParam);
spdlog::info("ret: {}", ret); spdlog::info("ret: {}", ret);
...@@ -762,24 +953,24 @@ int main(int argc, char *argv[]) ...@@ -762,24 +953,24 @@ int main(int argc, char *argv[])
// //
MaQueVideoEncodeCfg_s cfg; MaQueVideoEncodeCfg_s cfg;
init_stream_cfg(&cfg); init_stream_cfg(&cfg);
cfg.nFps = 15; cfg.nFps = gConfigVideo.module.sys.fps;
cfg.eIFrmIntvType = IFRAME_INTV_TYPE_TIME; cfg.eIFrmIntvType = IFRAME_INTV_TYPE_TIME;
cfg.nIFrameInterval = 10; cfg.nIFrameInterval = 10;
cfg.eImageQuality = (MaQueImageQuality_e)videoQuality; cfg.eImageQuality = (MaQueImageQuality_e)gConfigVideo.module.sys.video_quality;
cfg.eVidComp = MAQUE_VIDEO_COMPRESS_H264; cfg.eVidComp = MAQUE_VIDEO_COMPRESS_H264;
cfg.eBitrateCtrl = MAQUE_BITRATE_CTRL_VBR; cfg.eBitrateCtrl = (MaQueBitrateCtrl_e)gConfigVideo.module.sys.bitrate_type;
cfg.nBitRate = 1024; // 1Mbps cfg.nBitRate = gConfigVideo.module.sys.bitrate_kb;
ret = configure_stream((MaQueStreamChannel_e)0, &cfg); ret = configure_stream((MaQueStreamChannel_e)0, &cfg);
spdlog::info("cfg stream ret: {}", ret); spdlog::info("cfg stream ret: {}", ret);
/// TODO: load or make defualt configuration
gConfigRecord.module.record.interval = 60 * 2; // 2 minutes interval
ret = LibXmMaQue_VideoEnc_startStream(0, (MaQueStreamChannel_e)0, cb_frame_proc, (void *)&args); ret = LibXmMaQue_VideoEnc_startStream(0, (MaQueStreamChannel_e)0, cb_frame_proc, (void *)&args);
if (XM_SUCCESS == ret) { if (XM_SUCCESS == ret) {
spdlog::info("created record task successfully"); spdlog::info("created record task successfully");
} }
/// start ptz service
ptz_service_start();
signal(SIGINT, clean_up); signal(SIGINT, clean_up);
// signal(SIGTERM, clean_up); // signal(SIGTERM, clean_up);
signal(SIGKILL, clean_up); signal(SIGKILL, clean_up);
...@@ -794,25 +985,25 @@ int main(int argc, char *argv[]) ...@@ -794,25 +985,25 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
/// TODO: configuration issues handling /// TODO: configuration issues handling
if(msg_field(gJsonConfig, consts::kMsgConfigVgw).empty()){ if(msg_field(gJsonConfig, consts::kMsgConfigVgw).empty()) {
spdlog::error("missing vgw config"); spdlog::error("missing vgw config");
} }
thread thPush = thread(frame_send_entry, &args); thread thPush = thread(frame_send_entry, &args);
spdlog::info("sizeof pkt header {}, sizeof tv {}", sizeof(evpacket_t), sizeof(timeval)); spdlog::info("sizeof pkt header {}, sizeof tv {}", sizeof(evpacket_t), sizeof(timeval));
if(thPush.joinable()){ if(thPush.joinable()) {
thPush.detach(); thPush.detach();
} }
thread thVideoRecord = thread(record_video_entry, nullptr); thread thVideoRecord = thread(record_video_entry, &gConfigRecord);
if(thVideoRecord.joinable()){ if(thVideoRecord.joinable()) {
thVideoRecord.detach(); thVideoRecord.detach();
} }
start_md_bd(&args); start_md_bd(&gConfigMotion);
/// subscribe to mqtt /// subscribe to mqtt
gMqttClient = start_mqtt(nullptr); gMqttClient = start_mqtt(nullptr);
thread thSmart = thread(maq_smart_task_entry, &args); thread thSmart = thread(maq_smart_task_entry, &gConfigAI);
thSmart.join(); thSmart.join();
} }
\ No newline at end of file
...@@ -78,12 +78,12 @@ typedef struct ev_module_config_t { ...@@ -78,12 +78,12 @@ typedef struct ev_module_config_t {
union { union {
struct { struct {
int interval; // in seconds int interval; // in seconds
}record; } record;
struct { struct {
float face_thresh; //[0,1] float face_thresh; //[0,1]
float human_thresh; float human_thresh;
} ai; } ai;
struct motion{ struct motion {
int level; // 1 - 6 int level; // 1 - 6
} motion; } motion;
} module; } module;
...@@ -394,7 +394,7 @@ void record_video_entry(void *args) ...@@ -394,7 +394,7 @@ void record_video_entry(void *args)
delete fp; delete fp;
} }
fp = new fstream(recFilePath+to_string(nowTs), std::ios::out|std::ios::binary); fp = new fstream(recFilePath+to_string(nowTs), std::ios::out|std::ios::binary);
if(!fp->is_open()){ if(!fp->is_open()) {
spdlog::error("failed to open record file"); spdlog::error("failed to open record file");
delete fp; delete fp;
fp = nullptr; fp = nullptr;
......
...@@ -7,6 +7,8 @@ extern "C" { ...@@ -7,6 +7,8 @@ extern "C" {
#include <stdio.h> #include <stdio.h>
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>
#include <evpacket.h>
#include "motion.h"
typedef struct { typedef struct {
MaQueMdParam_s stMdParam; MaQueMdParam_s stMdParam;
...@@ -51,8 +53,13 @@ static XM_S32 cb_blind_detect(XM_VOID *pUserArg, MaQueBdResult_s *pstBdRes) ...@@ -51,8 +53,13 @@ static XM_S32 cb_blind_detect(XM_VOID *pUserArg, MaQueBdResult_s *pstBdRes)
return 0; return 0;
} }
void start_md_bd(void *args) void start_md_bd(ev_module_config_t *pArgs)
{ {
if(!pArgs->enabled) {
spdlog::warn("motion detect is disabled");
}
spdlog::info("starting motion detect module");
MdModParam_s mdParam = { 0 }; MdModParam_s mdParam = { 0 };
BdModParam_s bdParam = { 0 }; BdModParam_s bdParam = { 0 };
...@@ -62,9 +69,9 @@ void start_md_bd(void *args) ...@@ -62,9 +69,9 @@ void start_md_bd(void *args)
mdParam.stMdParam.y = 48; mdParam.stMdParam.y = 48;
mdParam.stMdParam.w = 352 - 58*2; mdParam.stMdParam.w = 352 - 58*2;
mdParam.stMdParam.h = 288 - 48*2; mdParam.stMdParam.h = 288 - 48*2;
mdParam.stMdParam.eMdAlarmlevel = 2; // [1, 6] mdParam.stMdParam.eMdAlarmlevel = pArgs->module.motion.level; // [1, 6]
bdParam.stBdParam.bEnabled = 1; bdParam.stBdParam.bEnabled = 1;
bdParam.stBdParam.eBdAlarmLevel = 2; bdParam.stBdParam.eBdAlarmLevel = pArgs->module.motion.level;
if (mdParam.stMdParam.bEnabled) { if (mdParam.stMdParam.bEnabled) {
LibXmMaQue_MD_create(0); LibXmMaQue_MD_create(0);
......
#ifndef __MOTION_H__ #ifndef __MOTION_H__
#define __MOTION_H__ #define __MOTION_H__
#pragma once
void start_md_bd(void *args); #include "internal_types.h"
void start_md_bd(ev_module_config_t *pArgs);
void stop_md_bd(); void stop_md_bd();
#endif #endif
\ No newline at end of file
#include <libxmmaque_api.h>
#include <spdlog/spdlog.h>
#include <fmt/format.h>
#include "ptz.h"
#include <mutex>
#include "evutils.hpp"
#include <string.h>
using namespace std;
using namespace evutils;
using namespace jsoncons;
mutex gMut;
map<string, int> mapDirection = {
{"left", MOTOR_MOVE_LEFT},
{"right", MOTOR_MOVE_RIGHT},
{"up", MOTOR_MOVE_UP},
{"down", MOTOR_MOVE_DOWN},
{"upright", MOTOR_MOVE_RIGHT|MOTOR_MOVE_UP},
{"upleft", MOTOR_MOVE_LEFT|MOTOR_MOVE_UP},
{"downleft", MOTOR_MOVE_LEFT|MOTOR_MOVE_DOWN},
{"downright", MOTOR_MOVE_RIGHT|MOTOR_MOVE_DOWN}
};
void ptz_waitfor_idle()
{
XM_U32 action,x,y;
usleep(100*1000);
while (1) {
if (0 == LibXmMaQue_Motor_getPostion(&action,&x,&y)) {
if (MOTOR_IDLE == action) {
break;
}
else {
usleep(300*1000);
}
}
}
}
string ptz_service_start()
{
static int first_startup = 1;
string rc;
spdlog::info("start ptz service: calibration/re-locating");
if (LibXmMaQue_Motor_create() < 0) {
rc = "ptz failed start service";
return rc;
}
rc = ptz_load();
if(!rc.empty()) {
return rc;
}
spdlog::info("ptz start service finished.");
return rc;
}
string ptz_service_stop()
{
LibXmMaQue_Motor_stop();
LibXmMaQue_Motor_destroy();
}
string ptz_get_params(ptz_param_position & param)
{
uint32_t x, y;
string rc;
if(LibXmMaQue_Motor_getMaxSteps(&x, &y)< 0) {
rc = "ptz failed to get max steps";
return rc;
}
uint32_t direction, cx, cy;
if(LibXmMaQue_Motor_getPostion(&direction, &cx, &cy) < 0) {
rc = "ptz failed to get current position";
return rc;
}
param.min_x = 0;
param.max_x = x;
param.step_x = EV_PTZ_X_DEGREE/x;
param.current_x_deg = cx * param.step_x;
param.current_x_steps = cx;
param.min_y = 0;
param.max_y = y;
param.step_y = EV_PTZ_Y_DEGREE/x;
param.current_y_deg = cy * param.step_y;
param.current_y_steps = cy;
return rc;
}
static const string extraSysConfigFile = "/etc/evsys.json";
string ptz_save()
{
ptz_param_position pos;
auto rc = ptz_get_params(pos);
if(!rc.empty()) {
return rc;
}
json js;
js["ptz"] = json();
js["ptz"]["x"] = pos.current_x_steps;
js["ptz"]["y"] = pos.current_y_steps;
rc = save_json(js, extraSysConfigFile, true, evutils::make_default_sys_extra_config);
return rc;
}
string ptz_load()
{
string rc;
json js;
rc = load_json(js, extraSysConfigFile, evutils::make_default_sys_extra_config);
if(!rc.empty()) {
return rc;
}
auto x = js["ptz"]["x"].as<uint32_t>();
auto y = js["ptz"]["y"].as<uint32_t>();
if(LibXmMaQue_Motor_setPostion(x,y)) {
rc = fmt::format("failed to set position to {}, {}", x, y);
}
ptz_waitfor_idle();
return rc;
}
string ptz_move(string dire, float degree)
{
lock_guard<mutex> lk(gMut);
string rc;
if(mapDirection.count(dire) == 0) {
rc = fmt::format("ptz move invalid direction: {}", dire);
return rc;
}
ptz_param_position curr_pos;
rc = ptz_get_params(curr_pos);
if(!rc.empty()) {
return rc;
}
if(dire == "left" || dire == "rignt") {
curr_pos.current_x_steps = (degree *1.0 / curr_pos.step_x);
}
if(dire == "up" || dire == "down") {
curr_pos.current_y_steps = (degree *1.0 / curr_pos.step_y);
}
if(LibXmMaQue_Motor_move(mapDirection["dire"], 52, 52, curr_pos.current_x_steps, curr_pos.current_y_steps)< 0) {
rc = fmt::format("ptz failed to move {} {}: current({},{}), max({},{})", dire, degree, curr_pos.current_x_deg, curr_pos.current_y_deg, 355, 0);
if(!rc.empty()) {
return rc;
}
}
ptz_waitfor_idle();
rc = ptz_save();
return rc;
}
string ptz_reset()
{
lock_guard<mutex> lk(gMut);
ptz_param_position param;
string rc;
rc = ptz_get_params(param);
if(!rc.empty()) {
return rc;
}
auto targetx = param.max_x/2;
auto targety = param.max_y/2;
if (LibXmMaQue_Motor_setZero()) {
rc = "ptz failed to relocate to (0,0)";
return rc;
}
if(LibXmMaQue_Motor_move(mapDirection["upright"], 52, 52, targetx, targety)< 0) {
rc = fmt::format("ptz failed move to center: {} {}", targetx, targety);
}
ptz_waitfor_idle();
return rc;
}
\ No newline at end of file
#ifndef __EV_PTZ_H__
#define __EV_PTZ_H__
#pragma once
#include <maque_motor.h>
#include <string.h>
/// PTZ
#define EV_PTZ_X_DEGREE 355
// N/A for xiongmai camera
#define EV_PTZ_Y_DEGREE -1
// N/A for xiongmai camera
#define EV_PTZ_Z_DEGREE -1
typedef struct ptz_param_position {
int min_x; // steps
int max_x;
int min_y;
int max_y; // steps
float step_x; // degree per step
float step_y;
int current_x_deg; // in number of steps, not degree
int current_x_steps;
int current_y_deg;
int current_y_steps; //
}ptz_param_position;
std::string ptz_service_start();
std::string ptz_service_stop();
std::string ptz_reset(ptz_param_position);
std::string ptz_move(std::string dire, float degree);
std::string ptz_get_params(ptz_param_position & param);
std::string ptz_load();
#endif
\ No newline at end of file
...@@ -17,7 +17,7 @@ int raw_connect(std::string host, std::string port, int *socket_, int recv_timeo ...@@ -17,7 +17,7 @@ int raw_connect(std::string host, std::string port, int *socket_, int recv_timeo
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0; hints.ai_protocol = 0;
spdlog::info("raw_connect"); spdlog::info("raw connect: {}:{}", host, port);
addrinfo *addrinfo_result; addrinfo *addrinfo_result;
rv = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &addrinfo_result); rv = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &addrinfo_result);
......
...@@ -9,37 +9,64 @@ extern "C" ...@@ -9,37 +9,64 @@ extern "C"
} }
#include "mqtt_helper.hpp" #include "mqtt_helper.hpp"
#include <jsoncons/json.hpp>
#include <vector>
#include <chrono>
using namespace std;
using namespace jsoncons;
#define DIV_UP(x, a) ( ((x) + ((a) - 1) ) / a ) #define DIV_UP(x, a) ( ((x) + ((a) - 1) ) / a )
#define SMART_CAP_MIN_SECS 10
long long smartTsLast = 0;
static int smartCnt = 0;
XM_S32 MaQue_JpegEnc_getFrame_callback (XM_VOID *pUserArg, MaQueSmartJpegFrame_s *pstJpegFrame) XM_S32 MaQue_JpegEnc_getFrame_callback (XM_VOID *pUserArg, MaQueSmartJpegFrame_s *pstJpegFrame)
{ {
// if(smartCnt > 0) { ev_module_config_t * mod = (ev_module_config_t *)pUserArg;
// smartCnt--; auto now = chrono::duration_cast<chrono::seconds>(chrono::system_clock::now().time_since_epoch()).count();
// return 0; // no two capture within SMART_CAP_MIN_SECS seconds
// } if(now - smartTsLast < SMART_CAP_MIN_SECS) {
// smartCnt = 1000; return 0;
// XM_CHAR acFile[256] = {0}; }
// FILE *pFile;
// static XM_S32 jpeg_cnt = 0; smartTsLast = now;
json js;
printf("aClassName[%s] idx = %d, toltal = %d\n", pstJpegFrame->aClassName, pstJpegFrame->nIndex, pstJpegFrame->nToltalJpeg); string str = fmt::format("new ai capture: {}, idx = {}, toltal = {}", pstJpegFrame->aClassName, pstJpegFrame->nIndex, pstJpegFrame->nToltalJpeg);
spdlog::info(str);
// if (jpeg_cnt < 30000) { MaQueSmartTarget_s stMaQueSmartTarget = {0};
// sprintf(acFile, "ai/snap_%d.jpg", jpeg_cnt);
// pFile = fopen(acFile, "wb"); // face
// if (pFile == NULL) { int res = LibXmMaQue_SmartGetTarget(0, &stMaQueSmartTarget);
// spdlog::error("open file err"); if(stMaQueSmartTarget.targetFDNum > 0 ) {
// return XM_FAILURE; js["face"] = json();
// } js["face"]["num"] = stMaQueSmartTarget.targetFDNum;
js["face"]["minX"] = stMaQueSmartTarget.aFDRect[0].s16X1;
// fwrite(pstJpegFrame->pBuffer, pstJpegFrame->nDataLen, 1, pFile); js["face"]["minY"] = stMaQueSmartTarget.aFDRect[0].s16Y1;
// fflush(pFile); js["face"]["maxX"] = stMaQueSmartTarget.aFDRect[0].s16X2;
js["face"]["maxY"] = stMaQueSmartTarget.aFDRect[0].s16Y2;
// fclose(pFile); }
// jpeg_cnt++;
// } // pepole
if(stMaQueSmartTarget.targetPDNum > 0 ) {
js["pepole"] = json();
js["pepole"]["num"] = stMaQueSmartTarget.targetPDNum;
js["pepole"]["minX"] = stMaQueSmartTarget.aPDRect[0].s16X1;
js["pepole"]["minY"] = stMaQueSmartTarget.aPDRect[0].s16Y1;
js["pepole"]["maxX"] = stMaQueSmartTarget.aPDRect[0].s16X2;
js["pepole"]["maxY"] = stMaQueSmartTarget.aPDRect[0].s16Y2;
}
if(mod != nullptr && mod->pClient != nullptr) {
string b64;
vector<uint8_t> vec(pstJpegFrame->pBuffer, pstJpegFrame->pBuffer + pstJpegFrame->nDataLen -1);
jsoncons::encode_base64<vector<uint8_t>::iterator, string>(vec.begin(), vec.end(), b64);
js["data"] = b64;
js[consts::kMsgType] = consts::kMsgTypeImage;
js[consts::kMsgEncode] = consts::kMsgEncodeBase64;
js[consts::kMsgFormat] = consts::kMsgFormatJpeg;
MqttMgr::report_response_args(client, consts::pub_topic_response + client->id, 0, str, "ai_image", "", js);
}
return XM_SUCCESS; return XM_SUCCESS;
} }
...@@ -98,8 +125,13 @@ XM_S32 MaQue_Draw_OsdAreaRect(MaQueStreamChannel_e eStreamChn, XM_S32 enable, XM ...@@ -98,8 +125,13 @@ XM_S32 MaQue_Draw_OsdAreaRect(MaQueStreamChannel_e eStreamChn, XM_S32 enable, XM
return XM_SUCCESS; return XM_SUCCESS;
} }
void maq_smart_task_entry(void *pArg) void maq_smart_task_entry(ev_module_config_t *pArg)
{ {
if(!pArg->enabled) {
spdlog::warn("ai detection is disabled");
return;
}
spdlog::info("starting ai detection task");
XM_S32 res; XM_S32 res;
MaQueStreamChannel_e eStreamChn; MaQueStreamChannel_e eStreamChn;
MaQueSmartParams_s stMaQueSmartParams; MaQueSmartParams_s stMaQueSmartParams;
...@@ -228,13 +260,13 @@ void maq_smart_task_entry(void *pArg) ...@@ -228,13 +260,13 @@ void maq_smart_task_entry(void *pArg)
return; return;
} }
res = LibXmMaQue_SmartPDThreshold(0, pdThreshold); res = LibXmMaQue_SmartPDThreshold(0, pArg->module.ai.human_thresh);
if (XM_SUCCESS != res) { if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartPDThreshold() Failed!"); spdlog::error("LibXmMaQue_SmartPDThreshold() Failed!");
return; return;
} }
res = LibXmMaQue_SmartFDThreshold(0, fdThreshold); res = LibXmMaQue_SmartFDThreshold(0, pArg->module.ai.face_thresh);
if (XM_SUCCESS != res) { if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartFDThreshold() Failed!"); spdlog::error("LibXmMaQue_SmartFDThreshold() Failed!");
return; return;
...@@ -242,11 +274,14 @@ void maq_smart_task_entry(void *pArg) ...@@ -242,11 +274,14 @@ void maq_smart_task_entry(void *pArg)
stCallback.eType = MAQUE_SMART_JPEG_ENCODE_LARGE;//MAQUE_SMART_JPEG_ENCODE_CUTOUT; stCallback.eType = MAQUE_SMART_JPEG_ENCODE_LARGE;//MAQUE_SMART_JPEG_ENCODE_CUTOUT;
stCallback.eClass = MAQUE_SMART_JPEG_CLASS_NONE;//MAQUE_SMART_JPEG_CLASS_FD; stCallback.eClass = MAQUE_SMART_JPEG_CLASS_NONE;//MAQUE_SMART_JPEG_CLASS_FD;
stCallback.stCallback.pCallbackArg = NULL; stCallback.stCallback.pCallbackArg = pArg;
stCallback.stCallback.pCallbackFuncPtr = MaQue_JpegEnc_getFrame_callback; stCallback.stCallback.pCallbackFuncPtr = MaQue_JpegEnc_getFrame_callback;
LibXmMaQue_SmartRegisterCallback(0, &stCallback); LibXmMaQue_SmartRegisterCallback(0, &stCallback);
while (1) { while (1) {
this_thread::sleep_for(3s);
continue;
res = LibXmMaQue_SmartGetTarget(0, &stMaQueSmartTarget); res = LibXmMaQue_SmartGetTarget(0, &stMaQueSmartTarget);
if (XM_SUCCESS != res) { if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartGetTarget() Failed!"); spdlog::error("LibXmMaQue_SmartGetTarget() Failed!");
...@@ -272,7 +307,6 @@ void maq_smart_task_entry(void *pArg) ...@@ -272,7 +307,6 @@ void maq_smart_task_entry(void *pArg)
if (stMaQueSmartTarget.targetFDNum > 0) { if (stMaQueSmartTarget.targetFDNum > 0) {
printf("\n\nFace Detection : TotalNum %d\n", stMaQueSmartTarget.targetFDNum); printf("\n\nFace Detection : TotalNum %d\n", stMaQueSmartTarget.targetFDNum);
for (i = 0; i < stMaQueSmartTarget.targetFDNum; i++) { for (i = 0; i < stMaQueSmartTarget.targetFDNum; i++) {
printf("(%d, %d), (%d, %d)\n", stMaQueSmartTarget.aFDRect[i].s16X1, stMaQueSmartTarget.aFDRect[i].s16Y1, printf("(%d, %d), (%d, %d)\n", stMaQueSmartTarget.aFDRect[i].s16X1, stMaQueSmartTarget.aFDRect[i].s16Y1,
stMaQueSmartTarget.aFDRect[i].s16X2, stMaQueSmartTarget.aFDRect[i].s16Y2); stMaQueSmartTarget.aFDRect[i].s16X2, stMaQueSmartTarget.aFDRect[i].s16Y2);
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
#define __EV_SMART_HPP__ #define __EV_SMART_HPP__
#pragma once #pragma once
#include "internal_types.h"
void maq_smart_task_entry(void *pArg); void maq_smart_task_entry(ev_module_config_t *pArg);
#endif #endif
\ No newline at end of file
...@@ -61,4 +61,6 @@ static inline unsigned short crc16(const unsigned char* data_p, unsigned char le ...@@ -61,4 +61,6 @@ static inline unsigned short crc16(const unsigned char* data_p, unsigned char le
return crc; return crc;
} }
#endif #endif
\ No newline at end of file
...@@ -28,6 +28,8 @@ using namespace std; ...@@ -28,6 +28,8 @@ using namespace std;
using namespace jsoncons; using namespace jsoncons;
using namespace evutils; using namespace evutils;
#define EV_MAX_PRINTABLE_SIZE 512
extern void on_connected(void* context, MQTTAsync_successData* response); extern void on_connected(void* context, MQTTAsync_successData* response);
extern void on_connlost(void *context, char *cause); extern void on_connlost(void *context, char *cause);
extern int on_msgarrvd(void *context, char *topic, int topicLen, MQTTAsync_message *message); extern int on_msgarrvd(void *context, char *topic, int topicLen, MQTTAsync_message *message);
...@@ -142,7 +144,7 @@ public: ...@@ -142,7 +144,7 @@ public:
spdlog::debug("mqtt url: proto: {}, host: {}, port: {}, user: {}, pass: {}", uri.Protocol, uri.Host, uri.Port, uri.User, uri.Password); spdlog::debug("mqtt url: proto: {}, host: {}, port: {}, user: {}, pass: {}", uri.Protocol, uri.Host, uri.Port, uri.User, uri.Password);
if(MQTTASYNC_SUCCESS != MQTTAsync_create(&client, addr.c_str(), (string("EVC")+id).c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL)) { if(MQTTASYNC_SUCCESS != MQTTAsync_create(&client, addr.c_str(), ("EVC"+id).c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL)) {
msg = "failed to async create mqtt"; msg = "failed to async create mqtt";
spdlog::error(msg); spdlog::error(msg);
throw StrException(msg); throw StrException(msg);
...@@ -303,7 +305,7 @@ class MqttMgr { ...@@ -303,7 +305,7 @@ class MqttMgr {
lock_guard<mutex> lg(MqttMgr::mut); lock_guard<mutex> lg(MqttMgr::mut);
try{ try{
if(MqttMgr::insts.count(key) == 0){ if(MqttMgr::insts.count(key) == 0){
auto inst = new MqttHelper(mqtt_url, "evc." + devsn); auto inst = new MqttHelper(mqtt_url, devsn);
MqttMgr::insts[key] = inst; MqttMgr::insts[key] = inst;
}else{ }else{
// //
...@@ -345,6 +347,10 @@ class MqttMgr { ...@@ -345,6 +347,10 @@ class MqttMgr {
return; return;
} }
auto rc = (*client)[topic].pub(message.c_str(), message.size(), 2, false); auto rc = (*client)[topic].pub(message.c_str(), message.size(), 2, false);
if(message.size() > EV_MAX_PRINTABLE_SIZE){
string ellipsis = " <...> ";
message = message.substr(0, EV_MAX_PRINTABLE_SIZE/2 - ellipsis.size()) + ellipsis + message.substr(EV_MAX_PRINTABLE_SIZE/2, EV_MAX_PRINTABLE_SIZE/2);
}
if(rc < 0) { if(rc < 0) {
spdlog::error("failed to pub mqtt message on {}: {}", topic, message); spdlog::error("failed to pub mqtt message on {}: {}", topic, message);
} }
...@@ -398,6 +404,9 @@ class MqttMgr { ...@@ -398,6 +404,9 @@ class MqttMgr {
js.insert_or_assign(consts::kMsgCode, code); js.insert_or_assign(consts::kMsgCode, code);
js.insert_or_assign(consts::kMsgMsg, message); js.insert_or_assign(consts::kMsgMsg, message);
js.insert_or_assign(consts::kMsgSn, client->id); js.insert_or_assign(consts::kMsgSn, client->id);
if(!data.empty()){
js.insert_or_assign(consts::kMsgData, data);
}
_report_response(client, topic, js.to_string()); _report_response(client, topic, js.to_string());
} }
...@@ -422,6 +431,9 @@ class MqttMgr { ...@@ -422,6 +431,9 @@ class MqttMgr {
js.insert_or_assign(consts::kMsgCode, code); js.insert_or_assign(consts::kMsgCode, code);
js.insert_or_assign(consts::kMsgMsg, message); js.insert_or_assign(consts::kMsgMsg, message);
js.insert_or_assign(consts::kMsgSn, client->id); js.insert_or_assign(consts::kMsgSn, client->id);
if(!data.empty()){
js.insert_or_assign(consts::kMsgData, data);
}
_report_response(client, topic, js.to_string()); _report_response(client, topic, js.to_string());
} }
......
...@@ -138,12 +138,13 @@ int write_packet(packet_processor_t *processor, char *data, int len) ...@@ -138,12 +138,13 @@ int write_packet(packet_processor_t *processor, char *data, int len)
pkt.size = len; pkt.size = len;
AVRational time_base; AVRational time_base;
char *fps = getenv("FPS"); char *fps = getenv("FPS");
if(processor->hdr.vpara.fps > 0 && processor->hdr.vpara.fps < 100){ if(processor->hdr.vpara.fps > 0 && processor->hdr.vpara.fps < 100) {
time_base.den = processor->hdr.vpara.fps; time_base.den = processor->hdr.vpara.fps;
}else{ }
else {
time_base.den = 15; time_base.den = 15;
} }
if(fps){ if(fps) {
time_base.den = atoi(fps); time_base.den = atoi(fps);
} }
time_base.num = 1; time_base.num = 1;
...@@ -162,7 +163,8 @@ int write_packet(packet_processor_t *processor, char *data, int len) ...@@ -162,7 +163,8 @@ int write_packet(packet_processor_t *processor, char *data, int len)
} }
int setupRouter(void **ctx, void **s, string addr, int rcvQS=0){ int setupRouter(void **ctx, void **s, string addr, int rcvQS=0)
{
int ret = 0; int ret = 0;
*ctx = zmq_ctx_new(); *ctx = zmq_ctx_new();
*s = zmq_socket(*ctx, ZMQ_ROUTER); *s = zmq_socket(*ctx, ZMQ_ROUTER);
...@@ -183,7 +185,8 @@ int setupRouter(void **ctx, void **s, string addr, int rcvQS=0){ ...@@ -183,7 +185,8 @@ int setupRouter(void **ctx, void **s, string addr, int rcvQS=0){
return ret; return ret;
} }
int z_recv_multiple(void *s, vector<uint8_t> &buf, vector<int>&frameIdx) { int z_recv_multiple(void *s, vector<uint8_t> &buf, vector<int>&frameIdx)
{
int64_t more = 1; int64_t more = 1;
size_t more_size = sizeof(more); size_t more_size = sizeof(more);
int ret = 0; int ret = 0;
...@@ -222,7 +225,8 @@ int z_recv_multiple(void *s, vector<uint8_t> &buf, vector<int>&frameIdx) { ...@@ -222,7 +225,8 @@ int z_recv_multiple(void *s, vector<uint8_t> &buf, vector<int>&frameIdx) {
packet_processor_t globalProcess = {0}; packet_processor_t globalProcess = {0};
void dispatch(string sn, zmq_msg_t &msg){ void dispatch(string sn, zmq_msg_t &msg)
{
evpacket_t *pkt = (evpacket_t *)zmq_msg_data(&msg); evpacket_t *pkt = (evpacket_t *)zmq_msg_data(&msg);
globalProcess.packetId++; globalProcess.packetId++;
...@@ -230,12 +234,12 @@ void dispatch(string sn, zmq_msg_t &msg){ ...@@ -230,12 +234,12 @@ void dispatch(string sn, zmq_msg_t &msg){
auto crc = pkt->meta.crc; auto crc = pkt->meta.crc;
pkt->meta.crc = 0; pkt->meta.crc = 0;
auto crc_ = crc16((unsigned char*)pkt, sizeof(evpacket_t)); auto crc_ = crc16((unsigned char*)pkt, sizeof(evpacket_t));
if(crc != crc_){ if(crc != crc_) {
spdlog::error("invalid crc: {}", pkt->meta.packet_id); spdlog::error("invalid crc: {}", pkt->meta.packet_id);
return; return;
} }
if(globalProcess.pAvCtx == nullptr && globalProcess.packetId %15*10 == 0){ if(globalProcess.pAvCtx == nullptr && globalProcess.packetId %15*10 == 0) {
int rc = 0; int rc = 0;
globalProcess.pAvCtx = rtsp_init(darwinUrl + sn, 0, 1080, 1920, &rc); globalProcess.pAvCtx = rtsp_init(darwinUrl + sn, 0, 1080, 1920, &rc);
if(globalProcess.pAvCtx == nullptr || rc < 0) { if(globalProcess.pAvCtx == nullptr || rc < 0) {
...@@ -278,7 +282,7 @@ int main() ...@@ -278,7 +282,7 @@ int main()
void *pRouterCtx = nullptr, *pRouter = nullptr/*, *pDealerCtx = nullptr, *pDealer = nullptr*/; void *pRouterCtx = nullptr, *pRouter = nullptr/*, *pDealerCtx = nullptr, *pDealer = nullptr*/;
setupRouter(&pRouterCtx, &pRouter, addr, 10); setupRouter(&pRouterCtx, &pRouter, addr, 10);
while(1){ while(1) {
int64_t more = 1; int64_t more = 1;
size_t more_size = sizeof(more); size_t more_size = sizeof(more);
int ret = 0; int ret = 0;
...@@ -309,7 +313,8 @@ int main() ...@@ -309,7 +313,8 @@ int main()
break; break;
} }
dispatch(sn, msg); dispatch(sn, msg);
}else{ }
else {
spdlog::error("invalid packet fmt"); spdlog::error("invalid packet fmt");
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论