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, stream.pixfmt, &headlen); 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_t jobs) 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, streams->pixfmt, &headlen); 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_size; 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], output_file); 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, events)) { 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, SIZE_MAX)) { 157 epwriteall(fd, streams[j].buf, streams[j].ptr, ptrs[j], output_file); 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, streams[next].fd, events)) { 168 if ((errno == ENOMEM || errno == ENOSPC) && j) 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 }