diff options
Diffstat (limited to 'src/xine-engine/audio_out.c')
-rw-r--r-- | src/xine-engine/audio_out.c | 547 |
1 files changed, 361 insertions, 186 deletions
diff --git a/src/xine-engine/audio_out.c b/src/xine-engine/audio_out.c index ab5cb40f4..b56b8b0b4 100644 --- a/src/xine-engine/audio_out.c +++ b/src/xine-engine/audio_out.c @@ -1,7 +1,7 @@ /* * Copyright (C) 2000, 2001 the xine project * - * This file is part of xine, a unix video player. + * This file is part of xine, a free video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,7 +17,7 @@ * along with self program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * - * $Id: audio_out.c,v 1.23 2001/11/04 22:49:38 guenter Exp $ + * $Id: audio_out.c,v 1.24 2001/11/10 13:48:03 guenter Exp $ * * 22-8-2001 James imported some useful AC3 sections from the previous alsa driver. * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org> @@ -66,9 +66,14 @@ #include "metronom.h" #include "utils.h" -#define ZERO_BUF_SIZE 5000 +/* +#define AUDIO_OUT_LOG +*/ -#define MAX_GAP 90000 +#define NUM_AUDIO_BUFFERS 32 +#define AUDIO_BUF_SIZE 32768 + +#define ZERO_BUF_SIZE 5000 struct frmsize_s { @@ -119,57 +124,90 @@ static const struct frmsize_s frmsizecod_tbl[64] = { 640 ,{1280 ,1394 ,1920 } } }; -/* - * open the audio device for writing to - */ -static int ao_open(ao_instance_t *this, - uint32_t bits, uint32_t rate, int mode) -{ - int output_sample_rate; - if ((output_sample_rate=this->driver->open(this->driver,bits,(this->force_rate ? this->force_rate : rate),mode)) == 0) { - printf("audio_out: open failed!\n"); - return 0; - }; +struct audio_fifo_s { + audio_buffer_t *first; + audio_buffer_t *last; + int num_buffers; - printf("audio_out: output sample rate %d\n", output_sample_rate); + pthread_mutex_t mutex; + pthread_cond_t not_empty; +}; - this->mode = mode; - this->input_frame_rate = rate; - this->bits = bits; - this->audio_started = 0; - this->last_audio_vpts = 0; - this->output_frame_rate = output_sample_rate; +static audio_fifo_t *fifo_new () { - switch (this->resample_conf) { - case 1: /* force off */ - this->do_resample = 0; - break; - case 2: /* force on */ - this->do_resample = 1; - break; - default: /* AUTO */ - this->do_resample = this->output_frame_rate != this->input_frame_rate; + audio_fifo_t *fifo; + + fifo = (audio_fifo_t *) xmalloc (sizeof (audio_fifo_t)); + + if (!fifo) { + printf ("audio_out: out of memory!\n"); + return NULL; } - /* HACK: we do not have resample functions for 8-bit audio */ - if (this->bits==8) - this->do_resample = 0; + fifo->first = NULL; + fifo->last = NULL; + fifo->num_buffers = 0; + pthread_mutex_init (&fifo->mutex, NULL); + pthread_cond_init (&fifo->not_empty, NULL); - if (this->do_resample) - printf("audio_out: will resample audio from %d to %d\n", - this->input_frame_rate, this->output_frame_rate); + return fifo; +} - this->num_channels = this->driver->num_channels(this->driver); +static void fifo_append (audio_fifo_t *fifo, + audio_buffer_t *buf) { - this->frame_rate_factor = (double) this->output_frame_rate / (double) this->input_frame_rate; - this->audio_step = (uint32_t) 90000 * (uint32_t) 32768 / this->input_frame_rate; - this->frames_per_kpts = this->output_frame_rate * 1024 / 90000; - xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 frames\n", this->audio_step); + pthread_mutex_lock (&fifo->mutex); - this->metronom->set_audio_rate(this->metronom, this->audio_step); + buf->next = NULL; - return this->output_frame_rate; + if (!fifo->first) { + + fifo->first = buf; + fifo->last = buf; + fifo->num_buffers = 1; + + } else { + + fifo->last->next = buf; + fifo->last = buf; + fifo->num_buffers++; + + } + + pthread_cond_signal (&fifo->not_empty); + pthread_mutex_unlock (&fifo->mutex); +} + +static audio_buffer_t *fifo_remove (audio_fifo_t *fifo) { + + audio_buffer_t *buf; + + pthread_mutex_lock (&fifo->mutex); + + while (!fifo->first) { + pthread_cond_wait (&fifo->not_empty, &fifo->mutex); + } + + buf = fifo->first; + + if (buf) { + fifo->first = buf->next; + + if (!fifo->first) { + + fifo->last = NULL; + fifo->num_buffers = 0; + pthread_cond_init (&fifo->not_empty, NULL); + + } else + fifo->num_buffers--; + + } + + pthread_mutex_unlock (&fifo->mutex); + + return buf; } /* @@ -216,10 +254,6 @@ void write_pause_burst(ao_instance_t *this, uint32_t num_frames) static void ao_fill_gap (ao_instance_t *this, uint32_t pts_len) { int num_frames ; - xprintf (VERBOSE|AUDIO, "audio_out : fill_gap\n"); - - if (pts_len > MAX_GAP) - pts_len = MAX_GAP; num_frames = pts_len * this->frames_per_kpts / 1024; @@ -243,177 +277,297 @@ static void ao_fill_gap (ao_instance_t *this, uint32_t pts_len) { } -static int ao_write(ao_instance_t *this, - int16_t* output_frames, uint32_t num_frames, - uint32_t pts) -{ - uint32_t vpts, buffer_vpts; - int32_t gap; - int bDropPackage; - int delay; - int frame_size; - int fscod; - int frmsizecod; - uint8_t *data; - uint32_t cur_time; - - vpts = this->metronom->got_audio_samples (this->metronom, pts, num_frames); +static void *ao_loop (void *this_gen) { - xprintf (VERBOSE|AUDIO, "audio_out: got %d frames, vpts=%d pts=%d\n", - num_frames, vpts, pts); + ao_instance_t *this = (ao_instance_t *) this_gen; + uint32_t hw_vpts; + audio_buffer_t *buf; + int32_t gap; + int delay; + int frame_size; + int fscod; + int frmsizecod; + uint8_t *data; + uint32_t cur_time; + int num_output_frames ; - if (vpts<this->last_audio_vpts) { - /* reject this */ - xprintf (VERBOSE|AUDIO, "audio_out: rejected frame vpts=%d, last_audio_vpts=%d\n", vpts,this->last_audio_vpts) + this->audio_loop_running = 1; + + while ((this->audio_loop_running) || + (!this->audio_loop_running && this->out_fifo->first)) { - return 1; - } - this->last_audio_vpts = vpts; +#ifdef AUDIO_OUT_LOG + printf ("audio_out: fifo_remove\n"); +#endif + + buf = fifo_remove (this->out_fifo); - bDropPackage = 0; + if (!buf->num_frames) + break; - if (this->audio_started) delay = this->driver->delay(this->driver); - else - delay = 0; - /* - * where, in the timeline is the "end" of the audio buffer at the moment? - */ + /* + * where, in the timeline is the "end" of the + * hardware audio buffer at the moment? + */ - cur_time = this->metronom->get_current_time (this->metronom); - buffer_vpts = cur_time; - - /* External A52 decoder delay correction */ - if ((this->mode==AO_CAP_MODE_A52) || (this->mode==AO_CAP_MODE_AC5)) - delay+=10; - - buffer_vpts += delay * 1024 / this->frames_per_kpts; - - /* - * calculate gap: - */ + cur_time = this->metronom->get_current_time (this->metronom); + hw_vpts = cur_time; - gap = vpts - buffer_vpts; +#ifdef AUDIO_OUT_LOG + printf ("audio_out: current delay is %d, current time is %d\n", + delay, cur_time); +#endif + + /* External A52 decoder delay correction */ + if ((this->mode==AO_CAP_MODE_A52) || (this->mode==AO_CAP_MODE_AC5)) + delay+=10; - /* - printf ("vpts : %d buffer_vpts : %d gap %d\n", - vpts, buffer_vpts, gap); - */ + hw_vpts += delay * 1024 / this->frames_per_kpts; - if (gap>this->gap_tolerance) { - - - if (gap>15000) - ao_fill_gap (this, gap); - else { - printf ("audio_out: adjusting master clock %d -> %d\n", - cur_time, cur_time + gap); - this->metronom->adjust_clock (this->metronom, - cur_time + gap); - } + /* + * calculate gap: + */ - /* keep xine responsive */ + gap = buf->vpts - hw_vpts; - if (gap>MAX_GAP) - return 0; + /* + printf ("vpts : %d buffer_vpts : %d gap %d\n", + vpts, buffer_vpts, gap); + */ + + /* + * output audio data synced to master clock + */ + + if (gap < (-1 * this->gap_tolerance)) { + + /* drop package */ + + xprintf (VERBOSE|AUDIO, "audio_out: audio package (vpts = %d %d) dropped\n", + vpts, gap); + + } else { + + if (gap>this->gap_tolerance) { - } else if (gap < (-1 * this->gap_tolerance)) { - bDropPackage = 1; - xprintf (VERBOSE|AUDIO, "audio_out: audio package (vpts = %d %d)" - "dropped\n", vpts, gap); - } + if (gap>15000) + ao_fill_gap (this, gap); + else { + printf ("audio_out: adjusting master clock %d -> %d\n", + cur_time, cur_time + gap); + this->metronom->adjust_clock (this->metronom, + cur_time + gap); + } + + } + /* + * resample and output audio data + */ + + num_output_frames = (double) buf->num_frames * this->frame_rate_factor; + + if ((!this->do_resample) + && (this->mode != AO_CAP_MODE_A52) + && (this->mode != AO_CAP_MODE_AC5)) { + + this->driver->write (this->driver, buf->mem, + buf->num_frames ); + } else switch (this->mode) { + case AO_CAP_MODE_MONO: + audio_out_resample_mono (buf->mem, buf->num_frames, + this->frame_buffer, num_output_frames); + this->driver->write(this->driver, this->frame_buffer, num_output_frames); + break; + case AO_CAP_MODE_STEREO: + audio_out_resample_stereo (buf->mem, buf->num_frames, + this->frame_buffer, num_output_frames); + this->driver->write(this->driver, this->frame_buffer, num_output_frames); + break; + case AO_CAP_MODE_4CHANNEL: + audio_out_resample_4channel (buf->mem, buf->num_frames, + this->frame_buffer, num_output_frames); + this->driver->write(this->driver, this->frame_buffer, num_output_frames); + break; + case AO_CAP_MODE_5CHANNEL: + audio_out_resample_5channel (buf->mem, buf->num_frames, + this->frame_buffer, num_output_frames); + this->driver->write(this->driver, this->frame_buffer, num_output_frames); + break; + case AO_CAP_MODE_5_1CHANNEL: + audio_out_resample_6channel (buf->mem, buf->num_frames, + this->frame_buffer, num_output_frames); + this->driver->write(this->driver, this->frame_buffer, num_output_frames); + break; + case AO_CAP_MODE_A52: + + this->frame_buffer[0] = 0xf872; /* spdif syncword */ + this->frame_buffer[1] = 0x4e1f; /* ............. */ + this->frame_buffer[2] = 0x0001; /* AC3 data */ + + data = (uint8_t *)&buf->mem[1]; /* skip AC3 sync */ + fscod = (data[2] >> 6) & 0x3; + frmsizecod = data[2] & 0x3f; + frame_size = frmsizecod_tbl[frmsizecod].frm_size[fscod] << 4; + this->frame_buffer[3] = frame_size; + + /* ac3 seems to be swabbed data */ + swab(buf->mem,this->frame_buffer+4, buf->num_frames ); + this->driver->write(this->driver, this->frame_buffer, 1536); + + break; + case AO_CAP_MODE_AC5: + memset(this->frame_buffer,0xff,6144); + this->frame_buffer[0] = 0xf872; /* spdif syncword */ + this->frame_buffer[1] = 0x4e1f; /* ............. */ + this->frame_buffer[2] = 0x0001; /* */ + + this->frame_buffer[3] = 0x3ee0; + + /* ac3 seems to be swabbed data */ + swab(buf->mem,this->frame_buffer+4, 2014 ); + + this->driver->write(this->driver, this->frame_buffer, 1024); + + break; + + } + + } + + fifo_append (this->free_fifo, buf); + + } + + pthread_exit(NULL); + + return NULL; +} + +/* + * open the audio device for writing to, start audio output thread + */ + +static int ao_open(ao_instance_t *this, + uint32_t bits, uint32_t rate, int mode) { + + int output_sample_rate, err; + + if ((output_sample_rate=this->driver->open(this->driver,bits,(this->force_rate ? this->force_rate : rate),mode)) == 0) { + printf("audio_out: open failed!\n"); + return 0; + }; + + printf("audio_out: output sample rate %d\n", output_sample_rate); + + this->mode = mode; + this->input_frame_rate = rate; + this->bits = bits; + this->last_audio_vpts = 0; + this->output_frame_rate = output_sample_rate; + + switch (this->resample_conf) { + case 1: /* force off */ + this->do_resample = 0; + break; + case 2: /* force on */ + this->do_resample = 1; + break; + default: /* AUTO */ + this->do_resample = this->output_frame_rate != this->input_frame_rate; + } + + /* HACK: we do not have resample functions for 8-bit audio */ + if (this->bits==8) + this->do_resample = 0; + + if (this->do_resample) + printf("audio_out: will resample audio from %d to %d\n", + this->input_frame_rate, this->output_frame_rate); + + this->num_channels = this->driver->num_channels(this->driver); + + this->frame_rate_factor = (double) this->output_frame_rate / (double) this->input_frame_rate; + this->audio_step = (uint32_t) 90000 * (uint32_t) 32768 / this->input_frame_rate; + this->frames_per_kpts = this->output_frame_rate * 1024 / 90000; + xprintf (VERBOSE|AUDIO, "audio_out : audio_step %d pts per 32768 frames\n", this->audio_step); + + this->metronom->set_audio_rate(this->metronom, this->audio_step); + /* - * resample and output frames + * start output thread */ - if (!bDropPackage) { - int num_output_frames = (double) num_frames * this->frame_rate_factor; - - if ((!this->do_resample) - && (this->mode != AO_CAP_MODE_A52) - && (this->mode != AO_CAP_MODE_AC5)) { - xprintf (VERBOSE|AUDIO, "audio_out: writing without resampling\n"); - this->driver->write (this->driver, output_frames, - num_frames ); - } else switch (this->mode) { - case AO_CAP_MODE_MONO: - audio_out_resample_mono (output_frames, num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_STEREO: - audio_out_resample_stereo (output_frames, num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_4CHANNEL: - audio_out_resample_4channel (output_frames, num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_5CHANNEL: - audio_out_resample_5channel (output_frames, num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_5_1CHANNEL: - audio_out_resample_6channel (output_frames, num_frames, - this->frame_buffer, num_output_frames); - this->driver->write(this->driver, this->frame_buffer, num_output_frames); - break; - case AO_CAP_MODE_A52: - - this->frame_buffer[0] = 0xf872; /* spdif syncword */ - this->frame_buffer[1] = 0x4e1f; /* ............. */ - this->frame_buffer[2] = 0x0001; /* AC3 data */ + if ((err = pthread_create (&this->audio_thread, + NULL, ao_loop, this)) != 0) { - data = (uint8_t *)&output_frames[1]; /* skip AC3 sync */ - fscod = (data[2] >> 6) & 0x3; - frmsizecod = data[2] & 0x3f; - frame_size = frmsizecod_tbl[frmsizecod].frm_size[fscod] << 4; - this->frame_buffer[3] = frame_size; + /* FIXME: how does this happen ? */ - /* ac3 seems to be swabbed data */ - swab(output_frames,this->frame_buffer+4, num_frames ); - this->driver->write(this->driver, this->frame_buffer, 1536); + printf ("audio_out: can't create thread (%s)\n", strerror(err)); + printf ("audio_out: sorry, this should not happen. please restart xine.\n"); + exit(1); - break; - case AO_CAP_MODE_AC5: - memset(this->frame_buffer,0xff,6144); - this->frame_buffer[0] = 0xf872; /* spdif syncword */ - this->frame_buffer[1] = 0x4e1f; /* ............. */ - this->frame_buffer[2] = 0x0001; /* */ + } else + printf ("audio_out: thread created\n"); - this->frame_buffer[3] = 0x3ee0; - /* ac3 seems to be swabbed data */ - swab(output_frames,this->frame_buffer+4, 2014 ); - - this->driver->write(this->driver, this->frame_buffer, 1024); - - break; - } + return this->output_frame_rate; +} - xprintf (AUDIO|VERBOSE, "audio_out :audio package written\n"); +static audio_buffer_t *ao_get_buffer (ao_instance_t *this) { + return fifo_remove (this->free_fifo); +} - /* - * step values - */ +static void ao_put_buffer (ao_instance_t *this, audio_buffer_t *buf) { - this->audio_started = 1; + if (buf->num_frames == 0) { + fifo_append (this->free_fifo, buf); + return; } - return 1; + buf->vpts = this->metronom->got_audio_samples (this->metronom, buf->vpts, + buf->num_frames, buf->scr); + if ( buf->vpts<this->last_audio_vpts) { + + /* reject buffer */ + printf ("audio_out: rejected buffer vpts=%d, last_audio_vpts=%d\n", + buf->vpts, this->last_audio_vpts); + + fifo_append (this->free_fifo, buf); + + } else { + + fifo_append (this->out_fifo, buf); + this->last_audio_vpts = buf->vpts; + + } } +static void ao_close(ao_instance_t *this) { + + audio_buffer_t *audio_buffer; + + printf ("audio_out: stopping thread...\n"); + + if (this->audio_loop_running) { + void *p; + + this->audio_loop_running = 0; + + audio_buffer = fifo_remove(this->free_fifo); + audio_buffer->num_frames = 0; + fifo_append (this->out_fifo, audio_buffer); + + pthread_join (this->audio_thread, &p); + } + + printf ("audio_out: thread stopped, closing driver\n"); -static void ao_close(ao_instance_t *this) -{ this->driver->close(this->driver); } @@ -441,6 +595,7 @@ ao_instance_t *ao_new_instance (ao_driver_t *driver, metronom_t *metronom, config_values_t *config) { ao_instance_t *this; + int i; this = xmalloc (sizeof (ao_instance_t)) ; @@ -448,7 +603,8 @@ ao_instance_t *ao_new_instance (ao_driver_t *driver, metronom_t *metronom, this->metronom = metronom; this->open = ao_open; - this->write = ao_write; + this->get_buffer = ao_get_buffer; + this->put_buffer = ao_put_buffer; this->close = ao_close; this->exit = ao_exit; this->get_capabilities = ao_get_capabilities; @@ -462,6 +618,25 @@ ao_instance_t *ao_new_instance (ao_driver_t *driver, metronom_t *metronom, this->resample_conf = config->lookup_int (config, "audio_resample_mode", 0); this->force_rate = config->lookup_int (config, "audio_force_rate", 0); + /* + * pre-allocate memory for samples + */ + + this->free_fifo = fifo_new (); + this->out_fifo = fifo_new (); + + for (i=0; i<NUM_AUDIO_BUFFERS; i++) { + + audio_buffer_t *buf; + + buf = (audio_buffer_t *) malloc (sizeof (audio_buffer_t)); + buf->mem = malloc (AUDIO_BUF_SIZE); + buf->mem_size = AUDIO_BUF_SIZE; + + fifo_append (this->free_fifo, buf); + } + + return this; } |