Credit Andrew R for finding the direct copy mods for exr and ppm sequences
[goodguy/cinelerra.git] / cinelerra-5.1 / libzmpeg3 / mpeg3cc2txt.C
1 #include <stdio.h>
2 #include <stdint.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <string.h>
6 #include <errno.h>
7 #include <ctype.h>
8
9 #include "libzmpeg3.h"
10
11 const char *prog = 0;
12 int vtrk = 0;
13 int verbose = 0;
14 int wdw_mask = -1;
15 int cc_service = 1;
16 int done = 0;
17 long fpos = 0;
18 char dfrm[1000];
19 int dlen = 0;
20 long dsofs = 0, deofs = 0;
21 char *xfn = 0, *ofn = 0;
22 FILE *xfp = 0, *ofp = 0;
23
24 class Edit {
25   long start, length;
26   long offset;
27 public:
28   static int cmpr(const void *ap, const void *bp);
29   Edit(long st, long len, long ofs) : start(st), length(len), offset(ofs) {}
30   long sfrm() { return start; }
31   long efrm() { return start+length-1; }
32   long ofrm(long frm) { return frm-start+offset; }
33   ~Edit() {}
34 };
35
36 int Edit::cmpr(const void *ap, const void *bp)
37 {
38   Edit *a = *(Edit **)ap;
39   Edit *b = *(Edit **)bp;
40   int n = a->sfrm() - b->sfrm();
41   if( !n ) n = a->length - b->length;
42   return n;
43 }
44
45 class Edits {
46   int count, allocated;
47   Edit **list;
48   long pos;
49 public:
50   Edits() : count(0), allocated(0), list(0), pos(0) {}
51   ~Edits() { for( int i=0; i<count; ++i ) delete list[i]; }
52   void append(long st, long len);
53   bool finished(long frm) { return count>0 && frm>=list[count-1]->efrm(); }
54   Edit *active(long frm);
55   void sort() { qsort(list, count, sizeof(Edit*), Edit::cmpr); }
56   int len() { return count; }
57 } edits;
58
59 void Edits::append(long st, long len)
60 {
61   if( count >= allocated ) {
62     int sz = 2*allocated + 16;
63     Edit **new_list = new Edit*[sz];
64     for( int i=0; i<count; ++i ) new_list[i] = list[i];
65     delete list;  list = new_list;  allocated = sz;
66   }
67   list[count++] = new Edit(st, len, pos);
68   pos += len;
69 }
70
71 Edit *Edits::active(long frm)
72 {
73   Edit *mp = 0;
74   int l=-1, h=count;
75   while( h-l > 1 ) {
76     int m = (l+h) >> 1;
77     mp = list[m];
78     if( frm < mp->sfrm() ) l = m;
79     else if( frm > mp->sfrm() ) h = m;
80     else break;
81   }
82   if( !mp ) return 0;
83   if( frm < mp->sfrm() ) return 0;
84   if( frm > mp->efrm() ) return 0;
85   return mp;
86 }
87
88
89 void usage()
90 {
91   fprintf(stderr,"usage: %s ",prog);
92   fprintf(stderr," [-c cc_service ] ");
93   fprintf(stderr," [-s start:length, ...] ");
94   fprintf(stderr," [ -t track ] ");
95   fprintf(stderr," [-v verbose ]\n");
96   fprintf(stderr,"            ");
97   fprintf(stderr," [-w wdw mask ] ");
98   fprintf(stderr," [-x file.xml ] ");
99   fprintf(stderr," [-o file.udvd ] ");
100   fprintf(stderr," file.ts\n");
101   fprintf(stderr,"  cc_service   = closed caption service id\n");
102   fprintf(stderr,"  start:length = start:length frames (comma seperated list)\n");
103   fprintf(stderr,"  track        = video track number\n");
104   fprintf(stderr,"  verbose      = verbose level\n");
105   fprintf(stderr,"  wdw mask     = bit mask for windows (-1 = all)\n");
106   fprintf(stderr,"  file.xml     = filename for edl xml format subtitle data\n");
107   fprintf(stderr,"  file.udvd    = filename for udvd format subtitle data\n");
108   fprintf(stderr,"  file.ts      = filename for transport stream\n");
109   exit(1);
110 }
111
112 void edl_header()
113 {
114   fprintf(xfp,"<?xml version=\"1.0\"?>\n");
115   fprintf(xfp,"<EDL VERSION=4.4 PATH=%s>\n", xfn);
116   fprintf(xfp," <TRACK RECORD=1 NUDGE=0 PLAY=1 GANG=1 DRAW=1 EXPAND=0");
117   fprintf(xfp," TRACK_W=0 TRACK_H=0 TYPE=SUBTTL>\n");
118   fprintf(xfp,"<TITLE>Subttl 1</TITLE>\n");
119   fprintf(xfp,"<EDITS>\n");
120 }
121
122 void edit_data(const char *sp, int n, int len)
123 {
124   const char *cp, *ep = sp + n;
125   fprintf(xfp,"<EDIT STARTSOURCE=0 CHANNEL=0 LENGTH=%d TEXT=\"", len);
126   while( sp < ep ) {
127     int ch = *sp++;
128     switch( ch ) {
129     case '<':  cp = "&lt;";    break;
130     case '>':  cp = "&gt;";    break;
131     case '&':  cp = "&amp;";   break;
132     case '"':  cp = "&quot;";  break;
133     default:  fputc(ch, xfp);  continue;
134     }
135     while( *cp != 0 ) fputc(*cp++, xfp);
136   }
137   fprintf(xfp,"\"></EDIT>\n");
138 }
139
140
141 void edit_write()
142 {
143   if( !xfp ) return;
144   long df;
145   if( (df=dsofs-fpos) > 0 ) edit_data("", 0, df);
146   if( (df=deofs-dsofs) > 0 ) edit_data(dfrm, dlen, df);
147   fpos = deofs;
148 }
149
150 void edl_trailer()
151 {
152   if( dlen > 0 ) edit_write();
153   fprintf(xfp,"</EDITS>\n");
154   fprintf(xfp,"</TRACK>\n");
155   fprintf(xfp,"</EDL>\n");
156 }
157
158 int text_cb(int sid, int id, int sfrm, int efrm, const char *txt)
159 {
160   long sofs = sfrm, eofs = efrm;
161   if( ((1<<id) & wdw_mask) == 0 ) return 0;
162   if( edits.finished(sfrm) ) { done = 1; return 0; }
163   // check for edit range specs
164   if( edits.len() > 0 ) {
165     Edit *edit = edits.active(sfrm);
166     if( !edit ) return 0;
167     long frm;
168     if( sfrm < (frm=edit->sfrm()) ) sfrm = frm;
169     if( efrm > (frm=edit->efrm()) ) efrm = frm;
170     sofs = edit->ofrm(sfrm);
171     eofs = edit->ofrm(efrm);
172   }
173   // ltrim, rtrim data
174   const char *bp, *cp;
175   for( bp=txt; *bp; ++bp )
176     if( !isblank(*bp) && *bp != '|' ) break;
177   if( !*bp ) return 0;
178   const char *ep = bp;
179   for( cp=bp; *cp; ++cp )
180     if( !isblank(*cp) && *cp != '|' ) ep = cp;
181   int n = ep - bp + 1;
182   // logging
183   if( verbose ) {
184     if( verbose > 2 ) fprintf(stderr,"\r");
185     fprintf(stderr,"%d",sfrm);
186     if( verbose > 1 ) fprintf(stderr,": %*.*s", n, n, bp);
187     fprintf(stderr,"\n");
188   }
189   // output txt directly
190   if( ofp && sofs < eofs ) {
191     fprintf(ofp, "{%ld}{%ld}%*.*s\n", sofs, eofs-1, n, n, bp);
192   }
193   // last dfrm expired, output edit_data + reset dfrm
194   if( sofs >= deofs ) {
195     if( dlen > 0 ) edit_write();
196     dlen = 0;  dsofs = sofs;
197   }
198   // append overlapping timeline data
199   if( dlen > 0 && dlen < (int)sizeof(dfrm) ) dfrm[dlen++] = '|';
200   while( bp <= ep && dlen < (int)sizeof(dfrm) ) dfrm[dlen++] = *bp++;
201   if( eofs > deofs ) deofs = eofs;
202   return 0;
203 }
204
205 int main(int ac, char *av[])
206 {
207   char *cp = 0, *fn = 0;
208   prog = *av++;
209
210   while( --ac > 0 ) {
211     fn = *av++;
212     if( *fn == '-' ) {
213       switch( fn[1] ) {
214       case 'c':
215         if( --ac <= 0 ) usage();
216         cc_service = atoi(*av++);
217         break;
218       case 's':
219         if( --ac <= 0 ) usage();
220         for( cp=*av++; *cp; ++cp ) {
221           int st = strtol(cp,&cp,0);
222           int len = *cp == ':' ? strtol(cp+1,&cp,0) : INT_MAX;
223           edits.append(st,len);
224           if( *cp != ',' ) break;
225         }
226         if( *cp ) { fprintf(stderr,"edit list error at: %s\n",cp); usage(); }
227         edits.sort();
228         break;
229       case 't':
230         if( --ac <= 0 ) usage();
231         vtrk = atoi(*av++);
232         break;
233       case 'v':
234         if( --ac <= 0 ) usage();
235         verbose = atoi(*av++);
236         break;
237       case 'o':
238         if( --ac <= 0 ) usage();
239         ofn = *av++;
240         break;
241       case 'x':
242         if( --ac <= 0 ) usage();
243         xfn = *av++;
244         break;
245       case 'w':
246         if( --ac <= 0 ) usage();
247         wdw_mask = strtoul(*av++,0,0);
248         if( !wdw_mask ) {
249           fprintf(stderr,"no windows in bitmask\n");
250           exit(1);
251         }
252         break;
253       default:
254         usage();
255       }
256       continue;
257     }
258     if( ac > 1 ) usage();
259     if( !access(fn,R_OK) ) break;
260     fprintf(stderr, "no file %s\n", fn);
261     exit(0);
262   }
263
264   if( !fn ) usage();
265   setbuf(stderr, 0);
266
267   if( !xfn && !ofn ) {
268     fprintf(stderr,"no output file (-x,-o) in command line arguments\n");
269     usage();
270   }
271   if( xfn && ofn && !strcmp(xfn,ofn) ) {
272     fprintf(stderr,"output files (-x,-o) overlap command line arguments\n");
273     usage();
274   }
275   if( ofn ) {
276     if( !strcmp(ofn,"-") ) ofp = stdout;
277     else if( !(ofp=fopen(ofn,"w")) ) {
278       perror(ofn);  usage();
279     }
280   }
281   if( xfn ) {
282     if( !strcmp(xfn,"-") ) xfp = stdout;
283     else if( !(xfp=fopen(xfn,"w")) ) {
284       perror(xfn);  usage();
285     }
286   }
287
288   zmpeg3_t *in;
289   int error = 0;
290   if( !(in = mpeg3_open(fn, &error)) ) {
291     fprintf(stderr, "unable to open %s\n", fn);
292     exit(1);
293   }
294   if( !in->is_transport_stream() ) {
295     fprintf(stderr, "not transport stream %s\n", fn);
296     exit(1);
297   }
298   if( !mpeg3_has_video(in) ) {
299     fprintf(stderr, "no video %s\n", fn);
300     exit(1);
301   }
302
303   mpeg3_set_cpus(in, 8);
304   int ctrk = cc_service-1;
305   if( ctrk < 0 ) ctrk = 63;
306   mpeg3_show_subtitle(in, vtrk, ctrk);
307   if( mpeg3_set_cc_text_callback(in, vtrk, text_cb) ) {
308     fprintf(stderr, "no video track %d on %s\n", vtrk, fn);
309     exit(1);
310   }
311   if( xfp ) edl_header();
312
313   char *yp, *up, *vp;
314   while( !done && !mpeg3_read_yuvframe_ptr(in, &yp, &up, &vp, vtrk) )
315     if( verbose > 2 ) fprintf(stderr," \r%ld",  mpeg3_get_frame(in, vtrk));
316
317   if( xfp ) edl_trailer();
318   mpeg3_close(in);
319   exit(0);
320 }
321