summaryrefslogtreecommitdiff
path: root/src/xine-engine/audio_out.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/xine-engine/audio_out.c')
-rw-r--r--src/xine-engine/audio_out.c547
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;
}