source: tools/xupnpd/main.cpp

Last change on this file was 34374, checked in by Stephan, 9 years ago

add xupnpd

File size: 15.3 KB
Line 
1/*
2
3 Copyright (C) 2009 Anton Burdinuk
4
5 clark15b@gmail.com
6
7*/
8
9
10#include "ts.h"
11#include "mpls.h"
12
13namespace ts
14{
15    class ts_file_info
16    {
17    public:
18        std::string filename;
19        u_int64_t first_dts;
20        u_int64_t first_pts;
21        u_int64_t last_pts;
22
23        ts_file_info(void):first_dts(0),first_pts(0),last_pts(0) {}
24    };
25
26#ifdef _WIN32
27    inline int strcasecmp(const char* s1,const char* s2) { return lstrcmpi(s1,s2); }
28#endif
29
30    int scan_dir(const char* path,std::list<std::string>& l);
31    void load_playlist(const char* path,std::list<std::string>& l,std::map<int,std::string>& d);
32    int get_clip_number_by_filename(const std::string& s);
33
34    bool is_ts_filename(const std::string& s)
35    {
36        if(!s.length())
37            return false;
38
39        if(s[s.length()-1]=='/' || s[s.length()-1]=='\\')
40            return false;
41
42        std::string::size_type n=s.find_last_of('.');
43
44        if(n==std::string::npos)
45            return false;
46
47        std::string ext=s.substr(n+1);
48
49        if(!strcasecmp(ext.c_str(),"ts") || !strcasecmp(ext.c_str(),"m2ts"))
50            return true;
51
52        return false;
53    }
54
55    std::string trim_slash(const std::string& s)
56    {
57        const char* p=s.c_str()+s.length();
58
59        while(p>s.c_str() && (p[-1]=='/' || p[-1]=='\\'))
60            p--;
61
62        return s.substr(0,p-s.c_str());
63    }
64
65    const char* timecode_to_time(u_int32_t timecode)
66    {
67        static char buf[128];
68
69        int msec=timecode%1000;
70        timecode/=1000;
71
72        int sec=timecode%60;
73        timecode/=60;
74
75        int min=timecode%60;
76        timecode/=60;
77
78        sprintf(buf,"%.2i:%.2i:%.2i.%.3i",(int)timecode,min,sec,msec);
79
80        return buf;
81    }
82
83}
84
85#ifdef _WIN32
86int ts::scan_dir(const char* path,std::list<std::string>& l)
87{
88    _finddata_t fileinfo;
89
90    intptr_t dir=_findfirst((std::string(path)+"\\*.*").c_str(),&fileinfo);
91
92    if(dir==-1)
93        perror(path);
94    else
95    {
96        while(!_findnext(dir,&fileinfo))
97            if(!(fileinfo.attrib&_A_SUBDIR) && *fileinfo.name!='.')
98            {
99                char p[512];
100
101                int n=sprintf(p,"%s\\%s",path,fileinfo.name);
102
103                l.push_back(std::string(p,n));
104            }
105    }
106
107    _findclose(dir);
108
109    return l.size();
110}
111#else
112int ts::scan_dir(const char* path,std::list<std::string>& l)
113{
114    DIR* dir=opendir(path);
115
116    if(!dir)
117        perror(path);
118    else
119    {
120        dirent* d;
121
122        while((d=readdir(dir)))
123        {
124            if (d->d_type == DT_UNKNOWN) {
125                char p[512];
126
127                if (snprintf(p, sizeof(p), "%s/%s", path, d->d_name) > 0) {
128                    struct stat st;
129
130                    if (stat(p, &st) != -1) {
131                        if (S_ISREG(st.st_mode))
132                            d->d_type = DT_REG;
133                        else if (S_ISLNK(st.st_mode))
134                            d->d_type = DT_LNK;
135                    }
136                }
137            }
138            if(*d->d_name!='.' && (d->d_type==DT_REG || d->d_type==DT_LNK))
139            {
140                char p[512];
141
142                int n=sprintf(p,"%s/%s",path,d->d_name);
143
144                l.push_back(std::string(p,n));
145            }
146        }
147
148        closedir(dir);
149    }
150
151    return l.size();
152}
153#endif
154
155void ts::load_playlist(const char* path,std::list<std::string>& l,std::map<int,std::string>& d)
156{
157    FILE* fp=fopen(path,"r");
158
159    char buf[512];
160
161    while(fgets(buf,sizeof(buf),fp))
162    {
163        char* p=buf;
164        while(*p && (*p==' ' || *p=='\t'))
165            p++;
166
167        int len=0;
168
169        char* p2=strpbrk(p,"#\r\n");
170        if(p2)
171            len=p2-p;
172        else
173            len=strlen(p);
174
175        while(len>0 && (p[len-1]==' ' || p[len-1]=='\t'))
176            len--;
177
178        if(len>0)
179        {
180            l.push_back(std::string());
181
182            std::string& s=l.back();
183
184            std::string ss(p,len);
185            std::string::size_type n=ss.find_first_of(';');
186            if(n==std::string::npos)
187                s.swap(ss);
188            else
189            {
190                s=ss.substr(0,n);
191                d[get_clip_number_by_filename(s)]=ss.substr(n+1);
192            }
193        }
194    }
195}
196
197
198int ts::get_clip_number_by_filename(const std::string& s)
199{
200    int ll=s.length();
201    const char* p=s.c_str();
202
203    while(ll>0)
204    {
205        if(p[ll-1]=='/' || p[ll-1]=='\\')
206            break;
207        ll--;
208    }
209
210    p+=ll;
211    int cn=0;
212
213    const char* pp=strchr(p,'.');
214    if(pp)
215    {
216        for(int i=0;i<pp-p;i++)
217            cn=cn*10+(p[i]-48);
218    }
219
220    return cn;
221}
222
223
224int main(int argc,char** argv)
225{
226    fprintf(stderr,"tsdemux 1.53 AVCHD/Blu-Ray HDMV Transport Stream demultiplexer\n\nCopyright (C) 2009 Anton Burdinuk\n\nclark15b@gmail.com\nhttp://code.google.com/p/tsdemuxer\n\n");
227
228    if(argc<2)
229    {
230        fprintf(stderr,"USAGE: ./tsdemux [-d src] [-l mpls] [-s pls] [-o dst] [-c channel] [-u] [-j] [-m] [-z] [-p] [-e mode] [-v] *.ts|*.m2ts ...\n");
231        fprintf(stderr,"-d demux all mts/m2ts/ts files from directory\n");
232        fprintf(stderr,"-l use AVCHD/Blu-Ray playlist file (*.mpl,*.mpls)\n");
233        fprintf(stderr,"-s playlist text file\n");
234        fprintf(stderr,"-o redirect output to another directory or transport stream file\n");
235        fprintf(stderr,"-c channel number for demux\n");
236        fprintf(stderr,"-u demux unknown streams\n");
237        fprintf(stderr,"-j join elementary streams\n");
238        fprintf(stderr,"-m show mkvmerge command example\n");
239        fprintf(stderr,"-z demux to PES streams (instead of elementary streams)\n");
240        fprintf(stderr,"-p parse only\n");
241        fprintf(stderr,"-e dump TS structure to STDOUT (mode=1: dump M2TS timecodes, mode=2: dump PTS/DTS, mode=3: human readable PTS/DTS dump)\n");
242        fprintf(stderr,"-v turn on verbose output\n");
243        fprintf(stderr,"\ninput files can be *.m2ts, *.mts or *.ts\n");
244        fprintf(stderr,"output elementary streams to *.sup, *.m2v, *.264, *.vc1, *.ac3, *.m2a and *.pcm files\n");
245        fprintf(stderr,"\n");
246        return 0;
247    }
248
249    bool parse_only=false;
250    int dump=0;
251    bool av_only=true;
252    bool join=false;
253    int channel=0;
254    bool pes=false;
255    bool verb=false;
256    std::string output;
257    bool mkvmerge_opts=false;
258
259    std::string mpls_file;                      // MPLS file
260
261    std::list<std::string> playlist;            // playlist
262    std::map<int,std::string> mpls_datetime;    // AVCHD clip date/time
263
264    int opt;
265    while((opt=getopt(argc,argv,"pe:ujc:zo:d:l:vms:"))>=0)
266        switch(opt)
267        {
268        case 'p':
269            parse_only=true;
270            break;
271        case 'e':
272            dump=atoi(optarg);
273            break;
274        case 'u':
275            av_only=false;
276            break;
277        case 'j':
278            join=true;
279            break;
280        case 'c':
281            channel=atoi(optarg);
282            break;
283        case 'z':
284            pes=true;
285            break;
286        case 'o':
287            output=ts::trim_slash(optarg);
288            break;
289        case 'd':
290            {
291                std::list<std::string> l;
292                ts::scan_dir(ts::trim_slash(optarg).c_str(),l);
293                l.sort();
294                playlist.merge(l);
295            }
296            break;
297        case 'l':
298            mpls_file=optarg;
299            break;
300        case 's':
301            {
302                std::list<std::string> l;
303                ts::load_playlist(optarg,l,mpls_datetime);
304                playlist.merge(l);
305            }
306            break;
307        case 'v':
308            verb=true;
309            break;
310        case 'm':
311            mkvmerge_opts=true;
312            break;
313        }
314
315    while(optind<argc)
316    {
317        playlist.push_back(argv[optind]);
318        optind++;
319    }
320
321    if(mpls_file.length())
322    {
323        std::list<int> mpls;                        // list of clip id from mpls files
324        std::list<std::string> new_playlist;
325
326        if(mpls::parse(mpls_file.c_str(),mpls,mpls_datetime,playlist.size()?verb:1))
327            fprintf(stderr,"%s: invalid playlist file format\n",mpls_file.c_str());
328
329        if(mpls.size())
330        {
331            std::map<int,std::string> clips;
332            for(std::list<std::string>::iterator i=playlist.begin();i!=playlist.end();++i)
333            {
334                std::string& s=*i;
335                clips[ts::get_clip_number_by_filename(s)]=s;
336            }
337
338            for(std::list<int>::iterator i=mpls.begin();i!=mpls.end();++i)
339            {
340                std::string& s=clips[*i];
341
342                if(s.length())
343                    new_playlist.push_back(s);
344            }
345
346            playlist.swap(new_playlist);
347        }
348    }
349
350    time_t beg_time=time(0);
351
352    if(!ts::is_ts_filename(output))
353    {
354        if(join)
355        {
356            if(playlist.size())
357            {
358                std::string chapters_filename=output.length()?(output+os_slash+"chapters.xml"):"chapters.xml";
359
360                FILE* fp=0;
361
362                if(channel)
363                {
364                    fp=fopen(chapters_filename.c_str(),"w");
365
366                    if(!fp)
367                        fprintf(stderr,"can`t create %s\n",chapters_filename.c_str());
368                }
369
370                if(fp)
371                {
372                    fprintf(fp,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n");
373                    fprintf(fp,"<!-- <!DOCTYPE Tags SYSTEM \"matroskatags.dtd\"> -->\n\n");
374                    fprintf(fp,"<Chapters>\n");
375                    fprintf(fp,"  <EditionEntry>\n");
376                }
377
378                ts::demuxer demuxer;
379
380                int cn=1;
381
382                for(std::list<std::string>::iterator i=playlist.begin();i!=playlist.end();++i,cn++)
383                {
384                    const std::string& s=*i;
385
386                    int clip=ts::get_clip_number_by_filename(s);
387                    const std::string& date=mpls_datetime[clip];
388                    u_int32_t offset=demuxer.base_pts/90;
389
390                    if(verb)
391                        fprintf(stderr,"%.5i: '%s', %u\n",clip,date.c_str(),offset);
392
393                    if(fp)
394                    {
395                        fprintf(fp,"    <ChapterAtom>\n");
396                        fprintf(fp,"      <ChapterTimeStart>%s</ChapterTimeStart>\n",ts::timecode_to_time(offset));
397                        fprintf(fp,"      <ChapterDisplay>\n");
398                        if(date.length())
399                            fprintf(fp,"        <ChapterString>%s</ChapterString>\n",date.c_str());
400                        else
401                            fprintf(fp,"        <ChapterString>Clip %i</ChapterString>\n",cn);
402                        fprintf(fp,"      </ChapterDisplay>\n");
403                        fprintf(fp,"    </ChapterAtom>\n");
404                    }
405
406                    demuxer.av_only=av_only;
407                    demuxer.channel=channel;
408                    demuxer.pes_output=pes;
409                    demuxer.dst=output;
410                    demuxer.verb=verb;
411                    demuxer.es_parse=true;
412
413                    demuxer.demux_file(s.c_str());
414
415                    demuxer.gen_timecodes(date);
416
417                    demuxer.reset();
418                }
419
420                demuxer.show();
421
422                if(mkvmerge_opts)
423                {
424                    fprintf(stdout,"\n# mkvmerge -o output.mkv ");
425                    if(fp)
426                        fprintf(stdout,"--chapters %s ",chapters_filename.c_str());
427                    for(std::map<u_int16_t,ts::stream>::iterator i=demuxer.streams.begin();i!=demuxer.streams.end();++i)
428                    {
429                        ts::stream& s=i->second;
430
431                        if(s.file.filename.length())
432                        {
433                            std::string::size_type n=s.file.filename.find_last_of('.');
434                            if(n!=std::string::npos)
435                            {
436                                std::string filename=s.file.filename.substr(0,n);
437                                filename+=".tmc";
438                                fprintf(stdout,"--timecodes 0:%s ",filename.c_str());
439                            }
440                            fprintf(stdout,"%s ",s.file.filename.c_str());
441                        }
442                    }
443
444                    if(demuxer.subs_filename.length())
445                        fprintf(stdout,"%s",demuxer.subs_filename.c_str());
446
447                    fprintf(stdout,"\n");
448                }
449
450                if(fp)
451                {
452                    fprintf(fp,"  </EditionEntry>\n");
453                    fprintf(fp,"</Chapters>\n");
454                    fclose(fp);
455                }
456            }
457        }else
458        {
459            for(std::list<std::string>::iterator i=playlist.begin();i!=playlist.end();++i)
460            {
461                const std::string& s=*i;
462
463                ts::demuxer demuxer;
464
465                demuxer.parse_only=dump>0?true:parse_only;
466                demuxer.es_parse=demuxer.parse_only;
467                demuxer.dump=dump;
468                demuxer.av_only=av_only;
469                demuxer.channel=channel;
470                demuxer.pes_output=pes;
471                demuxer.dst=output;
472                demuxer.verb=verb;
473
474                demuxer.demux_file(s.c_str());
475
476                demuxer.show();
477            }
478        }
479    }else
480    {
481        // join to TS/M2TS file
482        fprintf(stderr,"join to TS/M2TS is not implemented!\n");
483/*
484        std::list<ts::ts_file_info> info;
485
486        if(!channel)
487        {
488            fprintf(stderr,"the channel is not chosen, set to 1\n");
489            channel=1;
490        }
491
492
493        fprintf(stderr,"\nstep 1 - analyze a stream\n\n");
494
495        for(std::list<std::string>::iterator i=playlist.begin();i!=playlist.end();++i)
496        {
497            const std::string& name=*i;
498
499            ts::demuxer demuxer;
500
501            demuxer.parse_only=true;
502            demuxer.av_only=av_only;
503            demuxer.channel=channel;
504
505            demuxer.demux_file(name.c_str());
506            demuxer.show();
507
508            info.push_back(ts::ts_file_info());
509            ts::ts_file_info& nfo=info.back();
510            nfo.filename=name;
511
512            for(std::map<u_int16_t,ts::stream>::const_iterator i=demuxer.streams.begin();i!=demuxer.streams.end();++i)
513            {
514                const ts::stream& s=i->second;
515
516                if(s.type!=0xff)
517                {
518                    if(s.first_dts)
519                    {
520                        if(s.first_dts<nfo.first_dts || !nfo.first_dts)
521                            nfo.first_dts=s.first_dts;
522                    }
523
524                    if(s.first_pts<nfo.first_pts || !nfo.first_pts)
525                        nfo.first_pts=s.first_pts;
526
527                    u_int64_t n=s.last_pts+s.frame_length;
528
529                    if(n>nfo.last_pts || !nfo.last_pts)
530                        nfo.last_pts=n;
531                }
532            }
533        }
534
535        fprintf(stderr,"\nstep 2 - remux\n\n");
536
537        u_int64_t cur_pts=0;
538
539        int fn=0;
540
541        for(std::list<ts::ts_file_info>::iterator i=info.begin();i!=info.end();++i,fn++)
542        {
543            ts::ts_file_info& nfo=*i;
544
545            u_int64_t first_pts=nfo.first_dts>0?(nfo.first_pts-nfo.first_dts):0;
546
547            if(!fn)
548                cur_pts=first_pts;
549
550            fprintf(stderr,"%s: %llu (len=%llu)\n",nfo.filename.c_str(),cur_pts,nfo.last_pts-first_pts);
551
552            // new_pes_pts=cur_pts+(pts_from_pes-first_pts)
553
554
555            cur_pts+=nfo.last_pts-first_pts;
556        }
557*/
558    }
559
560    fprintf(stderr,"\ntime: %li sec\n",time(0)-beg_time);
561
562    return 0;
563}
Note: See TracBrowser for help on using the repository browser.