blind

suckless command-line video editing utility
git clone git://git.suckless.org/blind
Log | Files | Refs | README | LICENSE

commit 0804a80954bf9b8c11392d77a4933ec0a4ff635b
parent 478b53f935264bdfe4efe394f8d804a1361a6770
Author: Mattias Andrée <maandree@kth.se>
Date:   Sat,  8 Apr 2017 23:02:48 +0200

Add blind-translate and fix errors

Signed-off-by: Mattias Andrée <maandree@kth.se>

Diffstat:
MMakefile | 1+
MTODO | 1-
Mman/blind-arithm.1 | 2+-
Mman/blind-crop.1 | 3++-
Mman/blind-extend.1 | 3++-
Mman/blind-gauss-blur.1 | 2+-
Mman/blind-invert-luma.1 | 2+-
Mman/blind-set-alpha.1 | 2+-
Mman/blind-set-luma.1 | 2+-
Mman/blind-set-saturation.1 | 2+-
Mman/blind-time-blur.1 | 2+-
Aman/blind-translate.1 | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/blind-translate.c | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/stream.c | 12++++++++----
Msrc/util.c | 12++++++++++++
Msrc/util/io.h | 16+++++++++++++---
16 files changed, 279 insertions(+), 17 deletions(-)

diff --git a/Makefile b/Makefile @@ -34,6 +34,7 @@ BIN =\ blind-to-image\ blind-to-text\ blind-to-video\ + blind-translate\ blind-transpose\ blind-write-head diff --git a/TODO b/TODO @@ -13,7 +13,6 @@ blind-affine-colour apply an affine transformation to the colour of each pixel, -a for ignoring the alpha channel, -l for linear transformation, -p for transforming each pixel with their own transformation. -blind-translate animated translation blind-invert-chroma invert the chroma blind-skip-pattern discard frames according to pattern, e.g. every other frame blind-from-sent convert a sent presentation to a one-frame-per-slide blind video diff --git a/man/blind-arithm.1 b/man/blind-arithm.1 @@ -21,7 +21,7 @@ on the two videos. .P If stdin is longer than .IR right-hand-stream , -the remainder or stdin is printed without any changes. +the remainder of stdin is printed without any changes. If stdin is shorter than .IR right-hand-stream , the remainder of diff --git a/man/blind-crop.1 b/man/blind-crop.1 @@ -66,7 +66,8 @@ has not been optimised for memory usage, but instead for code simplicity. .SH SEE ALSO .BR blind (7), -.BR blind-extend (1) +.BR blind-extend (1), +.BR blind-translate (1) .SH AUTHORS Mattias Andrée .RI < maandree@kth.se > diff --git a/man/blind-extend.1 b/man/blind-extend.1 @@ -63,7 +63,8 @@ has not been optimised for memory usage, but instead for code simplicity. .SH SEE ALSO .BR blind (7), -.BR blind-crop (1) +.BR blind-crop (1), +.BR blind-translate (1) .SH AUTHORS Mattias Andrée .RI < maandree@kth.se > diff --git a/man/blind-gauss-blur.1 b/man/blind-gauss-blur.1 @@ -29,7 +29,7 @@ deviation is 0, the pixel is not blurred. .P If stdin is longer than .IR sd-stream , -the remainder or stdin is printed without any changes. +the remainder of stdin is printed without any changes. If stdin is shorter than .IR sd-stream , the remainder of diff --git a/man/blind-invert-luma.1 b/man/blind-invert-luma.1 @@ -19,7 +19,7 @@ linearly extrapolated. .P If stdin is longer than .IR mask-stream , -the remainder or stdin is printed without any changes. +the remainder of stdin is printed without any changes. If stdin is shorter than .IR mask-stream , the remainder of diff --git a/man/blind-set-alpha.1 b/man/blind-set-alpha.1 @@ -16,7 +16,7 @@ the mask video. .P If stdin is longer than .IR alpha-stream , -the remainder or stdin is printed without any changes. +the remainder of stdin is printed without any changes. If stdin is shorter than .IR alpha-stream , the remainder of diff --git a/man/blind-set-luma.1 b/man/blind-set-luma.1 @@ -15,7 +15,7 @@ pixel and frame in the mask video. .P If stdin is longer than .IR luma-stream , -the remainder or stdin is printed without any changes. +the remainder of stdin is printed without any changes. If stdin is shorter than .IR luma-stream , the remainder of diff --git a/man/blind-set-saturation.1 b/man/blind-set-saturation.1 @@ -16,7 +16,7 @@ pixel and frame in the mask video. .P If stdin is longer than .IR saturation-stream , -the remainder or stdin is printed without any changes. +the remainder of stdin is printed without any changes. If stdin is shorter than .IR saturation-stream , the remainder of diff --git a/man/blind-time-blur.1 b/man/blind-time-blur.1 @@ -18,7 +18,7 @@ in the mask video, and then printed stdout. .P If stdin is longer than .IR alpha-stream , -the remainder or stdin is printed without any changes. +the remainder of stdin is printed without any changes. If stdin is shorter than .IR alpha-stream , the remainder of diff --git a/man/blind-translate.1 b/man/blind-translate.1 @@ -0,0 +1,50 @@ +.TH BLIND-TRANSLATE 1 blind +.SH NAME +blind-translate - Perform framewise translate of a video +.SH SYNOPSIS +.B blind-translate +[-pw] +.I translation-stream +.SH DESCRIPTION +.B blind-translate +reads a video from stdin and translation operands from +.IR translation-stream , +and perform framewise translation of the video and +prints the resulting video to stdout. +.P +.I translation-stream +is a video that is 1 pixel wide and 1 pixel high. +The first pixel value in a frame is used as the +translation value for the X-axis in the same frame +in the video, and the second pixel value is used +as the translation value for the Y-axis. The +remaining two pixel values are ignored. +.P +If stdin is longer than +.IR translation-stream , +the remainder of stdin is printed with the last translation +or no translation if +.I translation-stream +does not contain any translations. If stdin is shorter than +.IR translation-stream , +the remainder of +.I translation-stream +is ignored but may be partially read. +.SH OPTIONS +.TP +.B -w +Wrap video around the edges. +.TP +.B -p +The values in +.I translation-stream +is to be treat as the position, measured form the +top-left corner, in the input video that shall be +used at the top-left corner of the output video. +.SH SEE ALSO +.BR blind (7), +.BR blind-crop (1), +.BR blind-extend (1) +.SH AUTHORS +Mattias Andrée +.RI < maandree@kth.se > diff --git a/src/blind-translate.c b/src/blind-translate.c @@ -0,0 +1,184 @@ +/* See LICENSE file for copyright and license details. */ +#include "stream.h" +#include "util.h" + +#include <fcntl.h> +#include <inttypes.h> +#include <math.h> +#include <string.h> +#include <unistd.h> + +USAGE("[-wp] translation-stream") + +static int invtrans = 0; +static char zeroes[BUFSIZ]; + +static void* +next_pixel(struct stream *stream, size_t *ptr) +{ + void *ret; + if (*ptr + stream->pixel_size >= stream->ptr) { + memmove(stream->buf, stream->buf + *ptr, stream->ptr -= *ptr); + *ptr = 0; + while (stream->ptr < stream->pixel_size) + if (!eread_stream(stream, SIZE_MAX)) + return NULL; + } + ret = stream->buf + *ptr; + *ptr += stream->pixel_size; + return ret; +} + +static int +process_frame(struct stream *stream, char *buf, size_t n, + size_t above, size_t below, size_t left, size_t right) +{ +#define ZEROES(N) ewritezeroes(STDOUT_FILENO, zeroes, sizeof(zeroes), N, "<stdout>") + + size_t i, w = n - left - right; + int first = 1; + + if (!eread_row(stream, buf, n)) + return 0; + + for (i = 0; i < above; i++) + ZEROES(n); + for (i = 0; i < below; i++, first = 0) + if (!first && !eread_row(stream, buf, n)) + goto eof; + + for (i = above + below; i < stream->height; i++, first = 0) { + if (!first && !eread_row(stream, buf, n)) + goto eof; + ZEROES((size_t)left); + ewriteall(STDOUT_FILENO, buf + right, w, "<stdout>"); + ZEROES((size_t)right); + } + + for (i = 0; i < below; i++) + ZEROES(n); + for (i = 0; i < above; i++, first = 0) + if (!first && !eread_row(stream, buf, n)) + goto eof; + + return 1; +eof: + eprintf("%s: file is shorter than expected\n", stream->file); + return 0; + +#undef ZEROES +} + +static void +process(struct stream *stream, struct stream *trstream) +{ + char *buf; + size_t n, p = 0; + double *trans; + ssize_t trx = 0, try = 0; + size_t above = 0, below = 0, left = 0, right = 0; + + memset(zeroes, 0, sizeof(zeroes)); + + if (!check_frame_size(stream->width, 1, stream->pixel_size)) + eprintf("%s: video frame is too wide\n", stream->file); + n = stream->width * stream->pixel_size; + buf = emalloc(n); + + do { + if ((trans = next_pixel(trstream, &p))) { + trx = (ssize_t)round(invtrans ? -trans[0] : trans[0]); + try = (ssize_t)round(invtrans ? -trans[1] : trans[1]); + + above = try > 0 ? (size_t)try : 0; + below = try < 0 ? (size_t)-try : 0; + left = (trx > 0 ? (size_t)trx : 0) * stream->pixel_size; + right = (trx < 0 ? (size_t)-trx : 0) * stream->pixel_size; + + above = above < stream->height ? above : stream->height; + below = below < stream->height ? below : stream->height; + left = left < n ? left : n; + right = right < n ? right : n; + } + } while (process_frame(stream, buf, n, above, below, left, right)); + + free(buf); +} + +static void +process_wrap(struct stream *stream, struct stream *trstream) +{ + char *buf, *row; + size_t n, rown, p = 0; + double *trans; + ssize_t trx = 0, try = 0, py; + size_t off = 0, y; + + echeck_frame_size(stream->width, stream->height, stream->pixel_size, 0, "<stdin>"); + n = stream->height * (rown = stream->width * stream->pixel_size); + buf = emalloc(n); + + while (eread_frame(stream, buf, n)) { + if ((trans = next_pixel(trstream, &p))) { + trx = (ssize_t)round(invtrans ? -trans[0] : trans[0]); + try = (ssize_t)round(invtrans ? -trans[1] : trans[1]); + trx %= (ssize_t)stream->width; + if (trx < 0) + trx += (ssize_t)stream->width; + off = (stream->width - (size_t)trx) % stream->width; + off = off * stream->pixel_size; + } + + for (y = 0; y < stream->height; y++) { + py = ((ssize_t)y - try) % (ssize_t)stream->height; + if (py < 0) + py += (ssize_t)stream->height; + row = buf + (size_t)py * rown; + ewriteall(STDOUT_FILENO, row + off, rown - off, "<stdout>"); + ewriteall(STDOUT_FILENO, row, off, "<stdout>"); + } + } + + free(buf); +} + +int +main(int argc, char *argv[]) +{ + struct stream stream; + struct stream trstream; + int wrap = 0; + + ARGBEGIN { + case 'w': + wrap = 1; + break; + case 'p': + invtrans = 1; + break; + } ARGEND; + + if (argc != 1) + usage(); + + stream.file = "<stdin>"; + stream.fd = STDIN_FILENO; + einit_stream(&stream); + fprint_stream_head(stdout, &stream); + efflush(stdout, "<stdout>"); + + trstream.file = argv[0]; + trstream.fd = eopen(trstream.file, O_RDONLY); + einit_stream(&trstream); + + if (trstream.width != 1 || trstream.height != 1) + eprintf("translation-stream does not have 1x1 geometry\n"); + + if (strcmp(trstream.pixfmt, "xyza")) + eprintf("pixel format of translation-stream %s " + "is not supported, try xyza\n", trstream.pixfmt); + + (wrap ? process_wrap : process)(&stream, &trstream); + close(trstream.fd); + return 0; +} diff --git a/src/stream.c b/src/stream.c @@ -161,7 +161,7 @@ encheck_frame_size(int status, size_t width, size_t height, size_t pixel_size, c { if (!check_frame_size(width, height, pixel_size)) enprintf(status, "%s: %s%svideo frame is too large\n", - prefix ? prefix : "", (prefix && *prefix) ? " " : "", fname); + fname, prefix ? prefix : "", (prefix && *prefix) ? " " : ""); } @@ -218,9 +218,10 @@ nprocess_each_frame_segmented(int status, struct stream *stream, int output_fd, for (frame = 0; frame < stream->frames; frame++) { for (n = frame_size; n; n -= r) { - if (!enread_stream(status, stream, n)) + if (stream->ptr < n && !enread_stream(status, stream, SIZE_MAX)) enprintf(status, "%s: file is shorter than expected\n", stream->file); r = stream->ptr - (stream->ptr % stream->pixel_size); + r = r < n ? r : n; (process)(stream, r, frame); enwriteall(status, output_fd, stream->buf, r, output_fname); memmove(stream->buf, stream->buf + r, stream->ptr -= r); @@ -301,6 +302,8 @@ nprocess_multiple_streams(int status, struct stream *streams, size_t n_streams, if (streams[i].ptr && streams[i].ptr < n) n = streams[i].ptr; } + if (n == SIZE_MAX) + break; n -= n % streams->pixel_size; process(streams, n_streams, n); @@ -308,13 +311,14 @@ nprocess_multiple_streams(int status, struct stream *streams, size_t n_streams, closed = SIZE_MAX; for (i = 0; i < n_streams; i++) { - memmove(streams[i].buf, streams[i].buf + n, streams[i].ptr -= n); + if (streams[i].ptr) + memmove(streams[i].buf, streams[i].buf + n, streams[i].ptr -= n); if (streams[i].ptr < streams->pixel_size && streams[i].fd < 0 && closed == SIZE_MAX) closed = i; } if (closed != SIZE_MAX) { for (i = (j = closed) + 1; i < n_streams; i++) - if (streams[i].ptr < streams->pixel_size && streams[i].fd < 0) + if (streams[i].ptr >= streams->pixel_size || streams[i].fd >= 0) streams[j++] = streams[i]; n_streams = j; } diff --git a/src/util.c b/src/util.c @@ -157,6 +157,18 @@ pwriteall(int fd, void *buf, size_t n, size_t ptr) return 0; } +int +writezeroes(int fd, void *buf, size_t bufsize, size_t n) +{ + size_t p, m; + for (p = 0; p < n; p += m) { + m = bufsize < n - p ? bufsize : n - p; + if (writeall(fd, buf, m)) + return -1; + } + return 0; +} + static inline pid_t enfork(int status) diff --git a/src/util/io.h b/src/util/io.h @@ -1,8 +1,9 @@ /* See LICENSE file for copyright and license details. */ -#define ewriteall(...) enwriteall(1, __VA_ARGS__) -#define ereadall(...) enreadall(1, __VA_ARGS__) -#define epwriteall(...) enpwriteall(1, __VA_ARGS__) +#define ewriteall(...) enwriteall(1, __VA_ARGS__) +#define ereadall(...) enreadall(1, __VA_ARGS__) +#define epwriteall(...) enpwriteall(1, __VA_ARGS__) +#define ewritezeroes(...) enwritezeroes(1, __VA_ARGS__) int writeall(int fd, void *buf, size_t n); @@ -32,3 +33,12 @@ enpwriteall(int status, int fd, void *buf, size_t n, size_t ptr, const char *fna if (pwriteall(fd, buf, n, ptr)) enprintf(status, "pwrite %s:", fname); } + +int writezeroes(int fd, void *buf, size_t bufsize, size_t n); + +static inline void +enwritezeroes(int status, int fd, void *buf, size_t bufsize, size_t n, const char *fname) +{ + if (writezeroes(fd, buf, bufsize, n)) + enprintf(status, "write %s:", fname); +}