/************************************************************* * mpgtx an mpeg toolbox * * by Laurent Alacoque * * (c) 2001 * * You may copy, modify and redistribute this * * source file under the terms of the GNU Public License * ************************************************************/ #include #include #include #include "mpeg.hh" #include "mpegOut.hh" #include "chunkTab.hh" // declared in id3command.hh extern int ParseID3Command(int argc, char** argv, int argc_offset); // philipp bool desperate_mode = false; bool preserve_header = false; bool print_progress = true; // [L] ifdef the endMessage #ifdef NEED_END_MESSAGE void endMessage(void); #endif bool aspect_correction = false; byte forced_sequence_header = 0; char* progname; #ifndef NOSIGNAL_H // for bug report #include #include // for bug report char** myargv; int myargc; c_char mpgtx_stack[mpgtx_stack_count]; int mpgtx_stack_current=0; /*! * Callback for SIGSEGV, SIGBUS and SIGFPE. * prints a formated bug report, warns user and opens the bug report file * * @param signum catched signal: SIGSEGV, SIGBUS or SIGFPE */ void CatchBug(int signum) { //print a formated bug report and warn user //open the bug report file FILE* bugreport; bugreport=fopen("bug.report","w+"); //warn user fprintf(stderr,"BUG: Sorry you shouldn't read this...\n"); fprintf(stderr,"Please help me to correct this and send a bug report\n"); fprintf(stderr,"To send a bug report go to http://mpgtx.sourceforge.net\n"); fprintf(stderr,"and click the submit new bug link (top right)\n"); fprintf(stderr,"you can send it to either\n"); if (!bugreport) { bugreport=stderr; fprintf(stderr,"Please Include the following lines and the program output in the bug report\n"); fprintf(stderr, "---------------------------------------------------------------------------\n"); }else{ fprintf(stderr,"Please include the file \"bug.report\" and the program output in the bug report\n"); } //start of bug report //version fprintf(bugreport,"%s version %s\n",progname,MPGTX_VERSION); // arguments to the program fprintf(bugreport,"args : "); for (int i=0; i < myargc; i++){ fprintf (bugreport,"%s ",myargv[i]); } fprintf(bugreport,"\n"); //what killed me switch(signum){ case SIGSEGV: fprintf(bugreport,"SIGSEGV");break; case SIGBUS: fprintf(bugreport,"SIGBUS");break; case SIGFPE: fprintf(bugreport,"SIGFPE");break; } fprintf(bugreport," received\n"); //print the calling stack fprintf(bugreport,"stack : "); for (int j=0; j #include #include struct timeval tv; struct timezone tz; double MainClockStart; double Clock_Now(){ double time_now; gettimeofday(&tv,&tz); time_now=tv.tv_sec + 0.000001 * tv.tv_usec; gettimeofday(&tv,&tz); functions_cummulated_time[SELF]+=2*((tv.tv_sec + 0.000001 * tv.tv_usec)-time_now); return tv.tv_sec + 0.000001 * tv.tv_usec; } void AddTime(double timestart, int function){ functions_cummulated_time[function]+= Clock_Now()-timestart; } double functions_cummulated_time[N_FUNCTIONS]; void init_cummulated_time(){ MainClockStart=Clock_Now(); atexit(PrintTime); for (int i=0; i 1:1, n=2 -> 4:3, n=3 -> 16:9, n=4 -> 2.21:1 \n"); printf(" All Numbers from 0 - 16 will be accepted but the above are valid!\n"); if (invocation != mpgjoin) printf(" -b NAME set the basename for the output files\n"); if (invocation != mpgsplit && invocation != mpgdemux){ printf(" -o FILE set the output file name (join implied)\n"); printf(" if FILE is - , standard output will be used\n"); printf(" --force force joining of incompatible files\n"); } printf(" --no-parachute don't try to catch SIGSEGV (usefull for debugging)\n"); } if (invocation==mpgdemux) return; printf("Ranges :\n"); printf(" Ranges must follow an mpeg file\n"); printf(" [a-b] from 'a' inclusive to 'b' inclusive. If you want half opened\n"); printf(" ranges, you may want to use ]a-b], [a-b[ or ]a-b[ instead\n"); printf(" [num/total] the 'num' part if the mpeg file was split in 'total'.\n"); printf(" [1/4] would result in the first quarter of the mpeg file.\n"); if (invocation !=mpgjoin){ printf(" {a-b-...} where 'a', 'b', ... are in ascending order. Split the file\n"); printf(" at given values. {700M} is therefore equivalent to ranges\n"); printf(" [-700M] ]700M-]\n"); } printf("Values :\n"); printf(" Values can be time or offsets in the mpeg file\n"); printf(" Time Format HH:MM:SS where the HH: part can be omited\n"); printf(" Offset Format a number optionally followed by:\n"); printf(" M : offset is in Megabytes\n"); printf(" k : offset is in Kilobytes\n"); printf(" An empty value means the corresponding file boundary:\n"); printf(" [-10M] the first 10 Megabytes of file\n"); printf(" [500M-] from 500 Megabytes to the end of file\n"); return; } // added 22 March 2001 to get rid of unistd (portability) int mpgtx_access(char* filename){ BTRACK; // returns 0 if file exist (readable) // returns 1 if not. FILE* test_file = fopen(filename,"r+"); if (test_file == NULL) { return 1; } else { fclose(test_file); return 0; } } int main (int argc,char** argv){ #ifndef NOSIGNAL_H signal(SIGBUS,CatchBug); signal(SIGSEGV,CatchBug); signal(SIGFPE,CatchBug); signal(SIGINT,CatchBreak); for (int k=0; k < mpgtx_stack_count; k++) mpgtx_stack[k]=0; BTRACK; myargv=argv; myargc=argc; #endif #ifdef ENABLE_OPTIMIZATION init_cummulated_time(); #endif mpegOutFactory TheFac; chunkTab Tab(20); //char* current_filename=0; mpegOut* current_out; char* number; char* mybasename=0; char* myoutfile=0; chunk** ChunkTab; int ChunkCount; int nparts; char answer; bool want_info=false; bool basename_specified=false; bool joined=false; bool inparts=false; bool opresent=false; bool confirm_files=true; bool Id3Tag=false; bool force_option=false; bool catch_sigsegv=true; argtype lastarg=none; programname invocation; bool demux=false; int tmp_seq; FILE *tmp_file=NULL; //////////////////////////////////////// // Parse the program name // //////////////////////////////////////// ATRACK("parse progname"); int i; int length=strlen(argv[0]); int start=0; for ( i = length-1; i >= 0; i--) { if((argv[0][i] == '/') || (argv[0][i] == '\\')) { start = i + 1; break; } } progname=new char[length-start+1]; strcpy(progname,&argv[0][start]); if (!strcmp(progname,"mpgsplit")){ invocation=mpgsplit; inparts=true; } else { if (!strcmp(progname,"mpgcat")) { invocation=mpgcat; joined=true; opresent=true; myoutfile=new char[2]; myoutfile[0]='-'; myoutfile[1]=0; } else { if (!strcmp(progname,"mpgjoin")){ invocation=mpgjoin; joined=true; } else{ if (!strcmp(progname,"mpginfo")){ invocation=mpginfo; want_info=true; } else { if (!strcmp(progname,"mpgdemux")){ invocation=mpgdemux; demux=true; } else{ if (!strcmp(progname,"tagmp3")){ return ParseID3Command(argc,argv,1); } else{ invocation=other; } } } } } } // Warn user. /*fprintf(stderr,"%s version %s\n" "THIS IS *BETA* SOFTWARE I really need your help to correct all remaining bugs.\n" "If you find any bug in this program you are encouraged to post a bug report on\n" "mpgtx homepage .Thanks in advance for your help.\n\n" ,progname,MPGTX_VERSION); */ /////////////////////////////////////// // Parse remaining args // ///////////////////////////////////// ATRACK("Parse Args"); for ( i=1; i '0')&&(argv[i][1]<='9')) { number=&argv[i][1]; sscanf(number,"%d",&nparts); if(i+1>=argc){ fprintf(stderr,"%s option used without a filename\n",argv[i]); return 1; } if(!Tab.AddFile(argv[++i])){ fprintf(stderr,"%s is not a valid mpeg file\n",argv[i-1]); if(!want_info) return 1; } Tab.Nchunks(nparts); inparts=true; } else { switch (argv[i][1]){ case 'd' : demux=true; break; case 'X' : desperate_mode =true; break; case 'P' : preserve_header = true; break; case 'N': print_progress = false; break; case 'A': aspect_correction = true; tmp_seq = -1; sscanf(&(argv[i][2]),"%d",&tmp_seq); if ((tmp_seq < 0 ) || (tmp_seq > 15)) { fprintf(stderr,"invalid Value for aspect correction %d, exit!\n",tmp_seq); exit(1); } forced_sequence_header = tmp_seq << 4; break; case 'h' : print_help(invocation); return 0; break; case 'i' : want_info=true; break; case 'f' : confirm_files=false; break; case 's' : inparts=true; break; case 'j' : joined=true; if(!myoutfile) myoutfile="output.mpg"; break; case 'T' : Id3Tag=true; return ParseID3Command(argc,argv,i+1); break; case 'b' : inparts=true; if (joined) { fprintf(stderr,"-o and -b options are mutually exclusive\n"); return 1; } else { if (basename_specified) { fprintf(stderr,"only one basename is allowed\n"); return 1; } basename_specified=true; if(i+1>=argc){ fprintf(stderr,"-b option used without basename\n"); return 1; } else { //TODO copy basename i++; // fprintf(stderr,"option -b read, basename : %s\n",argv[i]); mybasename=new char [strlen(argv[i])+1]; strcpy(mybasename,argv[i]); } } break; case 'o' : if (basename_specified) { fprintf(stderr,"-b and -o options are mutually exclusive\n"); return 1; } else { if (opresent){ fprintf(stderr,"only one -o option allowed\n"); return 1; } opresent=true; if(i+1>=argc){ fprintf(stderr,"-o option used without outfile name\n"); return 1; } else { i++; // fprintf(stderr,"option -o read, will join all in file : %s\n",argv[i]); myoutfile=new char [strlen(argv[i])+1]; strcpy(myoutfile,argv[i]); } } break; case 'v': printf("%s Version %s\n",progname,MPGTX_VERSION); printf("Copyleft 2001 Laurent Alacoque \n"); printf("Partial work 2002 by Philipp Biermann \n"); printf("updates, bugs, patch, money : http://mpgtx.sourceforge.net\n"); return 0; break; case '-': // this is a long option (--xxxx) if (strcmp(argv[i],"--force")==0) force_option=true; if (strcmp(argv[i],"--no-parachute")==0) catch_sigsegv=false; break; default: fprintf(stderr,"unrecognized option %s\n",argv[i]); return 1; break; } } break; case ']': case '[': // this could be a range or a filename beginning with bracket // so check if it is not a file in current dir tmp_file=fopen(argv[i],"r"); if(tmp_file==NULL){ // this is a range lastarg=range; if(!Tab.ParseRange(argv[i])){ return 1; } } else{ // might be an input file, check! if (lastarg==file && !(want_info)){ // two file arguments encountered, apply 100% range to the first // one Tab.ParseRange("]0-]"); } if(tmp_file) fclose(tmp_file); lastarg=file; if(!Tab.AddFile(argv[i])){ fprintf(stderr,"%s is not a valid mpeg file\n",argv[i]); if(!want_info) return 1; } } break; case '{': // comma separated list of boundaries lastarg=range; if(!Tab.ParseBoundaries(argv[i])){ return 1; } break; default: // might be an input file, check! if (lastarg==file && !(want_info)){ // two file arguments encountered, apply 100% range to the first //one Tab.ParseRange("]0-]"); } lastarg=file; if(!Tab.AddFile(argv[i])) { fprintf(stderr,"%s is not a valid mpeg file\n",argv[i]); if(!want_info) return 1; } } }//for if (lastarg==file && (!want_info)){ //range's missing Tab.ParseRange("]0-]"); } /////////////////////////////////////////////////////////////////// // Args are parsed /////////////////////////////////////////////////////////////////// #ifndef NOSIGNAL_H if (!catch_sigsegv){ // if the --no-parachute was used, restore the default behaviour signal(SIGBUS,SIG_DFL); signal(SIGSEGV,SIG_DFL); signal(SIGFPE,SIG_DFL); signal(SIGINT,SIG_DFL); } #endif ChunkTab = Tab.GetChunks(&ChunkCount); #ifdef _DEBUG_ Tab.PrintTab(); #endif ATRACK("handling info mode"); ///////////////////////////////////////////////////////////////// // Handle Info mode ///////////////////////////////////////////////////////////////// if (want_info){ Tab.PrintInfos(); #ifdef NEED_END_MESSAGE endMessage(); #endif return 0; } ChunkTab=Tab.GetChunks(&ChunkCount); // from here we need at least one chunk if (ChunkCount==0){ fprintf(stderr,"nothing to do \n"); return 0; } RTRACK; ATRACK("demux mode"); ///////////////////////////////////////////////////////////////// // Handle demux mode ///////////////////////////////////////////////////////////////// if (demux){ if (ChunkCount!=1){ fprintf(stderr,"demux currently requires exactly one mpeg file\n"); return 1; } if (!basename_specified){ fprintf(stderr,"No base name specified, defaulting to basename : chunk\n"); mybasename="chunk"; } demuxer Dmux(ChunkTab[0]->mpegfile, mybasename,confirm_files); Dmux.Process(); //check the errors! #ifdef NEED_END_MESSAGE endMessage(); #endif return 0; } // from here we need at least one chunk if (ChunkCount==0){ fprintf(stderr,"nothing to do \n"); return 0; } // Tab.PrintTab(); RTRACK; ATRACK("join mode"); ///////////////////////////////////////////////////////////////// // All files are joined ///////////////////////////////////////////////////////////////// if (joined) { if (!opresent) { myoutfile = new char[500]; if (ChunkTab[0]->mpegfile->has_video() == false) sprintf(myoutfile, "chunk.mp3"); else sprintf(myoutfile, "chunk.mpg"); } if (ChunkCount >1) { for ( i = 1; i < ChunkCount ; i++) { if(!ChunkTab[0]->mpegfile->Match(ChunkTab[i]->mpegfile)) { if (force_option) fprintf(stderr, "Specified files don't seem to be compatible\n"); else { fprintf(stderr, "Specified files are not compatible, if you still want to join them use --force switch\n"); return 1; } } //okay they match } } if (!strcmp(myoutfile,"-")){ //output to stdout current_out=TheFac.NewMpegFrom(ChunkTab[0]->mpegfile,stdout); if (!current_out){ fprintf(stderr,"\nFatal : could not process file\n"); } } else { //output to myoutfile // if (!force_action) if (confirm_files && !mpgtx_access(myoutfile)) { fprintf(stderr,"file %s already exist. erase ? [n/y/a]:", myoutfile); fflush(stderr); answer=getchar(); while (getchar() != '\n'); //flush the leading \n if (answer!='y' && answer !='Y' && answer != 'a' && answer != 'A') { fprintf(stderr,"Aborted\n"); return 0; } if (answer == 'a' || answer == 'A') confirm_files = false; } current_out = TheFac.NewMpegFrom(ChunkTab[0]->mpegfile, myoutfile); if (!current_out) fprintf(stderr,"\nFatal : could not process file %s\n",myoutfile); } if (!current_out){ //something went wrong return 0; } for ( i=0;impegfile->FileName, i+1, ChunkCount); fflush(stderr); if (!current_out->WriteChunk(ChunkTab[i]->mpegfile, ChunkTab[i]->from, ChunkTab[i]->from_included, ChunkTab[i]->to, ChunkTab[i]->to_included)) fprintf (stderr," failed, range results in an empty chunk\n"); else fprintf(stderr,"\n"); } current_out->Finish(); #ifdef NEED_END_MESSAGE endMessage(); #endif return 0; } if (inparts) { RTRACK; ATRACK("split mode"); ///////////////////////////////////////////////////////////////// // each chunk in one file ///////////////////////////////////////////////////////////////// int myprec; char sChunkCount[50]; //write ChunkCount as a string sprintf(sChunkCount,"%d", ChunkCount + 1); //now we now how many digits we need -> in myprec myprec = strlen(sChunkCount); if (!basename_specified) { if (!opresent) //neither -o nor -b, use default basename mybasename="chunk"; else { if (ChunkCount >1){ fprintf(stderr,"-o option invalid for %d output files\n",ChunkCount); return 1; } mybasename = new char[strlen(myoutfile)+1]; strcpy(mybasename, myoutfile); } } myoutfile = new char[500]; for (i = 0;i < ChunkCount; i++) { //compute the name if (ChunkTab[i]->mpegfile->has_video()==false) { if (opresent) sprintf(myoutfile,"%s",mybasename); else { if (ChunkCount==1) sprintf(myoutfile,"%s.mp3",mybasename); else sprintf(myoutfile,"%s-%0*d.mp3",mybasename,myprec,i+1); } } else { if (opresent) sprintf(myoutfile,"%s",mybasename); else { if (ChunkCount==1) sprintf(myoutfile,"%s.mpg",mybasename); else sprintf(myoutfile,"%s-%0*d.mpg",mybasename,myprec,i+1); } } if (confirm_files && !mpgtx_access(myoutfile)) { fprintf(stderr,"file %s already exist. erase ?" " [n/y/a]:",myoutfile); fflush(stderr); answer=getchar(); while (getchar() != '\n'); //flush the leading \n if (answer != 'y' && answer != 'Y' && answer != 'a' && answer != 'A') { fprintf(stderr,"Aborted\n"); return 0; } if (answer == 'a' || answer == 'A') confirm_files = false; } current_out = TheFac.NewMpegFrom(ChunkTab[0]->mpegfile, myoutfile); if (!current_out) { fprintf(stderr,"\nFatal : could not process %s\n", myoutfile); return 1; } fprintf(stderr,"Now processing %s [%d/%d] ... ", ChunkTab[i]->mpegfile->FileName, i+1, ChunkCount); fflush(stderr); if (!current_out->WriteChunk(ChunkTab[i]->mpegfile, ChunkTab[i]->from, ChunkTab[i]->from_included, ChunkTab[i]->to, ChunkTab[i]->to_included)) fprintf (stderr," failed, range results in an empty chunk\n"); else fprintf(stderr,"\n"); current_out->Finish(); } // for delete [] myoutfile; #ifdef NEED_END_MESSAGE endMessage(); #endif return 0; } //no action were taken fprintf(stdout, "You must choose one options between -i -s -j\n"); fprintf(stdout, "Type %s -h for help\n", progname); return 0; } #ifdef NEED_END_MESSAGE void endMessage(void) { fprintf(stderr,"-------Done--------\n"); } #endif