UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
astat.cpp
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4#include "config_unix.h"
5#include "config_win32.h"
7
8#include <stdlib.h>
9#include <stdio.h>
10#include <thread>
11#include <mutex>
12#include <algorithm>
13#include <string_view>
14
15#include "astat.h"
16
17#ifndef MSG_NOSIGNAL
18#define MSG_NOSIGNAL 0
19#endif
20
21#ifndef MSG_DONTWAIT
22#define MSG_DONTWAIT 0
23#endif
24
25
26using namespace std;
27
28static void worker(ug_connection &);
29
30static ssize_t write_all(fd_t fd, const void *buf, size_t count)
31{
32 char *p = (char *) buf;
33 size_t rest = count;
34 ssize_t w = 0;
35
36 while (rest > 0 && (w = send(fd, p, rest, MSG_NOSIGNAL)) >= 0) {
37 p += w;
38 rest -= w;
39 }
40
41 if (rest > 0)
42 return w;
43 else
44 return count;
45}
46
49 ug_connection(int f, fd_t sef[]) : fd(f), should_exit_fd{sef[0], sef[1]}, t(worker, ref(*this)) {}
50 double volpeak[CH_COUNT] = {-INFINITY, -INFINITY};
51 double volrms[CH_COUNT] = {0, 0};
52 int sample_count = 0;
53 mutex lock;
54
55 volatile bool should_exit = false;
57 thread t;
58
59 bool connection_lost = false;
60};
61
62bool astat_parse_line(const char *str, double volpeak[CH_COUNT], double volrms[CH_COUNT]){
63 std::string_view prefix = "stats ARECV ";
64 if(strncmp(prefix.data(), str, prefix.size()) != 0)
65 return false;
66
67 const char *c = str + prefix.size();
68
69 int i = 0;
70 while(*c != '\0' && i < CH_COUNT){
71 int read_chars;
72 int ret = sscanf(c, "volrms%*d %lf volpeak%*d %lf %n",
73 &volrms[i], &volpeak[i], &read_chars);
74 c += read_chars;
75 i++;
76
77 if(ret != 2)
78 return false;
79 }
80
81 return true;
82}
83
84// Line format example:
85// stats ARECV volrms0 -18.0004 volpeak0 -14.9897 volrms1 -18.0004 volpeak1 -14.9897"
86static void parse_and_store(ug_connection &c, const char *str)
87{
88 double volpeak[CH_COUNT];
89 double volrms[CH_COUNT];
90 if (!astat_parse_line(str, volpeak, volrms)) {
91#ifdef ASTAT_DEBUG
92 fprintf(stderr, "Wrong line format!");
93#endif
94 return;
95 }
96
97 lock_guard<mutex> lk(c.lock);
98 for (int i = 0; i < CH_COUNT; i++) {
99 c.volpeak[i] = std::max(c.volpeak[i], volpeak[i]);
100 c.volrms[i] = (volrms[i] * c.sample_count + volrms[i]) / (c.sample_count + 1);
101 }
102 c.sample_count += 1;
103}
104
105static void worker(ug_connection &c)
106{
107 char line[10000] = "";
108 while (!c.should_exit) {
109 char buf[9000] = "";
110
111 fd_set fds;
112 FD_ZERO(&fds);
113 FD_SET(c.fd, &fds);
114 FD_SET(c.should_exit_fd[0], &fds);
115 fd_t nfds = std::max<fd_t>(c.fd, c.should_exit_fd[0]) + 1;
116
117 int rc = select(nfds, &fds, NULL, NULL, NULL);
118 if (rc <= 0) {
119 continue;
120 }
121
122 if (FD_ISSET(c.should_exit_fd[0], &fds)) {
123 break;
124 }
125
126 ssize_t ret = recv(c.fd, buf, sizeof buf, 0);
127 if (ret == 0) { // connection was closed
128 lock_guard<mutex> lk(c.lock);
129 c.connection_lost = true;
130#ifdef ASTAT_DEBUG
131 fprintf(stderr, "Connection lost!\n");
132#endif
133 break;
134 }
135
136 if (ret < 0) {
137#ifdef _WIN32
138 int err = WSAGetLastError();
139 if(err == WSAECONNRESET){
140 lock_guard<mutex> lk(c.lock);
141 c.connection_lost = true;
142#ifdef ASTAT_DEBUG
143 fprintf(stderr, "Connection lost!\n");
144#endif
145 break;
146 }
147 printf("recv: %d \n", WSAGetLastError());
148#else
149 perror("recv");
150#endif
151 continue;
152 }
153
154 assert(ret + strlen(line) < sizeof line - 1); // TODO...
155 memcpy(line + strlen(line), buf, ret);
156 // process individual lines
157 while (strchr(line, '\n')) {
158 char *next = strchr(line, '\n') + 1;
159 *strchr(line, '\n') = '\0';
160
161 // process one line
162 if (strstr(line, "stats ARECV") == line) { // only process those lines that belong to us
163 parse_and_store(c, line);
164 }
165
166 // move the rest
167 size_t rest_len = strlen(next);
168 memmove(line, next, rest_len);
169 memset(line + rest_len, 0, sizeof line - rest_len);
170 }
171 }
172}
173
178 int fd = socket(AF_INET6, SOCK_STREAM, 0);
179 if (fd == -1) {
180 return nullptr;
181 }
183 struct sockaddr_in6 sin;
184 memset(&sin, 0, sizeof sin);
185 sin.sin6_family = AF_INET6;
186 sin.sin6_port = htons(local_port);
187 sin.sin6_addr = in6addr_loopback;
188
189 if (connect(fd, (const sockaddr*) &sin, sizeof sin) == -1) {
190 CLOSESOCKET(fd);
191 return NULL;
192 }
193
194 char stats_on[] = "stats on\r\n";
195 if (write_all(fd, stats_on, sizeof stats_on) != sizeof stats_on) {
196 CLOSESOCKET(fd);
197 return NULL;
198 }
199
200 platform_pipe_init(should_exit_fd);
201
202 return new ug_connection{fd, should_exit_fd};
203}
204
206 if(!c){
207 return;
208 }
209 c->should_exit = true;
210 char ch = 0;
211 int ret = send(c->should_exit_fd[1], &ch, 1, MSG_DONTWAIT);
212 if (ret == -1) {
213 perror("ug_control_connection_done send");
214 }
215 c->t.join();
218 CLOSESOCKET(c->fd);
219 delete c;
220}
221
227bool ug_control_get_volumes(struct ug_connection *c, double peak[], double rms[], int *count) {
228 if (!c)
229 return false;
230
231 lock_guard<mutex> lk(c->lock);
232
233 if (c->connection_lost) {
234 return false;
235 }
236
237 memcpy(peak, c->volpeak, sizeof c->volpeak);
238 memcpy(rms, c->volrms, sizeof c->volrms);
239 *count = c->sample_count;
240
241 std::fill_n(c->volpeak, sizeof(c->volpeak)/sizeof(*c->volpeak), -INFINITY);
242 memset(c->volrms, 0, sizeof c->volrms);
243 c->sample_count = 0;
244
245 return true;
246}
247
249#ifdef _WIN32
250 WSADATA wsa;
251 WSAStartup(MAKEWORD(2,2), &wsa);
252#endif
253}
254
256#ifdef _WIN32
257 WSACleanup();
258#endif
259}
260
261#ifdef DEFINE_TEST_MAIN
262#define MAX_RECONNECTS 5
263
264static bool should_exit = false;
265
266static void signal_handler(int signal) {
267 should_exit = true;
268}
269
270int main() {
271 auto connection = ug_control_connection_init(8888);
272 while (!connection) {
273 sleep(1);
274 connection = ug_control_connection_init(8888);
275 fprintf(stderr, "Unable to initialize!\n");
276 }
277 int reconnect_attempt = 0;
278
279 signal(SIGINT, signal_handler);
280 signal(SIGTERM, signal_handler);
281
282 while (!should_exit) {
283 double volpeak[CH_COUNT];
284 double volrms[CH_COUNT];
285 int sample_count;
286
287 sleep(1);
288 bool ret = ug_control_get_volumes(connection, volpeak, volrms, &sample_count);
289 if (ret) {
290 printf("samples: %d, volpeak0: %lf, volrms0: %lf, volpeak1: %lf, volrms1: %lf\n", sample_count, volpeak[0], volrms[0], volpeak[1], volrms[1]);
291 } else {
292 fprintf(stderr, "Connection was closed!\n");
293 // try to reinit
294 ug_control_connection_done(connection);
295 connection = ug_control_connection_init(8888);
296 if (!connection) {
297 fprintf(stderr, "Unable to initialize!\n");
298 if (reconnect_attempt < MAX_RECONNECTS) {
299 continue;
300 } else {
301 return 1;
302 }
303 }
304 fprintf(stderr, "Successfully reconnected.\n");
305 }
306 }
307
308 ug_control_connection_done(connection);
309 printf("Exit.\n");
310}
311#endif // DEFINE_TEST_MAIN
312
void ug_control_connection_done(struct ug_connection *c)
Definition astat.cpp:205
void ug_control_init()
Definition astat.cpp:248
static void worker(ug_connection &)
Definition astat.cpp:105
#define MSG_NOSIGNAL
Definition astat.cpp:18
bool ug_control_get_volumes(struct ug_connection *c, double peak[], double rms[], int *count)
Definition astat.cpp:227
static void parse_and_store(ug_connection &c, const char *str)
Definition astat.cpp:86
bool astat_parse_line(const char *str, double volpeak[CH_COUNT], double volrms[CH_COUNT])
Definition astat.cpp:62
#define MSG_DONTWAIT
Definition astat.cpp:22
static ssize_t write_all(fd_t fd, const void *buf, size_t count)
Definition astat.cpp:30
void ug_control_cleanup()
Definition astat.cpp:255
struct ug_connection * ug_control_connection_init(int local_port)
Definition astat.cpp:177
#define CH_COUNT
Definition astat.h:3
int fd_t
#define CLOSESOCKET
static void signal_handler(int signum)
int count
Definition hd-rum.c:44
int platform_pipe_init(fd_t p[2])
void platform_pipe_close(fd_t pipe)
int sample_count
Definition astat.cpp:52
fd_t should_exit_fd[2]
Definition astat.cpp:56
thread t
Definition astat.cpp:57
double volpeak[CH_COUNT]
Definition astat.cpp:50
ug_connection(int f, fd_t sef[])
Definition astat.cpp:49
mutex lock
Definition astat.cpp:53
double volrms[CH_COUNT]
Definition astat.cpp:51
volatile bool should_exit
Definition astat.cpp:55
bool connection_lost
Definition astat.cpp:59