stream.c - blind - suckless command-line video editing utility
(HTM) git clone git://git.suckless.org/blind
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
stream.c (22533B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include "common.h"
3
4 static inline int
5 get_dimension(int status, size_t *out, const char *s, const char *fname, const char *dim)
6 {
7 char *end;
8 errno = 0;
9 *out = strtoul(s, &end, 10);
10 if (errno == ERANGE && *s != '-')
11 enprintf(status, "%s: video is too %s\n", fname, dim);
12 return -(errno || *end);
13 }
14
15 static inline int
16 sread(int status, struct stream *stream)
17 {
18 ssize_t r;
19 r = read(stream->fd, stream->buf + stream->ptr, sizeof(stream->buf) - stream->ptr);
20 if (r < 0)
21 enprintf(status, "read %s:", stream->file);
22 if (r == 0)
23 return 0;
24 stream->ptr += (size_t)r;
25 return 1;
26 }
27
28 void
29 eninit_stream(int status, struct stream *stream)
30 {
31 size_t n;
32 char *p = NULL, *w, *h, *f;
33
34 fadvise_sequential(stream->fd, 0, 0);
35
36 if (stream->fd >= 0) {
37 for (stream->ptr = 0; !p; p = memchr(stream->buf, '\n', stream->ptr))
38 if (!sread(status, stream))
39 goto bad_format;
40 } else {
41 p = memchr(stream->buf, '\n', stream->ptr);
42 }
43
44 *p = '\0';
45 if (!(w = strchr(stream->buf, ' ')) ||
46 !(h = strchr(w + 1, ' ')) ||
47 !(f = strchr(h + 1, ' ')))
48 goto bad_format;
49 *w++ = *h++ = *f++ = '\0';
50
51 if (strlen(f) >= sizeof(stream->pixfmt))
52 goto bad_format;
53 strcpy(stream->pixfmt, f);
54 if (get_dimension(status, &stream->frames, stream->buf, stream->file, "long") ||
55 get_dimension(status, &stream->width, w, stream->file, "wide") ||
56 get_dimension(status, &stream->height, h, stream->file, "tall"))
57 goto bad_format;
58
59 if (!stream->width)
60 eprintf("%s: width is zero\n", stream->file);
61 if (!stream->height)
62 eprintf("%s: height is zero\n", stream->file);
63
64 n = (size_t)(p - stream->buf) + 1;
65 memmove(stream->buf, stream->buf + n, stream->ptr -= n);
66 while (stream->ptr < 5)
67 if (!sread(status, stream))
68 goto bad_format;
69 if (stream->buf[0] != '\0' ||
70 stream->buf[1] != 'u' || stream->buf[2] != 'i' ||
71 stream->buf[3] != 'v' || stream->buf[4] != 'f')
72 goto bad_format;
73 memmove(stream->buf, stream->buf + 5, stream->ptr -= 5);
74 stream->headlen = n + 5;
75
76 enset_pixel_format(status, stream, NULL);
77
78 stream->xptr = 0;
79
80 return;
81 bad_format:
82 enprintf(status, "%s: file format not supported\n", stream->file);
83 }
84
85
86 void
87 enopen_stream(int status, struct stream *stream, const char *file)
88 {
89 stream->file = file ? file : "<stdin>";
90 stream->fd = file ? enopen(status, file, O_RDONLY) : STDIN_FILENO;
91 eninit_stream(status, stream);
92 }
93
94
95 int
96 set_pixel_format(struct stream *stream, const char *pixfmt)
97 {
98 #define TEST_ENCODING_AGNOSTIC(FMT) (!strcmp(stream->pixfmt, FMT) || !strcmp(stream->pixfmt, FMT" f"))
99
100 if (pixfmt) {
101 pixfmt = get_pixel_format(pixfmt, stream->pixfmt[0] ? stream->pixfmt : "xyza");
102 if (strlen(pixfmt) >= sizeof(stream->pixfmt))
103 return -1;
104 strcpy(stream->pixfmt, pixfmt);
105 }
106
107 stream->n_chan = 4;
108 stream->alpha = UNPREMULTIPLIED;
109 stream->encoding = DOUBLE;
110 stream->endian = HOST;
111 stream->alpha_chan = 3;
112 stream->luma_chan = -1;
113
114 if (!strcmp(stream->pixfmt, "xyza")) {
115 stream->space = CIEXYZ;
116 } else if (!strcmp(stream->pixfmt, "xyza f")) {
117 stream->space = CIEXYZ;
118 stream->encoding = FLOAT;
119 } else if (!strcmp(stream->pixfmt, "raw0")) {
120 stream->space = YUV_NONLINEAR;
121 stream->encoding = UINT16;
122 stream->endian = LITTLE;
123 stream->alpha_chan = 0;
124 stream->luma_chan = 1;
125 } else if (!strcmp(stream->pixfmt, "raw1")) {
126 stream->space = YUV_NONLINEAR;
127 stream->encoding = UINT16;
128 stream->endian = LITTLE;
129 } else if (!strcmp(stream->pixfmt, "raw2a") || !strcmp(stream->pixfmt, "raw2")) {
130 stream->space = YUV_NONLINEAR;
131 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA;
132 stream->encoding = UINT16;
133 } else if (TEST_ENCODING_AGNOSTIC("raw3") || TEST_ENCODING_AGNOSTIC("raw3a")) {
134 stream->space = YUV_NONLINEAR;
135 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA;
136 stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : DOUBLE;
137 } else if (TEST_ENCODING_AGNOSTIC("raw4") || TEST_ENCODING_AGNOSTIC("raw4a")) {
138 stream->space = SRGB_NONLINEAR;
139 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA;
140 stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : DOUBLE;
141 } else if (TEST_ENCODING_AGNOSTIC("raw5") || TEST_ENCODING_AGNOSTIC("raw5a")) {
142 stream->space = SRGB;
143 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLIED : NO_ALPHA;
144 stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : DOUBLE;
145 } else {
146 return -1;
147 }
148
149 if (stream->alpha == NO_ALPHA) {
150 stream->n_chan -= 1;
151 stream->alpha_chan = -1;
152 }
153
154 if (stream->luma_chan == -1) {
155 if (stream->space == CIEXYZ)
156 stream->luma_chan = 1;
157 else if (stream->space == YUV_NONLINEAR)
158 stream->luma_chan = 0;
159 }
160
161 switch (stream->encoding) {
162 case FLOAT:
163 stream->chan_size = sizeof(float);
164 break;
165 case DOUBLE:
166 stream->chan_size = sizeof(double);
167 break;
168 case LONG_DOUBLE:
169 stream->chan_size = sizeof(long double);
170 break;
171 case UINT8:
172 stream->chan_size = sizeof(uint8_t);
173 break;
174 case UINT16:
175 stream->chan_size = sizeof(uint16_t);
176 break;
177 case UINT32:
178 stream->chan_size = sizeof(uint32_t);
179 break;
180 case UINT64:
181 stream->chan_size = sizeof(uint64_t);
182 break;
183 default:
184 abort();
185 }
186
187 stream->pixel_size = stream->n_chan * stream->chan_size;
188 stream->row_size = stream->pixel_size * stream->width;
189 stream->col_size = stream->pixel_size * stream->height;
190 stream->frame_size = stream->pixel_size * stream->height * stream->width;
191 return 0;
192
193 #undef TEST_ENCODING_AGNOSTIC
194 }
195
196 void
197 enset_pixel_format(int status, struct stream *stream, const char *pixfmt)
198 {
199 if (set_pixel_format(stream, pixfmt)) {
200 if (pixfmt)
201 enprintf(status, "pixel format %s is not supported, try xyza\n", pixfmt);
202 else
203 enprintf(status, "%s: unsupported pixel format: %s\n",
204 stream->file, stream->pixfmt);
205 }
206 }
207
208
209 void
210 fprint_stream_head(FILE *fp, struct stream *stream)
211 {
212 FPRINTF_HEAD(fp, stream->frames, stream->width, stream->height, stream->pixfmt);
213 }
214
215
216 int
217 dprint_stream_head(int fd, struct stream *stream)
218 {
219 return DPRINTF_HEAD(fd, stream->frames, stream->width, stream->height, stream->pixfmt);
220 }
221
222
223 size_t
224 enread_stream(int status, struct stream *stream, size_t n)
225 {
226 ssize_t r = read(stream->fd, stream->buf + stream->ptr,
227 MIN(sizeof(stream->buf) - stream->ptr, n));
228 if (r < 0)
229 enprintf(status, "read %s:", stream->file);
230 stream->ptr += (size_t)r;
231 return (size_t)r;
232 }
233
234
235 void
236 eninf_check_fd(int status, int fd, const char *file)
237 {
238 struct stat st;
239 if (fstat(fd, &st))
240 enprintf(status, "fstat %s:", file);
241 if (S_ISREG(st.st_mode))
242 enprintf(status, "%s is a regular file, refusing infinite write\n", file);
243 }
244
245
246 void
247 encheck_dimensions(int status, const struct stream *stream, enum dimension dimensions, const char *prefix)
248 {
249 size_t n;
250
251 if (!stream->pixel_size)
252 enprintf(status, "%s: %s%svideo frame doesn't have a pixel size\n",
253 stream->file, prefix ? prefix : "",
254 (prefix && *prefix) ? " " : "");
255
256 n = SIZE_MAX / stream->pixel_size;
257
258 if ((dimensions & WIDTH) && stream->width > n)
259 enprintf(status, "%s: %s%svideo frame is too wide\n",
260 stream->file, prefix ? prefix : "",
261 (prefix && *prefix) ? " " : "");
262
263 if ((dimensions & HEIGHT) && stream->height > n)
264 enprintf(status, "%s: %s%svideo frame is too wide\n",
265 stream->file, prefix ? prefix : "",
266 (prefix && *prefix) ? " " : "");
267
268 if (!stream->width || !stream->height)
269 return;
270
271 if ((dimensions & (WIDTH | HEIGHT)) == (WIDTH | HEIGHT)) {
272 if (stream->width > n / stream->height)
273 enprintf(status, "%s: %s%svideo frame is too large\n",
274 stream->file, prefix ? prefix : "",
275 (prefix && *prefix) ? " " : "");
276 }
277
278 if (!(dimensions & LENGTH))
279 return;
280 if (dimensions & WIDTH)
281 n /= stream->width;
282 if (dimensions & HEIGHT)
283 n /= stream->height;
284
285 if (stream->frames > n)
286 enprintf(status, "%s: %s%svideo is too large\n",
287 stream->file, prefix ? prefix : "",
288 (prefix && *prefix) ? " " : "");
289 }
290
291
292 void
293 encheck_compat(int status, const struct stream *a, const struct stream *b)
294 {
295 if (a->width != b->width || a->height != b->height)
296 enprintf(status, "videos do not have the same geometry\n");
297 if (strcmp(a->pixfmt, b->pixfmt))
298 enprintf(status, "videos use incompatible pixel formats\n");
299 }
300
301
302 const char *
303 get_pixel_format(const char *specified, const char *current)
304 {
305 enum colour_space space = CIEXYZ;
306 enum alpha alpha = UNPREMULTIPLIED;
307 enum encoding encoding = UINT16;
308 int level = -1;
309 size_t n = strlen(specified);
310
311 if ((n >= 2 && !strcmp(specified - 2, " f")) ||
312 !strcmp(specified, "raw0") || !strcmp(specified, "raw1") ||
313 !strcmp(specified, "raw2") || !strcmp(specified, "raw2a"))
314 return specified;
315
316 if (!strcmp(current, "xyza")) space = CIEXYZ, encoding = DOUBLE;
317 else if (!strcmp(current, "xyza f")) space = CIEXYZ, encoding = FLOAT;
318 else if (!strcmp(current, "raw0")) level = 0;
319 else if (!strcmp(current, "raw1")) level = 1;
320 else if (!strcmp(current, "raw2")) level = 2, alpha = NO_ALPHA;
321 else if (!strcmp(current, "raw2a")) level = 2;
322 else if (!strcmp(current, "raw3")) level = 3, encoding = DOUBLE, alpha = NO_ALPHA;
323 else if (!strcmp(current, "raw3a")) level = 3, encoding = DOUBLE;
324 else if (!strcmp(current, "raw3 f")) level = 3, encoding = FLOAT, alpha = NO_ALPHA;
325 else if (!strcmp(current, "raw3a f")) level = 3, encoding = FLOAT;
326 else if (!strcmp(current, "raw4")) level = 4, encoding = DOUBLE, alpha = NO_ALPHA;
327 else if (!strcmp(current, "raw4a")) level = 4, encoding = DOUBLE;
328 else if (!strcmp(current, "raw4 f")) level = 4, encoding = FLOAT, alpha = NO_ALPHA;
329 else if (!strcmp(current, "raw4a f")) level = 4, encoding = FLOAT;
330 else if (!strcmp(current, "raw5")) level = 5, encoding = DOUBLE, alpha = NO_ALPHA;
331 else if (!strcmp(current, "raw5a")) level = 5, encoding = DOUBLE;
332 else if (!strcmp(current, "raw5 f")) level = 5, encoding = FLOAT, alpha = NO_ALPHA;
333 else if (!strcmp(current, "raw5a f")) level = 5, encoding = FLOAT;
334 else
335 return specified;
336
337 if (!strcmp(specified, "f")) encoding = FLOAT;
338 else if (!strcmp(specified, "!f")) encoding = DOUBLE;
339 else if (!strcmp(specified, "xyza")) level = -1, alpha = UNPREMULTIPLIED, space = CIEXYZ;
340 else if (!strcmp(specified, "raw3")) level = 3, alpha = NO_ALPHA;
341 else if (!strcmp(specified, "raw3a")) level = 3, alpha = UNPREMULTIPLIED;
342 else if (!strcmp(specified, "raw4")) level = 4, alpha = NO_ALPHA;
343 else if (!strcmp(specified, "raw4a")) level = 4, alpha = UNPREMULTIPLIED;
344 else if (!strcmp(specified, "raw5")) level = 5, alpha = NO_ALPHA;
345 else if (!strcmp(specified, "raw5a")) level = 5, alpha = UNPREMULTIPLIED;
346 else if (!strcmp(specified, "xyza !f")) return "xyza";
347 else if (!strcmp(specified, "raw3 !f")) return "raw3";
348 else if (!strcmp(specified, "raw3a !f")) return "raw3a";
349 else if (!strcmp(specified, "raw4 !f")) return "raw4";
350 else if (!strcmp(specified, "raw4a !f")) return "raw4a";
351 else if (!strcmp(specified, "raw5 !f")) return "raw5";
352 else if (!strcmp(specified, "raw5a !f")) return "raw5a";
353 else
354 return specified;
355
356 if (level == 0 && encoding == UINT16) return "raw0";
357 else if (level == 1 && encoding == UINT16) return "raw1";
358 else if (level == 2 && encoding == UINT16) return alpha ? "raw2a" : "raw2";
359 else if (level == 3 && encoding == DOUBLE) return alpha ? "raw3a" : "raw3";
360 else if (level == 3 && encoding == FLOAT) return alpha ? "raw3a f" : "raw3 f";
361 else if (level == 4 && encoding == DOUBLE) return alpha ? "raw4a" : "raw4";
362 else if (level == 4 && encoding == FLOAT) return alpha ? "raw4a f" : "raw4 f";
363 else if (level == 5 && encoding == DOUBLE) return alpha ? "raw5a" : "raw5";
364 else if (level == 5 && encoding == FLOAT) return alpha ? "raw5a f" : "raw5 f";
365 else if (level < 0 && space == CIEXYZ && alpha == UNPREMULTIPLIED)
366 return encoding == FLOAT ? "xyza f" : encoding == DOUBLE ? "xyza" : specified;
367 else
368 return specified;
369 }
370
371
372 const char *
373 nselect_print_format(int status, const char *format, enum encoding encoding, const char *fmt)
374 {
375 static char retbuf[512];
376 int with_plus = 0, inttyped = -1;
377 const char *f = "", *orig = fmt;
378 char *proto = alloca((fmt ? strlen(fmt) : 0) + sizeof("%+#.50llx")), *p;
379 char *ret = retbuf;
380 size_t n, len;
381
382 if (!orig)
383 goto check_done;
384
385 for (; *fmt == '+'; fmt++)
386 with_plus = 1;
387 f = fmt + strspn(fmt, "0123456789");
388 if (f[0] && f[1])
389 enprintf(status, "invalid format: %s\n", orig);
390
391 switch (*f) {
392 case '\0':
393 inttyped = -1;
394 break;
395 case 'd': case 'i':
396 inttyped = 1;
397 break;
398 case 'a': case 'A':
399 case 'e': case 'E':
400 case 'f': case 'F':
401 case 'g': case 'G':
402 inttyped = 0;
403 break;
404 default:
405 enprintf(status, "invalid format: %s\n", orig);
406 }
407
408 switch (encoding) {
409 case FLOAT:
410 case DOUBLE:
411 case LONG_DOUBLE:
412 if (inttyped == 1)
413 enprintf(status, "invalid format `%s' is incompatible with the video format\n", orig);
414 inttyped = 0;
415 break;
416 case UINT8:
417 case UINT16:
418 case UINT32:
419 case UINT64:
420 if (*f != *fmt)
421 enprintf(status, "invalid format: %s\n", orig);
422 if (inttyped == 0)
423 enprintf(status, "invalid format `%s' is incompatible with the video format\n", orig);
424 inttyped = 1;
425 break;
426 default:
427 abort();
428 }
429 check_done:
430
431 p = proto;
432 *p++ = '%';
433 if (with_plus)
434 *p++ = '+';
435
436 if (orig && *f != *fmt) {
437 *p++ = '.';
438 p = stpncpy(p, fmt, (size_t)(f - fmt));
439 } else if (orig && inttyped && *f != 'a' && *f != 'A') {
440 *p++ = '.';
441 *p++ = '2';
442 *p++ = '5';
443 }
444
445 inttyped = 1;
446 switch (encoding) {
447 case FLOAT:
448 inttyped = 0;
449 break;
450 case DOUBLE:
451 *p++ = 'l';
452 inttyped = 0;
453 break;
454 case LONG_DOUBLE:
455 *p++ = 'L';
456 inttyped = 0;
457 break;
458 case UINT8:
459 fmt = PRIi8;
460 break;
461 case UINT16:
462 fmt = PRIi16;
463 break;
464 case UINT32:
465 fmt = PRIi32;
466 break;
467 case UINT64:
468 fmt = PRIi64;
469 break;
470 default:
471 abort();
472 }
473
474 if (inttyped)
475 while (*fmt == 'l' || *fmt == 'L')
476 *p++ = *fmt++;
477
478 switch (orig ? *f : '\0') {
479 case '\0':
480 *p++ = inttyped ? 'i' : 'f';
481 break;
482 case 'd': case 'i':
483 *p++ = 'i';
484 break;
485 case 'a': case 'A':
486 *p++ = 'a';
487 break;
488 case 'e': case 'E':
489 *p++ = 'e';
490 break;
491 case 'f': case 'F':
492 *p++ = 'f';
493 break;
494 case 'g': case 'G':
495 *p++ = 'g';
496 break;
497 }
498
499 *p = '\0';
500
501 len = strlen(proto);
502 for (n = 1, f = format; *f; f++) {
503 if (f[0] == '%' && f[1] == '!') {
504 f++;
505 n += len;
506 } else {
507 n++;
508 }
509 }
510
511 if (n > sizeof(retbuf))
512 ret = enmalloc(status, n);
513 for (p = ret, f = format; *f; f++) {
514 if (f[0] == '%' && f[1] == '!') {
515 f++;
516 p = stpcpy(p, proto);
517 } else {
518 *p++ = *f;
519 }
520 }
521
522 return ret;
523 }
524
525
526 int
527 enread_segment(int status, struct stream *stream, void *buf, size_t n)
528 {
529 char *buffer = buf;
530 ssize_t r;
531 size_t m;
532
533 if (stream->ptr) {
534 m = MIN(stream->ptr, n);
535 memcpy(buffer + stream->xptr, stream->buf, m);
536 memmove(stream->buf, stream->buf + m, stream->ptr -= m);
537 stream->xptr += m;
538 }
539
540 for (; stream->xptr < n; stream->xptr += (size_t)r) {
541 r = read(stream->fd, buffer + stream->xptr, n - stream->xptr);
542 if (r < 0) {
543 enprintf(status, "read %s:", stream->file);
544 } else if (r == 0) {
545 if (!stream->xptr)
546 break;
547 enprintf(status, "%s: incomplete frame", stream->file);
548 }
549 }
550
551 if (!stream->xptr)
552 return 0;
553 stream->xptr -= n;
554 return 1;
555 }
556
557
558 size_t
559 ensend_frames(int status, struct stream *stream, int outfd, size_t frames, const char *outfname)
560 {
561 size_t h, w, p, n, ret;
562
563 for (ret = 0; ret < frames; ret++) {
564 for (p = stream->pixel_size; p; p--) {
565 for (h = stream->height; h; h--) {
566 for (w = stream->width; w; w -= n) {
567 if (!stream->ptr && !enread_stream(status, stream, w))
568 goto done;
569 n = MIN(stream->ptr, w);
570 if (outfd >= 0)
571 enwriteall(status, outfd, stream->buf, n, outfname);
572 memmove(stream->buf, stream->buf + n, stream->ptr -= n);
573 }
574 }
575 }
576 }
577
578 return ret;
579 done:
580 if (p != stream->pixel_size || h != stream->height || w != stream->width)
581 enprintf(status, "%s: incomplete frame", stream->file);
582 return ret;
583 }
584
585
586 size_t
587 ensend_rows(int status, struct stream *stream, int outfd, size_t rows, const char *outfname)
588 {
589 size_t w, p, n, ret;
590
591 for (ret = 0; ret < rows; ret++) {
592 for (p = stream->pixel_size; p; p--) {
593 for (w = stream->width; w; w -= n) {
594 if (!stream->ptr && !enread_stream(status, stream, w))
595 goto done;
596 n = MIN(stream->ptr, w);
597 if (outfd >= 0)
598 enwriteall(status, outfd, stream->buf, n, outfname);
599 memmove(stream->buf, stream->buf + n, stream->ptr -= n);
600 }
601 }
602 }
603
604 return ret;
605 done:
606 if (p != stream->pixel_size || w != stream->width)
607 enprintf(status, "%s: incomplete row", stream->file);
608 return ret;
609 }
610
611
612 size_t
613 ensend_pixels(int status, struct stream *stream, int outfd, size_t pixels, const char *outfname)
614 {
615 size_t p, n, ret;
616
617 for (ret = 0; ret < pixels; ret++) {
618 for (p = stream->pixel_size; p; p -= n) {
619 if (!stream->ptr && !enread_stream(status, stream, p))
620 goto done;
621 n = MIN(stream->ptr, p);
622 if (outfd >= 0)
623 enwriteall(status, outfd, stream->buf, n, outfname);
624 memmove(stream->buf, stream->buf + n, stream->ptr -= n);
625 }
626 }
627
628 return ret;
629 done:
630 if (p != stream->pixel_size)
631 enprintf(status, "%s: incomplete pixel", stream->file);
632 return ret;
633 }
634
635
636 int
637 ensend_stream(int status, struct stream *stream, int outfd, const char *outfname)
638 {
639 do {
640 if (writeall(outfd, stream->buf, stream->ptr)) {
641 if (outfname)
642 eprintf("write %s:", outfname);
643 return -1;
644 }
645 stream->ptr = 0;
646 } while (enread_stream(status, stream, SIZE_MAX));
647 return 0;
648 }
649
650
651 void
652 nprocess_stream(int status, struct stream *stream, void (*process)(struct stream *stream, size_t n))
653 {
654 size_t n;
655 do {
656 n = stream->ptr - (stream->ptr % stream->pixel_size);
657 process(stream, n);
658 memmove(stream->buf, stream->buf + n, stream->ptr -= n);
659 } while (enread_stream(status, stream, SIZE_MAX));
660 }
661
662
663 void
664 nprocess_each_frame_segmented(int status, struct stream *stream, int output_fd, const char* output_fname,
665 void (*process)(struct stream *stream, size_t n, size_t frame))
666 {
667 size_t frame, r, n;
668 encheck_dimensions(status, stream, WIDTH | HEIGHT, NULL);
669 for (frame = 0; frame < stream->frames; frame++) {
670 for (n = stream->frame_size; n; n -= r) {
671 if (stream->ptr < n && !enread_stream(status, stream, SIZE_MAX))
672 enprintf(status, "%s: file is shorter than expected\n", stream->file);
673 r = stream->ptr - (stream->ptr % stream->pixel_size);
674 r = MIN(r, n);
675 process(stream, r, frame);
676 enwriteall(status, output_fd, stream->buf, r, output_fname);
677 memmove(stream->buf, stream->buf + r, stream->ptr -= r);
678 }
679 }
680 }
681
682
683 void
684 nprocess_two_streams(int status, struct stream *left, struct stream *right, int output_fd, const char* output_fname,
685 void (*process)(struct stream *left, struct stream *right, size_t n))
686 {
687 size_t n;
688 int have_both = 1;
689
690 encheck_compat(status, left, right);
691
692 while (have_both) {
693 if (left->ptr < sizeof(left->buf) && !enread_stream(status, left, SIZE_MAX)) {
694 close(left->fd);
695 left->fd = -1;
696 have_both = 0;
697 }
698 if (right->ptr < sizeof(right->buf) && !enread_stream(status, right, SIZE_MAX)) {
699 close(right->fd);
700 right->fd = -1;
701 have_both = 0;
702 }
703
704 n = MIN(left->ptr, right->ptr);
705 n -= n % left->pixel_size;
706 left->ptr -= n;
707 right->ptr -= n;
708
709 process(left, right, n);
710
711 enwriteall(status, output_fd, left->buf, n, output_fname);
712 if ((n & 3) || left->ptr != right->ptr) {
713 memmove(left->buf, left->buf + n, left->ptr);
714 memmove(right->buf, right->buf + n, right->ptr);
715 }
716 }
717
718 if (right->fd >= 0)
719 close(right->fd);
720
721 enwriteall(status, output_fd, left->buf, left->ptr, output_fname);
722
723 if (left->fd >= 0) {
724 for (;;) {
725 left->ptr = 0;
726 if (!enread_stream(status, left, SIZE_MAX)) {
727 close(left->fd);
728 left->fd = -1;
729 break;
730 }
731 enwriteall(status, output_fd, left->buf, left->ptr, output_fname);
732 }
733 }
734 }
735
736
737 void
738 nprocess_multiple_streams(int status, struct stream *streams, size_t n_streams, int output_fd, const char* output_fname,
739 int shortest, void (*process)(struct stream *streams, size_t n_streams, size_t n))
740 {
741 size_t closed, i, j, n;
742
743 for (i = 1; i < n_streams; i++)
744 encheck_compat(status, streams + i, streams);
745
746 while (n_streams) {
747 n = SIZE_MAX;
748 for (i = 0; i < n_streams; i++) {
749 if (streams[i].ptr < streams->pixel_size && !enread_stream(status, streams + i, SIZE_MAX)) {
750 close(streams[i].fd);
751 streams[i].fd = -1;
752 if (shortest)
753 return;
754 }
755 if (streams[i].ptr && streams[i].ptr < n)
756 n = streams[i].ptr;
757 }
758 if (n == SIZE_MAX)
759 break;
760 n -= n % streams->pixel_size;
761
762 process(streams, n_streams, n);
763 enwriteall(status, output_fd, streams->buf, n, output_fname);
764
765 closed = SIZE_MAX;
766 for (i = 0; i < n_streams; i++) {
767 if (streams[i].ptr)
768 memmove(streams[i].buf, streams[i].buf + n, streams[i].ptr -= n);
769 if (streams[i].ptr < streams->pixel_size && streams[i].fd < 0 && closed == SIZE_MAX)
770 closed = i;
771 }
772 if (closed != SIZE_MAX) {
773 for (i = (j = closed) + 1; i < n_streams; i++)
774 if (streams[i].ptr >= streams->pixel_size || streams[i].fd >= 0)
775 streams[j++] = streams[i];
776 n_streams = j;
777 }
778 }
779 }
780
781
782 void
783 nprocess_each_frame_two_streams(int status, struct stream *left, struct stream *right, int output_fd, const char* output_fname,
784 void (*process)(char *restrict output, char *restrict lbuf, char *restrict rbuf,
785 struct stream *left, struct stream *right))
786 {
787 char *lbuf, *rbuf, *image;
788
789 encheck_dimensions(status, left, WIDTH | HEIGHT, NULL);
790 encheck_dimensions(status, right, WIDTH | HEIGHT, NULL);
791
792 if (left->frame_size > SIZE_MAX - left->frame_size ||
793 2 * left->frame_size > SIZE_MAX - right->frame_size)
794 enprintf(status, "video frame is too large\n");
795
796 image = mmap(0, 2 * left->frame_size + right->frame_size,
797 PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
798 if (image == MAP_FAILED)
799 enprintf(status, "mmap:");
800 lbuf = image + 1 * left->frame_size;
801 rbuf = image + 2 * left->frame_size;
802
803 for (;;) {
804 if (!enread_frame(status, left, lbuf)) {
805 close(left->fd);
806 left->fd = -1;
807 break;
808 }
809 if (!enread_frame(status, right, rbuf)) {
810 close(right->fd);
811 right->fd = -1;
812 break;
813 }
814
815 process(image, lbuf, rbuf, left, right);
816 enwriteall(status, output_fd, image, left->frame_size, output_fname);
817 }
818
819 if (right->fd >= 0)
820 close(right->fd);
821
822 if (left->fd >= 0) {
823 memcpy(image, lbuf, left->ptr);
824 while (enread_frame(status, left, lbuf))
825 enwriteall(status, output_fd, image, left->frame_size, output_fname);
826 }
827
828 munmap(image, 2 * left->frame_size + right->frame_size);
829 }