1 /*
2  *  dwfa.c:     Decoding of WFA-files
3  *
4  *  Written by:     Ullrich Hafner
5  *          Michael Unger
6  *
7  *  This file is part of FIASCO (Fractal Image And Sequence COdec)
8  *  Copyright (C) 1994-2000 Ullrich Hafner
9  */
10 
11 /*
12  *  $Date: 2000/10/28 17:39:29 $
13  *  $Author: hafner $
14  *  $Revision: 5.7 $
15  *  $State: Exp $
16  */
17 
18 #define _DEFAULT_SOURCE 1 /* New name for SVID & BSD source defines */
19 #define _BSD_SOURCE 1   /* Make sure strdup() is in string.h */
20 #define _XOPEN_SOURCE 500  /* Make sure strdup() is in string.h */
21 
22 #include "config.h"
23 #include "pnm.h"
24 
25 #include <stdlib.h>
26 #include <string.h>
27 #include <math.h>
28 
29 #include "nstring.h"
30 
31 #include "types.h"
32 #include "macros.h"
33 
34 #include <getopt.h>
35 
36 #include "binerror.h"
37 #include "misc.h"
38 #include "params.h"
39 #include "fiasco.h"
40 
41 #ifndef X_DISPLAY_MISSING
42 
43 #   include "display.h"
44 #   include "buttons.h"
45 
46 static x11_info_t *xinfo = NULL;
47 
48 #endif /* not X_DISPLAY_MISSING */
49 
50 /*****************************************************************************
51 
52                 prototypes
53 
54 *****************************************************************************/
55 
56 static int
57 checkargs (int argc, char **argv, bool_t *double_resolution, bool_t *panel,
58            int *fps, char **image_name, fiasco_d_options_t **options);
59 static void
60 video_decoder (const char *wfa_name, const char *image_name, bool_t panel,
61                bool_t double_resolution, int fps, fiasco_d_options_t *options);
62 static void
63 get_output_template (const char *image_name, const char *wfa_name,
64                      bool_t color, char **basename, char **suffix);
65 
66 #ifndef X_DISPLAY_MISSING
67 
68 static void
69 show_stored_frames (unsigned char * const *frame_buffer, int last_frame,
70                     x11_info_t *xinfo, binfo_t *binfo, size_t size,
71                     unsigned frame_time);
72 
73 #endif /* not X_DISPLAY_MISSING */
74 
75 /*****************************************************************************
76 
77                 public code
78 
79 *****************************************************************************/
80 
81 int
main(int argc,char ** argv)82 main (int argc, char **argv)
83 {
84     char               *image_name        = NULL; /* output filename */
85     bool_t              double_resolution = NO; /* double resolution of image */
86     bool_t              panel             = NO; /* control panel */
87     int             fps               = -1; /* frame display rate */
88     fiasco_d_options_t *options           = NULL; /* additional coder options */
89     int                 last_arg;    /* last processed cmdline parameter */
90 
91     init_error_handling (argv[0]);
92 
93     last_arg = checkargs (argc, argv, &double_resolution, &panel, &fps,
94                           &image_name, &options);
95 
96     if (last_arg >= argc)
97         video_decoder ("-", image_name, panel, double_resolution, fps, options);
98     else
99         while (last_arg++ < argc)
100             video_decoder (argv [last_arg - 1], image_name, panel,
101                            double_resolution, fps, options);
102 
103     return 0;
104 }
105 
106 /*****************************************************************************
107 
108                 private code
109 
110 *****************************************************************************/
111 
112 static param_t params [] =
113 {
114 #ifdef X_DISPLAY_MISSING
115     {"output", "FILE", 'o', PSTR, {0}, "-",
116      "Write raw PNM frame(s) to `%s'."},
117 #else  /* not X_DISPLAY_MISSING */
118     {"output", "FILE", 'o', POSTR, {0}, NULL,
119      "Write raw PNM frame(s) to INPUT.ppm/pgm [or `%s']."},
120 #endif /* not X_DISPLAY_MISSING */
121     {"double", NULL, 'd', PFLAG, {0}, "FALSE",
122      "Interpolate images to double size before display."},
123     {"fast", NULL, 'r', PFLAG, {0}, "FALSE",
124      "Use 4:2:0 format for fast, low quality output."},
125     {"panel", NULL, 'p', PFLAG, {0}, "FALSE",
126      "Display control panel."},
127     {"magnify", "NUM", 'm', PINT, {0}, "0",
128      "Magnify/reduce image size by a factor of 4^`%s'."},
129     {"framerate", "NUM", 'F', PINT, {0}, "-1",
130      "Set display rate to `%s' frames per second."},
131     {"smoothing", "NUM", 's', PINT, {0}, "-1",
132      "Smooth image(s) by factor `%s' (0-100)"},
133     {NULL, NULL, 0, 0, {0}, NULL, NULL }
134 };
135 
136 static int
checkargs(int argc,char ** argv,bool_t * double_resolution,bool_t * panel,int * fps,char ** image_name,fiasco_d_options_t ** options)137 checkargs (int argc, char **argv, bool_t *double_resolution, bool_t *panel,
138            int *fps, char **image_name, fiasco_d_options_t **options)
139 /*
140  *  Check validness of command line parameters and of the parameter files.
141  *
142  *  Return value.
143  *  index in argv of the first argv-element that is not an option.
144  *
145  *  Side effects:
146  *  'double_resolution', 'panel', 'fps', 'image_name' and 'options'
147  *      are modified.
148  */
149 {
150     int optind;              /* last processed commandline param */
151 
152     optind = parseargs (params, argc, argv,
153 #ifdef X_DISPLAY_MISSING
154                         "Decode FIASCO-FILEs and write frame(s) to disk.",
155 #else  /* not X_DISPLAY_MISSING */
156                         "Decode and display FIASCO-FILEs using X11.",
157 #endif /* not X_DISPLAY_MISSING */
158                         "With no FIASCO-FILE, or if FIASCO-FILE is -, "
159                         "read standard input.\n"
160 #ifndef X_DISPLAY_MISSING
161                         "With --output=[FILE] specified, "
162                         "write frames without displaying them.\n\n"
163 #endif  /* not X_DISPLAY_MISSING */
164                         "Environment:\n"
165                         "FIASCO_DATA   Search path for automata files. "
166                         "Default: ./\n"
167                         "FIASCO_IMAGES Save path for image files. "
168                         "Default: ./", " [FIASCO-FILE]...",
169                         FIASCO_SHARE, "system.fiascorc", ".fiascorc");
170 
171     *image_name        =   (char *)   parameter_value (params, "output");
172     *double_resolution = *((bool_t *) parameter_value (params, "double"));
173     *panel             = *((bool_t *) parameter_value (params, "panel"));
174     *fps           = *((int *)    parameter_value (params, "framerate"));
175 
176     /*
177      *  Additional options ... (have to be set with the fiasco_set_... methods)
178      */
179     *options = fiasco_d_options_new ();
180 
181     {
182         int const n = *((int *) parameter_value (params, "smoothing"));
183 
184         if (!fiasco_d_options_set_smoothing (*options, MAX(-1, n)))
185             error (fiasco_get_error_message ());
186     }
187 
188     {
189         int const n = *((int *) parameter_value (params, "magnify"));
190 
191         if (!fiasco_d_options_set_magnification (*options, n))
192             error (fiasco_get_error_message ());
193     }
194 
195     {
196         bool_t const n = *((bool_t *) parameter_value (params, "fast"));
197 
198         if (!fiasco_d_options_set_4_2_0_format (*options, n > 0 ? YES : NO))
199             error (fiasco_get_error_message ());
200     }
201 
202     return optind;
203 }
204 
205 static void
video_decoder(const char * wfa_name,const char * image_name,bool_t panel,bool_t double_resolution,int fps,fiasco_d_options_t * options)206 video_decoder (const char *wfa_name, const char *image_name, bool_t panel,
207                bool_t double_resolution, int fps, fiasco_d_options_t *options)
208 {
209 #ifndef X_DISPLAY_MISSING
210     fiasco_renderer_t  *renderer     = NULL;
211     unsigned char     **frame_buffer = NULL;
212     binfo_t            *binfo        = NULL; /* buttons info */
213 #endif /* not X_DISPLAY_MISSING */
214 
215     do
216     {
217         unsigned      width, height, frames, n;
218         fiasco_decoder_t *decoder_state;
219         char             *filename;
220         char             *basename;   /* basename of decoded frame */
221         char             *suffix;     /* suffix of decoded frame */
222         unsigned      frame_time;
223 
224         if (!(decoder_state = fiasco_decoder_new (wfa_name, options)))
225             error (fiasco_get_error_message ());
226 
227         if (fps <= 0)         /* then use value of FIASCO file */
228             fps = fiasco_decoder_get_rate (decoder_state);
229         frame_time = fps ? (1000 / fps) : (1000 / 25);
230 
231         if (!(width = fiasco_decoder_get_width (decoder_state)))
232             error (fiasco_get_error_message ());
233 
234         if (!(height = fiasco_decoder_get_height (decoder_state)))
235             error (fiasco_get_error_message ());
236 
237         if (!(frames = fiasco_decoder_get_length (decoder_state)))
238             error (fiasco_get_error_message ());
239 
240         get_output_template (image_name, wfa_name,
241                              fiasco_decoder_is_color (decoder_state),
242                              &basename, &suffix);
243 
244         filename = calloc (strlen (basename) + strlen (suffix) + 2
245                            + 10 + (int) (log10 (frames) + 1), sizeof (char));
246         if (!filename)
247             error ("Out of memory.");
248 
249         for (n = 0; n < frames; n++)
250         {
251             clock_t fps_timer;     /* frames per second timer struct */
252 
253             prg_timer (&fps_timer, START);
254 
255             if (image_name)        /* just write frame to disk */
256             {
257                 if (frames == 1)        /* just one image */
258                 {
259                     if (streq (image_name, "-"))
260                         strcpy (filename, "-");
261                     else
262                         sprintf (filename, "%s.%s", basename, suffix);
263                 }
264                 else
265                 {
266                     fprintf (stderr, "Decoding frame %d to file `%s.%0*d.%s\n",
267                              n, basename, (int) (log10 (frames - 1) + 1),
268                              n, suffix);
269                     sprintf (filename, "%s.%0*d.%s", basename,
270                              (int) (log10 (frames - 1) + 1), n, suffix);
271                 }
272 
273                 if (!fiasco_decoder_write_frame (decoder_state, filename))
274                     error (fiasco_get_error_message ());
275             }
276 #ifndef X_DISPLAY_MISSING
277             else
278             {
279                 fiasco_image_t *frame;
280 
281                 if (!(frame = fiasco_decoder_get_frame (decoder_state)))
282                     error (fiasco_get_error_message ());
283 
284                 if (frames == 1)
285                     panel = NO;
286 
287                 if (xinfo == NULL)      /* initialize X11 window */
288                 {
289                     const char * const title =
290                         fiasco_decoder_get_title (decoder_state);
291                     char        titlename [MAXSTRLEN];
292 
293 
294                     sprintf (titlename, "dfiasco " VERSION ": %s",
295                              strlen (title) > 0 ? title : wfa_name);
296                     xinfo =
297                         open_window (titlename, "dfiasco",
298                                      (width  << (double_resolution ? 1 : 0)),
299                                      (height << (double_resolution ? 1 : 0))
300                                      + (panel ? 30 : 0));
301                     alloc_ximage (xinfo, width  << (double_resolution ? 1 : 0),
302                                   height << (double_resolution ? 1 : 0));
303                     if (panel)       /* initialize button panel */
304                         binfo = init_buttons (xinfo, n, frames, 30, 10);
305                     renderer =
306                         fiasco_renderer_new (xinfo->ximage->red_mask,
307                                              xinfo->ximage->green_mask,
308                                              xinfo->ximage->blue_mask,
309                                              xinfo->ximage->bits_per_pixel,
310                                              double_resolution);
311                     if (!renderer)
312                         error (fiasco_get_error_message ());
313                 }
314                 renderer->render (renderer, xinfo->pixels, frame);
315                 frame->delete (frame);
316 
317                 if (frame_buffer != NULL) /* store next frame */
318                 {
319                     size_t size = (width  << (double_resolution ? 1 : 0))
320                         * (height << (double_resolution ? 1 : 0))
321                         * (xinfo->ximage->depth <= 8
322                            ? sizeof (byte_t)
323                            : (xinfo->ximage->depth <= 16
324                               ? sizeof (u_word_t)
325                               : sizeof (unsigned int)));
326 
327                     frame_buffer [n] = malloc (size);
328                     if (!frame_buffer [n])
329                         error ("Out of memory.");
330                     memcpy (frame_buffer [n], xinfo->pixels, size);
331 
332                     if (n == frames - 1)
333                     {
334                         show_stored_frames (frame_buffer, frames - 1,
335                                             xinfo, binfo, size, frame_time);
336                         break;
337                     }
338                 }
339 
340                 display_image (0, 0, xinfo);
341                 if (frames == 1)
342                     wait_for_input (xinfo);
343                 else if (panel)
344                 {
345                     check_events (xinfo, binfo, n, frames);
346                     if (binfo->pressed [QUIT_BUTTON])
347                         /* start from beginning */
348                         break;
349                     if (binfo->pressed [STOP_BUTTON])
350                         /* start from beginning */
351                         n = frames;
352 
353                     if (binfo->pressed [RECORD_BUTTON] && frame_buffer == NULL)
354                     {
355                         n = frames;
356                         frame_buffer =
357                             calloc (frames, sizeof (unsigned char *));
358                         if (!frame_buffer)
359                             error ("Out of memory.");
360                     }
361                 }
362                 while (prg_timer (&fps_timer, STOP) < frame_time) /* wait */
363                     ;
364             }
365 #else
366             if (frame_time) {/* defeat compiler warning */}
367 #endif /* not X_DISPLAY_MISSING */
368         }
369         free (filename);
370 
371         fiasco_decoder_delete (decoder_state);
372     } while (panel
373 
374 #ifndef X_DISPLAY_MISSING
375              && !binfo->pressed [QUIT_BUTTON]
376 #endif /* not X_DISPLAY_MISSING */
377 
378         );
379 
380 #ifndef X_DISPLAY_MISSING
381     if (renderer)
382         renderer->delete (renderer);
383 
384     if (!image_name)
385     {
386         close_window (xinfo);
387         free (xinfo);
388         xinfo = NULL;
389         if (binfo)
390             free (binfo);
391     }
392 #endif /* not X_DISPLAY_MISSING */
393 }
394 
395 static void
get_output_template(const char * image_name,const char * wfa_name,bool_t color,char ** basename,char ** suffix)396 get_output_template (const char *image_name, const char *wfa_name,
397                      bool_t color, char **basename, char **suffix)
398 /*
399  *  Generate image filename template for output of image sequences.
400  *  'wfa_name' is the filename of the WFA stream.
401  *  Images are either saved with filename 'basename'.'suffix' (still images)
402  *  or 'basename'.%03d.'suffix' (videos).
403  *
404  *  No return value.
405  *
406  *  Side effects:
407  *  '*basename' and '*suffix' is set.
408  */
409 {
410     if (!wfa_name || streq (wfa_name, "-"))
411         wfa_name = "stdin";
412     /*
413      *  Generate filename template
414      */
415     if (!image_name || streq (image_name, "") || streq (image_name, "-"))
416     {
417         *basename = strdup (wfa_name);
418         *suffix   = NULL;
419     }
420     else
421     {
422         *basename = strdup (image_name);
423         *suffix   = strrchr (*basename, '.');
424     }
425 
426     if (*suffix)         /* found name 'basename.suffix' */
427     {
428         **suffix = 0;         /* remove dot */
429         (*suffix)++;
430         if (**suffix == 0)
431             *suffix = strdup (color ? "ppm" : "pgm");
432     }
433     else             /* no suffix found, generate one */
434         *suffix = strdup (color ? "ppm" : "pgm");
435 }
436 
437 #ifndef X_DISPLAY_MISSING
438 
439 static void
show_stored_frames(unsigned char * const * frame_buffer,int last_frame,x11_info_t * xinfo,binfo_t * binfo,size_t size,unsigned frame_time)440 show_stored_frames (unsigned char * const *frame_buffer, int last_frame,
441                     x11_info_t *xinfo, binfo_t *binfo, size_t size,
442                     unsigned frame_time)
443 /*
444  *  After a WFA video stream has been saved, all frames have been
445  *  decoded and stored in memory. These frames are then displayed
446  *  in an endless loop.
447  *
448  *  This function never returns, the program is terminated if the
449  *  STOP button is pressed.
450  */
451 {
452     int n = last_frame;          /* frame number */
453 
454     while (1)
455     {
456         clock_t fps_timer;        /* frames per second timer struct */
457 
458         prg_timer (&fps_timer, START);
459 
460         display_image (0, 0, xinfo);
461         check_events (xinfo, binfo, n, last_frame + 1);
462 
463         if (binfo->pressed [STOP_BUTTON])
464             n = 0;
465         else if (binfo->pressed [QUIT_BUTTON])
466             break;
467         else if (binfo->pressed [PLAY_BUTTON])
468             n++;
469         else if (binfo->pressed [RECORD_BUTTON]) /* REWIND is mapped RECORD */
470             n--;
471         if (n < 0)
472             n = last_frame;
473         if (n > last_frame)
474             n = 0;
475 
476         memcpy (xinfo->pixels, frame_buffer [n], size);
477         while (prg_timer (&fps_timer, STOP) < frame_time) /* wait */
478             ;
479     };
480 }
481 
482 #endif /* not X_DISPLAY_MISSING */
483