Handle channel closure during a receive callback when the ringbuffer wraps.
[utcp] / stream.c
1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <stdbool.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <string.h>
8 #include <getopt.h>
9 #include <err.h>
10 #include <time.h>
11 #include <unistd.h>
12
13 int main(int argc, char *argv[]) {
14         static const struct option longopts[] = {
15                 {"verify", 0, NULL, 'v'},
16                 {"rate", 1, NULL, 'r'},
17                 {"fps", 1, NULL, 'f'},
18                 {"total", 1, NULL, 't'},
19                 {NULL, 0, NULL, 0},
20         };
21
22         int opt;
23         bool verify = false;
24         float rate = 1e6;
25         float fps = 60;
26         float total = 1.0 / 0.0;
27
28         while((opt = getopt_long(argc, argv, "vr:f:t:", longopts, &optind)) != -1) {
29                 switch(opt) {
30                 case 'v':
31                         verify = true;
32                         break;
33
34                 case 'r':
35                         rate = atof(optarg);
36                         break;
37
38                 case 'f':
39                         fps = atof(optarg);
40                         break;
41
42                 case 't':
43                         total = atof(optarg);
44                         break;
45
46                 default:
47                         fprintf(stderr, "Usage: %s [-v] [-r bitrate] [-f frames_per_second]\n", argv[0]);
48                         return 1;
49                 }
50         }
51
52         size_t framesize = rate / fps / 8;
53         framesize &= ~0xf;
54         long interval = 1e9 / fps;
55
56         if(!framesize || interval <= 0) {
57                 err(1, "invalid parameters");
58         }
59
60         char *buf = malloc(framesize + 16);
61
62         if(!buf) {
63                 err(1, "malloc(%zu)", framesize);
64         }
65
66         uint64_t counter = 0;
67         struct timespec now, next = {0};
68         clock_gettime(CLOCK_REALTIME, &now);
69
70         while(total > 0) {
71                 if(!verify) {
72                         size_t tosend = framesize;
73                         char *p = buf;
74
75                         memcpy(buf, &now, sizeof(now));
76
77                         for(uint64_t *q = (uint64_t *)(buf + sizeof(now)); (char *)q < buf + framesize; q++) {
78                                 *q = counter++;
79                         }
80
81                         while(tosend) {
82                                 ssize_t sent = write(1, p, tosend);
83
84                                 if(sent <= 0) {
85                                         err(1, "write(1, %p, %zu)", p, tosend);
86                                 }
87
88                                 tosend -= sent;
89                                 p += sent;
90                         }
91
92                         next = now;
93                         next.tv_nsec += interval;
94
95                         while(next.tv_nsec >= 1000000000) {
96                                 next.tv_nsec -= 1000000000;
97                                 next.tv_sec++;
98                         }
99
100                         clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL);
101                         now = next;
102                         total -= framesize;
103                 } else {
104                         struct timespec *ts = (struct timespec *)buf;
105                         size_t toread = sizeof(*ts);
106                         char *p = buf;
107
108                         while(toread) {
109                                 ssize_t result = read(0, p, toread);
110
111                                 if(result <= 0) {
112                                         err(1, "read(1, %p, %zu)", p, toread);
113                                 }
114
115                                 toread -= result;
116                                 p += result;
117                         }
118
119                         clock_gettime(CLOCK_REALTIME, &now);
120
121                         toread = framesize - sizeof(now);
122
123                         while(toread) {
124                                 ssize_t result = read(0, p, toread);
125
126                                 if(result <= 0) {
127                                         err(1, "read(1, %p, %zu)", p, toread);
128                                 }
129
130                                 toread -= result;
131                                 p += result;
132                         }
133
134                         clock_gettime(CLOCK_REALTIME, &next);
135
136                         for(uint64_t *q = (uint64_t *)(buf + sizeof(now)); (char *)q < buf + framesize; q++) {
137                                 if(*q != counter++) {
138                                         uint64_t offset = (counter - 1) * 8;
139                                         offset += ((counter * 8) / (framesize - sizeof(now))) * sizeof(now);
140                                         err(1, "verification failed at offset %lu", offset);
141                                 }
142                         }
143
144                         float dt1 = now.tv_sec - ts->tv_sec + 1e-9 * (now.tv_nsec - ts->tv_nsec);
145                         float dt2 = next.tv_sec - now.tv_sec + 1e-9 * (next.tv_nsec - now.tv_nsec);
146
147                         fprintf(stderr, "\rDelay: %8.3f ms, burst bandwidth: %8.0f Mbit/s", dt1 * 1e3, (framesize - sizeof(now)) / dt2 * 8 / 1e6);
148
149                         total -= framesize;
150                 }
151         }
152
153         if(verify) {
154                 fprintf(stderr, "\n");
155         }
156 }