blind-from-video.c - blind - suckless command-line video editing utility | |
git clone git://git.suckless.org/blind | |
Log | |
Files | |
Refs | |
README | |
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] inpu… | |
5 | |
6 static int draft = 0; | |
7 static void (*convert_segment)(char *buf, size_t n, int fd, const char *… | |
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_stre… | |
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])) -… | |
87 u = (TYPE)((long int)(le16toh(pix[2])) -… | |
88 v = (TYPE)((long int)(le16toh(pix[3])) -… | |
89 scaled_yuv_to_ciexyz(y, u, v, pixels[i] … | |
90 pixels[i] + 1, pixe… | |
91 if (++i == 1024) {\ | |
92 i = 0;\ | |
93 ewriteall(fd, pixels, sizeof(pix… | |
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]) - 0… | |
102 u = (TYPE)((long int)le16toh(pix[2]) - 0… | |
103 v = (TYPE)((long int)le16toh(pix[3]) - 0… | |
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, p… | |
109 if (++i == 1024) {\ | |
110 i = 0;\ | |
111 ewriteall(fd, pixels, sizeof(pix… | |
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 cha… | |
120 static void convert_segment_xyzaf(char *buf, size_t n, int fd, const cha… | |
121 | |
122 static void | |
123 convert(const char *infile, int outfd, const char *outfile, size_t width… | |
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(bu… | |
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), "<subpro… | |
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 r… | |
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 all… | |
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, &he… | |
277 ewriteall(outfd, head, (size_t)headlen, outfile); | |
278 data = mmap(0, length + (size_t)headlen, PROT_READ | PRO… | |
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 } |