blind-from-video.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
       ---
       blind-from-video.c (7228B)
       ---
            1 /* See LICENSE file for copyright and license details. */
            2 #include "common.h"
            3 
            4 USAGE("[-F pixel-format] [-r frame-rate] [-w width -h height] [-dL] input-file [output-file]")
            5 
            6 static int draft = 0;
            7 static void (*convert_segment)(char *buf, size_t n, int fd, const char *file);
            8 
            9 static void
           10 read_metadata(FILE *fp, char *fname, size_t *width, size_t *height)
           11 {
           12         char *line = NULL;
           13         size_t size = 0;
           14         ssize_t len;
           15         char *p;
           16 
           17         while ((len = getline(&line, &size, fp)) != -1) {
           18                 if (len && line[len - 1])
           19                         line[--len] = '\0';
           20                 p = strchr(line, '=') + 1;
           21                 if (strstr(line, "width=") == line) {
           22                         if (tozu(p, 1, SIZE_MAX, width))
           23                                 eprintf("invalid width: %s\n", p);
           24                 } else if (strstr(line, "height=") == line) {
           25                         if (tozu(p, 1, SIZE_MAX, height))
           26                                 eprintf("invalid height: %s\n", p);
           27                 }
           28         }
           29 
           30         if (ferror(fp))
           31                 eprintf("getline %s:", fname);
           32         free(line);
           33 
           34         if (!*width || !*height)
           35                 eprintf("could not get all required metadata\n");
           36 }
           37 
           38 static void
           39 get_metadata(char *file, size_t *width, size_t *height)
           40 {
           41         FILE *fp;
           42         int fd, pipe_rw[2];
           43         pid_t pid;
           44         int status;
           45 
           46         epipe(pipe_rw);
           47         pid = efork();
           48 
           49         if (!pid) {
           50                 pdeath(SIGKILL);
           51                 fd = eopen(file, O_RDONLY);
           52                 edup2(fd, STDIN_FILENO);
           53                 close(fd);
           54                 close(pipe_rw[0]);
           55                 edup2(pipe_rw[1], STDOUT_FILENO);
           56                 close(pipe_rw[1]);
           57                 eexeclp("ffprobe", "ffprobe", "-v", "quiet", "-show_streams",
           58                         "-select_streams", "v", "-", NULL);
           59         }
           60 
           61         close(pipe_rw[1]);
           62         fp = fdopen(pipe_rw[0], "rb");
           63         if (!fp)
           64                 eprintf("fdopen <subprocess>:");
           65         read_metadata(fp, file, width, height);
           66         fclose(fp);
           67         close(pipe_rw[0]);
           68 
           69         ewaitpid(pid, &status, 0);
           70         if (status)
           71                 exit(1);
           72 }
           73 
           74 #define CONVERT_SEGMENT(TYPE)\
           75         do {\
           76                 typedef TYPE pixel_t[4];\
           77                 size_t i, ptr;\
           78                 TYPE y, u, v, max = (TYPE)0xFF00L, ymax = (TYPE)0xDAF4L;\
           79                 TYPE r, g, b;\
           80                 pixel_t pixels[1024];\
           81                 uint16_t *pix;\
           82                 if (draft) {\
           83                         for (ptr = i = 0; ptr < n; ptr += 8) {\
           84                                 pix = (uint16_t *)(buf + ptr);\
           85                                 pixels[i][3] = 1;\
           86                                 y = (TYPE)((long int)(le16toh(pix[1])) - 0x1001L);\
           87                                 u = (TYPE)((long int)(le16toh(pix[2])) - 0x8000L);\
           88                                 v = (TYPE)((long int)(le16toh(pix[3])) - 0x8000L);\
           89                                 scaled_yuv_to_ciexyz(y, u, v, pixels[i] + 0,\
           90                                                      pixels[i] + 1, pixels[i] + 2);\
           91                                 if (++i == 1024) {\
           92                                         i = 0;\
           93                                         ewriteall(fd, pixels, sizeof(pixels), file);\
           94                                 }\
           95                         }\
           96                 } else {\
           97                         for (ptr = i = 0; ptr < n; ptr += 8) {\
           98                                 pix = (uint16_t *)(buf + ptr);\
           99                                 pixels[i][3] = le16toh(pix[0]) / max;\
          100                                 pixels[i][3] = CLIP(0, pixels[i][3], 1);\
          101                                 y = (TYPE)((long int)le16toh(pix[1]) - 0x1001L) / ymax;\
          102                                 u = (TYPE)((long int)le16toh(pix[2]) - 0x8000L) / max;\
          103                                 v = (TYPE)((long int)le16toh(pix[3]) - 0x8000L) / max;\
          104                                 yuv_to_srgb(y, u, v, &r, &g, &b);\
          105                                 r = srgb_decode(r);\
          106                                 g = srgb_decode(g);\
          107                                 b = srgb_decode(b);\
          108                                 srgb_to_ciexyz(r, g, b, pixels[i] + 0, pixels[i] + 1, pixels[i] + 2);\
          109                                 if (++i == 1024) {\
          110                                         i = 0;\
          111                                         ewriteall(fd, pixels, sizeof(pixels), file);\
          112                                 }\
          113                         }\
          114                 }\
          115                 if (i)\
          116                         ewriteall(fd, pixels, i * sizeof(*pixels), file);\
          117         } while (0)
          118 
          119 static void convert_segment_xyza (char *buf, size_t n, int fd, const char *file) {CONVERT_SEGMENT(double);}
          120 static void convert_segment_xyzaf(char *buf, size_t n, int fd, const char *file) {CONVERT_SEGMENT(float);}
          121 
          122 static void
          123 convert(const char *infile, int outfd, const char *outfile, size_t width, size_t height, const char *frame_rate)
          124 {
          125         char geometry[2 * INTSTRLEN(size_t) + 2], buf[BUFSIZ];
          126         const char *cmd[13];
          127         int status, pipe_rw[2];
          128         size_t i = 0, n, ptr;
          129         pid_t pid;
          130 
          131         cmd[i++] = "ffmpeg";
          132         cmd[i++] = "-i", cmd[i++] = infile;
          133         cmd[i++] = "-f", cmd[i++] = "rawvideo";
          134         cmd[i++] = "-pix_fmt", cmd[i++] = "ayuv64le";
          135         if (width && height) {
          136                 sprintf(geometry, "%zux%zu", width, height);
          137                 cmd[i++] = "-s:v", cmd[i++] = geometry;
          138         }
          139         if (frame_rate)
          140                 cmd[i++] = "-r", cmd[i++] = frame_rate;
          141         cmd[i++] = "-";
          142         cmd[i++] = NULL;
          143 
          144         epipe(pipe_rw);
          145         pid = efork();
          146 
          147         if (!pid) {
          148                 pdeath(SIGKILL);
          149                 close(pipe_rw[0]);
          150                 edup2(pipe_rw[1], STDOUT_FILENO);
          151                 close(pipe_rw[1]);
          152                 eexecvp("ffmpeg", (char **)(void *)cmd);
          153         }
          154 
          155         close(pipe_rw[1]);
          156 
          157         if (convert_segment) {
          158                 for (ptr = 0;;) {
          159                         if (!(n = eread(pipe_rw[0], buf + ptr, sizeof(buf) - ptr, "<subprocess>")))
          160                                 break;
          161                         ptr += n;
          162                         n = ptr - (ptr % 8);
          163                         convert_segment(buf, n, outfd, outfile);
          164                         memmove(buf, buf + n, ptr -= n);
          165                 }
          166                 if (ptr)
          167                         eprintf("<subprocess>: incomplete frame\n");
          168         } else {
          169                 while ((n = eread(pipe_rw[0], buf, sizeof(buf), "<subprocess>")))
          170                         ewriteall(outfd, buf, (size_t)n, outfile);
          171         }
          172 
          173         close(pipe_rw[0]);
          174         ewaitpid(pid, &status, 0);
          175         if (status)
          176                 exit(1);
          177 }
          178 
          179 int
          180 main(int argc, char *argv[])
          181 {
          182         size_t width = 0, height = 0, frames;
          183         char head[STREAM_HEAD_MAX];
          184         char *frame_rate = NULL;
          185         char *infile;
          186         const char *outfile;
          187         char *data;
          188         const char *pixfmt = "xyza";
          189         ssize_t headlen;
          190         size_t length, frame_size, pixel_size;
          191         int outfd, skip_length = 0;
          192         struct stat st;
          193 
          194         ARGBEGIN {
          195         case 'd':
          196                 draft = 1;
          197                 break;
          198         case 'L':
          199                 skip_length = 1;
          200                 break;
          201         case 'F':
          202                 pixfmt = UARGF();
          203                 break;
          204         case 'r':
          205                 frame_rate = UARGF();
          206                 break;
          207         case 'w':
          208                 width = etozu_flag('w', UARGF(), 1, SIZE_MAX);
          209                 break;
          210         case 'h':
          211                 height = etozu_flag('h', UARGF(), 1, SIZE_MAX);
          212                 break;
          213         default:
          214                 usage();
          215         } ARGEND;
          216 
          217         if (argc < 1 || argc > 2 || !width != !height)
          218                 usage();
          219 
          220         infile = argv[0];
          221         outfile = argv[1] ? argv[1] : "-";
          222 
          223         pixfmt = get_pixel_format(pixfmt, "xyza");
          224         if (!strcmp(pixfmt, "xyza")) {
          225                 convert_segment = convert_segment_xyza;
          226                 pixel_size = 4 * sizeof(double);
          227         } else if (!strcmp(pixfmt, "xyza f")) {
          228                 convert_segment = convert_segment_xyzaf;
          229                 pixel_size = 4 * sizeof(float);
          230         } else if (!strcmp(pixfmt, "raw0")) {
          231                 convert_segment = NULL;
          232                 pixel_size = 4 * sizeof(uint16_t);
          233         } else {
          234                 eprintf("pixel format %s is not supported, try xyza or raw0 and blind-convert\n", pixfmt);
          235         }
          236 
          237         if (!width)
          238                 get_metadata(infile, &width, &height);
          239         if (width > SIZE_MAX / height)
          240                 eprintf("video frame too large\n");
          241         frame_size = width * height;
          242         if (pixel_size > SIZE_MAX / frame_size)
          243                 eprintf("video frame too large\n");
          244         frame_size *= pixel_size;
          245 
          246         if (!strcmp(outfile, "-")) {
          247                 outfile = "<stdout>";
          248                 outfd = STDOUT_FILENO;
          249                 if (!skip_length)
          250                         eprintf("standard out as output file is only allowed with -L\n");
          251         } else {
          252                 outfd = eopen(outfile, O_RDWR | O_CREAT | O_TRUNC, 0666);
          253         }
          254 
          255         if (skip_length) {
          256                 SPRINTF_HEAD_ZN(head, 0, width, height, pixfmt, &headlen);
          257                 ewriteall(outfd, head, (size_t)headlen, outfile);
          258         }
          259 
          260         convert(infile, outfd, outfile, width, height, frame_rate);
          261 
          262         if (outfd == STDOUT_FILENO)
          263                 return 0;
          264 
          265         if (fstat(outfd, &st))
          266                 eprintf("fstat %s:", outfile);
          267         length = (size_t)(st.st_size);
          268 
          269         if (skip_length)
          270                 length -= (size_t)headlen;
          271         if (length % frame_size)
          272                 eprintf("<subprocess>: incomplete frame\n");
          273         frames = length / frame_size;
          274 
          275         if (!skip_length) {
          276                 SPRINTF_HEAD_ZN(head, frames, width, height, pixfmt, &headlen);
          277                 ewriteall(outfd, head, (size_t)headlen, outfile);
          278                 data = mmap(0, length + (size_t)headlen, PROT_READ | PROT_WRITE, MAP_SHARED, outfd, 0);
          279                 memmove(data + headlen, data, length);
          280                 memcpy(data, head, (size_t)headlen);
          281                 munmap(data, length + (size_t)headlen);
          282         }
          283 
          284         close(outfd);
          285         return 0;
          286 }