1 /*
2 * batch_mode.c
3 *
4 * Routines to handle batch mode operation.
5 *
6 * (C) 1998 Randall Hopper
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met: 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 2.
12 * Redistributions in binary form must reproduce the above copyright notice,
13 * this list of conditions and the following disclaimer in the documentation
14 * and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30 /* ******************** Include Files ************** */
31
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include "tvtypes.h"
38 #include "rawvideo.h"
39 #include "imgsav.h"
40 #include "glob.h"
41
42 /* ******************** Local defines ************** */
43
44 /*#define DEBUGGING*/
45
46 #define FRAME_USEC(fps) (fps <= 0.0 ? 0.0 : (1000000L /(fps)))
47
48 typedef struct {
49 char stream_input[4][80];
50 TV_INT32 stream_num_input;
51 TV_INT32 stream_fps;
52 char *frame_fmt;
53 char *video_target;
54 char *audio_target;
55 } TV_BATCH_PARM;
56
57
58 /* ******************** Private variables ************** */
59
60 static TV_BOOL Got_signal = FALSE;
61
62 /* ******************** Forward declarations ************** */
63 /* ******************** Function Definitions ************** */
64
65 /* TVIntrHdlr - Called when we receive interrupts. Queue the last one */
66 /* and process it next time we get back to the work proc. */
67
68 /**@BEGINFUNC**************************************************************
69
70 Prototype : static void TVBatchIntrHdlr(
71 int sig )
72
73 Purpose : Called when we receive an INT or TERM signal. We
74 just set a state variable here, and check it during
75 long processing.
76
77 Programmer : 21-May-98 Randall Hopper
78
79 Parameters : sig - I: signal that occurred
80
81 Returns : None.
82
83 Globals : None.
84
85 **@ENDFUNC*****************************************************************/
86
TVBatchIntrHdlr(int sig)87 static void TVBatchIntrHdlr( int sig )
88 {
89 Got_signal = TRUE;
90 exit(1);
91 }
92
93
94 /**@BEGINFUNC**************************************************************
95
96 Prototype : static void DoStreamVideo(
97 TV_BATCH_PARM *parm )
98
99 Purpose : Called in batch mode to stream video in a particular format
100 to stdout.
101
102 Programmer : 15-Jan-98 Randall Hopper
103
104 Parameters : parm - I: batch parameters
105
106 Returns : None.
107
108 Globals : None.
109
110 **@ENDFUNC*****************************************************************/
111
DoStreamVideo(TV_BATCH_PARM * parm)112 static void DoStreamVideo( TV_BATCH_PARM *parm )
113 {
114 TV_STILL_FMT fmt;
115 char *raw_files[4] = { parm->stream_input[0],
116 parm->stream_input[1],
117 parm->stream_input[2],
118 parm->stream_input[3] },
119 *frame_ext = NULL;
120 TV_RAW_VIDEO_FILE *rf;
121 TV_RAW_IMAGE_HEADER head;
122 TV_RAW_IMAGE img;
123 TV_RAW_SOUND snd;
124 TV_BOOL eof,
125 video2stdout,
126 audio2stdout;
127 size_t bytes;
128 TV_INT32 write_cnt,
129 i,
130 frame_no,
131 capture_time,
132 mpeg_time,
133 delta,
134 mpeg_frame_time = FRAME_USEC( parm->stream_fps );
135 int aud_fd = -1;
136
137 /* Parse parms */
138 if (( parm->frame_fmt == NULL ) || ( parm->stream_num_input == 0 )) {
139 fprintf( stderr, "Missing streamformat and/or streaminput.\n" );
140 exit(1);
141 }
142 if (( parm->video_target == NULL ) && ( parm->audio_target == NULL )) {
143 fprintf( stderr, "Neither videotarget or audiotarget specified\n" );
144 exit(1);
145 }
146
147 video2stdout = (parm->video_target && STREQ( parm->video_target, "-" ));
148 audio2stdout = (parm->audio_target && STREQ( parm->audio_target, "-" ));
149
150 if ( video2stdout && audio2stdout ) {
151 fprintf( stderr, "Video and audio can't both be sent to stdout\n" );
152 exit(1);
153 }
154
155 if ( STREQ( parm->frame_fmt, "TIFF" ) )
156 fmt = TV_STILL_FMT_TIFF;
157 else if ( STREQ( parm->frame_fmt, "PPM" ) )
158 fmt = TV_STILL_FMT_PPM;
159 else if ( STREQ( parm->frame_fmt, "YUV" ) )
160 fmt = TV_STILL_FMT_YUV;
161 else {
162 fprintf( stderr, "Bad frameformat: %s\n", parm->frame_fmt );
163 exit(1);
164 }
165
166 /* Sanity check target with frame format */
167 if ( ( fmt != TV_STILL_FMT_PPM ) && ( fmt != TV_STILL_FMT_YUV ) &&
168 video2stdout ) {
169 fprintf( stderr, "Can't send this frame format to stdout: %s\n",
170 parm->frame_fmt );
171 exit(1);
172 }
173
174 /* Derive image file extension */
175 if ( parm->video_target && !video2stdout )
176 switch ( fmt ) {
177 case TV_STILL_FMT_TIFF : frame_ext = "tif"; break;
178 case TV_STILL_FMT_PPM : frame_ext = "ppm"; break;
179 case TV_STILL_FMT_YUV : frame_ext = "yuv"; break;
180 default: abort();
181 }
182
183 /* Open the raw input file(s) */
184 if ( !TVRAWVIDEOOpen( raw_files, parm->stream_num_input, TRUE, &rf ) ) {
185 fprintf( stderr, "Failed to open input file(s)\n" );
186 exit(1);
187 }
188
189 /* Prepare the audio output filedesc */
190 if ( parm->audio_target )
191 if ( audio2stdout )
192 aud_fd = 1;
193 else
194 if ( (aud_fd = open( parm->audio_target, O_CREAT|O_WRONLY|O_TRUNC,
195 0666 )) < 0 ) {
196 fprintf( stderr, "Failed to open output raw audio file: %s\n",
197 parm->audio_target );
198 exit(1);
199 }
200
201 /* Read header */
202 if ( !TVRAWVIDEOHeaderRead( rf, &img, &snd, &eof ) ) {
203 fprintf( stderr, "Failed reading raw capture file\n" );
204 exit(1);
205 }
206
207 /* If no frames, we're done */
208 if ( eof )
209 return;
210
211 /* Sanity check raw data with save type */
212 if ( ((( fmt == TV_STILL_FMT_TIFF ) || ( fmt == TV_STILL_FMT_PPM )) &&
213 ( img.pix_geom.type != TV_PIXELTYPE_RGB )) ||
214 (( fmt == TV_STILL_FMT_YUV ) &&
215 ( img.pix_geom.type != TV_PIXELTYPE_YUV )) ) {
216 fprintf( stderr, "Sorry, that stream format is not supported with "
217 "the type of this raw capture data.\n" );
218 exit(1);
219 }
220
221 /* Allocate image frame buffer */
222 bytes = TVRAWVIDEOCalcImageSize( &img );
223 if ( (img.buf = malloc( bytes )) == NULL )
224 TVUTILOutOfMemory();
225
226 frame_no = 1;
227 capture_time = mpeg_time = 0;
228 while ( !eof ) {
229 char img_last [ MAXPATHLEN ];
230
231 if ( !TVRAWVIDEOImageRead( rf, &head, &img, &snd, &eof ) ) {
232 fprintf( stderr, "Failed reading raw image file\n" );
233 exit(1);
234 }
235 if ( eof )
236 break;
237
238 /* Write any audio stored with the frame to the RAW audio file */
239 if ( snd.bytes ) {
240 if ( parm->audio_target )
241 write( aud_fd, snd.buf, snd.bytes );
242 free( snd.buf );
243 snd.buf = NULL;
244 }
245
246 #ifdef DEBUGGING
247 fprintf( stderr, "FRAME_USEC = %ld\n", mpeg_frame_time );
248 #endif
249 if ( parm->video_target ) {
250 if ( parm->stream_fps <= 0 )
251 write_cnt = 1;
252 else {
253
254 /* Figure out whether to drop frame or write multiple times */
255 capture_time += head.delay;
256 delta = capture_time - mpeg_time;
257
258 if ( delta < -0.5 * mpeg_frame_time )
259 /* We're ahead of the clock; drop this frame */
260 write_cnt = 0;
261
262 else
263 for ( write_cnt = 0; delta >= -0.5 * mpeg_frame_time;
264 write_cnt++ ) {
265 mpeg_time += mpeg_frame_time;
266 delta = capture_time - mpeg_time;
267 }
268 #ifdef DEBUGGING
269 fprintf( stderr, "-- delay =%6ld, capture =%7ld, mpeg =%7ld, "
270 "delta =%7ld, write_cnt = %ld\n",
271 head.delay, capture_time, mpeg_time, delta,
272 write_cnt );
273 #else
274 /* Avoid overflow */
275 mpeg_time -= capture_time;
276 capture_time = 0;
277 #endif
278
279 /* FIXME: Rezero action/mpeg after each frame */
280 }
281
282 /* Consider installing write buffer on stdout */
283 for ( i = 0; i < write_cnt; i++ ) {
284 char img_fname[ MAXPATHLEN ];
285
286 if ( video2stdout )
287 TVIMGSAVDoSave( NULL, fmt, &img );
288 else {
289 char suffix[80];
290
291 sprintf( suffix, ".%.5ld.%s", frame_no++, frame_ext );
292
293 sprintf( img_fname, parm->video_target, suffix );
294 if ( i == 0 ) {
295 TVIMGSAVDoSave( img_fname, fmt, &img );
296 strcpy( img_last, img_fname );
297 }
298 else
299 link( img_last, img_fname );
300 }
301 }
302 }
303 }
304
305 TVRAWVIDEOClose( &rf );
306 free( img.buf );
307
308 if ( parm->audio_target && !audio2stdout )
309 close( aud_fd );
310 }
311
312
313 /**@BEGINFUNC**************************************************************
314
315 Prototype : TV_BOOL DoBatchMode(
316 int argc,
317 char *argv[] )
318
319 Purpose : This mode is called before we do anything GUI related to
320 see if this is a Batch Mode run, and if so, to dispatch
321 the appropriate behavior.
322
323 Programmer : 15-Jan-98 Randall Hopper
324
325 Parameters : argc - main() argc
326 argv - main() argv
327
328 Returns : TRUE - This was a batch mode run, and we're done with it
329 FALSE - This isn't a Batch Mode run; nothing done yet
330
331 Globals : Batch_parm
332
333 **@ENDFUNC*****************************************************************/
334
DoBatchMode(int argc,char * argv[])335 TV_BOOL DoBatchMode( int argc, char *argv[] )
336 {
337 TV_BOOL batch_mode = FALSE;
338 int i;
339 char *mode = NULL,
340 *stream_input = NULL,
341 *p,
342 *tok;
343 TV_BATCH_PARM parm;
344
345 memset( &parm, '\0', sizeof(parm) );
346
347 /* Was batch mode requested? */
348 batch_mode = (( argc >= 2 ) && STREQ( argv[1], "-batch" ));
349 if ( !batch_mode )
350 return FALSE;
351
352 if ( argc == 2 ) {
353 fprintf( stderr, "No batch mode specified after -batch\n" );
354 exit(1);
355 }
356 mode = argv[2];
357
358 /* Yes. Parse args */
359 for ( i = 3; i < argc; i++ ) {
360 if ( STREQ( argv[i], "-frameformat" ) ) {
361 if ( i+1 >= argc ) {
362 fprintf( stderr, "No format after -frameformat\n" );
363 exit(1);
364 }
365 parm.frame_fmt = argv[++i];
366 }
367 else if ( STREQ( argv[i], "-streaminput" ) ) {
368 if ( i+1 >= argc ) {
369 fprintf( stderr, "No input filelist after -streaminput\n" );
370 exit(1);
371 }
372 stream_input = argv[++i];
373 }
374 else if ( STREQ( argv[i], "-streamfps" ) ) {
375 if ( i+1 >= argc ) {
376 fprintf( stderr, "No FPS after -streamfps\n" );
377 exit(1);
378 }
379 parm.stream_fps = atoi( argv[++i] );
380 }
381 else if ( STREQ( argv[i], "-videotarget" ) ) {
382 if ( i+1 >= argc ) {
383 fprintf( stderr, "No file/stream after -videotarget\n" );
384 exit(1);
385 }
386 parm.video_target = argv[++i];
387 }
388 else if ( STREQ( argv[i], "-audiotarget" ) ) {
389 if ( i+1 >= argc ) {
390 fprintf( stderr, "No file/stream after -audiotarget\n" );
391 exit(1);
392 }
393 parm.audio_target = argv[++i];
394 }
395 }
396
397 /* Set up cleanup signal handler */
398 signal( SIGHUP , TVBatchIntrHdlr );
399 signal( SIGINT , TVBatchIntrHdlr );
400 signal( SIGQUIT, TVBatchIntrHdlr );
401 signal( SIGPIPE, TVBatchIntrHdlr );
402 signal( SIGTERM, TVBatchIntrHdlr );
403
404 i = 0;
405 if ( stream_input )
406 for ( i=0, p = stream_input; (tok = strsep( &p, "," )) != NULL; ) {
407 if ( *tok == '\0' )
408 continue;
409 strncat( parm.stream_input[i++], tok,
410 sizeof(parm.stream_input[0]) );
411 }
412 parm.stream_num_input = i;
413
414 if ( STREQ( mode, "streamavcap" ) )
415 DoStreamVideo( &parm );
416 else {
417 fprintf( stderr, "Unknown batch mode: %s\n", mode );
418 exit(1);
419 }
420
421 return TRUE;
422 }
423