1 /* Copyright (C) 2007, 2008 Anders Holst
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU General Public License as
5 * published by the Free Software Foundation; either version 2, or
6 * (at your option) any later version.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this software; see the file COPYING. If not, write to
15 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
16 * Boston, MA 02111-1307 USA
19 #define _LARGEFILE64_SOURCE
20 #include <sys/types.h>
30 static off64_t* buf0 = 0;
31 static off64_t* buf1 = 0;
32 static off64_t time_offset;
33 static off64_t size_offset;
38 inline int absless(long long int x, int lim)
40 return (x<lim && x>-lim);
43 double strtotime(char* str)
49 tt = strtol(p1, &p2, 10);
50 if (p1==p2) return -1.0;
51 while (*p2 == ':' && i<2) {
53 if (p2-p1>2) return -1.0;
55 t1 = strtol(p1, &p2, 10);
56 if (p1==p2) return -1.0;
59 if (i>0 && p2-p1>2) return -1.0;
60 if (*p2 == 0) return (double)tt;
61 if (*p2 != '.') return -1.0;
63 t1 = strtol(p1, &p2, 10);
64 for (i=0, tmp=1.0; i<(p2-p1); tmp*=0.1, i++);
65 return (double)tt + tmp*t1;
68 char* timetostr(double tm)
72 sprintf(buf, "%d:%d:%.3f", r/60, r%60, tm-60*r);
76 inline unsigned int byteswop(unsigned int n)
78 return ((n&0xff000000)>>24) | ((n&0xff0000)>>8) | ((n&0xff00)<<8) | ((n&0xff)<<24);
81 inline unsigned long long int byteswopl(unsigned long long int n)
83 return (n>>56) | ((n>>40)&0xff00) | ((n>>24)&0xff0000) | ((n>>8)&0xff000000) | ((n&0xff000000)<<8) | ((n&0xff0000)<<24) | ((n&0xff00)<<40) | ((n&0xff)<<56);
86 double inttotime(unsigned int t1, unsigned int t2)
88 return (byteswop(t2)*1.1111111111111112e-05 + byteswop(t1)*47721.858844444447);
91 double lltotime(long long int t)
93 return ((unsigned int)(t&0xffffffff)*1.1111111111111112e-05) + ((unsigned int)(t>>32)*47721.858844444447);
96 void timetoint(double tm, unsigned int& t1, unsigned int& t2)
98 double tmp=tm/47721.858844444447;
99 t1 = byteswop((unsigned int)tmp);
100 t2 = byteswop((unsigned int)((tm - t1*47721.858844444447)*90000));
103 int readbufinternal(int f)
109 if (read(f, buf, 16) != 16)
111 buf[0] = (off64_t)byteswopl((unsigned long long int)buf[0]);
112 buf[1] = (off64_t)byteswopl((unsigned long long int)buf[1]);
116 void writebufinternal(int f)
119 buf2[0] = (off64_t)byteswopl((unsigned long long int)buf0[0] - size_offset);
120 buf2[1] = (off64_t)byteswopl((unsigned long long int)buf0[1]);
124 off64_t readoff(int f, int fo, double t, int beg, double& tr)
126 static off64_t lastreturn;
132 buf0 = new off64_t[2];
133 buf1 = new off64_t[2];
134 if (!(readbufinternal(f) && readbufinternal(f))) {
135 printf("The corresponding \".ap\"-file is empty.\n");
138 time_offset = buf0[1];
139 if (buf1[1] > buf0[1] && buf1[1] - buf0[1] < 900000)
140 time_offset -= (buf1[1]-buf0[1])*buf0[0]/(buf1[0]-buf0[0]);
141 size_offset = buf0[0];
146 if (t < last && t != -1.0) {
148 lseek(f, 0, SEEK_SET);
151 time_offset = buf0[1];
152 if (buf1[1]>buf0[1] && buf1[1]-buf0[1]<900000)
153 time_offset -= (buf1[1]-buf0[1])*buf0[0]/(buf1[0]-buf0[0]);
154 size_offset += buf0[0] - sizetmp;
159 if (t == last || endp == 1) {
163 writebufinternal(fo);
165 lt = lltotime(buf0[1] - time_offset);
166 tt = lltotime(buf1[1] - time_offset);
168 while (tt < t || t == -1.0) {
169 if (!readbufinternal(f))
172 writebufinternal(fo);
175 if (buf1[1] < buf0[1] || buf1[1] - buf0[1] > 900000) {
176 if (absless(buf1[1] + ((long long int)1)<<33 - buf0[1], 900000))
177 time_offset -= ((long long int)1)<<33;
179 time_offset += buf1[1] - buf0[1];
182 tt = lltotime(buf1[1] - time_offset);
186 } else if (beg ? (lt == tt || (t-lt > tt-t && tt-t<0.18)) : (t-lt >= tt-t || t-lt>0.18)) {
187 if (!readbufinternal(f))
190 writebufinternal(fo);
196 size_offset += buf0[0] - sizetmp;
197 lastreturn = buf0[0];
201 int framepid(char* buf, int pos)
203 return ((buf[pos+1] & 0x1f) << 8) + buf[pos+2];
206 int framesearch_f(char* buf, int start, int stop, int pid)
210 for (p = buf+start; p < buf+stop-3; p++)
211 if (p[0]==0 && p[1]==0 && p[2]==1 && p[3]==0) {
212 pos = ((p - buf)/188)*188;
213 if (pid == -1 || framepid(buf, pos) == pid)
219 int framesearch_b(char* buf, int start, int stop, int pid)
223 for (p = buf+stop-1; p >= buf+start+3; p--)
224 if (p[0]==0 && p[-1]==1 && p[-2]==0 && p[-3]==0) {
225 pos = ((p - buf)/188)*188;
226 if (pid == -1 || framepid(buf, pos) == pid)
232 int transfer_start(int f_ts, int f_out, off64_t n1, off64_t& n1ret)
243 tmp = LEN - (int)(num - n1), num = n1;
246 lseek64(f_ts, n1 - num, SEEK_SET);
247 if (read(f_ts, buf, tmp) != tmp) return 1;
248 } while ((pos = framesearch_b(buf, 0, tmp, -1)) == -1 && num < n1);
250 if (write(f_out, buf+pos, tmp-pos) != tmp-pos) return 1;
251 n1ret = n1 - (num - pos);
252 size_offset -= (num - pos);
255 if (read(f_ts, buf, LEN) != LEN) return 1;
256 if (write(f_out, buf, LEN) != LEN) return 1;
265 lseek64(f_ts, n1, SEEK_SET);
271 int transfer_rest(int f_ts, int f_out, off64_t n1, off64_t n2, off64_t& n2ret)
274 int num, pos, st, pid, tmp;
276 static off64_t lastn2 = -1, lastn2ret;
279 lseek64(f_ts, i, SEEK_SET);
282 for (; i+LEN<=n2; i+=LEN) {
283 if (read(f_ts, buf, LEN) != LEN) return 1;
284 if (write(f_out, buf, LEN) != LEN) return 1;
287 num = read(f_ts, buf, LEN);
288 pid = framepid(buf, n2-i);
289 st = (i < n2 ? n2-i : 0);
292 while ((pos = framesearch_f(buf, st, num, pid)) == -1 && num == LEN) {
293 if (write(f_out, buf, LEN) != LEN) return 1;
294 num = read(f_ts, buf, LEN);
300 else if (pos == -1) {
301 if (write(f_out, buf, num) != num) return 1;
305 if (write(f_out, buf, pos) != pos) return 1;
310 lastn2ret = n2ret = n2 + tmp;
314 if (read(f_ts, buf, n2-i) != n2-i) return 1;
315 if (write(f_out, buf, n2-i) != n2-i) return 1;
317 lastn2 = lastn2ret = n2ret = n2;
322 int donextinterval1(int fc, int fco, int fa, int fao, int fts, int ftso)
325 static double tlast, toff = 0.0;
327 off64_t c1, c1ret, c2ret;
330 unsigned int tmp, lcheck=0;
334 n = lseek(fc, 0, SEEK_END) / 12;
335 lseek(fc, 0, SEEK_SET);
341 tmp = byteswop(buf[2]);
343 c1 = readoff(fa, fao, 0.0, 1, toff);
344 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
345 c2 = readoff(fa, fao, inttotime(buf[0], buf[1]), 0, tlast);
346 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
347 printf("Interval: %lld - %lld\n", c1ret, c2ret);
348 // move all passed marks
349 lseek(fc, 0, SEEK_SET);
351 while (byteswop(buf[2]) != 1) {
356 } else if (tmp == 0) {
357 c1 = readoff(fa, fao, inttotime(buf[0], buf[1]), 1, toff);
358 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
371 tmp = byteswop(buf[2]);
373 c1 = readoff(fa, fao, inttotime(buf[0], buf[1]), 1, ttmp);
375 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
376 toff += ttmp - tlast;
378 } else if (tmp == 3) {
379 timetoint(tlast-toff, buf[0], buf[1]);
388 c2 = readoff(fa, fao, -1.0, 0, tlast);
389 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
390 printf("Interval: %lld - %lld\n", c1ret, c2ret);
395 tmp = byteswop(buf[2]);
397 c2 = readoff(fa, fao, inttotime(buf[0], buf[1]), 0, tlast);
398 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
399 printf("Interval: %lld - %lld\n", c1ret, c2ret);
401 } else if (tmp != 0) {
402 timetoint(inttotime(buf[0], buf[1])-toff, buf[0], buf[1]);
409 int donextinterval2(int barg, int earg, char* argv[], int fc, int fco, int fa, int fao, int fts, int ftso)
411 static int n = -1, i, lio = -1, lcheck=0;
412 static double tlast = 0.0, toff = 0.0;
413 static off64_t c2 = -1;
414 off64_t c1, c1ret, c2ret;
418 unsigned int buff[3];
421 if (!lcheck && n!=-1) {
422 lseek(fc, 0, SEEK_SET);
423 for (j=0; j<n; j++) {
425 tmp = byteswop(buf[2]);
427 timetoint(tlast-toff, buf[0], buf[1]);
433 if (lio != -1) { // Add an extra "out" at the end to avoid bug in playback
434 buff[2] = byteswop(1);
435 timetoint(tlast-toff, buff[0], buff[1]);
436 write(fco, buff, 12);
442 n = lseek(fc, 0, SEEK_END) / 12;
444 c1 = readoff(fa, fao, strtotime(argv[i]), 1, ttmp);
446 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
447 toff += ttmp - tlast;
448 c2 = readoff(fa, fao, strtotime(argv[i+1]), 0, tlast);
449 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
450 printf("Interval: %lld - %lld\n", c1ret, c2ret);
451 lseek(fc, 0, SEEK_SET);
452 for (j=0; j<n; j++) {
454 tmp = byteswop(buf[2]);
455 ttmp2=inttotime(buf[0], buf[1]);
459 timetoint(ttmp-toff, buf[0], buf[1]);
462 } else if (ttmp2 <= tlast) {
463 timetoint(ttmp2-toff, buf[0], buf[1]);
468 } else if (ttmp2 >= ttmp && ttmp2 <= tlast) {
470 if (lio != io && lio != -1) {
471 buff[2] = byteswop(io);
472 timetoint(ttmp-toff, buff[0], buff[1]);
473 write(fco, buff, 12);
477 timetoint(ttmp2-toff, buf[0], buf[1]);
479 } else if (tmp < 2) {
487 char* makefilename(const char* base, const char* pre, const char* ext, const char* post)
489 static char buf[256];
490 int len1, len2, len3;
492 len2 = (pre ? strlen(pre) : 0);
493 len3 = (ext ? strlen(ext) : 0);
495 if (ext && len1>=len3 && !strcmp(base+len1-len3,ext))
498 strcpy(buf+len1, pre);
500 strcpy(buf+len1+len2, ext);
502 strcpy(buf+len1+len2+len3, post);
506 void copymeta(int n, int f1, int f2, const char* title, const char* suff, const char* descr)
509 char* buf = new char[n];
516 for (j=i+1; j<n; j++)
521 for (k=0; title[k] && title[k] != 10; k++);
524 write(f2, buf+i, j-i);
526 write(f2, suff, strlen(suff));
530 for (j=i+1; j<n; j++)
535 for (k=0; descr[k] && descr[k] != 10; k++);
538 write(f2, buf+i, j-i);
541 write(f2, buf+j, n-j);
545 int main(int argc, char* argv[])
547 int f_ts, f_out, f_cuts, f_cutsout, f_ap, f_apout, f_meta, f_metaout;
549 const char* suff = 0;
554 int cutarg = 0, cutargend = 0;
556 int i, j, ok, bad = 0;
559 struct stat64 statbuf64;
561 for (i=1; i<argc; i++) {
562 if (!strcmp(argv[i], "-r"))
564 else if (!strcmp(argv[i], "-o")) {
570 } else if (!strcmp(argv[i], "-n")) {
576 } else if (!strcmp(argv[i], "-d")) {
582 } else if (!strcmp(argv[i], "-c")) {
584 for (j=i; j<argc; j+=2) {
585 t1 = strtotime(argv[j]);
586 t2 = (j+1<argc ? strtotime(argv[j+1]) : -1.0);
587 if (t1 < 0 || t2 < 0)
590 printf("Bad time interval: %s - %s\n", argv[j], argv[j+1]);
598 } else if (*argv[i] == '-' && (*(argv[i]+1) == 0 ||*(argv[i]+2) == 0)) {
608 if (argc == 1 || bad) {
609 printf("Usage: mcut [-r] [-o output_ts_file] [-n title] [-d description] ts_file [-c start1 end1 [start2 end2] ... ]\n");
610 printf(" -r : Replace (= remove) the original movie.\n");
611 printf(" -o : Filename of resulting movie (defaults to the original name appended by \" cut\", unless -r is given).\n");
612 printf(" -n : Title of resulting movie.\n");
613 printf(" -d : Description of resulting movie.\n");
614 printf(" -c : A sequence of starttime and endtime pairs. Each time is given as hour:min:sec. The portion between start and end is retained (i.e. not cut away).\n");
621 suff = (replace ? "_" : " cut");
623 tmpname = makefilename(inname, 0, ".ts", 0);
624 f_ts = open(tmpname, O_RDONLY | O_LARGEFILE);
626 printf("Failed to open input stream file \"%s\"\n", tmpname);
629 tmpname = makefilename(inname, 0, ".ts", ".cuts");
630 f_cuts = open(tmpname, O_RDONLY);
632 printf("Failed to open input cuts file \"%s\"\n", tmpname);
636 tmpname = makefilename(inname, 0, ".ts", ".ap");
637 f_ap = open(tmpname, O_RDONLY);
639 printf("Failed to open input ap file \"%s\"\n", tmpname);
644 if (fstat64(f_ts, &statbuf64)) {
645 printf("Failed to stat input stream file.\n");
651 tmpname = makefilename(outname, suff, ".ts", 0);
652 f_out = open(tmpname, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, statbuf64.st_mode & 0xfff);
654 printf("Failed to open output stream file \"%s\"\n", tmpname);
660 if (fstat(f_cuts, &statbuf)) {
661 printf("Failed to stat input cuts file.\n");
666 unlink(makefilename(outname, suff, ".ts", 0));
669 tmpname = makefilename(outname, suff, ".ts", ".cuts");
670 f_cutsout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
671 if (f_cutsout == -1) {
672 printf("Failed to open output cuts file \"%s\"\n", tmpname);
677 unlink(makefilename(outname, suff, ".ts", 0));
680 if (fstat(f_ap, &statbuf)) {
681 printf("Failed to stat input ap file.\n");
687 unlink(makefilename(outname, suff, ".ts", 0));
688 unlink(makefilename(outname, suff, ".ts", ".cuts"));
691 tmpname = makefilename(outname, suff, ".ts", ".ap");
692 f_apout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
694 printf("Failed to open output ap file \"%s\"\n", tmpname);
700 unlink(makefilename(outname, suff, ".ts", 0));
701 unlink(makefilename(outname, suff, ".ts", ".cuts"));
706 ok = donextinterval2(cutarg, cutargend, argv, f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
708 ok = donextinterval1(f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
710 printf("There are no cuts specified. Leaving the movie as it is.\n");
717 unlink(makefilename(outname, suff, ".ts", 0));
718 unlink(makefilename(outname, suff, ".ts", ".cuts"));
719 unlink(makefilename(outname, suff, ".ts", ".ap"));
725 ok = donextinterval2(cutarg, cutargend, argv, f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
727 ok = donextinterval1(f_cuts, f_cutsout, f_ap, f_apout, f_ts, f_out);
737 printf("Copying %s failed, read/write error.\n", makefilename(inname, 0, ".ts", 0));
738 unlink(makefilename(outname, suff, ".ts", 0));
739 unlink(makefilename(outname, suff, ".ts", ".cuts"));
740 unlink(makefilename(outname, suff, ".ts", ".ap"));
744 tmpname = makefilename(inname, 0, ".ts", ".meta");
745 f_meta = open(tmpname, O_RDONLY);
747 printf("Failed to open input meta file \"%s\"\n", tmpname);
750 if (fstat(f_meta, &statbuf)) {
751 printf("Failed to stat input meta file.\n");
755 tmpname = makefilename(outname, suff, ".ts", ".meta");
756 f_metaout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
757 if (f_metaout == -1) {
758 printf("Failed to open output meta file \"%s\"\n", tmpname);
762 copymeta((int)statbuf.st_size, f_meta, f_metaout, title, (replace ? 0 : suff), descr);
768 tmpname = makefilename(inname, 0, ".ts", 0);
769 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
771 rename(makefilename(outname, suff, ".ts", 0), tmpname);
772 tmpname = makefilename(inname, 0, ".ts", ".cuts");
773 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
775 rename(makefilename(outname, suff, ".ts", ".cuts"), tmpname);
776 tmpname = makefilename(inname, 0, ".ts", ".ap");
777 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
779 rename(makefilename(outname, suff, ".ts", ".ap"), tmpname);
780 tmpname = makefilename(inname, 0, ".ts", ".meta");
781 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
783 rename(makefilename(outname, suff, ".ts", ".meta"), tmpname);
785 unlink(makefilename(inname, 0, ".ts", 0));
786 unlink(makefilename(inname, 0, ".ts", ".cuts"));
787 unlink(makefilename(inname, 0, ".ts", ".ap"));
788 unlink(makefilename(inname, 0, ".ts", ".meta"));