blind-concat.c - blind - suckless command-line video editing utility | |
git clone git://git.suckless.org/blind | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
blind-concat.c (5171B) | |
--- | |
1 /* See LICENSE file for copyright and license details. */ | |
2 #include "common.h" | |
3 | |
4 USAGE("[-o output-file [-j jobs]] first-stream ... last-stream") | |
5 | |
6 static void | |
7 concat_to_stdout(int argc, char *argv[], const char *fname) | |
8 { | |
9 struct stream *streams; | |
10 size_t frames = 0; | |
11 int i; | |
12 | |
13 streams = emalloc2((size_t)argc, sizeof(*streams)); | |
14 | |
15 for (i = 0; i < argc; i++) { | |
16 eopen_stream(streams + i, argv[i]); | |
17 if (i) | |
18 echeck_compat(streams + i, streams); | |
19 if (streams[i].frames > SIZE_MAX - frames) | |
20 eprintf("resulting video is too long\n"); | |
21 frames += streams[i].frames; | |
22 } | |
23 | |
24 streams->frames = frames; | |
25 fprint_stream_head(stdout, streams); | |
26 efflush(stdout, fname); | |
27 | |
28 for (i = 0; i < argc; i++) { | |
29 esend_stream(streams + i, STDOUT_FILENO, fname); | |
30 close(streams[i].fd); | |
31 } | |
32 | |
33 free(streams); | |
34 } | |
35 | |
36 static void | |
37 concat_to_file(int argc, char *argv[], char *output_file) | |
38 { | |
39 struct stream stream, refstream; | |
40 int first = 1; | |
41 int fd = eopen(output_file, O_RDWR | O_CREAT | O_TRUNC, 0666); | |
42 char head[STREAM_HEAD_MAX]; | |
43 ssize_t headlen; | |
44 size_t size; | |
45 off_t pos; | |
46 char *data; | |
47 | |
48 for (; argc--; argv++) { | |
49 eopen_stream(&stream, *argv); | |
50 | |
51 if (first) { | |
52 refstream = stream; | |
53 first = 1; | |
54 } else { | |
55 if (refstream.frames > SIZE_MAX - stream.frames) | |
56 eprintf("resulting video is too long\n"); | |
57 refstream.frames += stream.frames; | |
58 echeck_compat(&stream, &refstream); | |
59 } | |
60 | |
61 esend_stream(&stream, fd, output_file); | |
62 close(stream.fd); | |
63 } | |
64 | |
65 SPRINTF_HEAD_ZN(head, stream.frames, stream.width, stream.height… | |
66 ewriteall(fd, head, (size_t)headlen, output_file); | |
67 | |
68 size = (size_t)(pos = elseek(fd, 0, SEEK_CUR, output_file)); | |
69 if ((uintmax_t)pos > SIZE_MAX) | |
70 eprintf("%s\n", strerror(EFBIG)); | |
71 | |
72 data = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | |
73 if (data == MAP_FAILED) | |
74 eprintf("mmap %s:", output_file); | |
75 memmove(data + headlen, data, size - (size_t)headlen); | |
76 memcpy(data, head, (size_t)headlen); | |
77 munmap(data, size); | |
78 | |
79 close(fd); | |
80 } | |
81 | |
82 static void | |
83 concat_to_file_parallel(int argc, char *argv[], char *output_file, size_… | |
84 { | |
85 #if !defined(HAVE_EPOLL) | |
86 int fd = eopen(output_file, O_WRONLY | O_CREAT | O_TRUNC, 0666); | |
87 if (fd != STDOUT_FILENO) | |
88 edup2(fd, STDOUT_FILENO); | |
89 concat_to_stdout(argc, argv, output_file); | |
90 #else | |
91 struct epoll_event *events; | |
92 struct stream *streams; | |
93 off_t *ptrs, ptr; | |
94 char head[STREAM_HEAD_MAX]; | |
95 size_t frames = 0, next = 0, j; | |
96 ssize_t headlen; | |
97 int fd, i, n, pollfd; | |
98 | |
99 if (jobs > (size_t)argc) | |
100 jobs = (size_t)argc; | |
101 | |
102 fd = eopen(output_file, O_RDWR | O_CREAT | O_TRUNC, 0666); | |
103 events = emalloc2(jobs, sizeof(*events)); | |
104 streams = emalloc2((size_t)argc, sizeof(*streams)); | |
105 ptrs = emalloc2((size_t)argc, sizeof(*ptrs)); | |
106 | |
107 for (i = 0; i < argc; i++) { | |
108 eopen_stream(streams + i, argv[i]); | |
109 if (i) | |
110 echeck_compat(streams + i, streams); | |
111 if (streams[i].frames > SIZE_MAX - frames) | |
112 eprintf("resulting video is too long\n"); | |
113 frames += streams[i].frames; | |
114 } | |
115 | |
116 SPRINTF_HEAD_ZN(head, frames, streams->width, streams->height, s… | |
117 | |
118 echeck_dimensions(streams, WIDTH | HEIGHT, NULL); | |
119 ptr = (off_t)headlen; | |
120 for (i = 0; i < argc; i++) { | |
121 ptrs[i] = ptr; | |
122 ptr += (off_t)streams->frames * (off_t)streams->frame_si… | |
123 } | |
124 if (ftruncate(fd, (off_t)ptr)) | |
125 eprintf("ftruncate %s:", output_file); | |
126 fadvise_random(fd, (off_t)headlen, 0); | |
127 | |
128 pollfd = epoll_create1(0); | |
129 if (pollfd == -1) | |
130 eprintf("epoll_create1:"); | |
131 | |
132 epwriteall(fd, head, (size_t)headlen, 0, output_file); | |
133 for (i = 0; i < argc; i++) { | |
134 epwriteall(fd, streams[i].buf, streams[i].ptr, ptrs[i], … | |
135 ptrs[i] += (off_t)(streams[i].ptr); | |
136 streams[i].ptr = 0; | |
137 } | |
138 | |
139 for (j = 0; j < jobs; j++, next++) { | |
140 events->events = EPOLLIN; | |
141 events->data.u64 = next; | |
142 if (epoll_ctl(pollfd, EPOLL_CTL_ADD, streams[next].fd, e… | |
143 if ((errno == ENOMEM || errno == ENOSPC) && j) | |
144 break; | |
145 eprintf("epoll_ctl:"); | |
146 } | |
147 } | |
148 jobs = j; | |
149 | |
150 while (jobs) { | |
151 n = epoll_wait(pollfd, events, (int)jobs, -1); | |
152 if (n < 0) | |
153 eprintf("epoll_wait:"); | |
154 for (i = 0; i < n; i++) { | |
155 j = events[i].data.u64; | |
156 if (streams[j].ptr || eread_stream(streams + j, … | |
157 epwriteall(fd, streams[j].buf, streams[j… | |
158 ptrs[j] += (off_t)(streams[j].ptr); | |
159 streams[j].ptr = 0; | |
160 continue; | |
161 } | |
162 | |
163 close(streams[j].fd); | |
164 if (next < (size_t)argc) { | |
165 events->events = EPOLLIN; | |
166 events->data.u64 = next; | |
167 if (epoll_ctl(pollfd, EPOLL_CTL_ADD, str… | |
168 if ((errno == ENOMEM || errno ==… | |
169 break; | |
170 eprintf("epoll_ctl:"); | |
171 } | |
172 next++; | |
173 } else { | |
174 jobs--; | |
175 } | |
176 } | |
177 } | |
178 | |
179 close(pollfd); | |
180 free(events); | |
181 free(streams); | |
182 free(ptrs); | |
183 #endif | |
184 } | |
185 | |
186 int | |
187 main(int argc, char *argv[]) | |
188 { | |
189 char *output_file = NULL; | |
190 size_t jobs = 0; | |
191 | |
192 ARGBEGIN { | |
193 case 'o': | |
194 output_file = UARGF(); | |
195 break; | |
196 case 'j': | |
197 jobs = etozu_flag('j', UARGF(), 1, SHRT_MAX); | |
198 break; | |
199 default: | |
200 usage(); | |
201 } ARGEND; | |
202 | |
203 if (argc < 2 || (jobs && !output_file)) | |
204 usage(); | |
205 | |
206 if (jobs) | |
207 concat_to_file_parallel(argc, argv, output_file, jobs); | |
208 else if (output_file) | |
209 concat_to_file(argc, argv, output_file); | |
210 else | |
211 concat_to_stdout(argc, argv, "<stdout>"); | |
212 | |
213 return 0; | |
214 } |