commit 835df7bd1e81852062dd70ce1a054fc9b510e50f
Author:     Mattias Andrée <[email protected]>
AuthorDate: Tue Jul 25 22:12:23 2017 +0200
Commit:     Mattias Andrée <[email protected]>
CommitDate: Tue Jul 25 22:12:23 2017 +0200

    Fix blind-kernel and blind-temporal-mean,d add 
blind-{spatial,temporal}-arithm and blind-spatial-mean, and add support for 
multiple streams in blind-arithm
    
    Signed-off-by: Mattias Andrée <[email protected]>

diff --git a/Makefile b/Makefile
index 8f6426b..1bc0f16 100644
--- a/Makefile
+++ b/Makefile
@@ -43,6 +43,7 @@ BIN =\
        blind-interleave\
        blind-invert-luma\
        blind-invert-matrix\
+       blind-kernel\
        blind-linear-gradient\
        blind-make-kernel\
        blind-matrix-orthoproject\
@@ -76,6 +77,8 @@ BIN =\
        blind-sinc-wave\
        blind-sine-wave\
        blind-skip-pattern\
+       blind-spatial-arithm\
+       blind-spatial-mean\
        blind-spectrum\
        blind-spiral-gradient\
        blind-split\
@@ -85,6 +88,8 @@ BIN =\
        blind-square-gradient\
        blind-stack\
        blind-tee\
+       blind-temporal-arithm\
+       blind-temporal-mean\
        blind-time-blur\
        blind-triangular-wave\
        blind-to-image\
@@ -97,13 +102,7 @@ BIN =\
        blind-triangle-tessellation\
        blind-unpremultiply\
        blind-vector-projection\
-       blind-write-head\
-       blind-kernel\
-       blind-temporal-mean
-
-# TODO Not tested yet (and doesn't have any manpages):
-#    blind-kernel
-#    blind-temporal-mean
+       blind-write-head
 
 SH_SCRIPTS =\
        blind-rotate-90\
diff --git a/README b/README
index 8da87c7..f844730 100644
--- a/README
+++ b/README
@@ -126,6 +126,9 @@ UTILITIES
        blind-invert-matrix(1)
               Invert matrix-video
 
+       blind-kernel(1)
+              Create a convolution matrix
+
        blind-linear-gradient(1)
               Generate a video with a linear gradient
 
@@ -240,6 +243,12 @@ UTILITIES
        blind-skip-pattern(1)
               Skips frames in a video according to pattern
 
+       blind-spatial-arithm(1)
+              Perform simple arithmetic over all pixels for each frame in a 
video
+
+       blind-spatial-mean(1)
+              Calculate the mean over all pixel for each frame in a video
+
        blind-spectrum(1)
               Transform a gradient into a spectrum
 
@@ -267,6 +276,12 @@ UTILITIES
        blind-tee(1)
               /dev/fd/ aware tee(1) implementation
 
+       blind-temporal-arithm(1)
+              Perform simple arithmetic over all frames in a video for each 
pixel
+
+       blind-temporal-mean(1)
+              Calculate the mean over all frames in a video for each pixel
+
        blind-time-blur(1)
               Draw new frames on top of old frames with partial alpha
 
diff --git a/TODO b/TODO
index ec222b4..fb62d11 100644
--- a/TODO
+++ b/TODO
@@ -31,7 +31,6 @@ blind-mean            mean of multiple streams
                        https://en.wikipedia.org/wiki/Identric_mean
                        https://en.wikipedia.org/wiki/Logarithmic_mean
                        https://en.wikipedia.org/wiki/Stolarsky_mean
-blind-temporal-arithm  blind-arithm but over all frames in a video instead of 
over all streams
 blind-apply-icc                apply ICC profile to video
 blind-convex-gradient  create a gradient in the shape of a convex lens
 blind-concave-gradient create a gradient in the shape of a concave lens
@@ -51,8 +50,6 @@ blind-from-video: add options to:
 
 blind-cone-gradient: add ability to make gradient superelliptic
 
-blind-arithm: add support for multiple streams
-
 Add [-j jobs] to blind-from-video and blind-to-video.
 
 long double is slightly faster than long.
diff --git a/man/blind-apply-kernel.1 b/man/blind-apply-kernel.1
index f09cc6e..a4c28f1 100644
--- a/man/blind-apply-kernel.1
+++ b/man/blind-apply-kernel.1
@@ -63,6 +63,7 @@ A frame or row requires 32 bytes per pixel it contains.
 .SH SEE ALSO
 .BR blind (7),
 .BR blind-make-kernel (1),
+.BR blind-kernel (1),
 .BR blind-gauss-blur (1)
 .SH AUTHORS
 Mattias Andrée
diff --git a/man/blind-arithm.1 b/man/blind-arithm.1
index 6df053a..22d7fd7 100644
--- a/man/blind-arithm.1
+++ b/man/blind-arithm.1
@@ -5,7 +5,7 @@ blind-arithm - Perform simple arithmetic on a video
 .B blind-arithm
 [-axyz]
 .I operation
-.I right-hand-stream
+.IR right-hand-stream \ ...
 .SH DESCRIPTION
 .B blind-arithm
 reads left-hand operands from stdin, and right-hand
@@ -27,6 +27,16 @@ If stdin is shorter than
 the remainder of
 .I right-hand-stream
 is ignored but may be partially read.
+.P
+IF multiple
+.I right-hand-stream
+are specified, they are applied from left to right,
+with the exception for if      
+.I operation
+is
+.BR exp ,
+in which case they are applied from right to left with
+stdin applied last.
 .SH OPERATIONS
 .TP
 .B add
@@ -86,6 +96,10 @@ Do not modify the Z channel (the third channel).
 .BR blind-cross-product (1),
 .BR blind-quaternion-product (1),
 .BR blind-vector-projection (1),
+.BR blind-spatial-mean (1),
+.BR blind-spatial-arithm (1),
+.BR blind-temporal-mean (1),
+.BR blind-temporal-arithm (1),
 .BR blind-single-colour (1),
 .BR blind-set-alpha (1),
 .BR blind-set-luma (1),
diff --git a/man/blind-gauss-blur.1 b/man/blind-gauss-blur.1
index e0e84a9..7b92045 100644
--- a/man/blind-gauss-blur.1
+++ b/man/blind-gauss-blur.1
@@ -81,6 +81,7 @@ memory. A frame requires 32 bytes per pixel it contains.
 .BR blind-single-colour (1),
 .BR blind-time-blur (1),
 .BR blind-make-kernel (1),
