]> git.meshlink.io Git - utcp/blobdiff - stream.c
Add a tool to generate a stream with a configurable bitrate.
[utcp] / stream.c
diff --git a/stream.c b/stream.c
new file mode 100644 (file)
index 0000000..378809c
--- /dev/null
+++ b/stream.c
@@ -0,0 +1,156 @@
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <getopt.h>
+#include <err.h>
+#include <time.h>
+#include <unistd.h>
+
+int main(int argc, char *argv[]) {
+       static const struct option longopts[] = {
+               {"verify", 0, NULL, 'v'},
+               {"rate", 1, NULL, 'r'},
+               {"fps", 1, NULL, 'f'},
+               {"total", 1, NULL, 't'},
+               {NULL, 0, NULL, 0},
+       };
+
+       int opt;
+       bool verify = false;
+       float rate = 1e6;
+       float fps = 60;
+       float total = 1.0 / 0.0;
+
+       while((opt = getopt_long(argc, argv, "vr:f:t:", longopts, &optind)) != -1) {
+               switch(opt) {
+               case 'v':
+                       verify = true;
+                       break;
+
+               case 'r':
+                       rate = atof(optarg);
+                       break;
+
+               case 'f':
+                       fps = atof(optarg);
+                       break;
+
+               case 't':
+                       total = atof(optarg);
+                       break;
+
+               default:
+                       fprintf(stderr, "Usage: %s [-v] [-r bitrate] [-f frames_per_second]\n", argv[0]);
+                       return 1;
+               }
+       }
+
+       size_t framesize = rate / fps / 8;
+       framesize &= ~0xf;
+       long interval = 1e9 / fps;
+
+       if(!framesize || interval <= 0) {
+               err(1, "invalid parameters");
+       }
+
+       char *buf = malloc(framesize + 16);
+
+       if(!buf) {
+               err(1, "malloc(%zu)", framesize);
+       }
+
+       uint64_t counter = 0;
+       struct timespec now, next = {0};
+       clock_gettime(CLOCK_REALTIME, &now);
+
+       while(total > 0) {
+               if(!verify) {
+                       size_t tosend = framesize;
+                       char *p = buf;
+
+                       memcpy(buf, &now, sizeof(now));
+
+                       for(uint64_t *q = (uint64_t *)(buf + sizeof(now)); (char *)q < buf + framesize; q++) {
+                               *q = counter++;
+                       }
+
+                       while(tosend) {
+                               ssize_t sent = write(1, p, tosend);
+
+                               if(sent <= 0) {
+                                       err(1, "write(1, %p, %zu)", p, tosend);
+                               }
+
+                               tosend -= sent;
+                               p += sent;
+                       }
+
+                       next = now;
+                       next.tv_nsec += interval;
+
+                       while(next.tv_nsec >= 1000000000) {
+                               next.tv_nsec -= 1000000000;
+                               next.tv_sec++;
+                       }
+
+                       clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL);
+                       now = next;
+                       total -= framesize;
+               } else {
+                       struct timespec *ts = (struct timespec *)buf;
+                       size_t toread = sizeof(*ts);
+                       char *p = buf;
+
+                       while(toread) {
+                               ssize_t result = read(0, p, toread);
+
+                               if(result <= 0) {
+                                       err(1, "read(1, %p, %zu)", p, toread);
+                               }
+
+                               toread -= result;
+                               p += result;
+                       }
+
+                       clock_gettime(CLOCK_REALTIME, &now);
+
+                       toread = framesize - sizeof(now);
+
+                       while(toread) {
+                               ssize_t result = read(0, p, toread);
+
+                               if(result <= 0) {
+                                       err(1, "read(1, %p, %zu)", p, toread);
+                               }
+
+                               toread -= result;
+                               p += result;
+                       }
+
+                       clock_gettime(CLOCK_REALTIME, &next);
+
+                       for(uint64_t *q = (uint64_t *)(buf + sizeof(now)); (char *)q < buf + framesize; q++) {
+                               if(*q != counter++) {
+                                       uint64_t offset = (counter - 1) * 8;
+                                       offset += ((counter * 8) / (framesize - sizeof(now))) * sizeof(now);
+                                       err(1, "verification failed at offset %lu", offset);
+                               }
+                       }
+
+                       float dt1 = now.tv_sec - ts->tv_sec + 1e-9 * (now.tv_nsec - ts->tv_nsec);
+                       float dt2 = next.tv_sec - now.tv_sec + 1e-9 * (next.tv_nsec - now.tv_nsec);
+
+                       fprintf(stderr, "\rDelay: %8.3f ms, burst bandwidth: %8.0f Mbit/s", dt1 * 1e3, (framesize - sizeof(now)) / dt2 * 8 / 1e6);
+
+                       total -= framesize;
+               }
+       }
+
+       if(verify) {
+               fprintf(stderr, "\n");
+       }
+}