200 lines
5.3 KiB
C++
200 lines
5.3 KiB
C++
#include "snapshot_notify.h"
|
|
|
|
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <netinet/in.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
|
|
static int g_snapshot_fd = -1;
|
|
static struct sockaddr_in g_snapshot_addr;
|
|
static unsigned long g_notify_seq = 0;
|
|
|
|
static void set_reason(char *reason, int reason_size, const char *text)
|
|
{
|
|
if (reason && reason_size > 0) {
|
|
snprintf(reason, reason_size, "%s", text ? text : "unknown");
|
|
}
|
|
}
|
|
|
|
static int json_escape(char *out, int out_size, const char *in)
|
|
{
|
|
if (!out || out_size <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
int used = 0;
|
|
const char *src = in ? in : "";
|
|
for (const char *p = src; *p; ++p) {
|
|
const char *rep = NULL;
|
|
char tmp[7];
|
|
|
|
switch (*p) {
|
|
case '\\':
|
|
rep = "\\\\";
|
|
break;
|
|
case '"':
|
|
rep = "\\\"";
|
|
break;
|
|
case '\b':
|
|
rep = "\\b";
|
|
break;
|
|
case '\f':
|
|
rep = "\\f";
|
|
break;
|
|
case '\n':
|
|
rep = "\\n";
|
|
break;
|
|
case '\r':
|
|
rep = "\\r";
|
|
break;
|
|
case '\t':
|
|
rep = "\\t";
|
|
break;
|
|
default:
|
|
if ((unsigned char)*p < 0x20) {
|
|
snprintf(tmp, sizeof(tmp), "\\u%04x", (unsigned char)*p);
|
|
rep = tmp;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (rep) {
|
|
int len = strlen(rep);
|
|
if (used + len >= out_size) {
|
|
return -1;
|
|
}
|
|
memcpy(out + used, rep, len);
|
|
used += len;
|
|
} else {
|
|
if (used + 1 >= out_size) {
|
|
return -1;
|
|
}
|
|
out[used++] = *p;
|
|
}
|
|
}
|
|
out[used] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
int snapshot_notify_init(void)
|
|
{
|
|
if (g_snapshot_fd >= 0) {
|
|
return 0;
|
|
}
|
|
|
|
g_snapshot_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (g_snapshot_fd < 0) {
|
|
return -1;
|
|
}
|
|
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 20000;
|
|
setsockopt(g_snapshot_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
|
|
|
memset(&g_snapshot_addr, 0, sizeof(g_snapshot_addr));
|
|
g_snapshot_addr.sin_family = AF_INET;
|
|
g_snapshot_addr.sin_port = htons(SNAPSHOT_NOTIFY_DEFAULT_PORT);
|
|
if (inet_pton(AF_INET, SNAPSHOT_NOTIFY_DEFAULT_HOST, &g_snapshot_addr.sin_addr) != 1) {
|
|
close(g_snapshot_fd);
|
|
g_snapshot_fd = -1;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void snapshot_notify_close(void)
|
|
{
|
|
if (g_snapshot_fd >= 0) {
|
|
close(g_snapshot_fd);
|
|
g_snapshot_fd = -1;
|
|
}
|
|
}
|
|
|
|
int snapshot_notify_send(const snapshot_notify_event_t *event, char *reason, int reason_size)
|
|
{
|
|
if (!event) {
|
|
set_reason(reason, reason_size, "invalid_event");
|
|
return -1;
|
|
}
|
|
if (snapshot_notify_init() != 0) {
|
|
set_reason(reason, reason_size, "socket_init_failed");
|
|
return -1;
|
|
}
|
|
|
|
char camera_id[256];
|
|
char rtsp[1024];
|
|
char label[256];
|
|
if (json_escape(camera_id, sizeof(camera_id), event->camera_id) != 0 ||
|
|
json_escape(rtsp, sizeof(rtsp), event->rtsp) != 0 ||
|
|
json_escape(label, sizeof(label), event->label) != 0) {
|
|
set_reason(reason, reason_size, "json_escape_failed");
|
|
return -1;
|
|
}
|
|
|
|
unsigned long notify_id = ++g_notify_seq;
|
|
char notify_id_text[32];
|
|
snprintf(notify_id_text, sizeof(notify_id_text), "%lu", notify_id);
|
|
|
|
char payload[2048];
|
|
int n = snprintf(payload, sizeof(payload),
|
|
"{\"notify_id\":\"%s\",\"camera_id\":\"%s\",\"rtsp\":\"%s\",\"ts\":%ld,\"frame\":%lld,"
|
|
"\"ccid\":%d,\"label\":\"%s\",\"confidence\":%.3f,"
|
|
"\"box\":[%d,%d,%d,%d]}",
|
|
notify_id_text,
|
|
camera_id,
|
|
rtsp,
|
|
event->ts,
|
|
event->frame,
|
|
event->ccid,
|
|
label,
|
|
event->confidence,
|
|
event->box_left,
|
|
event->box_top,
|
|
event->box_right,
|
|
event->box_bottom);
|
|
if (n <= 0 || n >= (int)sizeof(payload)) {
|
|
set_reason(reason, reason_size, "payload_too_large");
|
|
return -1;
|
|
}
|
|
|
|
ssize_t sent = sendto(g_snapshot_fd, payload, n, 0,
|
|
(struct sockaddr *)&g_snapshot_addr,
|
|
sizeof(g_snapshot_addr));
|
|
if (sent != n) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "send_failed_%d", errno);
|
|
set_reason(reason, reason_size, buf);
|
|
return -1;
|
|
}
|
|
|
|
char ack[128];
|
|
ssize_t got = recv(g_snapshot_fd, ack, sizeof(ack) - 1, 0);
|
|
if (got <= 0) {
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
set_reason(reason, reason_size, "ack_timeout");
|
|
} else {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "ack_failed_%d", errno);
|
|
set_reason(reason, reason_size, buf);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
ack[got] = '\0';
|
|
char expected_ack[64];
|
|
snprintf(expected_ack, sizeof(expected_ack), "OK %s", notify_id_text);
|
|
if (strncmp(ack, expected_ack, strlen(expected_ack)) == 0) {
|
|
set_reason(reason, reason_size, "ok");
|
|
return 0;
|
|
}
|
|
|
|
set_reason(reason, reason_size, ack);
|
|
return -1;
|
|
}
|