stream.c - blind - suckless command-line video editing utility | |
git clone git://git.suckless.org/blind | |
Log | |
Files | |
Refs | |
README | |
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,… | |
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->b… | |
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', … | |
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->… | |
55 get_dimension(status, &stream->width, w, stream->… | |
56 get_dimension(status, &stream->height, h, stream->… | |
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_FILEN… | |
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) || !st… | |
99 | |
100 if (pixfmt) { | |
101 pixfmt = get_pixel_format(pixfmt, stream->pixfmt[0] ? st… | |
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->p… | |
130 stream->space = YUV_NONLINEAR; | |
131 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLI… | |
132 stream->encoding = UINT16; | |
133 } else if (TEST_ENCODING_AGNOSTIC("raw3") || TEST_ENCODING_AGNOS… | |
134 stream->space = YUV_NONLINEAR; | |
135 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLI… | |
136 stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : … | |
137 } else if (TEST_ENCODING_AGNOSTIC("raw4") || TEST_ENCODING_AGNOS… | |
138 stream->space = SRGB_NONLINEAR; | |
139 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLI… | |
140 stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : … | |
141 } else if (TEST_ENCODING_AGNOSTIC("raw5") || TEST_ENCODING_AGNOS… | |
142 stream->space = SRGB; | |
143 stream->alpha = stream->pixfmt[4] == 'a' ? UNPREMULTIPLI… | |
144 stream->encoding = strlen(stream->pixfmt) > 5 ? FLOAT : … | |
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 * strea… | |
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 support… | |
202 else | |
203 enprintf(status, "%s: unsupported pixel format: … | |
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, … | |
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->h… | |
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 infinit… | |
243 } | |
244 | |
245 | |
246 void | |
247 encheck_dimensions(int status, const struct stream *stream, enum dimensi… | |
248 { | |
249 size_t n; | |
250 | |
251 if (!stream->pixel_size) | |
252 enprintf(status, "%s: %s%svideo frame doesn't have a pix… | |
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 lar… | |
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 *… | |
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\… | |
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 =… | |
317 else if (!strcmp(current, "xyza f")) space = CIEXYZ, encoding =… | |
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_ALPH… | |
321 else if (!strcmp(current, "raw2a")) level = 2; | |
322 else if (!strcmp(current, "raw3")) level = 3, encoding = DOUB… | |
323 else if (!strcmp(current, "raw3a")) level = 3, encoding = DOUB… | |
324 else if (!strcmp(current, "raw3 f")) level = 3, encoding = FLOA… | |
325 else if (!strcmp(current, "raw3a f")) level = 3, encoding = FLOA… | |
326 else if (!strcmp(current, "raw4")) level = 4, encoding = DOUB… | |
327 else if (!strcmp(current, "raw4a")) level = 4, encoding = DOUB… | |
328 else if (!strcmp(current, "raw4 f")) level = 4, encoding = FLOA… | |
329 else if (!strcmp(current, "raw4a f")) level = 4, encoding = FLOA… | |
330 else if (!strcmp(current, "raw5")) level = 5, encoding = DOUB… | |
331 else if (!strcmp(current, "raw5a")) level = 5, encoding = DOUB… | |
332 else if (!strcmp(current, "raw5 f")) level = 5, encoding = FLOA… | |
333 else if (!strcmp(current, "raw5a f")) level = 5, encoding = FLOA… | |
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 = UNP… | |
340 else if (!strcmp(specified, "raw3")) level = 3, alpha = NO_A… | |
341 else if (!strcmp(specified, "raw3a")) level = 3, alpha = UNPR… | |
342 else if (!strcmp(specified, "raw4")) level = 4, alpha = NO_A… | |
343 else if (!strcmp(specified, "raw4a")) level = 4, alpha = UNPR… | |
344 else if (!strcmp(specified, "raw5")) level = 5, alpha = NO_A… | |
345 else if (!strcmp(specified, "raw5a")) level = 5, alpha = UNPR… | |
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… | |
359 else if (level == 3 && encoding == DOUBLE) return alpha ? "raw3a… | |
360 else if (level == 3 && encoding == FLOAT) return alpha ? "raw3a… | |
361 else if (level == 4 && encoding == DOUBLE) return alpha ? "raw4a… | |
362 else if (level == 4 && encoding == FLOAT) return alpha ? "raw4a… | |
363 else if (level == 5 && encoding == DOUBLE) return alpha ? "raw5a… | |
364 else if (level == 5 && encoding == FLOAT) return alpha ? "raw5a… | |
365 else if (level < 0 && space == CIEXYZ && alpha == UNPREMULTIPLIE… | |
366 return encoding == FLOAT ? "xyza f" : encoding == DOUBLE… | |
367 else | |
368 return specified; | |
369 } | |
370 | |
371 | |
372 const char * | |
373 nselect_print_format(int status, const char *format, enum encoding encod… | |
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… | |
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 incompa… | |
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 incompa… | |
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->… | |
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-… | |
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 frame… | |
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_stre… | |
568 goto done; | |
569 n = MIN(stream->ptr, w); | |
570 if (outfd >= 0) | |
571 enwriteall(status, outfd… | |
572 memmove(stream->buf, stream->buf… | |
573 } | |
574 } | |
575 } | |
576 } | |
577 | |
578 return ret; | |
579 done: | |
580 if (p != stream->pixel_size || h != stream->height || w != strea… | |
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, c… | |
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(statu… | |
595 goto done; | |
596 n = MIN(stream->ptr, w); | |
597 if (outfd >= 0) | |
598 enwriteall(status, outfd, stream… | |
599 memmove(stream->buf, stream->buf + n, st… | |
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 pixel… | |
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, strea… | |
620 goto done; | |
621 n = MIN(stream->ptr, p); | |
622 if (outfd >= 0) | |
623 enwriteall(status, outfd, stream->buf, n… | |
624 memmove(stream->buf, stream->buf + n, stream->pt… | |
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 *… | |
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)(struc… | |
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 out… | |
665 void (*process)(struct stream *stream, siz… | |
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, st… | |
672 enprintf(status, "%s: file is shorter th… | |
673 r = stream->ptr - (stream->ptr % stream->pixel_s… | |
674 r = MIN(r, n); | |
675 process(stream, r, frame); | |
676 enwriteall(status, output_fd, stream->buf, r, ou… | |
677 memmove(stream->buf, stream->buf + r, stream->pt… | |
678 } | |
679 } | |
680 } | |
681 | |
682 | |
683 void | |
684 nprocess_two_streams(int status, struct stream *left, struct stream *rig… | |
685 void (*process)(struct stream *left, struct stream … | |
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(stat… | |
694 close(left->fd); | |
695 left->fd = -1; | |
696 have_both = 0; | |
697 } | |
698 if (right->ptr < sizeof(right->buf) && !enread_stream(st… | |
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->p… | |
732 } | |
733 } | |
734 } | |
735 | |
736 | |
737 void | |
738 nprocess_multiple_streams(int status, struct stream *streams, size_t n_s… | |
739 int shortest, void (*process)(struct stream *s… | |
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 && !enr… | |
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_fn… | |
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 +… | |
769 if (streams[i].ptr < streams->pixel_size && stre… | |
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_siz… | |
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 … | |
784 void (*process)(char *restrict output, c… | |
785 struct stream *left, str… | |
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,… | |
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, o… | |
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… | |
826 } | |
827 | |
828 munmap(image, 2 * left->frame_size + right->frame_size); | |
829 } |