1 /* ogg123.c by Kenneth Arnold <kcarnold-xiph@arnoldnet.net> */
2 /* Maintained by Stan Seibert <volsung@xiph.org>, Monty <monty@xiph.org> */
3 
4 /********************************************************************
5  *                                                                  *
6  * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
7  * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
8  * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
9  * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
10  *                                                                  *
11  * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2005                *
12  * by Stan Seibert <volsung@xiph.org> AND OTHER CONTRIBUTORS        *
13  * http://www.xiph.org/                                             *
14  *                                                                  *
15  ********************************************************************
16 
17  last mod: $Id$
18 
19  ********************************************************************/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <sys/types.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <time.h>
31 #include <getopt.h>
32 #include <signal.h>
33 #include <unistd.h>
34 #include <sys/time.h>
35 #include <sys/stat.h>
36 #include <unistd.h>
37 #include <locale.h>
38 
39 #include "audio.h"
40 #include "buffer.h"
41 #include "callbacks.h"
42 #include "cfgfile_options.h"
43 #include "cmdline_options.h"
44 #include "format.h"
45 #include "transport.h"
46 #include "status.h"
47 #include "playlist.h"
48 #include "compat.h"
49 #include "remote.h"
50 
51 #include "ogg123.h"
52 #include "i18n.h"
53 
54 extern int exit_status; /* from status.c */
55 
56 void play (const char *source_string);
57 
58 #define PRIMAGIC (2*2*2*2*3*3*3*5*7)
59 /* take buffer out of the data segment, not the stack */
60 #define AUDIO_CHUNK_SIZE ((16384 + PRIMAGIC - 1)/ PRIMAGIC * PRIMAGIC)
61 static unsigned char convbuffer[AUDIO_CHUNK_SIZE];
62 static int convsize = AUDIO_CHUNK_SIZE;
63 
64 ogg123_options_t options;
65 stat_format_t *stat_format;
66 static buf_t *audio_buffer=NULL;
67 
68 static audio_play_arg_t audio_play_arg;
69 
70 
71 /* ------------------------- config file options -------------------------- */
72 
73 /* This macro is used to create some dummy variables to hold default values
74    for the options. */
75 #define INIT(type, value) static type type##_##value = value
76 INIT(int, 0);
77 
78 file_option_t file_opts[] = {
79   /* found, name, description, type, ptr, default */
80   {0, "default_device", N_("default output device"), opt_type_string,
81    &options.default_device, NULL},
82   {0, "shuffle",        N_("shuffle playlist"),      opt_type_bool,
83    &options.shuffle,        &int_0},
84   {0, "repeat",         N_("repeat playlist forever"),   opt_type_bool,
85    &options.repeat,         &int_0},
86   {0, NULL,             NULL,                    0,               NULL,                NULL}
87 };
88 
89 
90 /* Flags set by the signal handler to control the threads */
91 signal_request_t sig_request = {0, 0, 0, 0, 0};
92 
93 
94 /* ------------------------------- signal handler ------------------------- */
95 
96 
signal_handler(int signo)97 void signal_handler (int signo)
98 {
99   struct timeval tv;
100   ogg_int64_t now;
101   switch (signo) {
102   case SIGINT:
103 
104     gettimeofday(&tv, 0);
105 
106     /* Units of milliseconds (need the cast to force 64 arithmetics) */
107     now = (ogg_int64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;
108 
109     if ( (now - sig_request.last_ctrl_c) <= options.delay)
110       sig_request.exit = 1;
111     else
112       sig_request.skipfile = 1;
113 
114     sig_request.cancel = 1;
115     sig_request.last_ctrl_c = now;
116     break;
117 
118   case SIGTERM:
119     sig_request.exit = 1;
120     break;
121 
122   case SIGTSTP:
123     sig_request.pause = 1;
124     /* buffer_Pause (Options.outputOpts.buffer);
125        buffer_WaitForPaused (Options.outputOpts.buffer);
126        }
127        if (Options.outputOpts.devicesOpen == 0) {
128        close_audio_devices (Options.outputOpts.devices);
129        Options.outputOpts.devicesOpen = 0;
130        }
131     */
132     /* open_audio_devices();
133        if (Options.outputOpts.buffer) {
134        buffer_Unpause (Options.outputOpts.buffer);
135        }
136     */
137     break;
138 
139   case SIGCONT:
140     break;  /* Don't need to do anything special to resume */
141   }
142 }
143 
144 /* -------------------------- util functions ---------------------------- */
145 
options_init(ogg123_options_t * opts)146 void options_init (ogg123_options_t *opts)
147 {
148   opts->verbosity = 2;
149   opts->shuffle = 0;
150   opts->delay = 500;
151   opts->nth = 1;
152   opts->ntimes = 1;
153   opts->seekoff = 0.0;
154   opts->endpos = -1.0; /* Mark as unset */
155   opts->seekmode = DECODER_SEEK_NONE;
156   opts->buffer_size = 128 * 1024;
157   opts->prebuffer = 0.0f;
158   opts->input_buffer_size = 64 * 1024;
159   opts->input_prebuffer = 50.0f;
160   opts->default_device = NULL;
161 
162   opts->status_freq = 10.0;
163   opts->playlist = NULL;
164   opts->remote = 0;
165   opts->repeat = 0;
166 
167 }
168 
strtotime(char * s)169 double strtotime(char *s)
170 {
171 	double time;
172 
173 	time = strtod(s, &s);
174 
175 	while (*s == ':')
176 		time = 60 * time + strtod(s + 1, &s);
177 
178 	return time;
179 }
180 
set_seek_opt(ogg123_options_t * ogg123_opts,char * buf)181 void set_seek_opt(ogg123_options_t *ogg123_opts, char *buf) {
182 
183   char *b = buf;
184 
185   /* skip spaces */
186   while (*b && (*b == ' ')) b++;
187 
188   if (*b == '-') {
189   /* relative seek back */
190     ogg123_opts->seekoff = -1 * strtotime(b+1);
191     ogg123_opts->seekmode = DECODER_SEEK_CUR;
192   } else
193   if (*b == '+') {
194   /* relative seek forward */
195     ogg123_opts->seekoff = strtotime(b+1);
196     ogg123_opts->seekmode = DECODER_SEEK_CUR;
197   } else {
198   /* absolute seek */
199     ogg123_opts->seekoff = strtotime(b);
200     ogg123_opts->seekmode = DECODER_SEEK_START;
201   }
202 }
203 
handle_seek_opt(ogg123_options_t * options,decoder_t * decoder,format_t * format)204 int handle_seek_opt(ogg123_options_t *options, decoder_t *decoder, format_t *format) {
205 
206   float pos=decoder->format->statistics(decoder)->current_time;
207 
208   /* this functions handles a seek request. It prevents seeking out
209      of band, i.e. before the beginning or after the end. Instead,
210 	 it seeks to the start or near-end resp. */
211 
212   if (options->seekmode != DECODER_SEEK_NONE) {
213 
214     if (options->seekmode == DECODER_SEEK_START) {
215       pos = options->seekoff;
216     } else {
217       pos += options->seekoff;
218     }
219 
220     if (pos < 0) {
221       pos = 0;
222     }
223 
224     if (pos > decoder->format->statistics(decoder)->total_time) {
225       /* seek to almost the end of the stream */
226       pos = decoder->format->statistics(decoder)->total_time - 0.01;
227     }
228 
229     if (!format->seek(decoder, pos, DECODER_SEEK_START)) {
230       status_error(_("Could not skip to %f in audio stream."), options->seekoff);
231 #if 0
232       /* Handle this fatally -- kill the audio thread */
233       if (audio_buffer != NULL)
234 	buffer_thread_kill(audio_buffer);
235 #endif
236     }
237   }
238 
239   options->seekmode = DECODER_SEEK_NONE;
240 
241   return 1;
242 }
243 
244 /* This function selects which statistics to display for our
245    particular configuration.  This does not have anything to do with
246    verbosity, but rather with which stats make sense to display. */
select_stats(stat_format_t * stats,ogg123_options_t * opts,data_source_t * source,decoder_t * decoder,buf_t * audio_buffer)247 void select_stats (stat_format_t *stats, ogg123_options_t *opts,
248 		   data_source_t *source, decoder_t *decoder,
249 		   buf_t *audio_buffer)
250 {
251   data_source_stats_t *data_source_stats;
252 
253   if (audio_buffer != NULL) {
254     /* Turn on output buffer stats */
255     stats[8].enabled = 1; /* Fill */
256     stats[9].enabled = 1; /* State */
257   } else {
258     stats[8].enabled = 0;
259     stats[9].enabled = 0;
260   }
261 
262   data_source_stats = source->transport->statistics(source);
263   if (data_source_stats->input_buffer_used) {
264     /* Turn on input buffer stats */
265     stats[6].enabled = 1; /* Fill */
266     stats[7].enabled = 1; /* State */
267   } else {
268     stats[6].enabled = 0;
269     stats[7].enabled = 0;
270   }
271   free(data_source_stats);
272 
273   /* Assume we need total time display, and let display_statistics()
274      determine at what point it should be turned off during playback */
275   stats[2].enabled = 1;  /* Remaining playback time */
276   stats[3].enabled = 1;  /* Total playback time */
277 }
278 
279 
280 /* Handles printing statistics depending upon whether or not we have
281    buffering going on */
display_statistics(stat_format_t * stat_format,buf_t * audio_buffer,data_source_t * source,decoder_t * decoder)282 void display_statistics (stat_format_t *stat_format,
283 			 buf_t *audio_buffer,
284 			 data_source_t *source,
285 			 decoder_t *decoder)
286 {
287   print_statistics_arg_t *pstats_arg;
288   buffer_stats_t *buffer_stats;
289 
290   pstats_arg = new_print_statistics_arg(stat_format,
291 					source->transport->statistics(source),
292 					decoder->format->statistics(decoder));
293 
294   if (options.remote) {
295 
296     /* Display statistics via the remote interface */
297     remote_time(pstats_arg->decoder_statistics->current_time,
298                 pstats_arg->decoder_statistics->total_time);
299 
300   } else {
301 
302 	/* Disable/Enable statistics as needed */
303 
304 	if (pstats_arg->decoder_statistics->total_time <
305     	pstats_arg->decoder_statistics->current_time) {
306       stat_format[2].enabled = 0;  /* Remaining playback time */
307       stat_format[3].enabled = 0;  /* Total playback time */
308 	}
309 
310 	if (pstats_arg->data_source_statistics->input_buffer_used) {
311       stat_format[6].enabled = 1;  /* Input buffer fill % */
312       stat_format[7].enabled = 1;  /* Input buffer state  */
313 	}
314 
315 	if (audio_buffer) {
316       /* Place a status update into the buffer */
317       buffer_append_action_at_end(audio_buffer,
318 				  &print_statistics_action,
319 				  pstats_arg);
320 
321       /* And if we are not playing right now, do an immediate
322     	 update just the output buffer */
323       buffer_stats = buffer_statistics(audio_buffer);
324       if (buffer_stats->paused || buffer_stats->prebuffering) {
325     	pstats_arg = new_print_statistics_arg(stat_format,
326 					      NULL,
327 					      NULL);
328     	print_statistics_action(audio_buffer, pstats_arg);
329       }
330       free(buffer_stats);
331 
332 	} else
333       print_statistics_action(NULL, pstats_arg);
334   }
335 }
336 
337 
display_statistics_quick(stat_format_t * stat_format,buf_t * audio_buffer,data_source_t * source,decoder_t * decoder)338 void display_statistics_quick (stat_format_t *stat_format,
339 			       buf_t *audio_buffer,
340 			       data_source_t *source,
341 			       decoder_t *decoder)
342 {
343   print_statistics_arg_t *pstats_arg;
344 
345   pstats_arg = new_print_statistics_arg(stat_format,
346 					source->transport->statistics(source),
347 					decoder->format->statistics(decoder));
348 
349   if (audio_buffer) {
350     print_statistics_action(audio_buffer, pstats_arg);
351   } else
352     print_statistics_action(NULL, pstats_arg);
353 }
354 
current_time(decoder_t * decoder)355 double current_time (decoder_t *decoder)
356 {
357   decoder_stats_t *stats;
358   double ret;
359 
360   stats = decoder->format->statistics(decoder);
361   ret = stats->current_time;
362 
363   free(stats);
364 
365   return ret;
366 }
367 
print_audio_devices_info(audio_device_t * d)368 void print_audio_devices_info(audio_device_t *d)
369 {
370   ao_info *info;
371 
372   while (d != NULL) {
373     info = ao_driver_info(d->driver_id);
374 
375     status_message(2, _("\nAudio Device:   %s"), info->name);
376     status_message(3, _("Author:   %s"), info->author);
377     status_message(3, _("Comments: %s"), info->comment);
378     status_message(2, "");
379 
380     d = d->next_device;
381   }
382 
383 }
384 
385 
386 /* --------------------------- main code -------------------------------- */
387 
388 
389 
main(int argc,char ** argv)390 int main(int argc, char **argv)
391 {
392   int optind;
393   char **playlist_array;
394   int items;
395   struct stat stat_buf;
396   int i;
397 
398   setlocale(LC_ALL, "");
399   bindtextdomain(PACKAGE, LOCALEDIR);
400   textdomain(PACKAGE);
401 
402   ao_initialize();
403   stat_format = stat_format_create();
404   options_init(&options);
405   file_options_init(file_opts);
406 
407   parse_std_configs(file_opts);
408   options.playlist = playlist_create();
409   optind = parse_cmdline_options(argc, argv, &options, file_opts);
410 
411   audio_play_arg.devices = options.devices;
412   audio_play_arg.stat_format = stat_format;
413 
414   /* Add remaining arguments to playlist */
415   for (i = optind; i < argc; i++) {
416     if (stat(argv[i], &stat_buf) == 0) {
417 
418       if (S_ISDIR(stat_buf.st_mode)) {
419 	if (playlist_append_directory(options.playlist, argv[i]) == 0)
420 	  fprintf(stderr,
421 		  _("WARNING: Could not read directory %s.\n"), argv[i]);
422       } else {
423 	playlist_append_file(options.playlist, argv[i]);
424       }
425     } else /* If we can't stat it, it might be a non-disk source */
426       playlist_append_file(options.playlist, argv[i]);
427 
428 
429   }
430 
431 
432   /* Do we have anything left to play? */
433   if (playlist_length(options.playlist) == 0) {
434     cmdline_usage();
435     exit(1);
436   } else {
437     playlist_array = playlist_to_array(options.playlist, &items);
438     playlist_destroy(options.playlist);
439     options.playlist = NULL;
440   }
441 
442   /* Don't use status_message until after this point! */
443   status_init(options.verbosity);
444 
445   print_audio_devices_info(options.devices);
446 
447 
448   /* Setup buffer */
449   if (options.buffer_size > 0) {
450     /* Keep sample size alignment for surround sound with up to 10 channels */
451     options.buffer_size = (options.buffer_size + PRIMAGIC - 1) / PRIMAGIC * PRIMAGIC;
452     audio_buffer = buffer_create(options.buffer_size,
453 				 options.buffer_size * options.prebuffer / 100,
454 				 audio_play_callback, &audio_play_arg,
455 				 AUDIO_CHUNK_SIZE);
456     if (audio_buffer == NULL) {
457       status_error(_("Error: Could not create audio buffer.\n"));
458       exit(1);
459     }
460   } else
461     audio_buffer = NULL;
462 
463 
464   /* Setup signal handlers and callbacks */
465 
466   signal (SIGINT, signal_handler);
467   signal (SIGTSTP, signal_handler);
468   signal (SIGCONT, signal_handler);
469   signal (SIGTERM, signal_handler);
470 
471   if (options.remote) {
472     /* run the mainloop for the remote interface */
473     remote_mainloop();
474 
475   } else {
476 
477     do {
478       /* Shuffle playlist */
479       if (options.shuffle) {
480         int i;
481 
482         srandom(time(NULL));
483 
484         for (i = 0; i < items; i++) {
485           int j = i + random() % (items - i);
486           char *temp = playlist_array[i];
487           playlist_array[i] = playlist_array[j];
488           playlist_array[j] = temp;
489         }
490       }
491 
492       /* Play the files/streams */
493       i = 0;
494       while (i < items && !sig_request.exit) {
495         play(playlist_array[i]);
496         i++;
497       }
498     } while (options.repeat);
499 
500   }
501   playlist_array_destroy(playlist_array, items);
502   status_deinit();
503 
504   if (audio_buffer != NULL) {
505     buffer_destroy (audio_buffer);
506     audio_buffer = NULL;
507   }
508 
509   ao_onexit (options.devices);
510 
511   exit (exit_status);
512 }
513 
play(const char * source_string)514 void play (const char *source_string)
515 {
516   const transport_t *transport;
517   format_t *format;
518   data_source_t *source;
519   decoder_t *decoder;
520 
521   decoder_callbacks_t decoder_callbacks;
522   void *decoder_callbacks_arg;
523 
524   /* Preserve between calls so we only open the audio device when we
525      have to */
526   static audio_format_t old_audio_fmt = { 0, 0, 0, 0, 0 };
527   audio_format_t new_audio_fmt;
528   audio_reopen_arg_t *reopen_arg;
529 
530   /* Flags and counters galore */
531   int eof = 0, eos = 0, ret;
532   int nthc = 0, ntimesc = 0;
533   int next_status = 0;
534   static int status_interval = 0;
535 
536   /* Reset all of the signal flags */
537   sig_request.cancel   = 0;
538   sig_request.skipfile = 0;
539   sig_request.exit     = 0;
540   sig_request.pause    = 0;
541 
542   /* Set preferred audio format (used by decoder) */
543   new_audio_fmt.big_endian = ao_is_big_endian();
544   new_audio_fmt.signed_sample = 1;
545   new_audio_fmt.word_size = 2;
546 
547   /* Select appropriate callbacks */
548   if (audio_buffer != NULL) {
549     decoder_callbacks.printf_error = &decoder_buffered_error_callback;
550     decoder_callbacks.printf_metadata = &decoder_buffered_metadata_callback;
551     decoder_callbacks_arg = audio_buffer;
552   } else {
553     decoder_callbacks.printf_error = &decoder_error_callback;
554     decoder_callbacks.printf_metadata = &decoder_metadata_callback;
555     decoder_callbacks_arg = NULL;
556   }
557 
558   /* Locate and use transport for this data source */
559   if ( (transport = select_transport(source_string)) == NULL ) {
560     status_error(_("No module could be found to read from %s.\n"), source_string);
561     return;
562   }
563 
564   if ( (source = transport->open(source_string, &options)) == NULL ) {
565     status_error(_("Cannot open %s.\n"), source_string);
566     return;
567   }
568 
569   /* Detect the file format and initialize a decoder */
570   if ( (format = select_format(source)) == NULL ) {
571     status_error(_("The file format of %s is not supported.\n"), source_string);
572     return;
573   }
574 
575   if ( (decoder = format->init(source, &options, &new_audio_fmt,
576 			       &decoder_callbacks,
577 			       decoder_callbacks_arg)) == NULL ) {
578 
579     /* We may have failed because of user command */
580     if (!sig_request.cancel)
581       status_error(_("Error opening %s using the %s module."
582 		     "  The file may be corrupted.\n"), source_string,
583 		   format->name);
584     return;
585   }
586 
587   /* Decide which statistics are valid */
588   select_stats(stat_format, &options, source, decoder, audio_buffer);
589 
590   /* Start the audio playback thread before we begin sending data */
591   if (audio_buffer != NULL) {
592 
593     /* First reset mutexes and other synchronization variables */
594     buffer_reset (audio_buffer);
595     buffer_thread_start (audio_buffer);
596   }
597 
598   /* Show which file we are playing */
599   decoder_callbacks.printf_metadata(decoder_callbacks_arg, 1,
600 				    _("Playing: %s"), source_string);
601 
602   /* Skip over audio */
603   if (options.seekoff > 0.0) {
604     /* Note: it may be simpler to handle this condition by just calling:
605      *   handle_seek_opt(&options, decoder, format);
606      * which was introduced with the remote control interface. However, that
607      * function does not call buffer_thread_kill() on error, which is
608      * necessary in this situation.
609      */
610     if (!format->seek(decoder, options.seekoff, DECODER_SEEK_START)) {
611       status_error(_("Could not skip %f seconds of audio."), options.seekoff);
612       if (audio_buffer != NULL)
613 	buffer_thread_kill(audio_buffer);
614       return;
615     }
616   }
617 
618   /* Main loop:  Iterates over all of the logical bitstreams in the file */
619   while (!eof && !sig_request.exit) {
620 
621     /* Loop through data within a logical bitstream */
622     eos = 0;
623     while (!eos && !sig_request.exit) {
624 
625       /* Check signals */
626       if (sig_request.skipfile) {
627 	eof = eos = 1;
628 	break;
629       }
630 
631 	if (options.remote) {
632 
633 		/* run the playloop for the remote interface */
634 		if (remote_playloop()) {
635 			/* end song requested */
636 			eof = eos = 1;
637 			break;
638 		}
639 
640 		/* Skip over audio */
641 		handle_seek_opt(&options, decoder, format);
642 	}
643 
644 	if (sig_request.pause) {
645 	if (audio_buffer)
646 	  buffer_thread_pause (audio_buffer);
647 
648 	kill (getpid(), SIGSTOP); /* We block here until we unpause */
649 
650 	/* Done pausing */
651 	if (audio_buffer)
652 	  buffer_thread_unpause (audio_buffer);
653 
654 	sig_request.pause = 0;
655       }
656 
657 
658       /* Read another block of audio data */
659       ret = format->read(decoder, convbuffer, convsize, &eos, &new_audio_fmt);
660 
661       /* Bail if we need to */
662       if (ret == 0) {
663 	eof = eos = 1;
664 	break;
665       } else if (ret < 0) {
666 	status_error(_("ERROR: Decoding failure.\n"));
667 	break;
668       }
669 
670       /* Check to see if the audio format has changed */
671       if (!audio_format_equal(&new_audio_fmt, &old_audio_fmt)) {
672 	old_audio_fmt = new_audio_fmt;
673 
674 	/* Update our status printing interval */
675 	status_interval = new_audio_fmt.word_size * new_audio_fmt.channels *
676 	  new_audio_fmt.rate / options.status_freq;
677 	next_status = 0;
678 
679 	reopen_arg = new_audio_reopen_arg(options.devices, &new_audio_fmt);
680 
681 	if (audio_buffer)
682 	  buffer_insert_action_at_end(audio_buffer, &audio_reopen_action,
683 				      reopen_arg);
684 	else
685 	  audio_reopen_action(NULL, reopen_arg);
686       }
687 
688 
689       /* Update statistics display if needed */
690       if (next_status <= 0) {
691 	display_statistics(stat_format, audio_buffer, source, decoder);
692 	next_status = status_interval;
693       } else
694 	next_status -= ret;
695 
696       if (options.endpos > 0.0 && options.endpos <= current_time(decoder)) {
697 	eof = eos = 1;
698 	break;
699       }
700 
701 
702       /* Write audio data block to output, skipping or repeating chunks
703 	 as needed */
704       do {
705 
706 	if (nthc-- == 0) {
707           if (audio_buffer) {
708             if (!buffer_submit_data(audio_buffer, convbuffer, ret)) {
709               status_error(_("ERROR: buffer write failed.\n"));
710               eof = eos = 1;
711               break;
712             }
713           } else
714 	    audio_play_callback(convbuffer, ret, eos, &audio_play_arg);
715 
716 	  nthc = options.nth - 1;
717 	}
718 
719       } while (!sig_request.exit && !sig_request.skipfile &&
720 	       ++ntimesc < options.ntimes);
721 
722       ntimesc = 0;
723 
724     } /* End of data loop */
725 
726   } /* End of logical bitstream loop */
727 
728   /* Done playing this logical bitstream.  Clean up house. */
729 
730   if (audio_buffer) {
731 
732     if (!sig_request.exit && !sig_request.skipfile) {
733       buffer_mark_eos(audio_buffer);
734       buffer_wait_for_empty(audio_buffer);
735     }
736 
737     buffer_thread_kill(audio_buffer);
738   }
739 
740   /* Print final stats */
741   display_statistics_quick(stat_format, audio_buffer, source, decoder);
742 
743   format->cleanup(decoder);
744   transport->close(source);
745   status_reset_output_lock();  /* In case we were killed mid-output */
746 
747   status_message(1, _("Done."));
748 
749   if (sig_request.exit)
750     exit (exit_status);
751 }
752 
753