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 }