+.BR blind-kernel (1),
 .BR blind-apply-kernel (1)
 .SH AUTHORS
 Mattias Andrée
diff --git a/man/blind-kernel.1 b/man/blind-kernel.1
new file mode 100644
index 0000000..df789cc
--- /dev/null
+++ b/man/blind-kernel.1
@@ -0,0 +1,116 @@
+.TH BLIND-KERNEL 1 blind
+.SH NAME
+blind-kernel - Create a convolution matrix
+.SH SYNOPSIS
+.B blind-kernel
+[-xyza]
+.I kernel
+.RI [ parameter ]\ ...
+.SH DESCRIPTION
+.B blind-kernel
+creates a convolution matrix that can be applied to
+a video using
+.BR blind-apply-kernel (1).
+The convolution matrix is created from a set
+of standard formulae. The formula is selected
+using the
+.I kernel
+argument and is tuned with
+.IR kernel -specific
+.IR parameter s.
+.SH KERNELS
+.TP
+.BI kirsch\  direction
+Create a Kirsch kernel with the specified
+.IR direction .
+The
+.I direction
+must be
+.B 1
+or
+.BR N ;
+.BR 2 ,
+.BR NW ,
+or
+.BR WN ;
+.BR 3
+or
+.BR W ;
+.BR 4 ,
+.BR SW ,
+or
+.BR WS ;
+.BR 5
+or
+.BR S ;
+.BR 6 ,
+.BR SE ,
+or
+.BR ES ;
+.BR 7
+or
+.BR E ;
+or
+.BR 8 ,
+.BR NE ,
+or
+.BR EN .
+.TP
+.RI ' \fBbox\ blur\fP '\ [-w\  weight ]\ [ spread \ |\  x-spread \  y-spread ]
+Creates a box blur kernel. Unless
+.B -w
+is used, the kernel is unweighted, otherwise it has the specified
+.IR weight .
+The kernel will have the spread 1, the specified
+.IR spread ,
+or
+.I x-spread
+as the horizontal spread and
+.I y-spread
+as the vertical spread.
+.TP
+.BR sharpen \ [-i]
+Creates a sharpen kernel. If
+.B -i
+is used, an intensified sharpen kernel is created.
+.TP
+.RI \fBgaussian\fP\ [-s\  spread ]\ [-u]\  standard-deviation
+Creates a Gaussian blur kernel with the standard deviation
+.IR standard-deviation .
+If
+.B -u
+is used, the a Gaussian unsharpen kernel is created. If
+.B -s
+is specified, the specified
+.I spread
+will be used, otherwise the spread will be selected automatically.
+.SH OPTIONS
+.TP
+.B -a
+Apply the values to the alpha channel, set the
+values for all unselected channels to zero.
+.TP
+.B -x
+Apply the values to the X channel, set the values
+for all unselected channels to zero.
+.TP
+.B -y
+Apply the values to the Y channel, set the values
+for all unselected channels to zero.
+.TP
+.B -z
+Apply the values to the Z channel, set the values
+for all unselected channels to zero.
+.SH NOTES
+.B blind-make-kernel
+Create a single frame, to that it can be stored to
+disc. When applying it to a video, you want to use
+.BR blind-repeat (1).
+.SH SEE ALSO
+.BR blind (7),
+.BR blind-apply-kernel (1),
+.BR blind-kernel (1),
+.BR blind-repeat (1)
+.SH AUTHORS
+Mattias Andrée
+.RI < [email protected] >
diff --git a/man/blind-make-kernel.1 b/man/blind-make-kernel.1
index 903f489..69e3739 100644
--- a/man/blind-make-kernel.1
+++ b/man/blind-make-kernel.1
@@ -73,11 +73,12 @@ for all unselected channels to zero.
 .SH NOTES
 .B blind-make-kernel
 Create a single frame, to that it can be stored to
-disk. When applying it to a video, you want to use
+disc. When applying it to a video, you want to use
 .BR blind-repeat (1).
 .SH SEE ALSO
 .BR blind (7),
 .BR blind-apply-kernel (1),
+.BR blind-kernel (1),
 .BR blind-repeat (1)
 .SH AUTHORS
 Mattias Andrée
