| 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 } |