ogg123.c 16.8 KB
Newer Older
1
/* ogg123.c by Kenneth Arnold <kcarnold-xiph@arnoldnet.net> */
2
/* Maintained by Stan Seibert <volsung@xiph.org> */
Jack Moffitt's avatar
Jack Moffitt committed
3 4 5

/********************************************************************
 *                                                                  *
Monty's avatar
 
Monty committed
6
 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
Jack Moffitt's avatar
Jack Moffitt committed
7 8
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
Monty's avatar
 
Monty committed
9
 * PLEASE READ THESE TERMS BEFORE DISTRIBUTING.                     *
Jack Moffitt's avatar
Jack Moffitt committed
10
 *                                                                  *
Ralph Giles's avatar
Ralph Giles committed
11
 * THE Ogg123 SOURCE CODE IS (C) COPYRIGHT 2000-2005                *
12
 * by Stan Seibert <volsung@xiph.org> AND OTHER CONTRIBUTORS        *
Jack Moffitt's avatar
Jack Moffitt committed
13 14 15 16
 * http://www.xiph.org/                                             *
 *                                                                  *
 ********************************************************************

17
 last mod: $Id: ogg123.c,v 1.71 2003/11/27 19:38:29 volsung Exp $
Jack Moffitt's avatar
Jack Moffitt committed
18 19 20

 ********************************************************************/

21 22 23 24
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

25
#include <sys/types.h>
Jack Moffitt's avatar
Jack Moffitt committed
26
#include <string.h>
27 28
#include <stdlib.h>
#include <stdio.h>
Jack Moffitt's avatar
Jack Moffitt committed
29
#include <errno.h>
Kenneth Arnold's avatar
Kenneth Arnold committed
30
#include <time.h>
31
#include <getopt.h>
32
#include <signal.h>
Michael Smith's avatar
Michael Smith committed
33
#include <unistd.h>
34
#include <sys/time.h>
35 36
#include <sys/stat.h>
#include <unistd.h>
37
#include <locale.h>
Jack Moffitt's avatar
Jack Moffitt committed
38

39 40 41 42 43 44 45 46
#include "audio.h"
#include "buffer.h"
#include "callbacks.h"
#include "cfgfile_options.h"
#include "cmdline_options.h"
#include "format.h"
#include "transport.h"
#include "status.h"
47
#include "playlist.h"
48
#include "compat.h"
49

50
#include "ogg123.h"
51 52
#include "i18n.h"

53
extern int exit_status; /* from status.c */
54

55 56 57
void exit_cleanup ();
void play (char *source_string);

58
/* take buffer out of the data segment, not the stack */
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
#define AUDIO_CHUNK_SIZE 4096
unsigned char convbuffer[AUDIO_CHUNK_SIZE];
int convsize = AUDIO_CHUNK_SIZE;

ogg123_options_t options;
stat_format_t *stat_format;
buf_t *audio_buffer;

audio_play_arg_t audio_play_arg;


/* ------------------------- config file options -------------------------- */

/* This macro is used to create some dummy variables to hold default values
   for the options. */
#define INIT(type, value) type type##_##value = value
INIT(int, 1);
INIT(int, 0);