diff --git a/man/blind-spatial-arithm.1 b/man/blind-spatial-arithm.1
new file mode 100644
index 0000000..f512944
--- /dev/null
+++ b/man/blind-spatial-arithm.1
@@ -0,0 +1,35 @@
+.TH BLIND-SPATIAL-ARITHM 1 blind
+.SH NAME
+blind-spatial-arithm - Perform simple arithmetic over all pixels for each 
frame in a video
+.SH SYNOPSIS
+.B blind-spatial-arithm
+.I operation
+.SH DESCRIPTION
+.B blind-spatial-arithm
+reads a video from stdin and applies an arithmetic
+operation over all pixels, for each frame in the
+video, and prints the resulting video, with one
+pixel in each frame, to stdout.
+.SH OPERATIONS
+.TP
+.B add
+Calculate the sum of the operands.
+.TP
+.B mul
+Calculate the product of the operands.
+.TP
+.B min
+Select the lowest operand.
+.TP
+.B max
+Select the highest operand.
+.SH SEE ALSO
+.BR blind (7),
+.BR blind-temporal-arithm (1),
+.BR blind-arithm (1),
+.BR blind-spatial-mean (1),
+.BR blind-temporal-mean (1),
+.BR blind-rewrite-head (1)
+.SH AUTHORS
+Mattias Andrée
+.RI < [email protected] >
diff --git a/man/blind-spatial-mean.1 b/man/blind-spatial-mean.1
new file mode 100644
index 0000000..74ffa53
--- /dev/null
+++ b/man/blind-spatial-mean.1
@@ -0,0 +1,48 @@
+.TH BLIND-SPATIAL-MEAN 1 blind
+.SH NAME
+blind-spatial-mean - Calculate the mean over all pixel for each frame in a 
video
+.SH SYNOPSIS
+.B blind-spatial-mean
+[-g | -h | -l
+.I power
+| -p
+.I power
+| -v]
+.SH DESCRIPTION
+.B blind-spatial-mean
+reads a video from stdin and calculates the mean
+over all pixels for frames, and outputs a
+video with one pixel in each frame, to stdout with
+the mean for each frame.
+.P
+Unless otherwise specified, the arithmetic mean
+is calculated.
+.SH OPTIONS
+.TP
+.B -g
+Calculate the geometric mean.
+.TP
+.B -h
+Calculate the harmonic mean.
+.TP
+.BR -l \ \fIpower\fP
+Calculate the Lehmer mean with the specified
+.IR power .
+.TP
+.BR -p \ \fIpower\fP
+Calculate the power mean (Hölder mean) with
+the specified
+.IR power .
+.TP
+.B -v
+Calculate the variance.
+.SH SEE ALSO
+.BR blind (7),
+.BR blind-temporal-arithm (1),
+.BR blind-spatial-arithm (1),
+.BR blind-arithm (1),
+.BR blind-temporal-mean (1),
+.BR blind-rewrite-head (1)
+.SH AUTHORS
+Mattias Andrée
+.RI < [email protected] >
diff --git a/man/blind-temporal-arithm.1 b/man/blind-temporal-arithm.1
new file mode 100644
index 0000000..1ccfc2a
--- /dev/null
+++ b/man/blind-temporal-arithm.1
@@ -0,0 +1,39 @@
+.TH BLIND-TEMPORAL-ARITHM 1 blind
+.SH NAME
+blind-temporal-arithm - Perform simple arithmetic over all frames in a video 
for each pixel
+.SH SYNOPSIS
+.B blind-temporal-arithm
+.I operation
+.SH DESCRIPTION
+.B blind-temporal-arithm
+reads a video from stdin and applies an arithmetic
+operation over all frames in the video, for each
+pixel, and prints the resulting single-frame video
+to stdout.
+.SH OPERATIONS
+.TP
+.B add
+Calculate the sum of the operands.
+.TP
+.B mul
+Calculate the product of the operands.
+.TP
+.B min
+Select the lowest operand.
+.TP
+.B max
+Select the highest operand.
+.SH REQUIREMENTS
+.B blind-temporal-arithm
+requires enough free memory to load one full frames memory.
+A frame requires 32 bytes per pixel it contains.
+.SH SEE ALSO
+.BR blind (7),
+.BR blind-temporal-mean (1),
+.BR blind-spatial-arithm (1),
+.BR blind-spatial-mean (1),
+.BR blind-arithm (1),
+.BR blind-rewrite-head (1)
+.SH AUTHORS
+Mattias Andrée
+.RI < [email protected] >
diff --git a/man/blind-temporal-mean.1 b/man/blind-temporal-mean.1
new file mode 100644
index 0000000..88e0d0c
--- /dev/null
+++ b/man/blind-temporal-mean.1
@@ -0,0 +1,60 @@
+.TH BLIND-TEMPORAL-MEAN 1 blind
+.SH NAME
+blind-temporal-mean - Calculate the mean over all frames in a video for each 
pixel
+.SH SYNOPSIS
+.B blind-temporal-mean
+[-g | -h | -l
+.I power
+| -p
+.I power
+| -v]
+.SH DESCRIPTION
+.B blind-temporal-mean
+reads a video from stdin and calculates the mean
+over all frames for each pixel, and outputs a
+single frame video to stdout with the mean for
+each pixel.
+.P
+Unless otherwise specified, the arithmetic mean
+is calculated.
+.SH OPTIONS
+.TP
+.B -g
+Calculate the geometric mean.
+.TP
+.B -h
+Calculate the harmonic mean.
+.TP
+.BR -l \ \fIpower\fP
+Calculate the Lehmer mean with the specified
+.IR power .
+.TP
+.BR -p \ \fIpower\fP
+Calculate the power mean (Hölder mean) with
+the specified
+.IR power .
+.TP
+.B -v
+Calculate the variance.
+.SH REQUIREMENTS
+.B blind-temporal-mean
+requires enough free memory to load two full frames memory.
+A frame requires 32 bytes per pixel it contains. If
+.B -l
+or
+.B -v
+is used, enough free memory to load three full frames
+memory is required.
+.P
+.B blind-temporal-mean
+is optimised for simplicity rather than memory usage.
+.SH SEE ALSO
+.BR blind (7),
+.BR blind-temporal-arithm (1),
+.BR blind-spatial-arithm (1),
+.BR blind-arithm (1),
+.BR blind-spatial-mean (1),
+.BR blind-rewrite-head (1)
+.SH AUTHORS
+Mattias Andrée
+.RI < [email protected] >
diff --git a/man/blind.7 b/man/blind.7
index acc4bb5..2e32b5e 100644
--- a/man/blind.7
+++ b/man/blind.7
@@ -144,6 +144,9 @@ Invert the luminosity of a video
 .BR blind-invert-matrix (1)
 Invert matrix-video
 .TP
+.BR blind-kernel (1)
+Create a convolution matrix
+.TP
 .BR blind-linear-gradient (1)
 Generate a video with a linear gradient
 .TP
@@ -255,6 +258,12 @@ Apply sine-wave repetition to gradient
 .BR blind-skip-pattern (1)
 Skips frames in a video according to pattern
 .TP
+.BR blind-spatial-arithm (1)
+Perform simple arithmetic over all pixels for each frame in a video
+.TP
+.BR blind-spatial-mean (1)
+Calculate the mean over all pixel for each frame in a video
+.TP
 .BR blind-spectrum (1)
 Transform a gradient into a spectrum
 .TP
@@ -284,6 +293,12 @@ Overlay videos
 .BR tee (1)
 implementation
 .TP
+.BR blind-temporal-arithm (1)
+Perform simple arithmetic over all frames in a video for each pixel
+.TP
+.BR blind-temporal-mean (1)
+Calculate the mean over all frames in a video for each pixel
+.TP
 .BR blind-time-blur (1)
 Draw new frames on top of old frames with partial alpha
 .TP
diff --git a/src/blind-arithm.c b/src/blind-arithm.c
index 494977b..4c6ca14 100644
--- a/src/blind-arithm.c
+++ b/src/blind-arithm.c
@@ -1,67 +1,71 @@
 /* See LICENSE file for copyright and license details. */
 #include "common.h"
 
-USAGE("[-axyz] operation right-hand-stream")
+USAGE("[-axyz] operation right-hand-stream ...")
 
-static int skip_a = 0;
-static int skip_x = 0;
-static int skip_y = 0;
-static int skip_z = 0;
+static int skip_ch[4] = {0, 0, 0, 0};
 
 /* Because the syntax for a function returning a function pointer is 
disgusting. */
-typedef void (*process_func)(struct stream *left, struct stream *right, size_t 
n);
+typedef void (*process_func)(struct stream *streams, size_t n_streams, size_t 
n);
 
 #define LIST_OPERATORS(PIXFMT, TYPE)\
