#include "xfpp3d_main.h" #include "mpeg.h" char Mpeg_fname_default[] = "xfpp3d.mpg"; int Mpeg_state = MPEG_NORMAL; int Mpeg_newdata = 0; int Mpeg_seqno = 1; char *Mpeg_dir = (char *) NULL; char *Mpeg_fname = (char *) NULL; char Mpeg_buffer[1024]; /*========================================================================= Handles mpeg button. =========================================================================*/ void cb_button_mpeg(FL_OBJECT *the, long val) { if( fl_form_is_visible(Mpeg->mpeg) ) { fl_hide_form(Mpeg->mpeg); } else { fl_show_form(Mpeg->mpeg,FL_PLACE_HOTSPOT,FL_FULLBORDER,"xfpp3d mpeg"); fl_redraw_form(Mpeg->mpeg); widgets_set_mpeg(); } } /*========================================================================= Hides mpeg form. =========================================================================*/ int at_close_mpeg_form(FL_FORM *form, void *data) { fl_hide_form(form); return FL_IGNORE; } /*========================================================================= Handles MPEG generation =========================================================================*/ void cb_mpeg(FL_OBJECT *ob, long val) { static char R[] = "cb_mpeg"; int ltrace = Ltrace; char *dir, *fname, *fname_new; if( ltrace ) { fprintf(stderr,"%s: In routine val=%d\n",R,val); } switch( (int) val ) { case CB_MPEG_NAME: fname = (char *) fl_get_input(Mpeg->input_mpeg_name); fname_new = str2anu(fname); if( fname_new && strlen(fname_new) && (strspn(fname_new," ") != strlen(fname_new)) ) { if( is_mpeg_fname(fname_new) ) { if( Mpeg_fname ) free(Mpeg_fname); Mpeg_fname = fname_new; } else { fl_show_messages("File name should have '.mpg', '.MPG', '.mpeg' or '.MPEG' extension"); free(fname_new); } } break; case CB_MPEG_CWD: dir = (char *) fl_get_input(Mpeg->input_mpeg_cwd); if( is_dir(dir) ) { if( Mpeg_dir ) free(Mpeg_dir); Mpeg_dir = strdup(dir); } break; case CB_MPEG_CONTROL_OFF: Mpeg_state = MPEG_NORMAL; break; case CB_MPEG_CONTROL_RECORD: if( check_mpeg_size(Main->canvas) ) { Mpeg_state = MPEG_RECORD; } else { Mpeg_state = MPEG_NORMAL; } break; case CB_MPEG_CONTROL_GENERATE: Mpeg_state = MPEG_GENERATE; if( Mpeg_seqno > 1 ) { if( file_exists(Mpeg_fname) ) { sprintf(Mpeg_buffer,"File '%s' already exists. Overwrite?",Mpeg_fname); if( !fl_show_question(Mpeg_buffer,1) ) { Mpeg_state = MPEG_NORMAL; } } if( Mpeg_state == MPEG_GENERATE ) { jpeg_seq_to_mpeg(Mpeg_dir,Mpeg_fname,1,Mpeg_seqno-1); } } else { fl_show_messages("No .jpg images have been recorded yet."); } Mpeg_state = MPEG_NORMAL; Mpeg_seqno = 1; break; case CB_MPEG_CONTROL_PLAYBACK: if( file_exists(Mpeg_fname) ) { sprintf(Mpeg_buffer,"mpeg_play -dither color %s &",Mpeg_fname); system(Mpeg_buffer); } else { sprintf(Mpeg_buffer,"File '%s' does not exist.",Mpeg_fname); fl_show_messages(Mpeg_buffer); } Mpeg_state = MPEG_NORMAL; break; default: fprintf(stderr,"%s: Unexpected case val=%d\n",R,val); break; } widgets_set_mpeg(); if( ltrace ) { fprintf(stderr,"%s: Mpeg_state=%d\n",R,Mpeg_state); } } /*========================================================================= Checks that GL window width is mutiple of 16 pixels. =========================================================================*/ int check_mpeg_size(FL_OBJECT *gl) { int vp[4]; char buf[256]; fl_activate_glcanvas(gl); glGetIntegerv(GL_VIEWPORT,vp); if( !(vp[2] % 16) ) { return 1; } else { sprintf(buf,"Canvas width=%d is not a mulitple of 16\n",vp[2]); fl_show_messages(buf); return 0; } } /*========================================================================= Callback for mpeg browser window ... currently not used. =========================================================================*/ void cb_browser_mpeg(FL_OBJECT *ob, long val) { fprintf(stderr,"cb_browser_mpeg: In routine val=%d\n",val); } /*========================================================================= Sets widgets in mpeg window. =========================================================================*/ void widgets_set_mpeg(void) { int index_max; int index_curr; int ltrace = 1; if( !fl_form_is_visible(Mpeg->mpeg) ) return; if( !Mpeg_dir ) { Mpeg_dir = getcwd(NULL, 0); } if( !Mpeg_fname ) { Mpeg_fname = strdup(Mpeg_fname_default); } fl_set_input(Mpeg->input_mpeg_cwd,Mpeg_dir); fl_set_input(Mpeg->input_mpeg_name,Mpeg_fname); sprintf(Mpeg_buffer,"%04d",Mpeg_seqno); fl_set_input(Mpeg->input_mpeg_seqno,Mpeg_buffer); fl_set_button(Mpeg->button_mpeg_control_off,Mpeg_state == MPEG_NORMAL); fl_set_button(Mpeg->button_mpeg_control_record,Mpeg_state == MPEG_RECORD); fl_set_button(Mpeg->button_mpeg_control_generate,Mpeg_state == MPEG_GENERATE); update_browser_mpeg(); } /*========================================================================= Saves GL window as jpg file. Slightly modifed version of 'scr_save' from 'DV' (F. Pretorius). =========================================================================*/ void gl_to_jpeg(FL_OBJECT *gl,char *dir,char *name,int seqno) { static char R[] = "gl_to_jpeg"; int vp[4],image_size,width,height,row; char *image; struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; JSAMPROW rp; FILE *file=0; int jpeg_qf = 100; int ltrace = 1; fl_activate_glcanvas(gl); sprintf(Mpeg_buffer,"%s/%s_%04d.jpg",dir,name,seqno); /* Obtain the image dimension, [vp]=[x,y,width,height] */ glGetIntegerv(GL_VIEWPORT,vp); if( ltrace ) { fprintf(stderr,"%s: Saving %dx%d to '%s'\n",R,vp[2],vp[3],Mpeg_buffer); } width=vp[2]; height=vp[3]; image_size=sizeof(char)*3*width*height; if (!(image=(char *)malloc(image_size))) { fprintf(stderr,"%s: Out of memory ... can't save the image\n",R); return; } glPixelStorei(GL_PACK_ALIGNMENT,1); glReadPixels(0,0,width,height,GL_RGB,GL_UNSIGNED_BYTE,image); /* Following 'libjpeg.doc' to use the jpeg library: */ cinfo.err=jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); if (!(file=fopen(Mpeg_buffer,"w"))) { fprintf(stderr,"%s: cannot open file %s for writing\n",R,Mpeg_buffer); goto cleanup; } jpeg_stdio_dest(&cinfo,file); cinfo.image_width=width; cinfo.image_height=height; cinfo.input_components=3; cinfo.in_color_space=JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo,jpeg_qf,0); jpeg_start_compress(&cinfo, TRUE); /* The jpeg library wants the image from top-to-bottom, */ /* glReadPixels() returns bottom-to-top, so invert below: */ for(row=height-1; row>=0; row--) { rp=(JSAMPROW)(&image[row*width*3]); jpeg_write_scanlines(&cinfo,&rp,1); } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); cleanup: if (file) fclose(file); free(image); return; } /*========================================================================= Converts jpg sequence to mpeg file. =========================================================================*/ void jpeg_seq_to_mpeg(char *dir,char *name,int seqmin, int seqmax) { static char R[] = "jpeg_seq_to_mpeg"; static char Param[] = ".mpeg_encode.param"; FILE *fparam; int i; int ltrace = 1; int remove_param_file = 1; int remove_jpeg_files = 1; if( ltrace ) { fprintf(stderr,"%s: dir='%s'\n",R,dir); fprintf(stderr,"%s: name='%s'\n",R,name); fprintf(stderr,"%s: seqmin=%d\n",R,seqmin); fprintf(stderr,"%s: seqmax=%d\n",R,seqmax); } if( chdir(dir) ) { sprintf(Mpeg_buffer,"Could not cd to '%s'",dir); fl_show_messages(Mpeg_buffer); return; } if( ltrace ) { fprintf(stderr,"%s: Output to '%s'\n",R,Mpeg_fname); } if( !(fparam = fopen(Param,"w")) ) { sprintf(Mpeg_buffer,"Could not open '%s' for write",Param); fl_show_messages(Mpeg_buffer); } fprintf(fparam,"PATTERN IBBPBBPBBP\n"); fprintf(fparam,"OUTPUT %s\n",Mpeg_fname); fprintf(fparam,"BASE_FILE_FORMAT JPEG\n"); fprintf(fparam,"INPUT_CONVERT *\n"); fprintf(fparam,"GOP_SIZE 16\n"); fprintf(fparam,"SLICES_PER_FRAME 1\n"); fprintf(fparam,"INPUT_DIR %s\n",dir); fprintf(fparam,"INPUT\n"); fprintf(fparam,"%s_*.jpg [%04d-%04d]\n",name,seqmin,seqmax); fprintf(fparam,"END_INPUT\n"); fprintf(fparam,"PIXEL HALF\n"); fprintf(fparam,"RANGE 10\n"); fprintf(fparam,"PSEARCH_ALG LOGARITHMIC\n"); fprintf(fparam,"BSEARCH_ALG CROSS2\n"); fprintf(fparam,"IQSCALE 1\n"); fprintf(fparam,"PQSCALE 1\n"); fprintf(fparam,"BQSCALE 1\n"); fprintf(fparam,"REFERENCE_FRAME ORIGINAL\n"); fprintf(fparam,"FRAME_RATE 30\n"); fprintf(fparam,"FORCE_ENCODE_LAST_FRAME\n"); fclose(fparam); if( ltrace ) { sprintf(Mpeg_buffer,"cat %s",Param); system(Mpeg_buffer); } sprintf(Mpeg_buffer,"mpeg_encode %s",Param); if( ltrace ) { fprintf(stderr,"%s: Execuring '%s'\n",R,Mpeg_buffer); } system(Mpeg_buffer); if( remove_param_file ) sprintf(Mpeg_buffer,"/bin/rm %s\n",Param); system(Mpeg_buffer); if( remove_jpeg_files ) { for( i = seqmin; i <= seqmax; i++ ) { sprintf(Mpeg_buffer,"/bin/rm %s_%04d.jpg\n",name,i); system(Mpeg_buffer); } } } /*========================================================================= Updates mpeg browser window. Memory leakage here; haven't figured out how to free storage allocated by scandir. =========================================================================*/ void update_browser_mpeg(void) { struct dirent **fnamelist = (struct dirent **) NULL; int i, nfname; fl_freeze_form(Mpeg->browser_mpeg->form); fl_clear_browser(Mpeg->browser_mpeg); if( !Mpeg_dir ) goto Return; if( !is_dir(Mpeg_dir) ) goto Return; nfname = scandir(Mpeg_dir,&fnamelist,is_mpeg_dirent,alphasort); if( nfname ) { for( i = 0; i < nfname; i++ ) { if( Mpeg_fname && !strcmp(Mpeg_fname,fnamelist[i]->d_name) ) { sprintf(Mpeg_buffer,"@C1%s",fnamelist[i]->d_name); } else { sprintf(Mpeg_buffer,"%s",fnamelist[i]->d_name); } fl_add_browser_line(Mpeg->browser_mpeg,Mpeg_buffer); } } Return: fl_unfreeze_form(Mpeg->browser_mpeg->form); } /*========================================================================= Checks that file has an extension common for mpeg files. =========================================================================*/ int is_mpeg_dirent(struct dirent *d) { return is_mpeg_fname(d->d_name); } int is_mpeg_fname(char *fname) { return has_ext(fname,".mpg") || has_ext(fname,".MPG") || has_ext(fname,".mpeg") || has_ext(fname,".MPEG") ; } /*========================================================================= Returns index of substring s2 in s1, -1 if not found. =========================================================================*/ int istrstr(char *s1, char *s2) { char *c; int ls1, ls2, i1 = 0, newi1, j; if( (ls1 = strlen(s1)) && (ls2 = strlen(s2)) ) { while( i1 < ls1 ) { if( c = strchr(s1+i1,s2[0]) ) { newi1 = i1 + (int) (c - s1+i1); if( (ls1 - newi1) < ls2 ) { return -1; } else { for( j = 0; j < ls2 && s1[newi1++] == s2[j++];); if( j == ls2 ) { return newi1 - ls2; } else { i1 = newi1 + 1; } } } else { return -1; } } return -1; } else { return -1; } } /*========================================================================= Returns 1 if fname (file name presumably) has extension ext. =========================================================================*/ int has_ext(char *fname, char *ext) { int offset; offset = strlen(fname) - strlen(ext); return (offset >= 0) && (istrstr(fname,ext) == offset); } /*========================================================================= Strips special (non alpha-numeric-underscore,.,-) characters and returns new string. =========================================================================*/ char *str2anu(char *str_in) { char valid[] = ".-_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUWXYZ0123456789"; char *str_out = (char *) NULL; int pos, out_pos, len_str_out = 0; if( str_in && strlen(str_in) ) { for( pos = 0; pos < strlen(str_in); pos++ ) { if( strchr(valid,str_in[pos]) ) ++len_str_out; } if( len_str_out ) { str_out = (char *) malloc((len_str_out + 1) * sizeof(char)); str_out[len_str_out] = '\0'; for( pos = 0, out_pos = 0; pos < strlen(str_in); pos++ ) { if( strchr(valid,str_in[pos]) ) { str_out[out_pos] = str_in[pos]; out_pos++; } } } } return str_out; } /*========================================================================= Returns 1 if fname is a directory. =========================================================================*/ int is_dir(char *fname) { struct stat info; stat(fname,&info); return S_ISDIR(info.st_mode); } /*========================================================================= Checks for file existence. =========================================================================*/ int file_exists(char *fname) { FILE *fp; if( (fp = fopen(fname,"r")) != NULL ) { fclose(fp); return 1; } else { return 0; } }