file_option_t file_opts[] = {
  /* 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 85
  {0, "repeat",         N_("repeat playlist forever"),   opt_type_bool,
   &options.repeat,         &int_0}, 
86
  {0, NULL,             NULL,                    0,               NULL,                NULL}
Kenneth Arnold's avatar
Kenneth Arnold committed
87
};
Jack Moffitt's avatar
Jack Moffitt committed
88 89


90
/* Flags set by the signal handler to control the threads */
91
signal_request_t sig_request = {0, 0, 0, 0, 0};
Jack Moffitt's avatar
Jack Moffitt committed
92

Kenneth Arnold's avatar
Kenneth Arnold committed
93

94
/* ------------------------------- signal handler ------------------------- */
95

Jack Moffitt's avatar
Jack Moffitt committed
96

97 98
void signal_handler (int signo)
{
99 100
  struct timeval tv;
  ogg_int64_t now;
101 102
  switch (signo) {
  case SIGINT:
103 104 105 106 107 108 109

    gettimeofday(&tv, 0);

    /* Units of milliseconds (need the cast to force 64 arithmetics) */
    now = (ogg_int64_t) tv.tv_sec * 1000 + tv.tv_usec / 1000;

    if ( (now - sig_request.last_ctrl_c) <= options.delay)
110 111 112
      sig_request.exit = 1;
    else
      sig_request.skipfile = 1;
113

114
    sig_request.cancel = 1;
115
    sig_request.last_ctrl_c = now;
116 117
    break;

118 119 120 121
  case SIGTERM:
    sig_request.exit = 1;
    break;

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
  case SIGTSTP:
    sig_request.pause = 1;
    /* buffer_Pause (Options.outputOpts.buffer);
       buffer_WaitForPaused (Options.outputOpts.buffer);
       }
       if (Options.outputOpts.devicesOpen == 0) {
       close_audio_devices (Options.outputOpts.devices);
       Options.outputOpts.devicesOpen = 0;
       }
    */
    /* open_audio_devices();
       if (Options.outputOpts.buffer) {
       buffer_Unpause (Options.outputOpts.buffer);
       }
    */
    break;
138

139 140 141 142
  case SIGCONT:
    break;  /* Don't need to do anything special to resume */
  }
}
Jack Moffitt's avatar
Jack Moffitt committed
143

144
/* -------------------------- util functions ---------------------------- */
145

146 147
void options_init (ogg123_options_t *opts)
{
148
  opts->verbosity = 2;
149
  opts->shuffle = 0;
150
  opts->delay = 500;
151 152 153
  opts->nth = 1;
  opts->ntimes = 1;
  opts->seekpos = 0.0;
154
  opts->endpos = -1.0; /* Mark as unset */
155
  opts->buffer_size = 128 * 1024;
156
  opts->prebuffer = 0.0f;
157
  opts->input_buffer_size = 64 * 1024;
158
  opts->input_prebuffer = 50.0f;
159 160 161
  opts->default_device = NULL;

  opts->status_freq = 10.0;
162
  opts->playlist = NULL;
163
  opts->repeat = 0;
164

165
}
Jack Moffitt's avatar
Jack Moffitt committed
166

167

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
/* This function selects which statistics to display for our
   particular configuration.  This does not have anything to do with
   verbosity, but rather with which stats make sense to display. */
void select_stats (stat_format_t *stats, ogg123_options_t *opts, 
		   data_source_t *source, decoder_t *decoder, 
		   buf_t *audio_buffer)
{
  data_source_stats_t *data_source_stats;

  if (audio_buffer != NULL) {
    /* Turn on output buffer stats */
    stats[8].enabled = 1; /* Fill */
    stats[9].enabled = 1; /* State */
  } else {
    stats[8].enabled = 0;
    stats[9].enabled = 0;
  }

  data_source_stats = source->transport->statistics(source);
  if (data_source_stats->input_buffer_used) {
    /* Turn on input buffer stats */
    stats[6].enabled = 1; /* Fill */
    stats[7].enabled = 1; /* State */
  } else {
    stats[6].enabled = 0;
    stats[7].enabled = 0;
  }
Ralph Giles's avatar
Ralph Giles committed
195
  free(data_source_stats);
196
    
197 198 199 200
  /* Assume we need total time display, and let display_statistics()
     determine at what point it should be turned off during playback */
  stats[2].enabled = 1;  /* Remaining playback time */
  stats[3].enabled = 1;  /* Total playback time */
Jack Moffitt's avatar
Jack Moffitt committed
201 202
}

203

204 205 206 207 208 209
/* Handles printing statistics depending upon whether or not we have 
   buffering going on */
void display_statistics (stat_format_t *stat_format,
			 buf_t *audio_buffer, 
			 data_source_t *source,
			 decoder_t *decoder)
210
{
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
  print_statistics_arg_t *pstats_arg;
  buffer_stats_t *buffer_stats;

  pstats_arg = new_print_statistics_arg(stat_format,
					source->transport->statistics(source),
					decoder->format->statistics(decoder));

  /* Disable/Enable statistics as needed */

  if (pstats_arg->decoder_statistics->total_time <
      pstats_arg->decoder_statistics->current_time) {
    stat_format[2].enabled = 0;  /* Remaining playback time */
    stat_format[3].enabled = 0;  /* Total playback time */
  }

  if (pstats_arg->data_source_statistics->input_buffer_used) {
    stat_format[6].enabled = 1;  /* Input buffer fill % */
    stat_format[7].enabled = 1;  /* Input buffer state  */
229 230
  }

231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
  if (audio_buffer) {
    /* Place a status update into the buffer */
    buffer_append_action_at_end(audio_buffer,
				&print_statistics_action,
				pstats_arg);
    
    /* And if we are not playing right now, do an immediate
       update just the output buffer */
    buffer_stats = buffer_statistics(audio_buffer);
    if (buffer_stats->paused || buffer_stats->prebuffering) {
      pstats_arg = new_print_statistics_arg(stat_format,
					    NULL,
					    NULL);
      print_statistics_action(audio_buffer, pstats_arg);
    }
    free(buffer_stats);
    
  } else
    print_statistics_action(NULL, pstats_arg);
250 251
}

252 253 254 255 256

void display_statistics_quick (stat_format_t *stat_format,
			       buf_t *audio_buffer, 
			       data_source_t *source,
			       decoder_t *decoder)
257
{
258 259 260 261 262 263 264 265 266 267
  print_statistics_arg_t *pstats_arg;

  pstats_arg = new_print_statistics_arg(stat_format,
					source->transport->statistics(source),
					decoder->format->statistics(decoder));

  if (audio_buffer) {
    print_statistics_action(audio_buffer, pstats_arg);
  } else
    print_statistics_action(NULL, pstats_arg);
268 269
}

270 271 272 273 274 275 276 277 278 279 280 281
double current_time (decoder_t *decoder)
{
  decoder_stats_t *stats;
  double ret;

  stats = decoder->format->statistics(decoder);
  ret = stats->current_time;

  free(stats);

  return ret;
}
282

283
void print_audio_devices_info(audio_device_t *d)
Jack Moffitt's avatar
Jack Moffitt committed
284
{
285
  ao_info *info;
Kenneth Arnold's avatar
Kenneth Arnold committed
286

287 288 289
  while (d != NULL) {
    info = ao_driver_info(d->driver_id);
    
290 291 292 293
    status_message(2, _("\nAudio Device:   %s"), info->name);
    status_message(3, _("Author:   %s"), info->author);
    status_message(3, _("Comments: %s"), info->comment);
    status_message(2, "");
Kenneth Arnold's avatar
Kenneth Arnold committed
294

295 296
    d = d->next_device;
  }
Jack Moffitt's avatar
Jack Moffitt committed
297

298
}
299 300


301
/* --------------------------- main code -------------------------------- */
Kenneth Arnold's avatar
Kenneth Arnold committed
302

Jack Moffitt's avatar
Jack Moffitt committed
303 304


305 306 307
int main(int argc, char **argv)
{
  int optind;
308 309 310 311
  char **playlist_array;
  int items;
  struct stat stat_buf;
  int i;
312

313
  setlocale(LC_ALL, "");
314 315
  bindtextdomain(PACKAGE, LOCALEDIR);
  textdomain(PACKAGE);
316

317 318 319 320
  ao_initialize();
  stat_format = stat_format_create();
  options_init(&options);
  file_options_init(file_opts);
321

322
  parse_std_configs(file_opts);
323
  options.playlist = playlist_create();
324
  optind = parse_cmdline_options(argc, argv, &options, file_opts);
Kenneth Arnold's avatar
Kenneth Arnold committed
325

326 327
  audio_play_arg.devices = options.devices;
  audio_play_arg.stat_format = stat_format;
Jack Moffitt's avatar
Jack Moffitt committed
328

329 330 331 332 333
  /* Add remaining arguments to playlist */
  for (i = optind; i < argc; i++) {
    if (stat(argv[i], &stat_buf) == 0) {

      if (S_ISDIR(stat_buf.st_mode)) {
334
	if (playlist_append_directory(options.playlist, argv[i]) == 0)
335 336 337
	  fprintf(stderr, 
		  _("Warning: Could not read directory %s.\n"), argv[i]);
      } else {
338
	playlist_append_file(options.playlist, argv[i]);
339
      }
Stan Seibert's avatar
Stan Seibert committed
340
    } else /* If we can't stat it, it might be a non-disk source */
341
      playlist_append_file(options.playlist, argv[i]);
Stan Seibert's avatar
Stan Seibert committed
342

Jack Moffitt's avatar
Jack Moffitt committed
343

344
  }
Jack Moffitt's avatar
Jack Moffitt committed
345 346


347
  /* Do we have anything left to play? */
348
  if (playlist_length(options.playlist) == 0) {
349 350
    cmdline_usage();
    exit(1);
351
  } else {
352 353 354
    playlist_array = playlist_to_array(options.playlist, &items);
    playlist_destroy(options.playlist);
    options.playlist = NULL;
355
  }
356 357

  /* Don't use status_message until after this point! */
358
  status_init(options.verbosity);
359 360 361

  print_audio_devices_info(options.devices);

362 363 364 365 366 367 368 369
  
  /* Setup buffer */ 
  if (options.buffer_size > 0) {
    audio_buffer = buffer_create(options.buffer_size,
				 options.buffer_size * options.prebuffer / 100,
				 audio_play_callback, &audio_play_arg,
				 AUDIO_CHUNK_SIZE);
    if (audio_buffer == NULL) {
370
      status_error(_("Error: Could not create audio buffer.\n"));
371
      exit(1);
Jack Moffitt's avatar
Jack Moffitt committed
372
    }
373 374
  } else
    audio_buffer = NULL;
Jack Moffitt's avatar
Jack Moffitt committed
375 376


377
  /* Setup signal handlers and callbacks */
378

379 380 381 382
  ATEXIT (exit_cleanup);
  signal (SIGINT, signal_handler);
  signal (SIGTSTP, signal_handler);
  signal (SIGCONT, signal_handler);
383
  signal (SIGTERM, signal_handler);
384

385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  do {
    /* Shuffle playlist */
    if (options.shuffle) {
      int i;
    
      srandom(time(NULL));
    
      for (i = 0; i < items; i++) {
        int j = i + random() % (items - i);
        char *temp = playlist_array[i];
        playlist_array[i] = playlist_array[j];
        playlist_array[j] = temp;
      }
    }

    /* Play the files/streams */
    i = 0;
    while (i < items && !sig_request.exit) {
      play(playlist_array[i]);
      i++;
    }
  } while (options.repeat);
407

408
  playlist_array_destroy(playlist_array, items);
409

410
  exit (exit_status);
Kenneth Arnold's avatar
Kenneth Arnold committed
411
}
Michael Smith's avatar
 
Michael Smith committed
412

413
void play (char *source_string)
Michael Smith's avatar
 
Michael Smith committed
414
{
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
  transport_t *transport;
  format_t *format;
  data_source_t *source;
  decoder_t *decoder;

  decoder_callbacks_t decoder_callbacks;
  void *decoder_callbacks_arg;

  /* Preserve between calls so we only open the audio device when we 
     have to */
  static audio_format_t old_audio_fmt = { 0, 0, 0, 0, 0 };
  audio_format_t new_audio_fmt;
  audio_reopen_arg_t *reopen_arg;

  /* Flags and counters galore */
  int eof = 0, eos = 0, ret;
  int nthc = 0, ntimesc = 0;
  int next_status = 0;
433
  static int status_interval = 0;
434

435 436 437 438 439
  /* Reset all of the signal flags */
  sig_request.cancel   = 0;
  sig_request.skipfile = 0;
  sig_request.exit     = 0;
  sig_request.pause    = 0;
440 441 442 443 444 445 446 447 448

  /* Set preferred audio format (used by decoder) */
  new_audio_fmt.big_endian = ao_is_big_endian();
  new_audio_fmt.signed_sample = 1;
  new_audio_fmt.word_size = 2;

  /* Select appropriate callbacks */
  if (audio_buffer != NULL) {
    decoder_callbacks.printf_error = &decoder_buffered_error_callback;
449
    decoder_callbacks.printf_metadata = &decoder_buffered_metadata_callback;
450 451 452
    decoder_callbacks_arg = audio_buffer;
  } else {
    decoder_callbacks.printf_error = &decoder_error_callback;
453
    decoder_callbacks.printf_metadata = &decoder_metadata_callback;
454 455
    decoder_callbacks_arg = NULL;
  }
456

457 458
  /* Locate and use transport for this data source */  
  if ( (transport = select_transport(source_string)) == NULL ) {
459
    status_error(_("No module could be found to read from %s.\n"), source_string);
460 461
    return;
  }
462
  
463
  if ( (source = transport->open(source_string, &options)) == NULL ) {
464
    status_error(_("Cannot open %s.\n"), source_string);
465 466 467 468 469
    return;
  }

  /* Detect the file format and initialize a decoder */
  if ( (format = select_format(source)) == NULL ) {
470
    status_error(_("The file format of %s is not supported.\n"), source_string);
471 472
    return;
  }
473
  
474
  if ( (decoder = format->init(source, &options, &new_audio_fmt, 
475 476
			       &decoder_callbacks,
			       decoder_callbacks_arg)) == NULL ) {
477

478
    /* We may have failed because of user command */
479 480 481 482
    if (!sig_request.cancel)
      status_error(_("Error opening %s using the %s module."
		     "  The file may be corrupted.\n"), source_string,
		   format->name);
483 484 485 486 487 488 489 490
    return;
  }

  /* Decide which statistics are valid */
  select_stats(stat_format, &options, source, decoder, audio_buffer);

  /* Start the audio playback thread before we begin sending data */    
  if (audio_buffer != NULL) {
491
    
492 493 494 495
    /* First reset mutexes and other synchronization variables */
    buffer_reset (audio_buffer);
    buffer_thread_start (audio_buffer);
  }
496

497 498
  /* Show which file we are playing */
  decoder_callbacks.printf_metadata(decoder_callbacks_arg, 1,
499
				    _("Playing: %s"), source_string);
500

501 502
  /* Skip over audio */
  if (options.seekpos > 0.0) {
Stan Seibert's avatar
Stan Seibert committed
503
    if (!format->seek(decoder, options.seekpos, DECODER_SEEK_START)) {
504
      status_error(_("Could not skip %f seconds of audio."), options.seekpos);
Stan Seibert's avatar
Stan Seibert committed
505 506 507 508
      if (audio_buffer != NULL)
	buffer_thread_kill(audio_buffer);
      return;
    }
509 510 511 512 513 514 515 516 517 518 519 520
  }

  /* Main loop:  Iterates over all of the logical bitstreams in the file */
  while (!eof && !sig_request.exit) {
    
    /* Loop through data within a logical bitstream */
    eos = 0;    
    while (!eos && !sig_request.exit) {
      
      /* Check signals */
      if (sig_request.skipfile) {
	eof = eos = 1;
521
	break;
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
      }

      if (sig_request.pause) {
	if (audio_buffer)
	  buffer_thread_pause (audio_buffer);

	kill (getpid(), SIGSTOP); /* We block here until we unpause */
	
	/* Done pausing */
	if (audio_buffer)
	  buffer_thread_unpause (audio_buffer);

	sig_request.pause = 0;
      }


      /* Read another block of audio data */
      ret = format->read(decoder, convbuffer, convsize, &eos, &new_audio_fmt);

      /* Bail if we need to */
      if (ret == 0) {
	eof = eos = 1;
544
	break;
545
      } else if (ret < 0) {
546
	status_error(_("Error: Decoding failure.\n"));
547 548
	break;
      }
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576

      
      /* Check to see if the audio format has changed */
      if (!audio_format_equal(&new_audio_fmt, &old_audio_fmt)) {
	old_audio_fmt = new_audio_fmt;
	
	/* Update our status printing interval */
	status_interval = new_audio_fmt.word_size * new_audio_fmt.channels * 
	  new_audio_fmt.rate / options.status_freq;
	next_status = 0;

	reopen_arg = new_audio_reopen_arg(options.devices, &new_audio_fmt);

	if (audio_buffer)	  
	  buffer_insert_action_at_end(audio_buffer, &audio_reopen_action,
				      reopen_arg);
	else
	  audio_reopen_action(NULL, reopen_arg);
      }
      

      /* Update statistics display if needed */
      if (next_status <= 0) {
	display_statistics(stat_format, audio_buffer, source, decoder); 
	next_status = status_interval;
      } else
	next_status -= ret;

577 578 579 580 581
      if (options.endpos > 0.0 && options.endpos <= current_time(decoder)) {
	eof = eos = 1;
	break;
      }

582 583 584 585

      /* Write audio data block to output, skipping or repeating chunks
	 as needed */
      do {
586
	
587 588 589 590 591 592 593 594 595
	if (nthc-- == 0) {
	  if (audio_buffer)
	    buffer_submit_data(audio_buffer, convbuffer, ret);
	  else
	    audio_play_callback(convbuffer, ret, eos, &audio_play_arg);
	  
	  nthc = options.nth - 1;
	}
	
596 597
      } while (!sig_request.exit && !sig_request.skipfile &&
	       ++ntimesc < options.ntimes);
598 599 600 601 602 603 604 605 606 607 608 609 610 611

      ntimesc = 0;
            
    } /* End of data loop */
    
  } /* End of logical bitstream loop */
  
  /* Done playing this logical bitstream.  Clean up house. */

  if (audio_buffer) {
    
    if (!sig_request.exit && !sig_request.skipfile) {
      buffer_mark_eos(audio_buffer);
      buffer_wait_for_empty(audio_buffer);
612
    }
613 614

    buffer_thread_kill(audio_buffer);
615
  }
616 617 618 619

  /* Print final stats */
  display_statistics_quick(stat_format, audio_buffer, source, decoder); 
   
620
  
621 622 623 624
  format->cleanup(decoder);
  transport->close(source);
  status_reset_output_lock();  /* In case we were killed mid-output */

625
  status_message(1, _("Done."));
626
  
627
  if (sig_request.exit)
628
    exit (exit_status);
629 630 631 632 633 634 635 636 637 638 639 640
}


void exit_cleanup ()
{
      
  if (audio_buffer != NULL) {
    buffer_destroy (audio_buffer);
    audio_buffer = NULL;
  }

  ao_onexit (options.devices);
Michael Smith's avatar
 
Michael Smith committed
641
}