/* * vidsav_dlg.c * * Code for the video save control dialog * * (C) 1997 Randall Hopper * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* ******************** Include Files ************** */ #include #include #include #include #include #include #include #include #include #include #include #include "voxware.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tvdefines.h" #include "glob.h" #include "actions.h" #include "xutil.h" #include "tvutil.h" #include "rawvideo.h" #include "audsav_dlg.h" #include "imgsav_dlg.h" #include "vidsav_dlg.h" #include "app_rsrc.h" /* ******************** Local defines ************** */ #define AV_RAWNAME_FMT "%s.AVraw" #define SCRIPT_FNAME_FMT "%s%s.sh" #define OPTIMIZE_NUM_FRAMES 200 typedef struct { char fname_base[ MAXPATHLEN ]; /* prefix of gen'ed files */ TV_BOOL audio_enabled; /* audio was captured */ TV_BOOL target_mpeg; /* T = MPEG target; F = imgs */ TV_BOOL streaming; /* T = stream to mpeg_encode */ TV_SOUND_FMT snd_fmt; /* desired sound format */ TV_GEOM geom; /* captured image geometry */ TV_PIXEL_GEOM pix_geom; /* captured image format */ TV_ICAP_FMT img_cap_fmt; /* captured image format */ TV_STILL_FMT img_sav_fmt; /* desired image save format */ char raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ]; TV_INT32 num_raw_fnames; /* where Raw AV data captured */ TV_UINT32 fps; /* FPS we captured at */ TV_UINT32 fps_max; /* Max FPS for this signal */ TV_BOOL cleanup_tmp_files; /* T = cleanup; F = don't */ char script_fname[ MAXPATHLEN ]; /* Name of conversion script */ } TV_CAP_PARM; typedef struct { TV_ICAP_FMT fmt; char *file_ext; char *wgt_name; Widget wgt; } TV_ICAPFMT_ITEM_DEF; typedef struct { TV_VIDEO_TARGET fmt; char *wgt_name; Widget wgt; } TV_VTRG_ITEM_DEF; typedef struct { TV_INT32 frames; TV_INT32 time_us; } TV_VID_STATS; typedef struct { char **cmd; /* Cmd running and its args */ char *tmpstr; Widget dialog_shell; char raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ]; } TV_CNVT_CMD_STATE; /* "mpeg_encode" param file template */ #define ENCODE_SCRIPT_HEADER "#!/bin/sh\n\ # \n\ # %s\n\ # Fxtv Video/System Stream Conversion Script\n\ # \n\ # Automatically generated by BSD X TV v" VERS_STR "\n\ # \n\ \n\ " #define YUV_PIX_GEOM_MATCH(a,b) \ ( !memcmp( (a)->samp_size ,(b)->samp_size ,sizeof((a)->samp_size) ) && \ !memcmp( (a)->samp_int_h,(b)->samp_int_h,sizeof((a)->samp_int_h) ) && \ !memcmp( (a)->samp_int_v,(b)->samp_int_v,sizeof((a)->samp_int_v) ) && \ (a)->frame_packing == (b)->frame_packing && \ !strcmp( (a)->comp_order, (b)->comp_order ) && \ (a)->order_t_to_b == (a)->order_t_to_b && \ (a)->order_l_to_r == (a)->order_l_to_r && \ (a)->y_trans == (a)->y_trans ) #define IMG_CAP_FMT_IS_RGB(icap) ( (icap) == TV_ICAP_FMT_RGB16 ) #define IMG_ENC_FMT_IS_RGB(ienc) ( (ienc) != TV_STILL_FMT_YUV ) /* ******************** Private variables ************** */ static TV_BOOL Audio_enabled; static TV_AUDIO_FILE_FMT Sel_ffmt; static TV_AUDIO_SAMPLE_FMT Sel_sfmt; static TV_BOOL Sel_stereo; static TV_UINT32 Sel_rate; static TV_VIDEO_TARGET Sel_vtarg; static TV_STILL_FMT Sel_ifilefmt; static TV_ICAP_FMT Sel_icapfmt; static TV_VID_STATS Vid_stats; static Widget Dialog_wgt = NULL, Main_wgt = NULL, Audio_cap_mgr = NULL, Audio_enc_mgr = NULL, File_label = NULL, Fname_text = NULL, Res_text = NULL, FPS_text = NULL, Icap_fmt_menu_btn = NULL, Ifile_fmt_menu_btn = NULL, Vtrg_menu_btn = NULL, Cleanup_form = NULL, Cleanup_wgt = NULL, Record_btn = NULL, Stop_btn = NULL, Dismiss_btn = NULL, Wait_dialog = NULL, Ffmt_menu_btn = NULL, Sfmt_menu_btn = NULL, Chan_menu_btn = NULL, Rate_menu_btn = NULL, Audio_cap_wgt = NULL; static TV_BOOL Recording = FALSE, Optimizing = FALSE, First_image = TRUE; static TV_RAW_VIDEO_FILE *Out_rf = NULL; static TV_SOUND Snd; static int Dsp_fd = -1; /* FIXME: Copied from audsav_dlg.c - This needs abstracted better */ static TV_FFMT_ITEM_DEF Ffmt_item_def[] = { { TV_AUDIO_FILE_FMT_RAW , "raw" , NULL }, { TV_AUDIO_FILE_FMT_SUNAU , "au" , "-t raw -U -b -c 1 -r 8012" }, { TV_AUDIO_FILE_FMT_WAV , "wav" , "-t wav" }, { TV_AUDIO_FILE_FMT_VOC , "voc" , "-t voc" }, { TV_AUDIO_FILE_FMT_AIFF , "aiff" , "-t aiff" }, { TV_AUDIO_FILE_FMT_MPEG2 , "mp2" , "-t aiff " }, /* precond */ { TV_AUDIO_FILE_FMT_MPEG3 , "mp3" , "-t aiff" }, /* precond */ }; static TV_SFMT_ITEM_DEF Sfmt_item_def[] = { { TV_AUDIO_SAMPLE_FMT_MULAW_U8 , "mulawU8" , "-t raw -U -b" }, { TV_AUDIO_SAMPLE_FMT_LIN_S8 , "linS8" , "-t raw -s -b" }, { TV_AUDIO_SAMPLE_FMT_LIN_U8 , "linU8" , "-t raw -u -b" }, { TV_AUDIO_SAMPLE_FMT_LIN_S16_LE, "linS16LE" , "-t raw -s -w" }, { TV_AUDIO_SAMPLE_FMT_LIN_U16_LE, "linU16LE" , "-t raw -u -w" }, { TV_AUDIO_SAMPLE_FMT_LIN_S16_BE, "linS16BE" , "-t raw -s -w -x" }, { TV_AUDIO_SAMPLE_FMT_LIN_U16_BE, "linU16BE" , "-t raw -u -w -x" } }; static TV_CHAN_ITEM_DEF Chan_item_def[] = { { FALSE , "mono" }, { TRUE , "stereo" } }; static TV_RATE_ITEM_DEF Rate_item_def[] = { { 8012 , "r8012" }, { 11025 , "r11025" }, { 22050 , "r22050" }, { 44100 , "r44100" }, }; static TV_VTRG_ITEM_DEF Vtrg_item_def[] = { #ifdef DEBUG { TV_VIDEO_TARGET_RAW , "raw" }, #endif { TV_VIDEO_TARGET_IMAGES , "images" }, { TV_VIDEO_TARGET_MPEG_READY , "mpegready" }, { TV_VIDEO_TARGET_MPEG , "mpeg" } }; static TV_ICAPFMT_ITEM_DEF Icap_fmt_item_def[] = { { TV_ICAP_FMT_RGB16 , NULL , "rgb16Cmd" }, { TV_ICAP_FMT_IYUV , "iyuv" , "iyuvCmd" }, { TV_ICAP_FMT_YUY2 , "yuy2" , "yuy2Cmd" }, { TV_ICAP_FMT_YUY2L , "yuy2l", "yuy2lCmd" } }; static TV_IFILEFMT_ITEM_DEF Ifile_fmt_item_def[] = { { TV_STILL_FMT_TIFF , "tiff", "tiffCmd" }, { TV_STILL_FMT_PPM , "ppm" , "ppmCmd" }, { TV_STILL_FMT_YUV , "yuv" , "yuvCmd" } }; static TV_INT32 Vtrg_item_def_size = XtNumber( Vtrg_item_def ), Ifile_fmt_item_def_size = XtNumber( Ifile_fmt_item_def ), Icap_fmt_item_def_size = XtNumber( Icap_fmt_item_def ), Ffmt_item_def_size = XtNumber( Ffmt_item_def ), Sfmt_item_def_size = XtNumber( Sfmt_item_def ), Chan_item_def_size = XtNumber( Chan_item_def ), Rate_item_def_size = XtNumber( Rate_item_def ); /* ******************** Forward declarations ************** */ static void TVVIDSAVDialogBuild( Widget *dialog_wgt ); /* ******************** Function Definitions ************** */ /* SetMenuSelection: Set the active selection of the option menus */ static void SetMenuSelection( Widget menu_btn, TV_UINT32 choice ) { TV_INT32 i; String label; if ( menu_btn == Icap_fmt_menu_btn ) { for ( i = 0; i < Icap_fmt_item_def_size; i++ ) if ( Icap_fmt_item_def[i].fmt == choice ) { XtVaGetValues( Icap_fmt_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= Icap_fmt_item_def_size ) { fprintf( stderr, "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n", choice ); exit(1); } Sel_icapfmt = choice; } else if ( menu_btn == Ifile_fmt_menu_btn ) { for ( i = 0; i < Ifile_fmt_item_def_size; i++ ) if ( Ifile_fmt_item_def[i].fmt == choice ) { XtVaGetValues( Ifile_fmt_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= Ifile_fmt_item_def_size ) { fprintf( stderr, "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n", choice ); exit(1); } Sel_ifilefmt = choice; } else if ( menu_btn == Ffmt_menu_btn ) { for ( i = 0; i < Ffmt_item_def_size; i++ ) if ( Ffmt_item_def[i].fmt == choice ) { XtVaGetValues( Ffmt_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= Ffmt_item_def_size ) { fprintf( stderr, "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n", choice ); exit(1); } Sel_ffmt = choice; } else if ( menu_btn == Sfmt_menu_btn ) { for ( i = 0; i < Sfmt_item_def_size; i++ ) if ( Sfmt_item_def[i].fmt == choice ) { XtVaGetValues( Sfmt_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= Sfmt_item_def_size ) { fprintf( stderr, "TVVIDSAVDIALOGSetSel: Unsupported sampfmt %lu\n", choice ); exit(1); } Sel_sfmt = choice; } else if ( menu_btn == Chan_menu_btn ) { for ( i = 0; i < Chan_item_def_size; i++ ) if ( Chan_item_def[i].stereo == choice ) { XtVaGetValues( Chan_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= Chan_item_def_size ) { fprintf( stderr, "TVVIDSAVDIALOGSetSel: Unsupported #chan %lu\n", choice ); exit(1); } Sel_stereo = choice; } else if ( menu_btn == Rate_menu_btn ) { for ( i = 0; i < Rate_item_def_size; i++ ) if ( Rate_item_def[i].rate == choice ) { XtVaGetValues( Rate_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= Rate_item_def_size ) { fprintf( stderr, "TVVIDSAVDIALOGSetSel: Unsupported rate %lu\n", choice ); exit(1); } Sel_rate = choice; } else if ( menu_btn == Vtrg_menu_btn ) { for ( i = 0; i < XtNumber( Vtrg_item_def ); i++ ) if ( Vtrg_item_def[i].fmt == choice ) { XtVaGetValues( Vtrg_item_def[i].wgt, XtNlabel, &label, NULL ); XtVaSetValues( menu_btn, XtNlabel, label, NULL ); break; } if ( i >= XtNumber( Vtrg_item_def ) ) { fprintf( stderr, "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n", choice ); exit(1); } Sel_vtarg = choice; } else { fprintf( stderr, "TVVIDSAV:SetMenuSelection: Bad menu_btn\n" ); exit(1); } /* HANDLE CONSTRAINTS */ /* When change IMGCAPFMT, ensure IMGENCFMT stays compatible */ /* and vice versa. */ if ( menu_btn == Icap_fmt_menu_btn ) { if ( IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) && !IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) ) SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_PPM ); else if ( !IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) && IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) ) SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_YUV ); } else if ( menu_btn == Ifile_fmt_menu_btn ) { if ( IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) && !IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) ) SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_RGB16 ); else if ( !IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) && IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) ) SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_IYUV ); } else if ( menu_btn == Vtrg_menu_btn ) { /* Enable/disable & set/reset related options */ XtSetSensitive( Cleanup_form, choice == TV_VIDEO_TARGET_MPEG ); /* When change target to MPEG, switch to IYUV/MPEG2 by default */ if (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) || ( Sel_vtarg == TV_VIDEO_TARGET_MPEG )) { #ifdef WHEN_ITS_READY_FOR_PRIME_TIME SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_IYUV ); SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_YUV ); SetMenuSelection( Ffmt_menu_btn, TV_AUDIO_FILE_FMT_MPEG2 ); #else SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_RGB16 ); SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_PPM ); SetMenuSelection( Ffmt_menu_btn, TV_AUDIO_FILE_FMT_MPEG2 ); #endif } } } static void TextValUpdate( Widget text_wgt, char *str ) { XawTextBlock tblk; char *old_str; int old_len; assert( text_wgt != NULL ); memset( &tblk, '\0', sizeof( tblk ) ); tblk.firstPos = 0; tblk.length = strlen( str ); tblk.ptr = str; tblk.format = XawFmt8Bit; XtVaGetValues( text_wgt, XtNstring, &old_str, NULL ); old_len = (old_str == NULL) ? 0 : strlen( old_str ); XawTextReplace( text_wgt, 0, old_len, &tblk ); } /* UpdateButtons - Enable/disable btns based on state */ static void UpdateButtons() { TV_BOOL rec, stop, dismiss; if ( Recording ) stop = TRUE , rec = dismiss = FALSE; else stop = FALSE, rec = dismiss = TRUE; XtSetSensitive( Record_btn , rec ); XtSetSensitive( Stop_btn , stop ); XtSetSensitive( Dismiss_btn , dismiss ); } /* DialogSetEnabled - Utility rtn to sensitive/desensitize the video dialog */ static void DialogSetEnabled( TV_BOOL enabled ) { if ( Main_wgt == NULL ) return; XtSetSensitive( Main_wgt, enabled ); if ( enabled ) { UpdateButtons(); XtSetSensitive( Audio_cap_mgr, Audio_enabled ); } } /* PrepareForVideo - Take dialog values, save them in globals, open */ /* the video device and setup its play/record parameters. */ static TV_BOOL PrepareForVideo( TV_BOOL optimize_only ) { TV_CAPTURE *c = &G_glob.capture; TV_DISK *d = &G_glob.disk; TV_BOOL error = FALSE; String filename, str, str2; char msg[100]; TV_GEOM g = { 0,0,0,0 }; TV_INT32 fps; Boolean cleanup_temp; /* ...Filename base */ XtVaGetValues( Fname_text, XtNstring, &filename, NULL ); if ( filename == NULL ) filename = ""; if ( strlen( filename ) == 0 ) { XUTILDialogPause( TVTOPLEVEL, "Error", "No filename specified.", TV_DIALOG_TYPE_OK ); error = TRUE; goto RETURN; } /* ...Size */ XtVaGetValues( Res_text, XtNstring, &str, NULL ); if ( str == NULL ) str = ""; if (( sscanf( str, "%ldx%ld", &g.w, &g.h ) != 2 ) || !TVCAPTUREValidRegionGeom( c, &g )) { XUTILDialogPause( TVTOPLEVEL, "Error", "Invalid size.", TV_DIALOG_TYPE_OK ); error = TRUE; goto RETURN; } if ( !optimize_only ) { /* ...Speed */ XtVaGetValues( FPS_text, XtNstring, &str, NULL ); if ( str == NULL ) str = ""; if (( sscanf( str, "%ld", &fps ) != 1 ) || ( fps < 1 ) || ( fps > c->fps_max )) { XUTILDialogPause( TVTOPLEVEL, "Error", "Invalid speed.", TV_DIALOG_TYPE_OK ); error = TRUE; goto RETURN; } /* ...ImageCapFmt/ImageEncFmt dependency */ if (( Sel_vtarg != TV_VIDEO_TARGET_RAW ) && ( IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) != IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) )) { str = IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) ? "RGB" : "YUV"; str2 = IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) ? "RGB" : "YUV"; sprintf( msg, "Image Capture Format is %s-based but\n" "Image Encode Format is %s-based.", str, str2 ); XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK ); error = TRUE; goto RETURN; } /* ...Target/AudioEncFmt dependency */ if ( (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) || ( Sel_vtarg == TV_VIDEO_TARGET_MPEG )) && Audio_enabled && (( Sel_ffmt != TV_AUDIO_FILE_FMT_MPEG2 ) && ( Sel_ffmt != TV_AUDIO_FILE_FMT_MPEG3 )) ) { XUTILDialogPause( TVTOPLEVEL, "Error", "Valid Audio Encoding Formats for the\n" "MPEG and MPEG-Ready Targets are:\n" "MPEG-2 and MPEG-3.\n", TV_DIALOG_TYPE_OK ); error = TRUE; goto RETURN; } /* Cleanup Temp Files */ XtVaGetValues( Cleanup_wgt, XtNstate, &cleanup_temp, NULL); } /* Save off settings */ d->fn_video_base[0] = '\0'; strncat( d->fn_video_base, filename, sizeof( d->fn_video_base ) - 1 ); d->video.geom = g; if ( !optimize_only ) { d->video.target = Sel_vtarg; d->video.fps = fps; d->video.cleanup_temp = (cleanup_temp != 0); } RETURN: return !error; } /* DoCmdFailDialog - Display a dialog citing the command that failed */ /* and its exit status; wait on user to dismiss. */ static void DoCmdFailDialog( char *cmd[], int status ) { char msg[ 2*MAXPATHLEN + 160 ]; TV_INT32 i; sprintf( msg, "Video conversion failed.\nCMD = " ); for ( i = 0; cmd[i] != NULL; i++ ) sprintf( msg+strlen(msg), "%s ", cmd[i] ); sprintf( msg+strlen(msg), "\nSTATUS = 0x%.4x", status ); XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK ); } /* OkToWriteTo - Determine if its OK to write to the specified file. */ /* If the file exists, user is prompted to overwrite, and file is */ /* unlinked. If device file, user is prompted for verification. */ /* If insuff perms, error dialog displayed. */ static TV_BOOL OkToWriteTo( char filename[ MAXPATHLEN ], TV_BOOL *dont_unlink ) { struct stat sb; TV_BOOL exists = TRUE, ok = FALSE; char msg[MAXPATHLEN+160]; *dont_unlink = FALSE; if ( stat( filename, &sb ) < 0 ) { if ( errno != ENOENT ) { fprintf( stderr, "Whoah! stat() failed on '%s'.\n", filename ); XBell( TVDISPLAY, 100 ); ok = FALSE; goto RETURN; } exists = FALSE; } if ( exists ) { if ( access( filename, R_OK | W_OK ) < 0 ) { XUTILDialogPause( TVTOPLEVEL, "Error", "Can't read and write to this file.", TV_DIALOG_TYPE_OK ); goto RETURN; } if ( S_ISREG(sb.st_mode) ) { sprintf( msg, "This file exists (%s).\nOverwrite?", filename ); if ( XUTILDialogPause( TVTOPLEVEL, "Confirm", msg, TV_DIALOG_TYPE_YES_NO ) != TV_DIALOG_YES ) goto RETURN; unlink( filename ); } #ifdef FIXME__NOT_SUPPORTED_ANYMORE else if ( S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode) ) { sprintf( msg, "This is a device file (%s).\nAre you sure " "you want to write to it?", filename ); if ( XUTILDialogPause( TVTOPLEVEL, "Confirm", msg, TV_DIALOG_TYPE_YES_NO ) != TV_DIALOG_YES ) goto RETURN; *dont_unlink = TRUE; } #endif else { XUTILDialogPause( TVTOPLEVEL, "Error", "Can't write to this type of file.", TV_DIALOG_TYPE_OK ); goto RETURN; } } ok = TRUE; RETURN: return ok; } /* VideoPixelGeom - Returns ptr to a pixel geom that we're going to */ /* use for capture. */ static TV_PIXEL_GEOM *VideoPixelGeom( TV_ICAP_FMT img_cap_fmt ) { static TV_PIXEL_GEOM IYUV = { -1,TV_PIXELTYPE_YUV,0,{},0,0, {8,8,8},{1,2,2},{1,2,2},TV_FRAME_PLANAR,"YUV",1,1,0 }, YUY2 = { -1,TV_PIXELTYPE_YUV,0,{},0,0, {8,8,8},{1,2,2},{1,1,1},TV_FRAME_PACKED,"YUYV",1,1,0 }, YUY2L = { -1,TV_PIXELTYPE_YUV,0,{},0,0, {8,8,8},{1,2,2},{1,1,1},TV_FRAME_PLANAR,"YUV",1,1,0 }; static TV_PIXEL_GEOM pg; TV_CAPTURE *c = &G_glob.capture; TV_UINT32 num_pg, i; TV_BOOL found = FALSE; TVCAPTUREGetNumPixFmts( c, &num_pg ); for ( i = 0; (i < num_pg) && !found; i++ ) { TVCAPTUREGetNthPixFmt( c, i, &pg ); switch ( img_cap_fmt ) { case TV_ICAP_FMT_RGB16 : /* Pull out 565 16bpp byte swapped */ if (( pg.type == TV_PIXELTYPE_RGB ) && ( pg.Bpp == 2 ) && ( pg.mask[0] == 0xf800 ) && ( pg.mask[1] == 0x07e0 ) && ( pg.mask[2] == 0x001f ) && pg.swap_bytes ) found = TRUE; break; case TV_ICAP_FMT_IYUV : if ( YUV_PIX_GEOM_MATCH( &pg ,&IYUV ) ) found = TRUE; break; case TV_ICAP_FMT_YUY2 : if ( YUV_PIX_GEOM_MATCH( &pg ,&YUY2 ) ) found = TRUE; break; case TV_ICAP_FMT_YUY2L : if ( YUV_PIX_GEOM_MATCH( &pg ,&YUY2L ) ) found = TRUE; break; default : fprintf( stderr, "VideoPixelGeom: Unsupported pixel cap fmt: %d\n", img_cap_fmt ); exit(1); } } if ( !found ) { fprintf( stderr, "VideoPixelGeom: Can't find cap fmt %d\n", img_cap_fmt ); exit(1); } return &pg; } /* GetRawFilenames - Fill in list of raw filenames to use for */ /* video capture */ static void GetRawFilenames( char cap_file[ MAXPATHLEN ], TV_BOOL target_raw, char fnames[ 4 ][ MAXPATHLEN ], TV_INT32 *num_raw_files ) { TV_INT32 i; String *app_res = App_res.video_cap_file; *num_raw_files = 0; for ( i = 0; i < 4; i++ ) fnames[i][0] = '\0'; /* If user didn't set resources, fall-back on a capture file based */ /* on the name of the user-entered target-file. */ if ( !app_res[0] || !app_res[0][0] ) { /* FIXME: for raw devices, no difference for target_raw */ if ( !target_raw ) sprintf( fnames[0], AV_RAWNAME_FMT, cap_file ); else { fnames[0][0] = '\0'; strncat( fnames[0], cap_file, MAXPATHLEN-1 ); } *num_raw_files = 1; } /* User set at least one capture file resource; use those */ else { for ( i = 0; i < 4; i++ ) if ( app_res[i] && app_res[i][0] ) strncat( fnames[i], app_res[i], MAXPATHLEN-1 ); else break; *num_raw_files = i; } } /* Unlink the raw files used for video capture */ static void UnlinkRawFiles( char fnames[ 4 ][ MAXPATHLEN ] ) { TV_INT32 i; for ( i = 0; i < 4; i++ ) if ( fnames[i][0] ) unlink( fnames[i] ); } /**@BEGINFUNC************************************************************** Prototype : static TV_BOOL WriteVidConvertShScript( TV_CAP_PARM *p ) Purpose : Write an SH script to perform the encoding of a raw capture file into images and an audio file or an MPEG video and/or system stream as selected by the user. Programmer : 16-Jan-98 Randall Hopper Parameters : p - I: image conversion parameters Returns : T = success; F = failure Globals : None. **@ENDFUNC*****************************************************************/ static TV_BOOL WriteVidConvertShScript( TV_CAP_PARM *p ) { FILE *fp = NULL; TV_BOOL ret = FALSE; char *str; struct stat stat; TV_INT32 i; TV_BOOL has_path; /* Open output script file */ has_path = strchr( p->fname_base, '/' ) != NULL; sprintf( p->script_fname, SCRIPT_FNAME_FMT, (has_path ? "" : "./"), p->fname_base ); if ( (fp = fopen( p->script_fname, "wt" )) == NULL ) { fprintf( stderr, "Failed to open for write: %s\n", p->script_fname ); goto RETURN; } /* Write the header */ fprintf( fp, ENCODE_SCRIPT_HEADER, p->script_fname ); /*fprintf( fp, "\n\n\n exec 1>LOG 2>&1 \n\n\n\n" );*/ /* Write the conversion settings */ fprintf( fp, "AV_RAW_FILES='" ); for ( i = 0; i < p->num_raw_fnames; i++ ) fprintf( fp, "%s%s", (( i==0 ) ? "" : "," ), p->raw_fnames[i] ); fprintf( fp, "'\n" ); fprintf( fp, "AV_TARGET='%s'\n\n", (p->target_mpeg ? "MPEG" : "IMAGES") ); fprintf( fp, "TARGET_FN_BASE='%s'\n\n", p->fname_base ); fprintf( fp, "AUDIO_ENABLED='%s'\n", (p->audio_enabled ? "YES" : "NO") ); switch ( p->snd_fmt.samp_fmt ) { case TV_AUDIO_SAMPLE_FMT_MULAW_U8 : str = "MULAW_U8" ; break; case TV_AUDIO_SAMPLE_FMT_LIN_S8 : str = "LIN_S8" ; break; case TV_AUDIO_SAMPLE_FMT_LIN_U8 : str = "LIN_U8" ; break; case TV_AUDIO_SAMPLE_FMT_LIN_S16_LE : str = "LIN_S16_LE"; break; case TV_AUDIO_SAMPLE_FMT_LIN_U16_LE : str = "LIN_U16_LE"; break; case TV_AUDIO_SAMPLE_FMT_LIN_S16_BE : str = "LIN_S16_BE"; break; case TV_AUDIO_SAMPLE_FMT_LIN_U16_BE : str = "LIN_U16_BE"; break; default : str = "" ; break; } fprintf( fp, "AUDIO_CAP_FMT_SAMPLE='%s'\n", str ); fprintf( fp, "AUDIO_CAP_FMT_CHAN='%d'\n", p->snd_fmt.stereo ? 2 : 1 ); fprintf( fp, "AUDIO_CAP_FMT_FREQ='%ld'\n", p->snd_fmt.samp_rate ); switch ( p->snd_fmt.file_fmt ) { case TV_AUDIO_FILE_FMT_RAW : str = "RAW" ; break; case TV_AUDIO_FILE_FMT_SUNAU : str = "SUNAU"; break; case TV_AUDIO_FILE_FMT_WAV : str = "WAV" ; break; case TV_AUDIO_FILE_FMT_VOC : str = "VOC" ; break; case TV_AUDIO_FILE_FMT_AIFF : str = "AIFF" ; break; case TV_AUDIO_FILE_FMT_MPEG2 : str = "MPEG2"; break; case TV_AUDIO_FILE_FMT_MPEG3 : str = "MPEG3"; break; default : str = "" ; break; } fprintf( fp, "AUDIO_TARGET_FMT='%s'\n\n", str ); fprintf( fp, "VIDEO_RES_X='%ld'\n", p->geom.w ); fprintf( fp, "VIDEO_RES_Y='%ld'\n", p->geom.h ); switch ( p->img_cap_fmt ) { case TV_ICAP_FMT_RGB16 : str = "RGB16"; break; case TV_ICAP_FMT_IYUV : str = "IYUV" ; break; case TV_ICAP_FMT_YUY2 : str = "YUY2" ; break; case TV_ICAP_FMT_YUY2L : str = "YUY2L"; break; default : str = "" ; break; } fprintf( fp, "VIDEO_CAP_FMT='%s'\n", str ); fprintf( fp, "VIDEO_TARGET_FPS='%ld'\n", p->fps ); fprintf( fp, "VIDEO_STREAM='%s'\n\n", ( p->streaming ? "YES" : "NO" ) ); switch ( p->img_sav_fmt ) { case TV_STILL_FMT_TIFF : str = "TIFF"; break; case TV_STILL_FMT_PPM : str = "PPM" ; break; case TV_STILL_FMT_YUV : str = "YUV" ; break; default : str = "" ; break; } fprintf( fp, "IMAGE_TARGET_FMT='%s'\n\n", str ); fprintf( fp, "CLEANUP_TEMP_FILES='%s'\n", (p->cleanup_tmp_files ? "YES" : "NO" ) ); /* Source the main script with the methods */ fprintf( fp, "\n\n\n" "#\n" "# Source the videoCnvtScript with the conversion methods\n" "#\n" ". %s\n", App_res.video_cnvt_script ); /* Add execute permission */ fstat ( fileno(fp), &stat ); fchmod( fileno(fp), stat.st_mode | S_IXUSR ); ret = TRUE; RETURN: if ( fp != NULL ) fclose( fp ); if ( !ret ) unlink( p->script_fname ); return ret; } /* CnvtCmdCancelTestCB */ /* - Used by CnvtCmd invocations of XUTILRunCmdAllowCancel to */ /* identify when the the user aborted the operation. */ static TV_BOOL CnvtCmdCancelTestCB( void *cb_data ) { TV_CNVT_CMD_STATE *state = (TV_CNVT_CMD_STATE *) cb_data; return !XtIsRealized( state->dialog_shell ); } /* CnvtCmdDoneCB */ /* - Called when the conversion script completes or is aborted */ static void CnvtCmdDoneCB( TV_BOOL aborted, int status, void *cb_data ) { TV_CNVT_CMD_STATE *state = (TV_CNVT_CMD_STATE *) cb_data; /* At this stage, always pull down the "wait" dialog and destroy it */ if ( !aborted ) XtPopdown( state->dialog_shell ); XtDestroyWidget( state->dialog_shell ); /* If the command failed, tell the user about it */ if ( !aborted && ( status != 0 ) ) DoCmdFailDialog( state->cmd, status ); /* Do post-cmd cleanup */ free( state->cmd ); free( state->tmpstr ); /* If we completed successfully or the user canceled the conversion, */ /* remove raw capture files (if passed). */ /* NOTE: if we're capturing to devices, don't clean those up. */ if ( aborted || ( status == 0 ) ) { TV_INT32 i; struct stat stat_s; for ( i = 0; i < XtNumber( state->raw_fnames ); i++ ) if (( state->raw_fnames[i][0] != '\0' ) && ( stat( state->raw_fnames[i], &stat_s ) == 0 ) && ( stat_s.st_mode & S_IFREG )) unlink( state->raw_fnames[i] ); } /* FIXME: The fxtv_conv.sh script isn't nuking its child processes */ /* and temporary files when it's killed. It just dies. */ DialogSetEnabled( True ); Recording = FALSE; UpdateButtons(); free( state ); return; } /* RunCnvtCmd - Exec command to convert raw capture to desired format(s) */ static void RunCnvtCmd( char cmd[], char raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ] ) { TVUTIL_PIPE_END end[3] = {{ -1 }, { -1 }, { -1 }}; TV_CNVT_CMD_STATE *state; Widget dialog_shell; if ( (state = calloc( 1, sizeof(*state) )) == NULL ) TVUTILOutOfMemory(); TVUTILCmdStrToArgList( cmd, &state->cmd, &state->tmpstr ); dialog_shell = XUTILDialogBuild( TVTOPLEVEL, "Please Wait", "Conversion in Progress...", TV_DIALOG_TYPE_CANCEL ); XUTILXtPopup( dialog_shell, XtGrabNone, TVTOPLEVEL ); /* Execute conversion cmd & wait on it to finish or user to cancel */ state->dialog_shell = dialog_shell; if ( raw_fnames ) memcpy( state->raw_fnames, raw_fnames, sizeof( state->raw_fnames ) ); else memset( state->raw_fnames, '\0', sizeof( state->raw_fnames ) ); XUTILRunCmdAllowCancel( TVAPPCTX, state->cmd, end, CnvtCmdCancelTestCB, state, CnvtCmdDoneCB , state ); } /**@BEGINFUNC************************************************************** Prototype : static void RecordCmdCB( Widget w, XtPointer cl, XtPointer cb ) Purpose : Callback used for Record and Optimize functions. Programmer : 17-Jan-98 Randall Hopper Parameters : w - I: what widget was pressed that invoked us (don't care) cl - I: "optimize" or "record" cb - I: (don't care) Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void RecordCmdCB( Widget w, XtPointer cl, XtPointer cb ) { TV_CAPTURE *c = &G_glob.capture; TV_DISK *d = &G_glob.disk; char *error_msg, *cfg_fail_msg, *raw_fnames[ TV_RAW_MAX_FILES ]; int dsp_fd = -1; TV_BOOL optimizing = STREQ( (char *)cl, "optimize" ), dont_unlink, video_disabled = FALSE, cmd_running = FALSE; TV_RAW_VIDEO_FILE *rf = NULL; Widget dialog_shell = NULL; TV_INT32 i; TV_CAP_PARM *p = NULL; /* If we haven't created the dialog yet, do so */ if ( Dialog_wgt == NULL ) { TVVIDSAVDialogBuild( &Dialog_wgt ); TVVIDSAVDIALOGResync(); } /* If already recording, ignore user */ if ( Recording ) { XBell( TVDISPLAY, 100 ); return; } /* Save off old freeze state, and stop capture if necessary */ TVSCREENSetScreenUpdateEnabled( FALSE ); video_disabled = TRUE; /* Grab values off dialog */ if ( !PrepareForVideo( optimizing ) ) goto RETURN; /* Fill in capture parameters */ if ( (p = calloc( 1, sizeof(*p) )) == NULL ) TVUTILOutOfMemory(); p->fname_base[0] = '\0'; strncat( p->fname_base, d->fn_video_base, sizeof(p->fname_base)-1 ); p->audio_enabled = Audio_enabled; p->target_mpeg = (Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY) || (Sel_vtarg == TV_VIDEO_TARGET_MPEG); /* FIXME: Allow user to set whether we'll do streaming or not */ /* (for YUV, uses YUV; for RGB, uses PPM) */ p->streaming = (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) || ( Sel_vtarg == TV_VIDEO_TARGET_MPEG )) && (( Sel_icapfmt == TV_ICAP_FMT_IYUV ) || (( Sel_icapfmt == TV_ICAP_FMT_RGB16 ) && ( Sel_ifilefmt == TV_STILL_FMT_PPM ))); p->snd_fmt.samp_fmt = Sel_sfmt, /* Cap sample format */ p->snd_fmt.stereo = Sel_stereo, /* Cap num chan */ p->snd_fmt.samp_rate = Sel_rate, /* Cap sample rate */ p->snd_fmt.file_fmt = Sel_ffmt; /* Desired output format */ memcpy( &p->geom, &d->video.geom, sizeof(p->geom) ); memcpy( &p->pix_geom, VideoPixelGeom(Sel_icapfmt), sizeof(p->pix_geom) ); p->img_cap_fmt = Sel_icapfmt; p->img_sav_fmt = Sel_ifilefmt; GetRawFilenames( p->fname_base, (Sel_vtarg == TV_VIDEO_TARGET_RAW), p->raw_fnames, &p->num_raw_fnames ); p->fps = d->video.fps; TVCAPTUREGetFPSMax( c, &p->fps_max ); p->cleanup_tmp_files = d->video.cleanup_temp; p->script_fname[0] = '\0'; /* See if the output file exists; if so, we need to verify user wants */ /* to overwrite. */ /* FIXME: We really need to check all the possible output filenames */ /* FIXME: Add RAW DEVICE SUPPORT or add check for target NOT raw device */ /* For RAW devices, we need to write EOF record (read() doesn't */ /* return 0 on end of stream). Also, quit unlinking raw device files */ /* And if raw write is to device file, must write frames someplace */ /* else; also, can't use tempname when writing to this file. */ if ( !OkToWriteTo( p->fname_base, &dont_unlink ) ) goto RETURN; /* Now open the raw output file */ for ( i = 0; i < XtNumber( raw_fnames ); i++ ) raw_fnames[i] = p->raw_fnames[i]; if ( !TVRAWVIDEOOpen( raw_fnames, p->num_raw_fnames, FALSE, &rf ) ) { XUTILDialogPause( TVTOPLEVEL, "Error", "Couldn't open output file\n", TV_DIALOG_TYPE_OK ); goto RETURN; } if ( p->audio_enabled ) { Snd.sample_fmt = p->snd_fmt.samp_fmt, Snd.stereo = p->snd_fmt.stereo, Snd.sample_rate = p->snd_fmt.samp_rate, Snd.buf = NULL, Snd.bytes = 0; if ( !TVAUDIOOpenDsp( &Snd, TRUE, &dsp_fd, &error_msg ) ) { XUTILDialogPause( TVTOPLEVEL, "Error", error_msg, TV_DIALOG_TYPE_OK ); dsp_fd = -1; goto RETURN; } TVAUDIOSelectLineForRecord(); } /* Disable all but stop btn */ Recording = TRUE; Optimizing = optimizing; First_image = TRUE; d->video.capture_on = TRUE; if ( !optimizing ) UpdateButtons(); else { DialogSetEnabled( False ); /* Build wait dialog */ dialog_shell = XUTILDialogBuild( TVTOPLEVEL, "Please Wait", "Optimizing Capture Speed...", TV_DIALOG_TYPE_CANCEL ); XUTILXtPopup( dialog_shell, XtGrabNone, TVTOPLEVEL ); Wait_dialog = dialog_shell; } /* Flush X events (update GUI buttons, etc.) */ XSync( TVDISPLAY, False ); /* Queue up an initial request to capture a frame (to driver buffer) */ TVCAPTURESetFrameDoneCBEnabled( c, TRUE ); TVCAPTURESetCaptureMode ( c, TV_CAPTURE_CONTINUOUS ); TVCAPTURESetTransferMode ( c, TV_TRANSFER_STD_IMAGE ); TVCAPTURESetRegionGeom ( c, &p->geom ); TVCAPTURESetPixelGeom ( c, &p->pix_geom ); TVCAPTURESetFPS ( c, p->fps ); if ( !TVCAPTUREConfigure( c, &cfg_fail_msg ) ) { fprintf( stderr, "TVCAPTUREConfigure() failed: %s\n", cfg_fail_msg ); UnlinkRawFiles( p->raw_fnames ); goto RETURN; } TVCAPTUREStart( c ); Out_rf = rf; if ( Audio_enabled ) Dsp_fd = dsp_fd; /*******************************************************************/ /* Now, madly capture frames/audio and write to disk in raw form */ /*******************************************************************/ TVSetWorkProcTimeout( 0 ); while ( Recording && ( !optimizing || XtIsRealized( Wait_dialog ) ) ) { XEvent ev; XtAppNextEvent( TVAPPCTX, &ev ); XtDispatchEvent( &ev ); } TVSetWorkProcTimeout( -1 ); TVCAPTUREStop( c ); Out_rf = NULL; TVRAWVIDEOClose( &rf ); if ( Audio_enabled ) { close( dsp_fd ); Dsp_fd = dsp_fd = -1; } /* Turn TV back on so user can watch while they wait */ TVCAPTUREClearPendingFrames(); TVSCREENSetScreenUpdateEnabled( TRUE ); d->video.capture_on = FALSE; video_disabled = FALSE; if ( optimizing ) { TV_INT32 fps; char str[20]; /* If user canceled optimization, bail out */ if ( Optimizing ) goto RETURN; /* This is a cheesy first-cut */ fps = Vid_stats.frames*1000000L/Vid_stats.time_us; sprintf( str, "%ld", fps ); TextValUpdate( FPS_text, str ); } /* Does user want more than just the vid/aud captured to a raw file? */ if ( Sel_vtarg == TV_VIDEO_TARGET_RAW ) goto RETURN; /* Are we optimizing? If so, done */ if ( optimizing ) goto RETURN; /* FIXME: The Record and Optimize callbacks now aren't very different */ /* before this point. Merge them. */ /* FIXME: Consider parallelizing aud/vid encode in fxtv_cnvt.sh script */ /* Write conversion script */ /* FIXME: Snapshot these parms when we start capture */ if ( !WriteVidConvertShScript( p ) ) { XUTILDialogPause( TVTOPLEVEL, "Error", "Failed writing format conversion script\n", TV_DIALOG_TYPE_OK ); goto RETURN; } /* Does user want us to go ahead and produce images/video/audio */ if ( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) goto RETURN; /* Run conversion subprocess */ DialogSetEnabled( False ); RunCnvtCmd( p->script_fname, ( p->cleanup_tmp_files ? p->raw_fnames : NULL ) ); cmd_running = TRUE; RETURN: if ( dialog_shell ) XtDestroyWidget( dialog_shell ); if ( optimizing && p ) UnlinkRawFiles( p->raw_fnames ); if ( rf != NULL ) TVRAWVIDEOClose( &rf ); if ( !cmd_running ) { Recording = FALSE; free(p); } d->video.capture_on = FALSE; if ( dsp_fd >= 0 ) close( dsp_fd ); if ( video_disabled ) { TVCAPTUREClearPendingFrames(); TVSCREENSetScreenUpdateEnabled( TRUE ); } if ( !optimizing ) UpdateButtons(); else DialogSetEnabled( True ); } /* StopCmdCB - Stop recording, if we're recording now */ static void StopCmdCB( Widget w, XtPointer cl, XtPointer cb ) { if ( !Recording ) { XBell( TVDISPLAY, 100 ); return; } Recording = FALSE; } /* DismissCmdCB - Dismiss the dialog, if we're not recording */ static void DismissCmdCB( Widget w, XtPointer cl, XtPointer cb ) { if ( Recording ) { XBell( TVDISPLAY, 100 ); return; } XtPopdown( Dialog_wgt ); } /* ICapFmtMenuCB - Update menu button text */ static void ICapFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Icap_fmt_menu_btn; TV_ICAPFMT_ITEM_DEF *def = (TV_ICAPFMT_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->fmt ); } /* IFileFmtMenuCB - Update menu button text */ static void IFileFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Ifile_fmt_menu_btn; TV_IFILEFMT_ITEM_DEF *def = (TV_IFILEFMT_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->fmt ); } /* VFmtMenuItemCB - Update menu button text with selected choice */ static void VFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Vtrg_menu_btn; TV_VTRG_ITEM_DEF *def = (TV_VTRG_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->fmt ); } /* FFmtMenuItemCB - Update menu button text with selected choice */ static void FFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Ffmt_menu_btn; TV_FFMT_ITEM_DEF *def = (TV_FFMT_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->fmt ); /* If Sun AU, default to 8-bit MULAW, Mono, & 8012 s/s */ if ( def->fmt == TV_AUDIO_FILE_FMT_SUNAU ) { SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_MULAW_U8 ); SetMenuSelection( Chan_menu_btn, FALSE ); SetMenuSelection( Rate_menu_btn, 8012 ); } /* If any of the rest, go for the max */ else { SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_LIN_S16_LE ); SetMenuSelection( Chan_menu_btn, TRUE ); SetMenuSelection( Rate_menu_btn, 44100 ); } } /* SFmtMenuItemCB - Update menu button text with selected choice */ static void SFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Sfmt_menu_btn; TV_SFMT_ITEM_DEF *def = (TV_SFMT_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->fmt ); /* If 8-bit ULAW, default to Mono & 8012 s/s */ if ( def->fmt == TV_AUDIO_SAMPLE_FMT_MULAW_U8 ) { SetMenuSelection( Chan_menu_btn, FALSE ); SetMenuSelection( Rate_menu_btn, 8012 ); } } /* ChanMenuItemCB - Update menu button text with selected choice */ static void ChanMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Chan_menu_btn; TV_CHAN_ITEM_DEF *def = (TV_CHAN_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->stereo ); } /* RateMenuItemCB - Update menu button text with selected choice */ static void RateMenuItemCB( Widget w, XtPointer cl, XtPointer cb ) { Widget menu_btn = Rate_menu_btn; TV_RATE_ITEM_DEF *def = (TV_RATE_ITEM_DEF *) cl; SetMenuSelection( menu_btn, def->rate ); } /* AudioEnableToggleCB - Enable/Disable capturing of audio with video */ static void AudioEnableToggleCB( Widget w, XtPointer cl, XtPointer cb ) { XtVaGetValues( w, XtNstate, &Audio_enabled, NULL ); EVPRINTF(( "Audio Enable = %s\n", Audio_enabled ? "yes" : "no" )); DialogSetEnabled( TRUE ); } /* CreateSingleLineTextField - convenience rtn to create single line */ /* text fields the way we like 'em. */ Widget CreateSingleLineTextField( char wgt_name[], Widget parent ) { XtTranslations transl; Widget wgt; wgt = XtVaCreateManagedWidget( wgt_name, asciiTextWidgetClass, parent, XtNtype , XawAsciiString, XtNuseStringInPlace, False, XtNscrollHorizontal, XawtextScrollNever, XtNscrollVertical , XawtextScrollNever, XtNdisplayCaret , False, XtNeditType , XawtextEdit, XtNresize , XawtextResizeNever, NULL ); /* Text widget translation overrides */ transl = XtParseTranslationTable( G_transl_ovr_ascii_text ); XtOverrideTranslations( wgt, transl ); transl = XtParseTranslationTable( G_transl_ovr_ascii_text_1line ); XtOverrideTranslations( wgt, transl ); return wgt; } static void BuildImageCapWgtPanel( Widget parent ) { Widget w, gbox, cbox, menu_shell; TV_INT32 i; gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent, XtNorientation, XtorientVertical, NULL ); w = XtVaCreateManagedWidget( "imageCapLabel", labelWidgetClass, gbox, XtNjustify, XtJustifyLeft, XtNwidth, 263, NULL ); /* Resolution text */ cbox = XtVaCreateManagedWidget( "resBox", boxWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "resLabel", labelWidgetClass, cbox, XtNjustify, XtJustifyRight, XtNwidth, 90, NULL ); Res_text = CreateSingleLineTextField( "resText", cbox ); /* Capture format menu */ cbox = XtVaCreateManagedWidget( "iCapFmtBox", boxWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "iCapFmtLabel", labelWidgetClass, cbox, XtNjustify, XtJustifyRight, XtNwidth, 90, NULL ); w = XtVaCreateManagedWidget( "iCapFmtMenuBox", boxWidgetClass, cbox, XtNorientation, XtorientHorizontal, NULL ); Icap_fmt_menu_btn = XtVaCreateManagedWidget( "iCapFmtMenu", menuButtonWidgetClass, w, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Icap_fmt_menu_btn, NULL ); /* Create all format items for this menu */ for ( i = 0; i < XtNumber( Icap_fmt_item_def ); i++ ) { Widget item; item = XtVaCreateManagedWidget( Icap_fmt_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Icap_fmt_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, ICapFmtMenuItemCB, (XtPointer) &Icap_fmt_item_def[i] ); /* Add a separator between the YUV and RGB entries */ if ( i == TV_ICAP_LAST_RGB ) XtVaCreateManagedWidget( "separator", smeLineObjectClass, menu_shell, NULL ); } /* FPS text & optimize button */ cbox = XtVaCreateManagedWidget( "fpsBox", boxWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "fpsLabel", labelWidgetClass, cbox, XtNjustify, XtJustifyRight, XtNwidth, 90, NULL ); FPS_text = CreateSingleLineTextField( "fpsText", cbox ); w = XtVaCreateManagedWidget( "optimizeCmd", commandWidgetClass, cbox, NULL ); XtAddCallback( w, XtNcallback, RecordCmdCB, "optimize" ); } static void BuildImageEncWgtPanel( Widget parent ) { Widget w, gbox, cbox, menu_shell; TV_INT32 i; gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent, XtNorientation, XtorientVertical, NULL ); /* Box label */ w = XtVaCreateManagedWidget( "imageEncLabel", labelWidgetClass, gbox, XtNjustify, XtJustifyLeft, XtNwidth, 263, NULL ); /* Format menu panel */ cbox = XtVaCreateManagedWidget( "iFileFmtBox", boxWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "iFileFmtLabel", labelWidgetClass, cbox, NULL ); w = XtVaCreateManagedWidget( "iFileFmtMenuBox", boxWidgetClass, cbox, XtNorientation, XtorientHorizontal, NULL ); Ifile_fmt_menu_btn = XtVaCreateManagedWidget( "iFileFmtMenu", menuButtonWidgetClass, w, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Ifile_fmt_menu_btn, NULL ); /* Create all format items for this menu */ for ( i = 0; i < XtNumber( Ifile_fmt_item_def ); i++ ) { Widget item; item = XtVaCreateManagedWidget( Ifile_fmt_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Ifile_fmt_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, IFileFmtMenuItemCB, (XtPointer) &Ifile_fmt_item_def[i] ); } } static void BuildAudioCapWgtPanel( Widget parent ) { Widget w, gbox, cbox, fbox, lbox, form, menu_shell; TV_INT32 i; gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent, XtNorientation, XtorientVertical, NULL ); w = XtVaCreateManagedWidget( "audioCapLabel", labelWidgetClass, gbox, XtNjustify, XtJustifyLeft, XtNwidth, 345, NULL ); /* Audio Capture On/Off Toggle */ form = XtVaCreateManagedWidget( "audCapForm", formWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form, XtNjustify, XtJustifyLeft, XtNlabel, " ", XtNresize, False, XtNencoding, XawTextEncoding8bit, XtNwidth, 70, NULL ); w = XtVaCreateManagedWidget( "audCapToggle", toggleWidgetClass, form, XtNlabel, " ", XtNfromHoriz, w, NULL ); Audio_cap_wgt = w; XtAddCallback( w, XtNcallback, AudioEnableToggleCB, NULL ); w = XtVaCreateManagedWidget( "audCapToggleLabel", labelWidgetClass, form, XtNresize, False, XtNfromHoriz, w, XtNencoding, XawTextEncoding8bit, XtNjustify, XtJustifyLeft, NULL ); /* Capture format box */ fbox = XtVaCreateManagedWidget( "captureBox", boxWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); Audio_cap_mgr = fbox; lbox = XtVaCreateManagedWidget( "sampFmtLabelBox", boxWidgetClass, fbox, XtNorientation, XtorientVertical, NULL ); w = XtVaCreateManagedWidget( "sampFmtLabel", labelWidgetClass, lbox, XtNresize, False, XtNencoding, XawTextEncoding8bit, NULL ); cbox = XtVaCreateManagedWidget( "sampFmtWgtBox", boxWidgetClass, fbox, XtNorientation, XtorientVertical, NULL ); /* Sample Format Choice Box */ Sfmt_menu_btn = XtVaCreateManagedWidget( "sampFmtMenu", menuButtonWidgetClass, cbox, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Sfmt_menu_btn, NULL ); for ( i = 0; i < Sfmt_item_def_size; i++ ) { Widget item; item = XtVaCreateManagedWidget( Sfmt_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Sfmt_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, SFmtMenuItemCB, &Sfmt_item_def[i] ); } /* # Channels Choice Box */ w = XtVaCreateManagedWidget( "chanMenuBox", boxWidgetClass, cbox, XtNorientation, XtorientHorizontal, NULL ); Chan_menu_btn = XtVaCreateManagedWidget( "chanMenu", menuButtonWidgetClass, w, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Chan_menu_btn, NULL ); for ( i = 0; i < Chan_item_def_size; i++ ) { Widget item; item = XtVaCreateManagedWidget( Chan_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Chan_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, ChanMenuItemCB, &Chan_item_def[i] ); } /* Sample Rate Choice Box */ w = XtVaCreateManagedWidget( "rateMenuBox", boxWidgetClass, cbox, XtNorientation, XtorientHorizontal, NULL ); Rate_menu_btn = XtVaCreateManagedWidget( "rateMenu", menuButtonWidgetClass, w, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Rate_menu_btn, NULL ); for ( i = 0; i < Rate_item_def_size; i++ ) { Widget item; item = XtVaCreateManagedWidget( Rate_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Rate_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, RateMenuItemCB, &Rate_item_def[i] ); } } static void BuildAudioEncWgtPanel( Widget parent ) { Widget w, gbox, cbox, menu_shell; TV_INT32 i; gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent, XtNorientation, XtorientVertical, NULL ); Audio_enc_mgr = gbox; w = XtVaCreateManagedWidget( "audioEncLabel", labelWidgetClass, gbox, XtNjustify, XtJustifyLeft, XtNwidth, 345, NULL ); cbox = XtVaCreateManagedWidget( "fileFmtBox", boxWidgetClass, gbox, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "fileFmtLabel", labelWidgetClass, cbox, XtNjustify, XtJustifyRight, NULL ); /* File Format Choice Box */ Ffmt_menu_btn = XtVaCreateManagedWidget( "fileFmtMenu", menuButtonWidgetClass, cbox, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Ffmt_menu_btn, NULL ); for ( i = 0; i < Ffmt_item_def_size; i++ ) { Widget item; item = XtVaCreateManagedWidget( Ffmt_item_def[i].file_ext, smeBSBObjectClass, menu_shell, NULL ); Ffmt_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, FFmtMenuItemCB, &Ffmt_item_def[i] ); } } static void TVVIDSAVDialogBuild( Widget *dialog_wgt ) { Widget w, main1, main2, sbox, cbox, gbox, form, menu_shell; TV_INT32 i; /* Create the dialog widgets */ *dialog_wgt = XtVaCreatePopupShell( "videoSaveDialog", transientShellWidgetClass, TVTOPLEVEL, NULL ); main1 = XtVaCreateManagedWidget( "mainBox", boxWidgetClass, *dialog_wgt, XtNorientation, XtorientVertical, NULL ); Main_wgt = main1; main2 = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, main1, XtNorientation, XtorientVertical, NULL ); /* Filename base text */ cbox = XtVaCreateManagedWidget( "fileBaseBox", boxWidgetClass, main2, XtNorientation, XtorientHorizontal, NULL ); w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, cbox, XtNlabel, " ", XtNresize, False, XtNwidth, 170, NULL ); File_label = XtVaCreateManagedWidget( "fileBaseLabel", labelWidgetClass, cbox, XtNresize, False, XtNwidth, 100, XtNjustify, XtJustifyRight, NULL ); Fname_text = CreateSingleLineTextField( "fileBaseText", cbox ); /* File format selection box */ cbox = XtVaCreateManagedWidget( "fileFmtBox", boxWidgetClass, main2, XtNorientation, XtorientHorizontal, XtNfromVert, cbox, NULL ); w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, cbox, XtNlabel, " ", XtNresize, False, XtNwidth, 170, NULL ); w = XtVaCreateManagedWidget( "targetLabel", labelWidgetClass, cbox, XtNjustify, XtJustifyRight, XtNwidth, 100, NULL ); Vtrg_menu_btn = XtVaCreateManagedWidget( "targetMenu", menuButtonWidgetClass, cbox, XtNresize, XawtextResizeNever, NULL ); menu_shell = XtVaCreatePopupShell( "menu", simpleMenuWidgetClass, Vtrg_menu_btn, NULL ); for ( i = 0; i < Vtrg_item_def_size; i++ ) { Widget item; item = XtVaCreateManagedWidget( Vtrg_item_def[i].wgt_name, smeBSBObjectClass, menu_shell, NULL ); Vtrg_item_def[i].wgt = item; XtAddCallback( item, XtNcallback, VFmtMenuItemCB, &Vtrg_item_def[i] ); } /* Parameter panels (left and right) */ sbox = XtVaCreateManagedWidget( "sideBox", boxWidgetClass, main2, XtNorientation, XtorientVertical, XtNfromVert, cbox, NULL ); gbox = XtVaCreateManagedWidget( "group1Box", boxWidgetClass, sbox, XtNorientation, XtorientHorizontal, NULL ); BuildImageCapWgtPanel( gbox ); BuildAudioCapWgtPanel( gbox ); gbox = XtVaCreateManagedWidget( "group2Box", boxWidgetClass, sbox, XtNorientation, XtorientHorizontal, NULL ); BuildImageEncWgtPanel( gbox ); BuildAudioEncWgtPanel( gbox ); /* Cleanup Temp Files toggle */ form = XtVaCreateManagedWidget( "cleanupForm", formWidgetClass, main2, XtNorientation, XtorientHorizontal, NULL ); Cleanup_form = form; w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form, XtNjustify, XtJustifyLeft, XtNlabel, " ", XtNresize, False, XtNencoding, XawTextEncoding8bit, XtNwidth, 250, NULL ); w = XtVaCreateManagedWidget( "cleanupToggle", toggleWidgetClass, form, XtNlabel, " ", XtNfromHoriz, w, NULL ); Cleanup_wgt = w; w = XtVaCreateManagedWidget( "cleanupLabel", labelWidgetClass, form, XtNresize, False, XtNfromHoriz, w, XtNencoding, XawTextEncoding8bit, XtNjustify, XtJustifyLeft, NULL ); /* Action button form */ form = XtVaCreateManagedWidget( "actionForm", formWidgetClass, main1, XtNfromVert, gbox, NULL ); w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form, XtNjustify, XtJustifyLeft, XtNlabel, " ", XtNresize, False, XtNencoding, XawTextEncoding8bit, XtNwidth, 200, NULL ); w = XtVaCreateManagedWidget( "recordCmd", commandWidgetClass, form, XtNfromHoriz, w, NULL ); XtAddCallback( w, XtNcallback, RecordCmdCB, "record" ); Record_btn = w; w = XtVaCreateManagedWidget( "stopCmd", commandWidgetClass, form, XtNfromHoriz, w, NULL ); XtAddCallback( w, XtNcallback, StopCmdCB, NULL ); Stop_btn = w; w = XtVaCreateManagedWidget( "dismissCmd", commandWidgetClass, form, XtNfromHoriz, w, NULL ); XtAddCallback( w, XtNcallback, DismissCmdCB, NULL ); Dismiss_btn = w; } void TVVIDSAVDIALOGPopUp() { /* Do dialog */ if ( Dialog_wgt == NULL ) TVVIDSAVDialogBuild( &Dialog_wgt ); TVVIDSAVDIALOGResync(); XUTILXtPopup( Dialog_wgt, XtGrabNone, TVTOPLEVEL ); } void TVVIDSAVDIALOGResync() { TV_DISK *d = &G_glob.disk; char str[80]; /* FIXME: Also install EnterNotify handler for this dialog to */ /* resync values on entry of it's shell. */ if ( Dialog_wgt == NULL ) return; /* Set text fields to current settings */ TextValUpdate( Fname_text, d->fn_video_base ); sprintf( str, "%ldx%ld", d->video.geom.w, d->video.geom.h ); TextValUpdate( Res_text, str ); sprintf( str, "%ld", d->video.fps ); TextValUpdate( FPS_text, str ); /* Set selections based on active format */ SetMenuSelection( Ffmt_menu_btn, d->audio.file_fmt ); SetMenuSelection( Sfmt_menu_btn, d->audio.sample_fmt ); SetMenuSelection( Chan_menu_btn, d->audio.stereo ); SetMenuSelection( Rate_menu_btn, d->audio.sample_rate ); SetMenuSelection( Icap_fmt_menu_btn , d->video.icap_fmt ); SetMenuSelection( Ifile_fmt_menu_btn, d->freeze_fmt ); SetMenuSelection( Vtrg_menu_btn, d->video.target ); XtVaSetValues( Cleanup_wgt , XtNstate, d->video.cleanup_temp, NULL); Audio_enabled = d->video.cap_audio && App_res.do_audio; XtVaSetValues( Audio_cap_wgt, XtNstate, Audio_enabled , NULL ); XtSetSensitive( Audio_cap_mgr, d->video.cap_audio ); XtSetSensitive( Audio_enc_mgr, d->video.cap_audio ); /* Set button sensitivites */ UpdateButtons(); } /**@BEGINFUNC************************************************************** Prototype : void TVVIDSAVDIALOGNewFrameHdlr( TV_IMAGE *img ) Purpose : Called to handle a new frame when we're capturing video frames to disk. Programmer : 31-May-97 Randall Hopper Parameters : img - I: captured image Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVVIDSAVDIALOGNewFrameHdlr( TV_IMAGE *img ) { static long last_time, /* FIXME */ last_save_time, in_a_row = 0; TV_RAW_IMAGE_HEADER head; /* This shouldn't happen */ if ( !Recording ) return; /* FIXME: Remove this */ #ifdef COPY_FRAME TV_IMAGE tmp_img; static TV_UINT8 *buf = NULL; static TV_INT32 buf_size = 0; TV_INT32 bytes; /* Alloc mem for tmp copy of image -- FIXME hack */ /* We do this to avoid the driver stomping on the image */ /* before we've written it. */ /* Fixme: modify the driver for multiple staging buffers */ /* we shouldn't have to do this copy */ memcpy( &tmp_img, img, sizeof( tmp_img ) ); bytes = TVRAWVIDEOCalcImageSize( &tmp_img ); if (( buf == NULL ) || ( bytes != buf_size )) { free( buf ); if ( (buf = malloc( bytes )) == NULL ) TVUTILOutOfMemory(); buf_size = bytes; } memcpy( buf, img->buf, buf_size ); tmp_img.buf = buf; # define img (&tmp_img) #endif /* If this is the first image, write a header block */ if ( First_image && !TVRAWVIDEOHeaderWrite( Out_rf, img, &Snd ) ) { fprintf( stderr, "TVVIDSAVDIALOGNewFrameHdlr: header disk write failed\n" ); Recording = FALSE; return; } /* Take note of elapsed time since the last frame so we know how */ /* to temporally space resulting images in the video stream */ { struct timeval tv; long new_time; long diff; gettimeofday( &tv, NULL ); new_time = (tv.tv_sec & 0xFFFF) * 1000000L + tv.tv_usec; if ( First_image ) { VDPRINTF(( "Frame Delays (in microseconds)\n" )); First_image = FALSE; head.delay = 0; in_a_row = 1; Vid_stats.frames = 0; Vid_stats.time_us = 0; last_time = last_save_time = new_time; } else { diff = new_time - last_time; head.delay = new_time - last_save_time; Vid_stats.time_us += diff; Vid_stats.frames++; last_time = new_time; if ( /*++*/in_a_row < 2 ) last_save_time = new_time; else in_a_row = 0; VDPRINTF(("%4ld: Delay = %7ld us (Avg = %7ld ms, FPS = %2ld)%s\n", Vid_stats.frames, diff, Vid_stats.time_us/Vid_stats.frames/1000, Vid_stats.frames*1000000L/Vid_stats.time_us, (last_time == last_save_time) ? "" : "...Skipped" )); } } /* FIXME: Maybe check that img w,h,Bpp stay same between images */ if ( last_time == last_save_time ) { char buf[65536]; /* Read pending audio */ if ( Audio_enabled ) { #ifdef OLD audio_buf_info info; ioctl( Dsp_fd, SNDCTL_DSP_GETISPACE, &info ); VDPRINTF(( "Bufs = %d / %d, FragSize = %d, TotalBytes = %d\n", info.fragments, info.fragstotal, info.fragsize, info.bytes )); #endif Snd.bytes = read( Dsp_fd, buf, 65536 ); if ( Snd.bytes == -1 ) Snd.bytes = 0; Snd.buf = buf; /* FIXME: Deal with case where more than 64k may be ready */ /* Also deal with case where more sound data may become */ /* available while waiting for a frame than can fit in sound */ /* buffers. */ } if ( !TVRAWVIDEOImageWrite( Out_rf, &head, img, &Snd ) ) { fprintf( stderr, "TVVIDSAVDIALOGNewFrameHdlr: image disk write failed\n" ); Recording = FALSE; return; } if ( Audio_enabled ) Snd.buf = NULL, Snd.bytes = 0; } /* If we're optimizing, we're done after X num of images */ if ( Optimizing && ( Vid_stats.frames >= OPTIMIZE_NUM_FRAMES ) ) { Optimizing = False; XtPopdown ( Wait_dialog ); XtUnrealizeWidget( Wait_dialog ); /* Wake up optimize Xt loop */ } } /**@BEGINFUNC************************************************************** Prototype : void TVVIDSAVDIALOGRecordStart( TV_INT32 w, TV_INT32 h ) Purpose : Start recording video using the default video capture parameters. Programmer : 31-May-98 Randall Hopper Parameters : w,h - Suggested resolution (e.g. 320x240) (if 0, use default resolution) Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVVIDSAVDIALOGRecordStart( TV_INT32 w, TV_INT32 h ) { char *dir, *file; TV_BOOL alloc = TRUE; TV_DISK *d = &G_glob.disk; char res_str[20]; /* FIXME: Really, we need to separate this save functionality out */ /* from the user interface. */ /* Thus the bogusness of setting the values on the dialog if it */ /* exists. */ /* If no filename, generate one at random. */ if ( d->fn_video_base[0] == '\0' ){ if ( (dir = getcwd( NULL, 0 )) == NULL ) { dir = "/tmp"; alloc = FALSE; } unsetenv( "TMPDIR" ); file = tempnam( dir, "Fxtv-video." ); if ( alloc ) free( dir ); dir = NULL; d->fn_video_base[0] = '\0'; strncat( d->fn_video_base, file, sizeof(d->fn_video_base)-1 ); free(file); if ( Fname_text ) TextValUpdate( Fname_text, d->fn_video_base ); } if (( w > 0 ) && ( h > 0 )) { d->video.geom.w = w; d->video.geom.h = h; if ( Res_text ) { sprintf( res_str, "%ldx%ld", w,h ); TextValUpdate( Res_text, res_str ); } } RecordCmdCB( NULL, "record", NULL ); } /**@BEGINFUNC************************************************************** Prototype : void TVVIDSAVDIALOGRecordStop( void ) Purpose : Stop recording video. Programmer : 31-May-98 Randall Hopper Parameters : None. Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ void TVVIDSAVDIALOGRecordStop( void ) { StopCmdCB( NULL, NULL, NULL ); /* This is a hack. We want the record event loop to "wake up" and */ /* loop. It doesn't when just any event comes in (timer, file */ /* descriptor activity, because apparently some events are processed */ /* internally by XtAppNextEvent. This is bad. Of course, our */ /* having internal X event loop is bad too but anyway. */ /* So we send ourselves a dummy client message event. This magically */ /* XtAppNextEvent to return, so we can test for loop termination */ /* conditions and get on with life. */ { static Atom xa_FXTV_WAKEUP_NEXTEVENT = None; XClientMessageEvent ev; if ( xa_FXTV_WAKEUP_NEXTEVENT == None ) xa_FXTV_WAKEUP_NEXTEVENT = XInternAtom(TVDISPLAY, "FXTV_WAKEUP_NEXTEVENT", False); ev.type = ClientMessage; ev.display = TVDISPLAY; ev.message_type = xa_FXTV_WAKEUP_NEXTEVENT; ev.format = 8; sprintf( ev.data.b, "Hey, wake up!" ); ev.window = XtWindow(TVTOPLEVEL); XSendEvent( TVDISPLAY, XtWindow(TVTOPLEVEL), True, NoEventMask, (XEvent *) &ev ); XFlush (TVDISPLAY); } }