-       X(add, *lh += rh,                  PIXFMT, TYPE)\
-       X(sub, *lh -= rh,                  PIXFMT, TYPE)\
-       X(mul, *lh *= rh,                  PIXFMT, TYPE)\
-       X(div, *lh /= rh,                  PIXFMT, TYPE)\
-       X(mod, *lh = posmod(*lh, rh),      PIXFMT, TYPE)\
-       X(exp, *lh = pow(*lh, rh),         PIXFMT, TYPE)\
-       X(log, *lh = log2(*lh) / log2(rh), PIXFMT, TYPE)\
-       X(min, *lh = MIN(*lh, rh),         PIXFMT, TYPE)\
-       X(max, *lh = MAX(*lh, rh),         PIXFMT, TYPE)\
-       X(abs, *lh = abs(*lh - rh) + rh,   PIXFMT, TYPE)
+       X(add, 0, *lh += rh,                  PIXFMT, TYPE)\
+       X(sub, 0, *lh -= rh,                  PIXFMT, TYPE)\
+       X(mul, 0, *lh *= rh,                  PIXFMT, TYPE)\
+       X(div, 0, *lh /= rh,                  PIXFMT, TYPE)\
+       X(mod, 0, *lh = posmod(*lh, rh),      PIXFMT, TYPE)\
+       X(exp, 1, *lh = pow(*lh, rh),         PIXFMT, TYPE)\
+       X(log, 0, *lh = log2(*lh) / log2(rh), PIXFMT, TYPE)\
+       X(min, 0, *lh = MIN(*lh, rh),         PIXFMT, TYPE)\
+       X(max, 0, *lh = MAX(*lh, rh),         PIXFMT, TYPE)\
+       X(abs, 0, *lh = abs(*lh - rh) + rh,   PIXFMT, TYPE)
 
-#define C(CH, CHI, ALGO, TYPE)\
-       (!skip_##CH ? ((lh = ((TYPE *)(left->buf + i)) + (CHI),\
-                       rh = ((TYPE *)(right->buf + i))[CHI],\
-                       (ALGO)), 0) : 0)
+#define P(L, R, ALGO, TYPE)\
+       (lh = (TYPE *)(streams[L].buf + k),\
+        rh = *((TYPE *)(streams[R].buf + k)),\
+        (ALGO))
 
-#define X(NAME, ALGO, PIXFMT, TYPE)\
+#define X(NAME, RTL, ALGO, PIXFMT, TYPE)\
        static void\
