// Please see the README file or the LEGAL NOTES-section in the manual before // modifying, compiling or using 'xmrm' or 'xmrm_mpeg' // Idea: Manfred Kopp // Programming: Gerhard Waldhör, Andreas Artmann #include #include #include #include #include "xmrm_mpeg.h" #include "const.h" #define MAX_PIC_NUM 999 #define BORDER_WIDTH -1 FD_CREATE_MPEG *fd_CREATE_MPEG; // The form the user will see int got_both_frames = 0; //---------------------------------------------------- // two instances are derived in main(), one as backup // because many strings are changed during conversion // and if the user wants a second run, the old becomes // the new, and so forth // maybe not pretty, but easy to do :-) //---------------------------------------------------- class names_n_numbers { public: names_n_numbers(); char *START_Frame; // complete filename of first frame char START_dir_only [BUFSIZ]; char *START_fname_only; char *END_Frame; // complete filename of last frame char *filename_ppm, *filename_yuv; char *extension; // points to '.###.tif' - used to modify '###' char number_str[6]; // holds '.###.' char even[6]; // ".even" if files have to be even-sized, ""(empty) otherwise int abs_count; // absolute count of valid pictures int first_width, first_height; int start_number; // number of first valid TIFF-file int end_number; // number of last TIFF-file } *work_class, *backup_class; //------------------------------ // constructor //------------------------------ names_n_numbers::names_n_numbers() { START_Frame = (char*)NULL; START_fname_only = (char*)NULL; END_Frame = (char*)NULL; filename_ppm = filename_yuv = extension = (char*)NULL; abs_count = first_width = first_height = start_number = 0; end_number = MAX_PIC_NUM; } //-------------------------------------------------------------- // Extract number ### from filename basename.###.tif(f) // returns: extracted number (MAX_PIC_NUM+1 if invalid filename) //-------------------------------------------------------------- int Extract_Number(char **ext, char *fname_only) { int count = 0; strcpy(backup_class->number_str, ".000."); while ( !(*ext = strstr(fname_only,backup_class->number_str)) && (count <= MAX_PIC_NUM) ) { count++; backup_class->number_str[1] = '0' + count/100; backup_class->number_str[2] = '0' + (count%100)/10; backup_class->number_str[3] = '0' + (count%10); } return count; } //---------------------------------------------------------------- // Ask user for starting TIFF-File if necessary and extract number // returns: 0: success // 1: error // 2: cancel //---------------------------------------------------------------- int Get_START_Frame() { const char *directory; // so that START end END-frame isn´t the same !!! fl_use_fselector(0); if ( !backup_class->START_Frame ) { fl_addto_browser(fd_CREATE_MPEG->BR_Log,"Getting the START-Frame..."); XFlush(fl_get_display()); if ( backup_class->START_Frame = (char *)fl_show_fselector("Select START-Frame...","","*.tif*",".tif") ) { directory = (char *)fl_get_directory(); (void) strcpy(backup_class->START_dir_only, directory); } else return 2; } else { (void) strcpy(backup_class->START_dir_only, backup_class->START_Frame); if ( !(backup_class->START_fname_only = strrchr(backup_class->START_dir_only,'/')) ) { fl_show_alert("ERROR:","Wrong parameter !","Consult the man-page or the manual !",1); return 1; } *backup_class->START_fname_only = '\0'; } do { if ( !(backup_class->START_fname_only = strrchr(backup_class->START_Frame,'/')) ) { fl_show_alert("ERROR:","Wrong parameter !","Consult the man-page or the manual !",1); return 1; } backup_class->START_fname_only++; backup_class->start_number = Extract_Number(&backup_class->extension, backup_class->START_fname_only); if( !backup_class->extension ) { fl_show_alert("ERROR:","Invalid Filename !","Filename example: basename.001.tif(f)",1); if ( backup_class->START_Frame = (char *)fl_show_fselector("Select START-Frame...","","*.tif*",".tif") ) { directory = (char *)fl_get_directory(); (void) strcpy(backup_class->START_dir_only, directory); } else return 1; } } while ( !backup_class->extension ); backup_class->filename_ppm = (char *)malloc( strlen(backup_class->START_fname_only) ); backup_class->filename_yuv = (char *)malloc( strlen(backup_class->START_fname_only) ); if ( (!backup_class->filename_ppm) || (!backup_class->filename_yuv) ) { fl_show_alert("ERROR:","Memory Allocation failed !","Aborting...",1); return 1; } return 0; } //----------------------------------------------- // Ask user for STOP-TIFF-File and extract number // returns: 0: success // 1: user abort //----------------------------------------------- int Get_END_Frame() { const char *directory; char *filename, *ext; int some_error; char filter[BUFSIZ]; // filter for fselector() // so that START end END-frame isn´t the same !!! fl_use_fselector(1); directory = backup_class->START_dir_only; (void) strcpy(filter, backup_class->START_fname_only); if ( !(ext = strstr(filter,backup_class->number_str)) ) { fl_show_alert("ERROR:","UNEXPECTED ERROR !!!","Bailing out...",1); return 1; } *ext = '\0'; (void )strcat(filter,"*"); if ( !backup_class->END_Frame ) { fl_addto_browser(fd_CREATE_MPEG->BR_Log,"Getting the STOP-Frame..."); XFlush(fl_get_display()); if ( backup_class->END_Frame = (char *)fl_show_fselector("Select STOP-Frame...",\ backup_class->START_dir_only,filter,".tif") ) directory = fl_get_directory(); else return 1; } do { some_error = FALSE; filename = strrchr(backup_class->END_Frame,'/'); filename++; if ( strcmp(directory, backup_class->START_dir_only) ) { fl_show_alert("ERROR:","START and STOP-Frames must be in SAME directory !","",1); some_error = TRUE; } else { backup_class->end_number = Extract_Number(&ext, filename); if ( backup_class->start_number >= backup_class->end_number ) { fl_show_alert("ERROR:","STOP-Frame number must be HIGHER","than START-Frame number !",1); some_error = TRUE; } else if( !ext ) { fl_show_alert("ERROR:","Invalid Filename !","Filename example: basename.001.tif(f)",1); some_error = TRUE; } } if ( some_error ) if ( backup_class->END_Frame = (char *)fl_show_fselector("Select STOP-Frame...",\ backup_class->START_dir_only,filter,".tif") ) directory = fl_get_directory(); else return 1; } while( some_error ); return 0; } //---------------------------------------------------- // make size of picture even or mpeg-encoder won't run // returns: 0: success // 1: failure //---------------------------------------------------- int Even_Size(TIFF *tif, int width, int height) { int even_width, even_height; char Logbuf[BUFSIZ]; char *Save_Name; size_t npixels; uint32 *raster; sprintf(Logbuf,"@bMaking %s even-sized -",work_class->START_Frame); fl_addto_browser(fd_CREATE_MPEG->BR_Log,Logbuf); XFlush(fl_get_display()); fl_addto_browser(fd_CREATE_MPEG->BR_Log," Creating temporary file..."); XFlush(fl_get_display()); npixels = (uint32) (width * height); if ( raster = (uint32*) _TIFFmalloc(npixels * sizeof (uint32)) ) { if ( TIFFReadRGBAImage(tif, width, height, raster, 0) ) { TIFFClose(tif); uint32 *raster_clone; even_width = width + (width % 2); even_height = height + (height % 2); npixels = (uint32) (even_width * even_height); raster_clone = (uint32*) _TIFFmalloc(npixels * sizeof (uint32)); if ( raster_clone == NULL ) { fl_show_alert("ERROR:","Memory Allocation failed !","Aborting...",1); _TIFFfree(raster); return 1; } // copy old raster into new raster and // make it even by cloning the last row and/or column for (int h=0; h < height; h++) { if (width % 2) raster_clone[(even_height-h)*even_width-1] = raster[(h+1)*width-1]; for (int w=0; w < width; w++) { raster_clone[(even_height-1-h)*even_width+w] = raster[h*width+w]; } } if (height % 2) for (int i = 0; i < even_width; i++) raster_clone[i] = raster_clone[i+even_width]; _TIFFfree(raster); Save_Name = (char *)malloc(strlen(work_class->START_Frame)+6); if ( Save_Name == NULL ) { fl_show_alert("ERROR:","Memory Allocation failed !","Aborting...",1); _TIFFfree(raster_clone); return 1; } strcpy(Save_Name,work_class->START_Frame); strcat(Save_Name,work_class->even); // Check if file exists already tif = TIFFOpen(Save_Name,"r"); if ( tif ) { fl_show_alert("ERROR: temporary file exists:",Save_Name,"Aborting...",1); _TIFFfree(raster_clone); TIFFClose(tif); free(Save_Name); return 1; } // Opening TIFF-File: tif = TIFFOpen(Save_Name,"w"); if ( tif == NULL ) { fl_show_alert("ERROR:","Unable to open TIFF File !","Aborting...",1); _TIFFfree(raster_clone); free(Save_Name); return 1; } // Setting TIFF-Image informations TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,even_width); TIFFSetField(tif,TIFFTAG_IMAGELENGTH,even_height); TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,8); TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,4); TIFFSetField(tif,TIFFTAG_COMPRESSION,COMPRESSION_DEFLATE); TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG); TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,PHOTOMETRIC_RGB); TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT); TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_INCH); TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,even_height); TIFFSetField(tif,TIFFTAG_XRESOLUTION,1200.0); TIFFSetField(tif,TIFFTAG_YRESOLUTION,1200.0); if ( TIFFWriteEncodedStrip(tif, 0, (char *)raster_clone, even_width * even_height * sizeof(uint32)) == -1 ) { fl_show_alert("ERROR:","Unable to save TIFF File !","Aborting...",1); _TIFFfree(raster_clone); TIFFClose(tif); free(Save_Name); return 1; } _TIFFfree(raster_clone); TIFFClose(tif); free(Save_Name); } else { fl_show_alert("ERROR:","Unable to read TIFF-File !","Aborting...",1); _TIFFfree(raster); TIFFClose(tif); return 1; } } else { fl_show_alert("ERROR:","Memory Allocation failed !","Aborting...",1); TIFFClose(tif); return 1; } return 0; } //------------------------------------- // convert TIFF to PPM to Y-,U-,V-Files // returns: 0: success // 1: failure //------------------------------------- int Make_YUV_File(int increase) { char Logbuf[BUFSIZ]; char cmdbuf[BUFSIZ]; char TIFF_Name[BUFSIZ]; int tif_w, tif_h, test; FILE *fp; TIFF *tif; strcpy(TIFF_Name,work_class->START_Frame); strcat(TIFF_Name,work_class->even); // Look if file exists (if stepsize is larger than 1) if ( fp=fopen(increase ? work_class->START_Frame:TIFF_Name,"r") ) { fclose(fp); //test if its a valid TIFF-File if ( tif=TIFFOpen(increase ? work_class->START_Frame:TIFF_Name,"r") ) { // Getting TIFF-size if ( TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&tif_w) != 1 || TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&tif_h) != 1 ) { fl_show_alert("ERROR:","Unable to get image-size !","Aborting...",1); TIFFClose(tif); return 1; } if ( !work_class->first_width ) work_class->first_width = tif_w; if ( !work_class->first_height ) work_class->first_height = tif_h; // Check for unexpected change of size if ( (tif_w != work_class->first_width) || (tif_h != work_class->first_height) ) { fl_show_alert("ERROR:","Unexpected change of image-size !","Aborting...",1); TIFFClose(tif); return 1; } // Check for even picture size if ( (tif_w % 2) || (tif_h % 2) ) { strcpy(work_class->even,".even"); if ( Even_Size( tif, tif_w, tif_h) ) return 1; } else TIFFClose(tif); // Make SAVENAME for PPM (void) strcpy(work_class->filename_ppm,work_class->START_fname_only); work_class->extension = strstr(work_class->filename_ppm,".tif"); *(++work_class->extension) = 'p'; *(++work_class->extension) = 'p'; *(++work_class->extension) = 'm'; *(++work_class->extension) = '\0'; // Execute tifftopnm sprintf(cmdbuf, "tifftopnm %s/%s%s > %s/%s",work_class->START_dir_only,work_class->START_fname_only,\ work_class->even,work_class->START_dir_only,work_class->filename_ppm); sprintf(Logbuf,"Converting %s%s to ppm...",work_class->START_fname_only,work_class->even); fl_addto_browser(fd_CREATE_MPEG->BR_Log,Logbuf); XFlush(fl_get_display()); test = system(cmdbuf); if(test==-1 || test==127) { fl_show_alert("ERROR:","Execution of 'tifftopnm' failed !","Aborting...",1); return 1; } // MAKE SAVENAME for YUV (void) strcpy(work_class->filename_yuv,work_class->START_fname_only); work_class->extension = strstr(work_class->filename_yuv,work_class->number_str); if(work_class->abs_count/100 != 0) { *work_class->extension = '0' + work_class->abs_count/100; *(++work_class->extension) = '0' + (work_class->abs_count%100)/10; *(++work_class->extension) = '0' + (work_class->abs_count%10); *(++work_class->extension) = '\0'; } else if((work_class->abs_count%100)/10 != 0) { *work_class->extension = '0' + (work_class->abs_count%100)/10; *(++work_class->extension) = '0' + (work_class->abs_count%10); *(++work_class->extension) = '\0'; } else { *work_class->extension = '0' + (work_class->abs_count%10); *(++work_class->extension) = '\0'; } // Execute ppmtoyuvsplit sprintf(cmdbuf, "ppmtoyuvsplit %s/%s %s/%s",work_class->START_dir_only,work_class->filename_yuv,\ work_class->START_dir_only,work_class->filename_ppm); sprintf(Logbuf,"Splitting %s into Y-,U- and V-Files...",work_class->filename_ppm); fl_addto_browser(fd_CREATE_MPEG->BR_Log,Logbuf); XFlush(fl_get_display()); test = system(cmdbuf); if(test==-1 || test==127) { fl_show_alert("ERROR","Execution of 'ppmtoyuvsplit' failed !","Aborting...",1); return 1; } ++work_class->abs_count; } else //not a valid TIFF-File... { fl_show_alert("ERROR:","Invalid TIFF-File encountered !","Aborting...",1); return 1; } } return 0; } //--------------------------------------------- // Call 'Make_YUV_File' with increasing numbers // returns: 0: success // 1: failure //--------------------------------------------- int Make_YUV_Files_INC() { work_class->abs_count=0; *work_class->even = '\0'; work_class->number_str[1] = '0' + work_class->start_number/100; work_class->number_str[2] = '0' + (work_class->start_number%100)/10; work_class->number_str[3] = '0' + (work_class->start_number%10); //try all possible numbers for( int i = work_class->start_number; i <= work_class->end_number; i++ ) { work_class->extension = strstr(work_class->START_fname_only,work_class->number_str); *(++work_class->extension) = '0' + i/100; *(++work_class->extension) = '0' + (i%100)/10; *(++work_class->extension) = '0' + (i%10); work_class->number_str[1] = '0' + i/100; work_class->number_str[2] = '0' + (i%100)/10; work_class->number_str[3] = '0' + (i%10); if ( Make_YUV_File(TRUE) ) return 1; } return 0; } //--------------------------------------------- // Call 'Make_YUV_File' with decreasing numbers // returns: 0: success // 1: failure //--------------------------------------------- int Make_YUV_Files_DEC() { for( int i = work_class->end_number; i >= work_class->start_number; i-- ) { work_class->extension = strstr(work_class->START_fname_only,work_class->number_str); *(++work_class->extension) = '0' + i/100; *(++work_class->extension) = '0' + (i%100)/10; *(++work_class->extension) = '0' + (i%10); work_class->number_str[1] = '0' + i/100; work_class->number_str[2] = '0' + (i%100)/10; work_class->number_str[3] = '0' + (i%10); if ( Make_YUV_File(FALSE) ) return 1; } return 0; } //----------------------------------------------------------------- // run the MPEG encoder 'mpeg': converts Y-,U-,V-Files into an MPEG // // returns: return value of system call // //----------------------------------------------------------------- int MPEG_Encoder() { int test; char cmdbuf[BUFSIZ]; // MAKE SAVENAME for MPEG work_class->extension = strstr(work_class->START_fname_only,work_class->number_str); *work_class->extension = '\0'; fl_addto_browser(fd_CREATE_MPEG->BR_Log,"@bMPEG Encoder Running - Please be patient !"); XFlush(fl_get_display()); // Execute mpeg-encoder sprintf(cmdbuf, "mpeg -PF -a %i -b %i -h %i -v %i %s/%s", 0, work_class->abs_count-1,work_class->first_width,\ work_class->first_height,work_class->START_dir_only, work_class->START_fname_only); test = system(cmdbuf); if (test == -1 || test == 127) { sprintf(cmdbuf,"rm -f %s/%s.mpg",work_class->START_dir_only,work_class->START_fname_only); (void) system(cmdbuf); } return test; } //--------- // Clean up //--------- void Remove_Garbage() { char cmdbuf[BUFSIZ]; // Get filename for removal if ( work_class->extension = strstr(work_class->START_fname_only,work_class->number_str) ) *work_class->extension = '\0'; if ( strcmp(work_class->even,".even")==0 ) { fl_addto_browser(fd_CREATE_MPEG->BR_Log,"@iRemoving temporary .even-Files..."); XFlush(fl_get_display()); sprintf(cmdbuf,"rm -f %s/%s.*.even",work_class->START_dir_only,work_class->START_fname_only); (void) system(cmdbuf); } fl_addto_browser(fd_CREATE_MPEG->BR_Log,"@iRemoving temporary ppm-Files..."); XFlush(fl_get_display()); sprintf(cmdbuf,"rm -f %s/%s.*.ppm",work_class->START_dir_only,work_class->START_fname_only); (void) system(cmdbuf); fl_addto_browser(fd_CREATE_MPEG->BR_Log,"@iRemoving temporary Y-,U- and V-Files..."); XFlush(fl_get_display()); sprintf(cmdbuf,"rm -f %s/%s*.Y",work_class->START_dir_only,work_class->START_fname_only); (void) system(cmdbuf); sprintf(cmdbuf,"rm -f %s/%s*.U",work_class->START_dir_only,work_class->START_fname_only); (void) system(cmdbuf); sprintf(cmdbuf,"rm -f %s/%s*.V",work_class->START_dir_only,work_class->START_fname_only); (void) system(cmdbuf); } //----------------------------------------------- // callback for ALL BUTTONS/CHECKBUTTONS //----------------------------------------------- void callback_Button (FL_OBJECT * ob,long arg) { char cmdbuf[BUFSIZ]; int test; switch (arg) { case 1: // GO Button // (void) fl_show_command_log(FL_FULLBORDER); // copy backup class Get_START_Frame(); Get_END_Frame(); delete work_class; work_class = backup_class; backup_class = new names_n_numbers; backup_class->START_Frame = (char *)malloc( strlen(work_class->START_Frame) ); backup_class->END_Frame = (char *)malloc( strlen(work_class->END_Frame) ); strcpy(backup_class->START_Frame, work_class->START_Frame); strcpy(backup_class->END_Frame, work_class->END_Frame); fl_set_cursor(fd_CREATE_MPEG->CREATE_MPEG->window, XC_watch); // Do some file-conversion // prevent mpeg-encoding when error occured while creating PPM or YUV-files if ( Make_YUV_Files_INC() ) { Remove_Garbage(); fl_call_object_callback(fd_CREATE_MPEG->BT_Exit); return; } // correct size to even numbers work_class->first_width += (work_class->first_width % 2); work_class->first_height += (work_class->first_height % 2); // If 'Cycle' chosen do the same backwards if ( fl_get_button(fd_CREATE_MPEG->CB_Create_Cycle) ) // prevent mpeg-encoding when error occured while creating PPM or YUV-files if ( Make_YUV_Files_DEC() ) { Remove_Garbage(); fl_call_object_callback(fd_CREATE_MPEG->BT_Exit); return; } // Call the MPEG-Encoder test = MPEG_Encoder(); if (test==-1 || test==127) { fl_show_alert("ERROR:","Execution of MPEG-Encoder 'mpeg' failed !","Aborting...",0); Remove_Garbage(); fl_call_object_callback(fd_CREATE_MPEG->BT_Exit); return; } sprintf(cmdbuf,"@bMPEG-Encoder 'mpeg' exit code: %i !",test); fl_addto_browser(fd_CREATE_MPEG->BR_Log, cmdbuf); XFlush(fl_get_display()); Remove_Garbage(); fl_set_browser_hscrollbar(fd_CREATE_MPEG->BR_Log,FL_ON); fl_set_browser_vscrollbar(fd_CREATE_MPEG->BR_Log,FL_ON); XFlush(fl_get_display()); fl_reset_cursor(fd_CREATE_MPEG->CREATE_MPEG->window); fl_show_alert("INFORMATION:","Conversion complete !",\ fl_get_button(fd_CREATE_MPEG->CB_mpeg_play) ? "Running 'mpeg_play'...":"",0); // Run MPEG-Player ? if ( fl_get_button(fd_CREATE_MPEG->CB_mpeg_play) && !test ) { XFlush(fl_get_display()); sprintf(cmdbuf,"mpeg_play -quiet -loop -controls on -dither color -framerate %i %s/%s%s &",\ 15,work_class->START_dir_only,work_class->START_fname_only,".mpg"); if ( system(cmdbuf) ) { fl_show_alert("ERROR:","Execution of 'mpeg-play' failed !","Aborting...",1); fl_call_object_callback(fd_CREATE_MPEG->BT_Exit); } } break; case 2: // Choose Start/Stop Frame backup_class->START_Frame = (char*)NULL; backup_class->END_Frame = (char*)NULL; fl_hide_object(fd_CREATE_MPEG->BT_Go); // Ask user for start file if( Get_START_Frame() ) break; else // Ask user for stop file if ( Get_END_Frame() ) break; fl_show_object(fd_CREATE_MPEG->BT_Go); break; case 3: // Cycle Checkbutton break; case 4: // mpeg_play Ckeckbutton break; case 5: // EXIT // fl_hide_command_log(); fl_hide_form(fd_CREATE_MPEG->CREATE_MPEG); exit(0); break; default: ; } } //----------------------------------------------- // returns: 0: success // 1: failure //----------------------------------------------- int main(int argc, char *argv[]) { int c; // allocate classes work_class = new names_n_numbers; backup_class = new names_n_numbers; // initialize... fl_initialize(&argc, argv, 0, 0, 0); // Set global borderwidth fl_set_border_width(BORDER_WIDTH); fl_set_goodies_font(FL_BOLD_STYLE,FL_NORMAL_SIZE); // Create the form so we can set checkbuttons fd_CREATE_MPEG = create_form_CREATE_MPEG(); opterr = 0; // suppress Error-messages from getopt while( (c=getopt(argc, argv, "h")) != -1) switch (c) { case 'h': fprintf(stderr,"\nusage: %s: [-h] [xforms]\n\n",argv[0]); fprintf(stderr," -h: this information\n"); fprintf(stderr," xforms: These options will be passed to fl_initialize() of the XForms library.\n"); fprintf(stderr," You shouldn't need these options.\n\n"); return 0; case '?': break; } // redirect TIFF-Error- and Warnig-Handler (TIFFErrorHandler) TIFFSetErrorHandler((TIFFErrorHandler)NULL); (TIFFErrorHandler) TIFFSetWarningHandler((TIFFErrorHandler)NULL); // show the first form fl_hide_object(fd_CREATE_MPEG->BT_Go); fl_set_form_position(fd_CREATE_MPEG->CREATE_MPEG,-fd_CREATE_MPEG->CREATE_MPEG->w-15,0); fl_show_form(fd_CREATE_MPEG->CREATE_MPEG,FL_PLACE_GEOMETRY,FL_FULLBORDER,"XMRM_MPEG V1.0"); XFlush( fl_get_display() ); fl_set_browser_fontsize(fd_CREATE_MPEG->BR_Log,FL_NORMAL_SIZE); fl_set_browser_hscrollbar(fd_CREATE_MPEG->BR_Log,FL_OFF); fl_set_browser_vscrollbar(fd_CREATE_MPEG->BR_Log,FL_OFF); fl_do_forms(); }