Add blind-peek-head - blind - suckless command-line video editing utility | |
git clone git://git.suckless.org/blind | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 23100155b5d0ecbf95a8d1c4fa8d02557b69de94 | |
parent 6ca85b3362761f24f8913314bf5cfdfa35241fef | |
Author: Mattias Andrée <[email protected]> | |
Date: Wed, 23 Aug 2017 00:39:26 +0200 | |
Add blind-peek-head | |
Signed-off-by: Mattias Andrée <[email protected]> | |
Diffstat: | |
M Makefile | 3 ++- | |
M README | 3 +++ | |
M config.mk | 8 ++++---- | |
A man/blind-peek-head.1 | 28 ++++++++++++++++++++++++++++ | |
M man/blind-read-head.1 | 1 + | |
M man/blind.7 | 3 +++ | |
A src/blind-peek-head.c | 152 +++++++++++++++++++++++++++++… | |
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_… | |
- -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_… | |
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 < [email protected] > | |
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"); | |
+} |