-       process_##PIXFMT##_##NAME(struct stream *left, struct stream *right, 
size_t n)\
+       process_##PIXFMT##_##NAME(struct stream *streams, size_t n_streams, 
size_t n)\
        {\
-               size_t i;\
+               size_t i, j, k;\
                TYPE *lh, rh;\
-               for (i = 0; i < n; i += 4 * sizeof(TYPE)) {\
-                       C(x, 0, ALGO, TYPE);\
-                       C(y, 1, ALGO, TYPE);\
-                       C(z, 2, ALGO, TYPE);\
-                       C(a, 3, ALGO, TYPE);\
+               if (RTL) {\
+                       for (i = 0; i < streams->n_chan; i++)\
+                               if (!skip_ch[i])\
+                                       for (j = n_streams; --j;)\
+                                               for (k = i * sizeof(TYPE); k < 
n; k += 4 * sizeof(TYPE))\
+                                                       P(j - 1, j, ALGO, 
TYPE);\
+               } else {\
+                       for (i = 0; i < streams->n_chan; i++)\
+                               if (!skip_ch[i])\
+                                       for (j = 1; j < n_streams; j++)\
+                                               for (k = i * sizeof(TYPE); k < 
n; k += 4 * sizeof(TYPE))\
+                                                       P(0, j, ALGO, TYPE);\
                }\
        }
-LIST_OPERATORS(xyza, double)
-LIST_OPERATORS(xyzaf, float)
+LIST_OPERATORS(lf, double)
+LIST_OPERATORS(f, float)
 #undef X
 
 static process_func
-get_process_xyza(const char *operation)
+get_process_lf(const char *operation)
 {
-#define X(NAME, ALGO, PIXFMT, TYPE)\
+#define X(NAME, _RTL, _ALGO, PIXFMT, _TYPE)\
        if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
-       LIST_OPERATORS(xyza, double)
+       LIST_OPERATORS(lf, double)
 #undef X
        eprintf("algorithm not recognised: %s\n", operation);
        return NULL;
 }
 
 static process_func
-get_process_xyzaf(const char *operation)
+get_process_f(const char *operation)
 {
-#define X(NAME, ALGO, PIXFMT, TYPE)\
+#define X(NAME, _RTL, _ALGO, PIXFMT, _TYPE)\
        if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
-       LIST_OPERATORS(xyzaf, float)
+       LIST_OPERATORS(f, float)
 #undef X
        eprintf("algorithm not recognised: %s\n", operation);
        return NULL;
@@ -70,41 +74,46 @@ get_process_xyzaf(const char *operation)
 int
 main(int argc, char *argv[])
 {
-       struct stream left, right;
+       struct stream *streams;
        process_func process;
+       const char *operation;
+       size_t frames = SIZE_MAX, tmp;
+       int i;
 
        ARGBEGIN {
        case 'a':
-               skip_a = 1;
+               skip_ch[3] = 1;
                break;
        case 'x':
-               skip_x = 1;
-               break;
        case 'y':
-               skip_y = 1;
-               break;
        case 'z':
-               skip_z = 1;
+               skip_ch[ARGC() - 'x'] = 1;
                break;
        default:
                usage();
        } ARGEND;
 
-       if (argc != 2)
+       if (argc < 2)
                usage();
 
-       eopen_stream(&left, NULL);
-       eopen_stream(&right, argv[1]);
+       operation = *argv;
+       streams = alloca((size_t)argc * sizeof(*streams));
+       *argv = NULL;
+       for (i = 0; i < argc; i++) {
+               eopen_stream(streams + i, argv[i]);
+               if (streams[i].frames && streams[i].frames < frames)
+                       frames = streams[i].frames;
+       }
 
-       if (!strcmp(left.pixfmt, "xyza"))
-               process = get_process_xyza(argv[0]);
-       else if (!strcmp(left.pixfmt, "xyza f"))
-               process = get_process_xyzaf(argv[0]);
+       if (streams->encoding == DOUBLE)
+               process = get_process_lf(operation);
        else
-               eprintf("pixel format %s is not supported, try xyza\n", 
left.pixfmt);
+               process = get_process_f(operation);
 
-       fprint_stream_head(stdout, &left);
+       tmp = streams->frames, streams->frames = frames;
+       fprint_stream_head(stdout, streams);
        efflush(stdout, "<stdout>");
-       process_two_streams(&left, &right, STDOUT_FILENO, "<stdout>", process);
+       streams->frames = tmp;
+       process_multiple_streams(streams, (size_t)argc, STDOUT_FILENO, 
"<stdout>", 1, process);
        return 0;
 }
diff --git a/src/blind-kernel.c b/src/blind-kernel.c
index 7bf1a60..ae66b24 100644
--- a/src/blind-kernel.c
+++ b/src/blind-kernel.c
@@ -3,7 +3,7 @@
 
 USAGE("[-xyza] kernel [parameter] ...")
 
-#define SUBUSAGE(FORMAT)          "usage: %s [-xyza] " FORMAT, argv0
+#define SUBUSAGE(FORMAT)          "usage: %s [-xyza] " FORMAT "\n", argv0
 #define STRCASEEQ3(A, B1, B2, B3) (!strcasecmp(A, B1) || !strcasecmp(A, B2) || 
!strcasecmp(A, B3))
 
 #define LIST_KERNELS\
@@ -37,7 +37,7 @@ kernel_kirsch(int argc, char *argv[], size_t *rows, size_t 
*cols, double **free_
        if (STRCASEEQ3(argv[0], "6", "SE", "ES")) return matrices[5];
        if (STRCASEEQ3(argv[0], "7", "E",  "E"))  return matrices[6];
        if (STRCASEEQ3(argv[0], "8", "NE", "EN")) return matrices[7];
-       eprintf("Unrecognised direction: %s\n", argv[0]);
+       eprintf("unrecognised direction: %s\n", argv[0]);
        return NULL;
 }
 
@@ -129,6 +129,7 @@ static const double *
 kernel_gaussian(int argc, char *argv[], size_t *rows, size_t *cols, double 
**free_this)
 {
        size_t spread = 0, y, x;
+       ssize_t xx, yy;
        int unsharpen = 0;
        double sigma, value, c, k;
        char *arg;
@@ -160,29 +161,17 @@ kernel_gaussian(int argc, char *argv[], size_t *rows, 
size_t *cols, double **fre
 
        *free_this = emalloc3(*rows, *cols, sizeof(double));
 
-       k = sigma * sigma * 2;
+       k = sigma * sigma * 2.;
        c = M_PI * k;
-       c = sqrt(c);
        c = 1.0 / c;
        k = 1.0 / -k;
-
-       for (x = 0; x <= spread; x++) {
-               value = (double)(spread - x);
-               value *= value * k;
-               value = exp(value) * c;
-               for (y = 0; y < *rows; y++) {
+       for (y = 0; y < 2 * spread + 1; y++) {
+               yy = (ssize_t)spread - (ssize_t)y, yy *= yy;
+               for (x = 0; x < 2 * spread + 1; x++) {
+                       xx = (ssize_t)spread - (ssize_t)x, xx *= xx;
+                       value = (double)(xx + yy) * k;
+                       value = exp(value) * c;
                        (*free_this)[y * *cols + x] = value;
-                       (*free_this)[y + 1 * *cols + *cols - 1 - x] = value;
-               }
-       }
-
-       for (y = 0; y <= spread; y++) {
-               value = (double)(spread - y);
-               value *= value * k;
-               value = exp(value) * c;
-               for (x = 0; x < *cols; x++) {
-                       (*free_this)[y * *cols + x] *= value;
-                       (*free_this)[y + 1 * *cols + *cols - 1 - x] *= value;
                }
        }
 
@@ -235,13 +224,16 @@ main(int argc, char *argv[])
                usage();
        } ARGEND;
 
+       if (!argc)
+               usage();
+
        if (null_x && null_y && null_z && null_a)
                null_x = null_y = null_z = null_a = 0;
 
        if (0);
 #define X(FUNC, NAME)\
        else if (!strcmp(argv[0], NAME))\
-               kernel = FUNC(argc, argv + 1, &rows, &cols, &free_this);
+               kernel = FUNC(argc - 1, argv + 1, &rows, &cols, &free_this);
        LIST_KERNELS
 #undef X
        else
diff --git a/src/blind-spatial-arithm.c b/src/blind-spatial-arithm.c
new file mode 100644
index 0000000..b63dc89
--- /dev/null
+++ b/src/blind-spatial-arithm.c
@@ -0,0 +1,94 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("operation")
+
+/* Because the syntax for a function returning a function pointer is 
disgusting. */
+typedef void (*process_func)(struct stream *stream);
+
+#define LIST_OPERATORS(PIXFMT, TYPE)\
+       X(add, img[j & 3] + *buf,     PIXFMT, TYPE)\
+       X(mul, img[j & 3] * *buf,     PIXFMT, TYPE)\
+       X(min, MIN(img[j & 3], *buf), PIXFMT, TYPE)\
+       X(max, MAX(img[j & 3], *buf), PIXFMT, TYPE)
+
+#define X(NAME, ALGO, PIXFMT, TYPE)\
+       static void\
+       process_##PIXFMT##_##NAME(struct stream *stream)\
+       {\
+               TYPE img[4], *buf;\
+               size_t i, n, j = 0, m = stream->frame_size / sizeof(*img);\
+               int first = 1;\
+               do {\
+                       n = stream->ptr / stream->pixel_size * stream->n_chan;\
+                       buf = (TYPE *)(stream->buf);\
+                       for (i = 0; i < n; i++, buf++, j++, j %= m) {\
+                               if (!j) {\
+                                       if (!first)\
+                                               ewriteall(STDOUT_FILENO, img, 
sizeof(img), "<stdout>");\
+                                       first = 0;\
+                                       img[0] = *buf++;\
+                                       img[1] = *buf++;\
+                                       img[2] = *buf++;\
+                                       img[3] = *buf;\
+                                       i += 3;\
+                                       j = 3;\
+                               } else {\
+                                       img[j & 3] = ALGO;\
+                               }\
+                       }\
+                       n *= sizeof(TYPE);\
+                       memmove(stream->buf, stream->buf + n, stream->ptr -= 
n);\
+               } while (eread_stream(stream, SIZE_MAX));\
+               if (!first)\
+                       ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
+       }
+LIST_OPERATORS(lf, double)
+LIST_OPERATORS(f, float)
+#undef X
+
+static process_func
+get_process_lf(const char *operation)
+{
+#define X(NAME, _ALGO, PIXFMT, TYPE)\
+       if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
+       LIST_OPERATORS(lf, double)
+#undef X
+       eprintf("algorithm not recognised: %s\n", operation);
+       return NULL;
+}
+
+static process_func
+get_process_f(const char *operation)
+{
+#define X(NAME, _ALGO, PIXFMT, TYPE)\
+       if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
+       LIST_OPERATORS(f, float)
+#undef X
+       eprintf("algorithm not recognised: %s\n", operation);
+       return NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct stream stream;
+       process_func process;
+
+       UNOFLAGS(argc != 1);
+
+       eopen_stream(&stream, NULL);
+       echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
+
+       if (stream.encoding == DOUBLE)
+               process = get_process_lf(argv[0]);
+       else
+               process = get_process_f(argv[0]);
+
+       if (DPRINTF_HEAD(STDOUT_FILENO, stream.frames, 1, 1, stream.pixfmt) < 0)
+               eprintf("dprintf:");
+       process(&stream);
+       if (stream.ptr)
+               eprintf("%s: incomplete frame\n", stream.file);
+       return 0;
+}
diff --git a/src/blind-spatial-mean.c b/src/blind-spatial-mean.c
new file mode 100644
index 0000000..848af2d
--- /dev/null
+++ b/src/blind-spatial-mean.c
@@ -0,0 +1,142 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("[-g | -h | -l power | -p power | -v]")
+/* TODO add [-w weight-stream] for -l */
+
+/* Because the syntax for a function returning a function pointer is 
disgusting. */
+typedef void (*process_func)(struct stream *stream);
+
+/*
+ * X-parameter 1: method enum value
+ * X-parameter 2: identifier-friendly name
+ * X-parameter 3: initial assignments
+ * X-parameter 4: initial value
+ * X-parameter 5: subcell processing
+ * X-parameter 6: subcell finalisation
+ */
+#define LIST_MEANS(TYPE)\
+       /* [default] arithmetic mean */\
+       X(ARITHMETIC, arithmetic,, 0, img[j & 3] += *buf, img[j & 3] /= pixels)\
+       /* geometric mean */\
+       X(GEOMETRIC, geometric,, 1, img[j & 3] *= *buf, img[j & 3] = 
nnpow(img[j & 3], 1 / pixels))\
+       /* harmonic mean */\
+       X(HARMONIC, harmonic,, 0, img[j & 3] += (TYPE)1 / *buf, img[j & 3] = 
pixels / img[j & 3])\
+       /* Lehmer mean */\
+       X(LEHMER, lehmer, (a = (TYPE)power, b = a - (TYPE)1), 0,\
+         (img[j & 3] += nnpow(*buf, a), aux[j & 3] += nnpow(*buf, b)), img[j & 
3] /= aux[j & 3])\
+       /* power mean (Hölder mean) (m = 2 for root square mean; m = 3 for 
cubic mean) */\
+       X(POWER, power, a = (TYPE)power, 0, img[j & 3] += nnpow(*buf, a),\
+         img[j & 3] = nnpow(img[j & 3], (TYPE)(1. / power)) / pixels)\
+       /* variance */\
+       X(VARIANCE, variance,, 0, (img[j & 3] += *buf * *buf, aux[j & 3] += 
*buf),\
+         img[j & 3] = (img[j & 3] - aux[j & 3] * aux[j & 3] / pixels) / pixels)
+
+#define X(V, ...) V,
+enum method { LIST_MEANS() };
+#undef X
+
+static double power;
+
+#define MAKE_PROCESS(PIXFMT, TYPE,\
+                    _1, NAME, INIT, INITIAL, PROCESS_SUBCELL, 
FINALISE_SUBCELL)\
+       static void\
+       process_##PIXFMT##_##NAME(struct stream *stream)\
+       {\
+               TYPE img[4], aux[4], *buf, a, b;\
+               TYPE pixels = (TYPE)(stream->frame_size / sizeof(img));\
+               size_t i, n, j = 0, m = stream->frame_size / sizeof(*img);\
+               int first = 1;\
+               INIT;\
+               do {\
+                       n = stream->ptr / stream->pixel_size * stream->n_chan;\
+                       buf = (TYPE *)(stream->buf);\
+                       for (i = 0; i < n; i++, buf++, j++, j %= m) {\
+                               if (!j) {\
+                                       if (!first) {\
+                                               for (j = 0; j < 
ELEMENTSOF(img); j++)\
+                                                       FINALISE_SUBCELL;\
+                                               j = 0;\
+                                               ewriteall(STDOUT_FILENO, img, 
sizeof(img), "<stdout>");\
+                                       }\
+                                       first = 0;\
+                                       img[0] = aux[0] = INITIAL;\
+                                       img[1] = aux[1] = INITIAL;\
+                                       img[2] = aux[2] = INITIAL;\
+                                       img[3] = aux[3] = INITIAL;\
+                               }\
+                               PROCESS_SUBCELL;\
+                       }\
+                       n *= sizeof(TYPE);\
+                       memmove(stream->buf, stream->buf + n, stream->ptr -= 
n);\
+               } while (eread_stream(stream, SIZE_MAX));\
+               if (!first) {\
+                       for (j = 0; j < ELEMENTSOF(img); j++)\
+                               FINALISE_SUBCELL;\
+                       ewriteall(STDOUT_FILENO, img, sizeof(img), "<stdout>");\
+               }\
+               (void) aux, (void) a, (void) b, (void) pixels;\
+       }
+#define X(...) MAKE_PROCESS(lf, double, __VA_ARGS__)
+LIST_MEANS(double)
+#undef X
+#define X(...) MAKE_PROCESS(f, float, __VA_ARGS__)
+LIST_MEANS(float)
+#undef X
+#undef MAKE_PROCESS
+
+#define X(ID, NAME, ...) [ID] = process_lf_##NAME,
+static const process_func process_functions_lf[] = { LIST_MEANS() };
+#undef X
+
+#define X(ID, NAME, ...) [ID] = process_f_##NAME,
+static const process_func process_functions_f[] = { LIST_MEANS() };
+#undef X
+
+int
+main(int argc, char *argv[])
+{
+       struct stream stream;
+       process_func process;
+       enum method method = ARITHMETIC;
+
+       ARGBEGIN {
+       case 'g':
+               method = GEOMETRIC;
+               break;
+       case 'h':
+               method = HARMONIC;
+               break;
+       case 'l':
+               method = LEHMER;
+               power = etolf_flag('l', UARGF());
+               break;
+       case 'p':
+               method = POWER;
+               power = etolf_flag('p', UARGF());
+               break;
+       case 'v':
+               method = VARIANCE;
+               break;
+       default:
+               usage();
+       } ARGEND;
+
+       if (argc)
+               usage();
+
+       eopen_stream(&stream, NULL);
+
+        if (stream.encoding == DOUBLE)
+                process = process_functions_lf[method];
+        else
+                process = process_functions_f[method];
+
+
+       if (DPRINTF_HEAD(STDOUT_FILENO, stream.frames, 1, 1, stream.pixfmt) < 0)
+               eprintf("dprintf:");
+       process(&stream);
+       if (stream.ptr)
+               eprintf("%s: incomplete frame\n", stream.file);
+       return 0;
+}
diff --git a/src/blind-stack.c b/src/blind-stack.c
index e74b62d..29e20b0 100644
--- a/src/blind-stack.c
+++ b/src/blind-stack.c
@@ -52,6 +52,7 @@ main(int argc, char *argv[])
                break;
        case 's':
                shortest = 1;
+               frames = SIZE_MAX;
                break;
        default:
                usage();
diff --git a/src/blind-temporal-arithm.c b/src/blind-temporal-arithm.c
new file mode 100644
index 0000000..f9f7b51
--- /dev/null
+++ b/src/blind-temporal-arithm.c
@@ -0,0 +1,94 @@
+/* See LICENSE file for copyright and license details. */
+#include "common.h"
+
+USAGE("operation")
+
+/* Because the syntax for a function returning a function pointer is 
disgusting. */
+typedef void (*process_func)(struct stream *stream, void *image);
+
+#define LIST_OPERATORS(PIXFMT, TYPE)\
+       X(add, *img + *buf,     PIXFMT, TYPE)\
+       X(mul, *img * *buf,     PIXFMT, TYPE)\
+       X(min, MIN(*img, *buf), PIXFMT, TYPE)\
+       X(max, MAX(*img, *buf), PIXFMT, TYPE)
+
+#define X(NAME, ALGO, PIXFMT, TYPE)\
+       static void\
+       process_##PIXFMT##_##NAME(struct stream *stream, void *image)\
+       {\
+               TYPE *buf, *img = image;\
+               size_t i, n, j = 0, m = stream->frame_size / sizeof(TYPE);\
+               do {\
+                       n = stream->ptr / sizeof(TYPE);\
+                       buf = (TYPE *)(stream->buf);\
+                       for (i = 0; i < n; i++, buf++) {\
+                               *img = ALGO;\
+                               if (++j == m) {\
+                                       j = 0;\
+                                       img = image;\
+                               } else {\
+                                       img++;\
+                               }\
+                       }\
+                       n *= sizeof(TYPE);\
+                       memmove(stream->buf, stream->buf + n, stream->ptr -= 
n);\
+               } while (eread_stream(stream, SIZE_MAX));\
+       }
+LIST_OPERATORS(lf, double)
+LIST_OPERATORS(f, float)
+#undef X
+
+static process_func
+get_process_lf(const char *operation)
+{
+#define X(NAME, _ALGO, PIXFMT, TYPE)\
+       if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
+       LIST_OPERATORS(lf, double)
+#undef X
+       eprintf("algorithm not recognised: %s\n", operation);
+       return NULL;
+}
+
+static process_func
+get_process_f(const char *operation)
+{
+#define X(NAME, _ALGO, PIXFMT, TYPE)\
+       if (!strcmp(operation, #NAME)) return process_##PIXFMT##_##NAME;
+       LIST_OPERATORS(f, float)
+#undef X
+       eprintf("algorithm not recognised: %s\n", operation);
+       return NULL;
+}
+
+int
+main(int argc, char *argv[])
+{
+       struct stream stream;
+       process_func process;
+       char *img;
+
+       UNOFLAGS(argc != 1);
+
+       eopen_stream(&stream, NULL);
+
+       if (stream.encoding == DOUBLE)
+               process = get_process_lf(argv[0]);
+       else
+               process = get_process_f(argv[0]);
+
+       echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
+       img = emalloc(stream.frame_size);
+       if (!eread_frame(&stream, img))
+               eprintf("video has no frames\n");
+
+       process(&stream, img);
+       if (stream.ptr)
+               eprintf("%s: incomplete frame\n", stream.file);
+
+       stream.frames = 1;
+       fprint_stream_head(stdout, &stream);
+       efflush(stdout, "<stdout>");
+       ewriteall(STDOUT_FILENO, img, stream.frame_size, "<stdout>");
+       free(img);
+       return 0;
+}
diff --git a/src/blind-temporal-mean.c b/src/blind-temporal-mean.c
index b80d2d3..12d0cad 100644
--- a/src/blind-temporal-mean.c
+++ b/src/blind-temporal-mean.c
@@ -1,8 +1,8 @@
 /* See LICENSE file for copyright and license details. */
 #include "common.h"
 
-USAGE("[-g | -h | -l power | -p power]")
-/* TODO add -w weight-stream */
+USAGE("[-g | -h | -l power | -p power | -v]")
+/* TODO add [-w weight-stream] for -l */
 
 /* Because the syntax for a function returning a function pointer is 
disgusting. */
 typedef void (*process_func)(struct stream *stream, void *buffer, void *image, 
size_t frame);
@@ -20,20 +20,23 @@ typedef void (*process_func)(struct stream *stream, void 
*buffer, void *image, s
 #define LIST_MEANS(TYPE)\
        /* [default] arithmetic mean */\
        X(ARITHMETIC, arithmetic, 1, COPY_FRAME,, *img1 += *buf,\
-         a = (TYPE)1.0 / (TYPE)frame, *img1 *= a)\
+         a = (TYPE)1 / (TYPE)frame, *img1 *= a)\
        /* geometric mean */\
        X(GEOMETRIC, geometric, 1, COPY_FRAME,, *img1 *= *buf,\
-         a = (TYPE)1.0 / (TYPE)frame, *img1 = nnpow(*img1, a))\
+         a = (TYPE)1 / (TYPE)frame, *img1 = nnpow(*img1, a))\
        /* harmonic mean */\
        X(HARMONIC, harmonic, 1, ZERO_AND_PROCESS_FRAME,, *img1 += (TYPE)1 / 
*buf,\
          a = (TYPE)frame, *img1 = a / *img1)\
-       /* lehmer mean */\
+       /* Lehmer mean */\
        X(LEHMER, lehmer, 2, ZERO_AND_PROCESS_FRAME, (a = (TYPE)power, b = a - 
(TYPE)1),\
          (*img1 += nnpow(*buf, a), *img2 += nnpow(*buf, b)),, *img1 /= *img2)\
        /* power mean (Hölder mean) (m = 2 for root square mean; m = 3 for 
cubic mean) */\
        X(POWER, power, 1, ZERO_AND_PROCESS_FRAME, a = (TYPE)power,\
-         *img1 += nnpow(*buf, a), (a = (TYPE)1 / (TYPE)frame, b = (TYPE)(1.0 / 
power)),\
-         *img1 = a * nnpow(*img1, b))
+         *img1 += nnpow(*buf, a), (a = (TYPE)1 / (TYPE)frame, b = (TYPE)(1. / 
power)), \
+         *img1 = a * nnpow(*img1, b))\
+       /* variance */\
+       X(VARIANCE, variance, 2, ZERO_AND_PROCESS_FRAME,, (*img1 += *buf * 
*buf, *img2 += *buf),\
+         a = (TYPE)1 / (TYPE)frame, *img1 = (*img1 - *img2 * *img2 * a) * a)
 
 enum first_frame_action {
        COPY_FRAME,
@@ -54,34 +57,37 @@ static double power;
        {\
                TYPE *buf = buffer, *img1 = image, a, b;\
                TYPE *img2 = (TYPE *)(((char *)image) + stream->frame_size);\
-               size_t x, y;\
-               if (!stream) {\
+               size_t x, y, z;\
+               if (!buf) {\
                        PRE_FINALISE;\
-                       for (y = 0; y < stream->height; y++)\
-                               for (x = 0; x < stream->width; x++, img1++, 
img2++, buf++)\
-                                       FINALISE_SUBCELL;\
+                       for (z = 0; z < stream->n_chan; z++)\
+                               for (y = 0; y < stream->height; y++)\
+                                       for (x = 0; x < stream->width; x++, 
img1++, img2++)\
+                                               FINALISE_SUBCELL;\
                } else {\
                        PRE_PROCESS;\
-                       for (y = 0; y < stream->height; y++)\
-                               for (x = 0; x < stream->width; x++, img1++, 
img2++, buf++)\
-                                       PROCESS_SUBCELL;\
+                       for (z = 0; z < stream->n_chan; z++)\
+                               for (y = 0; y < stream->height; y++)\
+                                       for (x = 0; x < stream->width; x++, 
img1++, img2++, buf++) {\
+                                               PROCESS_SUBCELL;\
+                                       }\
                }\
                (void) img2, (void) a, (void) b, (void) frame;\
        }
-#define X(...) MAKE_PROCESS(xyza, double, __VA_ARGS__)
+#define X(...) MAKE_PROCESS(lf, double, __VA_ARGS__)
 LIST_MEANS(double)
 #undef X
-#define X(...) MAKE_PROCESS(xyzaf, float, __VA_ARGS__)
+#define X(...) MAKE_PROCESS(f, float, __VA_ARGS__)
 LIST_MEANS(float)
 #undef X
 #undef MAKE_PROCESS
 
-#define X(ID, NAME, ...) [ID] = process_xyza_##NAME,
-static const process_func process_functions_xyza[] = { LIST_MEANS() };
+#define X(ID, NAME, ...) [ID] = process_lf_##NAME,
+static const process_func process_functions_lf[] = { LIST_MEANS() };
 #undef X
 
-#define X(ID, NAME, ...) [ID] = process_xyzaf_##NAME,
-static const process_func process_functions_xyzaf[] = { LIST_MEANS() };
+#define X(ID, NAME, ...) [ID] = process_f_##NAME,
+static const process_func process_functions_f[] = { LIST_MEANS() };
 #undef X
 
 int
@@ -109,6 +115,9 @@ main(int argc, char *argv[])
                method = POWER;
                power = etolf_flag('p', UARGF());
                break;
+       case 'v':
+               method = VARIANCE;
+               break;
        default:
                usage();
        } ARGEND;
@@ -130,12 +139,10 @@ main(int argc, char *argv[])
 
        eopen_stream(&stream, NULL);
 
-        if (!strcmp(stream.pixfmt, "xyza"))
-                process = process_functions_xyza[method];
-        else if (!strcmp(stream.pixfmt, "xyza f"))
-                process = process_functions_xyzaf[method];
+        if (stream.encoding == DOUBLE)
+                process = process_functions_lf[method];
         else
-                eprintf("pixel format %s is not supported, try xyza\n", 
stream.pixfmt);
+                process = process_functions_f[method];
 
        stream.frames = 1;
        echeck_dimensions(&stream, WIDTH | HEIGHT, NULL);
@@ -149,14 +156,14 @@ main(int argc, char *argv[])
 
        frames = 0;
        if (first_frame_action == COPY_FRAME) {
-               if (!eread_frame(&stream, buf))
+               if (!eread_frame(&stream, img))
                        eprintf("video is no frames\n");
                frames++;
        }
        for (; eread_frame(&stream, buf); frames++)
                process(&stream, buf, img, frames);
        if (!frames)
-               eprintf("video is no frames\n");
+               eprintf("video has no frames\n");
        process(&stream, NULL, img, frames);
 
        ewriteall(STDOUT_FILENO, img, stream.frame_size, "<stdout>");
diff --git a/src/stream.c b/src/stream.c
index 46fed64..b69f9a5 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -487,7 +487,7 @@ nprocess_multiple_streams(int status, struct stream 
*streams, size_t n_streams,
        while (n_streams) {
                n = SIZE_MAX;
                for (i = 0; i < n_streams; i++) {
-                       if (streams[i].ptr < sizeof(streams->buf) && 
!enread_stream(status, streams + i, SIZE_MAX)) {
+                       if (streams[i].ptr < streams->pixel_size && 
!enread_stream(status, streams + i, SIZE_MAX)) {
                                close(streams[i].fd);
                                streams[i].fd = -1;
                                if (shortest)
diff --git a/src/util.c b/src/util.c
index 3fc3716..41d8104 100644
--- a/src/util.c
+++ b/src/util.c
@@ -32,6 +32,11 @@ tollu(const char *s, unsigned long long int min, unsigned 
long long int max, uns
                errno = ERANGE;
                return -1;
        }
+       if (!isdigit(s[*s == 'x' || *s == 'X' || *s == '#']) ||
+           (*s == '0' && !isdigit(s[1 + (*s == 'x' || *s == 'o' || *s == 
'b')]))) {
+               errno = EINVAL;
+               return -1;
+       }
        if (tolower(*s) == 'x' || *s == '#')
                *out = strtoull(s + 1, &end, 16);
        else if (*s == '0' && tolower(s[1]) == 'x')

Reply via email to