1 /* Copyright (C) 2007, 2008, 2009 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>
31 static off64_t time_offset;
32 static off64_t size_offset;
33 static off64_t curr_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 double inttotime(unsigned int t1, unsigned int t2)
70 return (bswap_32(t2)*1.1111111111111112e-05 + bswap_32(t1)*47721.858844444447);
73 double lltotime(long long int t)
75 return ((unsigned int)(t&0xffffffff)*1.1111111111111112e-05) + ((unsigned int)(t>>32)*47721.858844444447);
78 void timetoint(double tm, unsigned int& t1, unsigned int& t2)
80 double tmp=tm/47721.858844444447;
81 t1 = bswap_32((unsigned int)tmp);
82 t2 = bswap_32((unsigned int)((tm - t1*47721.858844444447)*90000));
85 void swapbuf(off64_t*& buf0, off64_t*& buf1)
93 int readbufinternal(int f, off64_t*& buf)
95 if (read(f, buf, 16) != 16)
97 buf[0] = (off64_t)bswap_64((unsigned long long int)buf[0]);
98 buf[1] = (off64_t)bswap_64((unsigned long long int)buf[1]);
102 void writebufinternal(int f, off64_t* buf)
105 tbuf[0] = (off64_t)bswap_64((unsigned long long int)buf[0] - curr_size_offset);
106 tbuf[1] = (off64_t)bswap_64((unsigned long long int)buf[1]);
110 void movesc(int fs, int fso, off64_t off, int beg)
112 static off64_t* buf = 0;
113 static off64_t lastoff;
115 if (fs == -1 || fso == -1)
118 buf = new off64_t[2];
123 lseek(fs, 0, SEEK_SET);
127 if (off == lastoff || endp)
131 writebufinternal(fso, buf);
132 while (readbufinternal(fs, buf)) {
136 writebufinternal(fso, buf);
141 off64_t readoff(int fa, int fao, int fs, int fso, double t, int beg, double& tr)
143 static off64_t* buf0 = 0;
144 static off64_t* buf1 = 0;
145 static off64_t lastreturn;
151 buf0 = new off64_t[2];
152 buf1 = new off64_t[2];
153 if (!(readbufinternal(fa, buf0) && readbufinternal(fa, buf1))) {
154 printf("The corresponding \".ap\"-file is empty.\n");
157 time_offset = buf0[1];
158 if (buf1[1] > buf0[1] && buf1[1] - buf0[1] < 450000)
159 time_offset -= (buf1[1]-buf0[1])*buf0[0]/(buf1[0]-buf0[0]);
160 else if (buf1[1] > buf0[1] || buf0[1] - buf1[1] > 45000)
161 time_offset = buf1[1];
162 size_offset = buf0[0];
167 if (t < last && t != -1.0) {
169 lseek(fa, 0, SEEK_SET);
170 readbufinternal(fa, buf0);
171 readbufinternal(fa, buf1);
172 time_offset = buf0[1];
173 if (buf1[1] > buf0[1] && buf1[1] - buf0[1] < 450000)
174 time_offset -= (buf1[1]-buf0[1])*buf0[0]/(buf1[0]-buf0[0]);
175 else if (buf1[1] > buf0[1] || buf0[1] - buf1[1] > 45000)
176 time_offset = buf1[1];
177 size_offset += buf0[0] - sizetmp;
182 if (t == last || endp == 1) {
186 writebufinternal(fao, buf0);
188 lt = lltotime(buf0[1] - time_offset);
189 if (buf0[1] - buf1[1] > 0 && buf0[1] - buf1[1] <= 45000)
190 tt = lt, buf1[1] = buf0[1];
192 tt = lltotime(buf1[1] - time_offset);
194 while (tt < t || t == -1.0) {
196 if (!readbufinternal(fa, buf1))
199 writebufinternal(fao, buf0);
202 if (buf0[1] - buf1[1] > 45000 || buf1[1] - buf0[1] > 450000) {
203 if (absless(buf1[1] + ((long long int)1)<<33 - buf0[1], 450000))
204 time_offset -= ((long long int)1)<<33;
206 time_offset += buf1[1] - buf0[1];
209 if (buf0[1] - buf1[1] > 0 && buf0[1] - buf1[1] <= 45000)
210 tt = lt, buf1[1] = buf0[1];
212 tt = lltotime(buf1[1] - time_offset);
216 } else if (beg ? (lt == tt || (t-lt > tt-t && tt-t<0.18)) : (t-lt >= tt-t || t-lt>0.18)) {
218 if (!readbufinternal(fa, buf1))
220 if (buf0[1] - buf1[1] > 0 && buf0[1] - buf1[1] <= 45000)
223 writebufinternal(fao, buf0);
229 size_offset += buf0[0] - sizetmp;
230 lastreturn = buf0[0];
234 int framepid(char* buf, int pos)
236 return ((buf[pos+1] & 0x1f) << 8) + buf[pos+2];
239 int framesearch_f(char* buf, int start, int stop, int pid, int& tp)
243 for (p = buf+start; p < buf+stop-5; p++)
244 if (p[0]==0 && p[1]==0 && p[2]==1) {
246 pos = ((p - buf)/188)*188;
247 if (pid == -1 || framepid(buf, pos) == pid) {
251 } else if (p[3]==0x09) {
252 pos = ((p - buf)/188)*188;
253 if ((buf[pos+1] & 0x40) && (pid == -1 || framepid(buf, pos) == pid)) {
254 tp = (p[4] >> 5) + 1;
262 int framesearch_b(char* buf, int start, int stop, int pid)
266 for (p = buf+stop-1; p >= buf+start+3; p--)
267 if (p[-1]==1 && p[-2]==0 && p[-3]==0) {
269 pos = ((p - buf)/188)*188;
270 if (pid == -1 || framepid(buf, pos) == pid)
272 } else if (p[0]==0x09) {
273 pos = ((p - buf)/188)*188;
274 if ((buf[pos+1] & 0x40) && (pid == -1 || framepid(buf, pos) == pid))
281 int transfer_start(int f_ts, int f_out, off64_t n1, off64_t& n1ret)
292 tmp = LEN - (int)(num - n1), num = n1;
295 lseek64(f_ts, n1 - num, SEEK_SET);
296 if (read(f_ts, buf, tmp) != tmp) return 1;
297 } while ((pos = framesearch_b(buf, 0, tmp, -1)) == -1 && num < n1);
299 if (write(f_out, buf+pos, tmp-pos) != tmp-pos) return 1;
300 n1ret = n1 - (num - pos);
301 size_offset -= (num - pos);
304 if (read(f_ts, buf, LEN) != LEN) return 1;
305 if (write(f_out, buf, LEN) != LEN) return 1;
314 lseek64(f_ts, n1, SEEK_SET);
320 int transfer_rest(int f_ts, int f_out, off64_t n1, off64_t n2, off64_t& n2ret)
323 int num, pos, st, pid, tp, tmp;
325 static off64_t lastn2 = -1, lastn2ret;
328 lseek64(f_ts, i, SEEK_SET);
331 for (; i+LEN<=n2; i+=LEN) {
332 if (read(f_ts, buf, LEN) != LEN) return 1;
333 if (write(f_out, buf, LEN) != LEN) return 1;
336 num = read(f_ts, buf, LEN);
337 pid = framepid(buf, n2-i);
338 st = (i < n2 ? n2-i : 0);
341 while ((pos = framesearch_f(buf, st, num, pid, tp)) == -1 ? num == LEN : tp == 3) {
345 if (write(f_out, buf, LEN) != LEN) return 1;
346 num = read(f_ts, buf, LEN);
353 else if (pos == -1) {
354 if (write(f_out, buf, num) != num) return 1;
358 if (write(f_out, buf, pos) != pos) return 1;
363 lastn2ret = n2ret = n2 + tmp;
367 if (read(f_ts, buf, n2-i) != n2-i) return 1;
368 if (write(f_out, buf, n2-i) != n2-i) return 1;
370 lastn2 = lastn2ret = n2ret = n2;
375 int donextinterval1(int fc, int fco, int fa, int fao, int fs, int fso, int fts, int ftso)
378 static double tlast, toff = 0.0;
380 off64_t c1, c1ret, c2ret;
383 unsigned int tmp, lcheck=0;
387 n = lseek(fc, 0, SEEK_END) / 12;
388 lseek(fc, 0, SEEK_SET);
394 tmp = bswap_32(buf[2]);
396 c1 = readoff(fa, fao, fs, fso, 0.0, 1, toff);
397 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
398 curr_size_offset = size_offset;
399 movesc(fs, fso, c1ret, 1);
400 c2 = readoff(fa, fao, fs, fso, inttotime(buf[0], buf[1]), 0, tlast);
401 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
402 movesc(fs, fso, c2ret, 0);
403 printf("Interval: %lld - %lld\n", c1ret, c2ret);
404 // move all passed marks
405 lseek(fc, 0, SEEK_SET);
407 while (bswap_32(buf[2]) != 1) {
412 } else if (tmp == 0) {
413 c1 = readoff(fa, fao, fs, fso, inttotime(buf[0], buf[1]), 1, toff);
414 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
415 curr_size_offset = size_offset;
416 movesc(fs, fso, c1ret, 1);
429 tmp = bswap_32(buf[2]);
431 c1 = readoff(fa, fao, fs, fso, inttotime(buf[0], buf[1]), 1, ttmp);
434 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
435 curr_size_offset = size_offset;
438 size_offset = curr_size_offset;
440 movesc(fs, fso, c1ret, 1);
441 toff += ttmp - tlast;
443 } else if (tmp == 3) {
444 timetoint(tlast-toff, buf[0], buf[1]);
453 c2 = readoff(fa, fao, fs, fso, -1.0, 0, tlast);
454 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
455 movesc(fs, fso, c2ret, 0);
456 printf("Interval: %lld - %lld\n", c1ret, c2ret);
461 tmp = bswap_32(buf[2]);
463 c2 = readoff(fa, fao, fs, fso, inttotime(buf[0], buf[1]), 0, tlast);
464 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
465 movesc(fs, fso, c2ret, 0);
466 printf("Interval: %lld - %lld\n", c1ret, c2ret);
468 } else if (tmp != 0) {
469 timetoint(inttotime(buf[0], buf[1])-toff, buf[0], buf[1]);
476 int donextinterval2(int barg, int earg, char* argv[], int fc, int fco, int fa, int fao, int fs, int fso, int fts, int ftso)
478 static int n = -1, i, lio = -1, lcheck=0;
479 static double tlast = 0.0, toff = 0.0;
480 static off64_t c2 = -1;
481 off64_t c1, c1ret, c2ret;
485 unsigned int buff[3];
488 if (!lcheck && n>0) {
489 lseek(fc, 0, SEEK_SET);
490 for (j=0; j<n; j++) {
492 tmp = bswap_32(buf[2]);
494 timetoint(tlast-toff, buf[0], buf[1]);
504 n = (fc != -1 ? lseek(fc, 0, SEEK_END) / 12 : 0);
506 c1 = readoff(fa, fao, fs, fso, strtotime(argv[i]), 1, ttmp);
508 if (transfer_start(fts, ftso, c1, c1ret)) return -1;
509 curr_size_offset = size_offset;
512 size_offset = curr_size_offset;
514 movesc(fs, fso, c1ret, 1);
516 toff += ttmp - tlast;
517 c2 = readoff(fa, fao, fs, fso, strtotime(argv[i+1]), 0, tlast);
518 if (transfer_rest(fts, ftso, c1, c2, c2ret)) return -1;
519 movesc(fs, fso, c2ret, 0);
520 printf("Interval: %lld - %lld\n", c1ret, c2ret);
521 if (n > 0) lseek(fc, 0, SEEK_SET);
522 for (j=0; j<n; j++) {
524 tmp = bswap_32(buf[2]);
525 ttmp2=inttotime(buf[0], buf[1]);
529 timetoint(ttmp-toff, buf[0], buf[1]);
532 } else if (ttmp2 <= tlast) {
533 timetoint(ttmp2-toff, buf[0], buf[1]);
538 } else if (ttmp2 >= ttmp && ttmp2 <= tlast) {
540 if (lio != io && lio != -1) {
541 buff[2] = bswap_32(io);
542 timetoint(ttmp-toff, buff[0], buff[1]);
543 write(fco, buff, 12);
547 timetoint(ttmp2-toff, buf[0], buf[1]);
549 } else if (tmp < 2) {
557 char* makefilename(const char* base, const char* pre, const char* ext, const char* post, int delext = 0)
559 static char buf[256];
560 int len1, len2, len3;
562 len2 = (pre ? strlen(pre) : 0);
563 len3 = (ext ? strlen(ext) : 0);
565 if (ext && len1>=len3 && !strcmp(base+len1-len3,ext))
568 strcpy(buf+len1, pre);
570 strcpy(buf+len1+len2, ext);
572 strcpy(buf+len1+len2+(delext ? 0 : len3), post);
576 void copymeta(int n, int f1, int f2, const char* title, const char* suff, const char* descr)
579 char* buf = new char[n];
586 for (j=i+1; j<n; j++)
591 for (k=0; title[k] && title[k] != 10; k++);
594 write(f2, buf+i, j-i);
596 write(f2, suff, strlen(suff));
600 for (j=i+1; j<n; j++)
605 for (k=0; descr[k] && descr[k] != 10; k++);
608 write(f2, buf+i, j-i);
611 write(f2, buf+j, n-j);
615 void copysmallfile(int n, int f1, int f2)
617 char* buf = new char[n];
623 int main(int argc, char* argv[])
625 int f_ts, f_out, f_cuts, f_cutsout, f_ap, f_apout, f_sc, f_scout, f_meta, f_metaout, f_eit, f_eitout;
627 const char* suff = 0;
632 int cutarg = 0, cutargend = 0;
633 int replace = 0, metafailed = 0;
634 int i, j, ok, bad = 0;
637 struct stat64 statbuf64;
639 for (i=1; i<argc; i++) {
640 if (!strcmp(argv[i], "-r"))
642 else if (!strcmp(argv[i], "-o")) {
648 } else if (!strcmp(argv[i], "-n")) {
654 } else if (!strcmp(argv[i], "-d")) {
660 } else if (!strcmp(argv[i], "-c")) {
662 for (j=i; j<argc; j+=2) {
663 t1 = strtotime(argv[j]);
664 t2 = (j+1<argc ? strtotime(argv[j+1]) : -1.0);
665 if (t1 < 0 || t2 < 0)
668 printf("Bad time interval: %s - %s\n", argv[j], argv[j+1]);
676 } else if (*argv[i] == '-' && (*(argv[i]+1) == 0 ||*(argv[i]+2) == 0)) {
686 if (argc == 1 || bad) {
687 printf("Usage: mcut [-r] [-o output_ts_file] [-n title] [-d description] ts_file [-c start1 end1 [start2 end2] ... ]\n");
688 printf(" -r : Replace (= remove) the original movie.\n");
689 printf(" -o : Filename of resulting movie (defaults to the original name appended by \" cut\", unless -r is given).\n");
690 printf(" -n : Title of resulting movie.\n");
691 printf(" -d : Description of resulting movie.\n");
692 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");
699 suff = (replace ? "_" : " cut");
701 tmpname = makefilename(inname, 0, ".ts", 0);
702 f_ts = open(tmpname, O_RDONLY | O_LARGEFILE);
704 printf("Failed to open input stream file \"%s\"\n", tmpname);
707 tmpname = makefilename(inname, 0, ".ts", ".cuts");
708 f_cuts = open(tmpname, O_RDONLY);
709 if (f_cuts == -1 && !cutarg) {
710 printf("Failed to open input cuts file \"%s\"\n", tmpname);
714 tmpname = makefilename(inname, 0, ".ts", ".ap");
715 f_ap = open(tmpname, O_RDONLY);
717 printf("Failed to open input ap file \"%s\"\n", tmpname);
719 if (f_cuts != -1) close(f_cuts);
722 tmpname = makefilename(inname, 0, ".ts", ".sc");
723 f_sc = open(tmpname, O_RDONLY);
725 if (fstat64(f_ts, &statbuf64)) {
726 printf("Failed to stat input stream file.\n");
729 if (f_sc != -1) close(f_sc);
730 if (f_cuts != -1) close(f_cuts);
733 tmpname = makefilename(outname, suff, ".ts", 0);
734 f_out = open(tmpname, O_WRONLY | O_CREAT | O_EXCL | O_LARGEFILE, statbuf64.st_mode & 0xfff);
736 printf("Failed to open output stream file \"%s\"\n", tmpname);
739 if (f_sc != -1) close(f_sc);
740 if (f_cuts != -1) close(f_cuts);
743 if (f_cuts != -1 && fstat(f_cuts, &statbuf)) {
745 printf("Failed to stat input cuts file.\n");
749 if (f_sc != -1) close(f_sc);
751 unlink(makefilename(outname, suff, ".ts", 0));
759 tmpname = makefilename(outname, suff, ".ts", ".cuts");
760 f_cutsout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
761 if (f_cutsout == -1) {
762 printf("Failed to open output cuts file \"%s\"\n", tmpname);
766 if (f_sc != -1) close(f_sc);
768 unlink(makefilename(outname, suff, ".ts", 0));
773 if (fstat(f_ap, &statbuf)) {
774 printf("Failed to stat input ap file.\n");
778 if (f_sc != -1) close(f_sc);
782 unlink(makefilename(outname, suff, ".ts", ".cuts"));
784 unlink(makefilename(outname, suff, ".ts", 0));
787 tmpname = makefilename(outname, suff, ".ts", ".ap");
788 f_apout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
790 printf("Failed to open output ap file \"%s\"\n", tmpname);
794 if (f_sc != -1) close(f_sc);
798 unlink(makefilename(outname, suff, ".ts", ".cuts"));
800 unlink(makefilename(outname, suff, ".ts", 0));
803 if (f_sc != -1 && fstat(f_sc, &statbuf)) {
808 tmpname = makefilename(outname, suff, ".ts", ".sc");
809 f_scout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
811 printf("Failed to open output sc file \"%s\"\n", tmpname);
820 unlink(makefilename(outname, suff, ".ts", ".cuts"));
822 unlink(makefilename(outname, suff, ".ts", 0));
823 unlink(makefilename(outname, suff, ".ts", ".ap"));
828 ok = donextinterval2(cutarg, cutargend, argv, f_cuts, f_cutsout, f_ap, f_apout, f_sc, f_scout, f_ts, f_out);
830 ok = donextinterval1(f_cuts, f_cutsout, f_ap, f_apout, f_sc, f_scout, f_ts, f_out);
832 printf("There are no cuts specified. Leaving the movie as it is.\n");
840 unlink(makefilename(outname, suff, ".ts", ".cuts"));
845 unlink(makefilename(outname, suff, ".ts", ".sc"));
847 unlink(makefilename(outname, suff, ".ts", 0));
848 unlink(makefilename(outname, suff, ".ts", ".ap"));
854 ok = donextinterval2(cutarg, cutargend, argv, f_cuts, f_cutsout, f_ap, f_apout, f_sc, f_scout, f_ts, f_out);
856 ok = donextinterval1(f_cuts, f_cutsout, f_ap, f_apout, f_sc, f_scout, f_ts, f_out);
872 printf("Copying %s failed, read/write error.\n", makefilename(inname, 0, ".ts", 0));
873 unlink(makefilename(outname, suff, ".ts", 0));
874 unlink(makefilename(outname, suff, ".ts", ".ap"));
876 unlink(makefilename(outname, suff, ".ts", ".cuts"));
878 unlink(makefilename(outname, suff, ".ts", ".sc"));
883 tmpname = makefilename(inname, 0, ".ts", ".eit", 1);
884 f_eit = open(tmpname, O_RDONLY);
886 if (fstat(f_eit, &statbuf))
889 tmpname = makefilename(outname, suff, ".ts", ".eit", 1);
890 f_eitout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
894 copysmallfile((int)statbuf.st_size, f_eit, f_eitout);
903 tmpname = makefilename(inname, 0, ".ts", ".meta");
904 f_meta = open(tmpname, O_RDONLY);
907 } else if (fstat(f_meta, &statbuf)) {
911 tmpname = makefilename(outname, suff, ".ts", ".meta");
912 f_metaout = open(tmpname, O_WRONLY | O_CREAT | O_TRUNC, statbuf.st_mode & 0xfff);
913 if (f_metaout == -1) {
917 copymeta((int)statbuf.st_size, f_meta, f_metaout, title, (replace ? 0 : suff), descr);
925 tmpname = makefilename(inname, 0, ".ts", 0);
926 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
928 rename(makefilename(outname, suff, ".ts", 0), tmpname);
929 tmpname = makefilename(inname, 0, ".ts", ".ap");
930 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
932 rename(makefilename(outname, suff, ".ts", ".ap"), tmpname);
934 tmpname = makefilename(inname, 0, ".ts", ".sc");
935 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
937 rename(makefilename(outname, suff, ".ts", ".sc"), tmpname);
940 tmpname = makefilename(inname, 0, ".ts", ".cuts");
941 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
943 rename(makefilename(outname, suff, ".ts", ".cuts"), tmpname);
946 tmpname = makefilename(inname, 0, ".ts", ".meta");
947 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
949 rename(makefilename(outname, suff, ".ts", ".meta"), tmpname);
952 unlink(makefilename(inname, 0, ".ts", 0));
953 unlink(makefilename(inname, 0, ".ts", ".ap"));
955 unlink(makefilename(inname, 0, ".ts", ".sc"));
957 unlink(makefilename(inname, 0, ".ts", ".cuts"));
958 tmpname = makefilename(inname, 0, ".ts", ".eit", 1);
959 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
960 rename(tmpname, makefilename(outname, 0, ".ts", ".eit", 1));
962 unlink(makefilename(inname, 0, ".ts", ".meta"));
964 tmpname = makefilename(inname, 0, ".ts", ".meta");
965 tmpname = strcpy(new char[strlen(tmpname)+1], tmpname);
966 rename(tmpname, makefilename(outname, 0, ".ts", ".meta"));