blind-concat.c - blind - suckless command-line video editing utility
 (HTM) git clone git://git.suckless.org/blind
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) 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, 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 }