#include "snapshot_notify.h" #include #include #include #include #include #include #include #include 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; }