1 /*
2 * vidsav_dlg.c
3 *
4 * Code for the video save control dialog
5 *
6 * (C) 1997 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 <stdlib.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <signal.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/uio.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include "voxware.h"
44 #include <X11/Xatom.h>
45 #include <X11/Intrinsic.h>
46 #include <X11/StringDefs.h>
47 #include <X11/Shell.h>
48 #include <X11/Xaw/AsciiText.h>
49 #include <X11/Xaw/Box.h>
50 #include <X11/Xaw/Command.h>
51 #include <X11/Xaw/Label.h>
52 #include <X11/Xaw/AsciiText.h>
53 #include <X11/Xaw/Form.h>
54 #include <X11/Xaw/MenuButton.h>
55 #include <X11/Xaw/SimpleMenu.h>
56 #include <X11/Xaw/SmeBSB.h>
57 #include <X11/Xaw/SmeLine.h>
58 #include <X11/Xaw/Toggle.h>
59 #include "tvdefines.h"
60 #include "glob.h"
61 #include "actions.h"
62 #include "xutil.h"
63 #include "tvutil.h"
64 #include "rawvideo.h"
65 #include "audsav_dlg.h"
66 #include "imgsav_dlg.h"
67 #include "vidsav_dlg.h"
68 #include "app_rsrc.h"
69
70 /* ******************** Local defines ************** */
71
72 #define AV_RAWNAME_FMT "%s.AVraw"
73 #define SCRIPT_FNAME_FMT "%s%s.sh"
74
75 #define OPTIMIZE_NUM_FRAMES 200
76
77 typedef struct {
78 char fname_base[ MAXPATHLEN ]; /* prefix of gen'ed files */
79 TV_BOOL audio_enabled; /* audio was captured */
80 TV_BOOL target_mpeg; /* T = MPEG target; F = imgs */
81 TV_BOOL streaming; /* T = stream to mpeg_encode */
82 TV_SOUND_FMT snd_fmt; /* desired sound format */
83 TV_GEOM geom; /* captured image geometry */
84 TV_PIXEL_GEOM pix_geom; /* captured image format */
85 TV_ICAP_FMT img_cap_fmt; /* captured image format */
86 TV_STILL_FMT img_sav_fmt; /* desired image save format */
87 char raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ];
88 TV_INT32 num_raw_fnames; /* where Raw AV data captured */
89 TV_UINT32 fps; /* FPS we captured at */
90 TV_UINT32 fps_max; /* Max FPS for this signal */
91 TV_BOOL cleanup_tmp_files; /* T = cleanup; F = don't */
92 char script_fname[ MAXPATHLEN ]; /* Name of conversion script */
93 } TV_CAP_PARM;
94
95 typedef struct {
96 TV_ICAP_FMT fmt;
97 char *file_ext;
98 char *wgt_name;
99 Widget wgt;
100 } TV_ICAPFMT_ITEM_DEF;
101
102 typedef struct {
103 TV_VIDEO_TARGET fmt;
104 char *wgt_name;
105 Widget wgt;
106 } TV_VTRG_ITEM_DEF;
107
108 typedef struct {
109 TV_INT32 frames;
110 TV_INT32 time_us;
111 } TV_VID_STATS;
112
113 typedef struct {
114 char **cmd; /* Cmd running and its args */
115 char *tmpstr;
116 Widget dialog_shell;
117 char raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ];
118 } TV_CNVT_CMD_STATE;
119
120
121 /* "mpeg_encode" param file template */
122 #define ENCODE_SCRIPT_HEADER "#!/bin/sh\n\
123 # \n\
124 # %s\n\
125 # Fxtv Video/System Stream Conversion Script\n\
126 # \n\
127 # Automatically generated by BSD X TV v" VERS_STR "\n\
128 # \n\
129 \n\
130 "
131
132 #define YUV_PIX_GEOM_MATCH(a,b) \
133 ( !memcmp( (a)->samp_size ,(b)->samp_size ,sizeof((a)->samp_size) ) && \
134 !memcmp( (a)->samp_int_h,(b)->samp_int_h,sizeof((a)->samp_int_h) ) && \
135 !memcmp( (a)->samp_int_v,(b)->samp_int_v,sizeof((a)->samp_int_v) ) && \
136 (a)->frame_packing == (b)->frame_packing && \
137 !strcmp( (a)->comp_order, (b)->comp_order ) && \
138 (a)->order_t_to_b == (a)->order_t_to_b && \
139 (a)->order_l_to_r == (a)->order_l_to_r && \
140 (a)->y_trans == (a)->y_trans )
141
142 #define IMG_CAP_FMT_IS_RGB(icap) ( (icap) == TV_ICAP_FMT_RGB16 )
143 #define IMG_ENC_FMT_IS_RGB(ienc) ( (ienc) != TV_STILL_FMT_YUV )
144
145 /* ******************** Private variables ************** */
146
147 static TV_BOOL Audio_enabled;
148 static TV_AUDIO_FILE_FMT Sel_ffmt;
149 static TV_AUDIO_SAMPLE_FMT Sel_sfmt;
150 static TV_BOOL Sel_stereo;
151 static TV_UINT32 Sel_rate;
152 static TV_VIDEO_TARGET Sel_vtarg;
153 static TV_STILL_FMT Sel_ifilefmt;
154 static TV_ICAP_FMT Sel_icapfmt;
155 static TV_VID_STATS Vid_stats;
156
157 static Widget Dialog_wgt = NULL,
158 Main_wgt = NULL,
159 Audio_cap_mgr = NULL,
160 Audio_enc_mgr = NULL,
161 File_label = NULL,
162 Fname_text = NULL,
163 Res_text = NULL,
164 FPS_text = NULL,
165 Icap_fmt_menu_btn = NULL,
166 Ifile_fmt_menu_btn = NULL,
167 Vtrg_menu_btn = NULL,
168 Cleanup_form = NULL,
169 Cleanup_wgt = NULL,
170 Record_btn = NULL,
171 Stop_btn = NULL,
172 Dismiss_btn = NULL,
173 Wait_dialog = NULL,
174 Ffmt_menu_btn = NULL,
175 Sfmt_menu_btn = NULL,
176 Chan_menu_btn = NULL,
177 Rate_menu_btn = NULL,
178 Audio_cap_wgt = NULL;
179 static TV_BOOL Recording = FALSE,
180 Optimizing = FALSE,
181 First_image = TRUE;
182 static TV_RAW_VIDEO_FILE *Out_rf = NULL;
183 static TV_SOUND Snd;
184 static int Dsp_fd = -1;
185
186 /* FIXME: Copied from audsav_dlg.c - This needs abstracted better */
187 static TV_FFMT_ITEM_DEF Ffmt_item_def[] = {
188 { TV_AUDIO_FILE_FMT_RAW , "raw" , NULL },
189 { TV_AUDIO_FILE_FMT_SUNAU , "au" , "-t raw -U -b -c 1 -r 8012" },
190 { TV_AUDIO_FILE_FMT_WAV , "wav" , "-t wav" },
191 { TV_AUDIO_FILE_FMT_VOC , "voc" , "-t voc" },
192 { TV_AUDIO_FILE_FMT_AIFF , "aiff" , "-t aiff" },
193 { TV_AUDIO_FILE_FMT_MPEG2 , "mp2" , "-t aiff " }, /* precond */
194 { TV_AUDIO_FILE_FMT_MPEG3 , "mp3" , "-t aiff" }, /* precond */
195 };
196
197 static TV_SFMT_ITEM_DEF Sfmt_item_def[] = {
198 { TV_AUDIO_SAMPLE_FMT_MULAW_U8 , "mulawU8" , "-t raw -U -b" },
199 { TV_AUDIO_SAMPLE_FMT_LIN_S8 , "linS8" , "-t raw -s -b" },
200 { TV_AUDIO_SAMPLE_FMT_LIN_U8 , "linU8" , "-t raw -u -b" },
201 { TV_AUDIO_SAMPLE_FMT_LIN_S16_LE, "linS16LE" , "-t raw -s -w" },
202 { TV_AUDIO_SAMPLE_FMT_LIN_U16_LE, "linU16LE" , "-t raw -u -w" },
203 { TV_AUDIO_SAMPLE_FMT_LIN_S16_BE, "linS16BE" , "-t raw -s -w -x" },
204 { TV_AUDIO_SAMPLE_FMT_LIN_U16_BE, "linU16BE" , "-t raw -u -w -x" }
205 };
206
207 static TV_CHAN_ITEM_DEF Chan_item_def[] = {
208 { FALSE , "mono" },
209 { TRUE , "stereo" }
210 };
211
212 static TV_RATE_ITEM_DEF Rate_item_def[] = {
213 { 8012 , "r8012" },
214 { 11025 , "r11025" },
215 { 22050 , "r22050" },
216 { 44100 , "r44100" },
217 };
218
219 static TV_VTRG_ITEM_DEF Vtrg_item_def[] = {
220 #ifdef DEBUG
221 { TV_VIDEO_TARGET_RAW , "raw" },
222 #endif
223 { TV_VIDEO_TARGET_IMAGES , "images" },
224 { TV_VIDEO_TARGET_MPEG_READY , "mpegready" },
225 { TV_VIDEO_TARGET_MPEG , "mpeg" }
226
227 };
228
229 static TV_ICAPFMT_ITEM_DEF Icap_fmt_item_def[] = {
230 { TV_ICAP_FMT_RGB16 , NULL , "rgb16Cmd" },
231 { TV_ICAP_FMT_IYUV , "iyuv" , "iyuvCmd" },
232 { TV_ICAP_FMT_YUY2 , "yuy2" , "yuy2Cmd" },
233 { TV_ICAP_FMT_YUY2L , "yuy2l", "yuy2lCmd" }
234 };
235
236 static TV_IFILEFMT_ITEM_DEF Ifile_fmt_item_def[] = {
237 { TV_STILL_FMT_TIFF , "tiff", "tiffCmd" },
238 { TV_STILL_FMT_PPM , "ppm" , "ppmCmd" },
239 { TV_STILL_FMT_YUV , "yuv" , "yuvCmd" }
240 };
241
242 static TV_INT32 Vtrg_item_def_size = XtNumber( Vtrg_item_def ),
243 Ifile_fmt_item_def_size = XtNumber( Ifile_fmt_item_def ),
244 Icap_fmt_item_def_size = XtNumber( Icap_fmt_item_def ),
245 Ffmt_item_def_size = XtNumber( Ffmt_item_def ),
246 Sfmt_item_def_size = XtNumber( Sfmt_item_def ),
247 Chan_item_def_size = XtNumber( Chan_item_def ),
248 Rate_item_def_size = XtNumber( Rate_item_def );
249
250 /* ******************** Forward declarations ************** */
251
252 static void TVVIDSAVDialogBuild( Widget *dialog_wgt );
253
254 /* ******************** Function Definitions ************** */
255
256 /* SetMenuSelection: Set the active selection of the option menus */
SetMenuSelection(Widget menu_btn,TV_UINT32 choice)257 static void SetMenuSelection( Widget menu_btn, TV_UINT32 choice )
258 {
259 TV_INT32 i;
260 String label;
261
262 if ( menu_btn == Icap_fmt_menu_btn ) {
263 for ( i = 0; i < Icap_fmt_item_def_size; i++ )
264 if ( Icap_fmt_item_def[i].fmt == choice ) {
265 XtVaGetValues( Icap_fmt_item_def[i].wgt, XtNlabel, &label,
266 NULL );
267 XtVaSetValues( menu_btn, XtNlabel, label,
268 NULL );
269 break;
270 }
271 if ( i >= Icap_fmt_item_def_size ) {
272 fprintf( stderr,
273 "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
274 choice );
275 exit(1);
276 }
277 Sel_icapfmt = choice;
278 }
279 else if ( menu_btn == Ifile_fmt_menu_btn ) {
280 for ( i = 0; i < Ifile_fmt_item_def_size; i++ )
281 if ( Ifile_fmt_item_def[i].fmt == choice ) {
282 XtVaGetValues( Ifile_fmt_item_def[i].wgt, XtNlabel, &label,
283 NULL );
284 XtVaSetValues( menu_btn, XtNlabel, label,
285 NULL );
286 break;
287 }
288 if ( i >= Ifile_fmt_item_def_size ) {
289 fprintf( stderr,
290 "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
291 choice );
292 exit(1);
293 }
294 Sel_ifilefmt = choice;
295 }
296 else if ( menu_btn == Ffmt_menu_btn ) {
297 for ( i = 0; i < Ffmt_item_def_size; i++ )
298 if ( Ffmt_item_def[i].fmt == choice ) {
299 XtVaGetValues( Ffmt_item_def[i].wgt, XtNlabel, &label,
300 NULL );
301 XtVaSetValues( menu_btn, XtNlabel, label,
302 NULL );
303 break;
304 }
305 if ( i >= Ffmt_item_def_size ) {
306 fprintf( stderr,
307 "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
308 choice );
309 exit(1);
310 }
311 Sel_ffmt = choice;
312 }
313 else if ( menu_btn == Sfmt_menu_btn ) {
314 for ( i = 0; i < Sfmt_item_def_size; i++ )
315 if ( Sfmt_item_def[i].fmt == choice ) {
316 XtVaGetValues( Sfmt_item_def[i].wgt, XtNlabel, &label,
317 NULL );
318 XtVaSetValues( menu_btn, XtNlabel, label,
319 NULL );
320 break;
321 }
322 if ( i >= Sfmt_item_def_size ) {
323 fprintf( stderr,
324 "TVVIDSAVDIALOGSetSel: Unsupported sampfmt %lu\n",
325 choice );
326 exit(1);
327 }
328 Sel_sfmt = choice;
329 }
330 else if ( menu_btn == Chan_menu_btn ) {
331 for ( i = 0; i < Chan_item_def_size; i++ )
332 if ( Chan_item_def[i].stereo == choice ) {
333 XtVaGetValues( Chan_item_def[i].wgt, XtNlabel, &label,
334 NULL );
335 XtVaSetValues( menu_btn, XtNlabel, label,
336 NULL );
337 break;
338 }
339 if ( i >= Chan_item_def_size ) {
340 fprintf( stderr,
341 "TVVIDSAVDIALOGSetSel: Unsupported #chan %lu\n",
342 choice );
343 exit(1);
344 }
345 Sel_stereo = choice;
346 }
347 else if ( menu_btn == Rate_menu_btn ) {
348 for ( i = 0; i < Rate_item_def_size; i++ )
349 if ( Rate_item_def[i].rate == choice ) {
350 XtVaGetValues( Rate_item_def[i].wgt, XtNlabel, &label,
351 NULL );
352 XtVaSetValues( menu_btn, XtNlabel, label,
353 NULL );
354 break;
355 }
356 if ( i >= Rate_item_def_size ) {
357 fprintf( stderr,
358 "TVVIDSAVDIALOGSetSel: Unsupported rate %lu\n",
359 choice );
360 exit(1);
361 }
362 Sel_rate = choice;
363 }
364 else if ( menu_btn == Vtrg_menu_btn ) {
365 for ( i = 0; i < XtNumber( Vtrg_item_def ); i++ )
366 if ( Vtrg_item_def[i].fmt == choice ) {
367 XtVaGetValues( Vtrg_item_def[i].wgt, XtNlabel, &label,
368 NULL );
369 XtVaSetValues( menu_btn, XtNlabel, label,
370 NULL );
371 break;
372 }
373 if ( i >= XtNumber( Vtrg_item_def ) ) {
374 fprintf( stderr,
375 "TVVIDSAVDIALOGSetSel: Unsupported filefmt %lu\n",
376 choice );
377 exit(1);
378 }
379 Sel_vtarg = choice;
380 }
381 else {
382 fprintf( stderr, "TVVIDSAV:SetMenuSelection: Bad menu_btn\n" );
383 exit(1);
384 }
385
386 /* HANDLE CONSTRAINTS */
387
388 /* When change IMGCAPFMT, ensure IMGENCFMT stays compatible */
389 /* and vice versa. */
390 if ( menu_btn == Icap_fmt_menu_btn ) {
391 if ( IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) &&
392 !IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) )
393 SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_PPM );
394 else if ( !IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) &&
395 IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) )
396 SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_YUV );
397 }
398 else if ( menu_btn == Ifile_fmt_menu_btn ) {
399 if ( IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) &&
400 !IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) )
401 SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_RGB16 );
402 else if ( !IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) &&
403 IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) )
404 SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_IYUV );
405 }
406
407 else if ( menu_btn == Vtrg_menu_btn ) {
408 /* Enable/disable & set/reset related options */
409 XtSetSensitive( Cleanup_form, choice == TV_VIDEO_TARGET_MPEG );
410
411 /* When change target to MPEG, switch to IYUV/MPEG2 by default */
412 if (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) ||
413 ( Sel_vtarg == TV_VIDEO_TARGET_MPEG )) {
414 #ifdef WHEN_ITS_READY_FOR_PRIME_TIME
415 SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_IYUV );
416 SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_YUV );
417 SetMenuSelection( Ffmt_menu_btn, TV_AUDIO_FILE_FMT_MPEG2 );
418 #else
419 SetMenuSelection( Icap_fmt_menu_btn, TV_ICAP_FMT_RGB16 );
420 SetMenuSelection( Ifile_fmt_menu_btn, TV_STILL_FMT_PPM );
421 SetMenuSelection( Ffmt_menu_btn, TV_AUDIO_FILE_FMT_MPEG2 );
422 #endif
423 }
424 }
425 }
426
TextValUpdate(Widget text_wgt,char * str)427 static void TextValUpdate( Widget text_wgt, char *str )
428 {
429 XawTextBlock tblk;
430 char *old_str;
431 int old_len;
432
433 assert( text_wgt != NULL );
434
435 memset( &tblk, '\0', sizeof( tblk ) );
436 tblk.firstPos = 0;
437 tblk.length = strlen( str );
438 tblk.ptr = str;
439 tblk.format = XawFmt8Bit;
440
441 XtVaGetValues( text_wgt, XtNstring, &old_str,
442 NULL );
443 old_len = (old_str == NULL) ? 0 : strlen( old_str );
444 XawTextReplace( text_wgt, 0, old_len, &tblk );
445 }
446
447
448 /* UpdateButtons - Enable/disable btns based on state */
UpdateButtons()449 static void UpdateButtons()
450 {
451 TV_BOOL rec, stop, dismiss;
452
453 if ( Recording )
454 stop = TRUE , rec = dismiss = FALSE;
455 else
456 stop = FALSE, rec = dismiss = TRUE;
457
458 XtSetSensitive( Record_btn , rec );
459 XtSetSensitive( Stop_btn , stop );
460 XtSetSensitive( Dismiss_btn , dismiss );
461 }
462
463
464 /* DialogSetEnabled - Utility rtn to sensitive/desensitize the video dialog */
DialogSetEnabled(TV_BOOL enabled)465 static void DialogSetEnabled( TV_BOOL enabled )
466 {
467 if ( Main_wgt == NULL )
468 return;
469
470 XtSetSensitive( Main_wgt, enabled );
471 if ( enabled ) {
472 UpdateButtons();
473
474 XtSetSensitive( Audio_cap_mgr, Audio_enabled );
475 }
476 }
477
478 /* PrepareForVideo - Take dialog values, save them in globals, open */
479 /* the video device and setup its play/record parameters. */
PrepareForVideo(TV_BOOL optimize_only)480 static TV_BOOL PrepareForVideo( TV_BOOL optimize_only )
481 {
482 TV_CAPTURE *c = &G_glob.capture;
483 TV_DISK *d = &G_glob.disk;
484 TV_BOOL error = FALSE;
485 String filename,
486 str,
487 str2;
488 char msg[100];
489 TV_GEOM g = { 0,0,0,0 };
490 TV_INT32 fps;
491 Boolean cleanup_temp;
492
493 /* ...Filename base */
494 XtVaGetValues( Fname_text, XtNstring, &filename,
495 NULL );
496 if ( filename == NULL )
497 filename = "";
498 if ( strlen( filename ) == 0 ) {
499 XUTILDialogPause( TVTOPLEVEL, "Error", "No filename specified.",
500 TV_DIALOG_TYPE_OK );
501 error = TRUE;
502 goto RETURN;
503 }
504
505 /* ...Size */
506 XtVaGetValues( Res_text, XtNstring, &str,
507 NULL );
508 if ( str == NULL )
509 str = "";
510 if (( sscanf( str, "%ldx%ld", &g.w, &g.h ) != 2 ) ||
511 !TVCAPTUREValidRegionGeom( c, &g )) {
512 XUTILDialogPause( TVTOPLEVEL, "Error", "Invalid size.",
513 TV_DIALOG_TYPE_OK );
514 error = TRUE;
515 goto RETURN;
516 }
517
518 if ( !optimize_only ) {
519 /* ...Speed */
520 XtVaGetValues( FPS_text, XtNstring, &str,
521 NULL );
522 if ( str == NULL )
523 str = "";
524 if (( sscanf( str, "%ld", &fps ) != 1 ) ||
525 ( fps < 1 ) || ( fps > c->fps_max )) {
526 XUTILDialogPause( TVTOPLEVEL, "Error", "Invalid speed.",
527 TV_DIALOG_TYPE_OK );
528 error = TRUE;
529 goto RETURN;
530 }
531
532 /* ...ImageCapFmt/ImageEncFmt dependency */
533 if (( Sel_vtarg != TV_VIDEO_TARGET_RAW ) &&
534 ( IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) !=
535 IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) )) {
536 str = IMG_CAP_FMT_IS_RGB( Sel_icapfmt ) ? "RGB" : "YUV";
537 str2 = IMG_ENC_FMT_IS_RGB( Sel_ifilefmt ) ? "RGB" : "YUV";
538 sprintf( msg, "Image Capture Format is %s-based but\n"
539 "Image Encode Format is %s-based.",
540 str, str2 );
541 XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK );
542 error = TRUE;
543 goto RETURN;
544 }
545
546 /* ...Target/AudioEncFmt dependency */
547 if ( (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) ||
548 ( Sel_vtarg == TV_VIDEO_TARGET_MPEG )) &&
549 Audio_enabled &&
550 (( Sel_ffmt != TV_AUDIO_FILE_FMT_MPEG2 ) &&
551 ( Sel_ffmt != TV_AUDIO_FILE_FMT_MPEG3 )) ) {
552 XUTILDialogPause( TVTOPLEVEL, "Error",
553 "Valid Audio Encoding Formats for the\n"
554 "MPEG and MPEG-Ready Targets are:\n"
555 "MPEG-2 and MPEG-3.\n",
556 TV_DIALOG_TYPE_OK );
557 error = TRUE;
558 goto RETURN;
559 }
560
561 /* Cleanup Temp Files */
562 XtVaGetValues( Cleanup_wgt, XtNstate, &cleanup_temp,
563 NULL);
564 }
565
566 /* Save off settings */
567 d->fn_video_base[0] = '\0';
568 strncat( d->fn_video_base, filename, sizeof( d->fn_video_base ) - 1 );
569 d->video.geom = g;
570
571 if ( !optimize_only ) {
572 d->video.target = Sel_vtarg;
573 d->video.fps = fps;
574 d->video.cleanup_temp = (cleanup_temp != 0);
575 }
576
577 RETURN:
578 return !error;
579 }
580
581
582 /* DoCmdFailDialog - Display a dialog citing the command that failed */
583 /* and its exit status; wait on user to dismiss. */
DoCmdFailDialog(char * cmd[],int status)584 static void DoCmdFailDialog(
585 char *cmd[],
586 int status )
587 {
588 char msg[ 2*MAXPATHLEN + 160 ];
589 TV_INT32 i;
590
591 sprintf( msg, "Video conversion failed.\nCMD = " );
592 for ( i = 0; cmd[i] != NULL; i++ )
593 sprintf( msg+strlen(msg), "%s ", cmd[i] );
594 sprintf( msg+strlen(msg), "\nSTATUS = 0x%.4x", status );
595 XUTILDialogPause( TVTOPLEVEL, "Error", msg, TV_DIALOG_TYPE_OK );
596 }
597
598 /* OkToWriteTo - Determine if its OK to write to the specified file. */
599 /* If the file exists, user is prompted to overwrite, and file is */
600 /* unlinked. If device file, user is prompted for verification. */
601 /* If insuff perms, error dialog displayed. */
OkToWriteTo(char filename[MAXPATHLEN],TV_BOOL * dont_unlink)602 static TV_BOOL OkToWriteTo(
603 char filename[ MAXPATHLEN ],
604 TV_BOOL *dont_unlink )
605 {
606 struct stat sb;
607 TV_BOOL exists = TRUE,
608 ok = FALSE;
609 char msg[MAXPATHLEN+160];
610
611 *dont_unlink = FALSE;
612
613 if ( stat( filename, &sb ) < 0 ) {
614 if ( errno != ENOENT ) {
615 fprintf( stderr, "Whoah! stat() failed on '%s'.\n", filename );
616 XBell( TVDISPLAY, 100 );
617 ok = FALSE;
618 goto RETURN;
619 }
620 exists = FALSE;
621 }
622
623 if ( exists ) {
624 if ( access( filename, R_OK | W_OK ) < 0 ) {
625 XUTILDialogPause( TVTOPLEVEL, "Error",
626 "Can't read and write to this file.",
627 TV_DIALOG_TYPE_OK );
628 goto RETURN;
629 }
630 if ( S_ISREG(sb.st_mode) ) {
631 sprintf( msg, "This file exists (%s).\nOverwrite?", filename );
632 if ( XUTILDialogPause( TVTOPLEVEL, "Confirm", msg,
633 TV_DIALOG_TYPE_YES_NO ) != TV_DIALOG_YES )
634 goto RETURN;
635 unlink( filename );
636 }
637 #ifdef FIXME__NOT_SUPPORTED_ANYMORE
638 else if ( S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode) ) {
639 sprintf( msg, "This is a device file (%s).\nAre you sure "
640 "you want to write to it?", filename );
641 if ( XUTILDialogPause( TVTOPLEVEL, "Confirm", msg,
642 TV_DIALOG_TYPE_YES_NO ) != TV_DIALOG_YES )
643 goto RETURN;
644 *dont_unlink = TRUE;
645 }
646 #endif
647 else {
648 XUTILDialogPause( TVTOPLEVEL, "Error",
649 "Can't write to this type of file.",
650 TV_DIALOG_TYPE_OK );
651 goto RETURN;
652 }
653 }
654
655 ok = TRUE;
656
657 RETURN:
658 return ok;
659 }
660
661
662 /* VideoPixelGeom - Returns ptr to a pixel geom that we're going to */
663 /* use for capture. */
VideoPixelGeom(TV_ICAP_FMT img_cap_fmt)664 static TV_PIXEL_GEOM *VideoPixelGeom( TV_ICAP_FMT img_cap_fmt )
665 {
666 static TV_PIXEL_GEOM
667 IYUV = { -1,TV_PIXELTYPE_YUV,0,{},0,0,
668 {8,8,8},{1,2,2},{1,2,2},TV_FRAME_PLANAR,"YUV",1,1,0 },
669 YUY2 = { -1,TV_PIXELTYPE_YUV,0,{},0,0,
670 {8,8,8},{1,2,2},{1,1,1},TV_FRAME_PACKED,"YUYV",1,1,0 },
671 YUY2L = { -1,TV_PIXELTYPE_YUV,0,{},0,0,
672 {8,8,8},{1,2,2},{1,1,1},TV_FRAME_PLANAR,"YUV",1,1,0 };
673
674 static TV_PIXEL_GEOM pg;
675
676 TV_CAPTURE *c = &G_glob.capture;
677 TV_UINT32 num_pg,
678 i;
679 TV_BOOL found = FALSE;
680
681 TVCAPTUREGetNumPixFmts( c, &num_pg );
682 for ( i = 0; (i < num_pg) && !found; i++ ) {
683 TVCAPTUREGetNthPixFmt( c, i, &pg );
684
685 switch ( img_cap_fmt ) {
686 case TV_ICAP_FMT_RGB16 :
687 /* Pull out 565 16bpp byte swapped */
688 if (( pg.type == TV_PIXELTYPE_RGB ) && ( pg.Bpp == 2 ) &&
689 ( pg.mask[0] == 0xf800 ) && ( pg.mask[1] == 0x07e0 ) &&
690 ( pg.mask[2] == 0x001f ) && pg.swap_bytes )
691 found = TRUE;
692 break;
693
694 case TV_ICAP_FMT_IYUV :
695 if ( YUV_PIX_GEOM_MATCH( &pg ,&IYUV ) )
696 found = TRUE;
697 break;
698
699 case TV_ICAP_FMT_YUY2 :
700 if ( YUV_PIX_GEOM_MATCH( &pg ,&YUY2 ) )
701 found = TRUE;
702 break;
703
704 case TV_ICAP_FMT_YUY2L :
705 if ( YUV_PIX_GEOM_MATCH( &pg ,&YUY2L ) )
706 found = TRUE;
707 break;
708
709 default :
710 fprintf( stderr,
711 "VideoPixelGeom: Unsupported pixel cap fmt: %d\n",
712 img_cap_fmt );
713 exit(1);
714 }
715 }
716
717 if ( !found ) {
718 fprintf( stderr, "VideoPixelGeom: Can't find cap fmt %d\n",
719 img_cap_fmt );
720 exit(1);
721 }
722
723 return &pg;
724 }
725
726 /* GetRawFilenames - Fill in list of raw filenames to use for */
727 /* video capture */
GetRawFilenames(char cap_file[MAXPATHLEN],TV_BOOL target_raw,char fnames[4][MAXPATHLEN],TV_INT32 * num_raw_files)728 static void GetRawFilenames( char cap_file[ MAXPATHLEN ],
729 TV_BOOL target_raw,
730 char fnames[ 4 ][ MAXPATHLEN ],
731 TV_INT32 *num_raw_files )
732 {
733 TV_INT32 i;
734 String *app_res = App_res.video_cap_file;
735
736 *num_raw_files = 0;
737 for ( i = 0; i < 4; i++ )
738 fnames[i][0] = '\0';
739
740 /* If user didn't set resources, fall-back on a capture file based */
741 /* on the name of the user-entered target-file. */
742 if ( !app_res[0] || !app_res[0][0] ) {
743
744 /* FIXME: for raw devices, no difference for target_raw */
745 if ( !target_raw )
746 sprintf( fnames[0], AV_RAWNAME_FMT, cap_file );
747 else {
748 fnames[0][0] = '\0';
749 strncat( fnames[0], cap_file, MAXPATHLEN-1 );
750 }
751 *num_raw_files = 1;
752 }
753
754 /* User set at least one capture file resource; use those */
755 else {
756 for ( i = 0; i < 4; i++ )
757 if ( app_res[i] && app_res[i][0] )
758 strncat( fnames[i], app_res[i], MAXPATHLEN-1 );
759 else
760 break;
761 *num_raw_files = i;
762 }
763 }
764
765
766 /* Unlink the raw files used for video capture */
UnlinkRawFiles(char fnames[4][MAXPATHLEN])767 static void UnlinkRawFiles( char fnames[ 4 ][ MAXPATHLEN ] )
768 {
769 TV_INT32 i;
770
771 for ( i = 0; i < 4; i++ )
772 if ( fnames[i][0] )
773 unlink( fnames[i] );
774 }
775
776
777 /**@BEGINFUNC**************************************************************
778
779 Prototype : static TV_BOOL WriteVidConvertShScript(
780 TV_CAP_PARM *p )
781
782 Purpose : Write an SH script to perform the encoding of a raw
783 capture file into images and an audio file or an MPEG
784 video and/or system stream as selected by the user.
785
786 Programmer : 16-Jan-98 Randall Hopper
787
788 Parameters : p - I: image conversion parameters
789
790 Returns : T = success; F = failure
791
792 Globals : None.
793
794 **@ENDFUNC*****************************************************************/
795
WriteVidConvertShScript(TV_CAP_PARM * p)796 static TV_BOOL WriteVidConvertShScript(
797 TV_CAP_PARM *p )
798 {
799 FILE *fp = NULL;
800 TV_BOOL ret = FALSE;
801 char *str;
802 struct stat stat;
803 TV_INT32 i;
804 TV_BOOL has_path;
805
806 /* Open output script file */
807 has_path = strchr( p->fname_base, '/' ) != NULL;
808 sprintf( p->script_fname, SCRIPT_FNAME_FMT,
809 (has_path ? "" : "./"), p->fname_base );
810
811 if ( (fp = fopen( p->script_fname, "wt" )) == NULL ) {
812 fprintf( stderr, "Failed to open for write: %s\n", p->script_fname );
813 goto RETURN;
814 }
815
816 /* Write the header */
817 fprintf( fp, ENCODE_SCRIPT_HEADER, p->script_fname );
818
819 /*fprintf( fp, "\n\n\n exec 1>LOG 2>&1 \n\n\n\n" );*/
820
821 /* Write the conversion settings */
822 fprintf( fp, "AV_RAW_FILES='" );
823 for ( i = 0; i < p->num_raw_fnames; i++ )
824 fprintf( fp, "%s%s", (( i==0 ) ? "" : "," ), p->raw_fnames[i] );
825 fprintf( fp, "'\n" );
826
827 fprintf( fp, "AV_TARGET='%s'\n\n", (p->target_mpeg ? "MPEG" : "IMAGES") );
828 fprintf( fp, "TARGET_FN_BASE='%s'\n\n", p->fname_base );
829 fprintf( fp, "AUDIO_ENABLED='%s'\n", (p->audio_enabled ? "YES" : "NO") );
830
831 switch ( p->snd_fmt.samp_fmt ) {
832 case TV_AUDIO_SAMPLE_FMT_MULAW_U8 : str = "MULAW_U8" ; break;
833 case TV_AUDIO_SAMPLE_FMT_LIN_S8 : str = "LIN_S8" ; break;
834 case TV_AUDIO_SAMPLE_FMT_LIN_U8 : str = "LIN_U8" ; break;
835 case TV_AUDIO_SAMPLE_FMT_LIN_S16_LE : str = "LIN_S16_LE"; break;
836 case TV_AUDIO_SAMPLE_FMT_LIN_U16_LE : str = "LIN_U16_LE"; break;
837 case TV_AUDIO_SAMPLE_FMT_LIN_S16_BE : str = "LIN_S16_BE"; break;
838 case TV_AUDIO_SAMPLE_FMT_LIN_U16_BE : str = "LIN_U16_BE"; break;
839 default : str = "" ; break;
840 }
841 fprintf( fp, "AUDIO_CAP_FMT_SAMPLE='%s'\n", str );
842 fprintf( fp, "AUDIO_CAP_FMT_CHAN='%d'\n", p->snd_fmt.stereo ? 2 : 1 );
843 fprintf( fp, "AUDIO_CAP_FMT_FREQ='%ld'\n", p->snd_fmt.samp_rate );
844
845 switch ( p->snd_fmt.file_fmt ) {
846 case TV_AUDIO_FILE_FMT_RAW : str = "RAW" ; break;
847 case TV_AUDIO_FILE_FMT_SUNAU : str = "SUNAU"; break;
848 case TV_AUDIO_FILE_FMT_WAV : str = "WAV" ; break;
849 case TV_AUDIO_FILE_FMT_VOC : str = "VOC" ; break;
850 case TV_AUDIO_FILE_FMT_AIFF : str = "AIFF" ; break;
851 case TV_AUDIO_FILE_FMT_MPEG2 : str = "MPEG2"; break;
852 case TV_AUDIO_FILE_FMT_MPEG3 : str = "MPEG3"; break;
853 default : str = "" ; break;
854 }
855
856 fprintf( fp, "AUDIO_TARGET_FMT='%s'\n\n", str );
857
858 fprintf( fp, "VIDEO_RES_X='%ld'\n", p->geom.w );
859 fprintf( fp, "VIDEO_RES_Y='%ld'\n", p->geom.h );
860
861 switch ( p->img_cap_fmt ) {
862 case TV_ICAP_FMT_RGB16 : str = "RGB16"; break;
863 case TV_ICAP_FMT_IYUV : str = "IYUV" ; break;
864 case TV_ICAP_FMT_YUY2 : str = "YUY2" ; break;
865 case TV_ICAP_FMT_YUY2L : str = "YUY2L"; break;
866 default : str = "" ; break;
867 }
868
869 fprintf( fp, "VIDEO_CAP_FMT='%s'\n", str );
870 fprintf( fp, "VIDEO_TARGET_FPS='%ld'\n", p->fps );
871 fprintf( fp, "VIDEO_STREAM='%s'\n\n", ( p->streaming ? "YES" : "NO" ) );
872
873 switch ( p->img_sav_fmt ) {
874 case TV_STILL_FMT_TIFF : str = "TIFF"; break;
875 case TV_STILL_FMT_PPM : str = "PPM" ; break;
876 case TV_STILL_FMT_YUV : str = "YUV" ; break;
877 default : str = "" ; break;
878 }
879 fprintf( fp, "IMAGE_TARGET_FMT='%s'\n\n", str );
880
881 fprintf( fp, "CLEANUP_TEMP_FILES='%s'\n",
882 (p->cleanup_tmp_files ? "YES" : "NO" ) );
883
884 /* Source the main script with the methods */
885 fprintf( fp, "\n\n\n"
886 "#\n"
887 "# Source the videoCnvtScript with the conversion methods\n"
888 "#\n"
889 ". %s\n",
890 App_res.video_cnvt_script );
891
892 /* Add execute permission */
893 fstat ( fileno(fp), &stat );
894 fchmod( fileno(fp), stat.st_mode | S_IXUSR );
895
896 ret = TRUE;
897
898 RETURN:
899 if ( fp != NULL )
900 fclose( fp );
901 if ( !ret )
902 unlink( p->script_fname );
903 return ret;
904 }
905
906
907 /* CnvtCmdCancelTestCB */
908 /* - Used by CnvtCmd invocations of XUTILRunCmdAllowCancel to */
909 /* identify when the the user aborted the operation. */
CnvtCmdCancelTestCB(void * cb_data)910 static TV_BOOL CnvtCmdCancelTestCB( void *cb_data )
911 {
912 TV_CNVT_CMD_STATE *state = (TV_CNVT_CMD_STATE *) cb_data;
913
914 return !XtIsRealized( state->dialog_shell );
915 }
916
917
918 /* CnvtCmdDoneCB */
919 /* - Called when the conversion script completes or is aborted */
CnvtCmdDoneCB(TV_BOOL aborted,int status,void * cb_data)920 static void CnvtCmdDoneCB( TV_BOOL aborted, int status, void *cb_data )
921 {
922 TV_CNVT_CMD_STATE *state = (TV_CNVT_CMD_STATE *) cb_data;
923
924 /* At this stage, always pull down the "wait" dialog and destroy it */
925 if ( !aborted )
926 XtPopdown( state->dialog_shell );
927 XtDestroyWidget( state->dialog_shell );
928
929 /* If the command failed, tell the user about it */
930 if ( !aborted && ( status != 0 ) )
931 DoCmdFailDialog( state->cmd, status );
932
933 /* Do post-cmd cleanup */
934 free( state->cmd );
935 free( state->tmpstr );
936
937 /* If we completed successfully or the user canceled the conversion, */
938 /* remove raw capture files (if passed). */
939 /* NOTE: if we're capturing to devices, don't clean those up. */
940 if ( aborted || ( status == 0 ) ) {
941 TV_INT32 i;
942 struct stat stat_s;
943
944 for ( i = 0; i < XtNumber( state->raw_fnames ); i++ )
945 if (( state->raw_fnames[i][0] != '\0' ) &&
946 ( stat( state->raw_fnames[i], &stat_s ) == 0 ) &&
947 ( stat_s.st_mode & S_IFREG ))
948 unlink( state->raw_fnames[i] );
949 }
950
951 /* FIXME: The fxtv_conv.sh script isn't nuking its child processes */
952 /* and temporary files when it's killed. It just dies. */
953
954 DialogSetEnabled( True );
955 Recording = FALSE;
956 UpdateButtons();
957 free( state );
958 return;
959 }
960
961
962 /* RunCnvtCmd - Exec command to convert raw capture to desired format(s) */
RunCnvtCmd(char cmd[],char raw_fnames[TV_RAW_MAX_FILES][MAXPATHLEN])963 static void RunCnvtCmd( char cmd[],
964 char raw_fnames[ TV_RAW_MAX_FILES ][ MAXPATHLEN ] )
965 {
966 TVUTIL_PIPE_END end[3] = {{ -1 }, { -1 }, { -1 }};
967 TV_CNVT_CMD_STATE *state;
968 Widget dialog_shell;
969
970 if ( (state = calloc( 1, sizeof(*state) )) == NULL )
971 TVUTILOutOfMemory();
972
973 TVUTILCmdStrToArgList( cmd, &state->cmd, &state->tmpstr );
974
975 dialog_shell = XUTILDialogBuild( TVTOPLEVEL, "Please Wait",
976 "Conversion in Progress...",
977 TV_DIALOG_TYPE_CANCEL );
978 XUTILXtPopup( dialog_shell, XtGrabNone, TVTOPLEVEL );
979
980 /* Execute conversion cmd & wait on it to finish or user to cancel */
981 state->dialog_shell = dialog_shell;
982 if ( raw_fnames )
983 memcpy( state->raw_fnames, raw_fnames, sizeof( state->raw_fnames ) );
984 else
985 memset( state->raw_fnames, '\0', sizeof( state->raw_fnames ) );
986
987 XUTILRunCmdAllowCancel( TVAPPCTX, state->cmd, end,
988 CnvtCmdCancelTestCB, state,
989 CnvtCmdDoneCB , state );
990 }
991
992
993 /**@BEGINFUNC**************************************************************
994
995 Prototype : static void RecordCmdCB(
996 Widget w,
997 XtPointer cl,
998 XtPointer cb )
999
1000 Purpose : Callback used for Record and Optimize functions.
1001
1002 Programmer : 17-Jan-98 Randall Hopper
1003
1004 Parameters : w - I: what widget was pressed that invoked us (don't care)
1005 cl - I: "optimize" or "record"
1006 cb - I: (don't care)
1007
1008 Returns : None.
1009
1010 Globals : None.
1011
1012 **@ENDFUNC*****************************************************************/
1013
RecordCmdCB(Widget w,XtPointer cl,XtPointer cb)1014 static void RecordCmdCB( Widget w, XtPointer cl, XtPointer cb )
1015 {
1016 TV_CAPTURE *c = &G_glob.capture;
1017 TV_DISK *d = &G_glob.disk;
1018 char *error_msg,
1019 *cfg_fail_msg,
1020 *raw_fnames[ TV_RAW_MAX_FILES ];
1021 int dsp_fd = -1;
1022 TV_BOOL optimizing = STREQ( (char *)cl, "optimize" ),
1023 dont_unlink,
1024 video_disabled = FALSE,
1025 cmd_running = FALSE;
1026 TV_RAW_VIDEO_FILE *rf = NULL;
1027 Widget dialog_shell = NULL;
1028 TV_INT32 i;
1029 TV_CAP_PARM *p = NULL;
1030
1031 /* If we haven't created the dialog yet, do so */
1032 if ( Dialog_wgt == NULL ) {
1033 TVVIDSAVDialogBuild( &Dialog_wgt );
1034 TVVIDSAVDIALOGResync();
1035 }
1036
1037 /* If already recording, ignore user */
1038 if ( Recording ) {
1039 XBell( TVDISPLAY, 100 );
1040 return;
1041 }
1042
1043 /* Save off old freeze state, and stop capture if necessary */
1044 TVSCREENSetScreenUpdateEnabled( FALSE );
1045 video_disabled = TRUE;
1046
1047 /* Grab values off dialog */
1048 if ( !PrepareForVideo( optimizing ) )
1049 goto RETURN;
1050
1051 /* Fill in capture parameters */
1052 if ( (p = calloc( 1, sizeof(*p) )) == NULL )
1053 TVUTILOutOfMemory();
1054
1055 p->fname_base[0] = '\0';
1056 strncat( p->fname_base, d->fn_video_base, sizeof(p->fname_base)-1 );
1057 p->audio_enabled = Audio_enabled;
1058 p->target_mpeg = (Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY) ||
1059 (Sel_vtarg == TV_VIDEO_TARGET_MPEG);
1060
1061 /* FIXME: Allow user to set whether we'll do streaming or not */
1062 /* (for YUV, uses YUV; for RGB, uses PPM) */
1063 p->streaming = (( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY ) ||
1064 ( Sel_vtarg == TV_VIDEO_TARGET_MPEG )) &&
1065 (( Sel_icapfmt == TV_ICAP_FMT_IYUV ) ||
1066 (( Sel_icapfmt == TV_ICAP_FMT_RGB16 ) &&
1067 ( Sel_ifilefmt == TV_STILL_FMT_PPM )));
1068
1069 p->snd_fmt.samp_fmt = Sel_sfmt, /* Cap sample format */
1070 p->snd_fmt.stereo = Sel_stereo, /* Cap num chan */
1071 p->snd_fmt.samp_rate = Sel_rate, /* Cap sample rate */
1072 p->snd_fmt.file_fmt = Sel_ffmt; /* Desired output format */
1073
1074 memcpy( &p->geom, &d->video.geom, sizeof(p->geom) );
1075 memcpy( &p->pix_geom, VideoPixelGeom(Sel_icapfmt), sizeof(p->pix_geom) );
1076 p->img_cap_fmt = Sel_icapfmt;
1077 p->img_sav_fmt = Sel_ifilefmt;
1078
1079 GetRawFilenames( p->fname_base, (Sel_vtarg == TV_VIDEO_TARGET_RAW),
1080 p->raw_fnames, &p->num_raw_fnames );
1081 p->fps = d->video.fps;
1082 TVCAPTUREGetFPSMax( c, &p->fps_max );
1083
1084 p->cleanup_tmp_files = d->video.cleanup_temp;
1085 p->script_fname[0] = '\0';
1086
1087 /* See if the output file exists; if so, we need to verify user wants */
1088 /* to overwrite. */
1089
1090 /* FIXME: We really need to check all the possible output filenames */
1091
1092 /* FIXME: Add RAW DEVICE SUPPORT or add check for target NOT raw device */
1093 /* For RAW devices, we need to write EOF record (read() doesn't */
1094 /* return 0 on end of stream). Also, quit unlinking raw device files */
1095 /* And if raw write is to device file, must write frames someplace */
1096 /* else; also, can't use tempname when writing to this file. */
1097
1098 if ( !OkToWriteTo( p->fname_base, &dont_unlink ) )
1099 goto RETURN;
1100
1101 /* Now open the raw output file */
1102 for ( i = 0; i < XtNumber( raw_fnames ); i++ )
1103 raw_fnames[i] = p->raw_fnames[i];
1104
1105 if ( !TVRAWVIDEOOpen( raw_fnames, p->num_raw_fnames, FALSE, &rf ) ) {
1106 XUTILDialogPause( TVTOPLEVEL, "Error", "Couldn't open output file\n",
1107 TV_DIALOG_TYPE_OK );
1108 goto RETURN;
1109 }
1110
1111 if ( p->audio_enabled ) {
1112 Snd.sample_fmt = p->snd_fmt.samp_fmt,
1113 Snd.stereo = p->snd_fmt.stereo,
1114 Snd.sample_rate = p->snd_fmt.samp_rate,
1115 Snd.buf = NULL,
1116 Snd.bytes = 0;
1117
1118 if ( !TVAUDIOOpenDsp( &Snd, TRUE, &dsp_fd, &error_msg ) ) {
1119 XUTILDialogPause( TVTOPLEVEL, "Error", error_msg,
1120 TV_DIALOG_TYPE_OK );
1121 dsp_fd = -1;
1122 goto RETURN;
1123 }
1124 TVAUDIOSelectLineForRecord();
1125 }
1126
1127 /* Disable all but stop btn */
1128 Recording = TRUE;
1129 Optimizing = optimizing;
1130 First_image = TRUE;
1131 d->video.capture_on = TRUE;
1132
1133 if ( !optimizing )
1134 UpdateButtons();
1135 else {
1136 DialogSetEnabled( False );
1137
1138 /* Build wait dialog */
1139 dialog_shell = XUTILDialogBuild( TVTOPLEVEL, "Please Wait",
1140 "Optimizing Capture Speed...",
1141 TV_DIALOG_TYPE_CANCEL );
1142 XUTILXtPopup( dialog_shell, XtGrabNone, TVTOPLEVEL );
1143 Wait_dialog = dialog_shell;
1144 }
1145
1146 /* Flush X events (update GUI buttons, etc.) */
1147 XSync( TVDISPLAY, False );
1148
1149 /* Queue up an initial request to capture a frame (to driver buffer) */
1150 TVCAPTURESetFrameDoneCBEnabled( c, TRUE );
1151 TVCAPTURESetCaptureMode ( c, TV_CAPTURE_CONTINUOUS );
1152 TVCAPTURESetTransferMode ( c, TV_TRANSFER_STD_IMAGE );
1153 TVCAPTURESetRegionGeom ( c, &p->geom );
1154 TVCAPTURESetPixelGeom ( c, &p->pix_geom );
1155 TVCAPTURESetFPS ( c, p->fps );
1156
1157 if ( !TVCAPTUREConfigure( c, &cfg_fail_msg ) ) {
1158 fprintf( stderr, "TVCAPTUREConfigure() failed: %s\n", cfg_fail_msg );
1159
1160 UnlinkRawFiles( p->raw_fnames );
1161 goto RETURN;
1162 }
1163 TVCAPTUREStart( c );
1164 Out_rf = rf;
1165 if ( Audio_enabled )
1166 Dsp_fd = dsp_fd;
1167
1168 /*******************************************************************/
1169 /* Now, madly capture frames/audio and write to disk in raw form */
1170 /*******************************************************************/
1171 TVSetWorkProcTimeout( 0 );
1172 while ( Recording && ( !optimizing || XtIsRealized( Wait_dialog ) ) ) {
1173 XEvent ev;
1174
1175 XtAppNextEvent( TVAPPCTX, &ev );
1176 XtDispatchEvent( &ev );
1177 }
1178 TVSetWorkProcTimeout( -1 );
1179
1180 TVCAPTUREStop( c );
1181
1182 Out_rf = NULL;
1183 TVRAWVIDEOClose( &rf );
1184 if ( Audio_enabled ) {
1185 close( dsp_fd );
1186 Dsp_fd = dsp_fd = -1;
1187 }
1188
1189 /* Turn TV back on so user can watch while they wait */
1190 TVCAPTUREClearPendingFrames();
1191 TVSCREENSetScreenUpdateEnabled( TRUE );
1192 d->video.capture_on = FALSE;
1193 video_disabled = FALSE;
1194
1195 if ( optimizing ) {
1196 TV_INT32 fps;
1197 char str[20];
1198
1199 /* If user canceled optimization, bail out */
1200 if ( Optimizing )
1201 goto RETURN;
1202
1203 /* This is a cheesy first-cut */
1204 fps = Vid_stats.frames*1000000L/Vid_stats.time_us;
1205 sprintf( str, "%ld", fps );
1206 TextValUpdate( FPS_text, str );
1207 }
1208
1209 /* Does user want more than just the vid/aud captured to a raw file? */
1210 if ( Sel_vtarg == TV_VIDEO_TARGET_RAW )
1211 goto RETURN;
1212
1213 /* Are we optimizing? If so, done */
1214 if ( optimizing )
1215 goto RETURN;
1216
1217 /* FIXME: The Record and Optimize callbacks now aren't very different */
1218 /* before this point. Merge them. */
1219
1220 /* FIXME: Consider parallelizing aud/vid encode in fxtv_cnvt.sh script */
1221
1222 /* Write conversion script */
1223 /* FIXME: Snapshot these parms when we start capture */
1224 if ( !WriteVidConvertShScript( p ) ) {
1225 XUTILDialogPause( TVTOPLEVEL, "Error",
1226 "Failed writing format conversion script\n",
1227 TV_DIALOG_TYPE_OK );
1228 goto RETURN;
1229 }
1230
1231 /* Does user want us to go ahead and produce images/video/audio */
1232 if ( Sel_vtarg == TV_VIDEO_TARGET_MPEG_READY )
1233 goto RETURN;
1234
1235 /* Run conversion subprocess */
1236 DialogSetEnabled( False );
1237 RunCnvtCmd( p->script_fname,
1238 ( p->cleanup_tmp_files ? p->raw_fnames : NULL ) );
1239 cmd_running = TRUE;
1240
1241 RETURN:
1242 if ( dialog_shell )
1243 XtDestroyWidget( dialog_shell );
1244
1245 if ( optimizing && p )
1246 UnlinkRawFiles( p->raw_fnames );
1247
1248 if ( rf != NULL )
1249 TVRAWVIDEOClose( &rf );
1250
1251 if ( !cmd_running ) {
1252 Recording = FALSE;
1253 free(p);
1254 }
1255
1256 d->video.capture_on = FALSE;
1257
1258 if ( dsp_fd >= 0 )
1259 close( dsp_fd );
1260
1261 if ( video_disabled ) {
1262 TVCAPTUREClearPendingFrames();
1263 TVSCREENSetScreenUpdateEnabled( TRUE );
1264 }
1265
1266 if ( !optimizing )
1267 UpdateButtons();
1268 else
1269 DialogSetEnabled( True );
1270 }
1271
1272
1273 /* StopCmdCB - Stop recording, if we're recording now */
StopCmdCB(Widget w,XtPointer cl,XtPointer cb)1274 static void StopCmdCB( Widget w, XtPointer cl, XtPointer cb )
1275 {
1276 if ( !Recording ) {
1277 XBell( TVDISPLAY, 100 );
1278 return;
1279 }
1280
1281 Recording = FALSE;
1282 }
1283
1284
1285 /* DismissCmdCB - Dismiss the dialog, if we're not recording */
DismissCmdCB(Widget w,XtPointer cl,XtPointer cb)1286 static void DismissCmdCB( Widget w, XtPointer cl, XtPointer cb )
1287 {
1288 if ( Recording ) {
1289 XBell( TVDISPLAY, 100 );
1290 return;
1291 }
1292
1293 XtPopdown( Dialog_wgt );
1294 }
1295
1296
1297 /* ICapFmtMenuCB - Update menu button text */
ICapFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1298 static void ICapFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1299 {
1300 Widget menu_btn = Icap_fmt_menu_btn;
1301 TV_ICAPFMT_ITEM_DEF *def = (TV_ICAPFMT_ITEM_DEF *) cl;
1302
1303 SetMenuSelection( menu_btn, def->fmt );
1304 }
1305
1306 /* IFileFmtMenuCB - Update menu button text */
IFileFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1307 static void IFileFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1308 {
1309 Widget menu_btn = Ifile_fmt_menu_btn;
1310 TV_IFILEFMT_ITEM_DEF *def = (TV_IFILEFMT_ITEM_DEF *) cl;
1311
1312 SetMenuSelection( menu_btn, def->fmt );
1313 }
1314
1315 /* VFmtMenuItemCB - Update menu button text with selected choice */
VFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1316 static void VFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1317 {
1318 Widget menu_btn = Vtrg_menu_btn;
1319 TV_VTRG_ITEM_DEF *def = (TV_VTRG_ITEM_DEF *) cl;
1320
1321 SetMenuSelection( menu_btn, def->fmt );
1322 }
1323
1324 /* FFmtMenuItemCB - Update menu button text with selected choice */
FFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1325 static void FFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1326 {
1327 Widget menu_btn = Ffmt_menu_btn;
1328 TV_FFMT_ITEM_DEF *def = (TV_FFMT_ITEM_DEF *) cl;
1329
1330 SetMenuSelection( menu_btn, def->fmt );
1331
1332 /* If Sun AU, default to 8-bit MULAW, Mono, & 8012 s/s */
1333 if ( def->fmt == TV_AUDIO_FILE_FMT_SUNAU ) {
1334 SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_MULAW_U8 );
1335 SetMenuSelection( Chan_menu_btn, FALSE );
1336 SetMenuSelection( Rate_menu_btn, 8012 );
1337 }
1338
1339 /* If any of the rest, go for the max */
1340 else {
1341 SetMenuSelection( Sfmt_menu_btn, TV_AUDIO_SAMPLE_FMT_LIN_S16_LE );
1342 SetMenuSelection( Chan_menu_btn, TRUE );
1343 SetMenuSelection( Rate_menu_btn, 44100 );
1344 }
1345 }
1346
1347
1348 /* SFmtMenuItemCB - Update menu button text with selected choice */
SFmtMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1349 static void SFmtMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1350 {
1351 Widget menu_btn = Sfmt_menu_btn;
1352 TV_SFMT_ITEM_DEF *def = (TV_SFMT_ITEM_DEF *) cl;
1353
1354 SetMenuSelection( menu_btn, def->fmt );
1355
1356 /* If 8-bit ULAW, default to Mono & 8012 s/s */
1357 if ( def->fmt == TV_AUDIO_SAMPLE_FMT_MULAW_U8 ) {
1358 SetMenuSelection( Chan_menu_btn, FALSE );
1359 SetMenuSelection( Rate_menu_btn, 8012 );
1360 }
1361 }
1362
1363
1364 /* ChanMenuItemCB - Update menu button text with selected choice */
ChanMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1365 static void ChanMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1366 {
1367 Widget menu_btn = Chan_menu_btn;
1368 TV_CHAN_ITEM_DEF *def = (TV_CHAN_ITEM_DEF *) cl;
1369
1370 SetMenuSelection( menu_btn, def->stereo );
1371 }
1372
1373
1374 /* RateMenuItemCB - Update menu button text with selected choice */
RateMenuItemCB(Widget w,XtPointer cl,XtPointer cb)1375 static void RateMenuItemCB( Widget w, XtPointer cl, XtPointer cb )
1376 {
1377 Widget menu_btn = Rate_menu_btn;
1378 TV_RATE_ITEM_DEF *def = (TV_RATE_ITEM_DEF *) cl;
1379
1380 SetMenuSelection( menu_btn, def->rate );
1381 }
1382
1383
1384 /* AudioEnableToggleCB - Enable/Disable capturing of audio with video */
AudioEnableToggleCB(Widget w,XtPointer cl,XtPointer cb)1385 static void AudioEnableToggleCB( Widget w, XtPointer cl, XtPointer cb )
1386 {
1387 XtVaGetValues( w, XtNstate, &Audio_enabled,
1388 NULL );
1389 EVPRINTF(( "Audio Enable = %s\n", Audio_enabled ? "yes" : "no" ));
1390
1391 DialogSetEnabled( TRUE );
1392 }
1393
1394
1395 /* CreateSingleLineTextField - convenience rtn to create single line */
1396 /* text fields the way we like 'em. */
CreateSingleLineTextField(char wgt_name[],Widget parent)1397 Widget CreateSingleLineTextField( char wgt_name[], Widget parent )
1398 {
1399 XtTranslations transl;
1400
1401 Widget wgt;
1402
1403 wgt = XtVaCreateManagedWidget( wgt_name, asciiTextWidgetClass, parent,
1404 XtNtype , XawAsciiString,
1405 XtNuseStringInPlace, False,
1406 XtNscrollHorizontal, XawtextScrollNever,
1407 XtNscrollVertical , XawtextScrollNever,
1408 XtNdisplayCaret , False,
1409 XtNeditType , XawtextEdit,
1410 XtNresize , XawtextResizeNever,
1411 NULL );
1412
1413 /* Text widget translation overrides */
1414 transl = XtParseTranslationTable( G_transl_ovr_ascii_text );
1415 XtOverrideTranslations( wgt, transl );
1416 transl = XtParseTranslationTable( G_transl_ovr_ascii_text_1line );
1417 XtOverrideTranslations( wgt, transl );
1418
1419 return wgt;
1420 }
1421
1422
BuildImageCapWgtPanel(Widget parent)1423 static void BuildImageCapWgtPanel( Widget parent )
1424 {
1425 Widget w, gbox, cbox, menu_shell;
1426 TV_INT32 i;
1427
1428 gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1429 XtNorientation, XtorientVertical,
1430 NULL );
1431
1432 w = XtVaCreateManagedWidget( "imageCapLabel", labelWidgetClass, gbox,
1433 XtNjustify, XtJustifyLeft,
1434 XtNwidth, 263,
1435 NULL );
1436
1437 /* Resolution text */
1438 cbox = XtVaCreateManagedWidget( "resBox", boxWidgetClass, gbox,
1439 XtNorientation, XtorientHorizontal,
1440 NULL );
1441
1442 w = XtVaCreateManagedWidget( "resLabel", labelWidgetClass, cbox,
1443 XtNjustify, XtJustifyRight,
1444 XtNwidth, 90,
1445 NULL );
1446
1447 Res_text = CreateSingleLineTextField( "resText", cbox );
1448
1449 /* Capture format menu */
1450 cbox = XtVaCreateManagedWidget( "iCapFmtBox", boxWidgetClass, gbox,
1451 XtNorientation, XtorientHorizontal,
1452 NULL );
1453
1454
1455 w = XtVaCreateManagedWidget( "iCapFmtLabel", labelWidgetClass, cbox,
1456 XtNjustify, XtJustifyRight,
1457 XtNwidth, 90,
1458 NULL );
1459
1460 w = XtVaCreateManagedWidget( "iCapFmtMenuBox", boxWidgetClass, cbox,
1461 XtNorientation, XtorientHorizontal,
1462 NULL );
1463
1464 Icap_fmt_menu_btn = XtVaCreateManagedWidget( "iCapFmtMenu",
1465 menuButtonWidgetClass, w,
1466 XtNresize, XawtextResizeNever,
1467 NULL );
1468
1469 menu_shell = XtVaCreatePopupShell( "menu",
1470 simpleMenuWidgetClass, Icap_fmt_menu_btn,
1471 NULL );
1472
1473 /* Create all format items for this menu */
1474 for ( i = 0; i < XtNumber( Icap_fmt_item_def ); i++ ) {
1475 Widget item;
1476
1477 item = XtVaCreateManagedWidget( Icap_fmt_item_def[i].wgt_name,
1478 smeBSBObjectClass,
1479 menu_shell,
1480 NULL );
1481 Icap_fmt_item_def[i].wgt = item;
1482
1483 XtAddCallback( item, XtNcallback, ICapFmtMenuItemCB,
1484 (XtPointer) &Icap_fmt_item_def[i] );
1485
1486 /* Add a separator between the YUV and RGB entries */
1487 if ( i == TV_ICAP_LAST_RGB )
1488 XtVaCreateManagedWidget( "separator", smeLineObjectClass,
1489 menu_shell, NULL );
1490 }
1491
1492 /* FPS text & optimize button */
1493 cbox = XtVaCreateManagedWidget( "fpsBox", boxWidgetClass, gbox,
1494 XtNorientation, XtorientHorizontal,
1495 NULL );
1496
1497 w = XtVaCreateManagedWidget( "fpsLabel", labelWidgetClass, cbox,
1498 XtNjustify, XtJustifyRight,
1499 XtNwidth, 90,
1500 NULL );
1501
1502 FPS_text = CreateSingleLineTextField( "fpsText", cbox );
1503
1504 w = XtVaCreateManagedWidget( "optimizeCmd", commandWidgetClass, cbox,
1505 NULL );
1506 XtAddCallback( w, XtNcallback, RecordCmdCB, "optimize" );
1507 }
1508
1509
BuildImageEncWgtPanel(Widget parent)1510 static void BuildImageEncWgtPanel( Widget parent )
1511 {
1512 Widget w, gbox, cbox, menu_shell;
1513 TV_INT32 i;
1514
1515 gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1516 XtNorientation, XtorientVertical,
1517 NULL );
1518
1519 /* Box label */
1520 w = XtVaCreateManagedWidget( "imageEncLabel", labelWidgetClass, gbox,
1521 XtNjustify, XtJustifyLeft,
1522 XtNwidth, 263,
1523 NULL );
1524
1525 /* Format menu panel */
1526 cbox = XtVaCreateManagedWidget( "iFileFmtBox", boxWidgetClass, gbox,
1527 XtNorientation, XtorientHorizontal,
1528 NULL );
1529
1530
1531 w = XtVaCreateManagedWidget( "iFileFmtLabel", labelWidgetClass, cbox,
1532 NULL );
1533
1534 w = XtVaCreateManagedWidget( "iFileFmtMenuBox", boxWidgetClass, cbox,
1535 XtNorientation, XtorientHorizontal,
1536 NULL );
1537
1538 Ifile_fmt_menu_btn = XtVaCreateManagedWidget( "iFileFmtMenu",
1539 menuButtonWidgetClass, w,
1540 XtNresize, XawtextResizeNever,
1541 NULL );
1542
1543 menu_shell = XtVaCreatePopupShell( "menu",
1544 simpleMenuWidgetClass, Ifile_fmt_menu_btn,
1545 NULL );
1546
1547 /* Create all format items for this menu */
1548 for ( i = 0; i < XtNumber( Ifile_fmt_item_def ); i++ ) {
1549 Widget item;
1550
1551 item = XtVaCreateManagedWidget( Ifile_fmt_item_def[i].wgt_name,
1552 smeBSBObjectClass,
1553 menu_shell,
1554 NULL );
1555 Ifile_fmt_item_def[i].wgt = item;
1556
1557 XtAddCallback( item, XtNcallback, IFileFmtMenuItemCB,
1558 (XtPointer) &Ifile_fmt_item_def[i] );
1559 }
1560 }
1561
1562
BuildAudioCapWgtPanel(Widget parent)1563 static void BuildAudioCapWgtPanel( Widget parent )
1564 {
1565 Widget w, gbox, cbox, fbox, lbox, form, menu_shell;
1566 TV_INT32 i;
1567
1568 gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1569 XtNorientation, XtorientVertical,
1570 NULL );
1571
1572 w = XtVaCreateManagedWidget( "audioCapLabel", labelWidgetClass, gbox,
1573 XtNjustify, XtJustifyLeft,
1574 XtNwidth, 345,
1575 NULL );
1576
1577 /* Audio Capture On/Off Toggle */
1578 form = XtVaCreateManagedWidget( "audCapForm", formWidgetClass, gbox,
1579 XtNorientation, XtorientHorizontal,
1580 NULL );
1581
1582 w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form,
1583 XtNjustify, XtJustifyLeft,
1584 XtNlabel, " ",
1585 XtNresize, False,
1586 XtNencoding, XawTextEncoding8bit,
1587 XtNwidth, 70,
1588 NULL );
1589
1590 w = XtVaCreateManagedWidget( "audCapToggle", toggleWidgetClass, form,
1591 XtNlabel, " ",
1592 XtNfromHoriz, w,
1593 NULL );
1594 Audio_cap_wgt = w;
1595
1596 XtAddCallback( w, XtNcallback, AudioEnableToggleCB, NULL );
1597
1598 w = XtVaCreateManagedWidget( "audCapToggleLabel", labelWidgetClass, form,
1599 XtNresize, False,
1600 XtNfromHoriz, w,
1601 XtNencoding, XawTextEncoding8bit,
1602 XtNjustify, XtJustifyLeft,
1603 NULL );
1604
1605 /* Capture format box */
1606 fbox = XtVaCreateManagedWidget( "captureBox", boxWidgetClass, gbox,
1607 XtNorientation, XtorientHorizontal,
1608 NULL );
1609 Audio_cap_mgr = fbox;
1610
1611 lbox = XtVaCreateManagedWidget( "sampFmtLabelBox", boxWidgetClass, fbox,
1612 XtNorientation, XtorientVertical,
1613 NULL );
1614
1615 w = XtVaCreateManagedWidget( "sampFmtLabel", labelWidgetClass, lbox,
1616 XtNresize, False,
1617 XtNencoding, XawTextEncoding8bit,
1618 NULL );
1619
1620 cbox = XtVaCreateManagedWidget( "sampFmtWgtBox", boxWidgetClass, fbox,
1621 XtNorientation, XtorientVertical,
1622 NULL );
1623
1624 /* Sample Format Choice Box */
1625 Sfmt_menu_btn = XtVaCreateManagedWidget( "sampFmtMenu",
1626 menuButtonWidgetClass, cbox,
1627 XtNresize, XawtextResizeNever,
1628 NULL );
1629
1630 menu_shell = XtVaCreatePopupShell( "menu",
1631 simpleMenuWidgetClass, Sfmt_menu_btn,
1632 NULL );
1633
1634 for ( i = 0; i < Sfmt_item_def_size; i++ ) {
1635 Widget item;
1636
1637 item = XtVaCreateManagedWidget( Sfmt_item_def[i].wgt_name,
1638 smeBSBObjectClass,
1639 menu_shell,
1640 NULL );
1641 Sfmt_item_def[i].wgt = item;
1642
1643 XtAddCallback( item, XtNcallback, SFmtMenuItemCB, &Sfmt_item_def[i] );
1644 }
1645
1646 /* # Channels Choice Box */
1647 w = XtVaCreateManagedWidget( "chanMenuBox", boxWidgetClass, cbox,
1648 XtNorientation, XtorientHorizontal,
1649 NULL );
1650
1651 Chan_menu_btn = XtVaCreateManagedWidget( "chanMenu",
1652 menuButtonWidgetClass, w,
1653 XtNresize, XawtextResizeNever,
1654 NULL );
1655
1656 menu_shell = XtVaCreatePopupShell( "menu",
1657 simpleMenuWidgetClass, Chan_menu_btn,
1658 NULL );
1659
1660 for ( i = 0; i < Chan_item_def_size; i++ ) {
1661 Widget item;
1662
1663 item = XtVaCreateManagedWidget( Chan_item_def[i].wgt_name,
1664 smeBSBObjectClass,
1665 menu_shell,
1666 NULL );
1667 Chan_item_def[i].wgt = item;
1668
1669 XtAddCallback( item, XtNcallback, ChanMenuItemCB, &Chan_item_def[i] );
1670 }
1671
1672 /* Sample Rate Choice Box */
1673 w = XtVaCreateManagedWidget( "rateMenuBox", boxWidgetClass, cbox,
1674 XtNorientation, XtorientHorizontal,
1675 NULL );
1676
1677 Rate_menu_btn = XtVaCreateManagedWidget( "rateMenu",
1678 menuButtonWidgetClass, w,
1679 XtNresize, XawtextResizeNever,
1680 NULL );
1681
1682 menu_shell = XtVaCreatePopupShell( "menu",
1683 simpleMenuWidgetClass, Rate_menu_btn,
1684 NULL );
1685
1686 for ( i = 0; i < Rate_item_def_size; i++ ) {
1687 Widget item;
1688
1689 item = XtVaCreateManagedWidget( Rate_item_def[i].wgt_name,
1690 smeBSBObjectClass,
1691 menu_shell,
1692 NULL );
1693 Rate_item_def[i].wgt = item;
1694
1695 XtAddCallback( item, XtNcallback, RateMenuItemCB, &Rate_item_def[i] );
1696 }
1697 }
1698
1699
BuildAudioEncWgtPanel(Widget parent)1700 static void BuildAudioEncWgtPanel( Widget parent )
1701 {
1702 Widget w, gbox, cbox, menu_shell;
1703 TV_INT32 i;
1704
1705 gbox = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, parent,
1706 XtNorientation, XtorientVertical,
1707 NULL );
1708 Audio_enc_mgr = gbox;
1709
1710 w = XtVaCreateManagedWidget( "audioEncLabel", labelWidgetClass, gbox,
1711 XtNjustify, XtJustifyLeft,
1712 XtNwidth, 345,
1713 NULL );
1714
1715 cbox = XtVaCreateManagedWidget( "fileFmtBox", boxWidgetClass, gbox,
1716 XtNorientation, XtorientHorizontal,
1717 NULL );
1718
1719 w = XtVaCreateManagedWidget( "fileFmtLabel", labelWidgetClass, cbox,
1720 XtNjustify, XtJustifyRight,
1721 NULL );
1722
1723 /* File Format Choice Box */
1724 Ffmt_menu_btn = XtVaCreateManagedWidget( "fileFmtMenu",
1725 menuButtonWidgetClass, cbox,
1726 XtNresize, XawtextResizeNever,
1727 NULL );
1728
1729 menu_shell = XtVaCreatePopupShell( "menu",
1730 simpleMenuWidgetClass, Ffmt_menu_btn,
1731 NULL );
1732
1733 for ( i = 0; i < Ffmt_item_def_size; i++ ) {
1734 Widget item;
1735
1736 item = XtVaCreateManagedWidget( Ffmt_item_def[i].file_ext,
1737 smeBSBObjectClass,
1738 menu_shell,
1739 NULL );
1740 Ffmt_item_def[i].wgt = item;
1741
1742 XtAddCallback( item, XtNcallback, FFmtMenuItemCB, &Ffmt_item_def[i] );
1743 }
1744 }
1745
1746
TVVIDSAVDialogBuild(Widget * dialog_wgt)1747 static void TVVIDSAVDialogBuild( Widget *dialog_wgt )
1748 {
1749 Widget w, main1, main2, sbox, cbox, gbox, form, menu_shell;
1750 TV_INT32 i;
1751
1752 /* Create the dialog widgets */
1753 *dialog_wgt = XtVaCreatePopupShell( "videoSaveDialog",
1754 transientShellWidgetClass, TVTOPLEVEL,
1755 NULL );
1756
1757 main1 = XtVaCreateManagedWidget( "mainBox", boxWidgetClass, *dialog_wgt,
1758 XtNorientation, XtorientVertical,
1759 NULL );
1760 Main_wgt = main1;
1761
1762 main2 = XtVaCreateManagedWidget( "groupBox", boxWidgetClass, main1,
1763 XtNorientation, XtorientVertical,
1764 NULL );
1765
1766 /* Filename base text */
1767 cbox = XtVaCreateManagedWidget( "fileBaseBox", boxWidgetClass, main2,
1768 XtNorientation, XtorientHorizontal,
1769 NULL );
1770
1771 w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, cbox,
1772 XtNlabel, " ",
1773 XtNresize, False,
1774 XtNwidth, 170,
1775 NULL );
1776
1777 File_label = XtVaCreateManagedWidget( "fileBaseLabel", labelWidgetClass,
1778 cbox,
1779 XtNresize, False,
1780 XtNwidth, 100,
1781 XtNjustify, XtJustifyRight,
1782 NULL );
1783
1784 Fname_text = CreateSingleLineTextField( "fileBaseText", cbox );
1785
1786 /* File format selection box */
1787 cbox = XtVaCreateManagedWidget( "fileFmtBox", boxWidgetClass, main2,
1788 XtNorientation, XtorientHorizontal,
1789 XtNfromVert, cbox,
1790 NULL );
1791
1792 w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, cbox,
1793 XtNlabel, " ",
1794 XtNresize, False,
1795 XtNwidth, 170,
1796 NULL );
1797
1798 w = XtVaCreateManagedWidget( "targetLabel", labelWidgetClass, cbox,
1799 XtNjustify, XtJustifyRight,
1800 XtNwidth, 100,
1801 NULL );
1802
1803 Vtrg_menu_btn = XtVaCreateManagedWidget( "targetMenu",
1804 menuButtonWidgetClass, cbox,
1805 XtNresize, XawtextResizeNever,
1806 NULL );
1807
1808 menu_shell = XtVaCreatePopupShell( "menu",
1809 simpleMenuWidgetClass, Vtrg_menu_btn,
1810 NULL );
1811
1812 for ( i = 0; i < Vtrg_item_def_size; i++ ) {
1813 Widget item;
1814
1815 item = XtVaCreateManagedWidget( Vtrg_item_def[i].wgt_name,
1816 smeBSBObjectClass,
1817 menu_shell,
1818 NULL );
1819 Vtrg_item_def[i].wgt = item;
1820
1821 XtAddCallback( item, XtNcallback, VFmtMenuItemCB, &Vtrg_item_def[i] );
1822 }
1823
1824 /* Parameter panels (left and right) */
1825 sbox = XtVaCreateManagedWidget( "sideBox", boxWidgetClass, main2,
1826 XtNorientation, XtorientVertical,
1827 XtNfromVert, cbox,
1828 NULL );
1829
1830 gbox = XtVaCreateManagedWidget( "group1Box", boxWidgetClass, sbox,
1831 XtNorientation, XtorientHorizontal,
1832 NULL );
1833
1834 BuildImageCapWgtPanel( gbox );
1835 BuildAudioCapWgtPanel( gbox );
1836
1837 gbox = XtVaCreateManagedWidget( "group2Box", boxWidgetClass, sbox,
1838 XtNorientation, XtorientHorizontal,
1839 NULL );
1840
1841 BuildImageEncWgtPanel( gbox );
1842 BuildAudioEncWgtPanel( gbox );
1843
1844
1845 /* Cleanup Temp Files toggle */
1846 form = XtVaCreateManagedWidget( "cleanupForm", formWidgetClass, main2,
1847 XtNorientation, XtorientHorizontal,
1848 NULL );
1849 Cleanup_form = form;
1850
1851 w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form,
1852 XtNjustify, XtJustifyLeft,
1853 XtNlabel, " ",
1854 XtNresize, False,
1855 XtNencoding, XawTextEncoding8bit,
1856 XtNwidth, 250,
1857 NULL );
1858
1859 w = XtVaCreateManagedWidget( "cleanupToggle", toggleWidgetClass, form,
1860 XtNlabel, " ",
1861 XtNfromHoriz, w,
1862 NULL );
1863 Cleanup_wgt = w;
1864
1865 w = XtVaCreateManagedWidget( "cleanupLabel", labelWidgetClass, form,
1866 XtNresize, False,
1867 XtNfromHoriz, w,
1868 XtNencoding, XawTextEncoding8bit,
1869 XtNjustify, XtJustifyLeft,
1870 NULL );
1871 /* Action button form */
1872 form = XtVaCreateManagedWidget( "actionForm", formWidgetClass, main1,
1873 XtNfromVert, gbox,
1874 NULL );
1875
1876 w = XtVaCreateManagedWidget( "spacerLabel", labelWidgetClass, form,
1877 XtNjustify, XtJustifyLeft,
1878 XtNlabel, " ",
1879 XtNresize, False,
1880 XtNencoding, XawTextEncoding8bit,
1881 XtNwidth, 200,
1882 NULL );
1883
1884 w = XtVaCreateManagedWidget( "recordCmd", commandWidgetClass, form,
1885 XtNfromHoriz, w,
1886 NULL );
1887 XtAddCallback( w, XtNcallback, RecordCmdCB, "record" );
1888 Record_btn = w;
1889
1890 w = XtVaCreateManagedWidget( "stopCmd", commandWidgetClass, form,
1891 XtNfromHoriz, w,
1892 NULL );
1893 XtAddCallback( w, XtNcallback, StopCmdCB, NULL );
1894 Stop_btn = w;
1895
1896 w = XtVaCreateManagedWidget( "dismissCmd", commandWidgetClass, form,
1897 XtNfromHoriz, w,
1898 NULL );
1899 XtAddCallback( w, XtNcallback, DismissCmdCB, NULL );
1900 Dismiss_btn = w;
1901 }
1902
1903
TVVIDSAVDIALOGPopUp()1904 void TVVIDSAVDIALOGPopUp()
1905 {
1906 /* Do dialog */
1907 if ( Dialog_wgt == NULL )
1908 TVVIDSAVDialogBuild( &Dialog_wgt );
1909
1910 TVVIDSAVDIALOGResync();
1911
1912 XUTILXtPopup( Dialog_wgt, XtGrabNone, TVTOPLEVEL );
1913 }
1914
TVVIDSAVDIALOGResync()1915 void TVVIDSAVDIALOGResync()
1916 {
1917 TV_DISK *d = &G_glob.disk;
1918 char str[80];
1919
1920 /* FIXME: Also install EnterNotify handler for this dialog to */
1921 /* resync values on entry of it's shell. */
1922
1923 if ( Dialog_wgt == NULL )
1924 return;
1925
1926 /* Set text fields to current settings */
1927 TextValUpdate( Fname_text, d->fn_video_base );
1928
1929 sprintf( str, "%ldx%ld", d->video.geom.w, d->video.geom.h );
1930 TextValUpdate( Res_text, str );
1931
1932 sprintf( str, "%ld", d->video.fps );
1933 TextValUpdate( FPS_text, str );
1934
1935 /* Set selections based on active format */
1936 SetMenuSelection( Ffmt_menu_btn, d->audio.file_fmt );
1937 SetMenuSelection( Sfmt_menu_btn, d->audio.sample_fmt );
1938 SetMenuSelection( Chan_menu_btn, d->audio.stereo );
1939 SetMenuSelection( Rate_menu_btn, d->audio.sample_rate );
1940
1941 SetMenuSelection( Icap_fmt_menu_btn , d->video.icap_fmt );
1942 SetMenuSelection( Ifile_fmt_menu_btn, d->freeze_fmt );
1943
1944 SetMenuSelection( Vtrg_menu_btn, d->video.target );
1945
1946 XtVaSetValues( Cleanup_wgt , XtNstate, d->video.cleanup_temp, NULL);
1947 Audio_enabled = d->video.cap_audio && App_res.do_audio;
1948 XtVaSetValues( Audio_cap_wgt, XtNstate, Audio_enabled , NULL );
1949
1950 XtSetSensitive( Audio_cap_mgr, d->video.cap_audio );
1951 XtSetSensitive( Audio_enc_mgr, d->video.cap_audio );
1952
1953 /* Set button sensitivites */
1954 UpdateButtons();
1955 }
1956
1957
1958 /**@BEGINFUNC**************************************************************
1959
1960 Prototype : void TVVIDSAVDIALOGNewFrameHdlr(
1961 TV_IMAGE *img )
1962
1963 Purpose : Called to handle a new frame when we're capturing video
1964 frames to disk.
1965
1966 Programmer : 31-May-97 Randall Hopper
1967
1968 Parameters : img - I: captured image
1969
1970 Returns : None.
1971
1972 Globals : None.
1973
1974 **@ENDFUNC*****************************************************************/
1975
TVVIDSAVDIALOGNewFrameHdlr(TV_IMAGE * img)1976 void TVVIDSAVDIALOGNewFrameHdlr( TV_IMAGE *img )
1977 {
1978 static long last_time, /* FIXME */
1979 last_save_time,
1980 in_a_row = 0;
1981 TV_RAW_IMAGE_HEADER head;
1982
1983 /* This shouldn't happen */
1984 if ( !Recording )
1985 return;
1986
1987 /* FIXME: Remove this */
1988 #ifdef COPY_FRAME
1989 TV_IMAGE tmp_img;
1990 static TV_UINT8 *buf = NULL;
1991 static TV_INT32 buf_size = 0;
1992 TV_INT32 bytes;
1993
1994
1995 /* Alloc mem for tmp copy of image -- FIXME hack */
1996 /* We do this to avoid the driver stomping on the image */
1997 /* before we've written it. */
1998 /* Fixme: modify the driver for multiple staging buffers */
1999 /* we shouldn't have to do this copy */
2000 memcpy( &tmp_img, img, sizeof( tmp_img ) );
2001 bytes = TVRAWVIDEOCalcImageSize( &tmp_img );
2002 if (( buf == NULL ) || ( bytes != buf_size )) {
2003 free( buf );
2004 if ( (buf = malloc( bytes )) == NULL )
2005 TVUTILOutOfMemory();
2006 buf_size = bytes;
2007 }
2008 memcpy( buf, img->buf, buf_size );
2009 tmp_img.buf = buf;
2010 # define img (&tmp_img)
2011 #endif
2012
2013 /* If this is the first image, write a header block */
2014 if ( First_image &&
2015 !TVRAWVIDEOHeaderWrite( Out_rf, img, &Snd ) ) {
2016 fprintf( stderr,
2017 "TVVIDSAVDIALOGNewFrameHdlr: header disk write failed\n" );
2018 Recording = FALSE;
2019 return;
2020 }
2021
2022 /* Take note of elapsed time since the last frame so we know how */
2023 /* to temporally space resulting images in the video stream */
2024 {
2025 struct timeval tv;
2026 long new_time;
2027 long diff;
2028
2029 gettimeofday( &tv, NULL );
2030 new_time = (tv.tv_sec & 0xFFFF) * 1000000L + tv.tv_usec;
2031 if ( First_image ) {
2032 VDPRINTF(( "Frame Delays (in microseconds)\n" ));
2033 First_image = FALSE;
2034 head.delay = 0;
2035 in_a_row = 1;
2036 Vid_stats.frames = 0;
2037 Vid_stats.time_us = 0;
2038 last_time = last_save_time = new_time;
2039 }
2040 else {
2041 diff = new_time - last_time;
2042 head.delay = new_time - last_save_time;
2043 Vid_stats.time_us += diff;
2044 Vid_stats.frames++;
2045 last_time = new_time;
2046 if ( /*++*/in_a_row < 2 )
2047 last_save_time = new_time;
2048 else
2049 in_a_row = 0;
2050
2051 VDPRINTF(("%4ld: Delay = %7ld us (Avg = %7ld ms, FPS = %2ld)%s\n",
2052 Vid_stats.frames, diff,
2053 Vid_stats.time_us/Vid_stats.frames/1000,
2054 Vid_stats.frames*1000000L/Vid_stats.time_us,
2055 (last_time == last_save_time) ? "" : "...Skipped" ));
2056 }
2057 }
2058
2059 /* FIXME: Maybe check that img w,h,Bpp stay same between images */
2060
2061 if ( last_time == last_save_time ) {
2062 char buf[65536];
2063
2064 /* Read pending audio */
2065 if ( Audio_enabled ) {
2066 #ifdef OLD
2067 audio_buf_info info;
2068
2069 ioctl( Dsp_fd, SNDCTL_DSP_GETISPACE, &info );
2070 VDPRINTF(( "Bufs = %d / %d, FragSize = %d, TotalBytes = %d\n",
2071 info.fragments, info.fragstotal, info.fragsize,
2072 info.bytes ));
2073 #endif
2074
2075 Snd.bytes = read( Dsp_fd, buf, 65536 );
2076 if ( Snd.bytes == -1 )
2077 Snd.bytes = 0;
2078 Snd.buf = buf;
2079 /* FIXME: Deal with case where more than 64k may be ready */
2080 /* Also deal with case where more sound data may become */
2081 /* available while waiting for a frame than can fit in sound */
2082 /* buffers. */
2083 }
2084
2085 if ( !TVRAWVIDEOImageWrite( Out_rf, &head, img, &Snd ) ) {
2086 fprintf( stderr,
2087 "TVVIDSAVDIALOGNewFrameHdlr: image disk write failed\n" );
2088 Recording = FALSE;
2089 return;
2090 }
2091
2092 if ( Audio_enabled )
2093 Snd.buf = NULL, Snd.bytes = 0;
2094 }
2095
2096 /* If we're optimizing, we're done after X num of images */
2097 if ( Optimizing && ( Vid_stats.frames >= OPTIMIZE_NUM_FRAMES ) ) {
2098 Optimizing = False;
2099 XtPopdown ( Wait_dialog );
2100 XtUnrealizeWidget( Wait_dialog ); /* Wake up optimize Xt loop */
2101 }
2102 }
2103
2104
2105 /**@BEGINFUNC**************************************************************
2106
2107 Prototype : void TVVIDSAVDIALOGRecordStart(
2108 TV_INT32 w, TV_INT32 h )
2109
2110 Purpose : Start recording video using the default video capture
2111 parameters.
2112
2113 Programmer : 31-May-98 Randall Hopper
2114
2115 Parameters : w,h - Suggested resolution (e.g. 320x240)
2116 (if 0, use default resolution)
2117
2118 Returns : None.
2119
2120 Globals : None.
2121
2122 **@ENDFUNC*****************************************************************/
2123
TVVIDSAVDIALOGRecordStart(TV_INT32 w,TV_INT32 h)2124 void TVVIDSAVDIALOGRecordStart( TV_INT32 w, TV_INT32 h )
2125 {
2126 char *dir, *file;
2127 TV_BOOL alloc = TRUE;
2128 TV_DISK *d = &G_glob.disk;
2129 char res_str[20];
2130
2131 /* FIXME: Really, we need to separate this save functionality out */
2132 /* from the user interface. */
2133 /* Thus the bogusness of setting the values on the dialog if it */
2134 /* exists. */
2135
2136 /* If no filename, generate one at random. */
2137 if ( d->fn_video_base[0] == '\0' ){
2138 if ( (dir = getcwd( NULL, 0 )) == NULL ) {
2139 dir = "/tmp";
2140 alloc = FALSE;
2141 }
2142
2143 unsetenv( "TMPDIR" );
2144 file = tempnam( dir, "Fxtv-video." );
2145 if ( alloc )
2146 free( dir );
2147 dir = NULL;
2148
2149 d->fn_video_base[0] = '\0';
2150 strncat( d->fn_video_base, file, sizeof(d->fn_video_base)-1 );
2151 free(file);
2152
2153 if ( Fname_text )
2154 TextValUpdate( Fname_text, d->fn_video_base );
2155 }
2156
2157 if (( w > 0 ) && ( h > 0 )) {
2158 d->video.geom.w = w;
2159 d->video.geom.h = h;
2160 if ( Res_text ) {
2161 sprintf( res_str, "%ldx%ld", w,h );
2162 TextValUpdate( Res_text, res_str );
2163 }
2164 }
2165
2166 RecordCmdCB( NULL, "record", NULL );
2167 }
2168
2169
2170 /**@BEGINFUNC**************************************************************
2171
2172 Prototype : void TVVIDSAVDIALOGRecordStop( void )
2173
2174 Purpose : Stop recording video.
2175
2176 Programmer : 31-May-98 Randall Hopper
2177
2178 Parameters : None.
2179
2180 Returns : None.
2181
2182 Globals : None.
2183
2184 **@ENDFUNC*****************************************************************/
2185
TVVIDSAVDIALOGRecordStop(void)2186 void TVVIDSAVDIALOGRecordStop( void )
2187 {
2188 StopCmdCB( NULL, NULL, NULL );
2189
2190 /* This is a hack. We want the record event loop to "wake up" and */
2191 /* loop. It doesn't when just any event comes in (timer, file */
2192 /* descriptor activity, because apparently some events are processed */
2193 /* internally by XtAppNextEvent. This is bad. Of course, our */
2194 /* having internal X event loop is bad too but anyway. */
2195 /* So we send ourselves a dummy client message event. This magically */
2196 /* XtAppNextEvent to return, so we can test for loop termination */
2197 /* conditions and get on with life. */
2198 {
2199 static Atom xa_FXTV_WAKEUP_NEXTEVENT = None;
2200 XClientMessageEvent ev;
2201
2202 if ( xa_FXTV_WAKEUP_NEXTEVENT == None )
2203 xa_FXTV_WAKEUP_NEXTEVENT = XInternAtom(TVDISPLAY,
2204 "FXTV_WAKEUP_NEXTEVENT", False);
2205
2206 ev.type = ClientMessage;
2207 ev.display = TVDISPLAY;
2208 ev.message_type = xa_FXTV_WAKEUP_NEXTEVENT;
2209 ev.format = 8;
2210 sprintf( ev.data.b, "Hey, wake up!" );
2211 ev.window = XtWindow(TVTOPLEVEL);
2212
2213 XSendEvent( TVDISPLAY, XtWindow(TVTOPLEVEL),
2214 True, NoEventMask, (XEvent *) &ev );
2215 XFlush (TVDISPLAY);
2216 }
2217 }
2218