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 <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:
Makefile | 3++-
README | 3+++
config.mk | 8++++----
man/blind-peek-head.1 | 28++++++++++++++++++++++++++++
man/blind-read-head.1 | 1+
man/blind.7 | 3+++
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_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"); +}