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 }