X-Git-Url: http://git.meshlink.io/?p=utcp;a=blobdiff_plain;f=stream.c;fp=stream.c;h=378809cc509865e234179df6364342387786ddda;hp=0000000000000000000000000000000000000000;hb=0c3abf9e3538c482639ed32b0e85104495db5b4f;hpb=68103ae7c369fdccd59b71afb554079d9e6c798d diff --git a/stream.c b/stream.c new file mode 100644 index 0000000..378809c --- /dev/null +++ b/stream.c @@ -0,0 +1,156 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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"); + } +}