blind

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

commit f29845d3bba7032c7f61d25a34441d9ab7ff8a4e
parent 4125cc7cee7817ec5c7e2e0328011f3e234a4b46
Author: Mattias Andrée <maandree@kth.se>
Date:   Sat, 14 Jan 2017 06:33:00 +0100

Add definition checks for posix_fadvise flags + blind-concat: add -j

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

Diffstat:
MTODO | 2--
Mconfig.mk | 4++--
Msrc/blind-concat.c | 137+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msrc/blind-cut.c | 2++
Msrc/blind-repeat.c | 2++
Msrc/blind-reverse.c | 2++
Msrc/util.c | 16++++++++++++++++
Msrc/util/io.h | 14++++++++++++--
8 files changed, 166 insertions(+), 13 deletions(-)

diff --git a/TODO b/TODO @@ -11,7 +11,6 @@ blind-apply-kernel apply a convolution matrix UNTESTED: blind-arithm blind-colour-srgb - blind-concat blind-crop blind-cut blind-dissolve @@ -27,5 +26,4 @@ UNTESTED: blind-set-saturation blind-single-colour blind-split - blind-stack blind-to-text diff --git a/config.mk b/config.mk @@ -1,5 +1,5 @@ -# You may want to remove -DHAVE_PRCTL from CPPFLAGS if you are not using Linux. +# You may want to remove -DHAVE_PRCTL and -DHAVE_EPOLL from CPPFLAGS if you are not using Linux. CFLAGS = -std=c99 -Wall -pedantic -O2 -CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64 -DHAVE_PRCTL +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_FILE_OFFSET_BITS=64 -DHAVE_PRCTL -DHAVE_EPOLL LDFLAGS = -lm -s diff --git a/src/blind-concat.c b/src/blind-concat.c @@ -2,16 +2,21 @@ #include "stream.h" #include "util.h" +#if defined(HAVE_SYS_EPOLL_H) +# include <sys/epoll.h> +#endif #include <sys/mman.h> +#include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <limits.h> #include <string.h> #include <unistd.h> -USAGE("[-o output-file] first-stream ... last-stream") +USAGE("[-o output-file [-j jobs]] first-stream ... last-stream") static void -concat_to_stdout(int argc, char *argv[]) +concat_to_stdout(int argc, char *argv[], const char *fname) { struct stream *streams; size_t frames = 0; @@ -32,11 +37,11 @@ concat_to_stdout(int argc, char *argv[]) streams->frames = frames; fprint_stream_head(stdout, streams); - efflush(stdout, "<stdout>"); + efflush(stdout, fname); for (i = 0; i < argc; i++) { for (; eread_stream(streams + i, SIZE_MAX); streams[i].ptr = 0) - ewriteall(STDOUT_FILENO, streams[i].buf, streams[i].ptr, "<stdout>"); + ewriteall(STDOUT_FILENO, streams[i].buf, streams[i].ptr, fname); close(streams[i].fd); } @@ -90,26 +95,144 @@ concat_to_file(int argc, char *argv[], char *output_file) close(fd); } +static void +concat_to_file_parallel(int argc, char *argv[], char *output_file, size_t jobs) +{ + struct epoll_event *events; + struct stream *streams; + size_t *ptrs; + char head[STREAM_HEAD_MAX]; + size_t ptr, frame_size, frames = 0, next = 0, j; + ssize_t headlen; + int fd, i, n, pollfd; + +#if !defined(HAVE_SYS_EPOLL_H) + fd = eopen(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (fd != STDOUT_FILENO && dup2(fd, STDOUT_FILENO) == -1) + eprintf("dup2:"); + concat_to_stdout(argc, argv, output_file); + return; +#else + + if (jobs > (size_t)argc) + jobs = (size_t)argc; + + fd = eopen(output_file, O_RDWR | O_CREAT | O_TRUNC, 0666); + events = emalloc(jobs * sizeof(*events)); + streams = emalloc((size_t)argc * sizeof(*streams)); + ptrs = emalloc((size_t)argc * sizeof(*ptrs)); + + for (i = 0; i < argc; i++) { + streams[i].file = argv[i]; + streams[i].fd = eopen(streams[i].file, O_RDONLY); + einit_stream(streams + i); + if (i) + echeck_compat(streams + i, streams); + if (streams[i].frames > SIZE_MAX - frames) + eprintf("resulting video is too long\n"); + frames += streams[i].frames; + } + + sprintf(head, "%zu %zu %zu %s\n%cuivf%zn", + frames, streams->width, streams->height, streams->pixfmt, 0, &headlen); + + echeck_frame_size(streams->width, streams->height, streams->pixel_size, 0, output_file); + frame_size = streams->width * streams->height * streams->pixel_size; + ptr = (size_t)headlen; + for (i = 0; i < argc; i++) { + ptrs[i] = ptr; + ptr += streams->frames * frame_size; + } + if (ftruncate(fd, (off_t)ptr)) + eprintf("ftruncate %s:", output_file); +#if defined(POSIX_FADV_RANDOM) + posix_fadvise(fd, (size_t)headlen, 0, POSIX_FADV_RANDOM); +#endif + + pollfd = epoll_create1(0); + if (pollfd == -1) + eprintf("epoll_create1:"); + + epwriteall(fd, head, (size_t)head_len, 0, output_file); + for (i = 0; i < argc; i++) { + epwriteall(fd, streams[i].buf, streams[i].ptr, ptrs[i], output_file); + ptrs[i] += streams[i].ptr; + streams[i].ptr = 0; + } + + for (j = 0; j < jobs; j++, next++) { + events->events = EPOLLIN; + events->data.u64 = next; + if (epoll_ctl(pollfd, EPOLL_CTL_ADD, streams[next].fd, events) == -1) { + if ((errno == ENOMEM || errno == ENOSPC) && j) + break; + eprintf("epoll_ctl:"); + } + } + jobs = j; + + while (jobs) { + n = epoll_wait(pollfd, events, jobs, -1); + if (n < 0) + eprintf("epoll_wait:"); + for (i = 0; i < n; i++) { + j = events[i].data.u64; + if (!eread_stream(streams + j)) { + close(streams[j].fd); + if (next < (size_t)argc) { + events->events = EPOLLIN; + events->data.u64 = next; + if (epoll_ctl(pollfd, EPOLL_CTL_ADD, streams[next].fd, events) == -1) { + if ((errno == ENOMEM || errno == ENOSPC) && j) + break; + eprintf("epoll_ctl:"); + } + next++; + } else { + jobs--; + } + } else { + epwriteall(fd, streams[j].buf, streams[j].ptr, ptrs[j], output_file); + ptrs[j] += streams[j].ptr; + streams[j].ptr = 0; + } + } + } + + close(pollfd); + free(events); + free(streams); + free(ptrs); + +#endif +} + int main(int argc, char *argv[]) { char *output_file = NULL; + size_t jobs = 0; ARGBEGIN { case 'o': output_file = EARG(); break; + case 'j': + jobs = etozu_flag('j', EARG(), 1, SHRT_MAX); + break; default: usage(); } ARGEND; - if (argc < 2) + if (argc < 2 || (jobs && !output_file)) usage(); - if (output_file) + if (jobs) + concat_to_file_parallel(argc, argv, output_file, jobs); + else if (output_file) concat_to_file(argc, argv, output_file); else - concat_to_stdout(argc, argv); + concat_to_stdout(argc, argv, "<stdout>"); return 0; } diff --git a/src/blind-cut.c b/src/blind-cut.c @@ -53,7 +53,9 @@ main(int argc, char *argv[]) end = end * frame_size + stream.headlen; start = start * frame_size + stream.headlen; +#if defined(POSIX_FADV_SEQUENTIAL) posix_fadvise(stream.fd, start, end - start, POSIX_FADV_SEQUENTIAL); +#endif for (ptr = start; ptr < end; ptr += (size_t)r) { max = end - ptr; max = max < sizeof(buf) ? max : sizeof(buf); diff --git a/src/blind-repeat.c b/src/blind-repeat.c @@ -39,7 +39,9 @@ main(int argc, char *argv[]) efflush(stdout, "<stdout>"); while (inf || count--) { +#if defined(POSIX_FADV_SEQUENTIAL) posix_fadvise(stream.fd, stream.headlen, 0, POSIX_FADV_SEQUENTIAL); +#endif for (ptr = stream.headlen;; ptr += (size_t)r) { r = pread(stream.fd, buf, sizeof(buf), ptr); if (r < 0) diff --git a/src/blind-reverse.c b/src/blind-reverse.c @@ -27,7 +27,9 @@ main(int argc, char *argv[]) if (stream.frames > SSIZE_MAX / frame_size) eprintf("%s: video is too large\n", stream.file); +#if defined(POSIX_FADV_RANDOM) posix_fadvise(stream.fd, 0, 0, POSIX_FADV_RANDOM); +#endif while (stream.frames--) { ptr = stream.frames * frame_size; end = ptr + frame_size; diff --git a/src/util.c b/src/util.c @@ -140,6 +140,22 @@ readall(int fd, void *buf, size_t n) return r; } +int +pwriteall(int fd, void *buf, size_t n, size_t ptr) +{ + char *buffer = buf; + ssize_t r; + while (n) { + r = pwrite(fd, buffer, n, ptr); + if (r < 0) + return -1; + buffer += (size_t)r; + n -= (size_t)r; + ptr += (size_t)r; + } + return 0; +} + static inline pid_t enfork(int status) diff --git a/src/util/io.h b/src/util/io.h @@ -1,7 +1,8 @@ /* See LICENSE file for copyright and license details. */ -#define ewriteall(...) enwriteall(1, __VA_ARGS__) -#define ereadall(...) enreadall(1, __VA_ARGS__) +#define ewriteall(...) enwriteall(1, __VA_ARGS__) +#define ereadall(...) enreadall(1, __VA_ARGS__) +#define epwriteall(...) enpwriteall(1, __VA_ARGS__) int writeall(int fd, void *buf, size_t n); @@ -22,3 +23,12 @@ enreadall(int status, int fd, void *buf, size_t n, const char *fname) enprintf(status, "read %s:", fname); return (size_t)r; } + +int pwriteall(int fd, void *buf, size_t n, size_t ptr); + +static inline void +enpwriteall(int status, int fd, void *buf, size_t n, size_t ptr, const char *fname) +{ + if (pwriteall(fd, buf, n, ptr)) + enprintf(status, "pwrite %s:", fname); +}