UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
hd-rum.c
Go to the documentation of this file.
1#include <stdlib.h>
2#include <stdio.h>
3#include <string.h>
4#include <unistd.h>
5#include <errno.h>
6#include <sys/time.h>
7#include <sys/types.h>
8#include <sys/socket.h>
9#include <sys/select.h>
10#include <arpa/inet.h>
11#include <netdb.h>
12#define __USE_GNU 1
13#include <pthread.h>
14#include <fcntl.h>
15#include <sched.h>
16
17
18#define SIZE 10000
19
20struct replica {
21 const char *host;
22 unsigned short port;
23 int sock;
24};
25
26
27struct item {
28 struct item *next;
29 long size;
30 char buf[SIZE];
31};
32
33static struct item *queue;
34static struct item *qhead;
35static struct item *qtail;
36static int qempty = 1;
37static int qfull = 0;
38static pthread_mutex_t qempty_mtx = PTHREAD_MUTEX_INITIALIZER;
39static pthread_mutex_t qfull_mtx = PTHREAD_MUTEX_INITIALIZER;
40static pthread_cond_t qempty_cond = PTHREAD_COND_INITIALIZER;
41static pthread_cond_t qfull_cond = PTHREAD_COND_INITIALIZER;
42
45
46static void qinit(int qsize)
47{
48 printf("initializing packet queue for %d items\n", qsize);
49
50 queue = (struct item *) calloc(qsize, sizeof(struct item));
51 if (queue == NULL) {
52 fprintf(stderr, "not enough memory\n");
53 exit(2);
54 }
55
56 for (int i = 0; i < qsize; i++)
57 queue[i].next = queue + i + 1;
58 queue[qsize - 1].next = queue;
59
60 qhead = qtail = queue;
61}
62
63
64static int buffer_size(int sock, int optname, int size)
65{
66 socklen_t len = sizeof(int);
67
68 if (setsockopt(sock, SOL_SOCKET, optname, (void *) &size, len)
69 || getsockopt(sock, SOL_SOCKET, optname, (void *) &size, &len)) {
70 perror("[sg]etsockopt()");
71 return -1;
72 }
73
74 printf("UDP %s buffer size set to %d bytes\n",
75 (optname == SO_SNDBUF) ? "send" : "receiver", size);
76
77 return 0;
78}
79
80
81static int output_socket(unsigned short port, const char *host, int bufsize)
82{
83 int s;
84 struct addrinfo hints;
85 struct addrinfo *res;
86 char saddr[INET_ADDRSTRLEN];
87 char p[6];
88
89 memset(&hints, '\0', sizeof(hints));
90 hints.ai_flags = AI_CANONNAME;
91 hints.ai_socktype = SOCK_DGRAM;
92 hints.ai_protocol = IPPROTO_UDP;
93 hints.ai_family = PF_INET;
94
95 snprintf(p, 6, "%d", port);
96 p[5] = '\0';
97
98 if (getaddrinfo(host, p, &hints, &res)) {
99 int err = errno;
100 if (err == 0)
101 fprintf(stderr, "Address not found: %s\n", host);
102 else
103 fprintf(stderr, "%s: %s\n", gai_strerror(err), host);
104 exit(2);
105 }
106
107 inet_ntop(AF_INET, &((struct sockaddr_in *) res->ai_addr)->sin_addr,
108 saddr, sizeof(saddr));
109 printf("connecting to %s (%s) port %d\n",
110 res->ai_canonname, saddr, port);
111
112 if ((s = socket(res->ai_family, res->ai_socktype, 0)) == -1) {
113 perror("socket");
114 exit(2);
115 }
116
117 if (buffer_size(s, SO_SNDBUF, bufsize))
118 exit(2);
119
120 if (connect(s, res->ai_addr, res->ai_addrlen)) {
121 perror("connect");
122 exit(2);
123 }
124
125 return s;
126}
127
128
129static void *writer(void *arg)
130{
131 (void)arg;
132
133 while (1) {
134 while (qhead != qtail) {
135 for (int i = 0; i < count; i++)
136 write(replicas[i].sock, qhead->buf, qhead->size);
137
138 qhead = qhead->next;
139
140 pthread_mutex_lock(&qfull_mtx);
141 qfull = 0;
142 pthread_cond_signal(&qfull_cond);
143 pthread_mutex_unlock(&qfull_mtx);
144 }
145
146 pthread_mutex_lock(&qempty_mtx);
147 if (qempty)
148 pthread_cond_wait(&qempty_cond, &qempty_mtx);
149 qempty = 1;
150 pthread_mutex_unlock(&qempty_mtx);
151 }
152
153 return NULL;
154}
155
156
157int main(int argc, char **argv)
158{
159 unsigned short port;
160 int qsize;
161 int bufsize;
162 struct sockaddr_in addr;
163 int sock_in;
164 pthread_t thread;
165 int err = 0;
166 int i;
167
168 if (argc < 4) {
169 fprintf(stderr, "%s buffer_size port host...\n", argv[0]);
170 return 1;
171 }
172
173 if ((bufsize = atoi(argv[1])) <= 0) {
174 fprintf(stderr, "invalid buffer size: %d\n", bufsize);
175 return 1;
176 }
177 switch (argv[1][strlen(argv[1]) - 1]) {
178 case 'K':
179 case 'k':
180 bufsize *= 1024;
181 break;
182
183 case 'M':
184 case 'm':
185 bufsize *= 1024*1024;
186 break;
187 }
188
189 qsize = bufsize / 8000;
190
191 printf("using UDP send and receive buffer size of %d bytes\n", bufsize);
192
193 if ((port = atoi(argv[2])) <= 0) {
194 fprintf(stderr, "invalid port: %d\n", port);
195 return 1;
196 }
197
198 qinit(qsize);
199
200 /* input socket */
201 if ((sock_in = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
202 perror("input socket");
203 return 2;
204 }
205
206 if (buffer_size(sock_in, SO_RCVBUF, bufsize))
207 exit(2);
208
209 addr.sin_family = AF_INET;
210 addr.sin_addr.s_addr = INADDR_ANY;
211 addr.sin_port = htons(port);
212 if (bind(sock_in, (struct sockaddr *) &addr,
213 sizeof(struct sockaddr_in))) {
214 perror("bind");
215 return 2;
216 }
217
218 printf("listening on *:%d\n", port);
219
220 /* output socket(s) */
221 count = argc - 3;
222 replicas = (struct replica *) calloc(count, sizeof(struct replica));
223 if (replicas == NULL) {
224 fprintf(stderr, "not enough memory for replica array");
225 return 2;
226 }
227
228 for (i = 0; i < count; i++) {
229 replicas[i].host = argv[3 + i];
230
231 if (i > 0 && strcmp(replicas[i - 1].host, replicas[i].host) == 0)
232 replicas[i].port = replicas[i - 1].port + 1;
233 else
234 replicas[i].port = port;
235
237 bufsize);
238 }
239
240 if (pthread_create(&thread, NULL, writer, NULL)) {
241 fprintf(stderr, "cannot create writer thread\n");
242 return 2;
243 }
244
245 /* main loop */
246 while (1) {
247 fcntl(sock_in, F_SETFL, O_NONBLOCK);
248
249 while (qtail->next != qhead
250 && ((qtail->size = read(sock_in, qtail->buf, SIZE)) > 0
251 || (err = errno) == EAGAIN)) {
252
253 if (qtail->size <= 0) {
254 sched_yield();
255 }
256 else {
257 qtail = qtail->next;
258
259 pthread_mutex_lock(&qempty_mtx);
260 qempty = 0;
261 pthread_cond_signal(&qempty_cond);
262 pthread_mutex_unlock(&qempty_mtx);
263 }
264 }
265
266 if (qtail->size <= 0)
267 break;
268
269 pthread_mutex_lock(&qfull_mtx);
270 if (qfull)
271 pthread_cond_wait(&qfull_cond, &qfull_mtx);
272 qfull = 1;
273 pthread_mutex_unlock(&qfull_mtx);
274 }
275
276 if (qtail->size < 0) {
277 printf("read: %s\n", strerror(err));
278 return 2;
279 }
280
281 return 0;
282}
283
int void size_t * len
static void qinit(int qsize)
Definition hd-rum.c:46
static pthread_mutex_t qfull_mtx
Definition hd-rum.c:39
static pthread_cond_t qempty_cond
Definition hd-rum.c:40
static void * writer(void *arg)
Definition hd-rum.c:129
int count
Definition hd-rum.c:44
struct replica * replicas
Definition hd-rum.c:43
static int buffer_size(int sock, int optname, int size)
Definition hd-rum.c:64
static struct item * queue
Definition hd-rum.c:33
#define SIZE
Definition hd-rum.c:18
static pthread_mutex_t qempty_mtx
Definition hd-rum.c:38
static struct item * qtail
Definition hd-rum.c:35
static int qfull
Definition hd-rum.c:37
static int qempty
Definition hd-rum.c:36
static int output_socket(unsigned short port, const char *host, int bufsize)
Definition hd-rum.c:81
static pthread_cond_t qfull_cond
Definition hd-rum.c:41
static struct item * qhead
Definition hd-rum.c:34
Definition hd-rum.c:27
long size
Definition hd-rum.c:29
struct item * next
Definition hd-rum.c:28
char buf[SIZE]
Definition hd-rum.c:30
int sock
Definition hd-rum.c:23
const char * host
Definition hd-rum.c:21
unsigned short port
Definition hd-rum.c:22