|
| 1 | +/** |
| 2 | + * Simple Duck IVF mixer for VP8/VP9/AV1. |
| 3 | + * |
| 4 | + * Can be used eg. to process exported (--record) VP9 files, that cannot be |
| 5 | + * simply concatenated as eg. H.264 frames. Specification: |
| 6 | + * <https://wiki.multimedia.cx/index.php/Duck_IVF> |
| 7 | + */ |
| 8 | + |
| 9 | +#include <assert.h> |
| 10 | +#include <errno.h> |
| 11 | +#include <stdint.h> |
| 12 | +#include <stdio.h> |
| 13 | +#include <stdlib.h> |
| 14 | +#include <string.h> |
| 15 | + |
| 16 | +const char * |
| 17 | +get_fcc(const char *ext) |
| 18 | +{ |
| 19 | + if (strcasecmp(ext, "vp8") == 0) { |
| 20 | + return "VP80"; |
| 21 | + } |
| 22 | + if (strcasecmp(ext, "vp9") == 0) { |
| 23 | + return "VP90"; |
| 24 | + } |
| 25 | + if (strcasecmp(ext, "av1") == 0) { |
| 26 | + return "av01"; |
| 27 | + } |
| 28 | + fprintf(stderr, "Unsupported extension: %s\n", ext); |
| 29 | + exit(EXIT_FAILURE); |
| 30 | +} |
| 31 | + |
| 32 | +#define OUTPUT(val) fwrite(&(val), sizeof (val), 1, stdout) |
| 33 | +int |
| 34 | +main(int argc, char *argv[]) |
| 35 | +{ |
| 36 | + if (argc <= 4) { |
| 37 | + fprintf( |
| 38 | + stderr, |
| 39 | + "Usage:\n\t%s <width> <height> <fps> INFILES > out.ivf\n", |
| 40 | + argv[0]); |
| 41 | + return EXIT_FAILURE; |
| 42 | + } |
| 43 | + argv += 1; // skip progname |
| 44 | + |
| 45 | + // file header |
| 46 | + printf("DKIF"); |
| 47 | + const uint16_t version = 0; |
| 48 | + OUTPUT(version); |
| 49 | + const uint16_t hdr_len = 32; |
| 50 | + OUTPUT(hdr_len); |
| 51 | + printf("%s", get_fcc(strchr(argv[3], '.') + 1)); // codec FourCC |
| 52 | + const uint16_t width = atoi(*argv++); |
| 53 | + const uint16_t height = atoi(*argv++); |
| 54 | + const uint32_t fps = atoi(*argv++); |
| 55 | + assert(width * height * fps != 0); |
| 56 | + OUTPUT(width); |
| 57 | + OUTPUT(height); |
| 58 | + const uint32_t fps_den = fps; |
| 59 | + OUTPUT(fps_den); |
| 60 | + const uint32_t fps_num = 1; |
| 61 | + OUTPUT(fps_num); |
| 62 | + const uint32_t nr_frames = argc - 3; |
| 63 | + OUTPUT(nr_frames); |
| 64 | + const uint32_t unused = 0; |
| 65 | + OUTPUT(unused); |
| 66 | + |
| 67 | + uint64_t pts = 0; |
| 68 | + while (*argv != NULL) { // iterate over files |
| 69 | + FILE *f = fopen(*argv++, "rb"); |
| 70 | + assert(f != NULL); |
| 71 | + |
| 72 | + fseek(f, 0, SEEK_END); |
| 73 | + const uint32_t file_len = ftell(f); |
| 74 | + fseek(f, 0, SEEK_SET); |
| 75 | + |
| 76 | + // frame header |
| 77 | + OUTPUT(file_len); |
| 78 | + OUTPUT(pts); |
| 79 | + pts += 1; |
| 80 | + |
| 81 | + // copy the content of the file to output |
| 82 | + int c = 0; |
| 83 | + while ((c = fgetc(f)) != EOF) { |
| 84 | + putc(c, stdout); |
| 85 | + } |
| 86 | + fclose(f); |
| 87 | + } |
| 88 | + // we do not do much error checking so at least return errno |
| 89 | + return errno; |
| 90 | +} |
0 commit comments