提交 b59d87d9 authored 作者: blu's avatar blu

evcamera: video quality adjustment and fix video glich

上级 a2ff0344
......@@ -195,10 +195,10 @@ REQUEST_VIDEOS_T
{
"time": 1567669674,
"cmd": "upload_video",
"rid": "<random_str>",
"rid": "001231554A20",
"data": {
"start": 1567669674,
"end": 1567668000,
"start": 1592985060,
"end": 1592985180,
"type": 6
}
}
......
......@@ -53,11 +53,10 @@ sequenceDiagram
EVCamera ->> Broker: power_on: pub online
EVCamera ->> Broker: sub: /evcamera/v1.0/request/<SN>
CloudSvc ->> Broker: pub: cmd = config|status|upload_video|ptz|get_config|qr
Broker ->> EVCamera: notify: request {"rid":"<RID>", "cmd": "<x>", ...}
CloudSvc ->> Broker: pub: config|status|upload_video|<br/>ptz|get_config|qr
Broker ->> EVCamera: notify: request {"rid":"<RID>", "cmd": "<x>"...}
EVCamera ->> EVCamera: [Handle cmd]
EVCamera ->> Broker: pub /evcamera/v1.0/response/<RID>
EVCamera ->> Broker: pub /evcamera/v1.0/report
EVCamera ->> Broker: pub: /evcamera/v1.0/response/<RID> or<br/>/evcamera/v1.0/report
```
#### MQTT Broker topics
......
#include "base_features.h"
#include <stdlib.h>
int ev_stop_record(ev_record_cfg_t **handle)
{
assert(handle != nullptr);
if(*handle != nullptr) {
free(*handle);
handle = nullptr;
}
}
int ev_start_record(const json &config, ev_record_cfg_t **handle)
{
assert(handle != nullptr);
*handle = (ev_record_cfg_t*)malloc(sizeof(ev_record_cfg_t));
//
}
\ No newline at end of file
#ifndef __BASE_FEATURES_H__
#define __BASE_FEATURES_H__
#include <jsoncons/json.hpp>
using namespace jsoncons;
typedef struct ev_record_status_t {
/// TODO:
} ev_record_status_t;
typedef struct ev_mdstatus_t {
/// TODO:
} ev_mdstatus_t;
typedef struct ev_ai_status_t {
/// TODO:
} ev_ai_status_t;
typedef struct ev_record_cfg {
} ev_record_cfg;
typedef struct ev_record_cfg_t {
} ev_record_cfg_t;
typedef struct ev_md_cfg_t {
} ev_md_cfg_t;
typedef struct ev_ai_cfg_t {
} ev_ai_cfg_t;
/// record
int ev_start_record(const json &config, ev_record_cfg_t **handle);
int ev_stop_record(ev_record_cfg_t **handle);
ev_record_status_t ev_get_record_status(ev_record_cfg_t **handle);
/// motion detect
int ev_start_md(const json &config, ev_md_cfg_t **handle);
int ev_stop_md(ev_md_cfg_t *handle);
ev_mdstatus_t ev_get_md_status(ev_md_cfg_t **handle);
/// ai detect
int ev_start_ai(const json &config, ev_ai_cfg_t **handle);
int ev_stop_ai(ev_ai_cfg_t **handle);
ev_ai_status_t ev_get_ai_status(ev_ai_cfg_t **handle);
#endif
\ No newline at end of file
#ifndef _RTSP_COMMON_H
#define _RTSP_COMMON_H
#include <stdlib.h>
#include <stdio.h>
/******************************************************************************
* DEFINITIONS
******************************************************************************/
/* this is necessary to get rid of 'useless keyword or type ..' shit */
#define __DEBUG
#define TYPES
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
#ifdef __DEBUG
/* -------------------------------------------------------------------------- */
#define DBG( fmt, args...) do {\
fprintf(stderr, "--- DEBUG:%s:%s:%d ---: ", __FILE__, __FUNCTION__, __LINE__);\
fprintf(stderr, fmt, ## args);} while(0)
#define ERR(fmt, args...) do {\
fprintf(stderr, "XXX ERROR:%s:%s:%d XXX: ",__FILE__,__FUNCTION__,__LINE__);\
fprintf(stderr, fmt, ## args);} while (0)
#define CHECK do {\
DBG("(*^-^)\n");} while (0)
/* debug assert (completely removed in release version) */
#define DASSERT(b,action) do {if(unlikely(!(b))){ ERR("debug assertion failure (%s)\n", #b);action;} } while (0)
/* must success (check omitted in release version) */
#define MUST(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* assertion */
#define ASSERT(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* sanity check wrapper (same in release and debug version) */
#define TEST(b,action) do {if(unlikely(!(b))){ ERR("unexpected (%s)\n", #b);action;} } while (0)
/* -------------------------------------------------------------------------- */
#else
/* -------------------------------------------------------------------------- */
#define DBG( fmt, args...)
#define ERR(fmt, args...) do {\
fprintf(stderr, "XXX ERROR:%s:%s:%d XXX: ",__FILE__,__FUNCTION__,__LINE__);\
fprintf(stderr, fmt, ## args);} while (0)
#define CHECK
/* debug assert (completely removed in release version) */
//#define DASSERT(b,action)
/* must success (check omitted in release version) */
//#define MUST(b,action) do {if ((b)){}} while (0)
/* debug assert (completely removed in release version) */
#define DASSERT(b,action) do {if(unlikely(!(b))){ ERR("debug assertion failure (%s)\n", #b);action;} } while (0)
/* must success (check omitted in release version) */
#define MUST(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* assertion */
#define ASSERT(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* sanity check wrapper (same in release and debug version) */
#define TEST(b,action) do {if(unlikely(!(b))){ ERR("unexpected (%s)\n", #b);action;} } while (0)
/* -------------------------------------------------------------------------- */
#endif
#define SUCCESS 0
#define FAILURE -1
#define ERR_RTSP_SHOULD_FIN -10
/* Defining infinite time */
#define FOREVER -1
#define CLEAR(x) memset (&(x), 0, sizeof (x))
#define CLOSE(x) do{if((x)>0){close(x);(x) = 0;}}while(0)
#define FCLOSE(x) do{if((x)!=NULL){fclose(x);(x) = NULL;}}while(0)
#define ALLOC(t) (t = (typeof(t))calloc(1,sizeof(typeof(*(t)))))
#define NEW(t) ((t *)calloc(1,sizeof(t)))
#define COPY(x,y) memcpy ((x),(y),sizeof (*(y)))
//#define FREE(h) do { if(h){printf("freeing 0x%08x\n",(unsigned int)h);free(h);h=NULL;}else{DBG("freeing NULL pointer\n");}} while (0);
#define FREE(h) do { if(h){free(h);h=NULL;}} while (0);
/* common object creation macro: calloc given pointer. when failed, perform 'action' */
#define TALLOC(h,action) do {h=NULL;if(!(ALLOC(h))){ERR("No memory!\n");action;}} while (0)
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef min
#define min(a,b) ((a)>(b)?(b):(a))
#endif
#define TRUE 1
#define FALSE 0
/* Type magic */
enum type_magic_e {
MAGIC_NAL_QUEUE_ITEM = 0xdead0000,
MAGIC_PACKET_QUEUE_ITEM,
MAGIC_BUFPOOL_ELEM
};
#define CHECK_MAGIC(magic,ptr) (*(ptr) && (((unsigned int*)(*(ptr)))[0] == (magic)))
#ifndef container_of
#define container_of(type,ptr,member) ({ const typeof( ((type *)0)->member) *__mptr=(ptr); (type *) ( (char *)__mptr - offsetof(type,member));})
#endif
#endif
#ifndef _RTSP_COMMON_H
#define _RTSP_COMMON_H
#include <stdio.h>
#include <pthread.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
/******************************************************************************
* DEFINITIONS
******************************************************************************/
/* this is necessary to get rid of 'useless keyword or type ..' shit */
#define typeof(x) __typeof__(x)
#define __DEBUG
#define TYPES
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
#ifdef __DEBUG
/* -------------------------------------------------------------------------- */
#define DBG( fmt, args...) do {\
fprintf(stderr, "--- DEBUG:%s:%s:%d ---: ", __FILE__, __FUNCTION__, __LINE__);\
fprintf(stderr, fmt, ## args);} while(0)
#define ERR(fmt, args...) do {\
fprintf(stderr, "XXX ERROR:%s:%s:%d XXX: ",__FILE__,__FUNCTION__,__LINE__);\
fprintf(stderr, fmt, ## args);} while (0)
#define CHECK do {\
DBG("(*^-^)\n");} while (0)
/* debug assert (completely removed in release version) */
#define DASSERT(b,action) do {if(unlikely(!(b))){ ERR("debug assertion failure (%s)\n", #b);action;} } while (0)
/* must success (check omitted in release version) */
#define MUST(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* assertion */
#define ASSERT(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* sanity check wrapper (same in release and debug version) */
#define TEST(b,action) do {if(unlikely(!(b))){ ERR("unexpected (%s)\n", #b);action;} } while (0)
/* -------------------------------------------------------------------------- */
#else
/* -------------------------------------------------------------------------- */
#define DBG( fmt, args...)
#define ERR(fmt, args...) do {\
fprintf(stderr, "XXX ERROR:%s:%s:%d XXX: ",__FILE__,__FUNCTION__,__LINE__);\
fprintf(stderr, fmt, ## args);} while (0)
#define CHECK
/* debug assert (completely removed in release version) */
//#define DASSERT(b,action)
/* must success (check omitted in release version) */
//#define MUST(b,action) do {if ((b)){}} while (0)
/* debug assert (completely removed in release version) */
#define DASSERT(b,action) do {if(unlikely(!(b))){ ERR("debug assertion failure (%s)\n", #b);action;} } while (0)
/* must success (check omitted in release version) */
#define MUST(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* assertion */
#define ASSERT(b,action) do {if(unlikely(!(b))){ ERR("assertion failure (%s)\n", #b);action;} } while (0)
/* sanity check wrapper (same in release and debug version) */
#define TEST(b,action) do {if(unlikely(!(b))){ ERR("unexpected (%s)\n", #b);action;} } while (0)
/* -------------------------------------------------------------------------- */
#endif
#define SUCCESS 0
#define FAILURE -1
#define ERR_RTSP_SHOULD_FIN -10
/* Defining infinite time */
#define FOREVER -1
#define CLEAR(x) memset (&(x), 0, sizeof (x))
#define CLOSE(x) do{if((x)>0){close(x);(x) = 0;}}while(0)
#define FCLOSE(x) do{if((x)!=NULL){fclose(x);(x) = NULL;}}while(0)
#define ALLOC(t) (t = (typeof(t))calloc(1,sizeof(typeof(*(t)))))
#define NEW(t) ((t *)calloc(1,sizeof(t)))
#define COPY(x,y) memcpy ((x),(y),sizeof (*(y)))
//#define FREE(h) do { if(h){printf("freeing 0x%08x\n",(unsigned int)h);free(h);h=NULL;}else{DBG("freeing NULL pointer\n");}} while (0);
#define FREE(h) do { if(h){free(h);h=NULL;}} while (0);
/* common object creation macro: calloc given pointer. when failed, perform 'action' */
#define TALLOC(h,action) do {h=NULL;if(!(ALLOC(h))){ERR("No memory!\n");action;}} while (0)
#ifndef max
#define max(a,b) ((a)>(b)?(a):(b))
#endif
#ifndef min
#define min(a,b) ((a)>(b)?(b):(a))
#endif
#define TRUE 1
#define FALSE 0
/* Type magic */
enum type_magic_e {
MAGIC_NAL_QUEUE_ITEM = 0xdead0000,
MAGIC_PACKET_QUEUE_ITEM,
MAGIC_BUFPOOL_ELEM
};
#define CHECK_MAGIC(magic,ptr) (*(ptr) && (((unsigned int*)(*(ptr)))[0] == (magic)))
#ifndef container_of
#define container_of(type,ptr,member) ({ const typeof( ((type *)0)->member) *__mptr=(ptr); (type *) ( (char *)__mptr - offsetof(type,member));})
#endif
/******************************************************************************
* DATA STRUCTURES
******************************************************************************/
struct global_state_t {
unsigned long long frames; /* Video frame counter */
pthread_mutex_t mutex; /* Mutex to protect the global data */
int quit;
};
/******************************************************************************
* INLINE FUNCTIONS
******************************************************************************/
static inline struct global_state_t *gbl_create()
{
struct global_state_t *nh;
TALLOC(nh,return NULL);
pthread_mutex_init(&nh->mutex,NULL);
return nh;
}
static inline void gbl_delete(struct global_state_t *h)
{
if(h) {
pthread_mutex_destroy(&h->mutex);
FREE(h);
}
}
static inline int
gbl_get_quit(struct global_state_t *gbl)
{
int quit;
pthread_mutex_lock(&gbl->mutex);
quit = gbl->quit;
pthread_mutex_unlock(&gbl->mutex);
return quit;
}
static inline void
gbl_set_quit(struct global_state_t *gbl)
{
pthread_mutex_lock(&gbl->mutex);
gbl->quit = TRUE;
pthread_mutex_unlock(&gbl->mutex);
}
#endif
......@@ -81,7 +81,7 @@ const string consts::pub_topic_eventdata = "/evcamera/v1.0/eventdata/";
const string consts::pub_topic_lastwill = "/evcamera/v1.0/lastwill/";
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::vgw_addr = "tcp://evcloudsvc.ilabservice.cloud:7123";
const string consts::vgw_addr = "tcp://vgw.ilabservice.cloud:7123";
const string consts::upload_addr = "tcp://evcloudsvc.ilabservice.cloud:10009";
const string consts::strPubUrl = "inproc://frame";
......@@ -111,11 +111,12 @@ json make_default_cloud_config()
{
return R"(
{
"vgw":"evcloudsvc.ilabservice.cloud:7123",
"vgw":"vgw.ilabservice.cloud:7123",
"mqtt":"admin:vJ3zHqWrHbrqxVMT@evcloudsvc.ilabservice.cloud:11883",
"upload":"evcloudsvc.ilabservice.cloud:10010",
"upload":"vup.ilabservice.cloud:10010",
"features":{
"push":0,
"fps":20,
"motion":{
"enabled":1,
"region":{
......@@ -128,7 +129,7 @@ json make_default_cloud_config()
},
"recordLen":120,
"ai":{
"enabled":1,
"enabled":0,
"faceThresh":0.75,
"humanThresh":0.63,
"region":{
......@@ -237,7 +238,7 @@ bool get_sdcard_megabytes(uint64_t &total, uint64_t &free, char* path )
}
/// 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 timeoutMs, int sndQS)
{
int ret = 0;
*ctx = zmq_ctx_new();
......@@ -250,20 +251,15 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int
ret = 2;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_CNT, &ret, sizeof (ret));
if(sndQS!=0) {
zmq_setsockopt(*s, ZMQ_SNDHWM, &sndQS, sizeof (sndQS));
}
if(timeoutMs != 0) {
if(timeoutMs == -1) {
timeoutMs = 5 *1000;
ret = zmq_setsockopt(*s, ZMQ_SNDHWM, &sndQS, sizeof (sndQS));
if(ret < 0) {
spdlog::error("failed to setup ZMQ_SNDHWM for Dealer {}@{}: {}", ident, addr, zmq_strerror(zmq_errno()));
}
zmq_setsockopt(*s, ZMQ_RCVTIMEO, &timeoutMs, sizeof(timeoutMs));
}
// uint64_t hwm = 12;
// zmq_setsockopt(*s, ZMQ_SNDHWM, &hwm, sizeof(hwm));
// int onlyOne = 1;
// zmq_setsockopt(*s, ZMQ_CONFLATE, &onlyOne, sizeof(onlyOne));
if(timeoutMs > 0) {
zmq_setsockopt(*s, ZMQ_RCVTIMEO, &timeoutMs, sizeof (timeoutMs));
zmq_setsockopt(*s, ZMQ_SNDTIMEO, &timeoutMs, sizeof (timeoutMs));
}
ret = zmq_setsockopt(*s, ZMQ_IDENTITY, ident.c_str(), ident.size());
if(ret < 0) {
......@@ -271,7 +267,7 @@ int setupDealer(void **ctx, void **s, string ident, string addr, int sndQS, int
zmq_ctx_destroy(*ctx);
*s = nullptr;
*ctx = nullptr;
spdlog::debug("{} failed setsockopts ZMQ_ROUTING_ID to {}: {}", ident, addr, zmq_strerror(zmq_errno()));
spdlog::debug("{} failed setsockopts ZMQ_IDENTITY to {}: {}", ident, addr, zmq_strerror(zmq_errno()));
}
else {
ret = zmq_connect(*s, addr.c_str());
......
......@@ -104,7 +104,7 @@ extern json make_online_msg(string sn);
extern json make_lastwill_msg(string sn);
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 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 timeoutMs = 0, int sndQS = 0);
extern int setupRouter(void **ctx, void **s, string addr, int rcvQS=0);
extern int z_recv_multiple(void *s, vector<uint8_t> &buf, int &frames);
extern vector<string> split(const string &s, char delim);
......@@ -129,7 +129,7 @@ public:
void insert(TN elem, cb_remove_elem<TN> fn=nullptr)
{
// list_.insert(lower_bound(list_.begin(), list_.end(), elem), elem);
if(cntInsert % 10 == 0) {
if(cntInsert % 100 == 0) {
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);
uint64_t mt, mf;
......
/**
* @file main.cc
*
* @brief this is the main file impelents evcamera
* @brief this is the main file implements evcamera
*
* @ingroup evcamera
*
......@@ -16,6 +16,7 @@
#include <sys/stat.h>
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include "spdlog/sinks/rotating_file_sink.h"
#include "ref-memory.hpp"
#include <maque_system.h>
#include <maque_video_enc.h>
......@@ -120,6 +121,10 @@ thread_upload_args_t gUploadArgs{&_upvariable_::mut, &_upvariable_::cv, &_upvari
//
string strMaQuePath = "/mnt/sd/Config/";
// rotating logger
// create a thread safe sink which will keep its file size to a maximum of 5MB and a maximum of 3 rotated files.
shared_ptr<spdlog::logger> rlogger = spdlog::rotating_logger_mt("evc", "/mnt/sd/logs.txt", 1048576 * 1, 3);
/// why using get function because global vriable initialization order between files is undefined!
/// this avoids getting empty result
vector<string> get_sdcard_resered_dirs()
......@@ -143,7 +148,7 @@ void clean_up(int sig)
LibXmMaQue_Watchdog_enable(0);
}
spdlog::warn("clearn up called, with sig: {}", sig);
rlogger->warn("clearn up called, with sig: {}", sig);
if (args.recFD) {
fclose(args.recFD);
}
......@@ -167,7 +172,7 @@ string save_configuration(json &data)
{
auto rc = save_json(data, configFilePath, false, nullptr);
if(!rc.empty()) {
spdlog::error(rc);
rlogger->error(rc);
}
return rc;
......@@ -200,7 +205,7 @@ int32_t configure_stream(MaQueStreamChannel_e eStreamChn, MaQueVideoEncodeCfg_s
MaQueVideoEncodeCfg_s oldCfg;
if (LibXmMaQue_VideoEnc_getCfg(0, eStreamChn, &oldCfg)) {
spdlog::error("LibXmMaQue_VideoEncode_getCfg error");
rlogger->error("LibXmMaQue_VideoEncode_getCfg error");
return -1;
}
......@@ -234,8 +239,7 @@ int32_t configure_stream(MaQueStreamChannel_e eStreamChn, MaQueVideoEncodeCfg_s
oldCfg.iQpMax = cfg->iQpMax;
oldCfg.bUseUserQp = 1;
}
spdlog::info("main: fps={}", oldCfg.nFps);
rlogger->info("main: fps={}", oldCfg.nFps);
res = LibXmMaQue_VideoEnc_setCfg(0, eStreamChn, &oldCfg);
if (!res) {
memcpy(cfg, &oldCfg, sizeof(oldCfg));
......@@ -258,12 +262,11 @@ XM_S32 cb_frame_proc(XM_VOID *pUserArg, MaQueVideoEncFrameInfo_s *frame)
int ret;
frameCntTotal++;
zmq_msg_t msg;
if(frame->eEncodeType == MAQUE_ENCODE_TYPE_H264 && frame->eFrameType == MAQUE_FRAME_TYPE_VIDEO && frame->nDataLen > 0) {
#ifdef RAW_FORMAT
evpacket_t* pkt = (evpacket_t*)malloc(sizeof(evpacket_t) + frame->nDataLen);
#else
evpacket_t* pkt = (evpacket_t*)((char*)frame->pData - sizeof(evpacket_t));
#endif
zmq_msg_init_size(&msg, sizeof(evpacket_t) + frame->nDataLen );
evpacket_t* pkt = (evpacket_t*)zmq_msg_data(&msg);
memcpy((char*)pkt + sizeof(evpacket_t), frame->pData, frame->nDataLen);
pkt->meta.magic[0] = 0xBE;
pkt->meta.magic[1] = 0xEF;
memcpy(pkt->meta.sn, dev_sn, sizeof(dev_sn));
......@@ -285,11 +288,9 @@ XM_S32 cb_frame_proc(XM_VOID *pUserArg, MaQueVideoEncFrameInfo_s *frame)
else if(frame->eSubType == MAQUE_FRAME_SUBTYPE_P) {
frameCntPframe++;
}
#ifdef RAW_FORMAT
memcpy((char*)pkt + sizeof(evpacket_t), frame->pData, frame->nDataLen);
#endif
zmq_msg_init_data(&msg, (char*)pkt, frame->nDataLen + sizeof(evpacket_t), my_free, nullptr);
zmq_msg_send(&msg, pPub, 0);
zmq_msg_close(&msg);
packetId++;
}
......@@ -300,107 +301,135 @@ XM_S32 cb_frame_proc(XM_VOID *pUserArg, MaQueVideoEncFrameInfo_s *frame)
void frame_send_entry(void *args)
{
int ret = 0;
int gPusherFailsFlag = 3; // 1 - sub, 2 - dealer , 3 - both, 4 - disabled
gPusherFailsFlag = 0; // 1 - sub, 2 - dealer , 3 - both, 4 - disabled
evpacket_t *pkt = nullptr;
zmq_msg_t msg;
string res;
chrono::system_clock::time_point ts_start, ts_now;
// should never exit
while(1) {
/// TODO: check enable status
this_thread::sleep_for(4s);
rlogger->info("perpare for pusher");
if(!gConfigSystem.module.sys.push) {
gPusherFailsFlag |= 1 >> 2;
this_thread::sleep_for(4s);
continue;
}
LibXmMaQue_VideoEnc_forceIFrame(0, (MaQueStreamChannel_e) 0);
gPusherFailsFlag &= ~4;
// setup dealer
if(gPusherFailsFlag & 2) {
string vgwUrl = gJsonConfig[consts::kMsgConfigVgw].as<string>();
if(vgwUrl.substr(0, 6) != "tcp://"){
vgwUrl = "tcp://" + vgwUrl;
}
auto uri = httplib::Uri::Parse(vgwUrl);
int s = 0;
auto r = raw_connect(uri.Host, uri.Port, &s);
if(r < 0 || s == 0){
auto res = fmt::format("failed to connect cloud vgw: {}", vgwUrl);
spdlog::error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "pusher", "", json());
}
this_thread::sleep_for(120s);
continue;
}
ret = setupDealer(&pDealerCtx, &pDealer, string(dev_sn), vgwUrl, 10);
if(ret < 0) {
auto res = fmt::format("failed to connect cloud vgw: {}", vgwUrl);
spdlog::error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "pusher", "", json());
}
continue;
string vgwUrl = gJsonConfig[consts::kMsgConfigVgw].as<string>();
if(vgwUrl.substr(0, 6) != "tcp://") {
vgwUrl = "tcp://" + vgwUrl;
}
auto uri = httplib::Uri::Parse(vgwUrl);
int s = 0;
auto r = raw_connect(uri.Host, uri.Port, &s);
if(r < 0 || s == 0) {
auto res = fmt::format("failed to connect cloud vgw: {}", vgwUrl);
rlogger->error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "pusher", "", json());
}
spdlog::info("success connect to vgw: {}", vgwUrl);
this_thread::sleep_for(4s);
continue;
}
ret = setupDealer(&pDealerCtx, &pDealer, string(dev_sn), vgwUrl, 10 * 1000, 0);
if(ret < 0) {
auto res = fmt::format("failed to connect cloud vgw: {}", vgwUrl);
rlogger->error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "pusher", "", json());
}
gPusherFailsFlag |= 2;
}
// setup sub
if(gPusherFailsFlag &1) {
if(!gPusherFailsFlag) {
rlogger->info("success connect to vgw: {}", vgwUrl);
gPusherFailsFlag &= ~2;
// setup sub
pSub = zmq_socket(pPubCtx, ZMQ_SUB);
if(pSub == nullptr) {
spdlog::error("failed to create frame receiving socket");
rlogger->error("failed to create frame receiving socket");
continue;
}
// uint64_t hwm = 12;
// zmq_setsockopt(pSub, ZMQ_RCVHWM, &hwm, sizeof(hwm));
// int onlyOne = 1;
// zmq_setsockopt(pSub, ZMQ_CONFLATE, &onlyOne, sizeof(onlyOne));
// if(ret < 0) {
// rlogger->error("failed to set ZMQ_CONFLATE on pusher {}", zmq_strerror(ret));
// }
// ret = 10;
// ret = zmq_setsockopt(pPub, ZMQ_RCVHWM, &ret, sizeof(ret));
// if(ret < 0) {
// rlogger->error("failed to set ZMQ_RCVHWM on pusher sub {}", zmq_strerror(zmq_errno()));
// }
ret = zmq_connect(pSub, consts::strPubUrl.c_str());
if(ret != 0) {
spdlog::error("failed connect frame pub: {}", consts::strPubUrl);
rlogger->error("failed connect to frame pub: {}", consts::strPubUrl);
continue;
}
ret = zmq_setsockopt(pSub, ZMQ_SUBSCRIBE, "", 0);
spdlog::info("zmq sub successed (for frame sending)");
rlogger->info("zmq sub successed (for frame sending)");
gPusherFailsFlag &= ~1;
}
zmq_msg_t msg;
while (1) {
// TODO: reconnect
zmq_msg_init(&msg);
ret = zmq_msg_recv(&msg, pSub, 0);
if(ret < 0) {
spdlog::error("failed to recv zmq msg: {}", zmq_strerror(zmq_errno()));
gPusherFailsFlag |= 1 << 0;
break;
}
// ssize_t sz = zmq_msg_size(&msg);
// char *ptr = (char*)zmq_msg_data(&msg);
if(!gConfigSystem.module.sys.push) {
gPusherFailsFlag |= 1 << 2;
zmq_msg_close(&msg);
continue;
}else{
ret = zmq_msg_send(&msg, pDealer, 0);
zmq_msg_close(&msg);
}
if(ret < 0) {
auto res = fmt::format("failed to send vgw {}: {}", gJsonConfig[consts::kMsgConfigVgw].as<string>(), zmq_strerror(zmq_errno()));
spdlog::error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "pusher", "", json());
if(!gPusherFailsFlag) {
// force I Frame
LibXmMaQue_VideoEnc_forceIFrame(0, (MaQueStreamChannel_e) 0);
rlogger->info("start pushing stream");
while (1) {
zmq_msg_init(&msg);
ret = zmq_msg_recv(&msg, pSub, 0);
if(ret < 0) {
rlogger->error("failed to recv zmq msg: {}", zmq_strerror(zmq_errno()));
gPusherFailsFlag |= 1 << 0;
break;
}
// ssize_t sz = zmq_msg_size(&msg);
// char *ptr = (char*)zmq_msg_data(&msg);
if(!gConfigSystem.module.sys.push) {
gPusherFailsFlag |= 1 << 2;
zmq_msg_close(&msg);
continue;
}
else {
ret = zmq_msg_send(&msg, pDealer, 0);
zmq_msg_close(&msg);
}
if(ret < 0) {
auto res = fmt::format("failed to send vgw {}: {}", gJsonConfig[consts::kMsgConfigVgw].as<string>(), zmq_strerror(zmq_errno()));
rlogger->error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "pusher", "", json());
}
gPusherFailsFlag |= 1 << 1;
break;
}
gPusherFailsFlag |= 1 << 1;
break;
}
}
if(gPusherFailsFlag & 1) {
zmq_close(pSub);
if(pSub) {
zmq_close(pSub);
}
pSub = nullptr;
}
if(gPusherFailsFlag & 2) {
zmq_close(pDealer);
zmq_ctx_destroy(pDealerCtx);
if(pDealer) {
zmq_close(pDealer);
}
if(pDealerCtx) {
zmq_ctx_destroy(pDealerCtx);
}
pDealer = nullptr;
pDealerCtx = nullptr;
}
this_thread::sleep_for(4s);
gPusherFailsFlag = 0;
}
}
......@@ -452,7 +481,7 @@ string verify_config(json &data)
}
if(!str.empty()) {
spdlog::error(str);
rlogger->error(str);
}
return str;
......@@ -466,7 +495,7 @@ string apply_config(json &data)
try {
/// make defaults
gConfigSystem.module.sys.fps = 15; // secs
gConfigSystem.module.sys.video_quality = MAQUE_IMG_QUALITY_BETTER;
gConfigSystem.module.sys.video_quality = MAQUE_IMG_QUALITY_GOOD;
gConfigSystem.module.record.interval = 60 * 2; // 2 minutes interval
gConfigSystem.module.sys.bitrate_kb = 1024; // 1Mbps
gConfigSystem.module.sys.bitrate_type = MAQUE_BITRATE_CTRL_VBR;
......@@ -510,7 +539,7 @@ string apply_config(json &data)
json &cfgFeatures = gJsonConfig[consts::kMsgConfigFeatures];
if(cfgFeatures.contains("logLevel")) {
logLevel = spdlog::level::from_str(cfgFeatures["logLevel"].as<string>());
spdlog::set_level(logLevel);
rlogger->set_level(logLevel);
}
// fps
......@@ -518,7 +547,7 @@ string apply_config(json &data)
gConfigSystem.module.sys.fps = cfgFeatures["fps"].as<int>();
}
else {
spdlog::warn("no fps configuration, using default: {}", gConfigSystem.module.sys.fps);
rlogger->warn("no fps configuration, using default: {}", gConfigSystem.module.sys.fps);
}
// record
......@@ -526,7 +555,7 @@ string apply_config(json &data)
gConfigSystem.module.record.interval = cfgFeatures["recordLen"].as<int>();
}
else {
spdlog::warn("no recordLen configuration, using default: {}", gConfigSystem.module.record.interval);
rlogger->warn("no recordLen configuration, using default: {}", gConfigSystem.module.record.interval);
}
// quality
......@@ -534,27 +563,30 @@ string apply_config(json &data)
gConfigSystem.module.sys.video_quality = cfgFeatures["videoQuality"].as<int>();
}
else {
spdlog::warn("no video quality configuration, using default: {}", gConfigSystem.module.sys.video_quality);
rlogger->warn("no video quality configuration, using default: {}", gConfigSystem.module.sys.video_quality);
}
// bitrate
if(cfgFeatures.contains("bitrate")) {
gConfigSystem.module.sys.bitrate_kb = cfgFeatures["bitrate"].as<int>();
if(gConfigSystem.module.sys.bitrate_kb > 4096 || gConfigSystem.module.sys.bitrate_kb < 200) {
gConfigSystem.module.sys.bitrate_kb = 1024;
}
}
else {
spdlog::warn("no video bitrate configuration, using default: {}", gConfigSystem.module.sys.bitrate_kb);
rlogger->warn("no video bitrate configuration, using default: {}", gConfigSystem.module.sys.bitrate_kb);
}
if(cfgFeatures.contains("bitrateType")) {
gConfigSystem.module.sys.bitrate_type = cfgFeatures["bitrateType"].as<int>();
}
else {
spdlog::warn("no video bitrate type configuration, using default: {}", gConfigSystem.module.sys.bitrate_type);
rlogger->warn("no video bitrate type configuration, using default: {}", gConfigSystem.module.sys.bitrate_type);
}
if(cfgFeatures.contains("push")) {
gConfigSystem.module.sys.push = cfgFeatures["push"].as<int>();
}
else {
spdlog::warn("no push configuration, using default: {}", gConfigSystem.module.sys.push);
rlogger->warn("no push configuration, using default: {}", gConfigSystem.module.sys.push);
}
// motion
......@@ -565,20 +597,24 @@ string apply_config(json &data)
}
if(motion.contains("level")) {
gConfigSystem.module.motion.level = motion["level"].as<int>();
if(gConfigSystem.module.motion.level > 6 || gConfigSystem.module.motion.level < 1) {
gConfigSystem.module.motion.level = 3;
}
}
if(motion.contains("region") && motion["region"].contains("minX") && motion["region"].contains("minY") && motion["region"].contains("maxX") && motion["region"].contains("maxY")) {
gConfigSystem.module.motion.region = {motion["region"]["minX"].as<float>(), motion["region"]["minY"].as<float>(), motion["region"]["maxX"].as<float>(), motion["region"]["maxY"].as<float>()};
if(gConfigSystem.module.motion.region.minx >= 0 && gConfigSystem.module.motion.region.maxx > gConfigSystem.module.motion.region.minx && gConfigSystem.module.motion.region.maxx <=1 &&
gConfigSystem.module.motion.region.miny >= 0 && gConfigSystem.module.motion.region.maxy > gConfigSystem.module.motion.region.miny && gConfigSystem.module.ai.region.maxy <=1
){
if(gConfigSystem.module.motion.region.minx >= 0 && gConfigSystem.module.motion.region.maxx > gConfigSystem.module.motion.region.minx && gConfigSystem.module.motion.region.maxx <=1 &&
gConfigSystem.module.motion.region.miny >= 0 && gConfigSystem.module.motion.region.maxy > gConfigSystem.module.motion.region.miny && gConfigSystem.module.ai.region.maxy <=1
) {
//
}else{
spdlog::error("invalid region for ai: {}", motion.to_string());
}
else {
rlogger->error("invalid region for ai: {}", motion.to_string());
gConfigSystem.module.motion.region = {0,0,1,1};
}
}
}
spdlog::info("motion configuration: enabled {}, level {}, region ({},{}), ({}, {})", gConfigSystem.module.motion.enabled, gConfigSystem.module.motion.level, gConfigSystem.module.motion.region.minx, gConfigSystem.module.motion.region.miny, gConfigSystem.module.motion.region.maxx, gConfigSystem.module.motion.region.maxy);
rlogger->info("motion configuration: enabled {}, level {}, region ({},{}), ({}, {})", gConfigSystem.module.motion.enabled, gConfigSystem.module.motion.level, gConfigSystem.module.motion.region.minx, gConfigSystem.module.motion.region.miny, gConfigSystem.module.motion.region.maxx, gConfigSystem.module.motion.region.maxy);
// ai
if(cfgFeatures.contains("ai")) {
......@@ -594,23 +630,24 @@ string apply_config(json &data)
}
if(ai.contains("region") && ai["region"].contains("minX") && ai["region"].contains("minY") && ai["region"].contains("maxX") && ai["region"].contains("maxY")) {
gConfigSystem.module.ai.region = {ai["region"]["minX"].as<float>(), ai["region"]["minY"].as<float>(), ai["region"]["maxX"].as<float>(), ai["region"]["maxY"].as<float>()};
if(gConfigSystem.module.ai.region.minx >= 0 && gConfigSystem.module.ai.region.maxx > gConfigSystem.module.ai.region.minx && gConfigSystem.module.ai.region.maxx <=1 &&
gConfigSystem.module.ai.region.miny >= 0 && gConfigSystem.module.ai.region.maxy > gConfigSystem.module.ai.region.miny && gConfigSystem.module.ai.region.maxy <=1
){
if(gConfigSystem.module.ai.region.minx >= 0 && gConfigSystem.module.ai.region.maxx > gConfigSystem.module.ai.region.minx && gConfigSystem.module.ai.region.maxx <=1 &&
gConfigSystem.module.ai.region.miny >= 0 && gConfigSystem.module.ai.region.maxy > gConfigSystem.module.ai.region.miny && gConfigSystem.module.ai.region.maxy <=1
) {
//
}else{
spdlog::error("invalid region for ai: {}", ai.to_string());
}
else {
rlogger->error("invalid region for ai: {}", ai.to_string());
gConfigSystem.module.ai.region = {0,0,1,1};
}
}
}
spdlog::info("ai configuration: enabled {}, faceThresh {}, humanThresh {}, region ({},{}), ({}, {})", gConfigSystem.module.ai.enabled, gConfigSystem.module.ai.face_thresh, gConfigSystem.module.ai.human_thresh, gConfigSystem.module.ai.region.minx, gConfigSystem.module.ai.region.miny, gConfigSystem.module.ai.region.maxx, gConfigSystem.module.ai.region.maxy);
rlogger->info("ai configuration: enabled {}, faceThresh {}, humanThresh {}, region ({},{}), ({}, {})", gConfigSystem.module.ai.enabled, gConfigSystem.module.ai.face_thresh, gConfigSystem.module.ai.human_thresh, gConfigSystem.module.ai.region.minx, gConfigSystem.module.ai.region.miny, gConfigSystem.module.ai.region.maxx, gConfigSystem.module.ai.region.maxy);
}
}
catch(exception &e) {
/// TODO: save for reporting
rc = fmt::format("failed to apply config: {}, {}", e.what(), data.to_string());
spdlog::error(rc);
rlogger->error(rc);
}
return rc;
}
......@@ -621,7 +658,7 @@ bool verify_qr_target(string target)
return true;
}
else {
spdlog::error("invalid qr target: {}", target);
rlogger->error("invalid qr target: {}", target);
return false;
}
}
......@@ -657,13 +694,13 @@ void process_qr_event(string strQR)
/// wifi
if(member.key() == "WIFI") {
if(v.size() != 2) {
spdlog::error("handle_qr: invalid WIFI config data: {}", v.size());
rlogger->error("handle_qr: invalid WIFI config data: {}", v.size());
}
else {
string buffer = fmt::format("network={{\nssid=\"{}\"\npsk=\"{}\"\n}}", v[0].as<string>(), v[1].as<string>());
ofstream wpa(sysWpaCfgFilePath, std::ios::trunc);
if(!wpa.is_open()) {
spdlog::error("handle_qr: failed to open wpa.conf");
rlogger->error("handle_qr: failed to open wpa.conf");
}
else {
wpa.write(buffer.c_str(), buffer.size());
......@@ -673,13 +710,13 @@ void process_qr_event(string strQR)
}
else if(member.key() == "SN") {
if(v.size() != 1) {
spdlog::error("handle_qr: invalid SN config data: {}", v.size());
rlogger->error("handle_qr: invalid SN config data: {}", v.size());
}
else {
json sysCfg;
string sn_ = v[0].as<string>();
if(sn_.size() != 12) {
spdlog::error("handle_qr: sn size is not 12 bytes but {}", sn_.size());
rlogger->error("handle_qr: sn size is not 12 bytes but {}", sn_.size());
}
else {
evutils::load_json(sysCfg, sysCfgJsonFilePath);
......@@ -691,7 +728,7 @@ void process_qr_event(string strQR)
}
else if(member.key() == "MQ") {
if(v.size() != 1) {
spdlog::error("handle_qr: invalid MQ config data: {}", v.size());
rlogger->error("handle_qr: invalid MQ config data: {}", v.size());
}
else {
json sysCfg;
......@@ -699,7 +736,7 @@ void process_qr_event(string strQR)
// std::replace(mq.begin(), mq.end(), '/', ':');
auto uri = httplib::Uri::Parse(mq);
if(uri.Host.empty() || uri.Port.empty()) {
spdlog::error("handle_qr: invalid MQ config data: {}", mq);
rlogger->error("handle_qr: invalid MQ config data: {}", mq);
}
else {
gJsonConfig["mqtt"] = mq;
......@@ -709,7 +746,7 @@ void process_qr_event(string strQR)
}
}
else {
spdlog::error("handle_qr: unkown content: {}", member.key(), member.value().to_string());
rlogger->error("handle_qr: unkown content: {}", member.key(), member.value().to_string());
// revoke any preceding reboot request
// reboot = false;
break;
......@@ -717,7 +754,7 @@ void process_qr_event(string strQR)
}
if(reboot) {
spdlog::warn("handler_qr: system rebooting");
rlogger->warn("handler_qr: system rebooting");
/// caveate: make sure it will reboot
LibXmMaQue_System_reboot();
// net goes here
......@@ -731,14 +768,14 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
{
char msg[len+1] = {0};
memcpy(msg, data, len);
spdlog::info("received request on {}:\n{}", topic, (char*)msg);
rlogger->info("received request on {}:\n{}", topic, (char*)msg);
auto now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
try {
auto js = json::parse(msg);
auto str = verify_request(js);
if(!str.empty()) {
spdlog::error(str);
rlogger->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());
return;
}
......@@ -753,12 +790,12 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
// verify configuration
if(data.size() == 0) {
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, 0, "OK", cmd, rid, gJsonConfig);
spdlog::info("local config sent");
rlogger->info("local config sent");
return;
}
auto rv = verify_config(data);
if(!rv.empty()) {
spdlog::error("failed to apply config: {}, {}", rv, data.to_string());
rlogger->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 {
......@@ -769,9 +806,9 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
}
else {
// apply diff to tmp and verify
spdlog::info("json diff: {}", patch.to_string());
rlogger->info("json diff: {}", patch.to_string());
auto strP = patch.to_string();
if(strP.find("mqtt") != string::npos || strP.find("upload") != string::npos || strP.find("vgw") != string::npos) {
if(strP.find("mqtt") != string::npos || strP.find("upload") != string::npos || strP.find("vgw") != string::npos || strP.find("region") != string::npos) {
needReboot = true;
}
auto rc = verify_config(data);
......@@ -781,7 +818,7 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
rc = apply_config(data);
}
if(!rc.empty()) {
spdlog::error(rc);
rlogger->error(rc);
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_response + rid, EV_MSG_ERROR_INVALID_PARAM, rc, cmd, rid, data);
}
else {
......@@ -789,7 +826,7 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
/// TODO: using json diff to apply change without reboot
save_configuration(data);
if(needReboot) {
spdlog::info("rebooting to apply new configuration");
rlogger->info("rebooting to apply new configuration");
LibXmMaQue_System_reboot();
}
}
......@@ -894,16 +931,16 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
}
else if(cmd == consts::kMsgCmdPtz) {
// response Ptz control
spdlog::info("mqtt got ptz cmd: {}", data.to_string());
rlogger->info("mqtt got ptz cmd: {}", data.to_string());
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);
}
else {
/// TODO: issue need to clear
if(true) {
spdlog::info("mqtt begin move");
rlogger->info("mqtt begin move");
string rc = ptz_move(data["action"].as<string>(), data["degree"].as<float>());
spdlog::info("mqtt end move");
rlogger->info("mqtt end move");
ptz_param_position pos;
ptz_get_params(pos);
json _js;
......@@ -929,19 +966,19 @@ void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string t
if(gRecFilesList != nullptr)
print_ts_files(*gRecFilesList);
else {
spdlog::warn("no local video files");
rlogger->warn("no local video files");
}
}
else {
auto str = fmt::format("unsupported cmd {}: {}", cmd, js.to_string());
spdlog::error(str);
rlogger->error(str);
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_report, EV_MSG_ERROR_UNSUPPORTED_CMD, str, cmd, rid, json());
}
}
catch(exception &e) {
auto str = string("exception: ") + e.what() + string(", ") + msg;
spdlog::error(str);
rlogger->error(str);
MqttMgr::report_response_args(gMqttClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, str, string(""), string(""), json());
}
//(*hlp)[to_string(stoi(topic) + 1)]("hello ack2", 11);
......@@ -952,7 +989,7 @@ MqttHelper* start_mqtt(void *arg)
{
auto client = MqttMgr::get_instance(consts::mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("failed to create mqtt instance to {}", consts::mqtt_url);
rlogger->error("failed to create mqtt instance to {}", consts::mqtt_url);
return nullptr;
}
client->subscribe(consts::sub_topic + dev_sn, handle_mqtt_req, false);
......@@ -966,7 +1003,7 @@ void create_sd_directories()
{
// create record paths
for(auto k:get_sdcard_resered_dirs()) {
spdlog::info("create path: {}", k);
rlogger->info("create path: {}", k);
system((string("mkdir -p ") + k).c_str());
}
}
......@@ -975,8 +1012,16 @@ void create_sd_directories()
int main(int argc, char *argv[])
{
int ret = XM_SUCCESS;
auto envlog = getenv("CONSOLE");
if(envlog != nullptr) {
if(!strncmp(envlog, "true", 4)){
rlogger = spdlog::default_logger();
}
}
// disable watchdog first
LibXmMaQue_Watchdog_enable(0);
signal(SIGINT, clean_up);
// signal(SIGTERM, clean_up);
......@@ -1002,7 +1047,7 @@ int main(int argc, char *argv[])
if(!snReady) {
get_mac_addr(dev_sn);
}
spdlog::info("hi3518ev300, sn: {}, version: {}, BE: {}", dev_sn, evutils::version, is_big_endian());
rlogger->info("hi3518ev300, sn: {}, version: {}, BE: {}", dev_sn, (char*)evutils::consts::version, is_big_endian());
// get env for host and port
auto host_ = getenv("VGW_HOST");
......@@ -1033,18 +1078,18 @@ int main(int argc, char *argv[])
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");
rlogger->error("failed to apply configuration: {}", gJsonConfig.to_string());
rlogger->error("defaults is used instead");
}
auto logLevel_ = getenv("LOG_LEVEL");
if(logLevel_) {
logLevel = spdlog::level::from_str(logLevel_);
spdlog::set_level(logLevel);
rlogger->set_level(logLevel);
}
else {
spdlog::info("log level configured to info");
spdlog::set_level(logLevel);
rlogger->info("log level configured to info");
rlogger->set_level(logLevel);
}
MaQue_Demo_Mem_Init();
......@@ -1052,12 +1097,12 @@ int main(int argc, char *argv[])
MaQueStartParam_s startParam = {MAQUE_VIDEO_STANDARD_PAL, {MAQUE_VIDEO_COMPRESS_H264, MAQUE_VIDEO_COMPRESS_H265}};
::memcpy(startParam.aWritableDir, strMaQuePath.c_str(), sizeof(startParam.aWritableDir));
ret = LibXmMaQue_System_startUp(&startParam);
spdlog::info("ret: {}", ret);
rlogger->info("ret: {}", ret);
//
MaQueMemoryApi_s memApi = {MaQue_Demo_Mem_alloc, MaQue_Demo_Mem_release, MaQue_Demo_Mem_addRef, MaQue_Demo_Mem_setLength};
ret = LibXmMaQue_Mem_init(&memApi);
spdlog::info("mem ret: {}", ret);
rlogger->info("mem ret: {}", ret);
MaQueSystemTime_s tm;
LibXmMaQue_Time_getCurrentTime(&tm);
......@@ -1065,13 +1110,13 @@ int main(int argc, char *argv[])
//
MaQueCodeAbilities_s capb;
ret = LibXmMaQue_VideoEnc_getAbilities(0, &capb);
spdlog::info("abt ret: {}", ret);
rlogger->info("abt ret: {}", ret);
if (XM_SUCCESS == ret) {
spdlog::info(fmt::format("capbilities: supported codecs {0:#b},"
"max fhd/s {0:d}, max res: {0:d}, per ch mres: {0:d}, {0:d}, {0:d}, {0:d}",
int(capb.videoEncTypeMask),
int(capb.maxEncPowerX1080P), int(capb.eDecImageSizeMax), int(capb.astVidEncChnAbility[0].eCapSizeMax),
int(capb.astVidEncChnAbility[1].eCapSizeMax), int(capb.astVidEncChnAbility[2].eCapSizeMax), int(capb.astVidEncChnAbility[3].eCapSizeMax)));
rlogger->info(fmt::format("capbilities: supported codecs {0:#b},"
"max fhd/s {0:d}, max res: {0:d}, per ch mres: {0:d}, {0:d}, {0:d}, {0:d}",
int(capb.videoEncTypeMask),
int(capb.maxEncPowerX1080P), int(capb.eDecImageSizeMax), int(capb.astVidEncChnAbility[0].eCapSizeMax),
int(capb.astVidEncChnAbility[1].eCapSizeMax), int(capb.astVidEncChnAbility[2].eCapSizeMax), int(capb.astVidEncChnAbility[3].eCapSizeMax)));
}
//
......@@ -1082,17 +1127,22 @@ int main(int argc, char *argv[])
gConfigSystem.pClient = nullptr;
cfg.nFps = gConfigSystem.module.sys.fps;
cfg.eIFrmIntvType = IFRAME_INTV_TYPE_TIME;
cfg.nIFrameInterval = 10;
cfg.nIFrameInterval = 5;
cfg.eImageQuality = (MaQueImageQuality_e)gConfigSystem.module.sys.video_quality;
cfg.eVidComp = MAQUE_VIDEO_COMPRESS_H264;
cfg.eBitrateCtrl = (MaQueBitrateCtrl_e)gConfigSystem.module.sys.bitrate_type;
cfg.nBitRate = gConfigSystem.module.sys.bitrate_kb;
cfg.eCapSize = MAQUE_CAPTURE_SIZE_1080P;
cfg.bUseUserQp = 1;
cfg.iQpMax = 48;
cfg.iQpMin = 24;
ret = configure_stream((MaQueStreamChannel_e)0, &cfg);
spdlog::info("cfg stream ret: {}", ret);
rlogger->info("cfg stream ret: {}", ret);
ret = LibXmMaQue_VideoEnc_startStream(0, (MaQueStreamChannel_e)0, cb_frame_proc, (void *)&args);
if (XM_SUCCESS == ret) {
spdlog::info("LibXmMaQue_VideoEnc_startStream success");
rlogger->info("LibXmMaQue_VideoEnc_startStream success");
}
//
......@@ -1107,11 +1157,11 @@ int main(int argc, char *argv[])
int64_t delta = 0;
while(delta < dura||always) {
string result;
spdlog::info("got qr job");
rlogger->info("got qr job");
string res = qr_analysis(result);
if(res.empty()) {
/// TODO: handle result
spdlog::info("processing qr event");
rlogger->info("processing qr event");
process_qr_event(result);
if(!always) {
// set to stop
......@@ -1136,12 +1186,12 @@ int main(int argc, char *argv[])
// no task
}
/// check for task every 3s
// spdlog::info("debug qr task");
// rlogger->info("debug qr task");
this_thread::sleep_for(chrono::seconds(4));
}
});
spdlog::info("created QR task");
rlogger->info("created QR task");
// blocking update time, since time is critical to every other component
bool bGotTime = false;
......@@ -1150,16 +1200,16 @@ int main(int argc, char *argv[])
LibXmMaQue_Watchdog_setTimeOutSecs(30);
while(!bGotTime) {
if(getNtpTime(&stm) >=0) {
spdlog::info("ntp got time");
rlogger->info("ntp got time");
::stime(&stm);
bGotTime = true;
}
else {
spdlog::warn("failed to get ntp time");
rlogger->warn("failed to get ntp time");
// enable QR
gQRCodeDuration.store(10, memory_order_relaxed);
}
//spdlog::info("debug ntp");
//rlogger->info("debug ntp");
LibXmMaQue_Watchdog_keepAlive();
this_thread::sleep_for(chrono::seconds(3));
}
......@@ -1167,7 +1217,7 @@ int main(int argc, char *argv[])
// disable qr
gQRCodeDuration.store(0, memory_order_relaxed);
/// subscribe to mqtt
/// subscribe to mqtt
while(gMqttClient == nullptr) {
gQRCodeDuration.store(-1, memory_order_relaxed);
/// TODO: handle result
......@@ -1182,28 +1232,28 @@ int main(int argc, char *argv[])
// frame dispatching
pPubCtx = zmq_ctx_new();
pPub = zmq_socket(pPubCtx, ZMQ_PUB);
// uint64_t hwm = 12;
// ret = zmq_setsockopt(pPub, ZMQ_SNDHWM, &hwm, sizeof(hwm));
// int onlyOne = 1;
// zmq_setsockopt(pPub, ZMQ_CONFLATE, &onlyOne, sizeof(onlyOne));
// if(ret < 0) {
// spdlog::error("failed to set ZMQ_SNDHWM on pub {}", zmq_strerror(ret));
// rlogger->error("failed to set ZMQ_CONFLATE on pub {}", zmq_strerror(zmq_errno()));
// }
// int onlyOne = 1;
// ret = zmq_setsockopt(pPub, ZMQ_CONFLATE, &onlyOne, sizeof(onlyOne));
// ret = 10;
// ret = zmq_setsockopt(pPub, ZMQ_SNDHWM, &ret, sizeof(ret));
// if(ret < 0) {
// spdlog::error("failed to set ZMQ_CONFLATE on pub {}", zmq_strerror(ret));
// rlogger->error("failed to set ZMQ_SNDHWM on pub {}", zmq_strerror(zmq_errno()));
// }
ret = zmq_bind(pPub, consts::strPubUrl.c_str());
if(ret < 0) {
spdlog::error("{} failed to create zmq pub topic: {}", consts::strPubUrl);
rlogger->error("{} failed to create zmq pub topic: {}", consts::strPubUrl);
exit(1);
}
/// TODO: configuration issues handling
if(msg_field(gJsonConfig, consts::kMsgConfigVgw).empty()) {
spdlog::error("missing vgw config");
rlogger->error("missing vgw config");
}
thread thPush = thread(frame_send_entry, &args);
spdlog::info("sizeof pkt header {}, sizeof tv {}", sizeof(evpacket_t), sizeof(timeval));
rlogger->info("sizeof pkt header {}, sizeof tv {}", sizeof(evpacket_t), sizeof(timeval));
if(thPush.joinable()) {
thPush.detach();
}
......@@ -1214,17 +1264,17 @@ int main(int argc, char *argv[])
}
while(gConfigSystem.isRecordRunning == false) {
spdlog::info("waiting for record svc to start");
rlogger->info("waiting for record svc to start");
this_thread::sleep_for(chrono::seconds(3));
}
spdlog::info("recording svc started, starting upload svc");
rlogger->info("recording svc started, starting upload svc");
// start upload thread
thread thUpload = thread(upload_svc_entry, &gUploadArgs);
if(thUpload.joinable()) {
thUpload.detach();
spdlog::info("started video upload service");
rlogger->info("started video upload service");
}
start_md_bd(&gConfigSystem);
......@@ -1232,14 +1282,14 @@ int main(int argc, char *argv[])
gQRCodeDuration.store(0);
thread thSmart = thread(maq_smart_task_entry, &gConfigSystem);
if(thSmart.joinable()){
if(thSmart.joinable()) {
thSmart.detach();
}
// thread for watchdog
LibXmMaQue_Watchdog_enable(1);
LibXmMaQue_Watchdog_setTimeOutSecs(33);
while(1){
while(1) {
this_thread::sleep_for(20s);
LibXmMaQue_Watchdog_keepAlive();
}
......
#include <iostream>
#include <string>
#include <chrono>
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include "ref-memory.hpp"
#include <maque_system.h>
#include <maque_video_enc.h>
#include <maque_time.h>
#include <evpacket.h>
#include "common.h"
#include "ntp.h"
#include <time.h>
#include "raw_tcp.h"
#include "smart.h"
#include "motion.h"
#include <thread>
#include "mqttmgr.hpp"
#include "utils.hpp"
#include <zmq.h>
using namespace std;
using namespace jsoncons;
using namespace evutils;
///
#define NUM_IFRAME_PICK 2
#define NUM_MAX_QUEQUE_SIZE 60
#define NUM_MAX_PACKET_BYTES 1200
#define INTERVAL_LOG_QFULL 3 // its about 60/15 *3 secs
/// device info
static char dev_sn[TERMINAL_SN_SIZE] = {0};
/// default video gateway info
static int videoQuality = MAQUE_IMG_QUALITY_BETTER;
static uint64_t frameCntTotal = 0;
static uint32_t frameCntIframe = 0;
static uint32_t frameCntPframe = 0;
static uint64_t packetId = 0;
static spdlog::level::level_enum logLevel = spdlog::level::info;
static bool bPFrameAvail = false;
static bool bNeedRetry = false;
static int readFaildCnt = 0;
static bool enablePush = false;
static bool enableRecord = false;
static bool bCanRecord = false;
static bool bConnected = false;
static queue<DataItem> _frameQueue;
static mutex _mutFrame;
static condition_variable _condFrame;
static Notifier _notiFrame = {&_mutFrame, &_condFrame};
static CallBackArg args = {nullptr, &_notiFrame, &_frameQueue};
static int raw_socket_ = 0;
static bool bGotTime = false;
// zmq
static void *pPubCtx = nullptr;
static void *pPub = nullptr;
static const string strPubUrl = "inproc://frame";
/// flags used to stop modules
/// 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 gConfigAI = {0};
static ev_module_config_t gConfigMotion = {0};
enum EV_MSG_ERROR_CODE {
EV_MSG_ERROR_NONE,
EV_MSG_ERROR_EXCEPTION,
EV_MSG_ERROR_UNSUPPORTED_CMD,
EV_MSG_ERROR_CONTENT_SYNTAX,
EV_MSG_ERROR_NUM
};
void clean_up(int sig)
{
if(sig == SIGPIPE) {
// vgw connection error
bConnected = false;
bNeedRetry = true;
return;
}
spdlog::warn("clearn up called, with sig: {}", sig);
if (args.recFD) {
fclose(args.recFD);
}
if (raw_socket_) {
::close(raw_socket_);
}
// 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();
LibXmMaQue_System_destroy();
exit(1);
}
bool is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
MaQueVideoEncodeCfg_s *init_stream_cfg(MaQueVideoEncodeCfg_s *cfg)
{
cfg->eVidComp = (MaQueVideoCompress_e)-1;
cfg->eCapSize = (MaQueCaptureSize_e)-1;
cfg->eBitrateCtrl = (MaQueBitrateCtrl_e)-1;
cfg->eImageQuality = (MaQueImageQuality_e)-1;
cfg->nFps = -1;
cfg->nBitRate = -1;
cfg->nIFrameInterval = -1;
cfg->iQpMin = -1;
cfg->iQpMax = -1;
return cfg;
}
int32_t configure_stream(MaQueStreamChannel_e eStreamChn, MaQueVideoEncodeCfg_s *cfg)
{
int32_t res;
MaQueVideoEncodeCfg_s oldCfg;
if (LibXmMaQue_VideoEnc_getCfg(0, eStreamChn, &oldCfg)) {
spdlog::error("LibXmMaQue_VideoEncode_getCfg error");
return -1;
}
if (cfg->eVidComp != -1) {
oldCfg.eVidComp = cfg->eVidComp;
}
if (cfg->eCapSize != -1) {
oldCfg.eCapSize = cfg->eCapSize;
}
if (cfg->eBitrateCtrl != -1) {
oldCfg.eBitrateCtrl = cfg->eBitrateCtrl;
}
if (cfg->eImageQuality != -1) {
oldCfg.eImageQuality = cfg->eImageQuality;
}
if (cfg->nFps != -1) {
oldCfg.nFps = cfg->nFps;
}
if (cfg->nBitRate != -1) {
oldCfg.nBitRate = cfg->nBitRate;
}
if (cfg->nIFrameInterval != -1) {
oldCfg.nIFrameInterval = cfg->nIFrameInterval;
}
if (cfg->iQpMin > 0 && cfg->iQpMin <= 51 && cfg->iQpMax > 0 && cfg->iQpMax <= 51) {
oldCfg.iQpMin = cfg->iQpMin;
oldCfg.iQpMax = cfg->iQpMax;
oldCfg.bUseUserQp = 1;
}
spdlog::info("main: fps={}", oldCfg.nFps);
res = LibXmMaQue_VideoEnc_setCfg(0, eStreamChn, &oldCfg);
if (!res) {
memcpy(cfg, &oldCfg, sizeof(oldCfg));
}
return res;
}
//
void frame_free(void *data, void*hint)
{
MaQueVideoEncFrameInfo_s *frame = (MaQueVideoEncFrameInfo_s *)hint;
MaQue_Demo_Mem_release(frame->handleMem);
}
//
XM_S32 cb_frame_proc(XM_VOID *pUserArg, MaQueVideoEncFrameInfo_s *frame)
{
MaQueSystemTime_s *pstSysTime = &frame->stTimeStamp.stSysTime;
CallBackArg *args = (CallBackArg *)pUserArg;
struct timeval stTimeVal;
int ret;
frameCntTotal++;
zmq_msg_t msg;
void *hint = NULL;
if(frame->eEncodeType == MAQUE_ENCODE_TYPE_H264 && frame->eFrameType == MAQUE_FRAME_TYPE_VIDEO && frame->nDataLen > 0) {
evpacket_t* pkt = (evpacket_t*)((char*)frame->pData - sizeof(evpacket_t));
pkt->meta.magic[0] = 0xBE;
pkt->meta.magic[1] = 0xEF;
memcpy(pkt->meta.sn, dev_sn, sizeof(dev_sn));
/// NOTES: since both ends use little-endian, we don't use htonl, htons to save clocks
pkt->vpara.frame_type = frame->eSubType + 1;
pkt->vpara.packet_type = frame->eEncodeType;
pkt->vpara.res.width = frame->nWidth;
pkt->vpara.res.height = frame->nHeight;
/// TODO:
pkt->vpara.fps = 15;
//pkt->vpara.ts = frame->stTimeStamp;
pkt->length = frame->nDataLen;
pkt->meta.packet_id = packetId;
pkt->meta.crc = 0;
pkt->meta.crc = crc16((unsigned char*)pkt, sizeof(evpacket_t));
if(frame->eSubType == MAQUE_FRAME_SUBTYPE_I) {
frameCntIframe++;
}
else if(frame->eSubType == MAQUE_FRAME_SUBTYPE_P) {
frameCntPframe++;
}
zmq_msg_init_data(&msg, (char*)pkt, frame->nDataLen + sizeof(evpacket_t), nullptr, nullptr);
zmq_msg_send(&msg, pPub, 0);
packetId++;
}
MaQue_Demo_Mem_release(frame->handleMem);
return XM_SUCCESS;
}
void frame_send_entry(void *args)
{
int ret = 0;
// setup sub
void *pSub = zmq_socket(pPubCtx, ZMQ_SUB);
if(pSub == nullptr) {
spdlog::error("failed to create frame sending socket");
exit(1);
}
// ret = 1;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE, &ret, sizeof (ret));
// ret = 2;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_IDLE, &ret, sizeof (ret));
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
ret = zmq_connect(pSub, strPubUrl.c_str());
if(ret != 0) {
spdlog::error("failed connect frame pub: {}", strPubUrl);
exit(1);
}
ret = zmq_setsockopt(pSub, ZMQ_SUBSCRIBE, "", 0);
spdlog::info("zmq sub successed (for frame sending)");
zmq_msg_t msg;
while (1) {
if(!bConnected||raw_socket_ <= 0) {
if(frameCntTotal % (NUM_MAX_QUEQUE_SIZE * 3) == 0) {
bNeedRetry = true;
if(raw_socket_) {
::close(raw_socket_);
raw_socket_ = 0;
}
}
if(bNeedRetry == true) {
// try reconect
bNeedRetry = false;
spdlog::info("try reconnecting to video gateway {}:{}", host, port);
auto ret = raw_connect(host, port, &raw_socket_);
if (ret < 0 || raw_socket_ <= 0) {
spdlog::error("failed to reconnect {}:{}", host, port);
bConnected = false;
}
else {
spdlog::info("successfully connected to {}:{}", host, port);
bConnected = true;
//LibXmMaQue_OSD_showTime(0, MAQUE_STREAM_CHN_MAIN, &stOsdTimeModParamMain.stOsdParam);
//freshConnect = pvArg->dataq->size();
/// pop all frames
}
}
continue;
}
zmq_msg_init(&msg);
ret = zmq_msg_recv(&msg, pSub, 0);
if(ret < 0) {
spdlog::error("failed to recv zmq msg: {}", zmq_strerror(ret));
continue;
}
ssize_t sz = zmq_msg_size(&msg);
char *ptr = (char*)zmq_msg_data(&msg);
ssize_t sent = 0;
ssize_t totalSent = 0;
while (sz > 0) {
ptr += sent;
auto tosent = sz > NUM_MAX_PACKET_BYTES? NUM_MAX_PACKET_BYTES:sz;
sent = ::send(raw_socket_, ptr, tosent, (tosent > sz? MSG_MORE:0)|MSG_NOSIGNAL);
if (sent <= 0) {
break;
}
sz -= sent;
totalSent += sent;
}
if (sent <= 0) {
spdlog::error("failed to send, peer down: {}", sz);
/// TODO: reconnection
bConnected = false;
bNeedRetry = true;
}
zmq_msg_close(&msg);
}
zmq_close (pSub);
}
using namespace chrono_literals;
void record_video_entry(void *args)
{
int ret = 0;
// this thread will never exit
while(1) {
if(!is_sdcard_avail()) {
spdlog::error("no sd card valial. can't record local videos");
this_thread::sleep_for(10s);
continue;
}
// setup sub
void *pSub = zmq_socket(pPubCtx, ZMQ_SUB);
// ret = 1;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE, &ret, sizeof (ret));
// ret = 2;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_IDLE, &ret, sizeof (ret));
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
ret = zmq_connect(pSub, strPubUrl.c_str());
if(ret != 0) {
spdlog::error("failed connect frame pub: {}", strPubUrl);
exit(1);
}
ret = zmq_setsockopt(pSub, ZMQ_SUBSCRIBE, "", 0);
spdlog::info("zmq sub successed (record)");
zmq_msg_t msg;
long long preTs = 0;
long long nowTs = 0;
uint32_t cnt = 0;
fstream * fp = nullptr;
while (1) {
zmq_msg_init(&msg);
ret = zmq_msg_recv(&msg, pSub, 0);
if(ret < 0) {
spdlog::error("failed to recv zmq msg: {}", zmq_strerror(ret));
continue;
}
ssize_t sz = zmq_msg_size(&msg);
char *ptr = (char*)zmq_msg_data(&msg);
/// we calc timestamps about every 2s
if(cnt % 30 == 0) {
nowTs = chrono::system_clock::now().time_since_epoch().count()/1000;
}
if(nowTs - preTs > gConfigRecord.module.record.interval*1000) {
if(fp != nullptr) {
if(fp->is_open())
fp->close();
delete fp;
}
fp = new fstream(recFilePath+to_string(nowTs), std::ios::out|std::ios::binary);
if(!fp->is_open()) {
spdlog::error("failed to open record file");
delete fp;
fp = nullptr;
}
preTs = nowTs;
}
if(fp != nullptr) {
fp->write(ptr, sz);
}
cnt++;
zmq_msg_close(&msg);
}
zmq_close (pSub);
/// DO NOT! do this
//zmq_ctx_destroy(pPubCtx);
}
}
/// verify request message
string verify_request(json &js)
{
string ret;
string check[] = {kMsgCmd,kMsgData, kMsgRid, kMsgTime, kMsgRid};
for(auto &k: check) {
if(!js.contains(k)) {
ret += k + ",";
}
}
if(!ret.empty()) {
ret = "missing fields in request: " + ret;
}
return ret;
}
/// low level api for report and response
void _report_response(string topic, string message)
{
try {
auto client = MqttMgr::get_instance(mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("failed to create mqtt instance to {}", mqtt_url);
return;
}
auto rc = (*client)[topic].pub(message.c_str(), message.size(), 2, false);
if(rc < 0) {
spdlog::error("failed to pub mqtt message on {}: {}", topic, message);
}
else {
spdlog::info("successfully pub message at {}:{}", topic, message);
}
}
catch(exception &e) {
spdlog::error("failed to send message: {}, {}, {}:{}", e.what(), message, __FILE__, __LINE__);
}
}
/// having data field along with code and msg
/// signature {"rid", "code", "msg", "cata", "data":{}}
void report_response_json(string topic, json& message)
{
// time
auto ts = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
message.insert_or_assign(kMsgTime, ts);
// rid
auto rid = message["rid"].as<string>();
message.insert_or_assign(kMsgRid, rid);
// type
if(rid.empty()) {
message.insert_or_assign(kMsgType, kMsgTypeReport);
}
else {
message.insert_or_assign(kMsgType, kMsgTypeResponse);
}
// sn
message.insert_or_assign(kMsgSn, string(dev_sn));
_report_response(topic, message.to_string());
}
/// no data field. only code and msg field
void report_response_args(string topic, int code, string message, string cata, string rid, json &&data)
{
json js;
/// current timestamp
auto ts = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
js.insert_or_assign(kMsgTime, ts);
/// rid
js.insert_or_assign(kMsgRid, rid);
/// type
if(rid.empty()) {
js.insert_or_assign(kMsgType, kMsgTypeReport);
}
else {
js.insert_or_assign(kMsgType, kMsgTypeResponse);
}
js.insert_or_assign(kMsgCata, cata);
///
js.insert_or_assign(kMsgCode, code);
js.insert_or_assign(kMsgMsg, message);
js.insert_or_assign(kMsgSn, string(dev_sn));
_report_response(topic, js.to_string());
}
int apply_config(json &js)
{
auto hasSD = is_sdcard_avail();
/// verify parameters
string str, strWarn;
if(!js.contains(kMsgData)) {
str = fmt::format("no data object in config: {}", js.to_string());
}
else {
json data = js[kMsgData];
string veri[] = {kMsgConfigVgw, kMsgConfigMqtt, kMsgConfigUpload};
for(auto &k:veri) {
auto value = msg_field(data, k);
if(value.empty()) {
str = fmt::format("no {} field in data: {}", k, js.to_string());
break;
}
else {
// parse host and port
auto uri = httplib::Uri::Parse(value);
if(uri.Host.empty()|| uri.Port.empty()) {
str = fmt::format("host addr invalid {}:{}, {}", k, value, js.to_string());
break;
}
// check network connections
int socket = 0;
auto rc = raw_connect(uri.Host, uri.Port, &socket);
if(rc < 0 || socket == 0) {
str = fmt::format("failed to connect to {}:{}, {}", k, value, js.to_string());
break;
}
else {
// we must close the test socket
::close(socket);
}
}
}
// check features field
json features = data.get_value_or<json>(kMsgConfigFeatures, json());
string feats[]= {kMsgConfigMotion, kMsgConfigRecord, kMsgConfigAi};
for(auto &k:feats) {
auto f = features.get_value_or<json>(k, json());
if(f.size() == 0) {
strWarn = fmt::format("{}, ", k);
}
if(!strWarn.empty()) {
strWarn = "no below feature in config, default configuration is applied: " + strWarn;
}
}
}
if(!str.empty()) {
report_response_args(pub_topic, EV_MSG_ERROR_CONTENT_SYNTAX, str, kMsgCmdConfig, msg_field(js, kMsgRid), json());
spdlog::error(str);
return -EV_MSG_ERROR_CONTENT_SYNTAX;
}
// try to apply
}
/// handles all incoming request from cloud services
void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string topic)
{
char msg[len];
memcpy(msg, data, len);
try {
auto js = json::parse(msg);
spdlog::info("received request on {}:\n{}", topic, (char*)msg);
auto str = verify_request(js);
if(!str.empty()) {
spdlog::warn(str);
report_response_args(pub_topic, EV_MSG_ERROR_CONTENT_SYNTAX, str, js.contains(kMsgCmd)?js[kMsgCmd].as<string>():string(""), js.contains(kMsgRid)?js[kMsgRid].as<string>():string(""), json());
return;
}
/// dispatch msg
auto cmd = js[kMsgCmd].as<string>();
if(cmd == kMsgCmdConfig) {
// apply config
}
else if(cmd == kMsgCmdUploadVideo) {
// upload video
}
else if(cmd == kMsgCmdGetConfig) {
// response config
}
else if(cmd == kMsgCmdStatus) {
// response status
}
else if(cmd == kMsgCmdPtz) {
// response Ptz control
}
else {
auto str = fmt::format("unsupported cmd {}: {}", cmd, js.to_string());
spdlog::warn(str);
report_response_args(pub_topic, EV_MSG_ERROR_UNSUPPORTED_CMD, str, cmd, msg_field(js, kMsgRid), json());
}
}
catch(exception &e) {
auto str = fmt::format("exception {}: {}, {}:{}", e.what(), msg, __FILE__, __LINE__);
spdlog::error(str);
report_response_args(pub_topic, EV_MSG_ERROR_EXCEPTION, str, string(""), string(""), json());
}
//(*hlp)[to_string(stoi(topic) + 1)]("hello ack2", 11);
}
///
void start_mqtt(void *arg)
{
auto client = MqttMgr::get_instance(mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("failed to create mqtt instance to {}", mqtt_url);
return;
}
client->subscribe(sub_topic + dev_sn, handle_mqtt_req, false);
}
///
int main(int argc, char *argv[])
{
int ret = XM_SUCCESS;
spdlog::info("test on hi3518ev300, {}", is_big_endian());
get_mac_addr(dev_sn);
// get env for host and port
auto host_ = getenv("VGW_HOST");
enablePush = true;
if(host_) {
host = host_;
}
else {
enablePush = false;
}
auto port_ = getenv("VGW_PORT");
if(port_) {
port_ = port;
}
else {
enablePush = false;
}
/// TODO: check sd availability
bool bSDAvail = is_sdcard_avail();
auto logLevel_ = getenv("LOG_LEVEL");
if(logLevel_) {
logLevel = spdlog::level::from_str(logLevel_);
spdlog::set_level(logLevel);
}
else {
spdlog::info("log level configured to info");
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;
}
}
MaQue_Demo_Mem_Init();
/// thread of utp time
auto thNtp = thread([] {
bool bGotTime = false;
time_t stm;
while(!bGotTime)
{
if(getNtpTime(&stm) >=0) {
spdlog::info("ntp got time");
::stime(&stm);
bGotTime = true;
}
else {
spdlog::warn("failed to get ntp time");
}
this_thread::sleep_for(chrono::seconds(20));
}
});
if(thNtp.joinable()) {
thNtp.detach();
}
MaQueStartParam_s startParam = {MAQUE_VIDEO_STANDARD_PAL, {MAQUE_VIDEO_COMPRESS_H264, MAQUE_VIDEO_COMPRESS_H265}, "/mnt/sd/Config/"};
ret = LibXmMaQue_System_startUp(&startParam);
spdlog::info("ret: {}", ret);
//
MaQueMemoryApi_s memApi = {MaQue_Demo_Mem_alloc, MaQue_Demo_Mem_release, MaQue_Demo_Mem_addRef, MaQue_Demo_Mem_setLength};
ret = LibXmMaQue_Mem_init(&memApi);
spdlog::info("mem ret: {}", ret);
MaQueSystemTime_s tm;
LibXmMaQue_Time_getCurrentTime(&tm);
LibXmMaQue_Time_print(&tm, nullptr);
//
MaQueCodeAbilities_s capb;
ret = LibXmMaQue_VideoEnc_getAbilities(0, &capb);
spdlog::info("abt ret: {}", ret);
if (XM_SUCCESS == ret) {
spdlog::info(fmt::format("capbilities: supported codecs {0:#b},"
"max fhd/s {0:d}, max res: {0:d}, per ch mres: {0:d}, {0:d}, {0:d}, {0:d}",
int(capb.videoEncTypeMask),
int(capb.maxEncPowerX1080P), int(capb.eDecImageSizeMax), int(capb.astVidEncChnAbility[0].eCapSizeMax),
int(capb.astVidEncChnAbility[1].eCapSizeMax), int(capb.astVidEncChnAbility[2].eCapSizeMax), int(capb.astVidEncChnAbility[3].eCapSizeMax)));
}
//
MaQueVideoEncodeCfg_s cfg;
init_stream_cfg(&cfg);
cfg.nFps = 15;
cfg.eIFrmIntvType = IFRAME_INTV_TYPE_TIME;
cfg.nIFrameInterval = 10;
cfg.eImageQuality = (MaQueImageQuality_e)videoQuality;
cfg.eVidComp = MAQUE_VIDEO_COMPRESS_H264;
cfg.nBitRate = 200 * 8; // 200KB
ret = configure_stream((MaQueStreamChannel_e)0, &cfg);
spdlog::info("cfg stream ret: {}", ret);
///
/// record setting
ret = LibXmMaQue_VideoEnc_startStream(0, (MaQueStreamChannel_e)0, cb_frame_proc, (void *)&args);
if (XM_SUCCESS == ret) {
spdlog::info("created record task successfully");
}
signal(SIGINT, clean_up);
// signal(SIGTERM, clean_up);
signal(SIGKILL, clean_up);
signal(SIGPIPE, SIG_IGN);
// setup zmq
pPubCtx = zmq_ctx_new();
pPub = zmq_socket(pPubCtx, ZMQ_PUB);
ret = zmq_bind(pPub, strPubUrl.c_str());
if(ret < 0) {
spdlog::error("{} failed to create zmq pub topic: {}", strPubUrl);
exit(1);
}
//
if (enablePush=true) {
ret = raw_connect(host, port, &raw_socket_);
if (ret < 0 || raw_socket_ <= 0) {
spdlog::error("failed connect to video gateway at {}:{}", host, port);
bConnected = false;
}
else {
bConnected = true;
spdlog::info("successfully connected to video gateway at {}:{}", host, port);
}
thread thPush = thread(frame_send_entry, &args);
spdlog::info("sizeof pkt header {}, sizeof tv {}", sizeof(evpacket_t), sizeof(timeval));
thPush.detach();
}
else {
spdlog::warn("push video is not enabled in environ settings");
}
start_md_bd(&args);
/// subscribe to mqtt
start_mqtt(nullptr);
thread thSmart = thread(maq_smart_task_entry, &args);
thSmart.join();
}
\ No newline at end of file
#include <iostream>
#include <string>
#include <chrono>
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include "ref-memory.hpp"
#include <maque_system.h>
#include <maque_video_enc.h>
#include <maque_time.h>
#include <evpacket.h>
#include "common.h"
#include "ntp.h"
#include <time.h>
#include "raw_tcp.h"
#include "smart.h"
#include "motion.h"
#include <thread>
#include "mqttmgr.hpp"
#include "utils.hpp"
#include <zmq.h>
using namespace std;
using namespace jsoncons;
using namespace evutils;
///
#define NUM_IFRAME_PICK 2
#define NUM_MAX_QUEQUE_SIZE 60
#define NUM_MAX_PACKET_BYTES 1200
#define INTERVAL_LOG_QFULL 3 // its about 60/15 *3 secs
/// device info
static char dev_sn[TERMINAL_SN_SIZE] = {0};
/// default video gateway info
static int videoQuality = MAQUE_IMG_QUALITY_BETTER;
static uint64_t frameCntTotal = 0;
static uint32_t frameCntIframe = 0;
static uint32_t frameCntPframe = 0;
static uint64_t packetId = 0;
static spdlog::level::level_enum logLevel = spdlog::level::info;
static bool bPFrameAvail = false;
static bool bNeedRetry = false;
static int readFaildCnt = 0;
static bool enablePush = false;
static bool enableRecord = false;
static bool bCanRecord = false;
static bool bConnected = false;
static queue<DataItem> _frameQueue;
static mutex _mutFrame;
static condition_variable _condFrame;
static Notifier _notiFrame = {&_mutFrame, &_condFrame};
static CallBackArg args = {nullptr, &_notiFrame, &_frameQueue};
static int raw_socket_ = 0;
static bool bGotTime = false;
// zmq
static void *pPubCtx = nullptr;
static void *pPub = nullptr;
static const string strPubUrl = "inproc://frame";
/// flags used to stop modules
/// 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 gConfigAI = {0};
static ev_module_config_t gConfigMotion = {0};
enum EV_MSG_ERROR_CODE {
EV_MSG_ERROR_NONE,
EV_MSG_ERROR_EXCEPTION,
EV_MSG_ERROR_UNSUPPORTED_CMD,
EV_MSG_ERROR_CONTENT_SYNTAX,
EV_MSG_ERROR_NUM
};
void clean_up(int sig)
{
if(sig == SIGPIPE) {
// vgw connection error
bConnected = false;
bNeedRetry = true;
return;
}
spdlog::warn("clearn up called, with sig: {}", sig);
if (args.recFD) {
fclose(args.recFD);
}
if (raw_socket_) {
::close(raw_socket_);
}
// 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();
LibXmMaQue_System_destroy();
exit(1);
}
bool is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
MaQueVideoEncodeCfg_s *init_stream_cfg(MaQueVideoEncodeCfg_s *cfg)
{
cfg->eVidComp = (MaQueVideoCompress_e)-1;
cfg->eCapSize = (MaQueCaptureSize_e)-1;
cfg->eBitrateCtrl = (MaQueBitrateCtrl_e)-1;
cfg->eImageQuality = (MaQueImageQuality_e)-1;
cfg->nFps = -1;
cfg->nBitRate = -1;
cfg->nIFrameInterval = -1;
cfg->iQpMin = -1;
cfg->iQpMax = -1;
return cfg;
}
int32_t configure_stream(MaQueStreamChannel_e eStreamChn, MaQueVideoEncodeCfg_s *cfg)
{
int32_t res;
MaQueVideoEncodeCfg_s oldCfg;
if (LibXmMaQue_VideoEnc_getCfg(0, eStreamChn, &oldCfg)) {
spdlog::error("LibXmMaQue_VideoEncode_getCfg error");
return -1;
}
if (cfg->eVidComp != -1) {
oldCfg.eVidComp = cfg->eVidComp;
}
if (cfg->eCapSize != -1) {
oldCfg.eCapSize = cfg->eCapSize;
}
if (cfg->eBitrateCtrl != -1) {
oldCfg.eBitrateCtrl = cfg->eBitrateCtrl;
}
if (cfg->eImageQuality != -1) {
oldCfg.eImageQuality = cfg->eImageQuality;
}
if (cfg->nFps != -1) {
oldCfg.nFps = cfg->nFps;
}
if (cfg->nBitRate != -1) {
oldCfg.nBitRate = cfg->nBitRate;
}
if (cfg->nIFrameInterval != -1) {
oldCfg.nIFrameInterval = cfg->nIFrameInterval;
}
if (cfg->iQpMin > 0 && cfg->iQpMin <= 51 && cfg->iQpMax > 0 && cfg->iQpMax <= 51) {
oldCfg.iQpMin = cfg->iQpMin;
oldCfg.iQpMax = cfg->iQpMax;
oldCfg.bUseUserQp = 1;
}
spdlog::info("main: fps={}", oldCfg.nFps);
res = LibXmMaQue_VideoEnc_setCfg(0, eStreamChn, &oldCfg);
if (!res) {
memcpy(cfg, &oldCfg, sizeof(oldCfg));
}
return res;
}
//
void frame_free(void *data, void*hint)
{
MaQueVideoEncFrameInfo_s *frame = (MaQueVideoEncFrameInfo_s *)hint;
MaQue_Demo_Mem_release(frame->handleMem);
}
//
XM_S32 cb_frame_proc(XM_VOID *pUserArg, MaQueVideoEncFrameInfo_s *frame)
{
MaQueSystemTime_s *pstSysTime = &frame->stTimeStamp.stSysTime;
CallBackArg *args = (CallBackArg *)pUserArg;
struct timeval stTimeVal;
int ret;
frameCntTotal++;
zmq_msg_t msg;
void *hint = NULL;
if(frame->eEncodeType == MAQUE_ENCODE_TYPE_H264 && frame->eFrameType == MAQUE_FRAME_TYPE_VIDEO && frame->nDataLen > 0) {
evpacket_t* pkt = (evpacket_t*)((char*)frame->pData - sizeof(evpacket_t));
pkt->meta.magic[0] = 0xBE;
pkt->meta.magic[1] = 0xEF;
memcpy(pkt->meta.sn, dev_sn, sizeof(dev_sn));
/// NOTES: since both ends use little-endian, we don't use htonl, htons to save clocks
pkt->vpara.frame_type = frame->eSubType + 1;
pkt->vpara.packet_type = frame->eEncodeType;
pkt->vpara.res.width = frame->nWidth;
pkt->vpara.res.height = frame->nHeight;
/// TODO:
pkt->vpara.fps = 15;
//pkt->vpara.ts = frame->stTimeStamp;
pkt->length = frame->nDataLen;
pkt->meta.packet_id = packetId;
pkt->meta.crc = 0;
pkt->meta.crc = crc16((unsigned char*)pkt, sizeof(evpacket_t));
if(frame->eSubType == MAQUE_FRAME_SUBTYPE_I) {
frameCntIframe++;
}
else if(frame->eSubType == MAQUE_FRAME_SUBTYPE_P) {
frameCntPframe++;
}
zmq_msg_init_data(&msg, (char*)pkt, frame->nDataLen + sizeof(evpacket_t), nullptr, nullptr);
zmq_msg_send(&msg, pPub, 0);
packetId++;
}
MaQue_Demo_Mem_release(frame->handleMem);
return XM_SUCCESS;
}
void frame_send_entry(void *args)
{
int ret = 0;
// setup sub
void *pSub = zmq_socket(pPubCtx, ZMQ_SUB);
if(pSub == nullptr) {
spdlog::error("failed to create frame sending socket");
exit(1);
}
// ret = 1;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE, &ret, sizeof (ret));
// ret = 2;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_IDLE, &ret, sizeof (ret));
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
ret = zmq_connect(pSub, strPubUrl.c_str());
if(ret != 0) {
spdlog::error("failed connect frame pub: {}", strPubUrl);
exit(1);
}
ret = zmq_setsockopt(pSub, ZMQ_SUBSCRIBE, "", 0);
spdlog::info("zmq sub successed (for frame sending)");
zmq_msg_t msg;
while (1) {
if(!bConnected||raw_socket_ <= 0) {
if(frameCntTotal % (NUM_MAX_QUEQUE_SIZE * 3) == 0) {
bNeedRetry = true;
if(raw_socket_) {
::close(raw_socket_);
raw_socket_ = 0;
}
}
if(bNeedRetry == true) {
// try reconect
bNeedRetry = false;
spdlog::info("try reconnecting to video gateway {}:{}", host, port);
auto ret = raw_connect(host, port, &raw_socket_);
if (ret < 0 || raw_socket_ <= 0) {
spdlog::error("failed to reconnect {}:{}", host, port);
bConnected = false;
}
else {
spdlog::info("successfully connected to {}:{}", host, port);
bConnected = true;
//LibXmMaQue_OSD_showTime(0, MAQUE_STREAM_CHN_MAIN, &stOsdTimeModParamMain.stOsdParam);
//freshConnect = pvArg->dataq->size();
/// pop all frames
}
}
continue;
}
zmq_msg_init(&msg);
ret = zmq_msg_recv(&msg, pSub, 0);
if(ret < 0) {
spdlog::error("failed to recv zmq msg: {}", zmq_strerror(ret));
continue;
}
ssize_t sz = zmq_msg_size(&msg);
char *ptr = (char*)zmq_msg_data(&msg);
ssize_t sent = 0;
ssize_t totalSent = 0;
while (sz > 0) {
ptr += sent;
auto tosent = sz > NUM_MAX_PACKET_BYTES? NUM_MAX_PACKET_BYTES:sz;
sent = ::send(raw_socket_, ptr, tosent, MSG_NOSIGNAL);
if (sent <= 0) {
break;
}
sz -= sent;
totalSent += sent;
}
if (sent <= 0) {
spdlog::error("failed to send, peer down: {}", sz);
/// TODO: reconnection
bConnected = false;
bNeedRetry = true;
}
zmq_msg_close(&msg);
}
zmq_close (pSub);
}
using namespace chrono_literals;
void record_video_entry(void *args)
{
int ret = 0;
// this thread will never exit
while(1) {
if(!is_sdcard_avail()) {
spdlog::error("no sd card valial. can't record local videos");
this_thread::sleep_for(10s);
continue;
}
// load existing videos
// setup sub
void *pSub = zmq_socket(pPubCtx, ZMQ_SUB);
// ret = 1;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE, &ret, sizeof (ret));
// ret = 2;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_IDLE, &ret, sizeof (ret));
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
ret = zmq_connect(pSub, strPubUrl.c_str());
if(ret != 0) {
spdlog::error("failed connect frame pub: {}", strPubUrl);
exit(1);
}
ret = zmq_setsockopt(pSub, ZMQ_SUBSCRIBE, "", 0);
spdlog::info("zmq sub successed (record)");
zmq_msg_t msg;
long long preTs = 0;
long long nowTs = 0;
uint32_t cnt = 0;
fstream * fp = nullptr;
while (1) {
zmq_msg_init(&msg);
ret = zmq_msg_recv(&msg, pSub, 0);
if(ret < 0) {
spdlog::error("failed to recv zmq msg: {}", zmq_strerror(ret));
continue;
}
ssize_t sz = zmq_msg_size(&msg);
char *ptr = (char*)zmq_msg_data(&msg);
/// we calc timestamps about every 2s
if(cnt % 30 == 0) {
nowTs = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
}
if(nowTs - preTs > gConfigRecord.module.record.interval*1000) {
if(fp != nullptr) {
if(fp->is_open())
fp->close();
delete fp;
}
fp = new fstream(recFilePath+to_string(nowTs), std::ios::out|std::ios::binary);
if(!fp->is_open()) {
spdlog::error("failed to open record file");
delete fp;
fp = nullptr;
}
preTs = nowTs;
}
if(fp != nullptr) {
fp->write(ptr, sz);
}
cnt++;
zmq_msg_close(&msg);
}
zmq_close (pSub);
/// DO NOT! do this
//zmq_ctx_destroy(pPubCtx);
}
}
/// verify request message
string verify_request(json &js)
{
string ret;
string check[] = {kMsgCmd,kMsgData, kMsgRid, kMsgTime, kMsgRid};
for(auto &k: check) {
if(!js.contains(k)) {
ret += k + ",";
}
}
if(!ret.empty()) {
ret = "missing fields in request: " + ret;
}
return ret;
}
/// low level api for report and response
void _report_response(string topic, string message)
{
auto client = MqttMgr::get_instance(mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("failed to create mqtt instance to {}", mqtt_url);
return;
}
auto rc = (*client)[topic].pub(message.c_str(), message.size(), 2, false);
if(rc < 0) {
spdlog::error("failed to pub mqtt message on {}: {}", topic, message);
}
else {
spdlog::info("successfully pub message at {}:{}", topic, message);
}
}
/// having data field along with code and msg
/// signature {"rid", "code", "msg", "cata", "data":{}}
void report_response_json(string topic, json& message)
{
// time
auto ts = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
message.insert_or_assign(kMsgTime, ts);
// rid
auto rid = message["rid"].as<string>();
message.insert_or_assign(kMsgRid, rid);
// type
if(rid.empty()) {
message.insert_or_assign(kMsgType, kMsgTypeReport);
}
else {
message.insert_or_assign(kMsgType, kMsgTypeResponse);
}
// sn
message.insert_or_assign(kMsgSn, string(dev_sn));
_report_response(topic, message.to_string());
}
/// no data field. only code and msg field
void report_response_args(string topic, int code, string message, string cata, string rid, json &&data)
{
json js;
/// current timestamp
auto ts = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
js.insert_or_assign(kMsgTime, ts);
/// rid
js.insert_or_assign(kMsgRid, rid);
/// type
if(rid.empty()) {
js.insert_or_assign(kMsgType, kMsgTypeReport);
}
else {
js.insert_or_assign(kMsgType, kMsgTypeResponse);
}
js.insert_or_assign(kMsgCata, cata);
///
js.insert_or_assign(kMsgCode, code);
js.insert_or_assign(kMsgMsg, message);
js.insert_or_assign(kMsgSn, string(dev_sn));
_report_response(topic, js.to_string());
}
int apply_config(json &js)
{
auto hasSD = is_sdcard_avail();
/// verify parameters
string str, strWarn;
if(!js.contains(kMsgData)) {
str = fmt::format("no data object in config: {}", js.to_string());
}
else {
json data = js[kMsgData];
string veri[] = {kMsgConfigVgw, kMsgConfigMqtt, kMsgConfigUpload};
for(auto &k:veri) {
auto value = msg_field(data, k);
if(value.empty()) {
str = fmt::format("no {} field in data: {}", k, js.to_string());
break;
}
else {
// parse host and port
auto uri = httplib::Uri::Parse(value);
if(uri.Host.empty()|| uri.Port.empty()) {
str = fmt::format("host addr invalid {}:{}, {}", k, value, js.to_string());
break;
}
// check network connections
int socket = 0;
auto rc = raw_connect(uri.Host, uri.Port, &socket);
if(rc < 0 || socket == 0) {
str = fmt::format("failed to connect to {}:{}, {}", k, value, js.to_string());
break;
}
else {
// we must close the test socket
::close(socket);
}
}
}
// check features field
json features = data.get_value_or<json>(kMsgConfigFeatures, json());
string feats[]= {kMsgConfigMotion, kMsgConfigRecord, kMsgConfigAi};
for(auto &k:feats) {
auto f = features.get_value_or<json>(k, json());
if(f.size() == 0) {
strWarn = fmt::format("{}, ", k);
}
if(!strWarn.empty()) {
strWarn = "no below feature in config, default configuration is applied: " + strWarn;
}
}
}
if(!str.empty()) {
report_response_args(pub_topic, EV_MSG_ERROR_CONTENT_SYNTAX, str, kMsgCmdConfig, msg_field(js, kMsgRid), json());
spdlog::error(str);
return -EV_MSG_ERROR_CONTENT_SYNTAX;
}
// try to apply
}
/// handles all incoming request from cloud services
void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string topic)
{
char msg[len];
memcpy(msg, data, len);
try {
auto js = json::parse(msg);
spdlog::info("received request on {}:\n{}", topic, (char*)msg);
auto str = verify_request(js);
if(!str.empty()) {
spdlog::warn(str);
report_response_args(pub_topic, EV_MSG_ERROR_CONTENT_SYNTAX, str, js.contains(kMsgCmd)?js[kMsgCmd].as<string>():string(""), js.contains(kMsgRid)?js[kMsgRid].as<string>():string(""), json());
return;
}
/// dispatch msg
auto cmd = js[kMsgCmd].as<string>();
if(cmd == kMsgCmdConfig) {
// apply config
}
else if(cmd == kMsgCmdUploadVideo) {
// upload video
}
else if(cmd == kMsgCmdGetConfig) {
// response config
}
else if(cmd == kMsgCmdStatus) {
// response status
}
else if(cmd == kMsgCmdPtz) {
// response Ptz control
}
else {
auto str = fmt::format("unsupported cmd {}: {}", cmd, js.to_string());
spdlog::warn(str);
report_response_args(pub_topic, EV_MSG_ERROR_UNSUPPORTED_CMD, str, cmd, msg_field(js, kMsgRid), json());
}
}
catch(exception &e) {
auto str = string("exception: ") + e.what() + string(", ") + msg;
spdlog::error(str);
report_response_args(pub_topic, EV_MSG_ERROR_EXCEPTION, str, string(""), string(""), json());
}
//(*hlp)[to_string(stoi(topic) + 1)]("hello ack2", 11);
}
///
void start_mqtt(void *arg)
{
auto client = MqttMgr::get_instance(mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("failed to create mqtt instance to {}", mqtt_url);
return;
}
client->subscribe(sub_topic + dev_sn, handle_mqtt_req, false);
}
///
int main(int argc, char *argv[])
{
int ret = XM_SUCCESS;
spdlog::info("test on hi3518ev300, {}", is_big_endian());
get_mac_addr(dev_sn);
// get env for host and port
auto host_ = getenv("VGW_HOST");
enablePush = true;
if(host_) {
host = host_;
}
else {
enablePush = false;
}
auto port_ = getenv("VGW_PORT");
if(port_) {
port_ = port;
}
else {
enablePush = false;
}
/// TODO: check sd availability
bool bSDAvail = is_sdcard_avail();
auto logLevel_ = getenv("LOG_LEVEL");
if(logLevel_) {
logLevel = spdlog::level::from_str(logLevel_);
spdlog::set_level(logLevel);
}
else {
spdlog::info("log level configured to info");
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;
}
}
MaQue_Demo_Mem_Init();
/// thread of utp time
auto thNtp = thread([] {
bool bGotTime = false;
time_t stm;
while(!bGotTime)
{
if(getNtpTime(&stm) >=0) {
spdlog::info("ntp got time");
::stime(&stm);
bGotTime = true;
}
else {
spdlog::warn("failed to get ntp time");
}
this_thread::sleep_for(chrono::seconds(20));
}
});
if(thNtp.joinable()) {
thNtp.detach();
}
MaQueStartParam_s startParam = {MAQUE_VIDEO_STANDARD_PAL, {MAQUE_VIDEO_COMPRESS_H264, MAQUE_VIDEO_COMPRESS_H265}, "/mnt/sd/Config/"};
ret = LibXmMaQue_System_startUp(&startParam);
spdlog::info("ret: {}", ret);
//
MaQueMemoryApi_s memApi = {MaQue_Demo_Mem_alloc, MaQue_Demo_Mem_release, MaQue_Demo_Mem_addRef, MaQue_Demo_Mem_setLength};
ret = LibXmMaQue_Mem_init(&memApi);
spdlog::info("mem ret: {}", ret);
MaQueSystemTime_s tm;
LibXmMaQue_Time_getCurrentTime(&tm);
LibXmMaQue_Time_print(&tm, nullptr);
//
MaQueCodeAbilities_s capb;
ret = LibXmMaQue_VideoEnc_getAbilities(0, &capb);
spdlog::info("abt ret: {}", ret);
if (XM_SUCCESS == ret) {
spdlog::info(fmt::format("capbilities: supported codecs {0:#b},"
"max fhd/s {0:d}, max res: {0:d}, per ch mres: {0:d}, {0:d}, {0:d}, {0:d}",
int(capb.videoEncTypeMask),
int(capb.maxEncPowerX1080P), int(capb.eDecImageSizeMax), int(capb.astVidEncChnAbility[0].eCapSizeMax),
int(capb.astVidEncChnAbility[1].eCapSizeMax), int(capb.astVidEncChnAbility[2].eCapSizeMax), int(capb.astVidEncChnAbility[3].eCapSizeMax)));
}
//
MaQueVideoEncodeCfg_s cfg;
init_stream_cfg(&cfg);
cfg.nFps = 15;
cfg.eIFrmIntvType = IFRAME_INTV_TYPE_TIME;
cfg.nIFrameInterval = 10;
cfg.eImageQuality = (MaQueImageQuality_e)videoQuality;
cfg.eVidComp = MAQUE_VIDEO_COMPRESS_H264;
cfg.eBitrateCtrl = MAQUE_BITRATE_CTRL_VBR;
cfg.nBitRate = 1024; // 1Mbps
ret = configure_stream((MaQueStreamChannel_e)0, &cfg);
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);
if (XM_SUCCESS == ret) {
spdlog::info("created record task successfully");
}
signal(SIGINT, clean_up);
// signal(SIGTERM, clean_up);
signal(SIGKILL, clean_up);
signal(SIGPIPE, SIG_IGN);
// setup zmq
pPubCtx = zmq_ctx_new();
pPub = zmq_socket(pPubCtx, ZMQ_PUB);
ret = zmq_bind(pPub, strPubUrl.c_str());
if(ret < 0) {
spdlog::error("{} failed to create zmq pub topic: {}", strPubUrl);
exit(1);
}
//
if (enablePush=true) {
ret = raw_connect(host, port, &raw_socket_);
if (ret < 0 || raw_socket_ <= 0) {
spdlog::error("failed connect to video gateway at {}:{}", host, port);
bConnected = false;
}
else {
bConnected = true;
spdlog::info("successfully connected to video gateway at {}:{}", host, port);
}
thread thPush = thread(frame_send_entry, &args);
spdlog::info("sizeof pkt header {}, sizeof tv {}", sizeof(evpacket_t), sizeof(timeval));
thPush.detach();
}
else {
spdlog::warn("push video is not enabled in environ settings");
}
thread thVideoRecord = thread(record_video_entry, nullptr);
if(thVideoRecord.joinable()) {
thVideoRecord.detach();
}
start_md_bd(&args);
/// subscribe to mqtt
start_mqtt(nullptr);
thread thSmart = thread(maq_smart_task_entry, &args);
thSmart.join();
}
\ No newline at end of file
#include <iostream>
#include <string>
#include <chrono>
#include <fmt/format.h>
#include <spdlog/spdlog.h>
#include "ref-memory.hpp"
#include <maque_system.h>
#include <maque_video_enc.h>
#include <maque_time.h>
#include <evpacket.h>
#include "common.h"
#include "ntp.h"
#include <time.h>
#include "raw_tcp.h"
#include "smart.h"
#include "motion.h"
#include <thread>
#include "mqttmgr.hpp"
#include "utils.hpp"
#include <zmq.h>
using namespace std;
using namespace jsoncons;
using namespace evutils;
///
#define NUM_IFRAME_PICK 2
#define NUM_MAX_QUEQUE_SIZE 60
#define NUM_MAX_PACKET_BYTES 1200
#define INTERVAL_LOG_QFULL 3 // its about 60/15 *3 secs
/// device info
static char dev_sn[13] = {0};
/// default video gateway info
static int videoQuality = MAQUE_IMG_QUALITY_BETTER;
static uint32_t frameCntTotal = 0;
static uint32_t frameCntIframe = 0;
static uint32_t frameCntPframe = 0;
static uint64_t packetId = 0;
static spdlog::level::level_enum logLevel = spdlog::level::info;
static bool bPFrameAvail = false;
static bool bNeedRetry = false;
static int readFaildCnt = 0;
static bool enablePush = false;
static bool enableRecord = false;
static bool bCanRecord = false;
static bool bConnected = false;
static queue<DataItem> _frameQueue;
static mutex _mutFrame;
static condition_variable _condFrame;
static Notifier _notiFrame = {&_mutFrame, &_condFrame};
static CallBackArg args = {nullptr, &_notiFrame, &_frameQueue};
static int raw_socket_ = 0;
static bool bGotTime = false;
// zmq
static void *pPubCtx = nullptr;
static void *pPub = nullptr;
static const string strPubUrl = "inproc://frame";
enum EV_MSG_ERROR_CODE {
EV_MSG_ERROR_NONE,
EV_MSG_ERROR_EXCEPTION,
EV_MSG_ERROR_UNSUPPORTED_CMD,
EV_MSG_ERROR_CONTENT_SYNTAX,
EV_MSG_ERROR_NUM
};
void clean_up(int sig)
{
spdlog::warn("clearn up called, with sig: {}", sig);
if (args.recFD) {
fclose(args.recFD);
}
if (raw_socket_) {
::close(raw_socket_);
}
// 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();
LibXmMaQue_System_destroy();
exit(1);
}
bool is_big_endian(void)
{
union {
uint32_t i;
char c[4];
} bint = {0x01020304};
return bint.c[0] == 1;
}
MaQueVideoEncodeCfg_s *init_stream_cfg(MaQueVideoEncodeCfg_s *cfg)
{
cfg->eVidComp = (MaQueVideoCompress_e)-1;
cfg->eCapSize = (MaQueCaptureSize_e)-1;
cfg->eBitrateCtrl = (MaQueBitrateCtrl_e)-1;
cfg->eImageQuality = (MaQueImageQuality_e)-1;
cfg->nFps = -1;
cfg->nBitRate = -1;
cfg->nIFrameInterval = -1;
cfg->iQpMin = -1;
cfg->iQpMax = -1;
return cfg;
}
int32_t configure_stream(MaQueStreamChannel_e eStreamChn, MaQueVideoEncodeCfg_s *cfg)
{
int32_t res;
MaQueVideoEncodeCfg_s oldCfg;
if (LibXmMaQue_VideoEnc_getCfg(0, eStreamChn, &oldCfg)) {
spdlog::error("LibXmMaQue_VideoEncode_getCfg error");
return -1;
}
if (cfg->eVidComp != -1) {
oldCfg.eVidComp = cfg->eVidComp;
}
if (cfg->eCapSize != -1) {
oldCfg.eCapSize = cfg->eCapSize;
}
if (cfg->eBitrateCtrl != -1) {
oldCfg.eBitrateCtrl = cfg->eBitrateCtrl;
}
if (cfg->eImageQuality != -1) {
oldCfg.eImageQuality = cfg->eImageQuality;
}
if (cfg->nFps != -1) {
oldCfg.nFps = cfg->nFps;
}
if (cfg->nBitRate != -1) {
oldCfg.nBitRate = cfg->nBitRate;
}
if (cfg->nIFrameInterval != -1) {
oldCfg.nIFrameInterval = cfg->nIFrameInterval;
}
if (cfg->iQpMin > 0 && cfg->iQpMin <= 51 && cfg->iQpMax > 0 && cfg->iQpMax <= 51) {
oldCfg.iQpMin = cfg->iQpMin;
oldCfg.iQpMax = cfg->iQpMax;
oldCfg.bUseUserQp = 1;
}
spdlog::info("main: fps={}", oldCfg.nFps);
res = LibXmMaQue_VideoEnc_setCfg(0, eStreamChn, &oldCfg);
if (!res) {
memcpy(cfg, &oldCfg, sizeof(oldCfg));
}
return res;
}
//
XM_S32 cb_frame_proc(XM_VOID *pUserArg, MaQueVideoEncFrameInfo_s *frame)
{
MaQueSystemTime_s *pstSysTime = &frame->stTimeStamp.stSysTime;
CallBackArg *args = (CallBackArg *)pUserArg;
struct timeval stTimeVal;
int ret;
frameCntTotal++;
if(logLevel <= spdlog::level::debug) {
printf("\n\nnew frame: %u, p0 h:%08X\n", frameCntTotal, (int)frame->handleMem);
}
if (bCanRecord) {
auto nWrite = fwrite(frame->pData, 1, frame->nDataLen, args->recFD);
if(frame->nDataLen != nWrite) {
spdlog::error("failed to write file");
if(args->recFD) {
fclose(args->recFD);
}
}
}
if(enablePush && bConnected == false && frameCntTotal % (NUM_MAX_QUEQUE_SIZE * 3) == 0) {
bNeedRetry = true;
bConnected = false;
if(raw_socket_) {
::close(raw_socket_);
raw_socket_ = 0;
}
}
//printf("\n==== prepare frame with enablePush:%d, dataQ: %08hhX, frametype: %d\n", enablePush, (int)args->dataq, frame->eEncodeType);
if (enablePush && args->dataq && frame->eEncodeType == MAQUE_ENCODE_TYPE_H264) {
unique_lock<mutex> lock(*args->noti->mut);
// check delayed packets for about 12s. sufficiently high
if (args->dataq->size() >= NUM_MAX_QUEQUE_SIZE) {
spdlog::warn("dataq full");
volatile MaQueVideoEncFrameInfo_s * p1 = (MaQueVideoEncFrameInfo_s *)args->dataq->front().ud;
if(logLevel == spdlog::level::debug) {
printf("%u, p1 h:%08X\n", frameCntTotal, (int)p1->handleMem);
}
args->dataq->pop();
if(p1 == frame) {
spdlog::error("BUG!!! new frame address same as what in Queue");
}
MaQue_Demo_Mem_release(p1->handleMem);
p1 = nullptr;
}
if (frame->pData && frame->nDataLen > 0) {
timeval tv;
//::gettimeofday(&tv,NULL);
DataItem dt = {(char *)frame->pData - sizeof(evpacket_t), frame->nDataLen + sizeof(evpacket_t), (void *)frame};
evpacket_t *pkt = (evpacket_t *)dt.buf;
memset(pkt, 0, sizeof(*pkt));
pkt->meta.magic[0] = 0xBE;
pkt->meta.magic[1] = 0xEF;
memcpy(pkt->meta.sn, dev_sn, sizeof(dev_sn));
/// NOTES: since both ends use little-endian, we don't use htonl, htons to save clocks
pkt->vpara.frame_type = frame->eSubType + 1;
pkt->vpara.packet_type = frame->eEncodeType;
pkt->vpara.res.width = frame->nWidth;
pkt->vpara.res.height = frame->nHeight;
/// TODO:
pkt->vpara.fps = 15;
//pkt->vpara.ts = frame->stTimeStamp;
pkt->length = frame->nDataLen;
pkt->meta.packet_id = packetId;
pkt->meta.crc = 0;
pkt->meta.crc = crc16((unsigned char*)pkt, sizeof(evpacket_t));
if (frame->eSubType == MAQUE_FRAME_SUBTYPE_I) {
frameCntIframe++;
}
else if(frame->eSubType == MAQUE_FRAME_SUBTYPE_P) {
frameCntPframe++;
}
MaQue_Demo_Mem_addRef(frame->handleMem);
args->dataq->push(dt);
bPFrameAvail = true;
spdlog::debug("frame meter ic:{}, pc:{}, tc:{}, len:{}, cid:{}", frameCntIframe, frameCntPframe,frameCntTotal, frame->nDataLen, packetId);
packetId++;
}
else {
spdlog::error("invalid frame");
}
lock.unlock();
args->noti->cond->notify_one();
}
else {
//spdlog::warn("pusher is disabled {}:{}", __FILE__, __LINE__);
}
if(logLevel <= spdlog::level::debug) {
printf("%u, p2 h:%08X\n", frameCntTotal, (int)frame->handleMem);
}
MaQue_Demo_Mem_release(frame->handleMem);
return XM_SUCCESS;
}
void frame_send_entry(void *args)
{
CallBackArg *pvArg = (CallBackArg *)args;
int freshConnect = 0;
DataItem elem = {0};
while (1) {
MaQueVideoEncFrameInfo_s *pMem;
{
unique_lock<mutex> lk(*pvArg->noti->mut);
pvArg->noti->cond->wait(lk, [pvArg] { return !pvArg->dataq->empty();});
if (pvArg->dataq->empty()) {
spdlog::warn("dataq empty");
continue;
}
// /// remove old frames
// lk.unlock();
// while(pvArg->dataq->size() > 2) {
// lk.lock();
// elem = pvArg->dataq->front();
// pvArg->dataq->pop();
// lk.unlock();
// pMem = (MaQueVideoEncFrameInfo_s *)elem.ud;
// MaQue_Demo_Mem_release(pMem->handleMem);
// }
// lk.lock();
elem = pvArg->dataq->front();
pvArg->dataq->pop();
lk.unlock();
pMem = (MaQueVideoEncFrameInfo_s *)elem.ud;
}
if(bConnected == false) {
if(bNeedRetry == true) {
// try reconect
bNeedRetry = false;
if(raw_socket_ != 0) {
::close(raw_socket_);
raw_socket_ = 0;
}
spdlog::info("try reconnecting to video gateway {}:{}", host, port);
auto ret = raw_connect(host, port, &raw_socket_);
if (ret < 0 || raw_socket_ <= 0) {
spdlog::error("failed to reconnect {}:{}", host, port);
bConnected = false;
}
else {
spdlog::info("successfully connected to {}:{}", host, port);
bConnected = true;
//LibXmMaQue_OSD_showTime(0, MAQUE_STREAM_CHN_MAIN, &stOsdTimeModParamMain.stOsdParam);
//freshConnect = pvArg->dataq->size();
/// pop all frames
}
}
}
if (bConnected == false || !pMem||elem.size <= 0 || elem.buf == nullptr || (elem.buf + sizeof(evpacket_t)) == nullptr) {
//printf("invalid frame. addr: %08X, len:%u\n", (uint32_t)elem.buf, elem.size);
string msg;
if(elem.size <=0) {
msg = fmt::format("invalid frame size: {}", elem.size);
spdlog::warn(msg);
}
else if(elem.buf == nullptr||elem.buf + sizeof(evpacket_t) == nullptr) {
msg = fmt::format("invalid buffer addr: {0:p}", elem.buf);
spdlog::warn(msg);
}
else {
/// bConnected is false. needn't to log again
//msg = fmt::format("no connection to video gateway server {}:{}", host, port);
//spdlog::warn(msg);
}
/// release msg
if(pMem && pMem->handleMem) {
if(logLevel <= spdlog::level::debug) {
printf("%u, p3 h:%08X\n", frameCntTotal, (int)pMem->handleMem);
}
MaQue_Demo_Mem_release(pMem->handleMem);
pMem = nullptr;
}
continue;
}
/// send frame
if (elem.size > 0) {
auto sz = elem.size;
char *ptr = elem.buf;
ssize_t sent = 0;
while (elem.size > 0) {
ptr += sent;
sent = ::send(raw_socket_, ptr, elem.size > NUM_MAX_PACKET_BYTES? NUM_MAX_PACKET_BYTES:elem.size, 0);
if (sent <= 0) {
break;
}
else {
//spdlog::debug("sent:{}, total:{}", sent, sz);
}
elem.size -= sent;
}
if (sent <= 0) {
spdlog::error("failed to send, peer down");
/// TODO: reconnection
bConnected = false;
bNeedRetry = true;
}
}
else {
spdlog::error("frame size error");
}
if(logLevel <= spdlog::level::debug) {
printf("%u, p4 h:%08X\n", frameCntTotal, (int)pMem->handleMem);
}
MaQue_Demo_Mem_release(pMem->handleMem);
}
}
/// verify request message
string verify_request(json &js)
{
string ret;
string check[] = {kMsgCmd,kMsgData, kMsgRid, kMsgTime, kMsgRid};
for(auto &k: check) {
if(!js.contains(k)) {
ret += k + ",";
}
}
if(!ret.empty()) {
ret = "missing fields in request: " + ret;
}
return ret;
}
/// low level api for report and response
void _report_response(string topic, string message)
{
auto client = MqttMgr::get_instance(mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("failed to create mqtt instance to {}", mqtt_url);
return;
}
auto rc = (*client)[topic].pub(message.c_str(), message.size(), 2, false);
if(rc < 0) {
spdlog::error("failed to pub mqtt message on {}: {}", topic, message);
}
else {
spdlog::info("successfully pub message at {}:{}", topic, message);
}
}
/// having data field along with code and msg
/// signature {"rid", "code", "msg", "cata", "data":{}}
void report_response_json(string topic, json& message)
{
// time
auto ts = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
message.insert_or_assign(kMsgTime, ts);
// rid
auto rid = message["rid"].as<string>();
message.insert_or_assign(kMsgRid, rid);
// type
if(rid.empty()) {
message.insert_or_assign(kMsgType, kMsgTypeReport);
}
else {
message.insert_or_assign(kMsgType, kMsgTypeResponse);
}
// sn
message.insert_or_assign(kMsgSn, string(dev_sn));
_report_response(topic, message.to_string());
}
/// no data field. only code and msg field
void report_response_args(string topic, int code, string message, string cata, string rid, json &&data)
{
json js;
/// current timestamp
auto ts = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
js.insert_or_assign(kMsgTime, ts);
/// rid
js.insert_or_assign(kMsgRid, rid);
/// type
if(rid.empty()) {
js.insert_or_assign(kMsgType, kMsgTypeReport);
}
else {
js.insert_or_assign(kMsgType, kMsgTypeResponse);
}
js.insert_or_assign(kMsgCata, cata);
///
js.insert_or_assign(kMsgCode, code);
js.insert_or_assign(kMsgMsg, message);
js.insert_or_assign(kMsgSn, string(dev_sn));
_report_response(topic, js.to_string());
}
int apply_config(json &js)
{
auto hasSD = is_sdcard_avail();
/// verify parameters
string str, strWarn;
if(!js.contains(kMsgData)) {
str = fmt::format("no data object in config: {}", js.to_string());
}
else {
json data = js[kMsgData];
string veri[] = {kMsgConfigVgw, kMsgConfigMqtt, kMsgConfigUpload};
for(auto &k:veri) {
auto value = msg_field(data, k);
if(value.empty()) {
str = fmt::format("no {} field in data: {}", k, js.to_string());
break;
}
else {
// parse host and port
auto uri = httplib::Uri::Parse(value);
if(uri.Host.empty()|| uri.Port.empty()) {
str = fmt::format("host addr invalid {}:{}, {}", k, value, js.to_string());
break;
}
// check network connections
int socket = 0;
auto rc = raw_connect(uri.Host, uri.Port, &socket);
if(rc < 0 || socket == 0) {
str = fmt::format("failed to connect to {}:{}, {}", k, value, js.to_string());
break;
}
else {
// we must close the test socket
::close(socket);
}
}
}
// check features field
json features = data.get_value_or<json>(kMsgConfigFeatures, json());
string feats[]= {kMsgConfigMotion, kMsgConfigRecord, kMsgConfigAi};
for(auto &k:feats) {
auto f = features.get_value_or<json>(k, json());
if(f.size() == 0) {
strWarn = fmt::format("{}, ", k);
}
if(!strWarn.empty()) {
strWarn = "no below feature in config, default configuration is applied: " + strWarn;
}
}
}
if(!str.empty()) {
report_response_args(pub_topic, EV_MSG_ERROR_CONTENT_SYNTAX, str, kMsgCmdConfig, msg_field(js, kMsgRid), json());
spdlog::error(str);
return -EV_MSG_ERROR_CONTENT_SYNTAX;
}
// try to apply
}
/// handles all incoming request from cloud services
void handle_mqtt_req(MqttHelper *hlp, const void * const data, int len, string topic)
{
char msg[len];
memcpy(msg, data, len);
try {
auto js = json::parse(msg);
spdlog::info("received request on {}:\n{}", topic, (char*)msg);
auto str = verify_request(js);
if(!str.empty()) {
spdlog::warn(str);
report_response_args(pub_topic, EV_MSG_ERROR_CONTENT_SYNTAX, str, js.contains(kMsgCmd)?js[kMsgCmd].as<string>():string(""), js.contains(kMsgRid)?js[kMsgRid].as<string>():string(""), json());
return;
}
/// dispatch msg
auto cmd = js[kMsgCmd].as<string>();
if(cmd == kMsgCmdConfig) {
// apply config
}
else if(cmd == kMsgCmdUploadVideo) {
// upload video
}
else if(cmd == kMsgCmdGetConfig) {
// response config
}
else if(cmd == kMsgCmdStatus) {
// response status
}
else if(cmd == kMsgCmdPtz) {
// response Ptz control
}
else {
auto str = fmt::format("unsupported cmd {}: {}", cmd, js.to_string());
spdlog::warn(str);
report_response_args(pub_topic, EV_MSG_ERROR_UNSUPPORTED_CMD, str, cmd, msg_field(js, kMsgRid), json());
}
}
catch(exception &e) {
auto str = string("exception: ") + e.what() + string(", ") + msg;
spdlog::error(str);
report_response_args(pub_topic, EV_MSG_ERROR_EXCEPTION, str, string(""), string(""), json());
}
//(*hlp)[to_string(stoi(topic) + 1)]("hello ack2", 11);
}
///
void start_mqtt(void *arg)
{
auto client = MqttMgr::get_instance(mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("failed to create mqtt instance to {}", mqtt_url);
return;
}
client->subscribe(sub_topic + dev_sn, handle_mqtt_req, false);
}
///
int main(int argc, char *argv[])
{
int ret = XM_SUCCESS;
spdlog::info("test on hi3518ev300, {}", is_big_endian());
get_mac_addr(dev_sn);
// get env for host and port
auto host_ = getenv("VGW_HOST");
enablePush = true;
if(host_) {
host = host_;
}
else {
enablePush = false;
}
auto port_ = getenv("VGW_PORT");
if(port_) {
port_ = port;
}
else {
enablePush = false;
}
/// TODO: check sd availability
bool bSDAvail = is_sdcard_avail();
auto logLevel_ = getenv("LOG_LEVEL");
if(logLevel_) {
logLevel = spdlog::level::from_str(logLevel_);
spdlog::set_level(logLevel);
}
else {
spdlog::info("log level configured to info");
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;
}
}
MaQue_Demo_Mem_Init();
/// thread of utp time
auto thNtp = thread([] {
bool bGotTime = false;
time_t stm;
while(!bGotTime)
{
if(getNtpTime(&stm) >=0) {
spdlog::info("ntp got time");
::stime(&stm);
bGotTime = true;
}
else {
spdlog::warn("failed to get ntp time");
}
this_thread::sleep_for(chrono::seconds(20));
}
});
if(thNtp.joinable()) {
thNtp.detach();
}
MaQueStartParam_s startParam = {MAQUE_VIDEO_STANDARD_PAL, {MAQUE_VIDEO_COMPRESS_H264, MAQUE_VIDEO_COMPRESS_H265}, "/mnt/sd/Config/"};
ret = LibXmMaQue_System_startUp(&startParam);
spdlog::info("ret: {}", ret);
//
MaQueMemoryApi_s memApi = {MaQue_Demo_Mem_alloc, MaQue_Demo_Mem_release, MaQue_Demo_Mem_addRef, MaQue_Demo_Mem_setLength};
ret = LibXmMaQue_Mem_init(&memApi);
spdlog::info("mem ret: {}", ret);
MaQueSystemTime_s tm;
LibXmMaQue_Time_getCurrentTime(&tm);
LibXmMaQue_Time_print(&tm, nullptr);
//
MaQueCodeAbilities_s capb;
ret = LibXmMaQue_VideoEnc_getAbilities(0, &capb);
spdlog::info("abt ret: {}", ret);
if (XM_SUCCESS == ret) {
spdlog::info(fmt::format("capbilities: supported codecs {0:#b},"
"max fhd/s {0:d}, max res: {0:d}, per ch mres: {0:d}, {0:d}, {0:d}, {0:d}",
int(capb.videoEncTypeMask),
int(capb.maxEncPowerX1080P), int(capb.eDecImageSizeMax), int(capb.astVidEncChnAbility[0].eCapSizeMax),
int(capb.astVidEncChnAbility[1].eCapSizeMax), int(capb.astVidEncChnAbility[2].eCapSizeMax), int(capb.astVidEncChnAbility[3].eCapSizeMax)));
}
//
MaQueVideoEncodeCfg_s cfg;
init_stream_cfg(&cfg);
cfg.nFps = 15;
cfg.eIFrmIntvType = IFRAME_INTV_TYPE_TIME;
cfg.nIFrameInterval = 10;
cfg.eImageQuality = (MaQueImageQuality_e)videoQuality;
cfg.eVidComp = MAQUE_VIDEO_COMPRESS_H264;
cfg.nBitRate = 200 * 8; // 200KB
ret = configure_stream((MaQueStreamChannel_e)0, &cfg);
spdlog::info("cfg stream ret: {}", ret);
///
/// record setting
if(enableRecord && bSDAvail) {
FILE *recFD = fopen(recFilePath, "w+");
if (!recFD) {
spdlog::error("fopen() Failed: {}", recFilePath);
bCanRecord = false;
}
else {
args.recFD = recFD;
bCanRecord = true;
}
}
ret = LibXmMaQue_VideoEnc_startStream(0, (MaQueStreamChannel_e)0, cb_frame_proc, (void *)&args);
if (XM_SUCCESS == ret) {
spdlog::info("created record task successfully");
}
signal(SIGINT, clean_up);
// signal(SIGTERM, clean_up);
signal(SIGKILL, clean_up);
// setup zmq
pPubCtx = zmq_ctx_new();
pPub = zmq_socket(pPubCtx, ZMQ_PUB);
ret = zmq_bind(pPub, strPubUrl.c_str());
if(ret < 0) {
spdlog::error("{} failed to create zmq pub topic: {}", strPubUrl);
exit(1);
}
//
if (enablePush) {
ret = raw_connect(host, port, &raw_socket_);
if (ret < 0 || raw_socket_ <= 0) {
spdlog::error("failed connect to video gateway at {}:{}", host, port);
bConnected = false;
}
else {
bConnected = true;
spdlog::info("successfully connected to video gateway at {}:{}", host, port);
}
thread thPush = thread(frame_send_entry, &args);
spdlog::info("sizeof pkt header {}, sizeof tv {}", sizeof(evpacket_t), sizeof(timeval));
thPush.detach();
}
else {
spdlog::warn("push video is not enabled in environ settings");
}
start_md_bd(&args);
/// subscribe to mqtt
start_mqtt(nullptr);
thread thSmart = thread(maq_smart_task_entry, &args);
thSmart.join();
}
\ No newline at end of file
#include "common.h"
#include "mime.h"
/******************************************************************************
* PRIVATE DEFINITIONS
******************************************************************************/
#define __MIME_BASE64_TEST_SOURCE "ABCDEFGnzmk.ghijdtyo9ryfyhszldfho;asrupq8w49ryaishv;zxvj;oalsurp89w4qytzjhv;sadzaas;fdh;qwoe45yp9gha;ovhajvas;fguasd;f2139457398579 "
#define __MIME_BASE64_TEST_RESULT "QUJDREVGR256bWsuZ2hpamR0eW85cnlmeWhzemxkZmhvO2FzcnVwcTh3NDlyeWFpc2h2O3p4dmo7b2Fsc3VycDg5dzRxeXR6amh2O3NhZHphYXM7ZmRoO3F3b2U0NXlwOWdoYTtvdmhhanZhcztmZ3Vhc2Q7ZjIxMzk0NTczOTg1Nzk="
/******************************************************************************
* PRIVATE DATA
******************************************************************************/
#ifdef I_THOUGHT_THIS_IS_NECESSARY_BUT_NOT_SO_KEEP_IT_FOR_GOODNESS
static unsigned char __byte_adjust_table[256] =
{0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255};
static inline char __byte_adjust(unsigned char x)
{
char ret = x;
#ifndef __RTSP_BIG_ENDIAN
ret = __byte_adjust_table[x];
#endif
return ret;
}
#endif
static char __base64_charmap[64] =
{'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'};
static char __base16_charmap[16] =
{'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
/******************************************************************************
* PRIATE FUNCTIONS
******************************************************************************/
/******************************************************************************
* PUBLIC FUNCTIONS
******************************************************************************/
mime_encoded_handle mime_base16_create(char *src, size_t len)
{
mime_encoded_handle nh = NULL;
int i;
char *result = NULL;
char *p;
DASSERT(src, return NULL);
DASSERT(len > 0, return NULL);
TALLOC(nh, return NULL);
ASSERT(result = calloc(len * 2 + 1, sizeof(char)), goto error);
p = result;
for (i = 0; i < len; i++) {
*(p++) = __base16_charmap[(src[i] & 0xF0) >> 4];
*(p++) = __base16_charmap[(src[i] & 0x0F)];
}
*p = 0;
nh->result = result;
nh->len_result = len * 2 + 1;
nh->len_src = len;
nh->base = 16;
DASSERT(strlen(nh->result) + 1 == nh->len_result, goto error);
return nh;
error:
mime_encoded_delete(nh);
return NULL;
}
mime_encoded_handle mime_base64_create(char *src, size_t len)
{
mime_encoded_handle nh = NULL;
int i;
char *result = NULL;
char *p;
DASSERT(src, return NULL);
DASSERT(len > 0, return NULL);
TALLOC(nh, return NULL);
ASSERT(result = calloc(len * 2 + 1, sizeof(char)), goto error);
p = result;
for(i = 0; i <= len - 3; i+=3) {
*(p++) = __base64_charmap[(src[i] & 0xFC) >> 2];
*(p++) = __base64_charmap[((src[i] & 0x03) << 4) | ((src[i+1] & 0xF0) >> 4)];
*(p++) = __base64_charmap[((src[i+1] & 0x0F) << 2) | ((src[i+2] & 0xC0) >> 6)];
*(p++) = __base64_charmap[src[i+2] & 0x3F];
}
switch(len - i) {
/* 111111 _ 11 | 1111 _ 1111 | 00 _ '=' */
case 2:
*(p++) = __base64_charmap[(src[i] & 0xFC) >> 2];
*(p++) = __base64_charmap[((src[i] & 0x03) << 4) | ((src[i+1] & 0xF0) >> 4)];
*(p++) = __base64_charmap[(src[i+1] & 0x0F) << 2];
*(p++) = '=';
break;
/* 111111 _ 11 | 0000 _ '=' _ '=' */
case 1:
*(p++) = __base64_charmap[(src[i] & 0xFC) >> 2];
*(p++) = __base64_charmap[(src[i] & 0x3) << 4];
*(p++) = '=';
*(p++) = '=';
break;
case 0:
break;
default:
ERR("BUG in base64 encoding\n");
goto error;
}
*p = 0;
nh->result = result;
nh->len_result= 1 + p - result;
nh->len_src = len;
nh->base = 64;
DASSERT(strlen(nh->result) + 1 == nh->len_result, goto error);
return nh;
error:
mime_encoded_delete(nh);
return NULL;
}
#ifndef _RTSP_MIME_H
#define _RTSP_MIME_H
#if defined (__cplusplus)
extern "C" {
#endif
/******************************************************************************
* DEFINITIONS
******************************************************************************/
/******************************************************************************
* DATA STRUCTURES
******************************************************************************/
struct __mime_encoded_obj_t {
char *result;
size_t len_result;
size_t len_src;
unsigned int base; /* 16 or 32 or 64 */
};
typedef struct __mime_encoded_obj_t *mime_encoded_handle;
/******************************************************************************
* DECLARATIONS
******************************************************************************/
mime_encoded_handle mime_base64_create(char *src, size_t len);
mime_encoded_handle mime_base16_create(char *src, size_t len);
static inline void mime_encoded_delete(mime_encoded_handle h)
{
if(h) {
free(h->result);
free(h);
}
}
#if defined (__cplusplus)
}
#endif
#endif
......@@ -36,26 +36,27 @@ typedef struct {
} MdBdMgr_s;
std::atomic<uint64_t> gMotionCounter(0);
extern shared_ptr<spdlog::logger> rlogger;
static XM_S32 cb_motion_detect(XM_VOID *pUserArg, MaQueMdAlarm_s *pstMdAlarm)
{
ev_module_config_t * pArgs = (ev_module_config_t *)pUserArg;
//ev_module_config_t * pArgs = (ev_module_config_t *)pUserArg;
gMotionCounter++;
//spdlog::info("MD eAlarmType: {}, alarmState: {}", pstMdAlarm->eAlarmType, pstMdAlarm->alarmState);
//rlogger->info("MD eAlarmType: {}, alarmState: {}", pstMdAlarm->eAlarmType, pstMdAlarm->alarmState);
return 0;
}
static XM_S32 cb_blind_detect(XM_VOID *pUserArg, MaQueBdResult_s *pstBdRes)
{
ev_module_config_t * pArgs = (ev_module_config_t *)pUserArg;
// ev_module_config_t * pArgs = (ev_module_config_t *)pUserArg;
static int prevState = 0;
if(pstBdRes->state != prevState) {
if(pstBdRes->state == 1) {
spdlog::info("The Camera is blind");
rlogger->info("The Camera is blind");
}
else {
spdlog::info("The Camera is blind recovered");
rlogger->info("The Camera is blind recovered");
}
}
prevState = pstBdRes->state;
......@@ -65,10 +66,10 @@ static XM_S32 cb_blind_detect(XM_VOID *pUserArg, MaQueBdResult_s *pstBdRes)
void start_md_bd(ev_module_config_t *pArgs)
{
if(!pArgs->module.motion.enabled) {
spdlog::warn("motion detect is disabled");
rlogger->warn("motion detect is disabled");
}
spdlog::info("starting motion detect module");
rlogger->info("starting motion detect module");
MdModParam_s mdParam = { 0 };
BdModParam_s bdParam = { 0 };
......@@ -79,7 +80,9 @@ void start_md_bd(ev_module_config_t *pArgs)
mdParam.stMdParam.w = (pArgs->module.motion.region.maxx - pArgs->module.motion.region.minx) * 352;
mdParam.stMdParam.h = (pArgs->module.motion.region.maxy - pArgs->module.motion.region.miny) * 288;
mdParam.stMdParam.eMdAlarmlevel = (MaQueMdAlarmLevel_e)pArgs->module.motion.level; // [1, 6]
bdParam.stBdParam.bEnabled = 1;
/// Blind detection
bdParam.stBdParam.bEnabled = 0;
bdParam.stBdParam.eBdAlarmLevel = (MaQueBdAlarmLevel_e)pArgs->module.motion.level;
if (mdParam.stMdParam.bEnabled) {
......@@ -88,11 +91,11 @@ void start_md_bd(ev_module_config_t *pArgs)
LibXmMaQue_MDAlarm_register(0, cb_motion_detect, pArgs);
}
if (bdParam.stBdParam.bEnabled) {
LibXmMaQue_BD_create(0);
LibXmMaQue_BD_setParam(0, &bdParam.stBdParam);
LibXmMaQue_BD_register(0, cb_blind_detect, pArgs);
}
// if (bdParam.stBdParam.bEnabled) {
// LibXmMaQue_BD_create(0);
// LibXmMaQue_BD_setParam(0, &bdParam.stBdParam);
// LibXmMaQue_BD_register(0, cb_blind_detect, pArgs);
// }
}
void stop_md_bd()
......
......@@ -38,18 +38,19 @@ namespace md{
const string kKeyP= "p";
const int kMaxCntMotionInPre = 5; // about 20s
const int kMaxDuration = 60*20; // max 20 minutes duration to force generating event videos
const int kMinMotionNum = 1;
/// helpers
auto event_inspection(auto e, std::map<const std::string, int> &m){
if(std::is_same<decltype(e), people>::value){
spdlog::info("type is people: {}", ++m[kKeyP]);
rlogger->info("type is people: {}", ++m[kKeyP]);
}else if(std::is_same<decltype(e), motion>::value){
spdlog::info("type is motion: {}", ++m[kKeyM]);
rlogger->info("type is motion: {}", ++m[kKeyM]);
}else if(std::is_same<decltype(e), mpboth>::value){
spdlog::info("type is mpboth");
rlogger->info("type is mpboth");
return true;
}else if(std::is_same<decltype(e), none>::value){
spdlog::info("type is none: {}", ++m[kKeyN]);
rlogger->info("type is none: {}", ++m[kKeyN]);
}
return false;
}
......@@ -85,13 +86,13 @@ namespace md{
const auto guard_in2post = make_guard_fn(kNumIn2Post, guards);
const auto guard_post2none = make_guard_fn(kNumPost2None, guards);
const auto action_none2in = [this](auto e){
hasMotion = false;
hasMotion = 0;
timeStart = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
if(std::is_same<decltype(e), people>::value){
spdlog::info("none -p-> in, {}", timeStart/1000);
rlogger->info("none -p-> in, {}", timeStart/1000);
}else if(std::is_same<decltype(e), mpboth>::value){
spdlog::info("none -b-> in, {}", timeStart/1000);
hasMotion = true;
rlogger->info("none -b-> in, {}", timeStart/1000);
hasMotion++;
}
/// TODO: force event generation when max time duration reached
......@@ -101,37 +102,50 @@ namespace md{
if(timeStart==0)
timeStart = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
if(std::is_same<decltype(e), people>::value){
spdlog::info("pre -p-> in: time {}", timeStart/1000);
rlogger->info("pre -p-> in: time {}", timeStart/1000);
}else if(std::is_same<decltype(e), mpboth>::value){
spdlog::info("pre -b-> in: time {}", timeStart/1000);
rlogger->info("pre -b-> in: time {}", timeStart/1000);
hasMotion++;
}
hasMotion = true;
};
const auto action_pre2pre = [this](auto e){
cntMotionOnly++;
if(cntMotionOnly >= kMaxCntMotionInPre) {
cntMotionOnly = 0;
hasMotion=0;
timeStart = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
}
hasMotion++;
};
const auto action_post2none = [this](auto e){
auto timeEnd = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
if(std::is_same<decltype(e), people>::value){
spdlog::info("post -p-> none, {}", timeEnd/1000);
rlogger->info("post -p-> none, {}", timeEnd/1000);
}else if(std::is_same<decltype(e), motion>::value){
spdlog::info("post -m-> none, {}", timeEnd/1000);
hasMotion++;
rlogger->info("post -m-> none, {}", timeEnd/1000);
}else if(std::is_same<decltype(e), none>::value){
spdlog::info("post -n-> none, {}", timeEnd/1000);
rlogger->info("post -n-> none, {}", timeEnd/1000);
}
upload_item_t item{timeStart, timeEnd, 7, -1, timeEnd};
{
lock_guard<mutex> lg(*gUploadArgs.mut);
gUploadArgs.que->push(item);
gUploadArgs.cv->notify_one();
if(hasMotion >= 1) {
upload_item_t item{timeStart, timeEnd, 7, -1, timeEnd};
{
lock_guard<mutex> lg(*gUploadArgs.mut);
gUploadArgs.que->push(item);
gUploadArgs.cv->notify_one();
}
}else{
rlogger->warn("ignore event since motion {} < {}", hasMotion, kMinMotionNum);
}
hasMotion = 0;
timeStart = 0;
};
const auto action_pre2pre = [this](auto e){
cntMotionOnly++;
if(cntMotionOnly >= kMaxCntMotionInPre) {
cntMotionOnly = 0;
timeStart = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
}
};
const auto may_generate_event = [this]{
auto now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
if(timeStart != 0 && ((now - timeStart) >= kMaxDuration *1000 /*|| this->is("none"_s)*/)){
......@@ -142,47 +156,48 @@ namespace md{
gUploadArgs.cv->notify_one();
}
timeStart = now;
hasMotion=0;
return true;
}
return false;
};
const auto generate_event = [this]{
auto now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
if(timeStart != 0){
upload_item_t item{timeStart, now, 7, -1, now};
{
lock_guard<mutex> lg(*gUploadArgs.mut);
gUploadArgs.que->push(item);
gUploadArgs.cv->notify_one();
}
spdlog::info("event generated: tss {}, tse {}, type {}", timeStart, now, 7);
}else{
//spdlog::error("failed generate event: tss {}, tse {}, type {}", timeStart, now, 7);
}
timeStart = 0;
hasMotion = false;
};
// const auto generate_event = [this]{
// auto now = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
// if(timeStart != 0){
// upload_item_t item{timeStart, now, 7, -1, now};
// {
// lock_guard<mutex> lg(*gUploadArgs.mut);
// gUploadArgs.que->push(item);
// gUploadArgs.cv->notify_one();
// }
// rlogger->info("event generated: tss {}, tse {}, type {}", timeStart, now, 7);
// }else{
// //rlogger->error("failed generate event: tss {}, tse {}, type {}", timeStart, now, 7);
// }
// timeStart = 0;
// hasMotion = 0;
// };
using namespace sml;
return make_transition_table(
*"init"_s = "none"_s,
"none"_s + event<none>/[]{spdlog::info("none -> none");} = "none"_s,
"none"_s + event<motion>/[]{spdlog::info("none -m-> pre");} = "pre"_s,
"none"_s + event<none>/[]{rlogger->info("none -> none");} = "none"_s,
"none"_s + event<motion>/[]{rlogger->info("none -m-> pre");} = "pre"_s,
"none"_s + event<people>/action_none2in = "in"_s,
"none"_s + event<mpboth>/action_none2in = "in"_s,
// pre
"pre"_s + event<none>/[]{spdlog::info("pre -n-> none");} = "none"_s,
"pre"_s + event<none>/[]{rlogger->info("pre -n-> none");} = "none"_s,
"pre"_s + event<motion>/action_pre2pre = "pre"_s,
"pre"_s + event<people>/action_pre2in = "in"_s,
"pre"_s + event<mpboth>/action_pre2in = "in"_s,
// in
"in"_s + event<none> [guard_in2post]/[]{spdlog::info("in -n-> post");} = "post"_s,
"in"_s + event<motion> [guard_in2post]/[]{spdlog::info("in -m-> post");} = "post"_s,
"in"_s + event<people> [guard_in2post]/[]{spdlog::info("in -p-> post");} = "post"_s,
"in"_s + event<none> [guard_in2post]/[]{rlogger->info("in -n-> post");} = "post"_s,
"in"_s + event<motion> [guard_in2post]/[]{rlogger->info("in -m-> post");} = "post"_s,
"in"_s + event<people> [guard_in2post]/[]{rlogger->info("in -p-> post");} = "post"_s,
"in"_s + event<mpboth>/guard_in2post,
"in"_s + sml::on_entry<_> / may_generate_event,
......@@ -190,15 +205,13 @@ namespace md{
"post"_s + event<mpboth>/guard_post2none = "in"_s,
"post"_s + event<people> [guard_post2none]/action_post2none = "none"_s,
"post"_s + event<motion> [guard_post2none]/action_post2none = "none"_s,
"post"_s + event<none> [guard_post2none]/action_post2none = "none"_s,
"post"_s + event<none> [guard_post2none]/action_post2none = "none"_s
// "post"_s + sml::on_entry<_> / may_generate_event,
//
"none"_s + sml::on_entry<_> / generate_event
// "none"_s + sml::on_entry<_> / generate_event
);
}
private:
bool hasMotion{false};
int hasMotion{0};
int cntMotionOnly = 0;
int64_t timeStart{0};
std::map<const std::string, int>guards{{"m", 0}, {"p", 0}, {"n", 0}};
......
......@@ -30,6 +30,8 @@
#define VN(packet) (uint8_t) ((packet.li_vn_mode & 0x38) >> 3) // (vn & 00 111 000) >> 3
#define MODE(packet) (uint8_t) ((packet.li_vn_mode & 0x07) >> 0) // (mode & 00 000 111) >> 0
extern std::shared_ptr<spdlog::logger> rlogger;
int getNtpTime(time_t * txTm)
{
int socket_ = 0, n = 0, rv = 0; // Socket file descriptor and the n return result from writing/reading from the socket.
......@@ -82,11 +84,11 @@ int getNtpTime(time_t * txTm)
hints.ai_protocol = 0;
int last_errno = 0;
spdlog::info("get ntp host info");
rlogger->info("get ntp host info");
addrinfo *addrinfo_result = nullptr;
rv = ::getaddrinfo(host_name, std::to_string(123).c_str(), &hints, &addrinfo_result);
if (rv != 0) {
spdlog::error("::getaddrinfo failed: {}", gai_strerror(rv));
rlogger->error("::getaddrinfo failed: {}", gai_strerror(rv));
goto end;
}
......@@ -101,14 +103,14 @@ int getNtpTime(time_t * txTm)
timeout.tv_usec = 0;
if (setsockopt (socket_, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
spdlog::error("setsockopt SO_RCVTIMEO failed");
rlogger->error("setsockopt SO_RCVTIMEO failed");
goto end;
}
timeout.tv_sec = 2;
if (setsockopt (socket_, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
spdlog::error("setsockopt SO_SNDTIMEO failed");
rlogger->error("setsockopt SO_SNDTIMEO failed");
goto end;
}
......@@ -116,10 +118,10 @@ int getNtpTime(time_t * txTm)
if (rv == 0) {
auto addr = (struct sockaddr_in *)rp->ai_addr;
auto hostIp = inet_ntoa((struct in_addr)addr->sin_addr);
spdlog::info("ntp host {} has ip {}", host_name, hostIp);
rlogger->info("ntp host {} has ip {}", host_name, hostIp);
// rv = fcntl(*socket_, F_SETFL, fcntl(*socket_, F_GETFL, 0) | O_NONBLOCK);
// if (rv == -1){
// spdlog::error("error calling fcntl");
// rlogger->error("error calling fcntl");
// }
break;
}
......@@ -145,7 +147,7 @@ end:
n = write( socket_, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 ) {
spdlog::error("ERROR writing to socket" );
rlogger->error("ERROR writing to socket" );
return -1;
}
......@@ -155,7 +157,7 @@ end:
n = read( socket_, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 ) {
spdlog::error("ERROR reading from socket" );
rlogger->error("ERROR reading from socket" );
return -1;
}
......@@ -178,7 +180,7 @@ end:
::close(socket_);
spdlog::info( "Time: {}", (int)ctime( ( const time_t* ) &txTm ) );
rlogger->info( "Time: {}", (int)ctime( ( const time_t* ) &txTm ) );
return 0;
}
\ No newline at end of file
......@@ -22,6 +22,8 @@ using namespace std;
using namespace evutils;
using namespace jsoncons;
extern shared_ptr<spdlog::logger> rlogger;
recursive_mutex gMut;
map<string, int> mapDirection = {
{"left", MOTOR_MOVE_LEFT},
......@@ -44,7 +46,7 @@ void ptz_waitfor_idle()
usleep(100*1000);
int cnt = 5 * 60; // wait for max 60s
while (cnt-- > 0) {
spdlog::info("ptz waiting");
rlogger->info("ptz waiting");
if (0 == LibXmMaQue_Motor_getPostion(&action,&x,&y)) {
if (MOTOR_IDLE == action) {
break;
......@@ -54,18 +56,18 @@ void ptz_waitfor_idle()
}
}
else {
spdlog::error("ptz failed get position when waiting for idle");
rlogger->error("ptz failed get position when waiting for idle");
break;
}
}
spdlog::info("ptz end wait");
rlogger->info("ptz end wait");
}
string ptz_service_start()
{
string rc;
spdlog::info("start ptz service: calibration/re-locating");
rlogger->info("start ptz service: calibration/re-locating");
if (LibXmMaQue_Motor_create() < 0) {
rc = "ptz failed start service";
return rc;
......@@ -75,7 +77,7 @@ string ptz_service_start()
return rc;
}
spdlog::info("ptz start service finished.");
rlogger->info("ptz start service finished.");
return rc;
}
......@@ -109,7 +111,7 @@ string ptz_get_params(ptz_param_position & param)
param.step_y = y/EV_PTZ_Y_DEGREE;
param.current_y_deg = cy / param.step_y;
param.current_y_steps = cy;
spdlog::info("ptz_get_params: max_x: {}, max_y: {}, current_x: {}, current_y: {}, step_x: {}, step_y: {}", param.max_x, param.max_y, param.current_x_steps, param.current_y_steps, param.step_x, param.step_y);
rlogger->info("ptz_get_params: max_x: {}, max_y: {}, current_x: {}, current_y: {}, step_x: {}, step_y: {}", param.max_x, param.max_y, param.current_x_steps, param.current_y_steps, param.step_x, param.step_y);
return rc;
}
......@@ -129,12 +131,12 @@ string ptz_save()
js["ptz"] = json();
js["ptz"]["x"] = pos.current_x_steps;
js["ptz"]["y"] = pos.current_y_steps;
spdlog::info("ptz_save current: {}", js.to_string());
rlogger->info("ptz_save current: {}", js.to_string());
rc = save_json(js, extraSysConfigFile, true, evutils::make_default_sys_extra_config);
if(!rc.empty()) {
spdlog::error(rc);
rlogger->error(rc);
}
spdlog::info("ptz_save current end: {}", js.to_string());
rlogger->info("ptz_save current end: {}", js.to_string());
return rc;
}
......@@ -148,18 +150,18 @@ string ptz_load()
}
rc = save_json(js, extraSysConfigFile, false, evutils::make_default_sys_extra_config);
if(!rc.empty()) {
spdlog::error(rc);
rlogger->error(rc);
return rc;
}
auto x = js["ptz"]["x"].as<uint32_t>();
auto y = js["ptz"]["y"].as<uint32_t>();
rc = ptz_relocate(x, y);
if(!rc.empty()) {
spdlog::error(rc);
rlogger->error(rc);
return rc;
}
spdlog::info("ptz loaded");
rlogger->info("ptz loaded");
return rc;
}
......@@ -192,9 +194,9 @@ string ptz_move(string dire, float degree)
else {
curr_pos.current_y_steps = 0;
}
spdlog::info("ptz executing: {}, {}", dire, degree);
rlogger->info("ptz executing: {}, {}", dire, degree);
spdlog::info("motor moving steps: {}, {}", curr_pos.current_x_steps, curr_pos.current_y_steps );
rlogger->info("motor moving steps: {}, {}", curr_pos.current_x_steps, curr_pos.current_y_steps );
if(LibXmMaQue_Motor_move(mapDirection[dire], 6, 6, 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);
......@@ -235,9 +237,9 @@ string ptz_relocate(int x, int y)
// move to origin(0,0)
LibXmMaQue_Motor_setZero();
spdlog::info("ptz set zero");
rlogger->info("ptz set zero");
ptz_waitfor_idle();
spdlog::info("ptz set to zero");
rlogger->info("ptz set to zero");
// move to target (x,y)
auto i = LibXmMaQue_Motor_move(mapDirection["upright"], 6, 6, targetx, targety);
......@@ -246,9 +248,9 @@ string ptz_relocate(int x, int y)
return rc;
}
spdlog::info("ptz move to center");
rlogger->info("ptz move to center");
ptz_waitfor_idle();
spdlog::info("ptz end center");
rlogger->info("ptz end center");
return rc;
}
\ No newline at end of file
......@@ -26,6 +26,7 @@ using namespace std;
using namespace zxing;
using namespace zxing::multi;
using namespace zxing::qrcode;
extern shared_ptr<spdlog::logger> rlogger;
/// TODO: refinment of extern
extern "C" {
......
......@@ -21,6 +21,8 @@
using namespace std;
extern shared_ptr<spdlog::logger> rlogger;
int _raw_connect(std::string host, std::string port, int *socket_, int recv_timeout, int send_timeout)
{
int rv = -1;
......@@ -31,12 +33,12 @@ int _raw_connect(std::string host, std::string port, int *socket_, int recv_time
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0;
spdlog::info("raw connect: {}:{}", host, port);
rlogger->info("raw connect: {}:{}", host, port);
addrinfo *addrinfo_result;
rv = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &addrinfo_result);
if (rv != 0) {
spdlog::error("raw_connect ::getaddrinfo failed: {}", gai_strerror(rv));
rlogger->error("raw_connect ::getaddrinfo failed: {}", gai_strerror(rv));
return rv;
}
......@@ -53,7 +55,7 @@ int _raw_connect(std::string host, std::string port, int *socket_, int recv_time
timeout.tv_sec = recv_timeout;
timeout.tv_usec = 0;
if (setsockopt (*socket_, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
spdlog::warn("raw_connect setsockopt SO_RCVTIMEO failed");
rlogger->warn("raw_connect setsockopt SO_RCVTIMEO failed");
continue;
}
}
......@@ -61,7 +63,7 @@ int _raw_connect(std::string host, std::string port, int *socket_, int recv_time
if(send_timeout != 0) {
timeout.tv_sec = send_timeout;
if (setsockopt (*socket_, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(timeout)) < 0) {
spdlog::warn("raw_connect setsockopt SO_SNDTIMEO failed");
rlogger->warn("raw_connect setsockopt SO_SNDTIMEO failed");
continue;
}
}
......@@ -74,9 +76,9 @@ int _raw_connect(std::string host, std::string port, int *socket_, int recv_time
// //auto hostIp = string(inet_ntoa((struct in_addr)addr->sin_addr));
// rv = fcntl(*socket_, F_SETFL, fcntl(*socket_, F_GETFL, 0) | O_NONBLOCK);
// if (rv == -1){
// spdlog::error("error calling fcntl");
// rlogger->error("error calling fcntl");
// }
spdlog::info("raw_connect success to {}:{}", host, port);
rlogger->info("raw_connect success to {}:{}", host, port);
break;
}
else {
......@@ -84,7 +86,7 @@ int _raw_connect(std::string host, std::string port, int *socket_, int recv_time
::close(*socket_);
*socket_ = -1;
rv = -1;
spdlog::warn("raw_connect failed to {}:{}", host, port);
rlogger->warn("raw_connect failed to {}:{}", host, port);
}
}
......
......@@ -32,6 +32,7 @@ extern "C" {
#define MAGIC_TAIL3 ((uint8_t)0xAD)
#define EV_UPLOAD_MAX_BUF_SIZE 1200
static const int kMaxHeapSize = 1 * 1024 * 1024;
using namespace std;
using namespace evutils;
......@@ -45,6 +46,8 @@ extern json gRecordStat;
const string kUploadMetaDir = "/mnt/sd/meta/upload/";
const int kMaxJobLoad = 20;
const int kMaxWaitSecsForJob = 5;
extern shared_ptr<spdlog::logger> rlogger;
///
string upload_video(string hostUrl, char* dev_sn, int64_t tss, int64_t tse, set<int64_t> slices, int8_t type)
{
......@@ -118,7 +121,7 @@ string upload_video(string hostUrl, char* dev_sn, int64_t tss, int64_t tse, set<
memcpy(buf+totalLen, &crc_, 2);
totalLen +=2;
assert(totalLen = headLen);
spdlog::info("header len: {}, crc: {}", totalLen, crc_);
rlogger->info("header len: {}, crc: {}", totalLen, crc_);
// send header
rc = raw_send(s, buf, totalLen);
if(!rc.empty()) {
......@@ -138,7 +141,7 @@ string upload_video(string hostUrl, char* dev_sn, int64_t tss, int64_t tse, set<
break;
}
else {
spdlog::info("sending file {}: {}", fpath, fsize[idx]);
rlogger->info("sending file {}: {}", fpath, fsize[idx]);
for(int i = 0; readsize > 0; i++) {
if(i == 0) {
readsize = ::fread(buf+4, 1, EV_UPLOAD_MAX_BUF_SIZE - 4, fp) + 4;
......@@ -156,7 +159,7 @@ string upload_video(string hostUrl, char* dev_sn, int64_t tss, int64_t tse, set<
else {
break;
}
//spdlog::info("sent bytes: {}", readsize);
//rlogger->info("sent bytes: {}", readsize);
total_sent += readsize;
}
......@@ -164,11 +167,11 @@ string upload_video(string hostUrl, char* dev_sn, int64_t tss, int64_t tse, set<
if(readsize < 0) {
rc = fmt::format("error read file: {}", strerror(errno));
spdlog::error(rc);
rlogger->error(rc);
break;
}
else {
spdlog::info("sent file {}: total {}, sent {}", fpath, fsize[idx], total_sent - 4);
rlogger->info("sent file {}: total {}, sent {}", fpath, fsize[idx], total_sent - 4);
}
}
idx++;
......@@ -211,7 +214,7 @@ bool has_existing_job(string path, upload_item_t &job)
found = true;
}
catch(exception &e) {
spdlog::error("failed to load upload meta file: {}, {}", path, e.what());
rlogger->error("failed to load upload meta file: {}, {}", path, e.what());
fs::remove(path);
}
}
......@@ -229,13 +232,13 @@ vector<upload_item_t> bulk_load_jobs(string path)
for (const auto & entry : fs::directory_iterator(path)) {
fname = entry.path();
if(entry.file_size() == 0 || !entry.is_regular_file()||entry.path().extension() != ".json") {
spdlog::warn("bulk_load_jobs remove {} (empty/directory/!h264)",fname);
rlogger->warn("bulk_load_jobs remove {} (empty/directory/!h264)",fname);
fs::remove(fname);
continue;
}
// auto fsTime = fs::last_write_time(fname);
// auto cftime = decltype(fsTime)::clock::to_time_t(fsTime);
// spdlog::info(" now {}, ft {}", now, cftime);
// rlogger->info(" now {}, ft {}", now, cftime);
// if(now - cftime < gConfigSystem.module.record.interval *2) {
// // maybe still in recording
// continue;
......@@ -250,28 +253,28 @@ vector<upload_item_t> bulk_load_jobs(string path)
auto last = k["last_access"].as<int64_t>();
auto retried = k["retried"].as<int>();
if(now - tse < delta || now - last < delta) {
spdlog::debug("skipped. upload meta contains videos still in recording or rescheduled interval: {}, {}", fname, k.to_string());
rlogger->debug("skipped. upload meta contains videos still in recording or rescheduled interval: {}, {}", fname, k.to_string());
}
else {
upload_item_t item{k["tss"].as<int64_t>(), tse, k["type"].as<uint8_t>(), retried, last};
ret.push_back(item);
spdlog::info("loaded upload meta json: {}, {}", fname, k.to_string());
rlogger->info("loaded upload meta json: {}, {}", fname, k.to_string());
}
}
catch(exception &e) {
spdlog::error("failed process job: {}, {}", k.to_string(), e.what());
rlogger->error("failed process job: {}, {}", k.to_string(), e.what());
fs::remove(fname);
}
}
catch(exception &e) {
spdlog::error("failed to load upload meta json: {}, {}", fname, e.what());
rlogger->error("failed to load upload meta json: {}, {}", fname, e.what());
fs::remove(fname);
}
}
}
}
catch(exception &e) {
spdlog::error("failed to load upload meta: {}", e.what());
rlogger->error("failed to load upload meta: {}", e.what());
}
return ret;
......@@ -288,7 +291,7 @@ bool save_job(string path, json &job)
}
catch(exception &e) {
auto res = fmt::format("failed save job file {}: {}, {}", path, e.what(), job.to_string());
spdlog::error(res);
rlogger->error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "record", "", json());
}
......@@ -301,6 +304,7 @@ bool save_job(string path, json &job)
void upload_svc_entry(thread_upload_args_t *pArg)
{
string rc;
system(fmt::format("mkdir -p {}", kUploadMetaDir).c_str());
auto jobs = bulk_load_jobs(kUploadMetaDir);
for(auto &k:jobs) {
pArg->que->push(k);
......@@ -316,7 +320,7 @@ void upload_svc_entry(thread_upload_args_t *pArg)
bUpload = true;
}
if(bUpload) {
spdlog::info("waiting for job");
rlogger->info("waiting for job");
}
{
unique_lock<mutex> lk(*pArg->mut);
......@@ -324,7 +328,7 @@ void upload_svc_entry(thread_upload_args_t *pArg)
}// lock released
if(got && pArg->que->size()> 0) {
spdlog::info("process one job, total left {}", pArg->que->size());
rlogger->info("process one job, total left {}", pArg->que->size());
auto elem = pArg->que->front();
pArg->que->pop();
......@@ -337,13 +341,13 @@ void upload_svc_entry(thread_upload_args_t *pArg)
catch(exception &e) {
/// TODO: report issue
auto str = fmt::format("failed access/create directory: {}, {}", kUploadMetaDir, e.what());
spdlog::error(str);
rlogger->error(str);
continue;
}
// save to file
string jfilepath = kUploadMetaDir + to_string(elem.tss) + "-" + to_string(elem.tse) + ".json";
if(elem.retried == -1 && has_existing_job(jfilepath, elem)) {
spdlog::warn("already has scheduled job, wait for scheduling: {}-{}.json", elem.tss, elem.tse);
rlogger->warn("already has scheduled job, wait for scheduling: {}-{}.json", elem.tss, elem.tse);
continue;
}
......@@ -360,7 +364,7 @@ void upload_svc_entry(thread_upload_args_t *pArg)
// find video files
if(*(pArg->recList) == nullptr) {
spdlog::error("recfile list is nullptr");
rlogger->error("recfile list is nullptr");
continue;
}
......@@ -370,11 +374,11 @@ void upload_svc_entry(thread_upload_args_t *pArg)
auto res = (*pArg->recList)->findByRange(elem.tss, elem.tse, offs, offe,gConfigSystem.module.record.interval, &status);
if(res.empty()) {
if(status <0 ) {
spdlog::warn("video file not found, they were in the past: ({},{})", elem.tss, elem.tse);
rlogger->warn("video file not found, they were in the past: ({},{})", elem.tss, elem.tse);
fs::remove(jfilepath);
}
else if(status > 0) {
spdlog::warn("video files are in the futrue: ({},{})", elem.tss, elem.tse);
rlogger->warn("video files are in the futrue: ({},{})", elem.tss, elem.tse);
}
continue;
......@@ -389,18 +393,18 @@ void upload_svc_entry(thread_upload_args_t *pArg)
rc = fmt::format("exception upload_video: {}", e.what());
}
if(!rc.empty()) {
spdlog::error(rc);
rlogger->error(rc);
/// TODO: report and retry
// update job
save_job(jfilepath, js);
}
else {
fs::remove(jfilepath);
spdlog::info("successfully uploaded video: {}", js.to_string());
rlogger->info("successfully uploaded video: {}", js.to_string());
}
}
else {
// spdlog::info("no job to process");
// rlogger->info("no job to process");
}
// preventing sleep
......@@ -411,7 +415,7 @@ void upload_svc_entry(thread_upload_args_t *pArg)
unique_lock<mutex> lk(*pArg->mut);
if(pArg->que->size() == 0) {
auto jobs = bulk_load_jobs(kUploadMetaDir);
spdlog::info("{} scheduled jobs loaded", jobs.size());
rlogger->info("{} scheduled jobs loaded", jobs.size());
for(auto &k:jobs) {
pArg->que->push(k);
}
......@@ -421,7 +425,7 @@ void upload_svc_entry(thread_upload_args_t *pArg)
void print_ts_files(OrderedList<int64_t> &_list)
{
spdlog::info("print file ts {}:", _list.items().size());
rlogger->info("print file ts {}:", _list.items().size());
int cnt = 0;
printf("\t");
for(auto &k:_list.items()) {
......@@ -431,7 +435,7 @@ void print_ts_files(OrderedList<int64_t> &_list)
printf("\n\t");
}
}
spdlog::info("\nlist_videos done!");
rlogger->info("\nlist_videos done!");
}
......@@ -443,7 +447,7 @@ void load_sd_files(OrderedList<int64_t> &_list)
for (const auto & entry : fs::directory_iterator(consts::recFilePath)) {
fname = entry.path().c_str();
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());
rlogger->warn("load_sd_files skipped {} (empty/directory/!h264)", entry.path().c_str());
fs::remove(fname);
continue;
}
......@@ -452,14 +456,14 @@ void load_sd_files(OrderedList<int64_t> &_list)
if(ts < consts::TS_2020) {
/// TODO: files records when offline with no valid time got
fs::remove(entry.path());
spdlog::warn("remove records having invalid timestamp: {}", ts);
rlogger->warn("remove records having invalid timestamp: {}", ts);
continue;
}
_list.insert(ts);
}
}
catch(exception &e) {
spdlog::error("{}:{} load_sd_files exception : {}", __FILE__, __LINE__, e.what());
rlogger->error("{}:{} load_sd_files exception : {}", __FILE__, __LINE__, e.what());
}
}
......@@ -473,7 +477,6 @@ void remove_ts_file(int64_t ts)
static int cached_sd_write(fstream *fp, char *buf, int sz, bool immediate = false)
{
int ret = 0;
static const int kMaxHeapSize = 2 * 1024 * 1024;
static char * heap_buff = (char*)malloc(kMaxHeapSize *3);
static int current_heap_size = 0;
......@@ -482,7 +485,7 @@ static int cached_sd_write(fstream *fp, char *buf, int sz, bool immediate = fals
if(current_heap_size + sz > kMaxHeapSize) {
fp->write(heap_buff, current_heap_size);
current_heap_size = 0;
spdlog::info("cached_sd_write");
rlogger->info("cached_sd_write");
}
memcpy(heap_buff + current_heap_size, buf, sz);
......@@ -498,7 +501,7 @@ static int cached_sd_write(fstream *fp, char *buf, int sz, bool immediate = fals
}
catch(exception &e) {
auto res = fmt::format("failed to write sd: {}", e.what());
spdlog::error(res);
rlogger->error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "record", "", json());
}
......@@ -515,23 +518,23 @@ void record_video_entry(ev_module_config_t *pArgs)
// this thread will never exit
while(1) {
if(*(pArgs->pPubCtx) == nullptr) {
spdlog::error("zmq is not started up");
rlogger->error("zmq is not started up");
this_thread::sleep_for(10s);
continue;
}
if(!is_sdcard_avail()) {
spdlog::error("no sd card valial. can't record local videos");
rlogger->error("no sd card valial. can't record local videos");
this_thread::sleep_for(10s);
continue;
}
uint64_t total = 0, avail = 0;
if(!get_sdcard_megabytes(total, avail)) {
spdlog::error("failed to get sd card size");
rlogger->error("failed to get sd card size");
continue;
}
spdlog::info("sd card: {}MB total, {}MB free", total, avail);
rlogger->info("sd card: {}MB total, {}MB free", total, avail);
// calc num of slices
// reserve 0.5GB space
// multipled by 1.5 because videos take less space in midnight (variable bitrate)
......@@ -542,23 +545,23 @@ void record_video_entry(ev_module_config_t *pArgs)
// setup sub
void *pSub = zmq_socket(*(pArgs->pPubCtx), ZMQ_SUB);
// ret = 1;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE, &ret, sizeof (ret));
// ret = 2;
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_IDLE, &ret, sizeof (ret));
// zmq_setsockopt(pSub, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
// uint64_t hwm = 12;
// zmq_setsockopt(pSub, ZMQ_RCVHWM, &hwm, sizeof(hwm));
// int onlyOne = 1;
// zmq_setsockopt(pSub, ZMQ_CONFLATE, &onlyOne, sizeof(onlyOne));
// if(ret < 0) {
// rlogger->error("failed to set ZMQ_CONFLATE on record {}", zmq_strerror(ret));
// }
// ret = 10;
// ret = zmq_setsockopt(pSub, ZMQ_RCVHWM, &ret, sizeof(ret));
// if(ret < 0) {
// rlogger->error("failed to set ZMQ_RCVHWM on record sub {}", zmq_strerror(zmq_errno()));
// }
ret = zmq_connect(pSub, consts::strPubUrl.c_str());
if(ret != 0) {
spdlog::error("failed connect frame pub: {}", consts::strPubUrl);
rlogger->error("failed connect frame pub: {}", consts::strPubUrl);
exit(1);
}
ret = zmq_setsockopt(pSub, ZMQ_SUBSCRIBE, "", 0);
spdlog::info("zmq sub successed (record)");
rlogger->info("zmq sub successed (record)");
zmq_msg_t msg;
int64_t preTs = 0;
......@@ -571,7 +574,7 @@ void record_video_entry(ev_module_config_t *pArgs)
zmq_msg_init(&msg);
ret = zmq_msg_recv(&msg, pSub, 0);
if(ret < 0) {
spdlog::error("failed to recv zmq msg: {}", zmq_strerror(ret));
rlogger->error("failed to recv zmq msg: {}", zmq_strerror(ret));
continue;
}
ssize_t sz = zmq_msg_size(&msg);
......@@ -592,7 +595,7 @@ void record_video_entry(ev_module_config_t *pArgs)
fp = new fstream(consts::recFilePath+to_string(nowTs) + ".h264", std::ios::out|std::ios::binary);
if(!fp->is_open()) {
auto res = "failed to create record video file";
spdlog::error(res);
rlogger->error(res);
if(gConfigSystem.pClient) {
MqttMgr::report_response_args(gConfigSystem.pClient, consts::pub_topic_report, EV_MSG_ERROR_EXCEPTION, res, "record", "", json());
}
......
......@@ -22,8 +22,6 @@ typedef struct {
static pthread_mutex_t g_mutexMem;
static pthread_mutexattr_t Attr;
// = PTHREAD_MUTEX_INITIALIZER;
void MaQue_Demo_Mem_Init(){
pthread_mutexattr_init(&Attr);
pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE);
......@@ -58,13 +56,8 @@ XM_S32 MaQue_Demo_Mem_alloc(XM_HANDLE *pHandle, MaQueMemAllocParam_s *pstAllocPa
case MAQUE_MEM_TYPE_YUV_GET:
{
/// NOTE by Bruce: ATTENTION! we place the extr evpacket_t ahead for saving memory ops later
#ifdef RAW_FORMAT
XM_U8 * raw = (XM_U8 *)malloc(pstAllocParam->nBufSize);
pstMem->pBuffer = raw;
#else
XM_U8 * raw = (XM_U8 *)malloc(pstAllocParam->nBufSize + sizeof(evpacket_t));
pstMem->pBuffer = raw + sizeof(evpacket_t);
#endif
//printf("malloc. raw: %08X, shifted: %08X\n", (uint32_t)raw, (uint32_t)pstMem->pBuffer);
if (raw) {
pstMem->index = 0xff;
......@@ -107,7 +100,6 @@ XM_S32 MaQue_Demo_Mem_release(XM_HANDLE handle)
return 0;
}
#ifdef RAW_FORMAT
if(pstMem == nullptr || pstMem->pBuffer == nullptr ){
pthread_mutex_unlock(&g_mutexMem);
return 0;
......@@ -128,32 +120,6 @@ XM_S32 MaQue_Demo_Mem_release(XM_HANDLE handle)
else{
spdlog::error("shouldn't be here");
}
#else
if(pstMem == nullptr || pstMem->pBuffer == nullptr || pstMem->pBuffer - sizeof(evpacket_t) == nullptr || pstMem->nRefCount == 0){
//printf("invalid free: %08X, %08X, %uref\n", (int)pstMem, (int)pstMem->pBuffer, pstMem->nRefCount);
pthread_mutex_unlock(&g_mutexMem);
return 0;
}
XM_U8* shifted = pstMem->pBuffer;
XM_U8* raw = shifted - sizeof(evpacket_t);
//printf("release. h:%08X, raw: %08X, shifted: %08X, %uref\n", (int)handle, (int)raw, (int)shifted, pstMem->nRefCount);
if (pstMem->nRefCount > 1) {
pstMem->nRefCount--;
}
else if (shifted && raw) {
/// NOTE by Bruce: ATTENTION!
free(raw);
free(pstMem);
}else if(pstMem->nRefCount < 0){
spdlog::error("shouldn't be here. refcnt:{}", pstMem->nRefCount);
}
else if(raw == nullptr || shifted == nullptr){
printf("BOOM! r: %08X, s: %08X\n", (int)raw, (int)shifted);
}else{
spdlog::error("shouldn't be here");
}
#endif
pthread_mutex_unlock(&g_mutexMem);
return 0;
......
#ifndef __REF_MEMORY_H__
#define __REF_MEMORY_H__
#include <maque_type.h>
#include <maque_mem.h>
#include <pthread.h>
#include <spdlog/spdlog.h>
#include <evpacket.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
XM_U8 *pBuffer;
XM_U32 nBufSize;
XM_U32 nUsedBytes;
XM_U32 nRefCount;
XM_S32 index;
} DemoMemory_s;
static pthread_mutex_t g_mutexMem;
static pthread_mutexattr_t Attr;
// = PTHREAD_MUTEX_INITIALIZER;
void MaQue_Demo_Mem_Init(){
pthread_mutexattr_init(&Attr);
pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&g_mutexMem, &Attr);
}
XM_S32 MaQue_Demo_Mem_setLength(XM_HANDLE handle, XM_U32 len)
{
pthread_mutex_lock(&g_mutexMem);
DemoMemory_s *pstMem = (DemoMemory_s *)handle;
pstMem->nUsedBytes = len;
pthread_mutex_unlock(&g_mutexMem);
return 0;
}
XM_S32 MaQue_Demo_Mem_alloc(XM_HANDLE *pHandle, MaQueMemAllocParam_s *pstAllocParam)
{
pthread_mutex_lock(&g_mutexMem);
DemoMemory_s *pstMem = NULL;
pstMem = (DemoMemory_s *)malloc(sizeof(DemoMemory_s));
memset(pstMem, 0, sizeof(*pstMem));
if (!pstMem) {
spdlog::error("malloc failed!");
pthread_mutex_unlock(&g_mutexMem);
return -1;
}
switch(pstAllocParam->eMemType) {
case MAQUE_MEM_TYPE_VIDEO_ENC:
case MAQUE_MEM_TYPE_JPEG_ENC:
case MAQUE_MEM_TYPE_YUV_GET:
{
pstMem->pBuffer = (XM_U8 *)malloc(pstAllocParam->nBufSize);
if (pstMem->pBuffer) {
pstMem->index = 0xff;
pstMem->nBufSize = pstAllocParam->nBufSize;
pstMem->nRefCount = 1;
}else {
spdlog::error("memory allocate failure");
free(pstMem);
pstMem = NULL;
}
}
break;
default:
spdlog::error("Invalid eMemType: {}", pstAllocParam->eMemType);
break;
}
if (!pstMem) {
spdlog::error("alloc failed");
pthread_mutex_unlock(&g_mutexMem);
return -1;
}
*pHandle = (XM_HANDLE)pstMem;
pstAllocParam->pBuffer = pstMem->pBuffer;
pthread_mutex_unlock(&g_mutexMem);
return 0;
}
XM_S32 MaQue_Demo_Mem_release(XM_HANDLE handle)
{
if(handle == 0){
return 0;
}
pthread_mutex_lock(&g_mutexMem);
DemoMemory_s *pstMem = (DemoMemory_s *)handle;
if (!handle) {
pthread_mutex_unlock(&g_mutexMem);
return 0;
}
if(pstMem == nullptr || pstMem->pBuffer == nullptr ||pstMem->nRefCount == 0){
//printf("invalid free: %08X, %08X, %uref\n", (int)pstMem, (int)pstMem->pBuffer, pstMem->nRefCount);
pthread_mutex_unlock(&g_mutexMem);
return 0;
}
if (pstMem->nRefCount > 1) {
pstMem->nRefCount--;
}
else if (pstMem->pBuffer) {
/// NOTE by Bruce: ATTENTION!
free(pstMem->pBuffer);
free(pstMem);
}else if(pstMem->nRefCount < 0){
spdlog::error("shouldn't be here. refcnt:{}", pstMem->nRefCount);
}
else if(pstMem->pBuffer == nullptr){
printf("BOOM! r: %08X\n", (int)pstMem->pBuffer);
}else{
spdlog::error("shouldn't be here");
}
pthread_mutex_unlock(&g_mutexMem);
return 0;
}
XM_S32 MaQue_Demo_Mem_addRef(XM_HANDLE handle)
{
pthread_mutex_lock(&g_mutexMem);
DemoMemory_s *pstMem = (DemoMemory_s *)handle;
// memory already released
if(!handle) {
pthread_mutex_unlock(&g_mutexMem);
spdlog::error("BOOM! ar");
return -1;
}
pstMem->nRefCount += 1;
pthread_mutex_unlock(&g_mutexMem);
return 0;
}
#ifdef __cplusplus
}
#endif
#endif
\ No newline at end of file
#ifndef _RTSP_RTP_H
#define _RTSP_RTP_H
#endif
#ifndef __RTSP_PUSHER_HPP__
#define __RTSP_PUSHER_HPP__
#include <fmt/format.h>
#include <string.h>
#include <stdio.h>
#include <thread>
#include <httplib.h>
#include <spdlog/spdlog.h>
#include <queue>
#include <mutex>
#include <rfc.h>
#include "mime.h"
#include "common.h"
using namespace std;
#define __RTP_MAXPAYLOADSIZE (1400 - 12 -4)
const std::string RTSP_FMT_OPTIONS = "OPTIONS {} RTSP/1.0\r\nCSeq: {}\r\nUser-Agent: {}\r\n\r\n";
const std::string TEST_URI1 = "rtsp://aa:bb@evcloudsvc.ilabservice.cloud:554/pusher_test";
const std::string TEST_URI2 = "rtsp://40.73.41.176:554/pusher_test";
typedef struct DataItem {
char *buf;
size_t size;
void *ud;
} *DataItemPtr;
typedef struct Notifier {
mutex *mut;
condition_variable *cond;
} *NotifierPtr;
typedef struct tcp_rtcp_t {
char tcphdr[4];
rtcp_t rtcp;
}tcp_rtcp_t;
typedef struct nal_rtp_t {
struct {
rtp_hdr_t header;
signed char payload[__RTP_MAXPAYLOADSIZE];
} packet;
int rtpsize;
}nal_rtp_t;
typedef struct rtp_tcp_t {
char tcphdr[4];
nal_rtp_t nal_rtp;
int size;
}rtp_tcp_t;
typedef struct __time_stat_t {
struct timeval prev_tv;
unsigned long long avg;
unsigned long long total_cnt;
unsigned long long jitter_mask;
unsigned int cnt;
unsigned int ts_offset;
} time_stat_t;
class RtspPusher {
public:
typedef std::string (RtspPusher::*SDPBuilderFuncType)();
private:
int socket_ = -1;
httplib::Uri targetParsedUri;
std::string targetRawUri;
thread th;
int seqNum;
std::string hostIp;
std::string sessionId;
NotifierPtr noti;
queue<DataItem> *dataq;
const std::string ua = "EVCamera";
mime_encoded_handle spropSpsB64 = NULL;
mime_encoded_handle spropSpsB16 = NULL;
mime_encoded_handle spropPpsB64 = NULL;
unsigned short rtp_seq=0;
unsigned int rtp_timestamp;
unsigned int ssrc=0;
time_stat_t timeStat;
unsigned int rtcp_octet=0;
unsigned int rtcp_packet_cnt=0;
int rtcp_tick=0;
int rtcp_tick_org=0;
int get_timestamp_offset(struct __time_stat_t *timeStat, struct timeval *p_tv)
{
unsigned long long kts;
if(timeStat->prev_tv.tv_sec == 0) {
timeStat->prev_tv = *p_tv;
timeStat->total_cnt = 0;
timeStat->cnt = 0;
timeStat->avg = 0;
timeStat->jitter_mask = 0;
return SUCCESS;
}
/* we fix time stamp offset in 5 years of running on 30fps.. */
if(timeStat->total_cnt < 0xFFFFFFFFLLU) {
kts = ((p_tv->tv_sec - timeStat->prev_tv.tv_sec) * 1000000 + (p_tv->tv_usec - timeStat->prev_tv.tv_usec)) * 90;
timeStat->avg = ((timeStat->avg * timeStat->total_cnt) + kts * 1000) / (timeStat->total_cnt + 1);
timeStat->prev_tv = *p_tv;
timeStat->total_cnt += 1;
}
timeStat->jitter_mask += timeStat->avg % 1000000;
timeStat->ts_offset = (timeStat->avg / 1000000);
if(timeStat->jitter_mask > 1000000) {
timeStat->ts_offset += 1;
timeStat->jitter_mask -= 1000000;
}
return SUCCESS;
}
int __split_nal(signed char *buf, signed char **nalptr, size_t *p_len, size_t max_len)
{
int i;
int start = -1;
for(i = (*nalptr) - buf + *p_len;i<max_len-5;i++) {
if(buf[i] == 0x00 &&
buf[i+1] == 0x00 &&
buf[i+2] == 0x00 &&
buf[i+3] == 0x01) {
if(start == -1){
i += 4;
start = i;
} else {
*nalptr = &(buf[start]);
while(buf[i-1] == 0) i--;
*p_len = i - start;
return 0;
}
}
}
if(start == -1) {
/* malformed NAL */
return -1;
}
*nalptr = &(buf[start]);
*p_len = max_len + 2 - start;
return 0;
}
int probe_sprop(signed char *buf, size_t len)
{
signed char *nalptr;
size_t single_len;
mime_encoded_handle base64 = NULL;
mime_encoded_handle base16 = NULL;
unsigned int pt;
/* check SPS is set */
if(!spropSpsB64){
nalptr = buf;
single_len = 0;
while (__split_nal(buf,&nalptr,&single_len,len) == SUCCESS) {
pt = nalptr[0] & 0x1F;
if(pt == H264_NAL_TYPE_SPS) {
ASSERT(base64 = mime_base64_create((char *)&(nalptr[0]),single_len), return FAILURE);
ASSERT(base16 = mime_base16_create((char *)&(nalptr[1]),3), return FAILURE);
DASSERT(base16->base == 16, return FAILURE);
DASSERT(base64->base == 64, return FAILURE);
if(spropSpsB64) {
DBG("sps is set by another thread?\n");
mime_encoded_delete(base64);
}
spropSpsB64 = base64;
if(spropSpsB16) {
DBG("sps is set by another thread?\n");
mime_encoded_delete(base16);
}
spropSpsB16 = base16;
}
}
base64 = NULL;
base16 = NULL;
}
/* check PPS is set */
if(!spropPpsB64){
nalptr = buf;
single_len = 0;
while (__split_nal(buf,&nalptr,&single_len,len) == SUCCESS) {
pt = nalptr[0] & 0x1F;
if(pt == H264_NAL_TYPE_PPS) {
ASSERT(single_len >= 4, return FAILURE);
ASSERT(base64 = mime_base64_create((char *)&(nalptr[0]),single_len), return FAILURE);
DASSERT(base64->base == 64, return FAILURE);
if(spropPpsB64) {
DBG("pps is set by another thread?\n");
mime_encoded_delete(base64);
}
spropPpsB64 = base64;
}
}
base64 = NULL;
}
return SUCCESS;
}
int connect(std::string host, std::string port){
int rv = 0;
struct addrinfo hints{};
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0;
addrinfo *addrinfo_result;
rv = ::getaddrinfo(host.c_str(), port.c_str(), &hints, &addrinfo_result);
if (rv != 0)
{
spdlog::error("::getaddrinfo failed: {}", gai_strerror(rv));
return rv;
}
int last_errno = 0;
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{
socket_ = ::socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (socket_ == -1)
{
last_errno = errno;
continue;
}
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
if (rv == 0)
{
auto addr = (struct sockaddr_in *)rp->ai_addr;
hostIp = string(inet_ntoa((struct in_addr)addr->sin_addr));
break;
}
else
{
last_errno = errno;
::close(socket_);
socket_ = -1;
rv = -1;
}
}
::freeaddrinfo(addrinfo_result);
return rv;
}
size_t send(const char *data, size_t n_bytes)
{
size_t bytes_sent = 0;
while (bytes_sent < n_bytes)
{
const int send_flags = 0;
auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
if (write_result == 0)
{
break;
}
bytes_sent += static_cast<size_t>(write_result);
}
return bytes_sent;
}
int srecv(char *ptr, size_t size/*, time_t sec, time_t usec*/) {
fd_set fds;
FD_ZERO(&fds);
FD_SET(socket_, &fds);
timeval tv;
tv.tv_sec = static_cast<long>(5);
tv.tv_usec = static_cast<long>(0);
if(select(static_cast<int>(socket_ + 1), &fds, nullptr, nullptr, &tv) > 0)
{
return ::recv(socket_, ptr, static_cast<int>(size), 0);
}else {
return -1;
}
}
static inline unsigned long long __get_random_byte(unsigned *ctx)
{
return (unsigned long long)(rand_r(ctx) % 256);
}
static inline unsigned long long __get_random_llu(unsigned *ctx)
{
return 0
| __get_random_byte(ctx)
| (__get_random_byte(ctx)) << 8
| (__get_random_byte(ctx)) << 16
| (__get_random_byte(ctx)) << 24
| (__get_random_byte(ctx)) << 32
| (__get_random_byte(ctx)) << 40
| (__get_random_byte(ctx)) << 48
| (__get_random_byte(ctx)) << 56;
}
int _sendRtp(void *v){
int send_bytes;
rtp_tcp_t *rtp_tcp = (rtp_tcp_t *) v;
nal_rtp_t *rtp = &(rtp_tcp->nal_rtp);
rtp_tcp->size = rtp->rtpsize + 4;
// rtp over tcp 4 bytes header
rtp_tcp->tcphdr[0] = '$';
rtp_tcp->tcphdr[1] = 0;
rtp_tcp->tcphdr[2] = (char)((rtp->rtpsize & 0xFF00)>>8);
rtp_tcp->tcphdr[3] = (char)(rtp->rtpsize&0xFF);
rtp->packet.header.seq = htons(rtp_seq);
rtp->packet.header.ts = htonl(rtp_timestamp);
rtp->packet.header.ssrc = htonl(ssrc);
rtp_seq += 1;
send_bytes = ::send(socket_,v, rtp_tcp->size, 0);
if(send_bytes == rtp_tcp->size) {
rtcp_packet_cnt += 1;
rtcp_octet += rtp_tcp->size;
return 0;
}
if(send_bytes == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)){
ERR("EAGAIN\n");
return -1;
}
ERR("send:%d:%s\n",send_bytes,strerror(errno));
return -1;
}
int sendRtp(signed char *nalbuf, size_t nalsize)
{
rtp_tcp_t rtp_tcp;
nal_rtp_t *rtp = &rtp_tcp.nal_rtp;
signed char *nalptr = nalbuf;
unsigned int nri = nalptr[0] & 0x60;
unsigned int pt = nalptr[0] & 0x1F;
rtp_hdr_t *p_header = &(rtp->packet.header);
signed char *payload = rtp->packet.payload;
int rv = 0;
p_header->version = 2;
p_header->p = 0;
p_header->x = 0;
p_header->cc = 0;
p_header->pt = 96 & 0x7F;
if(nalsize <= __RTP_MAXPAYLOADSIZE){
/* single packet */
/* SPS, PPS, SEI is not marked */
if(pt != 7 && pt != 8 && pt != 6) {
p_header->m = 1;
} else {
p_header->m = 0;
}
memcpy(payload, nalptr, nalsize);
rtp->rtpsize = nalsize + sizeof(rtp_hdr_t);
// TODO: send
rv = _sendRtp(&rtp_tcp);
if(rv < 0) {
spdlog::error("failed to send one packet rtp");
return rv;
}
} else {
//spdlog::warn("got big frame! {}", nalsize);
nalptr += 1;
nalsize -= 1;
payload[0] = 28;
payload[0] |= nri;
payload[1] = pt;
payload[1] |= 1 << 7;
/* send fragmented nal */
while(nalsize > __RTP_MAXPAYLOADSIZE - 2){
p_header->m = 0;
memcpy(&(payload[2]), nalptr, __RTP_MAXPAYLOADSIZE - 2);
rtp->rtpsize = sizeof(rtp_hdr_t) + __RTP_MAXPAYLOADSIZE;
nalptr += __RTP_MAXPAYLOADSIZE - 2;
nalsize -= __RTP_MAXPAYLOADSIZE - 2;
// ASSERT(__rtp_send_h264(&rtp,trans_list) == SUCCESS, return FAILURE);
// TODO: send
rv = _sendRtp(&rtp_tcp);
if(rv < 0) {
spdlog::error("failed to send full packet rtp");
return rv;
}
/* intended xor. blame vim :( */
payload[1] &= 0xFF ^ (1<<7);
}
/* send trailing nal */
p_header->m = 1;
payload[1] |= 1 << 6;
/* intended xor. blame vim :( */
payload[1] &= 0xFF ^ (1<<7);
rtp->rtpsize = nalsize + sizeof(rtp_hdr_t);
memcpy(&(payload[2]), nalptr, nalsize);
rv = _sendRtp(&rtp_tcp);
// ASSERT(__rtp_send_h264(&rtp, trans_list) == SUCCESS, return FAILURE);
// TODO: send
}
return rv;
}
int sendRtpFullPacket(char * nalbuf, size_t size) {
int ret = 0;
unsigned int len = size + 12;
// send 4 bytes magic first;
unsigned int nri = nalbuf[0] & 0x60;
unsigned int pt = nalbuf[0] & 0x1F;
char magic[4+12];
magic[0] = '$';
magic[1] = 0;
magic[2] = (char)((len& 0xFF00)>>8);
magic[3] = (char)(len&0xFF);
// 12 bytes header
rtp_hdr_t *p_header = (rtp_hdr_t *)(magic+4);
p_header->version = 2;
p_header->p = 0;
p_header->x = 0;
p_header->cc = 0;
p_header->pt = 96 & 0x7F;
p_header->seq = htons(rtp_seq);
p_header->ts = htonl(rtp_timestamp);
p_header->ssrc = htonl(ssrc);
if(pt != 7 && pt != 8 && pt != 6) {
p_header->m = 1;
} else {
p_header->m = 0;
}
ret = ::send(socket_, magic, 16, 0);
if(ret <0) {
spdlog::error("failed to send header");
exit(1);
}
ret = ::send(socket_, nalbuf, size, 0);
if(ret != size) {
spdlog::error("failed to send body, {}, {}", ret, size);
exit(1);
}
return 0;
}
string sendRtcpSr()
{
unsigned int ts_h;
unsigned int ts_l;
int send_bytes;
struct sockaddr_in to_addr;
tcp_rtcp_t tcp_rtcp;
rtcp_t *rtcp = &tcp_rtcp.rtcp;
tcp_rtcp.tcphdr[0] = 0x24;
tcp_rtcp.tcphdr[1] = 0; // channel
tcp_rtcp.tcphdr[2] = (char)((28 & 0xFF00)>>8);
tcp_rtcp.tcphdr[3] = (char)(28 & 0xFF);
struct timeval tv;
::gettimeofday(&tv,NULL);
ts_h = (unsigned int)tv.tv_sec + 2208988800u;
ts_l = (tv.tv_usec << 12) + (tv.tv_usec << 8) - ((tv.tv_usec * 3650) >> 6);
spdlog::info("timeofday sec:{}, usec:{}", tv.tv_sec, tv.tv_usec);
rtcp->common.version = 2; //
rtcp->common.length = htons(6);
rtcp->common.p = 0;
rtcp->common.count = 0;
rtcp->common.pt = RTCP_SR;
rtcp->r.sr.ssrc = htonl(ssrc);
rtcp->r.sr.ntp_sec = htonl(ts_h);
rtcp->r.sr.ntp_frac = htonl(ts_l);
rtcp->r.sr.rtp_ts = htonl(rtp_timestamp);
rtcp->r.sr.psent = htonl(rtcp_packet_cnt);
rtcp->r.sr.osent = htonl(rtcp_octet);
ASSERT((send_bytes = ::send(socket_, &(tcp_rtcp), 32, 0)) == 32, ({
ERR("send :%, %",send_bytes,strerror(errno));
return "error";}));
rtcp_packet_cnt = 0;
rtcp_octet = 0;
rtcp_tick = rtcp_tick_org;
return "";
}
std::string buildOptsString(){
std::string opts = fmt::format(RTSP_FMT_OPTIONS, targetRawUri, (seqNum = 1), ua);
return opts;
}
std::string buildAnnounceString()
{
std::string sdp = fmt::format(
"v=0\r\n"
"o=- {} 0 IN IP4 {}\r\n"
"s={}\r\n"
"c=IN IP4 {}\r\n"
"t=0 0\r\n"
"a=tool:libavformat 58.29.100\r\n"
//"a=control:*\r\n"
"m=video 0 RTP/AVP 96\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=fmtp:96 packetization-mode=1;"
" profile-level-id={};"
" sprop-parameter-sets={},{};\r\n"
"a=control:streamid={}\r\n",
0, "127.0.0.1",ua, "0.0.0.0",spropSpsB16->result, spropSpsB64->result, spropPpsB64->result, 0); //(long)std::time(NULL)
std::string ann = fmt::format(
"ANNOUNCE {} RTSP/1.0\r\n"
"Content-Type: application/sdp\r\n"
"CSeq: {}\r\n"
"User-Agent: {}\r\n"
"Session: {}\r\n"
"Content-Length: {}\r\n\r\n",
targetRawUri,
++seqNum,
ua,
sessionId,
sdp.length()
);
return ann + sdp;
}
std::string buildSetupString(){
std::string setup = fmt::format(
"SETUP {}/streamid={} RTSP/1.0\r\n"
"Transport: RTP/AVP/TCP;unicast;mode=record;interleaved={}-{}\r\n"
"CSeq: {}\r\n"
"User-Agent: {}\r\n"
"Session: {}\r\n"
"\r\n",
targetRawUri,
0,
0,
1,
++seqNum,
ua,
sessionId
);
return setup;
}
std::string buildStartString(){
std::string record = fmt::format(
"RECORD {} RTSP/1.0\r\n"
"Range: npt=0.000-\r\n"
"CSeq: {}\r\n"
"User-Agent: {}\r\n"
"Session: {}\r\n"
"\r\n",
targetRawUri,
++seqNum,
ua,
sessionId);
return record;
}
std::string buildStopString(){
std::string td = fmt::format(
"TEARDOWN {} RTSP/1.0\r\n"
"CSeq: {}\r\n"
"User-Agent: {}\r\n"
"Session: {}\r\n\r\n",
targetRawUri,
++seqNum,
ua,
sessionId);
return td;
}
int init_session(RtspPusher::SDPBuilderFuncType *builders){
// send OPTIONS
int rv = 0;
if(targetParsedUri.Port.empty()){
targetParsedUri.Port = "554";
}
rv = connect(targetParsedUri.Host, targetParsedUri.Port);
if(rv != 0) {
spdlog::error("failed to connect host {}:{}", targetParsedUri.Host, targetParsedUri.Port);
return rv;
}
char line[1024] = {0};
this->seqNum = 1;
for(int i = 0; i <4; i++){
std::string s;
size_t sz;
s = (this->*builders[i])();
sz = send(s.data(), s.size());
spdlog::info("\n\nsend: {},\n{}", s.size(), s);
memset(line, 0, sizeof(line));
sz = srecv(line, 1023);
if(sz < 0){
spdlog::error("failed to connect to server");
return sz;
}
spdlog::info("\nreads: {},\n{}", sz, line);
if(this->sessionId.empty()){
size_t pos = 0, opos = 0, tpos = 0;
std::string sline(line);
for(pos=sline.find("\r\n");pos!=string::npos && (pos+2)!=sline.size();opos = pos+2, pos=sline.find("\r\n", opos)){
std::string l = sline.substr(opos, pos-opos);
spdlog::info("line :{}", l);
if((tpos=l.find("Session: ")) != string::npos){
this->sessionId = l.substr(9);
spdlog::info( "sess string: {}", this->sessionId);
}
}
}
}
sendRtcpSr();
return 0;
}
public:
RtspPusher(std::string url, NotifierPtr noti ,queue<DataItem> *dataq): targetRawUri(url), noti(noti), dataq(dataq){
targetParsedUri = httplib::Uri::Parse(url);
rtp_seq = rand_r((unsigned *)this);
rtp_timestamp = (unsigned int)(__get_random_llu((unsigned int *)this));
ssrc = (unsigned int)(__get_random_llu((unsigned int *)this));
spdlog::info("u:{}, p:{}, h:{}, p:{}, {}, {} ", targetParsedUri.User, targetParsedUri.Password, targetParsedUri.Host, targetParsedUri.Port, targetParsedUri.Path, targetParsedUri.QueryString);
}
~RtspPusher(){
}
int start(thread &th) {
int rv = 0;
SDPBuilderFuncType builders[] = {
&RtspPusher::buildOptsString,
&RtspPusher::buildAnnounceString,
&RtspPusher::buildSetupString,
&RtspPusher::buildStartString,
};
th = thread([this, builders](){
int ret;
static bool sessionInited = false;
unsigned long long frameCnt = 0;
while(1){
unique_lock<mutex> lk(*this->noti->mut);
this->noti->cond->wait(lk, [this] {return !this->dataq->empty();});
auto elem = this->dataq->front();
this->dataq->pop();
MaQueVideoEncFrameInfo_s *pMem = (MaQueVideoEncFrameInfo_s *)elem.ud;
if(frameCnt %5 == 0){
frameCnt++;
spdlog::info("got {} frame to send {}", frameCnt, pMem->nDataLen);
}
// probe sps pps
if(!sessionInited &&(this->spropPpsB64 == NULL || this->spropSpsB16 == NULL||this-> spropSpsB64 == NULL)){
ret = this->probe_sprop((signed char*)elem.buf, elem.size);
if(ret != 0) {
spdlog::error("failed to probe sprops");
}
}else{
if(!sessionInited) {
if(pMem->eSubType == MAQUE_FRAME_SUBTYPE_I) {
spdlog::info("sps16 {}, sps64 {}, pps64 {}", this->spropSpsB16->result, this->spropSpsB64->result, this->spropPpsB64->result);
ret = init_session((RtspPusher::SDPBuilderFuncType*) builders);
if(ret != 0){
spdlog::error("failed to init_session");
exit(1);
}
sessionInited = true;
}
}
}
if(sessionInited){
struct timeval tv;
::gettimeofday(&tv,NULL);
get_timestamp_offset(&timeStat, &tv);
if(this->sendRtp((char *)elem.buf, elem.size) < 0){
spdlog::error("socket error: failed to send");
//sessionInited = false;
exit(1);
}
}
// get data
//send(elem.buf, elem.size);
MaQue_Demo_Mem_release(pMem->handleMem);
}
});
if(th.joinable()){
th.detach();
return 0;
}else{
return -5;
}
}
};
#endif
// int test(){
// //RtspPusher pusher1(TEST_URI1);
// RtspPusher pusher2(TEST_URI2);
// pusher2.start();
// }
// int main(){
// test();
// }
\ No newline at end of file
......@@ -26,7 +26,6 @@ extern "C"
#include <vector>
#include <chrono>
#include <motiondetect.hpp>
using namespace std;
using namespace jsoncons;
......@@ -34,6 +33,9 @@ using namespace jsoncons;
#define DIV_UP(x, a) ( ((x) + ((a) - 1) ) / a )
#define SMART_CAP_MIN_SECS 1
extern shared_ptr<spdlog::logger> rlogger;
#include <motiondetect.hpp>
long long smartTsLast = 0;
XM_S32 MaQue_JpegEnc_getFrame_callback (XM_VOID *pUserArg, MaQueSmartJpegFrame_s *pstJpegFrame)
{
......@@ -47,7 +49,7 @@ XM_S32 MaQue_JpegEnc_getFrame_callback (XM_VOID *pUserArg, MaQueSmartJpegFrame_s
smartTsLast = now;
json js;
string str = fmt::format("new ai capture: {}, idx = {}, toltal = {}", pstJpegFrame->aClassName, pstJpegFrame->nIndex, pstJpegFrame->nToltalJpeg);
spdlog::info(str);
rlogger->info(str);
return XM_SUCCESS;
}
......@@ -55,9 +57,9 @@ XM_S32 MaQue_JpegEnc_getFrame_callback (XM_VOID *pUserArg, MaQueSmartJpegFrame_s
void maq_smart_task_entry(ev_module_config_t *pArg)
{
if(!pArg->module.ai.enabled) {
spdlog::warn("ai detection is disabled. daemon will start and no video uploads");
rlogger->warn("ai detection is disabled. daemon will start and no video uploads");
}
spdlog::info("starting ai detection task");
rlogger->info("starting ai detection task");
XM_S32 res;
MaQueStreamChannel_e eStreamChn;
MaQueSmartParams_s stMaQueSmartParams;
......@@ -82,7 +84,7 @@ void maq_smart_task_entry(ev_module_config_t *pArg)
res = LibXmMaQue_SmartCreate(0);
if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartCreate() Failed!");
rlogger->error("LibXmMaQue_SmartCreate() Failed!");
return;
}
......@@ -131,10 +133,10 @@ void maq_smart_task_entry(ev_module_config_t *pArg)
// stMaQueSmartParams.pMbMap[idx] = 1;
// }
// }
spdlog::info("start LibXmMaQue_SmartSetParameter");
rlogger->info("start LibXmMaQue_SmartSetParameter");
res = LibXmMaQue_SmartSetParameter(0, &stMaQueSmartParams);
if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartSetParameter() Failed!");
rlogger->error("LibXmMaQue_SmartSetParameter() Failed!");
free(stMaQueSmartParams.pMbMap);
stMaQueSmartParams.pMbMap = NULL;
return;
......@@ -144,36 +146,36 @@ void maq_smart_task_entry(ev_module_config_t *pArg)
free(stMaQueSmartParams.pMbMap);
stMaQueSmartParams.pMbMap = NULL;
}
spdlog::info("finished LibXmMaQue_SmartSetParameter");
rlogger->info("finished LibXmMaQue_SmartSetParameter");
eStreamChn = MAQUE_STREAM_CHN_MAIN;
stMaQueSmartLowBitRate.bLowBitrate = 1;
stMaQueSmartLowBitRate.bIsoAdaptive = 1;
res = LibXmMaQue_SmartLowBitRate(0, eStreamChn, &stMaQueSmartLowBitRate);
if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartLowBitRate() Failed!");
rlogger->error("LibXmMaQue_SmartLowBitRate() Failed!");
return;
}
res = LibXmMaQue_SmartAdvanceIsp(0, bAdvanceIspEnable);
if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartAdvanceIsp() Failed!");
rlogger->error("LibXmMaQue_SmartAdvanceIsp() Failed!");
return;
}
res = LibXmMaQue_SmartPDThreshold(0, pArg->module.ai.human_thresh);
if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartPDThreshold() Failed!");
rlogger->error("LibXmMaQue_SmartPDThreshold() Failed!");
return;
}
res = LibXmMaQue_SmartFDThreshold(0, pArg->module.ai.face_thresh);
if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartFDThreshold() Failed!");
rlogger->error("LibXmMaQue_SmartFDThreshold() Failed!");
return;
}
spdlog::info("finished smart init");
rlogger->info("finished smart init");
stCallback.eType = MAQUE_SMART_JPEG_ENCODE_LARGE;//MAQUE_SMART_JPEG_ENCODE_CUTOUT;
stCallback.eClass = MAQUE_SMART_JPEG_CLASS_PD;//MAQUE_SMART_JPEG_CLASS_FD;
......@@ -199,19 +201,20 @@ void maq_smart_task_entry(ev_module_config_t *pArg)
// assuming it's 1s
this_thread::sleep_for(1s);
// check AI stat
res = LibXmMaQue_SmartGetTarget(0, &stMaQueSmartTarget);
if (XM_SUCCESS != res) {
spdlog::error("failed LibXmMaQue_SmartGetTarget");
}
if (stMaQueSmartTarget.targetPDNum > 0) {
hasHuman = true;
last_region = {stMaQueSmartTarget.aPDRect[0].s16X1, stMaQueSmartTarget.aPDRect[0].s16Y1, stMaQueSmartTarget.aPDRect[0].s16X2, stMaQueSmartTarget.aPDRect[0].s16Y2};
}
if (stMaQueSmartTarget.targetFDNum > 0) {
hasHuman = true;
//last_region = {stMaQueSmartTarget.aFDRect[0].s16X1, stMaQueSmartTarget.aFDRect[0].s16Y1, stMaQueSmartTarget.aFDRect[0].s16X2, stMaQueSmartTarget.aFDRect[0].s16Y2};
if(windowSecs %2 == 0){
res = LibXmMaQue_SmartGetTarget(0, &stMaQueSmartTarget);
if (XM_SUCCESS != res) {
rlogger->error("failed LibXmMaQue_SmartGetTarget");
}
if (stMaQueSmartTarget.targetPDNum > 0) {
hasHuman = true;
last_region = {stMaQueSmartTarget.aPDRect[0].s16X1, stMaQueSmartTarget.aPDRect[0].s16Y1, stMaQueSmartTarget.aPDRect[0].s16X2, stMaQueSmartTarget.aPDRect[0].s16Y2};
}else if (stMaQueSmartTarget.targetFDNum > 0) {
hasHuman = true;
//last_region = {stMaQueSmartTarget.aFDRect[0].s16X1, stMaQueSmartTarget.aFDRect[0].s16Y1, stMaQueSmartTarget.aFDRect[0].s16X2, stMaQueSmartTarget.aFDRect[0].s16Y2};
}
}
if(windowSecs > 0) {
windowSecs--;
}
......@@ -226,7 +229,7 @@ void maq_smart_task_entry(ev_module_config_t *pArg)
if(deltaTimeMs == 0) {
/// abnormal conditions, should restart and investigation
spdlog::warn("no video frames in last ~5 seconds");
rlogger->warn("no video frames in last ~5 seconds");
frameCntTotal.store(0, std::memory_order_relaxed);
frameCntLast = 0;
}
......@@ -235,19 +238,19 @@ void maq_smart_task_entry(ev_module_config_t *pArg)
hasMotion = true;
}
if(hasMotion && hasHuman) {
spdlog::info("ai_task: deltaTimeMs {}, mpboth", deltaTimeMs);
rlogger->info("ai_task: deltaTimeMs {}, mpboth", deltaTimeMs);
fsm.process_event(md::mpboth{});
}
else if(hasMotion) {
spdlog::info("ai_task: deltaTimeMs {}, motion", deltaTimeMs);
rlogger->info("ai_task: deltaTimeMs {}, motion", deltaTimeMs);
fsm.process_event(md::motion{});
}
else if(hasHuman) {
spdlog::info("ai_task: deltaTimeMs {}, people", deltaTimeMs);
rlogger->info("ai_task: deltaTimeMs {}, people", deltaTimeMs);
fsm.process_event(md::people{});
}
else {
spdlog::info("ai_task: deltaTimeMs {}, none", deltaTimeMs);
rlogger->info("ai_task: deltaTimeMs {}, none", deltaTimeMs);
fsm.process_event(md::none{});
}
}
......@@ -261,7 +264,7 @@ void maq_smart_task_entry(ev_module_config_t *pArg)
res = LibXmMaQue_SmartDestory(0);
if (XM_SUCCESS != res) {
spdlog::error("LibXmMaQue_SmartDestory() Failed!\n");
rlogger->error("LibXmMaQue_SmartDestory() Failed!\n");
return;
}
}
do
{
ret = send(sock, pmsg, len, 0);
if (ret == -1)
{
err = getLastSocketError();
if (
(err == EWOULDBLOCK) || (err == EAGAIN)
)
{
fd_set wfd;
FD_ZERO(&wfd);
FD_SET(sock, &wfd);
timeval timeout;
timeout.tv_sec = ...;
timeout.tv_usec = ...;
ret = select(
sock+1
, NULL, &wfd, NULL, &timeout);
if (ret > 0)
continue;
if (ret == 0)
{
printf("*J* Send Timeout\n");
// handle timeout as needed ...
return;
}
err = getLastSocketError();
}
{
printf("*J* Send Failed: %d\n", err);
// handle fatal error as needed ...
return;
}
}
else
{
pmsg += ret;
len -= ret;
}
}
while (len > 0);
\ No newline at end of file
......@@ -45,7 +45,7 @@ bool is_ready(std::future<R> const& f)
void on_connlost(void *context, char *cause)
{
MqttHelper *self = (MqttHelper *) context;
spdlog::error("mqtt connection lost: {}", cause? cause: "unkown reason");
rlogger->error("mqtt connection lost: {}", cause? cause: "unkown reason");
}
int on_msgarrvd(void *context, char *topic, int topicLen, MQTTAsync_message *message)
......@@ -75,13 +75,13 @@ void on_disconn(void* context, MQTTAsync_successData* response)
MqttHelper::AsyncResult as;
as.state = MqttHelper::State::Disconnected;
self->state.set_value(as);
spdlog::info("successful disconnected {}", self->addr);
rlogger->info("successful disconnected {}", self->addr);
}
void on_subscribed(void* context, MQTTAsync_successData* response)
{
MqttHelper *self = (MqttHelper *) context;
//spdlog::debug("subscribe succeeded");
//rlogger->debug("subscribe succeeded");
MqttHelper::AsyncResult as;
as.state = MqttHelper::State::SubscribeOK;
......@@ -92,7 +92,7 @@ void on_subscribed_fail(void* context, MQTTAsync_failureData* response)
{
MqttHelper *self = (MqttHelper *) context;
//string msg = fmt::format("subscribe failed: {}", MQTTAsync_strerror(response ? response->code : 0));
// spdlog::error(msg);
// rlogger->error(msg);
MqttHelper::AsyncResult as;
as.state = MqttHelper::State::SubScribeFail;
as.rc = response?response->code:0;
......@@ -111,7 +111,7 @@ void on_unsubscribe_fail(void* context, MQTTAsync_failureData* response)
{
MqttHelper *self = (MqttHelper *) context;
string msg = fmt::format("unsubscribe failed: {}", MQTTAsync_strerror(response ? response->code : 0));
spdlog::error(msg);
rlogger->error(msg);
MqttHelper::AsyncResult as;
as.state = MqttHelper::State::UnsubscribeFail;
as.rc = response?response->code:0;
......@@ -122,7 +122,7 @@ void on_conn_fail(void* context, MQTTAsync_failureData* response)
{
MqttHelper *self = (MqttHelper *) context;
string msg = fmt::format("Connect failed: {}", MQTTAsync_strerror(response ? response->code : 0));
spdlog::error(msg);
rlogger->error(msg);
//self->state.set_exception(StrException(msg));
MqttHelper::AsyncResult as;
as.state = MqttHelper::State::ConnFail;
......@@ -133,7 +133,7 @@ void on_conn_fail(void* context, MQTTAsync_failureData* response)
void on_sent(void* context, MQTTAsync_successData* response)
{
MqttHelper *self = (MqttHelper *) context;
spdlog::debug("Message delivery confirmed, tok: {}", response->token);
rlogger->debug("Message delivery confirmed, tok: {}", response->token);
}
void on_connected(void* context, MQTTAsync_successData* response)
......@@ -142,7 +142,7 @@ void on_connected(void* context, MQTTAsync_successData* response)
MqttHelper::AsyncResult as;
as.state = MqttHelper::State::Ready;
self->state.set_value(as);
spdlog::info("connected to mqtt {}", self->addr);
rlogger->info("connected to mqtt {}", self->addr);
}
map<string, MqttHelper *> MqttMgr::insts;
......@@ -158,7 +158,7 @@ void my_on_msg1(MqttHelper *hlp, const void * const data, int len, string topic)
msg.resize(len+1);
memcpy((void*)&msg[0], data, len);
*(char*)(&msg[0] +len) = 0;
spdlog::info("I1 recv on {}: {}", topic, msg);
rlogger->info("I1 recv on {}: {}", topic, msg);
(*hlp)[to_string(stoi(topic) + 1)]("hello ack1", 11);
}
......@@ -168,7 +168,7 @@ void my_on_msg2(MqttHelper *hlp, const void * const data, int len, string topic)
msg.resize(len+1);
memcpy((void*)&msg[0], data, len);
*(char*)(&msg[0] +len) = 0;
spdlog::info("I2 recv on {}: {}", topic, msg);
rlogger->info("I2 recv on {}: {}", topic, msg);
(*hlp)[to_string(stoi(topic) + 1)]("hello ack2", 11);
}
......@@ -187,7 +187,7 @@ int main()
mqtt_cid = string(tmpEnv);
}
spdlog::set_level(spdlog::level::debug);
rlogger->set_level(rlogger->level::debug);
{
try {
MqttHelper hlp(mqtt_url, mqtt_cid);
......@@ -218,7 +218,7 @@ int main()
this_thread::sleep_for(chrono::seconds(20));
}
catch(exception &e) {
spdlog::error("failed to connect mqtt: {}, {}", mqtt_url, e.what());
rlogger->error("failed to connect mqtt: {}, {}", mqtt_url, e.what());
exit(1);
}
}
......
......@@ -30,6 +30,8 @@ using namespace evutils;
#define EV_MAX_PRINTABLE_SIZE 512
extern shared_ptr<spdlog::logger> rlogger;
extern void on_connected(void* context, MQTTAsync_successData* response);
extern void on_connlost(void *context, char *cause);
extern int on_msgarrvd(void *context, char *topic, int topicLen, MQTTAsync_message *message);
......@@ -72,7 +74,7 @@ public:
if ((rc = MQTTAsync_sendMessage(client, topic.c_str(), &pubmsg, &opts)) != MQTTASYNC_SUCCESS) {
string msg = fmt::format("Failed to start sendMessage: {}", MQTTAsync_strerror(rc));
spdlog::error(msg);
rlogger->error(msg);
throw msg;
}
return 0;
......@@ -135,17 +137,17 @@ public:
addr = "tcp://" + uri.Host + ":" + uri.Port;
}
spdlog::debug("mqtt url: proto: {}, host: {}, port: {}, user: {}, pass: {}", uri.Protocol, uri.Host, uri.Port, uri.User, uri.Password);
rlogger->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(), ("EVC"+id).c_str(), MQTTCLIENT_PERSISTENCE_NONE, NULL)) {
msg = "failed to async create mqtt";
spdlog::error(msg);
rlogger->error(msg);
throw StrException(msg);
}
if(MQTTASYNC_SUCCESS != MQTTAsync_setCallbacks(client, this, on_connlost, on_msgarrvd, NULL)) {
msg = "failed to async setcallbacks";
spdlog::error(msg);
rlogger->error(msg);
throw StrException(msg);
}
conn_opts.keepAliveInterval = 20;
......@@ -167,21 +169,21 @@ public:
conn_opts.context = this;
conn_opts.will = &lwm;
conn_opts.automaticReconnect = true;
spdlog::info("trying to connect to {}", addr);
rlogger->info("trying to connect to {}", addr);
if ((rc = MQTTAsync_connect(client, &conn_opts)) != MQTTASYNC_SUCCESS) {
msg = fmt::format("Failed to start connect: {}", MQTTAsync_strerror(rc));
spdlog::error(msg);
rlogger->error(msg);
throw StrException(msg);
}
auto ar = state.get_future().get();
if(ar.state != State::Ready) {
string msg = fmt::format("ailed to initilaze mqtt: {}", MQTTAsync_strerror(ar.rc));
spdlog::error(msg);
rlogger->error(msg);
throw StrException(msg);
}
else {
spdlog::info("initialze mqtt {} successfully", addr);
rlogger->info("initialze mqtt {} successfully", addr);
}
......@@ -209,20 +211,20 @@ public:
state= promise<AsyncResult>();
if(st.state == State::UnsubscribeOk) {
msg += ", forced uninstall ok! ";
spdlog::info(msg);
rlogger->info(msg);
}
else if(st.state == State::UnsubscribeFail) {
msg += ", froce unsintall failed! " + string(MQTTAsync_strerror(st.rc));
spdlog::error(msg);
rlogger->error(msg);
return -1;
}
else {
spdlog::error("unkown uninstall status");
rlogger->error("unkown uninstall status");
}
}
else {
msg += ", ingored since force = false";
spdlog::warn(msg);
rlogger->warn(msg);
return 0;
}
}
......@@ -233,10 +235,10 @@ public:
opts.context = this;
if ((rc = MQTTAsync_subscribe(client, topic.c_str(), 1, &opts)) != MQTTASYNC_SUCCESS) {
spdlog::error("failed to subscribe {}: {} at {}",topic, MQTTAsync_strerror(rc), addr);
rlogger->error("failed to subscribe {}: {} at {}",topic, MQTTAsync_strerror(rc), addr);
}
else {
spdlog::info("subscribe to {} success at {}", topic, addr);
rlogger->info("subscribe to {} success at {}", topic, addr);
}
auto st = state.get_future().get();
......@@ -245,7 +247,7 @@ public:
}
else if(st.state == State::SubScribeFail) {
string msg = fmt::format("failed to subscribe topic {}: {}", topic, MQTTAsync_strerror(st.rc));
spdlog::error(msg);
rlogger->error(msg);
throw StrException(msg);
}
// reset state
......@@ -267,7 +269,7 @@ public:
topics[topic](this, data, len, topic);
}
else {
spdlog::warn("no handler installed for topic: {}", topic);
rlogger->warn("no handler installed for topic: {}", topic);
}
return 0;
}
......@@ -285,7 +287,7 @@ public:
while(is_ready(fu) && fu.valid());
state = promise<AsyncResult>();
if ((rc = MQTTAsync_disconnect(client, &opts)) != MQTTASYNC_SUCCESS) {
spdlog::error("failed to start disconnect: {}", MQTTAsync_strerror(rc));
rlogger->error("failed to start disconnect: {}", MQTTAsync_strerror(rc));
}
state.get_future().get();
......@@ -326,7 +328,7 @@ class MqttMgr {
ret = MqttMgr::insts[key];
}
}catch(exception &e) {
spdlog::error("failed to create mqtt adaptor: {}", e.what());
rlogger->error("failed to create mqtt adaptor: {}", e.what());
}
return ret;
......@@ -358,7 +360,7 @@ class MqttMgr {
{
// auto client = MqttMgr::get_instance(mqtt_url, dev_sn);
if(client == nullptr) {
spdlog::error("invalid mqtt client {}");
rlogger->error("invalid mqtt client {}");
return;
}
auto rc = (*client)[topic].pub(message.c_str(), message.size(), 2, false);
......@@ -367,10 +369,10 @@ class MqttMgr {
message = message.substr(0, EV_MAX_PRINTABLE_SIZE/2 - ellipsis.size()) + ellipsis + message.substr(message.size() -1 - EV_MAX_PRINTABLE_SIZE/2, EV_MAX_PRINTABLE_SIZE/2);
}
if(rc < 0) {
spdlog::error("failed to pub mqtt message on {}: {}", topic, message);
rlogger->error("failed to pub mqtt message on {}: {}", topic, message);
}
else {
spdlog::info("successfully pub message at {}:{}", topic, message);
rlogger->info("successfully pub message at {}:{}", topic, message);
}
}
......
......@@ -19,6 +19,9 @@ link_directories(${COMMON_LIB_DIR})
add_executable(vgw videogateway.cc)
target_link_libraries(vgw PUBLIC ${VGW_LIBS} ${COMM_LIBS})
add_executable(test_proxy test_proxy.cc)
target_link_libraries(test_proxy PUBLIC ${COMM_LIBS})
# add_executable(test_mqtt test_mqtt_rd.cc)
# target_link_libraries(test_mqtt PUBLIC ${COMM_LIBS})
......
......@@ -20,6 +20,9 @@
#include <map>
#include <queue>
#include <zmq.h>
#include <jsoncons/json.hpp>
#include <thread>
#include <atomic>
extern "C"
{
......@@ -30,27 +33,29 @@ extern "C"
}
using namespace std;
#define DEFAULT_BACKLOG 128
#define MAX_FRAME_SIZE 500000
#define MAX_RETRY_PUSH_INTV 450
using namespace jsoncons;
string darwinUrl = "rtsp://evcloudsvc.ilabservice.cloud:554/";
string bindPort = "7123";
string bindAddr = "0.0.0.0";
const char *inproc = "inproc://vproxy";
void *pRouterCtx = nullptr, *pRouter = nullptr/*, *pDealerCtx = nullptr, *pDealer = nullptr*/;
typedef struct {
typedef struct packet_processor_t {
evpacket_t hdr;
int state; // 0 - unkown; 1 - receiving header; 2 - header ready, receiving body; 3 - body ready, processing
AVFormatContext *pAvCtx;
uint64_t packetId;
mutex mut;
int failedCnt = 0;
queue<uint8_t *> pktQue;
int64_t pts;
string sn;
packet_processor_t():hdr{0}, state{0}, pAvCtx(0), packetId(0), pts(0) {}
} packet_processor_t;
AVIOInterruptCB int_cb;
//AVIOInterruptCB int_cb;
std::map<string, packet_processor_t *> deviceMap;
std::map<string, mutex*> deviceLock;
recursive_mutex gMutDeviceLock;
bool is_big_endian(void)
{
......@@ -94,6 +99,17 @@ int av_callback(void *ctx)
return 0;
}
void clean_av_resources(packet_processor_t *pro)
{
if(pro->pAvCtx && pro->pAvCtx->pb) {
avio_closep(&pro->pAvCtx->pb);
}
if(pro->pAvCtx) {
avformat_free_context(pro->pAvCtx);
}
pro->pAvCtx = nullptr;
}
AVFormatContext *rtsp_init(string rtsp_url, int codec, int height, int width, int *rc = nullptr)
{
int ret;
......@@ -125,7 +141,7 @@ AVFormatContext *rtsp_init(string rtsp_url, int codec, int height, int width, in
memset(out_codecpar, 0, sizeof(AVCodecParameters));
out_codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
out_codecpar->codec_id = AV_CODEC_ID_H264;
out_codecpar->bit_rate = 200 * 100 * 8; //400000;
out_codecpar->bit_rate = 1024 * 1024; //1Mbps
out_codecpar->width = width;
out_codecpar->height = height;
out_codecpar->codec_tag = 0;
......@@ -188,73 +204,15 @@ int write_packet(packet_processor_t *processor, char *data, int len)
return ret;
}
typedef void *(*fn_worker_t)(void *ctx);
int setupRouter(void **ctx, void **s, string addr, int rcvQS=0)
void dispatch(string sn, zmq_msg_t *msg)
{
int ret = 0;
*ctx = zmq_ctx_new();
*s = zmq_socket(*ctx, ZMQ_ROUTER);
ret = 1;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE, &ret, sizeof (ret));
ret = 5;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_IDLE, &ret, sizeof (ret));
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
ret = 2;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_CNT, &ret, sizeof (ret));
if(rcvQS !=0) {
zmq_setsockopt(*s, ZMQ_RCVHWM, &rcvQS, sizeof(rcvQS));
}
ret = zmq_bind(*s, addr.c_str());
if(ret < 0) {
spdlog::debug("failed to bind zmq at {} for reason: {}, retrying load configuration...", addr, zmq_strerror(zmq_errno()));
}
return ret;
}
/// TODO: allocate new instance of processor
zmq_msg_t resp;
evpacket_t *pkt = (evpacket_t *)zmq_msg_data(msg);
int z_recv_multiple(void *s, vector<uint8_t> &buf, vector<int>&frameIdx)
{
int64_t more = 1;
size_t more_size = sizeof(more);
int ret = 0;
int cnt = 0;
while(more > 0) {
cnt++;
zmq_msg_t msg;
ret = zmq_msg_init(&msg);
if(ret < 0) {
spdlog::debug("failed to receive multiple msg on zmq_msg_init: {}", zmq_strerror(zmq_errno()));
break;
}
ret = zmq_recvmsg(s, &msg, 0);
if(ret < 0) {
spdlog::debug("z_recv_multiple: {}", zmq_strerror(zmq_errno()));
break;
}
buf.insert(buf.end(), (uint8_t*)zmq_msg_data(&msg), (uint8_t*)zmq_msg_data(&msg)+ret);
// spdlog::info("buff size: {}", buf.size());
frameIdx.push_back(buf.size());
zmq_msg_close(&msg);
ret = zmq_getsockopt(s, ZMQ_RCVMORE, &more, &more_size);
if(ret < 0) {
spdlog::debug("z_recv_multiple: {}", zmq_strerror(zmq_errno()));
break;
}
}
if(ret < 0) {
spdlog::error("failed to recv msg: {}", ret < 0? zmq_strerror(ret): "invalid frames");
return -1;
}
return 0;
}
packet_processor_t globalProcess = {0};
void dispatch(string sn, zmq_msg_t &msg)
{
evpacket_t *pkt = (evpacket_t *)zmq_msg_data(&msg);
globalProcess.packetId++;
//debugHex((char*)zmq_msg_data(&msg), 64);
auto crc = pkt->meta.crc;
......@@ -264,27 +222,88 @@ void dispatch(string sn, zmq_msg_t &msg)
spdlog::error("invalid crc: {}", pkt->meta.packet_id);
return;
}
if(globalProcess.pAvCtx == nullptr && globalProcess.packetId %15*10 == 0) {
/// we don't need lock it
// lock_guard<mutex> lg(*deviceLock[sn]);
auto processor = deviceMap[sn];
if(processor->pAvCtx == nullptr) {
int rc = 0;
globalProcess.pAvCtx = rtsp_init(darwinUrl + sn, 0, 1080, 1920, &rc);
if(globalProcess.pAvCtx == nullptr || rc < 0) {
spdlog::error("failed to connect to darwin: {}", darwinUrl);
processor->pAvCtx = rtsp_init(darwinUrl + sn, 0, pkt->vpara.res.height, pkt->vpara.res.width, &rc);
if(processor->pAvCtx == nullptr) {
spdlog::error("failed to connect to darwin {}: {}", darwinUrl, av_err2str(rc));
return;
}
else {
memcpy((void*)&processor->hdr, zmq_msg_data(msg), sizeof(processor->hdr));
}
memcpy(&globalProcess.hdr, zmq_msg_data(&msg), sizeof(globalProcess.hdr));
}
int ret = write_packet(&globalProcess, (char*)zmq_msg_data(&msg) + sizeof(evpacket_t), zmq_msg_size(&msg) - sizeof(evpacket_t) );
ret = write_packet(processor, (char*)zmq_msg_data(msg) + sizeof(evpacket_t), zmq_msg_size(msg) - sizeof(evpacket_t) );
if(ret < 0) {
spdlog::error("failed to send packet!");
if(globalProcess.pAvCtx && globalProcess.pAvCtx->pb) {
avio_closep(&globalProcess.pAvCtx->pb);
clean_av_resources(processor);
}
}
void * fn_worker(void *pArg)
{
int ret = 0;
packet_processor_t *processor = (packet_processor_t*)pArg;
spdlog::info("worker {} started", processor->sn);
zmq_msg_t *msg;
while(1) {
msg = nullptr;
{
lock_guard<mutex> lg(*deviceLock[processor->sn]);
if(processor->pktQue.size() > 0) {
msg = (zmq_msg_t *)processor->pktQue.front();
processor->pktQue.pop();
}
}
if(nullptr != msg) {
dispatch(processor->sn, msg);
zmq_msg_close(msg);
free((char*)msg);
}
else {
this_thread::sleep_for(chrono::milliseconds(200));
}
if(globalProcess.pAvCtx) {
avformat_free_context(globalProcess.pAvCtx);
}
return NULL;
}
int setupRouter(void **ctx, void **s, string addr, int rcvQS=0)
{
int ret = 0;
*ctx = zmq_ctx_new();
*s = zmq_socket(*ctx, ZMQ_ROUTER);
ret = 1;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE, &ret, sizeof (ret));
ret = 30;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_IDLE, &ret, sizeof (ret));
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_INTVL, &ret, sizeof (ret));
ret = 2;
zmq_setsockopt(*s, ZMQ_TCP_KEEPALIVE_CNT, &ret, sizeof (ret));
// ret = 10 * 1000;
// zmq_setsockopt(*s, ZMQ_SNDTIMEO, &ret, sizeof (ret));
if(rcvQS !=0) {
zmq_setsockopt(*s, ZMQ_RCVHWM, &rcvQS, sizeof(rcvQS));
}
ret = zmq_bind(*s, addr.c_str());
if(ret < 0) {
spdlog::error("failed to bind zmq at {} for reason: {}, retrying load configuration...", addr, zmq_strerror(zmq_errno()));
if(*ctx) {
if(*s) {
zmq_close(*s);
}
zmq_ctx_destroy(*ctx);
}
globalProcess.pAvCtx = nullptr;
*s = nullptr;
*ctx = nullptr;
return ret;
}
return ret;
}
int main()
......@@ -305,8 +324,7 @@ int main()
}
string addr = "tcp://0.0.0.0:7123";
void *pRouterCtx = nullptr, *pRouter = nullptr/*, *pDealerCtx = nullptr, *pDealer = nullptr*/;
if(setupRouter(&pRouterCtx, &pRouter, addr, 10)<0) {
if(setupRouter(&pRouterCtx, &pRouter, addr)<0) {
exit(1);
}
......@@ -319,14 +337,16 @@ int main()
/// receive the first frame
ret = zmq_msg_init(&msg);
if(ret < 0) {
spdlog::debug("failed to receive multiple msg on zmq_msg_init: {}", zmq_strerror(zmq_errno()));
spdlog::error("failed to receive multiple msg on zmq_msg_init: {}", zmq_strerror(zmq_errno()));
break;
}
ret = zmq_msg_recv(&msg, pRouter, 0);
if(ret < 0) {
spdlog::debug("zmq_msg_recv: {}", zmq_strerror(zmq_errno()));
spdlog::error("zmq_msg_recv: {}", zmq_strerror(zmq_errno()));
break;
}
/// first frame is sn
string sn((char*)zmq_msg_data(&msg), zmq_msg_size(&msg));
zmq_msg_close(&msg);
......@@ -338,14 +358,42 @@ int main()
}
if(more > 0) {
ret = zmq_msg_init(&msg);
ret |= zmq_msg_recv(&msg, pRouter, 0);
zmq_msg_t *msg = (zmq_msg_t*)malloc(sizeof(zmq_msg_t));
ret = zmq_msg_init(msg);
ret |= zmq_msg_recv(msg, pRouter, 0);
if(ret < 0) {
spdlog::error("error zmq_msg_recv seconds frame: {}", zmq_strerror(zmq_errno()));
continue;
}
dispatch(sn, msg);
zmq_msg_close(&msg);
/// double check
if(deviceMap.count(sn) == 0) {
lock_guard<recursive_mutex> lg(gMutDeviceLock);
if(deviceMap.count(sn) == 0) {
deviceLock.emplace(sn, new mutex());
deviceMap.emplace(sn, new packet_processor_t());
deviceMap[sn]->sn = sn;
}
}
if(deviceMap[sn]->state == 0) {
deviceMap[sn]->state = 1;
thread thWorker = thread(fn_worker, deviceMap[sn]);
if(thWorker.joinable()) {
thWorker.detach();
}
}
{
lock_guard<mutex> lg(*deviceLock[sn]);
if(deviceMap[sn]->pktQue.size() > 10) {
zmq_msg_t* m = (zmq_msg_t*) (deviceMap[sn]->pktQue.front());
zmq_msg_close(m);
free((char*)m);
deviceMap[sn]->pktQue.pop();
}
deviceMap[sn]->pktQue.push((uint8_t*)msg);
}
//zmq_msg_close(msg);
}
else {
spdlog::error("invalid packet fmt: no seconds frame");
......@@ -357,8 +405,6 @@ int main()
}
}
this_thread::sleep_for(2s);
zmq_close(pRouter);
zmq_ctx_destroy(pRouterCtx);
......
......@@ -41,7 +41,7 @@
/* Version macros for compile-time API version detection */
#define ZMQ_VERSION_MAJOR 4
#define ZMQ_VERSION_MINOR 3
#define ZMQ_VERSION_PATCH 3
#define ZMQ_VERSION_PATCH 2
#define ZMQ_MAKE_VERSION(major, minor, patch) \
((major) *10000 + (minor) *100 + (patch))
......@@ -475,7 +475,6 @@ ZMQ_EXPORT const char *zmq_msg_gets (const zmq_msg_t *msg_,
#define ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION 0x20000003
#define ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE 0x20000004
#define ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA 0x20000005
#define ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED 0x30000000
ZMQ_EXPORT void *zmq_socket (void *, int type_);
ZMQ_EXPORT int zmq_close (void *s_);
......@@ -493,15 +492,6 @@ zmq_send_const (void *s_, const void *buf_, size_t len_, int flags_);
ZMQ_EXPORT int zmq_recv (void *s_, void *buf_, size_t len_, int flags_);
ZMQ_EXPORT int zmq_socket_monitor (void *s_, const char *addr_, int events_);
/******************************************************************************/
/* Hide socket fd type; this was before zmq_poller_event_t typedef below */
/******************************************************************************/
#if defined _WIN32
typedef SOCKET zmq_fd_t;
#else
typedef int zmq_fd_t;
#endif
/******************************************************************************/
/* Deprecated I/O multiplexing. Prefer using zmq_poller API */
......@@ -515,7 +505,11 @@ typedef int zmq_fd_t;
typedef struct zmq_pollitem_t
{
void *socket;
zmq_fd_t fd;
#if defined _WIN32
SOCKET fd;
#else
int fd;
#endif
short events;
short revents;
} zmq_pollitem_t;
......@@ -696,6 +690,12 @@ ZMQ_EXPORT const char *zmq_msg_group (zmq_msg_t *msg);
#define ZMQ_HAVE_POLLER
#if defined _WIN32
typedef SOCKET zmq_fd_t;
#else
typedef int zmq_fd_t;
#endif
typedef struct zmq_poller_event_t
{
void *socket;
......
File mode changed from 100755 to 100644
libzmq @ a84ffa12
Subproject commit a84ffa12b2eb3569ced199660bac5ad128bff1f0
# libzmq.la - a libtool library file
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-2
# Generated by libtool (GNU libtool) 2.4.2 Debian-2.4.2-1.7ubuntu1
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
......@@ -13,7 +13,7 @@ library_names='libzmq.so.5.2.2 libzmq.so.5 libzmq.so'
# The name of the static archive.
old_library='libzmq.a'
# Linker flags that cannot go in dependency_libs.
# Linker flags that can not go in dependency_libs.
inherited_linker_flags=''
# Libraries that this one depends upon.
......@@ -38,4 +38,4 @@ dlopen=''
dlpreopen=''
# Directory that this library needs to be installed in:
libdir='/mnt/shared/evcamera/vendor/libzmq../x64/lib'
libdir='/mnt/shared/evcamera/vendor/x64/lib'
prefix=/mnt/shared/evcamera/vendor/libzmq../x64
prefix=/mnt/shared/evcamera/vendor/x64
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
......@@ -9,4 +9,4 @@ Version: 4.3.2
Libs: -L${libdir} -lzmq
Libs.private: -lstdc++ -lpthread -lrt
Requires.private:
Cflags: -I${includedir} -DZMQ_BUILD_DRAFT_API=1
Cflags: -I${includedir}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论