/* * batch_mode.c * * Routines to handle batch mode operation. * * (C) 1998 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 "tvtypes.h" #include "rawvideo.h" #include "imgsav.h" #include "glob.h" /* ******************** Local defines ************** */ /*#define DEBUGGING*/ #define FRAME_USEC(fps) (fps <= 0.0 ? 0.0 : (1000000L /(fps))) typedef struct { char stream_input[4][80]; TV_INT32 stream_num_input; TV_INT32 stream_fps; char *frame_fmt; char *video_target; char *audio_target; } TV_BATCH_PARM; /* ******************** Private variables ************** */ static TV_BOOL Got_signal = FALSE; /* ******************** Forward declarations ************** */ /* ******************** Function Definitions ************** */ /* TVIntrHdlr - Called when we receive interrupts. Queue the last one */ /* and process it next time we get back to the work proc. */ /**@BEGINFUNC************************************************************** Prototype : static void TVBatchIntrHdlr( int sig ) Purpose : Called when we receive an INT or TERM signal. We just set a state variable here, and check it during long processing. Programmer : 21-May-98 Randall Hopper Parameters : sig - I: signal that occurred Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void TVBatchIntrHdlr( int sig ) { Got_signal = TRUE; exit(1); } /**@BEGINFUNC************************************************************** Prototype : static void DoStreamVideo( TV_BATCH_PARM *parm ) Purpose : Called in batch mode to stream video in a particular format to stdout. Programmer : 15-Jan-98 Randall Hopper Parameters : parm - I: batch parameters Returns : None. Globals : None. **@ENDFUNC*****************************************************************/ static void DoStreamVideo( TV_BATCH_PARM *parm ) { TV_STILL_FMT fmt; char *raw_files[4] = { parm->stream_input[0], parm->stream_input[1], parm->stream_input[2], parm->stream_input[3] }, *frame_ext = NULL; TV_RAW_VIDEO_FILE *rf; TV_RAW_IMAGE_HEADER head; TV_RAW_IMAGE img; TV_RAW_SOUND snd; TV_BOOL eof, video2stdout, audio2stdout; size_t bytes; TV_INT32 write_cnt, i, frame_no, capture_time, mpeg_time, delta, mpeg_frame_time = FRAME_USEC( parm->stream_fps ); int aud_fd = -1; /* Parse parms */ if (( parm->frame_fmt == NULL ) || ( parm->stream_num_input == 0 )) { fprintf( stderr, "Missing streamformat and/or streaminput.\n" ); exit(1); } if (( parm->video_target == NULL ) && ( parm->audio_target == NULL )) { fprintf( stderr, "Neither videotarget or audiotarget specified\n" ); exit(1); } video2stdout = (parm->video_target && STREQ( parm->video_target, "-" )); audio2stdout = (parm->audio_target && STREQ( parm->audio_target, "-" )); if ( video2stdout && audio2stdout ) { fprintf( stderr, "Video and audio can't both be sent to stdout\n" ); exit(1); } if ( STREQ( parm->frame_fmt, "TIFF" ) ) fmt = TV_STILL_FMT_TIFF; else if ( STREQ( parm->frame_fmt, "PPM" ) ) fmt = TV_STILL_FMT_PPM; else if ( STREQ( parm->frame_fmt, "YUV" ) ) fmt = TV_STILL_FMT_YUV; else { fprintf( stderr, "Bad frameformat: %s\n", parm->frame_fmt ); exit(1); } /* Sanity check target with frame format */ if ( ( fmt != TV_STILL_FMT_PPM ) && ( fmt != TV_STILL_FMT_YUV ) && video2stdout ) { fprintf( stderr, "Can't send this frame format to stdout: %s\n", parm->frame_fmt ); exit(1); } /* Derive image file extension */ if ( parm->video_target && !video2stdout ) switch ( fmt ) { case TV_STILL_FMT_TIFF : frame_ext = "tif"; break; case TV_STILL_FMT_PPM : frame_ext = "ppm"; break; case TV_STILL_FMT_YUV : frame_ext = "yuv"; break; default: abort(); } /* Open the raw input file(s) */ if ( !TVRAWVIDEOOpen( raw_files, parm->stream_num_input, TRUE, &rf ) ) { fprintf( stderr, "Failed to open input file(s)\n" ); exit(1); } /* Prepare the audio output filedesc */ if ( parm->audio_target ) if ( audio2stdout ) aud_fd = 1; else if ( (aud_fd = open( parm->audio_target, O_CREAT|O_WRONLY|O_TRUNC, 0666 )) < 0 ) { fprintf( stderr, "Failed to open output raw audio file: %s\n", parm->audio_target ); exit(1); } /* Read header */ if ( !TVRAWVIDEOHeaderRead( rf, &img, &snd, &eof ) ) { fprintf( stderr, "Failed reading raw capture file\n" ); exit(1); } /* If no frames, we're done */ if ( eof ) return; /* Sanity check raw data with save type */ if ( ((( fmt == TV_STILL_FMT_TIFF ) || ( fmt == TV_STILL_FMT_PPM )) && ( img.pix_geom.type != TV_PIXELTYPE_RGB )) || (( fmt == TV_STILL_FMT_YUV ) && ( img.pix_geom.type != TV_PIXELTYPE_YUV )) ) { fprintf( stderr, "Sorry, that stream format is not supported with " "the type of this raw capture data.\n" ); exit(1); } /* Allocate image frame buffer */ bytes = TVRAWVIDEOCalcImageSize( &img ); if ( (img.buf = malloc( bytes )) == NULL ) TVUTILOutOfMemory(); frame_no = 1; capture_time = mpeg_time = 0; while ( !eof ) { char img_last [ MAXPATHLEN ]; if ( !TVRAWVIDEOImageRead( rf, &head, &img, &snd, &eof ) ) { fprintf( stderr, "Failed reading raw image file\n" ); exit(1); } if ( eof ) break; /* Write any audio stored with the frame to the RAW audio file */ if ( snd.bytes ) { if ( parm->audio_target ) write( aud_fd, snd.buf, snd.bytes ); free( snd.buf ); snd.buf = NULL; } #ifdef DEBUGGING fprintf( stderr, "FRAME_USEC = %ld\n", mpeg_frame_time ); #endif if ( parm->video_target ) { if ( parm->stream_fps <= 0 ) write_cnt = 1; else { /* Figure out whether to drop frame or write multiple times */ capture_time += head.delay; delta = capture_time - mpeg_time; if ( delta < -0.5 * mpeg_frame_time ) /* We're ahead of the clock; drop this frame */ write_cnt = 0; else for ( write_cnt = 0; delta >= -0.5 * mpeg_frame_time; write_cnt++ ) { mpeg_time += mpeg_frame_time; delta = capture_time - mpeg_time; } #ifdef DEBUGGING fprintf( stderr, "-- delay =%6ld, capture =%7ld, mpeg =%7ld, " "delta =%7ld, write_cnt = %ld\n", head.delay, capture_time, mpeg_time, delta, write_cnt ); #else /* Avoid overflow */ mpeg_time -= capture_time; capture_time = 0; #endif /* FIXME: Rezero action/mpeg after each frame */ } /* Consider installing write buffer on stdout */ for ( i = 0; i < write_cnt; i++ ) { char img_fname[ MAXPATHLEN ]; if ( video2stdout ) TVIMGSAVDoSave( NULL, fmt, &img ); else { char suffix[80]; sprintf( suffix, ".%.5ld.%s", frame_no++, frame_ext ); sprintf( img_fname, parm->video_target, suffix ); if ( i == 0 ) { TVIMGSAVDoSave( img_fname, fmt, &img ); strcpy( img_last, img_fname ); } else link( img_last, img_fname ); } } } } TVRAWVIDEOClose( &rf ); free( img.buf ); if ( parm->audio_target && !audio2stdout ) close( aud_fd ); } /**@BEGINFUNC************************************************************** Prototype : TV_BOOL DoBatchMode( int argc, char *argv[] ) Purpose : This mode is called before we do anything GUI related to see if this is a Batch Mode run, and if so, to dispatch the appropriate behavior. Programmer : 15-Jan-98 Randall Hopper Parameters : argc - main() argc argv - main() argv Returns : TRUE - This was a batch mode run, and we're done with it FALSE - This isn't a Batch Mode run; nothing done yet Globals : Batch_parm **@ENDFUNC*****************************************************************/ TV_BOOL DoBatchMode( int argc, char *argv[] ) { TV_BOOL batch_mode = FALSE; int i; char *mode = NULL, *stream_input = NULL, *p, *tok; TV_BATCH_PARM parm; memset( &parm, '\0', sizeof(parm) ); /* Was batch mode requested? */ batch_mode = (( argc >= 2 ) && STREQ( argv[1], "-batch" )); if ( !batch_mode ) return FALSE; if ( argc == 2 ) { fprintf( stderr, "No batch mode specified after -batch\n" ); exit(1); } mode = argv[2]; /* Yes. Parse args */ for ( i = 3; i < argc; i++ ) { if ( STREQ( argv[i], "-frameformat" ) ) { if ( i+1 >= argc ) { fprintf( stderr, "No format after -frameformat\n" ); exit(1); } parm.frame_fmt = argv[++i]; } else if ( STREQ( argv[i], "-streaminput" ) ) { if ( i+1 >= argc ) { fprintf( stderr, "No input filelist after -streaminput\n" ); exit(1); } stream_input = argv[++i]; } else if ( STREQ( argv[i], "-streamfps" ) ) { if ( i+1 >= argc ) { fprintf( stderr, "No FPS after -streamfps\n" ); exit(1); } parm.stream_fps = atoi( argv[++i] ); } else if ( STREQ( argv[i], "-videotarget" ) ) { if ( i+1 >= argc ) { fprintf( stderr, "No file/stream after -videotarget\n" ); exit(1); } parm.video_target = argv[++i]; } else if ( STREQ( argv[i], "-audiotarget" ) ) { if ( i+1 >= argc ) { fprintf( stderr, "No file/stream after -audiotarget\n" ); exit(1); } parm.audio_target = argv[++i]; } } /* Set up cleanup signal handler */ signal( SIGHUP , TVBatchIntrHdlr ); signal( SIGINT , TVBatchIntrHdlr ); signal( SIGQUIT, TVBatchIntrHdlr ); signal( SIGPIPE, TVBatchIntrHdlr ); signal( SIGTERM, TVBatchIntrHdlr ); i = 0; if ( stream_input ) for ( i=0, p = stream_input; (tok = strsep( &p, "," )) != NULL; ) { if ( *tok == '\0' ) continue; strncat( parm.stream_input[i++], tok, sizeof(parm.stream_input[0]) ); } parm.stream_num_input = i; if ( STREQ( mode, "streamavcap" ) ) DoStreamVideo( &parm ); else { fprintf( stderr, "Unknown batch mode: %s\n", mode ); exit(1); } return TRUE; }