commit 23100155b5d0ecbf95a8d1c4fa8d02557b69de94
parent 6ca85b3362761f24f8913314bf5cfdfa35241fef
Author: Mattias Andrée <maandree@kth.se>
Date: Wed, 23 Aug 2017 00:39:26 +0200
Add blind-peek-head
Signed-off-by: Mattias Andrée <maandree@kth.se>
Diffstat:
7 files changed, 193 insertions(+), 5 deletions(-)
diff --git a/Makefile b/Makefile
@@ -60,8 +60,9 @@ BIN =\
blind-multiply-matrices\
blind-next-frame\
blind-norm\
- blind-quaternion-product\
+ blind-peek-head\
blind-premultiply\
+ blind-quaternion-product\
blind-radial-gradient\
blind-read-head\
blind-rectangle-tessellation\
diff --git a/README b/README
@@ -177,6 +177,9 @@ UTILITIES
blind-norm(1)
Calculate the norm of colours in a video
+ blind-peek-head
+ Peeks the head from a video
+
blind-premultiply(1)
Premultiply the alpha channel of a video
diff --git a/config.mk b/config.mk
@@ -13,9 +13,9 @@ KORN_SHELL = bash
# Commands
LN = ln -s
-# You may want to remove -DHAVE_PRCTL, -DHAVE_EPOLL, and
-# -DHAVE_SENDFILE from CPPFLAGS if you are not using Linux.
+# You may want to remove -DHAVE_PRCTL, -DHAVE_EPOLL, -DHAVE_TEE,
+# and -DHAVE_SENDFILE from CPPFLAGS if you are not using Linux.
CFLAGS = -std=c11 -Wall -pedantic -O2
-CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64\
- -DHAVE_PRCTL -DHAVE_EPOLL -DHAVE_SENDFILE
+CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE \
+ -D_FILE_OFFSET_BITS=64 -DHAVE_PRCTL -DHAVE_EPOLL -DHAVE_TEE -DHAVE_SENDFILE
LDFLAGS = -lm -s
diff --git a/man/blind-peek-head.1 b/man/blind-peek-head.1
@@ -0,0 +1,28 @@
+.TH BLIND-PEEK-HEAD 1 blind
+.SH NAME
+blind-peek-head - Peeks the head from a video
+.SH SYNOPSIS
+.B blind-peek-head
+.SH DESCRIPTION
+.B blind-peek-head
+peeks the head a video from stdin, and
+prints it, without the magic number, to stdout.
+The output will contain: the number of frames,
+<space>, the width, <space>, the height, <space>,
+and the pixel format. The output is text, and
+thus ends with a <newline>. The state of stdin
+will remain unchanged.
+.SH NOTES
+.B blind-peek-head
+requires that stdin is a socket, a regular file,
+or a (on Linux only) a pipe. Direct pipes and
+datagram sockets are supported but require that
+the entire head must be written in one write.
+.SH SEE ALSO
+.BR blind (7),
+.BR blind-read-head (1),
+.BR blind-write-head (1),
+.BR blind-next-frame (1)
+.SH AUTHORS
+Mattias Andrée
+.RI < maandree@kth.se >
diff --git a/man/blind-read-head.1 b/man/blind-read-head.1
@@ -18,6 +18,7 @@ can be used to get the first, and
any following, frame from stdin.
.SH SEE ALSO
.BR blind (7),
+.BR blind-peek-head (1),
.BR blind-write-head (1),
.BR blind-next-frame (1)
.SH AUTHORS
diff --git a/man/blind.7 b/man/blind.7
@@ -195,6 +195,9 @@ Extracts the next frame from a video
.BR blind-norm (1)
Calculate the norm of colours in a video
.TP
+.BR blind-peek-head (1)
+Peeks the head from a video
+.TP
.BR blind-premultiply (1)
Premultiply the alpha channel of a video
.TP
diff --git a/src/blind-peek-head.c b/src/blind-peek-head.c
@@ -0,0 +1,152 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("")
+
+static ssize_t
+peek_socket(char *buf, size_t n)
+{
+ ssize_t r = recv(STDIN_FILENO, buf, n, MSG_PEEK);
+ if (r < 0 && errno != ENOTSOCK)
+ eprintf("recv <stdin>:");
+ return r;
+}
+
+static ssize_t
+peek_regular(char *buf, size_t n)
+{
+ ssize_t r;
+ off_t pos = lseek(STDIN_FILENO, 0, SEEK_CUR);
+ if (pos < 0) {
+ if (errno != ESPIPE)
+ eprintf("lseek <stdin>:");
+ return -1;
+ }
+ r = pread(STDIN_FILENO, buf, n, pos);
+ if (r < 0 && errno != ESPIPE)
+ eprintf("pread <stdin>:");
+ return r;
+}
+
+#if defined(HAVE_TEE)
+static ssize_t
+peek_pipe(char *buf, size_t n)
+{
+ int rw[2];
+ ssize_t m;
+ size_t p;
+ if (pipe(rw))
+ eprintf("pipe");
+ m = tee(STDIN_FILENO, rw[1], n, 0);
+ if (m < 0) {
+ if (errno != EINVAL)
+ eprintf("tee <stdin>:");
+ return -1;
+ }
+ close(rw[1]);
+ p = ereadall(rw[0], buf, (size_t)m, "<pipe>");
+ close(rw[0]);
+ return (ssize_t)p;
+}
+#endif
+
+static size_t
+peek(char *buf, size_t n)
+{
+ static int method = 0;
+ ssize_t r;
+ switch (method) {
+ case 0:
+ if ((r = peek_socket(buf, n)) >= 0)
+ return (size_t)r;
+ method++;
+ /* fall-through */
+ case 1:
+ if ((r = peek_regular(buf, n)) >= 0)
+ return (size_t)r;
+ method++;
+#if defined(HAVE_TEE)
+ /* fall-through */
+ default:
+ if ((r = peek_pipe(buf, n)) >= 0)
+ return (size_t)r;
+ eprintf("can only peek pipes, sockets, and regular files\n");
+#else
+ eprintf("can only peek sockets and regular files\n");
+#endif
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buf[STREAM_HEAD_MAX], *p;
+ char magic[] = {'\0', 'u', 'i', 'v', 'f'};
+ size_t i, len = 0, last_len;
+#if defined(HAVE_EPOLL)
+ struct epoll_event ev;
+ int epfd, epr = 0;
+#endif
+
+ UNOFLAGS(argc);
+
+#if defined(HAVE_EPOLL)
+ epfd = epoll_create1(0);
+ if (epfd < 0)
+ eprintf("epoll_create1:");
+
+ memset(&ev, 0, sizeof(ev));
+ ev.events = EPOLLIN | EPOLLRDHUP | EPOLLET;
+ if (epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev))
+ eprintf("epoll_ctl EPOLL_CTL_ADD:");
+
+ do {
+ last_len = len;
+ len = peek(buf, sizeof(buf));
+ p = memchr(buf, '\n', len);
+ if (p && len >= (size_t)(++p - buf) + ELEMENTSOF(magic))
+ goto ready;
+ } while (len > last_len && (epr = epoll_wait(epfd, &ev, 1, -1)) >= 0);
+ if (epr < 0)
+ eprintf("epoll_wait:");
+#else
+ goto beginning;
+ do {
+ usleep(50000);
+ beginning:
+ last_len = len;
+ len = peek(buf, n);
+ p = memchr(buf, '\n', len);
+ if (p && len >= (size_t)(++p - buf) + ELEMENTSOF(magic))
+ goto ready;
+ } while (len > last_len);
+#endif
+ eprintf("could not read entire head\n");
+
+ready:
+ len = (size_t)(p - buf);
+ for (i = 0; i < ELEMENTSOF(magic); i++)
+ if (p[i] != magic[i])
+ goto bad_format;
+ p = buf;
+ for (i = 0; i < 3; i++) {
+ if (!isdigit(*p))
+ goto bad_format;
+ while (isdigit(*p)) p++;
+ if (*p++ != ' ')
+ goto bad_format;
+ }
+ while (isalnum(*p) || *p == ' ') {
+ if (p[0] == ' ' && p[-1] == ' ')
+ goto bad_format;
+ p++;
+ }
+ if (p[-1] == ' ' || p[0] != '\n')
+ goto bad_format;
+
+ ewriteall(STDOUT_FILENO, buf, len, "<stdout>");
+ return 0;
+
+bad_format:
+ eprintf("<stdin>: file format not supported\n");
+}