diff options
-rw-r--r-- | src/audio_out/audio_alsa_out.c | 168 | ||||
-rw-r--r-- | src/xine-engine/audio_out.c | 285 |
2 files changed, 291 insertions, 162 deletions
diff --git a/src/audio_out/audio_alsa_out.c b/src/audio_out/audio_alsa_out.c index a4a0c6131..f62b8f714 100644 --- a/src/audio_out/audio_alsa_out.c +++ b/src/audio_out/audio_alsa_out.c @@ -26,7 +26,7 @@ * (c) 2001 James Courtier-Dutton <James@superbug.demon.co.uk> * * - * $Id: audio_alsa_out.c,v 1.79 2002/09/16 15:09:36 jcdutton Exp $ + * $Id: audio_alsa_out.c,v 1.80 2002/10/10 13:12:17 jcdutton Exp $ */ #ifdef HAVE_CONFIG_H @@ -59,7 +59,12 @@ #include "compat.h" #include "audio_out.h" + #define ALSA_LOG +/* +#define LOG_DEBUG +*/ + #define AO_OUT_ALSA_IFACE_VERSION 4 #define BUFFER_TIME 1000*1000 @@ -87,6 +92,7 @@ typedef struct alsa_driver_s { uint32_t bits_per_sample; uint32_t bytes_per_frame; uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */ + snd_pcm_sframes_t buffer_size; struct { pthread_t thread; @@ -170,7 +176,7 @@ static int ao_alsa_open(xine_ao_driver_t *this_gen, uint32_t bits, uint32_t rate snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); - err = snd_output_stdio_attach(&jcd_out, stderr, 0); + err = snd_output_stdio_attach(&jcd_out, stdout, 0); switch (mode) { case AO_CAP_MODE_MONO: @@ -263,7 +269,8 @@ static int ao_alsa_open(xine_ao_driver_t *this_gen, uint32_t bits, uint32_t rate return 0; } /* We wanted non blocking open but now put it back to normal */ - snd_pcm_nonblock(this->audio_fd, 0); + //snd_pcm_nonblock(this->audio_fd, 0); + snd_pcm_nonblock(this->audio_fd, 1); /* * configure audio device */ @@ -316,10 +323,10 @@ static int ao_alsa_open(xine_ao_driver_t *this_gen, uint32_t bits, uint32_t rate printf ("audio_alsa_out: buffer time not available\n"); goto __close; } - buffer_size = snd_pcm_hw_params_get_buffer_size(params); + this->buffer_size = buffer_size = snd_pcm_hw_params_get_buffer_size(params); /* set the period time [us] (interrupt every x us|y samples ...) */ dir=0; - err = snd_pcm_hw_params_set_period_size_near(this->audio_fd, params, buffer_size/2, &dir); + err = snd_pcm_hw_params_set_period_size_near(this->audio_fd, params, buffer_size/8, &dir); /*err = snd_pcm_hw_params_set_period_time_near(this->audio_fd, params, PERIOD_TIME, &dir); */ if (err < 0) { printf ("audio_alsa_out: period time not available"); @@ -331,15 +338,16 @@ static int ao_alsa_open(xine_ao_driver_t *this_gen, uint32_t bits, uint32_t rate goto __close; } - /* Check for pause/resume support */ - this->has_pause_resume = ( snd_pcm_hw_params_can_pause (params) - && snd_pcm_hw_params_can_resume (params) ); /* write the parameters to device */ err = snd_pcm_hw_params(this->audio_fd, params); if (err < 0) { printf ("audio_alsa_out: pcm hw_params failed: %s\n", snd_strerror(err)); goto __close; } + /* Check for pause/resume support */ + this->has_pause_resume = ( snd_pcm_hw_params_can_pause (params) + && snd_pcm_hw_params_can_resume (params) ); + printf ("audio_alsa_out:open pause_resume=%d\n", this->has_pause_resume); this->sample_rate_factor = (double) this->output_sample_rate / (double) this->input_sample_rate; this->bytes_per_frame = snd_pcm_frames_to_bytes (this->audio_fd, 1); /* @@ -370,6 +378,13 @@ static int ao_alsa_open(xine_ao_driver_t *this_gen, uint32_t bits, uint32_t rate goto __close; } + /* never stop the transfer, even on xruns */ + err = snd_pcm_sw_params_set_stop_threshold(this->audio_fd, swparams, buffer_size); + if (err < 0) { + printf ("audio_alsa_out: Unable to set stop threshold: %s\n", snd_strerror(err)); + goto __close; + } + /* Install swparams into current parameters */ err = snd_pcm_sw_params(this->audio_fd, swparams); if (err < 0) { @@ -416,41 +431,51 @@ static int ao_alsa_get_gap_tolerance (xine_ao_driver_t *this_gen) /* * Return the delay. is frames measured by looking at pending samples */ +/* FIXME: delay returns invalid data if status is not RUNNING. + * e.g When there is an XRUN or we are in PREPARED mode. + */ static int ao_alsa_delay (xine_ao_driver_t *this_gen) { - snd_pcm_status_t *pcm_stat; - snd_pcm_sframes_t delay; - + snd_pcm_state_t state; + snd_pcm_sframes_t delay = 0; + int err = 0; + snd_timestamp_t tstamp; + struct timeval now; alsa_driver_t *this = (alsa_driver_t *) this_gen; - snd_pcm_status_alloca(&pcm_stat); - snd_pcm_status(this->audio_fd, pcm_stat); - /* Dump ALSA info to stderr */ - /* snd_pcm_status_dump(pcm_stat, jcd_out); */ - delay=snd_pcm_status_get_delay( pcm_stat ); - /* printf("audio_alsa_out:delay:delay=%ld\n",delay); */ - if (delay < 0) delay = 0; +#ifdef LOG_DEBUG + printf("audio_alsa_out:delay:ENTERED\n"); +#endif + err=snd_pcm_delay( this->audio_fd, &delay ); +#ifdef LOG_DEBUG + printf("audio_alsa_out:delay:delay all=%ld err=%d\n",delay, err); + gettimeofday(&now, 0); + printf("audio_alsa_out:delay: Time = %ld.%ld\n", now.tv_sec, now.tv_usec); + printf("audio_alsa_out:delay:FINISHED\n"); +#endif return delay; -} +} /* * Handle over/under-run */ static void xrun(alsa_driver_t *this) { - snd_pcm_status_t *status; + //snd_pcm_status_t *status; int res; - snd_pcm_status_alloca(&status); - if ((res = snd_pcm_status(this->audio_fd, status))<0) { - printf ("audio_alsa_out: status error: %s\n", snd_strerror(res)); - return; - } - if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { - struct timeval now, diff, tstamp; - gettimeofday(&now, 0); - snd_pcm_status_get_trigger_tstamp(status, &tstamp); - timersub(&now, &tstamp, &diff); - printf ("audio_alsa_out: xrun!!! (at least %.3f ms long)\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0); + //snd_pcm_status_alloca(&status); + //if ((res = snd_pcm_status(this->audio_fd, status))<0) { + // printf ("audio_alsa_out: status error: %s\n", snd_strerror(res)); + // return; + //} + //snd_pcm_status_dump(status, jcd_out); + if (snd_pcm_state(this->audio_fd) == SND_PCM_STATE_XRUN) { + //struct timeval now, diff, tstamp; + //gettimeofday(&now, 0); + //snd_pcm_status_get_trigger_tstamp(status, &tstamp); + //timersub(&now, &tstamp, &diff); + //printf ("audio_alsa_out: xrun!!! (at least %.3f ms long)\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0); + printf ("audio_alsa_out: XRUN!!!\n"); if ((res = snd_pcm_prepare(this->audio_fd))<0) { printf ("audio_alsa_out: xrun: prepare error: %s", snd_strerror(res)); return; @@ -460,29 +485,94 @@ static void xrun(alsa_driver_t *this) } /* - * Write audio data to output buffer (blocking) + * Write audio data to output buffer (blocking using snd_pcm_wait) */ static int ao_alsa_write(xine_ao_driver_t *this_gen,int16_t *data, uint32_t count) { snd_pcm_sframes_t result; + snd_pcm_status_t *pcm_stat; + snd_pcm_state_t state; + struct timeval now; + int wait_result; + int res; uint8_t *buffer=(uint8_t *)data; snd_pcm_uframes_t number_of_frames = (snd_pcm_uframes_t) count; alsa_driver_t *this = (alsa_driver_t *) this_gen; - + +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:ENTERED\n"); + gettimeofday(&now, 0); + printf("audio_alsa_out:write: Time = %ld.%ld\n", now.tv_sec, now.tv_usec); + printf("audio_alsa_out:write:count=%u\n",count); +#endif + snd_pcm_status_alloca(&pcm_stat); + state = snd_pcm_state(this->audio_fd); + if (state == SND_PCM_STATE_XRUN) { +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:XRUN before\n"); + snd_pcm_status(this->audio_fd, pcm_stat); + snd_pcm_status_dump(pcm_stat, jcd_out); +#endif + if ((res = snd_pcm_prepare(this->audio_fd))<0) { + printf ("audio_alsa_out: xrun: prepare error: %s", snd_strerror(res)); + assert(0); + } + state = snd_pcm_state(this->audio_fd); +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:XRUN after\n"); +#endif + } + if ( (state != SND_PCM_STATE_PREPARED) && + (state != SND_PCM_STATE_RUNNING) && + (state != SND_PCM_STATE_DRAINING) ) { + printf("audio_alsa_out:write:BAD STATE, state = %d\n",state); + } + while( number_of_frames > 0) { + if ( (state == SND_PCM_STATE_RUNNING) ) { +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:loop:waiting for Godot\n"); +#endif + wait_result = snd_pcm_wait(this->audio_fd, 1000000); +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:loop:wait_result=%d\n",wait_result); +#endif + } result = snd_pcm_writei(this->audio_fd, buffer, number_of_frames); - /* printf("audio_alsa_out:write:result=%ld\n",result); */ - if (result == -EAGAIN || (result >=0 && result < number_of_frames)) { - /* printf("audio_alsa_out:write:result=%ld\n",result); */ - snd_pcm_wait(this->audio_fd, 1000); - } else if (result == -EPIPE) { - xrun(this); + if (result < 0) { +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:result=%ld:%s\n",result, snd_strerror(result)); +#endif + state = snd_pcm_state(this->audio_fd); + if ( (state != SND_PCM_STATE_PREPARED) && + (state != SND_PCM_STATE_RUNNING) && + (state != SND_PCM_STATE_DRAINING) ) { + printf("audio_alsa_out:write:BAD STATE2, state = %d, going to try XRUN\n",state); + if ((res = snd_pcm_prepare(this->audio_fd))<0) { + printf ("audio_alsa_out: xrun: prepare error: %s", snd_strerror(res)); + assert(0); + } + } } if (result > 0) { number_of_frames -= result; buffer += result * this->bytes_per_frame; } } + if ( (state == SND_PCM_STATE_RUNNING) ) { +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:loop:waiting for Godot2\n"); +#endif + wait_result = snd_pcm_wait(this->audio_fd, 1000000); +#ifdef LOG_DEBUG + printf("audio_alsa_out:write:loop:wait_result=%d\n",wait_result); +#endif + } +#ifdef LOG_DEBUG + gettimeofday(&now, 0); + printf("audio_alsa_out:write: Time = %ld.%ld\n", now.tv_sec, now.tv_usec); + printf("audio_alsa_out:write:FINISHED\n"); +#endif return 1; /* audio samples were processed ok */ } diff --git a/src/xine-engine/audio_out.c b/src/xine-engine/audio_out.c index d66656afa..b82072d60 100644 --- a/src/xine-engine/audio_out.c +++ b/src/xine-engine/audio_out.c @@ -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.66 2002/09/19 01:59:50 guenter Exp $ + * $Id: audio_out.c,v 1.67 2002/10/10 13:12:18 jcdutton Exp $ * * 22-8-2001 James imported some useful AC3 sections from the previous alsa driver. * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org> @@ -284,120 +284,10 @@ static int mode_channels( int mode ) { return 0; } -static void *ao_loop (void *this_gen) { - - ao_instance_t *this = (ao_instance_t *) this_gen; - int64_t hw_vpts; - audio_buffer_t *buf, *in_buf; - int64_t gap; - int64_t delay; - int64_t cur_time; - int num_output_frames ; - int paused_wait; - int64_t last_sync_time; - int bufs_since_sync; +static audio_buffer_t* prepare_samples( ao_instance_t *this, audio_buffer_t *buf) { double acc_output_frames, output_frame_excess = 0; + int num_output_frames ; - last_sync_time = bufs_since_sync = 0; - - while ((this->audio_loop_running) || - (!this->audio_loop_running && this->out_fifo->first)) { - - in_buf = buf = fifo_remove (this->out_fifo); - bufs_since_sync++; - -#ifdef LOG - printf ("audio_out: got a buffer\n"); -#endif - - do { - pthread_mutex_lock( &this->driver_lock ); - delay = this->driver->delay(this->driver); - pthread_mutex_unlock( &this->driver_lock ); - - /* - * where, in the timeline is the "end" of the - * hardware audio buffer at the moment? - */ - - cur_time = this->metronom->get_current_time (this->metronom); - hw_vpts = cur_time; - -#ifdef LOG - printf ("audio_out: current delay is %lld, current time is %lld\n", - delay, cur_time); -#endif - - /* External A52 decoder delay correction */ - if ((this->output.mode==AO_CAP_MODE_A52) || (this->output.mode==AO_CAP_MODE_AC5)) - delay += this->passthrough_offset; - - hw_vpts += delay * 1024 / this->frames_per_kpts; - - /* - * calculate gap: - */ - - gap = buf->vpts - hw_vpts; - - /* wait until user unpauses stream - audio_paused == 1 means we are playing at a different speed - them we must process buffers otherwise the entire engine will stop. - */ - paused_wait = (this->audio_paused == 2) || - (this->audio_paused && gap > this->gap_tolerance); - - if ( paused_wait ) { - this->metronom->allow_full_ao_fill_gap = 1; - xine_usec_sleep (50000); - } - } while ( paused_wait ); - -#ifdef LOG - printf ("audio_out: hw_vpts : %lld buffer_vpts : %lld gap : %lld\n", - hw_vpts, buf->vpts, gap); -#endif - - /* - * output audio data synced to master clock - */ - pthread_mutex_lock( &this->driver_lock ); - - if (gap < (-1 * AO_MAX_GAP) || !buf->num_frames || - this->audio_paused ) { - - /* drop package */ - -#ifdef LOG - printf ("audio_out: audio package (vpts = %lld, gap = %lld) dropped\n", - buf->vpts, gap); -#endif - - } else { - - /* for small gaps ( tolerance < abs(gap) < AO_MAX_GAP ) - * feedback them into metronom's vpts_offset. - */ - if ( abs(gap) < AO_MAX_GAP && abs(gap) > this->gap_tolerance && - cur_time > (last_sync_time + SYNC_TIME_INVERVAL) && - bufs_since_sync >= SYNC_BUF_INTERVAL ) { - - this->metronom->set_option(this->metronom, METRONOM_ADJ_VPTS_OFFSET, - -gap/SYNC_GAP_RATE ); - last_sync_time = cur_time; - bufs_since_sync = 0; - } - - /* for big gaps output silence */ - if ( gap > AO_MAX_GAP ) { - if (this->metronom->allow_full_ao_fill_gap) { - ao_fill_gap (this, gap); - this->metronom->allow_full_ao_fill_gap = 0; - } else { - ao_fill_gap (this, gap / 2); - } - } - /* * resample and output audio data */ @@ -423,7 +313,7 @@ static void *ao_loop (void *this_gen) { ensure_buffer_size(this->frame_buf[1], 2*mode_channels(this->input.mode), buf->num_frames ); audio_out_resample_8to16((int8_t *)buf->mem, this->frame_buf[1]->mem, - mode_channels(this->input.mode) * buf->num_frames ); + mode_channels(this->input.mode) * buf->num_frames ); buf = swap_frame_buffers(this); } @@ -467,7 +357,7 @@ static void *ao_loop (void *this_gen) { break; } } - + /* mode conversion */ if ( this->input.mode != this->output.mode ) { switch (this->input.mode) { @@ -498,7 +388,7 @@ static void *ao_loop (void *this_gen) { break; } } - + /* convert back to 8 bits after resampling */ if( this->output.bits == 8 && (this->do_resample || this->input.mode != this->output.mode) ) { @@ -508,17 +398,166 @@ static void *ao_loop (void *this_gen) { mode_channels(this->output.mode) * buf->num_frames ); buf = swap_frame_buffers(this); } - - this->driver->write (this->driver, buf->mem, buf->num_frames ); +return buf; +} + + +/* Audio output loop: - + * 1) Check for pause. + * 2) Make sure audio hardware is in RUNNING state. + * 3) Get delay + * 4) Do drop, 0-fill or output samples. + * 5) Go round loop again. + */ +static void *ao_loop (void *this_gen) { + + ao_instance_t *this = (ao_instance_t *) this_gen; + int64_t hw_vpts; + audio_buffer_t *buf, *in_buf, *out_buf; + int64_t gap; + int64_t delay; + int64_t cur_time; + int num_output_frames ; + int paused_wait; + int64_t last_sync_time; + int bufs_since_sync; + double acc_output_frames, output_frame_excess = 0; + + last_sync_time = bufs_since_sync = 0; +#ifdef LOG + printf ("audio_out:loop: next fifo\n"); +#endif + in_buf = buf = fifo_remove (this->out_fifo); + bufs_since_sync++; +#ifdef LOG + printf ("audio_out: got a buffer\n"); +#endif + + while ((this->audio_loop_running) || + (!this->audio_loop_running && this->out_fifo->first)) { + + /* wait until user unpauses stream + audio_paused == 1 means we are playing at a different speed + them we must process buffers otherwise the entire engine will stop. + */ + + while ( this->audio_paused ) { + switch (this->audio_paused) { + case 1: + { + cur_time = this->metronom->get_current_time (this->metronom); + if (buf->vpts < cur_time ) { +#ifdef LOG + printf ("audio_out:loop: next fifo\n"); +#endif + fifo_append (this->free_fifo, in_buf); + in_buf = buf = fifo_remove (this->out_fifo); + bufs_since_sync++; + } + /* We want it to fall through to case 2: */ + } + case 2: + { + this->metronom->allow_full_ao_fill_gap = 1; + printf ("audio_out:loop:pause: I feel sleepy.\n"); + xine_usec_sleep (10000); + printf ("audio_out:loop:pause: I wake up.\n"); + break; + } + } } - pthread_mutex_unlock( &this->driver_lock ); + /* pthread_mutex_lock( &this->driver_lock ); What is this lock for ? */ + delay = this->driver->delay(this->driver); + while (delay <=0) { + /* Get the audio card into RUNNING state. */ + ao_fill_gap (this, 10000); /* FIXME, this PTS of 1000 should == period size */ + delay = this->driver->delay(this->driver); + } + /* pthread_mutex_unlock( &this->driver_lock ); */ + /* + * where, in the timeline is the "end" of the + * hardware audio buffer at the moment? + */ - fifo_append (this->free_fifo, in_buf); + cur_time = this->metronom->get_current_time (this->metronom); + hw_vpts = cur_time; + +#ifdef LOG + printf ("audio_out: current delay is %lld, current time is %lld\n", + delay, cur_time); +#endif + /* External A52 decoder delay correction */ + if ((this->output.mode==AO_CAP_MODE_A52) || (this->output.mode==AO_CAP_MODE_AC5)) + delay += this->passthrough_offset; - } + hw_vpts += delay * 1024 / this->frames_per_kpts; + + /* + * calculate gap: + */ + gap = buf->vpts - hw_vpts; +#ifdef LOG + printf ("audio_out: hw_vpts : %lld buffer_vpts : %lld gap : %lld\n", + hw_vpts, buf->vpts, gap); +#endif - pthread_exit(NULL); + /* + * output audio data synced to master clock + */ + /* pthread_mutex_lock( &this->driver_lock ); */ + + if (gap < (-1 * AO_MAX_GAP) || !buf->num_frames ) { + + /* drop package */ +#ifdef LOG + printf ("audio_out:loop: next fifo\n"); +#endif + fifo_append (this->free_fifo, in_buf); + in_buf = buf = fifo_remove (this->out_fifo); + bufs_since_sync++; + +#ifdef LOG + printf ("audio_out: audio package (vpts = %lld, gap = %lld) dropped\n", + buf->vpts, gap); +#endif + + + /* for small gaps ( tolerance < abs(gap) < AO_MAX_GAP ) + * feedback them into metronom's vpts_offset. + */ + } else if ( abs(gap) < AO_MAX_GAP && abs(gap) > this->gap_tolerance && + cur_time > (last_sync_time + SYNC_TIME_INVERVAL) && + bufs_since_sync >= SYNC_BUF_INTERVAL ) { + + printf ("audio_out: audio_loop: ADJ_VPTS\n"); + this->metronom->set_option(this->metronom, METRONOM_ADJ_VPTS_OFFSET, + -gap/SYNC_GAP_RATE ); + last_sync_time = cur_time; + bufs_since_sync = 0; + } else if ( gap > AO_MAX_GAP ) { + /* for big gaps output silence */ + if (this->metronom->allow_full_ao_fill_gap) { + ao_fill_gap (this, gap); + this->metronom->allow_full_ao_fill_gap = 0; + } else { + ao_fill_gap (this, gap / 2); + } + } else { + out_buf = prepare_samples(this, buf); + + this->driver->write (this->driver, out_buf->mem, out_buf->num_frames ); +#ifdef LOG + printf ("audio_out:loop: next fifo\n"); +#endif + fifo_append (this->free_fifo, in_buf); + in_buf = buf = fifo_remove (this->out_fifo); + bufs_since_sync++; + + } + /* pthread_mutex_unlock( &this->driver_lock ); */ + } + pthread_exit(NULL); return NULL; } @@ -667,7 +706,7 @@ static void ao_put_buffer (ao_instance_t *this, audio_buffer_t *buf) { buf->num_frames); #ifdef LOG - printf ("audio_out: got buffer, pts=%lld, vpts=%lld\n", + printf ("audio_out: ao_put_buffer, pts=%lld, vpts=%lld\n", pts, buf->vpts); #endif |